Add tender submission functionality and related routes
- Introduced the `tender_submission` domain, including entity, repository, service, and handler implementations for managing tender submissions. - Added new routes for both admin and public access to tender submissions, allowing for listing, ensuring, and retrieving submissions by ID and tender. - Enhanced the tender approval service to synchronize submission workflows with approval changes, ensuring proper state management. - Implemented validation and response structures for tender submission operations, improving API consistency and usability. - Added unit tests for tender submission status transitions and workflow logic, ensuring robust functionality. This update enhances the tender management system by providing comprehensive support for tender submissions, improving overall workflow and user experience.
This commit is contained in:
@@ -60,7 +60,8 @@ func (h *Handler) ToggleTenderApproval(c echo.Context) error {
|
||||
return response.BadRequest(c, "Company ID is required", "")
|
||||
}
|
||||
|
||||
tenderApproval, err := h.service.ToggleTenderApproval(c.Request().Context(), form, companyID)
|
||||
customerID, _ := customer.GetCustomerIDFromContext(c)
|
||||
tenderApproval, err := h.service.ToggleTenderApproval(c.Request().Context(), form, companyID, customerID)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrTenderNotFound) {
|
||||
return response.NotFound(c, "Tender not found")
|
||||
|
||||
@@ -11,6 +11,13 @@ import (
|
||||
"tm/pkg/response"
|
||||
)
|
||||
|
||||
// SubmissionLifecycleHook synchronizes tender submission workflow with approval changes.
|
||||
type SubmissionLifecycleHook interface {
|
||||
OnApprovalSubmitted(ctx context.Context, tenderID, companyID, customerID string) error
|
||||
OnApprovalRemoved(ctx context.Context, tenderID, companyID string) error
|
||||
OnApprovalRejected(ctx context.Context, tenderID, companyID, changedBy string) error
|
||||
}
|
||||
|
||||
// Service defines business logic for tender approval operations
|
||||
type Service interface {
|
||||
// Core tender approval operations
|
||||
@@ -34,7 +41,7 @@ type Service interface {
|
||||
SearchTenderApprovalsByCriteria(ctx context.Context, criteria *TenderApprovalSearchCriteria) ([]*TenderApproval, error)
|
||||
|
||||
// Business operations
|
||||
ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error)
|
||||
ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID, customerID string) (*TenderApprovalWithTenderResponse, error)
|
||||
|
||||
// Statistics and analytics
|
||||
GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error)
|
||||
@@ -51,17 +58,19 @@ type Service interface {
|
||||
|
||||
// tenderApprovalService implements the Service interface
|
||||
type tenderApprovalService struct {
|
||||
repository Repository
|
||||
tenderService tender.Service
|
||||
logger logger.Logger
|
||||
repository Repository
|
||||
tenderService tender.Service
|
||||
logger logger.Logger
|
||||
submissionHook SubmissionLifecycleHook
|
||||
}
|
||||
|
||||
// NewService creates a new tender approval service
|
||||
func NewService(repository Repository, tenderService tender.Service, logger logger.Logger) Service {
|
||||
func NewService(repository Repository, tenderService tender.Service, logger logger.Logger, submissionHook SubmissionLifecycleHook) Service {
|
||||
return &tenderApprovalService{
|
||||
repository: repository,
|
||||
tenderService: tenderService,
|
||||
logger: logger,
|
||||
repository: repository,
|
||||
tenderService: tenderService,
|
||||
logger: logger,
|
||||
submissionHook: submissionHook,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,7 +862,7 @@ func (s *tenderApprovalService) ListTenderApprovalsWithTender(ctx context.Contex
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error) {
|
||||
func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID, customerID string) (*TenderApprovalWithTenderResponse, error) {
|
||||
companyID = strings.TrimSpace(companyID)
|
||||
if companyID == "" {
|
||||
return nil, ErrInvalidCompanyID
|
||||
@@ -899,6 +908,7 @@ func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *T
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.syncSubmissionAfterToggle(ctx, tenderID, companyID, customerID, req.Status, true)
|
||||
return s.toggleResponseWithTender(ctx, approval)
|
||||
}
|
||||
|
||||
@@ -917,6 +927,7 @@ func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *T
|
||||
existing.Status = "unrejected"
|
||||
}
|
||||
|
||||
s.syncSubmissionAfterToggle(ctx, tenderID, companyID, customerID, req.Status, false)
|
||||
return existing.ToResponseWithTender(nil), nil
|
||||
}
|
||||
|
||||
@@ -939,9 +950,35 @@ func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *T
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.syncSubmissionAfterToggle(ctx, tenderID, companyID, customerID, req.Status, true)
|
||||
return s.toggleResponseWithTender(ctx, existing)
|
||||
}
|
||||
|
||||
func (s *tenderApprovalService) syncSubmissionAfterToggle(ctx context.Context, tenderID, companyID, customerID string, status ApprovalStatus, active bool) {
|
||||
if s.submissionHook == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
if !active {
|
||||
err = s.submissionHook.OnApprovalRemoved(ctx, tenderID, companyID)
|
||||
} else if status == ApprovalStatusSubmitted {
|
||||
err = s.submissionHook.OnApprovalSubmitted(ctx, tenderID, companyID, customerID)
|
||||
} else if status == ApprovalStatusRejected {
|
||||
err = s.submissionHook.OnApprovalRejected(ctx, tenderID, companyID, customerID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to sync tender submission workflow after approval toggle", map[string]interface{}{
|
||||
"tender_id": tenderID,
|
||||
"company_id": companyID,
|
||||
"status": status,
|
||||
"active": active,
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tenderApprovalService) toggleResponseWithTender(ctx context.Context, approval *TenderApproval) (*TenderApprovalWithTenderResponse, error) {
|
||||
if approval == nil {
|
||||
return nil, ErrInvalidData
|
||||
|
||||
Reference in New Issue
Block a user