Refactor Tender Approval Management and Update API Documentation
- Replaced the existing tender approval update and delete endpoints with a new toggle functionality, allowing for more streamlined management of tender approvals. - Introduced the `ToggleTenderApproval` method in the service layer to handle both approval and rejection actions based on the current state of the tender approval. - Updated the API documentation to reflect the new endpoint structure and response formats, including the `TenderApprovalWithTenderResponse`. - Enhanced Swagger documentation to ensure accurate representation of the updated tender approval operations. - Removed obsolete methods and routes to maintain a clean and efficient codebase, adhering to Clean Architecture principles.
This commit is contained in:
@@ -137,13 +137,13 @@ func (ta *TenderApproval) HasDecision() bool {
|
||||
}
|
||||
|
||||
// Approve approves the tender with optional notes
|
||||
func (ta *TenderApproval) Approve(notes *string) {
|
||||
func (ta *TenderApproval) Approve() {
|
||||
ta.Status = ApprovalStatusApproved
|
||||
ta.CreatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
// Reject rejects the tender with a reason
|
||||
func (ta *TenderApproval) Reject(reason string) {
|
||||
func (ta *TenderApproval) Reject() {
|
||||
ta.Status = ApprovalStatusRejected
|
||||
ta.CreatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
@@ -68,3 +68,10 @@ type TenderApprovalStatsResponse struct {
|
||||
ApprovalsBySubmission map[string]int64 `json:"approvals_by_submission"`
|
||||
LastUpdated int64 `json:"last_updated"`
|
||||
}
|
||||
|
||||
// ToggleTenderApprovalForm represents the form for toggling a tender approval
|
||||
type ToggleTenderApprovalForm struct {
|
||||
TenderID string `json:"tender_id" valid:"required"`
|
||||
Status ApprovalStatus `json:"status" valid:"required,in(approved|rejected)"`
|
||||
SubmissionMode SubmissionMode `json:"submission_mode" valid:"required,in(self-apply|partnership)"`
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ func NewHandler(service Service, logger logger.Logger) *Handler {
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Security BearerAuth
|
||||
// @Router /api/v1/tender-approvals [post]
|
||||
func (h *Handler) CreateTenderApproval(c echo.Context) error {
|
||||
form, err := response.Parse[CreateTenderApprovalForm](c)
|
||||
func (h *Handler) ToggleTenderApproval(c echo.Context) error {
|
||||
form, err := response.Parse[ToggleTenderApprovalForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (h *Handler) CreateTenderApproval(c echo.Context) error {
|
||||
return response.Unauthorized(c, "Unauthorized")
|
||||
}
|
||||
|
||||
tenderApproval, err := h.service.CreateTenderApproval(c.Request().Context(), form, companyID)
|
||||
tenderApproval, err := h.service.ToggleTenderApproval(c.Request().Context(), form, companyID)
|
||||
if err != nil {
|
||||
if err.Error() == "tender approval already exists for this tender and company" {
|
||||
return response.Conflict(c, err.Error())
|
||||
@@ -56,52 +56,7 @@ func (h *Handler) CreateTenderApproval(c echo.Context) error {
|
||||
return response.InternalServerError(c, "Failed to create tender approval")
|
||||
}
|
||||
|
||||
// Get the created approval with tender details
|
||||
tenderApprovalWithTender, err := h.service.GetTenderApprovalByIDWithTender(c.Request().Context(), tenderApproval.GetID())
|
||||
if err != nil {
|
||||
return response.InternalServerError(c, "Failed to get tender approval details")
|
||||
}
|
||||
|
||||
return response.Created(c, tenderApprovalWithTender, "Tender approval created successfully")
|
||||
}
|
||||
|
||||
// UpdateTenderApproval updates a tender approval
|
||||
// @Summary Update tender approval
|
||||
// @Description Update tender approval information
|
||||
// @Tags TenderApprovals
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Tender Approval ID"
|
||||
// @Param tender_approval body UpdateTenderApprovalForm true "Tender approval update information"
|
||||
// @Success 200 {object} response.APIResponse{data=TenderApprovalResponse} "Tender approval updated successfully"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||
// @Failure 404 {object} response.APIResponse "Not found - Tender approval not found"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Security BearerAuth
|
||||
// @Router /api/v1/tender-approvals/{id} [put]
|
||||
func (h *Handler) UpdateTenderApproval(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
form, err := response.Parse[UpdateTenderApprovalForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
_, err = h.service.UpdateTenderApproval(c.Request().Context(), id, form)
|
||||
if err != nil {
|
||||
if err.Error() == "tender approval not found" {
|
||||
return response.NotFound(c, "Tender approval not found")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to update tender approval")
|
||||
}
|
||||
|
||||
// Get the updated approval with tender details
|
||||
tenderApprovalWithTender, err := h.service.GetTenderApprovalByIDWithTender(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return response.InternalServerError(c, "Failed to get tender approval details")
|
||||
}
|
||||
|
||||
return response.Success(c, tenderApprovalWithTender, "Tender approval updated successfully")
|
||||
return response.Success(c, tenderApproval, "Tender approval updated successfully")
|
||||
}
|
||||
|
||||
// GetTenderApproval retrieves a tender approval by ID
|
||||
@@ -168,35 +123,6 @@ func (h *Handler) GetTenderApprovalByTenderAndCompany(c echo.Context) error {
|
||||
return response.Success(c, tenderApprovalWithTender, "Tender approval retrieved successfully")
|
||||
}
|
||||
|
||||
// DeleteTenderApproval deletes a tender approval
|
||||
// @Summary Delete tender approval
|
||||
// @Description Delete a tender approval
|
||||
// @Tags TenderApprovals
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Tender Approval ID"
|
||||
// @Success 200 {object} response.APIResponse "Tender approval deleted successfully"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid tender approval ID"
|
||||
// @Failure 404 {object} response.APIResponse "Not found - Tender approval not found"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Security BearerAuth
|
||||
// @Router /api/v1/tender-approvals/{id} [delete]
|
||||
func (h *Handler) DeleteTenderApproval(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
err := h.service.DeleteTenderApproval(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "tender approval not found" {
|
||||
return response.NotFound(c, "Tender approval not found")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to delete tender approval")
|
||||
}
|
||||
|
||||
return response.Success(c, map[string]interface{}{
|
||||
"message": "Tender approval deleted successfully",
|
||||
}, "Tender approval deleted successfully")
|
||||
}
|
||||
|
||||
// GetTenderApprovalsByCompanyID retrieves all tender approvals for a specific company
|
||||
// @Summary Get tender approvals by company ID
|
||||
// @Description Retrieve all tender approvals for a specific company with optional filtering
|
||||
@@ -410,7 +336,7 @@ func (h *Handler) GetTenderApprovalsBySubmissionMode(c echo.Context) error {
|
||||
// GetTenderApproval retrieves a tender approval by ID
|
||||
// @Summary Get tender approval by ID
|
||||
// @Description Retrieve detailed tender approval information by ID
|
||||
// @Tags TenderApprovals
|
||||
// @Tags Admin-TenderApprovals
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Tender Approval ID"
|
||||
|
||||
@@ -31,8 +31,7 @@ type Service interface {
|
||||
SearchTenderApprovalsByCriteria(ctx context.Context, criteria *TenderApprovalSearchCriteria) ([]*TenderApproval, error)
|
||||
|
||||
// Business operations
|
||||
ApproveTender(ctx context.Context, id string, form *ApproveTenderForm) (*TenderApproval, error)
|
||||
RejectTender(ctx context.Context, id string, form *RejectTenderForm) (*TenderApproval, error)
|
||||
ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error)
|
||||
|
||||
// Statistics and analytics
|
||||
GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error)
|
||||
@@ -636,67 +635,6 @@ func (s *tenderApprovalService) SearchTenderApprovalsByCriteria(ctx context.Cont
|
||||
return tenderApprovals, nil
|
||||
}
|
||||
|
||||
// ApproveTender approves a tender
|
||||
func (s *tenderApprovalService) ApproveTender(ctx context.Context, id string, form *ApproveTenderForm) (*TenderApproval, error) {
|
||||
// Get tender approval to check if exists
|
||||
tenderApproval, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.New("tender approval not found")
|
||||
}
|
||||
|
||||
// Approve the tender
|
||||
tenderApproval.Approve(form.Notes)
|
||||
|
||||
// Update in database
|
||||
err = s.repository.Update(ctx, tenderApproval)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to approve tender", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tender_approval_id": id,
|
||||
})
|
||||
return nil, errors.New("failed to approve tender")
|
||||
}
|
||||
|
||||
s.logger.Info("Tender approved successfully", map[string]interface{}{
|
||||
"tender_approval_id": tenderApproval.ID,
|
||||
"tender_id": tenderApproval.TenderID,
|
||||
"company_id": tenderApproval.CompanyID,
|
||||
})
|
||||
|
||||
return tenderApproval, nil
|
||||
}
|
||||
|
||||
// RejectTender rejects a tender
|
||||
func (s *tenderApprovalService) RejectTender(ctx context.Context, id string, form *RejectTenderForm) (*TenderApproval, error) {
|
||||
// Get tender approval to check if exists
|
||||
tenderApproval, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.New("tender approval not found")
|
||||
}
|
||||
|
||||
// Reject the tender
|
||||
tenderApproval.Reject(form.Reason)
|
||||
|
||||
// Update in database
|
||||
err = s.repository.Update(ctx, tenderApproval)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to reject tender", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tender_approval_id": id,
|
||||
})
|
||||
return nil, errors.New("failed to reject tender")
|
||||
}
|
||||
|
||||
s.logger.Info("Tender rejected successfully", map[string]interface{}{
|
||||
"tender_approval_id": tenderApproval.ID,
|
||||
"tender_id": tenderApproval.TenderID,
|
||||
"company_id": tenderApproval.CompanyID,
|
||||
"reason": form.Reason,
|
||||
})
|
||||
|
||||
return tenderApproval, nil
|
||||
}
|
||||
|
||||
// GetTenderApprovalStats returns tender approval statistics
|
||||
func (s *tenderApprovalService) GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error) {
|
||||
stats, err := s.repository.GetTenderApprovalStats(ctx)
|
||||
@@ -969,3 +907,68 @@ func (s *tenderApprovalService) ListTenderApprovalsWithTender(ctx context.Contex
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error) {
|
||||
existing, err := s.repository.GetByTenderAndCompany(ctx, req.TenderID, companyID)
|
||||
if err != nil {
|
||||
approval := &TenderApproval{
|
||||
TenderID: req.TenderID,
|
||||
CompanyID: companyID,
|
||||
SubmissionMode: req.SubmissionMode,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
err = s.repository.Create(ctx, approval)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create new tender approval", map[string]interface{}{
|
||||
"tender_id": req.TenderID,
|
||||
"company_id": companyID,
|
||||
"status": req.Status,
|
||||
"submission_mode": req.SubmissionMode,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return approval.ToResponseWithTender(nil), nil
|
||||
}
|
||||
|
||||
if existing.Status == req.Status && existing.SubmissionMode == req.SubmissionMode {
|
||||
err = s.repository.Delete(ctx, existing.GetID())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return existing.ToResponseWithTender(nil), nil
|
||||
} else if existing.Status == req.Status && existing.SubmissionMode != req.SubmissionMode {
|
||||
existing.SubmissionMode = req.SubmissionMode
|
||||
err = s.repository.Update(ctx, existing)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return existing.ToResponseWithTender(nil), nil
|
||||
}
|
||||
|
||||
if existing.Status == ApprovalStatusApproved {
|
||||
existing.Reject()
|
||||
} else {
|
||||
existing.Approve()
|
||||
}
|
||||
|
||||
err = s.repository.Update(ctx, existing)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return existing.ToResponseWithTender(nil), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user