Enhance tender approval and submission handling

- Added a new error for submission sync failures, improving error handling in the tender approval process.
- Updated the `ToggleTenderApproval` method to handle the new error, providing clearer responses for sync issues.
- Introduced a method to reopen terminal submissions when tender approvals are resubmitted, enhancing submission state management.
- Implemented optimistic locking in the repository to prevent concurrent updates, improving data integrity.
- Added unit tests for new submission handling logic, ensuring robust functionality and error management.

This update strengthens the tender approval workflow and submission handling, ensuring better error reporting and state management.
This commit is contained in:
Mazyar
2026-07-12 21:41:53 +03:30
parent 0cce9ef1b5
commit fa3d466579
9 changed files with 436 additions and 21 deletions
+26 -9
View File
@@ -98,25 +98,31 @@ func (h *Handler) EnsureSubmission(c echo.Context) error {
return response.Success(c, result, "Tender submission ensured successfully")
}
// GetSubmissionByID retrieves a tender submission by ID.
// GetSubmissionByID retrieves a tender submission by ID for the authenticated company.
// @Summary Get tender submission by ID
// @Description Retrieve a tender submission workflow with tender details
// @Description Retrieve a tender submission workflow with tender details for the authenticated company
// @Tags TenderSubmissions
// @Accept json
// @Produce json
// @Param id path string true "Submission ID"
// @Success 200 {object} response.APIResponse{data=TenderSubmissionWithTenderResponse}
// @Failure 401 {object} response.APIResponse
// @Failure 404 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security BearerAuth
// @Router /api/v1/tender-submissions/{id} [get]
func (h *Handler) GetSubmissionByID(c echo.Context) error {
companyID, ok := c.Get("company_id").(string)
if !ok || companyID == "" {
return response.Unauthorized(c, "Unauthorized")
}
id := strings.TrimSpace(c.Param("id"))
if id == "" {
return response.BadRequest(c, "Submission ID is required", "")
}
result, err := h.service.GetByIDWithTender(c.Request().Context(), id)
result, err := h.service.GetByIDForCompanyWithTender(c.Request().Context(), id, companyID)
if err != nil {
if errors.Is(err, ErrTenderSubmissionNotFound) {
return response.NotFound(c, "Tender submission not found")
@@ -124,11 +130,6 @@ func (h *Handler) GetSubmissionByID(c echo.Context) error {
return response.InternalServerError(c, "Failed to get tender submission")
}
companyID, _ := c.Get("company_id").(string)
if companyID != "" && result.CompanyID != companyID {
return response.NotFound(c, "Tender submission not found")
}
return response.Success(c, result, "Tender submission retrieved successfully")
}
@@ -213,6 +214,9 @@ func (h *Handler) UpdateSubmissionStatus(c echo.Context) error {
if errors.Is(err, ErrTerminalStatus) {
return response.ValidationError(c, "Submission is in a terminal state", err.Error())
}
if errors.Is(err, ErrConcurrentUpdate) {
return response.Conflict(c, "Tender submission was modified concurrently")
}
return response.InternalServerError(c, "Failed to update tender submission status")
}
@@ -287,7 +291,20 @@ func (h *Handler) AdminListSubmissions(c echo.Context) error {
// @Security BearerAuth
// @Router /admin/v1/tender-submissions/{id} [get]
func (h *Handler) AdminGetSubmissionByID(c echo.Context) error {
return h.GetSubmissionByID(c)
id := strings.TrimSpace(c.Param("id"))
if id == "" {
return response.BadRequest(c, "Submission ID is required", "")
}
result, err := h.service.GetByIDWithTender(c.Request().Context(), id)
if err != nil {
if errors.Is(err, ErrTenderSubmissionNotFound) {
return response.NotFound(c, "Tender submission not found")
}
return response.InternalServerError(c, "Failed to get tender submission")
}
return response.Success(c, result, "Tender submission retrieved successfully")
}
// AdminGetSubmissionStats returns global submission statistics.