From 392b31626d2688f85bca53de2039cb9b6ad2693d Mon Sep 17 00:00:00 2001 From: "n.nakhostin" Date: Tue, 19 Aug 2025 11:01:49 +0330 Subject: [PATCH] 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. --- cmd/web/docs/docs.go | 223 ++++++++++------------------ cmd/web/docs/swagger.json | 223 ++++++++++------------------ cmd/web/docs/swagger.yaml | 143 +++++++----------- cmd/web/router/routes.go | 5 +- internal/feedback/entity.go | 6 +- internal/feedback/service.go | 6 - internal/tender_approval/entity.go | 4 +- internal/tender_approval/form.go | 7 + internal/tender_approval/handler.go | 84 +---------- internal/tender_approval/service.go | 129 ++++++++-------- 10 files changed, 298 insertions(+), 532 deletions(-) diff --git a/cmd/web/docs/docs.go b/cmd/web/docs/docs.go index 3111882..ae1266f 100644 --- a/cmd/web/docs/docs.go +++ b/cmd/web/docs/docs.go @@ -4264,7 +4264,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "TenderApprovals" + "Admin-TenderApprovals" ], "summary": "Get tender approval by ID", "parameters": [ @@ -5886,7 +5886,7 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/tender_approval.TenderApprovalResponse" + "$ref": "#/definitions/tender_approval.TenderApprovalWithTenderResponse" } } } @@ -6114,139 +6114,6 @@ const docTemplate = `{ } } } - }, - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update tender approval information", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenderApprovals" - ], - "summary": "Update tender approval", - "parameters": [ - { - "type": "string", - "description": "Tender Approval ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Tender approval update information", - "name": "tender_approval", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/tender_approval.UpdateTenderApprovalForm" - } - } - ], - "responses": { - "200": { - "description": "Tender approval updated successfully", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.APIResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/tender_approval.TenderApprovalResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad request - Invalid input data", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "404": { - "description": "Not found - Tender approval not found", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "422": { - "description": "Validation error - Invalid request data", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Delete a tender approval", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenderApprovals" - ], - "summary": "Delete tender approval", - "parameters": [ - { - "type": "string", - "description": "Tender Approval ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Tender approval deleted successfully", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad request - Invalid tender approval ID", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "404": { - "description": "Not found - Tender approval not found", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } } }, "/api/v1/tenders": { @@ -7642,15 +7509,11 @@ const docTemplate = `{ "type": "string", "enum": [ "like", - "dislike", - "unlike", - "undislike" + "dislike" ], "x-enum-varnames": [ "FeedbackTypeLike", - "FeedbackTypeDislike", - "FeedbackTypeUnLike", - "FeedbackTypeUnDislike" + "FeedbackTypeDislike" ] }, "feedback.ToggleFeedbackForm": { @@ -7896,6 +7759,14 @@ const docTemplate = `{ } } }, + "tender_approval.OrganizationResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, "tender_approval.SubmissionMode": { "type": "string", "enum": [ @@ -7992,14 +7863,82 @@ const docTemplate = `{ } } }, - "tender_approval.UpdateTenderApprovalForm": { + "tender_approval.TenderApprovalWithTenderResponse": { "type": "object", "properties": { + "company_id": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "id": { + "type": "string" + }, "status": { "type": "string" }, "submission_mode": { "type": "string" + }, + "tender": { + "$ref": "#/definitions/tender_approval.TenderDetails" + }, + "tender_id": { + "type": "string" + } + } + }, + "tender_approval.TenderDetails": { + "type": "object", + "properties": { + "buyer_organization": { + "$ref": "#/definitions/tender_approval.OrganizationResponse" + }, + "country_code": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "duration_unit": { + "type": "string" + }, + "estimated_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "notice_publication_id": { + "type": "string" + }, + "procedure_code": { + "type": "string" + }, + "procurement_type_code": { + "type": "string" + }, + "publication_date": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "submission_deadline": { + "type": "integer" + }, + "tender_deadline": { + "type": "integer" + }, + "title": { + "type": "string" } } }, diff --git a/cmd/web/docs/swagger.json b/cmd/web/docs/swagger.json index 2fd91f0..1729b1b 100644 --- a/cmd/web/docs/swagger.json +++ b/cmd/web/docs/swagger.json @@ -4258,7 +4258,7 @@ "application/json" ], "tags": [ - "TenderApprovals" + "Admin-TenderApprovals" ], "summary": "Get tender approval by ID", "parameters": [ @@ -5880,7 +5880,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/tender_approval.TenderApprovalResponse" + "$ref": "#/definitions/tender_approval.TenderApprovalWithTenderResponse" } } } @@ -6108,139 +6108,6 @@ } } } - }, - "put": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Update tender approval information", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenderApprovals" - ], - "summary": "Update tender approval", - "parameters": [ - { - "type": "string", - "description": "Tender Approval ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Tender approval update information", - "name": "tender_approval", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/tender_approval.UpdateTenderApprovalForm" - } - } - ], - "responses": { - "200": { - "description": "Tender approval updated successfully", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.APIResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/tender_approval.TenderApprovalResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad request - Invalid input data", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "404": { - "description": "Not found - Tender approval not found", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "422": { - "description": "Validation error - Invalid request data", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Delete a tender approval", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenderApprovals" - ], - "summary": "Delete tender approval", - "parameters": [ - { - "type": "string", - "description": "Tender Approval ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Tender approval deleted successfully", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad request - Invalid tender approval ID", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "404": { - "description": "Not found - Tender approval not found", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } } }, "/api/v1/tenders": { @@ -7636,15 +7503,11 @@ "type": "string", "enum": [ "like", - "dislike", - "unlike", - "undislike" + "dislike" ], "x-enum-varnames": [ "FeedbackTypeLike", - "FeedbackTypeDislike", - "FeedbackTypeUnLike", - "FeedbackTypeUnDislike" + "FeedbackTypeDislike" ] }, "feedback.ToggleFeedbackForm": { @@ -7890,6 +7753,14 @@ } } }, + "tender_approval.OrganizationResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, "tender_approval.SubmissionMode": { "type": "string", "enum": [ @@ -7986,14 +7857,82 @@ } } }, - "tender_approval.UpdateTenderApprovalForm": { + "tender_approval.TenderApprovalWithTenderResponse": { "type": "object", "properties": { + "company_id": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "id": { + "type": "string" + }, "status": { "type": "string" }, "submission_mode": { "type": "string" + }, + "tender": { + "$ref": "#/definitions/tender_approval.TenderDetails" + }, + "tender_id": { + "type": "string" + } + } + }, + "tender_approval.TenderDetails": { + "type": "object", + "properties": { + "buyer_organization": { + "$ref": "#/definitions/tender_approval.OrganizationResponse" + }, + "country_code": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "duration_unit": { + "type": "string" + }, + "estimated_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "notice_publication_id": { + "type": "string" + }, + "procedure_code": { + "type": "string" + }, + "procurement_type_code": { + "type": "string" + }, + "publication_date": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "submission_deadline": { + "type": "integer" + }, + "tender_deadline": { + "type": "integer" + }, + "title": { + "type": "string" } } }, diff --git a/cmd/web/docs/swagger.yaml b/cmd/web/docs/swagger.yaml index e99a696..f0e1eb3 100644 --- a/cmd/web/docs/swagger.yaml +++ b/cmd/web/docs/swagger.yaml @@ -763,14 +763,10 @@ definitions: enum: - like - dislike - - unlike - - undislike type: string x-enum-varnames: - FeedbackTypeLike - FeedbackTypeDislike - - FeedbackTypeUnLike - - FeedbackTypeUnDislike feedback.ToggleFeedbackForm: properties: feedback_type: @@ -937,6 +933,11 @@ definitions: tender_id: type: string type: object + tender_approval.OrganizationResponse: + properties: + name: + type: string + type: object tender_approval.SubmissionMode: enum: - self-apply @@ -1002,12 +1003,57 @@ definitions: total_approvals: type: integer type: object - tender_approval.UpdateTenderApprovalForm: + tender_approval.TenderApprovalWithTenderResponse: properties: + company_id: + type: string + created_at: + type: integer + id: + type: string status: type: string submission_mode: type: string + tender: + $ref: '#/definitions/tender_approval.TenderDetails' + tender_id: + type: string + type: object + tender_approval.TenderDetails: + properties: + buyer_organization: + $ref: '#/definitions/tender_approval.OrganizationResponse' + country_code: + type: string + currency: + type: string + description: + type: string + duration: + type: string + duration_unit: + type: string + estimated_value: + type: number + id: + type: string + notice_publication_id: + type: string + procedure_code: + type: string + procurement_type_code: + type: string + publication_date: + type: integer + status: + type: string + submission_deadline: + type: integer + tender_deadline: + type: integer + title: + type: string type: object user.AuthResponse: properties: @@ -3814,7 +3860,7 @@ paths: - BearerAuth: [] summary: Get tender approval by ID tags: - - TenderApprovals + - Admin-TenderApprovals /admin/v1/tender-approvals/stats: get: consumes: @@ -4910,7 +4956,7 @@ paths: - properties: data: items: - $ref: '#/definitions/tender_approval.TenderApprovalResponse' + $ref: '#/definitions/tender_approval.TenderApprovalWithTenderResponse' type: array type: object "400": @@ -4973,40 +5019,6 @@ paths: tags: - TenderApprovals /api/v1/tender-approvals/{id}: - delete: - consumes: - - application/json - description: Delete a tender approval - parameters: - - description: Tender Approval ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Tender approval deleted successfully - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad request - Invalid tender approval ID - schema: - $ref: '#/definitions/response.APIResponse' - "404": - description: Not found - Tender approval not found - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/response.APIResponse' - security: - - BearerAuth: [] - summary: Delete tender approval - tags: - - TenderApprovals get: consumes: - application/json @@ -5046,55 +5058,6 @@ paths: summary: Get tender approval by ID tags: - TenderApprovals - put: - consumes: - - application/json - description: Update tender approval information - parameters: - - description: Tender Approval ID - in: path - name: id - required: true - type: string - - description: Tender approval update information - in: body - name: tender_approval - required: true - schema: - $ref: '#/definitions/tender_approval.UpdateTenderApprovalForm' - produces: - - application/json - responses: - "200": - description: Tender approval updated successfully - schema: - allOf: - - $ref: '#/definitions/response.APIResponse' - - properties: - data: - $ref: '#/definitions/tender_approval.TenderApprovalResponse' - type: object - "400": - description: Bad request - Invalid input data - schema: - $ref: '#/definitions/response.APIResponse' - "404": - description: Not found - Tender approval not found - schema: - $ref: '#/definitions/response.APIResponse' - "422": - description: Validation error - Invalid request data - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/response.APIResponse' - security: - - BearerAuth: [] - summary: Update tender approval - tags: - - TenderApprovals /api/v1/tender-approvals/tender/{tender_id}: get: consumes: diff --git a/cmd/web/router/routes.go b/cmd/web/router/routes.go index 4042786..51f2d25 100644 --- a/cmd/web/router/routes.go +++ b/cmd/web/router/routes.go @@ -115,7 +115,6 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler tenderApprovalGP.Use(userHandler.AuthMiddleware()) tenderApprovalGP.GET("", tenderApprovalHandler.ListTenderApprovals) tenderApprovalGP.GET("/:id", tenderApprovalHandler.GetTenderApproval) - tenderApprovalGP.PUT("/:id", tenderApprovalHandler.UpdateTenderApproval) tenderApprovalGP.GET("/stats", tenderApprovalHandler.GetTenderApprovalStats) tenderApprovalGP.GET("/submission-mode/:submission_mode", tenderApprovalHandler.GetTenderApprovalsBySubmissionMode) tenderApprovalGP.GET("/status/:status", tenderApprovalHandler.GetTenderApprovalsByStatus) @@ -159,11 +158,9 @@ func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tende tenderApprovalGP := v1.Group("/tender-approvals") { tenderApprovalGP.Use(customerHandler.AuthMiddleware()) - tenderApprovalGP.POST("", tenderApprovalHandler.CreateTenderApproval) - tenderApprovalGP.PUT("/:id", tenderApprovalHandler.UpdateTenderApproval) + tenderApprovalGP.POST("", tenderApprovalHandler.ToggleTenderApproval) tenderApprovalGP.GET("", tenderApprovalHandler.PublicGetTenderApprovals) tenderApprovalGP.GET("/:id", tenderApprovalHandler.GetPublicTenderApproval) tenderApprovalGP.GET("/tender/:tender_id", tenderApprovalHandler.GetTenderApprovalByTenderAndCompany) - tenderApprovalGP.DELETE("/:id", tenderApprovalHandler.DeleteTenderApproval) } } diff --git a/internal/feedback/entity.go b/internal/feedback/entity.go index d9d25a1..3691b5f 100644 --- a/internal/feedback/entity.go +++ b/internal/feedback/entity.go @@ -10,10 +10,8 @@ import ( type FeedbackType string const ( - FeedbackTypeLike FeedbackType = "like" - FeedbackTypeDislike FeedbackType = "dislike" - FeedbackTypeUnLike FeedbackType = "unlike" - FeedbackTypeUnDislike FeedbackType = "undislike" + FeedbackTypeLike FeedbackType = "like" + FeedbackTypeDislike FeedbackType = "dislike" ) // Feedback represents feedback given to a tender by a user diff --git a/internal/feedback/service.go b/internal/feedback/service.go index dffdc0f..3ba11c1 100644 --- a/internal/feedback/service.go +++ b/internal/feedback/service.go @@ -54,12 +54,6 @@ func (s *feedbackService) ToggleFeedback(ctx context.Context, req ToggleFeedback } if existing.FeedbackType == req.FeedbackType { - if existing.FeedbackType == FeedbackTypeLike { - existing.FeedbackType = FeedbackTypeUnLike - } else { - existing.FeedbackType = FeedbackTypeUnDislike - } - err = s.feedbackRepo.Delete(ctx, existing.GetID()) if err != nil { s.logger.Error("Failed to update existing feedback", map[string]interface{}{ diff --git a/internal/tender_approval/entity.go b/internal/tender_approval/entity.go index 1f1ad27..19e0de7 100644 --- a/internal/tender_approval/entity.go +++ b/internal/tender_approval/entity.go @@ -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() } diff --git a/internal/tender_approval/form.go b/internal/tender_approval/form.go index ba0b615..9a2e589 100644 --- a/internal/tender_approval/form.go +++ b/internal/tender_approval/form.go @@ -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)"` +} diff --git a/internal/tender_approval/handler.go b/internal/tender_approval/handler.go index 2852b49..8f419f5 100644 --- a/internal/tender_approval/handler.go +++ b/internal/tender_approval/handler.go @@ -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" diff --git a/internal/tender_approval/service.go b/internal/tender_approval/service.go index 2aac8c7..98f6186 100644 --- a/internal/tender_approval/service.go +++ b/internal/tender_approval/service.go @@ -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 +}