From c9654a387a2a447d022393af84a16cdadfff41e7 Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 30 Sep 2026 22:59:44 +0700 Subject: [PATCH 1/2] fix(migrations): keep every payment method type in use Production allows edc and delivery payment methods through a payment_methods_type_check changed outside the migrations, and has rows of both. 000094 rewrote the constraint with only cash, card, digital_wallet and point, so it failed on production (in its transaction, leaving the database dirty at 94 with nothing applied). 000094 now keeps edc and delivery, down included, and also allows qr, which the code accepts but no constraint did. 000098 sets the same list where the old 000094 already ran, as on staging. Co-Authored-By: Claude Opus 5.5 --- migrations/000094_add_point_payment_method.down.sql | 2 +- migrations/000094_add_point_payment_method.up.sql | 5 +++-- .../000098_allow_edc_delivery_payment_methods.down.sql | 2 ++ .../000098_allow_edc_delivery_payment_methods.up.sql | 7 +++++++ 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 migrations/000098_allow_edc_delivery_payment_methods.down.sql create mode 100644 migrations/000098_allow_edc_delivery_payment_methods.up.sql diff --git a/migrations/000094_add_point_payment_method.down.sql b/migrations/000094_add_point_payment_method.down.sql index dd86218..4bffcee 100644 --- a/migrations/000094_add_point_payment_method.down.sql +++ b/migrations/000094_add_point_payment_method.down.sql @@ -13,4 +13,4 @@ DROP INDEX IF EXISTS uq_payment_methods_point_per_organization; ALTER TABLE payment_methods DROP CONSTRAINT IF EXISTS payment_methods_type_check; ALTER TABLE payment_methods ADD CONSTRAINT payment_methods_type_check - CHECK (type IN ('cash', 'card', 'digital_wallet')); + CHECK (type IN ('cash', 'card', 'digital_wallet', 'qr', 'edc', 'delivery')); diff --git a/migrations/000094_add_point_payment_method.up.sql b/migrations/000094_add_point_payment_method.up.sql index e39899e..eab3509 100644 --- a/migrations/000094_add_point_payment_method.up.sql +++ b/migrations/000094_add_point_payment_method.up.sql @@ -1,10 +1,11 @@ -- Paying with EnakPoint (docs/prd-point-coin.md F9, §8, §10.5). --- A new payment method type. Every organization has exactly one method of it, made by +-- A new payment method type, next to the existing ones (edc and delivery were added to +-- the constraint outside the migrations). Every organization has exactly one method of it, made by -- the system, which cannot be deleted or change type. ALTER TABLE payment_methods DROP CONSTRAINT IF EXISTS payment_methods_type_check; ALTER TABLE payment_methods ADD CONSTRAINT payment_methods_type_check - CHECK (type IN ('cash', 'card', 'digital_wallet', 'point')); + CHECK (type IN ('cash', 'card', 'digital_wallet', 'qr', 'edc', 'delivery', 'point')); CREATE UNIQUE INDEX uq_payment_methods_point_per_organization ON payment_methods(organization_id) WHERE type = 'point'; diff --git a/migrations/000098_allow_edc_delivery_payment_methods.down.sql b/migrations/000098_allow_edc_delivery_payment_methods.down.sql new file mode 100644 index 0000000..d3eedb5 --- /dev/null +++ b/migrations/000098_allow_edc_delivery_payment_methods.down.sql @@ -0,0 +1,2 @@ +-- Nothing to undo: 000094 now allows the same types. +SELECT 1; diff --git a/migrations/000098_allow_edc_delivery_payment_methods.up.sql b/migrations/000098_allow_edc_delivery_payment_methods.up.sql new file mode 100644 index 0000000..c831678 --- /dev/null +++ b/migrations/000098_allow_edc_delivery_payment_methods.up.sql @@ -0,0 +1,7 @@ +-- Production also has edc and delivery payment methods, allowed by a constraint changed +-- outside the migrations, and the code also accepts qr, which no constraint allowed. +-- 000094 first rewrote the constraint without them, which +-- failed on production and, where it ran, dropped them. Keep every type in use. +ALTER TABLE payment_methods DROP CONSTRAINT IF EXISTS payment_methods_type_check; +ALTER TABLE payment_methods ADD CONSTRAINT payment_methods_type_check + CHECK (type IN ('cash', 'card', 'digital_wallet', 'qr', 'edc', 'delivery', 'point')); -- 2.54.0 From 892575202b26afd3e5f01d5e9ca205425ec005e2 Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 30 Sep 2026 23:29:17 +0700 Subject: [PATCH 2/2] fix(orders): keep the customer a new order is created for CreateOrderContractToModel never copied customer_id, so every order from POST /orders was saved without a customer. Paying it then earned no EnakPoint or EnakCoin (skipped as NO_CUSTOMER, which is not logged). The customer must now belong to the order's organization, as SetOrderCustomer already requires: it is who earns once the order is paid, and orders.customer_id has no foreign key. A nil UUID means no customer. Co-Authored-By: Claude Opus 5.5 --- internal/processor/order_processor.go | 14 +++++++++++++- internal/transformer/order_transformer.go | 1 + internal/transformer/order_transformer_test.go | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/internal/processor/order_processor.go b/internal/processor/order_processor.go index bea796c..60ad0f4 100644 --- a/internal/processor/order_processor.go +++ b/internal/processor/order_processor.go @@ -316,6 +316,18 @@ func resolveLineQuantity(product *entities.Product, quantity int, weight *float6 } func (p *OrderProcessorImpl) CreateOrder(ctx context.Context, req *models.CreateOrderRequest, organizationID uuid.UUID) (*models.OrderResponse, error) { + // The order's customer earns EnakPoint and EnakCoin once it is paid, so it must be + // one of the organization's own; orders.customer_id has no foreign key to catch it. + customerID := req.CustomerID + if customerID != nil && *customerID == uuid.Nil { + customerID = nil + } + if customerID != nil { + if _, err := p.customerRepo.GetByIDAndOrganization(ctx, *customerID, organizationID); err != nil { + return nil, fmt.Errorf("customer not found or does not belong to the organization: %w", err) + } + } + orderNumber, err := p.orderRepo.GetNextOrderNumber(ctx, organizationID, req.OutletID) if err != nil { return nil, fmt.Errorf("failed to generate order number: %w", err) @@ -401,7 +413,7 @@ func (p *OrderProcessorImpl) CreateOrder(ctx context.Context, req *models.Create OrganizationID: organizationID, OutletID: req.OutletID, UserID: req.UserID, - CustomerID: req.CustomerID, + CustomerID: customerID, OrderNumber: orderNumber, TableNumber: req.TableNumber, OrderType: entities.OrderType(req.OrderType), diff --git a/internal/transformer/order_transformer.go b/internal/transformer/order_transformer.go index 5bd19ba..2ed1194 100644 --- a/internal/transformer/order_transformer.go +++ b/internal/transformer/order_transformer.go @@ -27,6 +27,7 @@ func CreateOrderContractToModel(req *contract.CreateOrderRequest) *models.Create return &models.CreateOrderRequest{ OutletID: req.OutletID, UserID: req.UserID, + CustomerID: req.CustomerID, TableID: req.TableID, TableNumber: req.TableNumber, OrderType: constants.OrderType(req.OrderType), diff --git a/internal/transformer/order_transformer_test.go b/internal/transformer/order_transformer_test.go index 5c55afd..4c6fcde 100644 --- a/internal/transformer/order_transformer_test.go +++ b/internal/transformer/order_transformer_test.go @@ -32,6 +32,20 @@ func TestCreateOrderContractToModelCarriesWeight(t *testing.T) { require.Equal(t, weight, *result.OrderItems[0].Weight) } +func TestCreateOrderContractToModelCarriesCustomer(t *testing.T) { + customerID := uuid.New() + + result := CreateOrderContractToModel(&contract.CreateOrderRequest{ + OutletID: uuid.New(), + UserID: uuid.New(), + CustomerID: &customerID, + OrderItems: []contract.CreateOrderItemRequest{{ProductID: uuid.New(), Quantity: 1}}, + }) + + require.NotNil(t, result.CustomerID, "the order's customer is who earns EnakPoint and EnakCoin") + require.Equal(t, customerID, *result.CustomerID) +} + func TestAddToOrderContractToModelCarriesWeight(t *testing.T) { weight := 0.8 -- 2.54.0