From 23ac572e3f809bd05972553ac1c5ac08a9344368 Mon Sep 17 00:00:00 2001 From: Efril Date: Thu, 28 May 2026 13:49:57 +0700 Subject: [PATCH 1/2] add print_to_checker at product outlet --- internal/contract/product_contract.go | 29 +++--- .../contract/product_outlet_price_contract.go | 30 ++++--- internal/entities/product_outlet_price.go | 13 +-- .../mappers/product_outlet_price_mapper.go | 26 +++--- internal/models/product.go | 10 ++- internal/models/product_outlet_price.go | 23 ++--- .../product_outlet_price_processor.go | 7 +- internal/processor/product_processor.go | 88 +++++++++++++++---- .../product_outlet_price_repository.go | 17 ++-- .../service/product_outlet_price_service.go | 7 +- .../product_outlet_price_transformer.go | 23 ++--- internal/transformer/product_transformer.go | 32 ++++--- ..._checker_to_product_outlet_prices.down.sql | 1 + ...to_checker_to_product_outlet_prices.up.sql | 1 + 14 files changed, 199 insertions(+), 108 deletions(-) create mode 100644 migrations/000070_add_print_to_checker_to_product_outlet_prices.down.sql create mode 100644 migrations/000070_add_print_to_checker_to_product_outlet_prices.up.sql diff --git a/internal/contract/product_contract.go b/internal/contract/product_contract.go index 1fec481..61c9e90 100644 --- a/internal/contract/product_contract.go +++ b/internal/contract/product_contract.go @@ -17,6 +17,7 @@ type CreateProductRequest struct { BusinessType *string `json:"business_type,omitempty"` ImageURL *string `json:"image_url,omitempty" validate:"omitempty,max=500"` PrinterType *string `json:"printer_type,omitempty" validate:"omitempty,max=50"` + PrintToChecker *bool `json:"print_to_checker,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` IsActive *bool `json:"is_active,omitempty"` Variants []CreateProductVariantRequest `json:"variants,omitempty"` @@ -26,19 +27,20 @@ type CreateProductRequest struct { } type UpdateProductRequest struct { - OutletID *uuid.UUID `json:"outlet_id,omitempty"` - CategoryID *uuid.UUID `json:"category_id,omitempty"` - SKU *string `json:"sku,omitempty"` - Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"` - Description *string `json:"description,omitempty"` - Price *float64 `json:"price,omitempty" validate:"omitempty,min=0"` - Cost *float64 `json:"cost,omitempty" validate:"omitempty,min=0"` - BusinessType *string `json:"business_type,omitempty"` - ImageURL *string `json:"image_url,omitempty" validate:"omitempty,max=500"` - PrinterType *string `json:"printer_type,omitempty" validate:"omitempty,max=50"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - IsActive *bool `json:"is_active,omitempty"` - ReorderLevel *int `json:"reorder_level,omitempty" validate:"omitempty,min=0"` + OutletID *uuid.UUID `json:"outlet_id,omitempty"` + CategoryID *uuid.UUID `json:"category_id,omitempty"` + SKU *string `json:"sku,omitempty"` + Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"` + Description *string `json:"description,omitempty"` + Price *float64 `json:"price,omitempty" validate:"omitempty,min=0"` + Cost *float64 `json:"cost,omitempty" validate:"omitempty,min=0"` + BusinessType *string `json:"business_type,omitempty"` + ImageURL *string `json:"image_url,omitempty" validate:"omitempty,max=500"` + PrinterType *string `json:"printer_type,omitempty" validate:"omitempty,max=50"` + PrintToChecker *bool `json:"print_to_checker,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + IsActive *bool `json:"is_active,omitempty"` + ReorderLevel *int `json:"reorder_level,omitempty" validate:"omitempty,min=0"` } type CreateProductVariantRequest struct { @@ -71,6 +73,7 @@ type ProductResponse struct { BusinessType string `json:"business_type"` ImageURL *string `json:"image_url"` PrinterType string `json:"printer_type"` + PrintToChecker bool `json:"print_to_checker"` Metadata map[string]interface{} `json:"metadata"` IsActive bool `json:"is_active"` CreatedAt time.Time `json:"created_at"` diff --git a/internal/contract/product_outlet_price_contract.go b/internal/contract/product_outlet_price_contract.go index ed66de7..0e9b189 100644 --- a/internal/contract/product_outlet_price_contract.go +++ b/internal/contract/product_outlet_price_contract.go @@ -7,23 +7,26 @@ import ( ) type CreateProductOutletPriceRequest struct { - ProductID uuid.UUID `json:"product_id" validate:"required"` - OutletID uuid.UUID `json:"outlet_id" validate:"required"` - Price float64 `json:"price" validate:"required,min=0"` + ProductID uuid.UUID `json:"product_id" validate:"required"` + OutletID uuid.UUID `json:"outlet_id" validate:"required"` + Price float64 `json:"price" validate:"required,min=0"` + PrintToChecker bool `json:"print_to_checker"` } type UpdateProductOutletPriceRequest struct { - Price float64 `json:"price" validate:"required,min=0"` + Price float64 `json:"price" validate:"required,min=0"` + PrintToChecker *bool `json:"print_to_checker"` } type ProductOutletPriceResponse struct { - ID uuid.UUID `json:"id,omitempty"` - ProductID uuid.UUID `json:"product_id,omitempty"` - OutletID uuid.UUID `json:"outlet_id"` - OutletName string `json:"outlet_name,omitempty"` - Price float64 `json:"price"` - CreatedAt time.Time `json:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty"` + ID uuid.UUID `json:"id,omitempty"` + ProductID uuid.UUID `json:"product_id,omitempty"` + OutletID uuid.UUID `json:"outlet_id"` + OutletName string `json:"outlet_name,omitempty"` + Price float64 `json:"price"` + PrintToChecker bool `json:"print_to_checker"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` } type ListProductOutletPricesResponse struct { @@ -37,6 +40,7 @@ type BulkCreateProductOutletPriceRequest struct { } type CreateProductOutletPricePerOutletRequest struct { - OutletID uuid.UUID `json:"outlet_id" validate:"required"` - Price float64 `json:"price" validate:"required,min=0"` + OutletID uuid.UUID `json:"outlet_id" validate:"required"` + Price float64 `json:"price" validate:"required,min=0"` + PrintToChecker bool `json:"print_to_checker"` } diff --git a/internal/entities/product_outlet_price.go b/internal/entities/product_outlet_price.go index 16f623a..0cb39bf 100644 --- a/internal/entities/product_outlet_price.go +++ b/internal/entities/product_outlet_price.go @@ -8,12 +8,13 @@ import ( ) type ProductOutletPrice struct { - ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` - ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id"` - OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id"` - Price float64 `gorm:"type:decimal(10,2);not null" json:"price"` - CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` - UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` + ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id"` + OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id"` + Price float64 `gorm:"type:decimal(10,2);not null" json:"price"` + PrintToChecker bool `gorm:"not null;default:true" json:"print_to_checker"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` Product Product `gorm:"foreignKey:ProductID" json:"product,omitempty"` Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"` diff --git a/internal/mappers/product_outlet_price_mapper.go b/internal/mappers/product_outlet_price_mapper.go index 12556be..b7e181a 100644 --- a/internal/mappers/product_outlet_price_mapper.go +++ b/internal/mappers/product_outlet_price_mapper.go @@ -11,12 +11,13 @@ func ProductOutletPriceEntityToModel(entity *entities.ProductOutletPrice) *model } return &models.ProductOutletPrice{ - ID: entity.ID, - ProductID: entity.ProductID, - OutletID: entity.OutletID, - Price: entity.Price, - CreatedAt: entity.CreatedAt, - UpdatedAt: entity.UpdatedAt, + ID: entity.ID, + ProductID: entity.ProductID, + OutletID: entity.OutletID, + Price: entity.Price, + PrintToChecker: entity.PrintToChecker, + CreatedAt: entity.CreatedAt, + UpdatedAt: entity.UpdatedAt, } } @@ -26,12 +27,13 @@ func ProductOutletPriceModelToEntity(model *models.ProductOutletPrice) *entities } return &entities.ProductOutletPrice{ - ID: model.ID, - ProductID: model.ProductID, - OutletID: model.OutletID, - Price: model.Price, - CreatedAt: model.CreatedAt, - UpdatedAt: model.UpdatedAt, + ID: model.ID, + ProductID: model.ProductID, + OutletID: model.OutletID, + Price: model.Price, + PrintToChecker: model.PrintToChecker, + CreatedAt: model.CreatedAt, + UpdatedAt: model.UpdatedAt, } } diff --git a/internal/models/product.go b/internal/models/product.go index 77e6b6e..0e0b6a1 100644 --- a/internal/models/product.go +++ b/internal/models/product.go @@ -50,6 +50,7 @@ type CreateProductRequest struct { BusinessType constants.BusinessType `validate:"required"` ImageURL *string `validate:"omitempty,max=500"` PrinterType *string `validate:"omitempty,max=50"` + PrintToChecker *bool `validate:"omitempty"` UnitID *uuid.UUID `validate:"omitempty"` HasIngredients bool `validate:"omitempty"` Metadata map[string]interface{} @@ -70,6 +71,7 @@ type UpdateProductRequest struct { Cost *float64 `validate:"omitempty,min=0"` ImageURL *string `validate:"omitempty,max=500"` PrinterType *string `validate:"omitempty,max=50"` + PrintToChecker *bool `validate:"omitempty"` UnitID *uuid.UUID `validate:"omitempty"` HasIngredients *bool `validate:"omitempty"` Metadata map[string]interface{} @@ -108,6 +110,7 @@ type ProductResponse struct { BusinessType constants.BusinessType ImageURL *string PrinterType string + PrintToChecker bool UnitID *uuid.UUID HasIngredients bool Metadata map[string]interface{} @@ -118,9 +121,10 @@ type ProductResponse struct { } type OutletPrice struct { - OutletID uuid.UUID - OutletName string - Price float64 + OutletID uuid.UUID + OutletName string + Price float64 + PrintToChecker bool } type ProductVariantResponse struct { diff --git a/internal/models/product_outlet_price.go b/internal/models/product_outlet_price.go index 2a192da..0f6f44c 100644 --- a/internal/models/product_outlet_price.go +++ b/internal/models/product_outlet_price.go @@ -7,22 +7,25 @@ import ( ) type ProductOutletPrice struct { - ID uuid.UUID - ProductID uuid.UUID - OutletID uuid.UUID - Price float64 - CreatedAt time.Time - UpdatedAt time.Time + ID uuid.UUID + ProductID uuid.UUID + OutletID uuid.UUID + Price float64 + PrintToChecker bool + CreatedAt time.Time + UpdatedAt time.Time } type CreateProductOutletPriceRequest struct { - ProductID uuid.UUID `validate:"required"` - OutletID uuid.UUID `validate:"required"` - Price float64 `validate:"required,min=0"` + ProductID uuid.UUID `validate:"required"` + OutletID uuid.UUID `validate:"required"` + Price float64 `validate:"required,min=0"` + PrintToChecker bool } type UpdateProductOutletPriceRequest struct { - Price *float64 `validate:"required,min=0"` + Price *float64 `validate:"required,min=0"` + PrintToChecker *bool } type ProductOutletPriceResponse struct { diff --git a/internal/processor/product_outlet_price_processor.go b/internal/processor/product_outlet_price_processor.go index 97e00e0..6c5ba44 100644 --- a/internal/processor/product_outlet_price_processor.go +++ b/internal/processor/product_outlet_price_processor.go @@ -46,9 +46,10 @@ func (p *ProductOutletPriceProcessorImpl) Upsert(ctx context.Context, req *model } entity := &entities.ProductOutletPrice{ - ProductID: req.ProductID, - OutletID: req.OutletID, - Price: req.Price, + ProductID: req.ProductID, + OutletID: req.OutletID, + Price: req.Price, + PrintToChecker: req.PrintToChecker, } if err := p.repo.Upsert(ctx, entity); err != nil { diff --git a/internal/processor/product_processor.go b/internal/processor/product_processor.go index 6b1593c..f778833 100644 --- a/internal/processor/product_processor.go +++ b/internal/processor/product_processor.go @@ -5,6 +5,7 @@ import ( "fmt" "apskel-pos-be/internal/entities" + "apskel-pos-be/internal/logger" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" "apskel-pos-be/internal/repository" @@ -125,10 +126,15 @@ func (p *ProductProcessorImpl) CreateProduct(ctx context.Context, req *models.Cr // Upsert outlet-specific price if outlet context is present if req.OutletID != uuid.Nil { + printToChecker := true // default + if req.PrintToChecker != nil { + printToChecker = *req.PrintToChecker + } outletPriceEntity := &entities.ProductOutletPrice{ - ProductID: productEntity.ID, - OutletID: req.OutletID, - Price: req.Price, + ProductID: productEntity.ID, + OutletID: req.OutletID, + Price: req.Price, + PrintToChecker: printToChecker, } if err := p.outletPriceRepo.Upsert(ctx, outletPriceEntity); err != nil { return nil, fmt.Errorf("failed to assign outlet price: %w", err) @@ -196,16 +202,39 @@ func (p *ProductProcessorImpl) UpdateProduct(ctx context.Context, id uuid.UUID, } } - // Upsert outlet-specific price if outlet context is present - if req.OutletID != uuid.Nil && req.Price != nil { - outletPriceEntity := &entities.ProductOutletPrice{ - ProductID: id, - OutletID: req.OutletID, - Price: *req.Price, + // Upsert outlet-specific price if outlet context is present and price or print_to_checker is provided + if req.OutletID != uuid.Nil && (req.Price != nil || req.PrintToChecker != nil) { + // Fetch existing outlet price to use as fallback for fields not provided + existing, _ := p.outletPriceRepo.GetByProductAndOutlet(ctx, id, req.OutletID) + + price := float64(0) + if existing != nil { + price = existing.Price } + if req.Price != nil { + price = *req.Price + } + + printToChecker := true // default + if existing != nil { + printToChecker = existing.PrintToChecker + } + if req.PrintToChecker != nil { + printToChecker = *req.PrintToChecker + } + + outletPriceEntity := &entities.ProductOutletPrice{ + ProductID: id, + OutletID: req.OutletID, + Price: price, + PrintToChecker: printToChecker, + } + logger.FromContext(ctx).Infof("ProductProcessor::UpdateProduct -> upserting outlet price: productID=%s outletID=%s price=%f printToChecker=%v", id, req.OutletID, price, printToChecker) if err := p.outletPriceRepo.Upsert(ctx, outletPriceEntity); err != nil { return nil, fmt.Errorf("failed to assign outlet price: %w", err) } + } else { + logger.FromContext(ctx).Infof("ProductProcessor::UpdateProduct -> skipping outlet price upsert: outletID=%s price=%v printToChecker=%v", req.OutletID, req.Price, req.PrintToChecker) } productWithCategory, err := p.productRepo.GetWithCategory(ctx, id) @@ -256,6 +285,7 @@ func (p *ProductProcessorImpl) GetProductByID(ctx context.Context, id uuid.UUID, outletPrice, err := p.outletPriceRepo.GetByProductAndOutlet(ctx, id, outletID) if err == nil { response.OutletPrice = &outletPrice.Price + response.PrintToChecker = outletPrice.PrintToChecker } } else { // No outlet context — return all outlet prices for this product @@ -264,9 +294,10 @@ func (p *ProductProcessorImpl) GetProductByID(ctx context.Context, id uuid.UUID, prices := make([]models.OutletPrice, len(outletPrices)) for i, op := range outletPrices { prices[i] = models.OutletPrice{ - OutletID: op.OutletID, - OutletName: op.Outlet.Name, - Price: op.Price, + OutletID: op.OutletID, + OutletName: op.Outlet.Name, + Price: op.Price, + PrintToChecker: op.PrintToChecker, } } response.OutletPrices = prices @@ -303,10 +334,35 @@ func (p *ProductProcessorImpl) ListProducts(ctx context.Context, filters map[str } responses := make([]models.ProductResponse, len(productEntities)) - for i, entity := range productEntities { - response := mappers.ProductEntityToResponse(entity) - if response != nil { - responses[i] = *response + if outletID != uuid.Nil && len(productEntities) > 0 { + // Bulk-fetch outlet prices to populate OutletPrice and PrintToChecker per product + productIDs := make([]uuid.UUID, len(productEntities)) + for i, e := range productEntities { + productIDs[i] = e.ID + } + outletPrices, opErr := p.outletPriceRepo.GetByProductsAndOutlet(ctx, productIDs, outletID) + priceMap := make(map[uuid.UUID]*entities.ProductOutletPrice) + if opErr == nil { + for _, op := range outletPrices { + priceMap[op.ProductID] = op + } + } + for i, entity := range productEntities { + response := mappers.ProductEntityToResponse(entity) + if response != nil { + if op, ok := priceMap[entity.ID]; ok { + response.OutletPrice = &op.Price + response.PrintToChecker = op.PrintToChecker + } + responses[i] = *response + } + } + } else { + for i, entity := range productEntities { + response := mappers.ProductEntityToResponse(entity) + if response != nil { + responses[i] = *response + } } } diff --git a/internal/repository/product_outlet_price_repository.go b/internal/repository/product_outlet_price_repository.go index 6a0f4aa..5ad693c 100644 --- a/internal/repository/product_outlet_price_repository.go +++ b/internal/repository/product_outlet_price_repository.go @@ -7,7 +7,6 @@ import ( "github.com/google/uuid" "gorm.io/gorm" - "gorm.io/gorm/clause" ) type ProductOutletPriceRepository interface { @@ -53,10 +52,18 @@ func (r *ProductOutletPriceRepositoryImpl) GetByOutlet(ctx context.Context, outl } func (r *ProductOutletPriceRepositoryImpl) Upsert(ctx context.Context, price *entities.ProductOutletPrice) error { - return r.db.WithContext(ctx).Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "product_id"}, {Name: "outlet_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"price", "updated_at"}), - }).Create(price).Error + if price.ID == uuid.Nil { + price.ID = uuid.New() + } + return r.db.WithContext(ctx).Exec(` + INSERT INTO product_outlet_prices (id, product_id, outlet_id, price, print_to_checker, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, NOW(), NOW()) + ON CONFLICT (product_id, outlet_id) + DO UPDATE SET + price = EXCLUDED.price, + print_to_checker = EXCLUDED.print_to_checker, + updated_at = NOW() + `, price.ID, price.ProductID, price.OutletID, price.Price, price.PrintToChecker).Error } func (r *ProductOutletPriceRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error { diff --git a/internal/service/product_outlet_price_service.go b/internal/service/product_outlet_price_service.go index dc35f68..5f14deb 100644 --- a/internal/service/product_outlet_price_service.go +++ b/internal/service/product_outlet_price_service.go @@ -105,9 +105,10 @@ func (s *ProductOutletPriceServiceImpl) BulkUpsert(ctx context.Context, req *con prices := make([]models.CreateProductOutletPriceRequest, len(req.Prices)) for i, p := range req.Prices { prices[i] = models.CreateProductOutletPriceRequest{ - ProductID: req.ProductID, - OutletID: p.OutletID, - Price: p.Price, + ProductID: req.ProductID, + OutletID: p.OutletID, + Price: p.Price, + PrintToChecker: p.PrintToChecker, } } diff --git a/internal/transformer/product_outlet_price_transformer.go b/internal/transformer/product_outlet_price_transformer.go index 593f93e..fd571a7 100644 --- a/internal/transformer/product_outlet_price_transformer.go +++ b/internal/transformer/product_outlet_price_transformer.go @@ -11,9 +11,10 @@ func CreateProductOutletPriceRequestToModel(req *contract.CreateProductOutletPri } return &models.CreateProductOutletPriceRequest{ - ProductID: req.ProductID, - OutletID: req.OutletID, - Price: req.Price, + ProductID: req.ProductID, + OutletID: req.OutletID, + Price: req.Price, + PrintToChecker: req.PrintToChecker, } } @@ -23,7 +24,8 @@ func UpdateProductOutletPriceRequestToModel(req *contract.UpdateProductOutletPri } return &models.UpdateProductOutletPriceRequest{ - Price: &req.Price, + Price: &req.Price, + PrintToChecker: req.PrintToChecker, } } @@ -33,12 +35,13 @@ func ProductOutletPriceModelToResponse(m *models.ProductOutletPrice) *contract.P } return &contract.ProductOutletPriceResponse{ - ID: m.ID, - ProductID: m.ProductID, - OutletID: m.OutletID, - Price: m.Price, - CreatedAt: m.CreatedAt, - UpdatedAt: m.UpdatedAt, + ID: m.ID, + ProductID: m.ProductID, + OutletID: m.OutletID, + Price: m.Price, + PrintToChecker: m.PrintToChecker, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, } } diff --git a/internal/transformer/product_transformer.go b/internal/transformer/product_transformer.go index 47c309f..e6f0321 100644 --- a/internal/transformer/product_transformer.go +++ b/internal/transformer/product_transformer.go @@ -57,6 +57,7 @@ func CreateProductRequestToModel(apctx *appcontext.ContextInfo, req *contract.Cr BusinessType: businessType, ImageURL: req.ImageURL, PrinterType: req.PrinterType, + PrintToChecker: req.PrintToChecker, Metadata: metadata, Variants: variants, } @@ -75,17 +76,18 @@ func UpdateProductRequestToModel(apctx *appcontext.ContextInfo, req *contract.Up } return &models.UpdateProductRequest{ - OutletID: outletID, - CategoryID: req.CategoryID, - SKU: req.SKU, - Name: req.Name, - Description: req.Description, - Price: req.Price, - Cost: req.Cost, - ImageURL: req.ImageURL, - PrinterType: req.PrinterType, - Metadata: metadata, - IsActive: req.IsActive, + OutletID: outletID, + CategoryID: req.CategoryID, + SKU: req.SKU, + Name: req.Name, + Description: req.Description, + Price: req.Price, + Cost: req.Cost, + ImageURL: req.ImageURL, + PrinterType: req.PrinterType, + PrintToChecker: req.PrintToChecker, + Metadata: metadata, + IsActive: req.IsActive, } } @@ -119,9 +121,10 @@ func ProductModelResponseToResponse(prod *models.ProductResponse) *contract.Prod outletPriceResponses = make([]contract.ProductOutletPriceResponse, len(prod.OutletPrices)) for i, op := range prod.OutletPrices { outletPriceResponses[i] = contract.ProductOutletPriceResponse{ - OutletID: op.OutletID, - OutletName: op.OutletName, - Price: op.Price, + OutletID: op.OutletID, + OutletName: op.OutletName, + Price: op.Price, + PrintToChecker: op.PrintToChecker, } } } @@ -141,6 +144,7 @@ func ProductModelResponseToResponse(prod *models.ProductResponse) *contract.Prod BusinessType: string(prod.BusinessType), ImageURL: prod.ImageURL, PrinterType: prod.PrinterType, + PrintToChecker: prod.PrintToChecker, Metadata: prod.Metadata, IsActive: prod.IsActive, CreatedAt: prod.CreatedAt, diff --git a/migrations/000070_add_print_to_checker_to_product_outlet_prices.down.sql b/migrations/000070_add_print_to_checker_to_product_outlet_prices.down.sql new file mode 100644 index 0000000..a35ed5d --- /dev/null +++ b/migrations/000070_add_print_to_checker_to_product_outlet_prices.down.sql @@ -0,0 +1 @@ +ALTER TABLE product_outlet_prices DROP COLUMN IF EXISTS print_to_checker; diff --git a/migrations/000070_add_print_to_checker_to_product_outlet_prices.up.sql b/migrations/000070_add_print_to_checker_to_product_outlet_prices.up.sql new file mode 100644 index 0000000..930a67d --- /dev/null +++ b/migrations/000070_add_print_to_checker_to_product_outlet_prices.up.sql @@ -0,0 +1 @@ +ALTER TABLE product_outlet_prices ADD COLUMN print_to_checker BOOLEAN NOT NULL DEFAULT TRUE; -- 2.47.2 From 84222fc7f463c28ed664c58dc1d4f5b1746b3132 Mon Sep 17 00:00:00 2001 From: Efril Date: Thu, 28 May 2026 15:30:18 +0700 Subject: [PATCH 2/2] update --- internal/contract/order_contract.go | 1 + internal/entities/product.go | 15 ++++++++------- internal/mappers/order_mapper.go | 18 ++++++++++++++---- internal/mappers/order_mapper_test.go | 6 +++--- internal/models/order.go | 1 + internal/processor/order_processor.go | 2 +- internal/repository/order_repository.go | 3 +++ internal/transformer/order_transformer.go | 2 ++ 8 files changed, 33 insertions(+), 15 deletions(-) diff --git a/internal/contract/order_contract.go b/internal/contract/order_contract.go index 0895ff8..237874f 100644 --- a/internal/contract/order_contract.go +++ b/internal/contract/order_contract.go @@ -110,6 +110,7 @@ type OrderItemResponse struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PrinterType string `json:"printer_type"` + PrintToChecker bool `json:"print_to_checker"` PaidQuantity int `json:"paid_quantity"` } diff --git a/internal/entities/product.go b/internal/entities/product.go index 3aba21e..dc8c524 100644 --- a/internal/entities/product.go +++ b/internal/entities/product.go @@ -26,13 +26,14 @@ type Product struct { CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` - Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"` - Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` - Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"` - ProductVariants []ProductVariant `gorm:"foreignKey:ProductID" json:"variants,omitempty"` - ProductRecipes []ProductRecipe `gorm:"foreignKey:ProductID" json:"product_recipes,omitempty"` - Inventory []Inventory `gorm:"foreignKey:ProductID" json:"inventory,omitempty"` - OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"` + Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"` + Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` + Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"` + ProductVariants []ProductVariant `gorm:"foreignKey:ProductID" json:"variants,omitempty"` + ProductRecipes []ProductRecipe `gorm:"foreignKey:ProductID" json:"product_recipes,omitempty"` + Inventory []Inventory `gorm:"foreignKey:ProductID" json:"inventory,omitempty"` + OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"` + ProductOutletPrices []ProductOutletPrice `gorm:"foreignKey:ProductID" json:"product_outlet_prices,omitempty"` } func (p *Product) BeforeCreate(tx *gorm.DB) error { diff --git a/internal/mappers/order_mapper.go b/internal/mappers/order_mapper.go index 4e336d5..b4c84da 100644 --- a/internal/mappers/order_mapper.go +++ b/internal/mappers/order_mapper.go @@ -82,7 +82,7 @@ func OrderEntityToResponse(order *entities.Order) *models.OrderResponse { } for i, item := range order.OrderItems { - resp := OrderItemEntityToResponse(&item) + resp := OrderItemEntityToResponse(&item, order.OutletID) if resp != nil { resp.PaidQuantity = paidQtyByOrderItem[item.ID] response.OrderItems[i] = *resp @@ -101,11 +101,20 @@ func OrderEntityToResponse(order *entities.Order) *models.OrderResponse { return response } -func OrderItemEntityToResponse(item *entities.OrderItem) *models.OrderItemResponse { +func OrderItemEntityToResponse(item *entities.OrderItem, outletID uuid.UUID) *models.OrderItemResponse { if item == nil { return nil } + // Resolve print_to_checker from preloaded outlet prices + printToChecker := true // default + for _, op := range item.Product.ProductOutletPrices { + if op.OutletID == outletID { + printToChecker = op.PrintToChecker + break + } + } + response := &models.OrderItemResponse{ ID: item.ID, OrderID: item.OrderID, @@ -130,6 +139,7 @@ func OrderItemEntityToResponse(item *entities.OrderItem) *models.OrderItemRespon CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, PrinterType: item.Product.PrinterType, + PrintToChecker: printToChecker, } if item.Product.ID != uuid.Nil { @@ -324,14 +334,14 @@ func OrderEntitiesToResponses(orders []*entities.Order) []models.OrderResponse { return responses } -func OrderItemEntitiesToResponses(items []*entities.OrderItem) []models.OrderItemResponse { +func OrderItemEntitiesToResponses(items []*entities.OrderItem, outletID uuid.UUID) []models.OrderItemResponse { if items == nil { return nil } responses := make([]models.OrderItemResponse, len(items)) for i, item := range items { - response := OrderItemEntityToResponse(item) + response := OrderItemEntityToResponse(item, outletID) if response != nil { responses[i] = *response } diff --git a/internal/mappers/order_mapper_test.go b/internal/mappers/order_mapper_test.go index 58131ba..1ed76dd 100644 --- a/internal/mappers/order_mapper_test.go +++ b/internal/mappers/order_mapper_test.go @@ -45,7 +45,7 @@ func TestOrderItemEntityToResponse_WithProductNames(t *testing.T) { } // Act - result := OrderItemEntityToResponse(orderItem) + result := OrderItemEntityToResponse(orderItem, uuid.Nil) // Assert assert.NotNil(t, result) @@ -89,7 +89,7 @@ func TestOrderItemEntityToResponse_WithoutProductVariant(t *testing.T) { } // Act - result := OrderItemEntityToResponse(orderItem) + result := OrderItemEntityToResponse(orderItem, uuid.Nil) // Assert assert.NotNil(t, result) @@ -129,7 +129,7 @@ func TestOrderItemEntityToResponse_WithoutProductPreload(t *testing.T) { } // Act - result := OrderItemEntityToResponse(orderItem) + result := OrderItemEntityToResponse(orderItem, uuid.Nil) // Assert assert.NotNil(t, result) diff --git a/internal/models/order.go b/internal/models/order.go index 3a456af..313c496 100644 --- a/internal/models/order.go +++ b/internal/models/order.go @@ -209,6 +209,7 @@ type OrderItemResponse struct { CreatedAt time.Time UpdatedAt time.Time PrinterType string + PrintToChecker bool PaidQuantity int } diff --git a/internal/processor/order_processor.go b/internal/processor/order_processor.go index d146785..02e3d48 100644 --- a/internal/processor/order_processor.go +++ b/internal/processor/order_processor.go @@ -387,7 +387,7 @@ func (p *OrderProcessorImpl) AddToOrder(ctx context.Context, orderID uuid.UUID, return nil, fmt.Errorf("failed to create order item: %w", err) } - itemResponse := mappers.OrderItemEntityToResponse(orderItem) + itemResponse := mappers.OrderItemEntityToResponse(orderItem, order.OutletID) if itemResponse != nil { addedItemResponses = append(addedItemResponses, *itemResponse) } diff --git a/internal/repository/order_repository.go b/internal/repository/order_repository.go index 2a60aa9..9d13a48 100644 --- a/internal/repository/order_repository.go +++ b/internal/repository/order_repository.go @@ -61,6 +61,7 @@ func (r *OrderRepositoryImpl) GetWithRelations(ctx context.Context, id uuid.UUID Preload("OrderItems"). Preload("OrderItems.Product"). Preload("OrderItems.Product.Category"). + Preload("OrderItems.Product.ProductOutletPrices"). Preload("OrderItems.ProductVariant"). Preload("Payments"). Preload("Payments.PaymentMethod"). @@ -141,6 +142,7 @@ func (r *OrderRepositoryImpl) List(ctx context.Context, filters map[string]inter Preload("OrderItems"). Preload("OrderItems.Product"). Preload("OrderItems.Product.Category"). + Preload("OrderItems.Product.ProductOutletPrices"). Preload("OrderItems.ProductVariant"). Preload("Payments"). Preload("Payments.PaymentMethod"). @@ -158,6 +160,7 @@ func (r *OrderRepositoryImpl) ListBySessionID(ctx context.Context, sessionID str Preload("OrderItems"). Preload("OrderItems.Product"). Preload("OrderItems.Product.Category"). + Preload("OrderItems.Product.ProductOutletPrices"). Preload("OrderItems.ProductVariant"). Preload("Payments"). Preload("Payments.PaymentMethod"). diff --git a/internal/transformer/order_transformer.go b/internal/transformer/order_transformer.go index 7edbe3b..88f88d6 100644 --- a/internal/transformer/order_transformer.go +++ b/internal/transformer/order_transformer.go @@ -112,6 +112,7 @@ func OrderModelToContract(resp *models.OrderResponse) *contract.OrderResponse { CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, PrinterType: item.PrinterType, + PrintToChecker: item.PrintToChecker, PaidQuantity: item.PaidQuantity, } } @@ -181,6 +182,7 @@ func AddToOrderModelToContract(resp *models.AddToOrderResponse) *contract.AddToO Status: string(item.Status), CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, + PrintToChecker: item.PrintToChecker, } } return &contract.AddToOrderResponse{ -- 2.47.2