From 16d1358b3a85615ffe65f7b1d1a8070d927c4eb6 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Wed, 10 Dec 2025 11:53:21 +0700 Subject: [PATCH] FIX[BE}: really fixed duplicate SO number --- ...0044651_create_so_number_sequence.down.sql | 3 + ...210044651_create_so_number_sequence.up.sql | 12 +++ .../repositories/salesorder.repository.go | 79 ++----------------- 3 files changed, 21 insertions(+), 73 deletions(-) create mode 100644 internal/database/migrations/20251210044651_create_so_number_sequence.down.sql create mode 100644 internal/database/migrations/20251210044651_create_so_number_sequence.up.sql diff --git a/internal/database/migrations/20251210044651_create_so_number_sequence.down.sql b/internal/database/migrations/20251210044651_create_so_number_sequence.down.sql new file mode 100644 index 00000000..4d80dd2c --- /dev/null +++ b/internal/database/migrations/20251210044651_create_so_number_sequence.down.sql @@ -0,0 +1,3 @@ +-- Drop function and sequence for sales order numbers +DROP FUNCTION IF EXISTS generate_so_number(); +DROP SEQUENCE IF EXISTS so_number_seq; diff --git a/internal/database/migrations/20251210044651_create_so_number_sequence.up.sql b/internal/database/migrations/20251210044651_create_so_number_sequence.up.sql new file mode 100644 index 00000000..833a8323 --- /dev/null +++ b/internal/database/migrations/20251210044651_create_so_number_sequence.up.sql @@ -0,0 +1,12 @@ +-- Create sequence for sales order numbers +CREATE SEQUENCE so_number_seq START WITH 1 INCREMENT BY 1; + +CREATE OR REPLACE FUNCTION generate_so_number() +RETURNS VARCHAR AS $$ +DECLARE + next_val INTEGER; +BEGIN + next_val := nextval('so_number_seq'); + RETURN 'SO-' || LPAD(next_val::TEXT, 5, '0'); +END; +$$ LANGUAGE plpgsql; diff --git a/internal/modules/marketing/repositories/salesorder.repository.go b/internal/modules/marketing/repositories/salesorder.repository.go index ed33d194..51351e55 100644 --- a/internal/modules/marketing/repositories/salesorder.repository.go +++ b/internal/modules/marketing/repositories/salesorder.repository.go @@ -3,14 +3,10 @@ package repository import ( "context" "fmt" - "strconv" - "strings" "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" - "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" - "gorm.io/gorm/clause" ) type MarketingRepository interface { @@ -43,82 +39,19 @@ func (r *MarketingRepositoryImpl) GetNextSequence(ctx context.Context) (uint, er } func (r *MarketingRepositoryImpl) NextSoNumber(ctx context.Context, tx *gorm.DB) (string, error) { - return r.generateSequentialNumber(ctx, tx, "so_number", utils.MarketingSoNumberPrefix, utils.MarketingNumberPadding) -} - -func parseNumericSuffix(value, prefix string) (int, bool) { - if !strings.HasPrefix(value, prefix) { - return 0, false - } - suffix := strings.TrimPrefix(value, prefix) - if suffix == "" { - return 0, false - } - trimmed := strings.TrimLeft(suffix, "0") - if trimmed == "" { - trimmed = "0" - } - number, err := strconv.Atoi(trimmed) - if err != nil { - return 0, false - } - return number, true -} - -func (r *MarketingRepositoryImpl) numberExists(ctx context.Context, db *gorm.DB, column, value string) (bool, error) { - var count int64 - if err := db.WithContext(ctx). - Model(&entity.Marketing{}). - Where(fmt.Sprintf("%s = ?", column), value). - Where("deleted_at IS NULL"). - Count(&count).Error; err != nil { - return false, err - } - return count > 0, nil -} - -func (r *MarketingRepositoryImpl) generateSequentialNumber(ctx context.Context, tx *gorm.DB, column, prefix string, padding int) (string, error) { - db := tx if db == nil { db = r.DB() } - var values []string + var soNumber string err := db.WithContext(ctx). - Model(&entity.Marketing{}). - Where(fmt.Sprintf("%s LIKE ?", column), prefix+"%"). - Where("deleted_at IS NULL"). - Select(column). - Order(fmt.Sprintf("%s DESC", column)). - Limit(20). - Clauses(clause.Locking{Strength: "UPDATE"}). - Pluck(column, &values).Error + Raw("SELECT generate_so_number()"). + Scan(&soNumber).Error + if err != nil { - return "", err + return "", fmt.Errorf("failed to generate SO number: %w", err) } - next := 1 - for _, value := range values { - if number, ok := parseNumericSuffix(value, prefix); ok { - next = number + 1 - break - } - } - - const maxAttempts = 20 - for attempt := 0; attempt < maxAttempts; attempt++ { - candidate := fmt.Sprintf("%s%0*d", prefix, padding, next) - exists, err := r.numberExists(ctx, db, column, candidate) - if err != nil { - return "", err - } - if !exists { - return candidate, nil - } - next++ - } - - return "", fmt.Errorf("unable to generate unique %s", column) - + return soNumber, nil }