From 535e4c84f6fb78109b65cdcc90996bc8bfe777df Mon Sep 17 00:00:00 2001 From: efrilm Date: Thu, 16 Apr 2026 14:30:09 +0700 Subject: [PATCH] product variant route --- .../processor/purchase_order_processor.go | 7 +- internal/router/router.go | 9 ++ .../000062_create_discount_suite.down.sql | 39 ++++++++ .../000062_create_discount_suite.up.sql | 91 +++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 migrations/000062_create_discount_suite.down.sql create mode 100644 migrations/000062_create_discount_suite.up.sql diff --git a/internal/processor/purchase_order_processor.go b/internal/processor/purchase_order_processor.go index 005cfe6..2804de1 100644 --- a/internal/processor/purchase_order_processor.go +++ b/internal/processor/purchase_order_processor.go @@ -1,12 +1,11 @@ package processor import ( - "context" - "fmt" - "apskel-pos-be/internal/entities" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" + "context" + "fmt" "github.com/google/uuid" ) @@ -368,6 +367,8 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrderStatus(ctx context.Conte return nil, fmt.Errorf("purchase order not found: %w", err) } + fmt.Println("status:", po.Status) + // Check if status is changing to "received" and current status is not "received" if status == "received" && po.Status != "received" { // Get purchase order with items for inventory update diff --git a/internal/router/router.go b/internal/router/router.go index c19adc0..02a1b81 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -146,6 +146,7 @@ func NewRouter(cfg *config.Config, spinGameHandler: handler.NewSpinGameHandler(spinGameService), authMiddleware: authMiddleware, customerAuthMiddleware: customerAuthMiddleware, + productVariantHandler: handler.NewProductVariantHandler(productVariantService, productVariantValidator), } } @@ -270,6 +271,14 @@ func (r *Router) addAppRoutes(rg *gin.Engine) { products.DELETE("/:id", r.productHandler.DeleteProduct) } + productVariants := protected.Group("/product-variants") + { + productVariants.POST("", r.productVariantHandler.CreateProductVariant) + productVariants.PUT("/:id", r.productVariantHandler.UpdateProductVariant) + productVariants.DELETE("/:id", r.productVariantHandler.DeleteProductVariant) + productVariants.GET("/:id", r.productVariantHandler.GetProductVariant) + } + inventory := protected.Group("/inventory") inventory.Use(r.authMiddleware.RequireAdminOrManager()) { diff --git a/migrations/000062_create_discount_suite.down.sql b/migrations/000062_create_discount_suite.down.sql new file mode 100644 index 0000000..e26c593 --- /dev/null +++ b/migrations/000062_create_discount_suite.down.sql @@ -0,0 +1,39 @@ +-- ========================= +-- DROP ORDER DISCOUNTS +-- ========================= +DROP INDEX IF EXISTS idx_order_discounts_discount; +DROP INDEX IF EXISTS idx_order_discounts_outlet; +DROP INDEX IF EXISTS idx_order_discounts_order; +DROP TABLE IF EXISTS order_discounts; + +-- ========================= +-- DROP DISCOUNT CATEGORIES +-- ========================= +DROP INDEX IF EXISTS idx_discount_categories_category; +DROP INDEX IF EXISTS idx_discount_categories_discount; +DROP TABLE IF EXISTS discount_categories; + +-- ========================= +-- DROP DISCOUNT PRODUCTS +-- ========================= +DROP INDEX IF EXISTS idx_discount_products_product; +DROP INDEX IF EXISTS idx_discount_products_discount; +DROP TABLE IF EXISTS discount_products; + +-- ========================= +-- DROP DISCOUNT OUTLETS +-- ========================= +DROP INDEX IF EXISTS idx_discount_outlets_outlet; +DROP INDEX IF EXISTS idx_discount_outlets_discount; +DROP TABLE IF EXISTS discount_outlets; + +-- ========================= +-- DROP DISCOUNTS +-- ========================= +DROP INDEX IF EXISTS idx_discounts_customer_type; +DROP INDEX IF EXISTS idx_discounts_dates; +DROP INDEX IF EXISTS idx_discounts_active; +DROP INDEX IF EXISTS idx_discounts_code; +DROP INDEX IF EXISTS idx_discounts_organization; +DROP INDEX IF EXISTS idx_discounts_campaign; +DROP TABLE IF EXISTS discounts; diff --git a/migrations/000062_create_discount_suite.up.sql b/migrations/000062_create_discount_suite.up.sql new file mode 100644 index 0000000..27684cc --- /dev/null +++ b/migrations/000062_create_discount_suite.up.sql @@ -0,0 +1,91 @@ +CREATE TABLE discounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + campaign_id UUID NULL, + organization_id UUID NOT NULL, + code VARCHAR(50) NOT NULL, + name VARCHAR(255) NOT NULL, + type VARCHAR(20) NOT NULL CHECK (type IN ('percentage', 'fixed_amount', 'free_product')), + value DECIMAL(15,2) NOT NULL, + min_purchase_qty INT DEFAULT 0, + min_purchase_amount DECIMAL(15,2) DEFAULT 0, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + usage_limit_per_customer INT DEFAULT NULL, + usage_limit_total INT DEFAULT NULL, + usage_count INT DEFAULT 0, + customer_type VARCHAR(20) DEFAULT 'all' CHECK (customer_type IN ('all', 'member', 'vip')), + is_stackable BOOLEAN DEFAULT FALSE, + is_active BOOLEAN DEFAULT TRUE, + priority INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT chk_discount_ownership CHECK (campaign_id IS NOT NULL OR organization_id IS NOT NULL), + CONSTRAINT unique_code_per_org UNIQUE (organization_id, code) +); + +CREATE INDEX idx_discounts_campaign ON discounts(campaign_id); +CREATE INDEX idx_discounts_organization ON discounts(organization_id); +CREATE INDEX idx_discounts_code ON discounts(code); +CREATE INDEX idx_discounts_active ON discounts(is_active); +CREATE INDEX idx_discounts_dates ON discounts(start_date, end_date); +CREATE INDEX idx_discounts_customer_type ON discounts(customer_type); + +CREATE TABLE discount_outlets ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + discount_id UUID NOT NULL, + outlet_id UUID NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (discount_id) REFERENCES discounts(id) ON DELETE CASCADE, + FOREIGN KEY (outlet_id) REFERENCES outlets(id) ON DELETE CASCADE, + CONSTRAINT unique_discount_outlet UNIQUE (discount_id, outlet_id) +); + +CREATE INDEX idx_discount_outlets_discount ON discount_outlets(discount_id); +CREATE INDEX idx_discount_outlets_outlet ON discount_outlets(outlet_id); + +CREATE TABLE discount_products ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + discount_id UUID NOT NULL, + product_id UUID NOT NULL, + rule_type VARCHAR(20) NOT NULL CHECK (rule_type IN ('required', 'free', 'excluded')), + quantity INT DEFAULT 1, + free_quantity INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (discount_id) REFERENCES discounts(id) ON DELETE CASCADE, + FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE +); + +CREATE INDEX idx_discount_products_discount ON discount_products(discount_id); +CREATE INDEX idx_discount_products_product ON discount_products(product_id); + +CREATE TABLE discount_categories ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + discount_id UUID NOT NULL, + category_id UUID NOT NULL, + rule_type VARCHAR(20) NOT NULL CHECK (rule_type IN ('included', 'excluded')), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (discount_id) REFERENCES discounts(id) ON DELETE CASCADE, + FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE +); + +CREATE INDEX idx_discount_categories_discount ON discount_categories(discount_id); +CREATE INDEX idx_discount_categories_category ON discount_categories(category_id); + +CREATE TABLE order_discounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + order_id UUID NOT NULL, + outlet_id UUID NOT NULL, + discount_id UUID NOT NULL, + discount_amount DECIMAL(15,2) NOT NULL, + applied_rules JSON, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE, + FOREIGN KEY (outlet_id) REFERENCES outlets(id) ON DELETE RESTRICT, + FOREIGN KEY (discount_id) REFERENCES discounts(id) ON DELETE RESTRICT +); + +CREATE INDEX idx_order_discounts_order ON order_discounts(order_id); +CREATE INDEX idx_order_discounts_outlet ON order_discounts(outlet_id); +CREATE INDEX idx_order_discounts_discount ON order_discounts(discount_id); \ No newline at end of file