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:
@@ -11,12 +11,15 @@ import (
|
||||
mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
|
||||
)
|
||||
|
||||
const maxListLimit = 100
|
||||
|
||||
// Repository defines data access for tender submissions.
|
||||
type Repository interface {
|
||||
Create(ctx context.Context, submission *TenderSubmission) error
|
||||
GetByID(ctx context.Context, id string) (*TenderSubmission, error)
|
||||
GetByTenderAndCompany(ctx context.Context, tenderID, companyID string) (*TenderSubmission, error)
|
||||
Update(ctx context.Context, submission *TenderSubmission) error
|
||||
UpdateIfUpdatedAt(ctx context.Context, submission *TenderSubmission, expectedUpdatedAt int64) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
ListByCompany(ctx context.Context, companyID string, statuses []SubmissionStatus, stages []SubmissionStage, from, to *int64, limit, offset int, sortBy, sortOrder string) ([]*TenderSubmission, int64, error)
|
||||
Search(ctx context.Context, tenderID, companyID *string, statuses []SubmissionStatus, stages []SubmissionStage, from, to *int64, limit, offset int, sortBy, sortOrder string) ([]*TenderSubmission, int64, error)
|
||||
@@ -25,8 +28,9 @@ type Repository interface {
|
||||
}
|
||||
|
||||
type tenderSubmissionRepository struct {
|
||||
ormRepo mongo.Repository[TenderSubmission]
|
||||
logger logger.Logger
|
||||
ormRepo mongo.Repository[TenderSubmission]
|
||||
collection *mongodriver.Collection
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository {
|
||||
@@ -46,8 +50,9 @@ func NewRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger)
|
||||
})
|
||||
}
|
||||
|
||||
ormRepo := mongo.NewRepository[TenderSubmission](mongoManager.GetCollection("tender_submissions"), logger)
|
||||
return &tenderSubmissionRepository{ormRepo: ormRepo, logger: logger}
|
||||
collection := mongoManager.GetCollection("tender_submissions")
|
||||
ormRepo := mongo.NewRepository[TenderSubmission](collection, logger)
|
||||
return &tenderSubmissionRepository{ormRepo: ormRepo, collection: collection, logger: logger}
|
||||
}
|
||||
|
||||
func (r *tenderSubmissionRepository) Create(ctx context.Context, submission *TenderSubmission) error {
|
||||
@@ -56,6 +61,9 @@ func (r *tenderSubmissionRepository) Create(ctx context.Context, submission *Ten
|
||||
submission.SetUpdatedAt(now)
|
||||
|
||||
if err := r.ormRepo.Create(ctx, submission); err != nil {
|
||||
if mongo.IsDuplicateKeyError(err) {
|
||||
return ErrTenderSubmissionExists
|
||||
}
|
||||
r.logger.Error("Failed to create tender submission", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tender_id": submission.TenderID,
|
||||
@@ -108,6 +116,9 @@ func (r *tenderSubmissionRepository) GetByTenderAndCompany(ctx context.Context,
|
||||
func (r *tenderSubmissionRepository) Update(ctx context.Context, submission *TenderSubmission) error {
|
||||
submission.SetUpdatedAt(time.Now().Unix())
|
||||
if err := r.ormRepo.Update(ctx, submission); err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
return ErrTenderSubmissionNotFound
|
||||
}
|
||||
r.logger.Error("Failed to update tender submission", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"submission_id": submission.GetID(),
|
||||
@@ -117,8 +128,35 @@ func (r *tenderSubmissionRepository) Update(ctx context.Context, submission *Ten
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *tenderSubmissionRepository) UpdateIfUpdatedAt(ctx context.Context, submission *TenderSubmission, expectedUpdatedAt int64) error {
|
||||
objectID, err := bson.ObjectIDFromHex(submission.GetID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newUpdatedAt := time.Now().Unix()
|
||||
submission.SetUpdatedAt(newUpdatedAt)
|
||||
|
||||
filter := bson.M{"_id": objectID, "updated_at": expectedUpdatedAt}
|
||||
result, err := r.collection.UpdateOne(ctx, filter, bson.M{"$set": submission})
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to update tender submission with optimistic lock", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"submission_id": submission.GetID(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
if result.MatchedCount == 0 {
|
||||
return ErrConcurrentUpdate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *tenderSubmissionRepository) Delete(ctx context.Context, id string) error {
|
||||
if err := r.ormRepo.Delete(ctx, id); err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
return ErrTenderSubmissionNotFound
|
||||
}
|
||||
r.logger.Error("Failed to delete tender submission", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"submission_id": id,
|
||||
@@ -134,6 +172,11 @@ func (r *tenderSubmissionRepository) ListByCompany(ctx context.Context, companyI
|
||||
}
|
||||
|
||||
func (r *tenderSubmissionRepository) Search(ctx context.Context, tenderID, companyID *string, statuses []SubmissionStatus, stages []SubmissionStage, from, to *int64, limit, offset int, sortBy, sortOrder string) ([]*TenderSubmission, int64, error) {
|
||||
limit = capListLimit(limit)
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
filter := bson.M{}
|
||||
if tenderID != nil && *tenderID != "" {
|
||||
filter["tender_id"] = *tenderID
|
||||
@@ -291,3 +334,13 @@ func int64Value(v interface{}) int64 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func capListLimit(limit int) int {
|
||||
if limit <= 0 {
|
||||
return 20
|
||||
}
|
||||
if limit > maxListLimit {
|
||||
return maxListLimit
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user