diff --git a/internal/database/migrations/20250925040409_create_master_tables.down.sql b/internal/database/migrations/20250925040409_create_master_tables.down.sql index 9816b3e0..671f7579 100644 --- a/internal/database/migrations/20250925040409_create_master_tables.down.sql +++ b/internal/database/migrations/20250925040409_create_master_tables.down.sql @@ -1,4 +1,9 @@ - +DROP TABLE IF EXISTS stock_logs; +DROP INDEX IF EXISTS idx_product_warehouses_unique; +DROP INDEX IF EXISTS idx_product_warehouses_deleted_at; +DROP INDEX IF EXISTS idx_product_warehouses_warehouse_id; +DROP INDEX IF EXISTS idx_product_warehouses_product_id; +DROP TABLE IF EXISTS product_warehouses; DROP TABLE IF EXISTS fcr_standards; DROP INDEX IF EXISTS suppliers_name_unique; DROP TABLE IF EXISTS product_suppliers; @@ -35,10 +40,4 @@ DROP TABLE IF EXISTS fcrs; DROP TABLE IF EXISTS projects; DROP INDEX IF EXISTS users_id_user_unique; DROP INDEX IF EXISTS users_email_unique; -DROP TABLE IF EXISTS users; -DROP INDEX IF EXISTS idx_product_warehouses_unique; -DROP INDEX IF EXISTS idx_product_warehouses_deleted_at; -DROP INDEX IF EXISTS idx_product_warehouses_warehouse_id; -DROP INDEX IF EXISTS idx_product_warehouses_product_id; -DROP TABLE IF EXISTS product_warehouses; -DROP TABLE IF EXISTS stock_logs; \ No newline at end of file +DROP TABLE IF EXISTS users; \ No newline at end of file diff --git a/internal/database/migrations/20250925040409_create_master_tables.up.sql b/internal/database/migrations/20250925040409_create_master_tables.up.sql index 5d844b3e..07e3005a 100644 --- a/internal/database/migrations/20250925040409_create_master_tables.up.sql +++ b/internal/database/migrations/20250925040409_create_master_tables.up.sql @@ -1,276 +1,337 @@ -- USERS CREATE TABLE users ( - id BIGSERIAL PRIMARY KEY, - id_user BIGINT NOT NULL, - name VARCHAR NOT NULL, - email VARCHAR NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ + id BIGSERIAL PRIMARY KEY, + id_user BIGINT NOT NULL, + name VARCHAR NOT NULL, + email VARCHAR NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ ); -CREATE UNIQUE INDEX users_id_user_unique ON users (id_user) WHERE deleted_at IS NULL; -CREATE UNIQUE INDEX users_email_unique ON users (email) WHERE deleted_at IS NULL; +CREATE UNIQUE INDEX users_id_user_unique ON users (id_user) +WHERE + deleted_at IS NULL; + +CREATE UNIQUE INDEX users_email_unique ON users (email) +WHERE + deleted_at IS NULL; -- FLAGS CREATE TABLE flags ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - flagable_id BIGINT NOT NULL, - flagable_type VARCHAR(50) NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + flagable_id BIGINT NOT NULL, + flagable_type VARCHAR(50) NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE UNIQUE INDEX flags_unique_flagable ON flags ( + name, + flagable_id, + flagable_type ); -CREATE UNIQUE INDEX flags_unique_flagable ON flags (name, flagable_id, flagable_type); CREATE INDEX flags_flagable_lookup ON flags (flagable_type, flagable_id); -- PRODUCT CATEGORIES CREATE TABLE product_categories ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - code VARCHAR(10) NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + code VARCHAR(10) NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX product_categories_name_unique ON product_categories (name) WHERE deleted_at IS NULL; -CREATE UNIQUE INDEX product_categories_code_unique ON product_categories (code) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX product_categories_name_unique ON product_categories (name) +WHERE + deleted_at IS NULL; + +CREATE UNIQUE INDEX product_categories_code_unique ON product_categories (code) +WHERE + deleted_at IS NULL; -- UOM CREATE TABLE uoms ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX uoms_name_unique ON uoms (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX uoms_name_unique ON uoms (name) +WHERE + deleted_at IS NULL; -- BANKS CREATE TABLE banks ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - alias VARCHAR(5) NOT NULL, - owner VARCHAR, - account_number VARCHAR(50) NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + alias VARCHAR(5) NOT NULL, + owner VARCHAR, + account_number VARCHAR(50) NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX banks_name_unique ON banks (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX banks_name_unique ON banks (name) +WHERE + deleted_at IS NULL; -- AREAS CREATE TABLE areas ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX areas_name_unique ON areas (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX areas_name_unique ON areas (name) +WHERE + deleted_at IS NULL; -- LOCATIONS CREATE TABLE locations ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - address TEXT NOT NULL, - area_id BIGINT NOT NULL REFERENCES areas(id) ON DELETE RESTRICT ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + address TEXT NOT NULL, + area_id BIGINT NOT NULL REFERENCES areas (id) ON DELETE RESTRICT ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX locations_name_unique ON locations (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX locations_name_unique ON locations (name) +WHERE + deleted_at IS NULL; -- KANDANG CREATE TABLE kandangs ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - location_id BIGINT NOT NULL REFERENCES locations(id) ON DELETE RESTRICT ON UPDATE CASCADE, - pic_id BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + location_id BIGINT NOT NULL REFERENCES locations (id) ON DELETE RESTRICT ON UPDATE CASCADE, + pic_id BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX kandangs_name_unique ON kandangs (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX kandangs_name_unique ON kandangs (name) +WHERE + deleted_at IS NULL; -- WAREHOUSES CREATE TABLE warehouses ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - type VARCHAR(50) NOT NULL, - area_id BIGINT NOT NULL REFERENCES areas(id) ON DELETE RESTRICT ON UPDATE CASCADE, - location_id BIGINT REFERENCES locations(id) ON DELETE SET NULL ON UPDATE CASCADE, - kandang_id BIGINT REFERENCES kandangs(id) ON DELETE SET NULL ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + type VARCHAR(50) NOT NULL, + area_id BIGINT NOT NULL REFERENCES areas (id) ON DELETE RESTRICT ON UPDATE CASCADE, + location_id BIGINT REFERENCES locations (id) ON DELETE SET NULL ON UPDATE CASCADE, + kandang_id BIGINT REFERENCES kandangs (id) ON DELETE SET NULL ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX warehouses_name_unique ON warehouses (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX warehouses_name_unique ON warehouses (name) +WHERE + deleted_at IS NULL; -- CUSTOMERS CREATE TABLE customers ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - pic_id BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, - type VARCHAR(50) NOT NULL, - address TEXT NOT NULL, - phone VARCHAR(20) NOT NULL, - email VARCHAR NOT NULL, - account_number VARCHAR(50) NOT NULL, - balance NUMERIC(15,3) DEFAULT 0, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + pic_id BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, + type VARCHAR(50) NOT NULL, + address TEXT NOT NULL, + phone VARCHAR(20) NOT NULL, + email VARCHAR NOT NULL, + account_number VARCHAR(50) NOT NULL, + balance NUMERIC(15, 3) DEFAULT 0, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX customers_name_unique ON customers (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX customers_name_unique ON customers (name) +WHERE + deleted_at IS NULL; -- NONSTOCK CREATE TABLE nonstocks ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - uom_id BIGINT NOT NULL REFERENCES uoms(id) ON DELETE RESTRICT ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + uom_id BIGINT NOT NULL REFERENCES uoms (id) ON DELETE RESTRICT ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX nonstocks_name_unique ON nonstocks (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX nonstocks_name_unique ON nonstocks (name) +WHERE + deleted_at IS NULL; -- FCR CREATE TABLE fcrs ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX fcrs_name_unique ON fcrs (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX fcrs_name_unique ON fcrs (name) +WHERE + deleted_at IS NULL; CREATE TABLE fcr_standards ( - id BIGSERIAL PRIMARY KEY, - fcr_id BIGINT NOT NULL REFERENCES fcrs(id) ON DELETE CASCADE ON UPDATE CASCADE, - weight NUMERIC(15,3) NOT NULL, - fcr_number NUMERIC(15,3) NOT NULL, - mortality NUMERIC(15,3) NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ + id BIGSERIAL PRIMARY KEY, + fcr_id BIGINT NOT NULL REFERENCES fcrs (id) ON DELETE CASCADE ON UPDATE CASCADE, + weight NUMERIC(15, 3) NOT NULL, + fcr_number NUMERIC(15, 3) NOT NULL, + mortality NUMERIC(15, 3) NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ ); -- SUPPLIERS CREATE TABLE suppliers ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - alias VARCHAR(5) NOT NULL, - pic VARCHAR NOT NULL, - type VARCHAR(50) NOT NULL, - category VARCHAR(20) NOT NULL, - hatchery VARCHAR, - phone VARCHAR(20) NOT NULL, - email VARCHAR NOT NULL, - address TEXT NOT NULL, - npwp VARCHAR(50), - account_number VARCHAR(50), - balance NUMERIC(15,3) DEFAULT 0, - due_date INT NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + alias VARCHAR(5) NOT NULL, + pic VARCHAR NOT NULL, + type VARCHAR(50) NOT NULL, + category VARCHAR(20) NOT NULL, + hatchery VARCHAR, + phone VARCHAR(20) NOT NULL, + email VARCHAR NOT NULL, + address TEXT NOT NULL, + npwp VARCHAR(50), + account_number VARCHAR(50), + balance NUMERIC(15, 3) DEFAULT 0, + due_date INT NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX suppliers_name_unique ON suppliers (name) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX suppliers_name_unique ON suppliers (name) +WHERE + deleted_at IS NULL; CREATE TABLE nonstock_suppliers ( - nonstock_id BIGINT NOT NULL REFERENCES nonstocks(id) ON DELETE CASCADE ON UPDATE CASCADE, - supplier_id BIGINT NOT NULL REFERENCES suppliers(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - PRIMARY KEY (nonstock_id, supplier_id) + nonstock_id BIGINT NOT NULL REFERENCES nonstocks (id) ON DELETE CASCADE ON UPDATE CASCADE, + supplier_id BIGINT NOT NULL REFERENCES suppliers (id) ON DELETE CASCADE ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + PRIMARY KEY (nonstock_id, supplier_id) ); -- PRODUCTS CREATE TABLE products ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - brand VARCHAR NOT NULL, - sku VARCHAR(100), - uom_id BIGINT NOT NULL REFERENCES uoms(id) ON DELETE RESTRICT ON UPDATE CASCADE, - product_category_id BIGINT NOT NULL REFERENCES product_categories(id) ON DELETE RESTRICT ON UPDATE CASCADE, - product_price NUMERIC(15,3) NOT NULL, - selling_price NUMERIC(15,3), - tax NUMERIC(15,3), - expiry_period INT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + brand VARCHAR NOT NULL, + sku VARCHAR(100), + uom_id BIGINT NOT NULL REFERENCES uoms (id) ON DELETE RESTRICT ON UPDATE CASCADE, + product_category_id BIGINT NOT NULL REFERENCES product_categories (id) ON DELETE RESTRICT ON UPDATE CASCADE, + product_price NUMERIC(15, 3) NOT NULL, + selling_price NUMERIC(15, 3), + tax NUMERIC(15, 3), + expiry_period INT, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -CREATE UNIQUE INDEX products_name_unique ON products (name) WHERE deleted_at IS NULL; -CREATE UNIQUE INDEX products_sku_unique ON products (sku) WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX products_name_unique ON products (name) +WHERE + deleted_at IS NULL; + +CREATE UNIQUE INDEX products_sku_unique ON products (sku) +WHERE + deleted_at IS NULL; CREATE TABLE product_suppliers ( - product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE CASCADE ON UPDATE CASCADE, - supplier_id BIGINT NOT NULL REFERENCES suppliers(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - PRIMARY KEY (product_id, supplier_id) + product_id BIGINT NOT NULL REFERENCES products (id) ON DELETE CASCADE ON UPDATE CASCADE, + supplier_id BIGINT NOT NULL REFERENCES suppliers (id) ON DELETE CASCADE ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + PRIMARY KEY (product_id, supplier_id) ); -- PROJECTS CREATE TABLE projects ( - id BIGSERIAL PRIMARY KEY, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ, - created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + id BIGSERIAL PRIMARY KEY, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + created_by BIGINT REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -- PRODUCT WAREHOUSES TABLE CREATE TABLE product_warehouses ( id BIGSERIAL PRIMARY KEY, - product_id BIGINT NOT NULL REFERENCES products(id), - warehouse_id BIGINT NOT NULL REFERENCES warehouses(id), + product_id BIGINT NOT NULL REFERENCES products (id), + warehouse_id BIGINT NOT NULL REFERENCES warehouses (id), quantity INTEGER NOT NULL DEFAULT 0, - created_by BIGINT NOT NULL REFERENCES users(id), + created_by BIGINT NOT NULL REFERENCES users (id), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), deleted_at TIMESTAMPTZ ); -- INDEXES -CREATE INDEX idx_product_warehouses_product_id ON product_warehouses(product_id); -CREATE INDEX idx_product_warehouses_warehouse_id ON product_warehouses(warehouse_id); -CREATE INDEX idx_product_warehouses_deleted_at ON product_warehouses(deleted_at); -CREATE UNIQUE INDEX idx_product_warehouses_unique ON product_warehouses(product_id, warehouse_id) WHERE deleted_at IS NULL; +CREATE INDEX idx_product_warehouses_product_id ON product_warehouses (product_id); + +CREATE INDEX idx_product_warehouses_warehouse_id ON product_warehouses (warehouse_id); + +CREATE INDEX idx_product_warehouses_deleted_at ON product_warehouses (deleted_at); + +CREATE UNIQUE INDEX idx_product_warehouses_unique ON product_warehouses (product_id, warehouse_id) +WHERE + deleted_at IS NULL; -- STOCK LOGS CREATE TABLE stock_logs ( - id BIGSERIAL PRIMARY KEY, - trancaction_type VARCHAR(20) NOT NULL, - quantity NUMERIC(15,3) NOT NULL, - before_quantity NUMERIC(15,3) NOT NULL, - after_quantity NUMERIC(15,3) NOT NULL, - log_type VARCHAR(50) NOT NULL, - log_id BIGINT NOT NULL, - note TEXT, - product_warehouse_id BIGINT NOT NULL REFERENCES product_warehouses(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_by BIGINT NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - deleted_at TIMESTAMPTZ + id BIGSERIAL PRIMARY KEY, + transaction_type VARCHAR(20) NOT NULL, + quantity NUMERIC(15, 3) NOT NULL, + before_quantity NUMERIC(15, 3) NOT NULL, + after_quantity NUMERIC(15, 3) NOT NULL, + log_type VARCHAR(50) NOT NULL, + log_id BIGINT , + note TEXT, + product_warehouse_id BIGINT NOT NULL REFERENCES product_warehouses (id) ON DELETE CASCADE ON UPDATE CASCADE, + created_by BIGINT NOT NULL REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ ); -- Create indexes for better performance CREATE INDEX stock_logs_product_warehouse_id_idx ON stock_logs (product_warehouse_id); + CREATE INDEX stock_logs_log_type_log_id_idx ON stock_logs (log_type, log_id); + CREATE INDEX stock_logs_created_by_idx ON stock_logs (created_by); + CREATE INDEX stock_logs_created_at_idx ON stock_logs (created_at); + CREATE INDEX stock_logs_deleted_at_idx ON stock_logs (deleted_at); \ No newline at end of file diff --git a/internal/database/seed/seeder.go b/internal/database/seed/seeder.go index a9bac734..b321a784 100644 --- a/internal/database/seed/seeder.go +++ b/internal/database/seed/seeder.go @@ -74,6 +74,10 @@ func Run(db *gorm.DB) error { return err } + if err := seedProductWarehouse(tx, adminID); err != nil { + return err + } + fmt.Println("✅ Master data seeding completed") return nil }) @@ -675,10 +679,6 @@ func seedNonstocks(tx *gorm.DB, createdBy uint, uoms map[string]uint, suppliers } // nanti saya isi -func seedProductWarehouse(tx *gorm.DB, createdBy uint, products map[string]uint, warehouses map[string]uint) error { - - return nil -} func seedFlags(tx *gorm.DB, flagableID uint, flagableType string, flags []utils.FlagType) error { if len(flags) == 0 { @@ -766,6 +766,39 @@ func seedBanks(tx *gorm.DB, createdBy uint) error { return nil } +func seedProductWarehouse(tx *gorm.DB, createdBy uint) error { + + seeds := []struct { + ProductID uint + WarehouseID uint + Quantity float64 + }{ + {ProductID: 1, WarehouseID: 1, Quantity: 100}, + {ProductID: 2, WarehouseID: 2, Quantity: 200}, + {ProductID: 1, WarehouseID: 1, Quantity: 300}, + } + + for _, seed := range seeds { + var productWarehouse entity.ProductWarehouse + err := tx.Where("product_id = ? AND warehouse_id = ?", seed.ProductID, seed.WarehouseID).First(&productWarehouse).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + productWarehouse = entity.ProductWarehouse{ + ProductId: seed.ProductID, + WarehouseId: seed.WarehouseID, + Quantity: seed.Quantity, + CreatedBy: createdBy, + } + if err := tx.Create(&productWarehouse).Error; err != nil { + return err + } + } else if err != nil { + return err + } + } + + return nil +} + func ptr[T any](v T) *T { return &v } diff --git a/internal/entities/adjustment.go b/internal/entities/adjustment.go deleted file mode 100644 index 840bbe3a..00000000 --- a/internal/entities/adjustment.go +++ /dev/null @@ -1,18 +0,0 @@ -package entities - -import ( - "time" - - "gorm.io/gorm" -) - -type Adjustment struct { - Id uint `gorm:"primaryKey"` - Name string `gorm:"not null;uniqueIndex:idx_name,where:deleted_at IS NULL"` - CreatedBy uint `gorm:"not null"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoUpdateTime"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` - - CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"` -} diff --git a/internal/entities/product_warehouse.go b/internal/entities/product_warehouse.go index 84c3ae32..0c837ab1 100644 --- a/internal/entities/product_warehouse.go +++ b/internal/entities/product_warehouse.go @@ -10,7 +10,7 @@ type ProductWarehouse struct { Id uint `json:"id" gorm:"primaryKey;autoIncrement"` ProductId uint `json:"product_id" gorm:"not null"` WarehouseId uint `json:"warehouse_id" gorm:"not null"` - Quantity int `json:"quantity" gorm:"default:0"` + Quantity float64 `json:"quantity" gorm:"default:0"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` CreatedBy uint `json:"created_by" gorm:"not null"` diff --git a/internal/entities/stock_log.go b/internal/entities/stock_log.go index bdac1745..df505aa8 100644 --- a/internal/entities/stock_log.go +++ b/internal/entities/stock_log.go @@ -6,23 +6,30 @@ import ( "gorm.io/gorm" ) +const ( + LogTypeAdjustment = "ADJUSTMENT" +) + +const ( + TransactionTypeIncrease = "INCREASE" + TransactionTypeDecrease = "DECREASE" +) + type StockLog struct { - Id uint `json:"id" gorm:"primaryKey;"` - TransactionType string `json:"transaction_type" gorm:"type:varchar(20);not null"` - Quantity float64 `json:"quantity" gorm:"type:numeric(15,3);not null"` - BeforeQuantity float64 `json:"before_quantity" gorm:"type:numeric(15,3);not null"` - AfterQuantity float64 `json:"after_quantity" gorm:"type:numeric(15,3);not null"` - LogType string `json:"log_type" gorm:"type:varchar(50);not null"` - LogId uint `json:"log_id" gorm:"not null"` - Note string `json:"note" gorm:"type:text"` - ProductWarehouseId uint `json:"product_warehouse_id" gorm:"not null;index"` - CreatedBy uint `json:"created_by" gorm:"not null;index"` - CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` - UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` - DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"` + Id uint `json:"id" gorm:"primaryKey;column:id"` + TransactionType string `json:"transaction_type" gorm:"column:transaction_type;type:varchar(20);not null"` + Quantity float64 `json:"quantity" gorm:"column:quantity;type:numeric(15,3);not null"` + BeforeQuantity float64 `json:"before_quantity" gorm:"column:before_quantity;type:numeric(15,3);not null"` + AfterQuantity float64 `json:"after_quantity" gorm:"column:after_quantity;type:numeric(15,3);not null"` + LogType string `json:"log_type" gorm:"column:log_type;type:varchar(50);not null;index:stock_logs_flaggable_lookup,priority:1"` + LogId uint `json:"log_id" gorm:"column:log_id;not null;index:stock_logs_flaggable_lookup,priority:2"` + Note string `json:"note" gorm:"column:note;type:text"` + ProductWarehouseId uint `json:"product_warehouse_id" gorm:"column:product_warehouse_id;not null;index"` + CreatedBy uint `json:"created_by" gorm:"column:created_by;not null;index"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at;autoCreateTime"` + UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;autoUpdateTime"` + DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"column:deleted_at;index"` ProductWarehouse *ProductWarehouse `json:"product_warehouse,omitempty" gorm:"foreignKey:ProductWarehouseId;references:Id"` CreatedUser *User `json:"created_user,omitempty" gorm:"foreignKey:CreatedBy;references:Id"` } - - diff --git a/internal/modules/inventory/adjustments/controllers/adjustment.controller.go b/internal/modules/inventory/adjustments/controllers/adjustment.controller.go index 199e10fa..b5d275de 100644 --- a/internal/modules/inventory/adjustments/controllers/adjustment.controller.go +++ b/internal/modules/inventory/adjustments/controllers/adjustment.controller.go @@ -2,7 +2,6 @@ package controller import ( "math" - "strconv" "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/dto" service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" @@ -22,119 +21,61 @@ func NewAdjustmentController(adjustmentService service.AdjustmentService) *Adjus } } -func (u *AdjustmentController) GetAll(c *fiber.Ctx) error { - query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), - } - - result, totalResults, err := u.AdjustmentService.GetAll(c, query) - if err != nil { - return err - } - - return c.Status(fiber.StatusOK). - JSON(response.SuccessWithPaginate[dto.AdjustmentListDTO]{ - Code: fiber.StatusOK, - Status: "success", - Message: "Get all adjustments successfully", - Meta: response.Meta{ - Page: query.Page, - Limit: query.Limit, - TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), - TotalResults: totalResults, - }, - Data: dto.ToAdjustmentListDTOs(result), - }) -} - -func (u *AdjustmentController) GetOne(c *fiber.Ctx) error { - param := c.Params("id") - - id, err := strconv.Atoi(param) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") - } - - result, err := u.AdjustmentService.GetOne(c, uint(id)) - if err != nil { - return err - } - - return c.Status(fiber.StatusOK). - JSON(response.Success{ - Code: fiber.StatusOK, - Status: "success", - Message: "Get adjustment successfully", - Data: dto.ToAdjustmentListDTO(*result), - }) -} - -func (u *AdjustmentController) CreateOne(c *fiber.Ctx) error { +func (u *AdjustmentController) Adjustment(c *fiber.Ctx) error { req := new(validation.Create) if err := c.BodyParser(req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } - result, err := u.AdjustmentService.CreateOne(c, req) + stockLog, err := u.AdjustmentService.Adjustment(c, req) if err != nil { return err } + adjustmentDTO := dto.ToAdjustmentDetailDTO(stockLog) + return c.Status(fiber.StatusCreated). JSON(response.Success{ Code: fiber.StatusCreated, Status: "success", Message: "Create adjustment successfully", - Data: dto.ToAdjustmentListDTO(*result), + Data: adjustmentDTO, }) } -func (u *AdjustmentController) UpdateOne(c *fiber.Ctx) error { - req := new(validation.Update) - param := c.Params("id") - - id, err := strconv.Atoi(param) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") +func (u *AdjustmentController) AdjustmentHistory(c *fiber.Ctx) error { + query := &validation.Query{ + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + ProductID: c.QueryInt("product_id", 0), + WarehouseID: c.QueryInt("warehouse_id", 0), + TransactionType: c.Query("transaction_type", ""), } - if err := c.BodyParser(req); err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - result, err := u.AdjustmentService.UpdateOne(c, req, uint(id)) + result, totalResults, err := u.AdjustmentService.AdjustmentHistory(c, query) if err != nil { return err } - return c.Status(fiber.StatusOK). - JSON(response.Success{ - Code: fiber.StatusOK, - Status: "success", - Message: "Update adjustment successfully", - Data: dto.ToAdjustmentListDTO(*result), - }) -} - -func (u *AdjustmentController) DeleteOne(c *fiber.Ctx) error { - param := c.Params("id") - - id, err := strconv.Atoi(param) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") - } - - if err := u.AdjustmentService.DeleteOne(c, uint(id)); err != nil { - return err + // Convert to DTOs + adjustmentDTOs := make([]dto.AdjustmentDetailDTO, len(result)) + for i, stockLog := range result { + adjustmentDTOs[i] = dto.ToAdjustmentDetailDTO(stockLog) } return c.Status(fiber.StatusOK). - JSON(response.Common{ + JSON(response.SuccessWithPaginate[dto.AdjustmentDetailDTO]{ Code: fiber.StatusOK, Status: "success", - Message: "Delete adjustment successfully", + Message: "Get adjustment history successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + }, + Data: adjustmentDTOs, }) } diff --git a/internal/modules/inventory/adjustments/dto/adjustment.dto.go b/internal/modules/inventory/adjustments/dto/adjustment.dto.go index d0c2458a..72d58c2a 100644 --- a/internal/modules/inventory/adjustments/dto/adjustment.dto.go +++ b/internal/modules/inventory/adjustments/dto/adjustment.dto.go @@ -9,56 +9,123 @@ import ( // === DTO Structs === +type ProductBaseDTO struct { + Id uint `json:"id"` + Name string `json:"name"` + SKU string `json:"sku"` +} + +type WarehouseBaseDTO struct { + Id uint `json:"id"` + Name string `json:"name"` +} + +type ProductWarehouseDTO struct { + Id uint `json:"id"` + ProductId uint `json:"product_id"` + WarehouseId uint `json:"warehouse_id"` + Quantity float64 `json:"quantity"` + Product *ProductBaseDTO `json:"product,omitempty"` + Warehouse *WarehouseBaseDTO `json:"warehouse,omitempty"` +} + type AdjustmentBaseDTO struct { - Id uint `json:"id"` - Name string `json:"name"` + Id uint `json:"id"` + TransactionType string `json:"transaction_type"` + Quantity float64 `json:"quantity"` + BeforeQuantity float64 `json:"before_quantity"` + AfterQuantity float64 `json:"after_quantity"` + Note string `json:"note,omitempty"` + ProductWarehouseId uint `json:"product_warehouse_id"` + ProductWarehouse *ProductWarehouseDTO `json:"product_warehouse,omitempty"` } type AdjustmentListDTO struct { AdjustmentBaseDTO - CreatedUser *userDTO.UserBaseDTO `json:"created_user"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"` + CreatedAt time.Time `json:"created_at"` } type AdjustmentDetailDTO struct { AdjustmentListDTO + UpdatedAt time.Time `json:"updated_at"` } // === Mapper Functions === -func ToAdjustmentBaseDTO(e entity.Adjustment) AdjustmentBaseDTO { - return AdjustmentBaseDTO{ - Id: e.Id, - Name: e.Name, +func ToProductBaseDTO(e *entity.Product) *ProductBaseDTO { + if e == nil { + return nil + } + sku := "" + if e.Sku != nil { + sku = *e.Sku + } + return &ProductBaseDTO{ + Id: e.Id, + Name: e.Name, + SKU: sku, } } -func ToAdjustmentListDTO(e entity.Adjustment) AdjustmentListDTO { +func ToWarehouseBaseDTO(e *entity.Warehouse) *WarehouseBaseDTO { + if e == nil { + return nil + } + return &WarehouseBaseDTO{ + Id: e.Id, + Name: e.Name, + } +} + +func ToProductWarehouseDTO(e *entity.ProductWarehouse) *ProductWarehouseDTO { + if e == nil { + return nil + } + return &ProductWarehouseDTO{ + Id: e.Id, + ProductId: e.ProductId, + WarehouseId: e.WarehouseId, + Quantity: e.Quantity, + Product: ToProductBaseDTO(&e.Product), + Warehouse: ToWarehouseBaseDTO(&e.Warehouse), + } +} + +func ToAdjustmentBaseDTO(e *entity.StockLog) AdjustmentBaseDTO { + return AdjustmentBaseDTO{ + Id: e.Id, + TransactionType: e.TransactionType, + Quantity: e.Quantity, + BeforeQuantity: e.BeforeQuantity, + AfterQuantity: e.AfterQuantity, + Note: e.Note, + ProductWarehouseId: e.ProductWarehouseId, + ProductWarehouse: ToProductWarehouseDTO(e.ProductWarehouse), + } +} + +func ToAdjustmentListDTO(e *entity.StockLog) AdjustmentListDTO { var createdUser *userDTO.UserBaseDTO - if e.CreatedUser.Id != 0 { - mapped := userDTO.ToUserBaseDTO(e.CreatedUser) - createdUser = &mapped + if e.CreatedUser != nil { + createdUser = &userDTO.UserBaseDTO{ + Id: e.CreatedUser.Id, + IdUser: e.CreatedUser.IdUser, + Email: e.CreatedUser.Email, + Name: e.CreatedUser.Name, + } } return AdjustmentListDTO{ AdjustmentBaseDTO: ToAdjustmentBaseDTO(e), - CreatedAt: e.CreatedAt, - UpdatedAt: e.UpdatedAt, - CreatedUser: createdUser, + CreatedUser: createdUser, + CreatedAt: e.CreatedAt, } } -func ToAdjustmentListDTOs(e []entity.Adjustment) []AdjustmentListDTO { - result := make([]AdjustmentListDTO, len(e)) - for i, r := range e { - result[i] = ToAdjustmentListDTO(r) - } - return result -} - -func ToAdjustmentDetailDTO(e entity.Adjustment) AdjustmentDetailDTO { +func ToAdjustmentDetailDTO(e *entity.StockLog) AdjustmentDetailDTO { return AdjustmentDetailDTO{ AdjustmentListDTO: ToAdjustmentListDTO(e), + UpdatedAt: e.UpdatedAt, } } diff --git a/internal/modules/inventory/adjustments/module.go b/internal/modules/inventory/adjustments/module.go index 82126e43..294cf9dc 100644 --- a/internal/modules/inventory/adjustments/module.go +++ b/internal/modules/inventory/adjustments/module.go @@ -5,8 +5,10 @@ import ( "github.com/gofiber/fiber/v2" "gorm.io/gorm" - rAdjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/repositories" sAdjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" + rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" + rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories" + rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/stock-logs/repositories" rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" @@ -15,12 +17,13 @@ import ( type AdjustmentModule struct{} func (AdjustmentModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { - adjustmentRepo := rAdjustment.NewAdjustmentRepository(db) + stockLogsRepo := rStockLogs.NewStockLogRepository(db) + warehouseRepo := rWarehouse.NewWarehouseRepository(db) + productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db) userRepo := rUser.NewUserRepository(db) - adjustmentService := sAdjustment.NewAdjustmentService(adjustmentRepo, validate) + adjustmentService := sAdjustment.NewAdjustmentService(stockLogsRepo, warehouseRepo, productWarehouseRepo, validate) userService := sUser.NewUserService(userRepo, validate) AdjustmentRoutes(router, userService, adjustmentService) } - diff --git a/internal/modules/inventory/adjustments/repositories/adjustment.repository.go b/internal/modules/inventory/adjustments/repositories/adjustment.repository.go deleted file mode 100644 index b8f3cc2b..00000000 --- a/internal/modules/inventory/adjustments/repositories/adjustment.repository.go +++ /dev/null @@ -1,21 +0,0 @@ -package repository - -import ( - entity "gitlab.com/mbugroup/lti-api.git/internal/entities" - "gitlab.com/mbugroup/lti-api.git/internal/common/repository" - "gorm.io/gorm" -) - -type AdjustmentRepository interface { - repository.BaseRepository[entity.Adjustment] -} - -type AdjustmentRepositoryImpl struct { - *repository.BaseRepositoryImpl[entity.Adjustment] -} - -func NewAdjustmentRepository(db *gorm.DB) AdjustmentRepository { - return &AdjustmentRepositoryImpl{ - BaseRepositoryImpl: repository.NewBaseRepository[entity.Adjustment](db), - } -} diff --git a/internal/modules/inventory/adjustments/route.go b/internal/modules/inventory/adjustments/route.go index deba38ea..cb63defa 100644 --- a/internal/modules/inventory/adjustments/route.go +++ b/internal/modules/inventory/adjustments/route.go @@ -14,15 +14,8 @@ func AdjustmentRoutes(v1 fiber.Router, u user.UserService, s adjustment.Adjustme route := v1.Group("/adjustments") - // route.Get("/", m.Auth(u), ctrl.GetAll) - // route.Post("/", m.Auth(u), ctrl.CreateOne) - // route.Get("/:id", m.Auth(u), ctrl.GetOne) - // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) - // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) + // Standard CRUD routes following master data pattern + route.Get("/", ctrl.AdjustmentHistory) // Get all with pagination and filters + route.Post("/", ctrl.Adjustment) // Create adjustment - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) } diff --git a/internal/modules/inventory/adjustments/services/adjustment.service.go b/internal/modules/inventory/adjustments/services/adjustment.service.go index bdea7eab..ec500e1b 100644 --- a/internal/modules/inventory/adjustments/services/adjustment.service.go +++ b/internal/modules/inventory/adjustments/services/adjustment.service.go @@ -2,128 +2,184 @@ package service import ( "errors" + "strings" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" - repository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/repositories" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/validations" + ProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" + warehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories" + stockLogsRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/stock-logs/repositories" "gitlab.com/mbugroup/lti-api.git/internal/utils" + "gorm.io/gorm" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/sirupsen/logrus" - "gorm.io/gorm" ) type AdjustmentService interface { - GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Adjustment, int64, error) - GetOne(ctx *fiber.Ctx, id uint) (*entity.Adjustment, error) - CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Adjustment, error) - UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Adjustment, error) - DeleteOne(ctx *fiber.Ctx, id uint) error + Adjustment(ctx *fiber.Ctx, req *validation.Create) (*entity.StockLog, error) + GetOne(ctx *fiber.Ctx, id uint) (*entity.StockLog, error) + AdjustmentHistory(ctx *fiber.Ctx, query *validation.Query) ([]*entity.StockLog, int64, error) } type adjustmentService struct { - Log *logrus.Logger - Validate *validator.Validate - Repository repository.AdjustmentRepository + Log *logrus.Logger + Validate *validator.Validate + StockLogsRepository stockLogsRepo.StockLogRepository + WarehouseRepo warehouseRepo.WarehouseRepository + ProductWarehouseRepo ProductWarehouse.ProductWarehouseRepository } -func NewAdjustmentService(repo repository.AdjustmentRepository, validate *validator.Validate) AdjustmentService { +func NewAdjustmentService(stockLogsRepo stockLogsRepo.StockLogRepository, warehouseRepo warehouseRepo.WarehouseRepository, productWarehouseRepo ProductWarehouse.ProductWarehouseRepository, validate *validator.Validate) AdjustmentService { return &adjustmentService{ - Log: utils.Log, - Validate: validate, - Repository: repo, + Log: utils.Log, + Validate: validate, + StockLogsRepository: stockLogsRepo, + WarehouseRepo: warehouseRepo, + ProductWarehouseRepo: productWarehouseRepo, } } -func (s adjustmentService) withRelations(db *gorm.DB) *gorm.DB { - return db.Preload("CreatedUser") +func (s *adjustmentService) withRelations(db *gorm.DB) *gorm.DB { + return db. + Preload("ProductWarehouse"). + Preload("ProductWarehouse.Product"). + Preload("ProductWarehouse.Warehouse"). + Preload("CreatedUser") } -func (s adjustmentService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Adjustment, int64, error) { - if err := s.Validate.Struct(params); err != nil { +func (s *adjustmentService) GetOne(c *fiber.Ctx, id uint) (*entity.StockLog, error) { + stockLog, err := s.StockLogsRepository.GetByID(c.Context(), id, s.withRelations) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") + } + s.Log.Errorf("Failed to get adjustment by id: %+v", err) + return nil, err + } + + if stockLog.LogType != entity.LogTypeAdjustment { + return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") + } + + return stockLog, nil +} + +func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*entity.StockLog, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + ctx := c.Context() + + productWarehouseExists, err := s.ProductWarehouseRepo.ProductWarehouseExists(ctx, uint(req.ProductID), uint(req.WarehouseID), nil) + if err != nil { + return nil, err + } + if !productWarehouseExists { + return nil, fiber.NewError(fiber.StatusBadRequest, "Product warehouse not found") + } + + transactionType := strings.ToUpper(req.TransactionType) + if transactionType != entity.TransactionTypeIncrease && transactionType != entity.TransactionTypeDecrease { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid transaction type") + } + + var createdLogId uint + + err = s.StockLogsRepository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // Get product warehouse by product id and warehouse id (read operation, no transaction needed) + productWarehouse, err := s.ProductWarehouseRepo.GetProductWarehouseByProductAndWarehouseID(ctx, uint(req.ProductID), uint(req.WarehouseID)) + if err != nil { + return err + } + if productWarehouse == nil { + return fiber.NewError(fiber.StatusBadRequest, "Product warehouse not found") + } + s.Log.Infof("Product Warehouse found: %+v", productWarehouse.Id) + + afterQuantity := productWarehouse.Quantity + if transactionType == entity.TransactionTypeIncrease { + afterQuantity += req.Quantity + } else { + if productWarehouse.Quantity < req.Quantity { + return fiber.NewError(fiber.StatusBadRequest, "Insufficient stock for adjustment") + } + afterQuantity -= req.Quantity + } + + newLog := &entity.StockLog{ + TransactionType: transactionType, + Quantity: req.Quantity, + BeforeQuantity: productWarehouse.Quantity, + AfterQuantity: afterQuantity, + LogType: entity.LogTypeAdjustment, + LogId: 0, + Note: req.Note, + ProductWarehouseId: productWarehouse.Id, + CreatedBy: 1, // TODO: should Get from auth middleware + } + + if err := s.StockLogsRepository.WithTx(tx).CreateOne(ctx, newLog, nil); err != nil { + s.Log.Errorf("Failed to create stock log: %+v", err) + return err + } + s.Log.Infof("Stock log created: %+v", newLog.Id) + + productWarehouse.Quantity = afterQuantity + if err := s.ProductWarehouseRepo.WithTx(tx).UpdateOne(ctx, productWarehouse.Id, productWarehouse, nil); err != nil { + s.Log.Errorf("Failed to update product warehouse quantity: %+v", err) + return err + } + s.Log.Infof("Product warehouse quantity updated: %+v", productWarehouse.Id) + + // Set createdLogId to get the log with relations after transaction + createdLogId = newLog.Id + return nil + }) + + if err != nil { + s.Log.Errorf("Transaction failed in CreateOne: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to process adjustment transaction") + } + + return s.GetOne(c, createdLogId) +} + +func (s *adjustmentService) AdjustmentHistory(c *fiber.Ctx, query *validation.Query) ([]*entity.StockLog, int64, error) { + if err := s.Validate.Struct(query); err != nil { return nil, 0, err } - offset := (params.Page - 1) * params.Limit + offset := (query.Page - 1) * query.Limit + + stockLogs, total, err := s.StockLogsRepository.GetAll(c.Context(), offset, query.Limit, func(db *gorm.DB) *gorm.DB { - adjustments, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { db = s.withRelations(db) - if params.Search != "" { - return db.Where("name LIKE ?", "%"+params.Search+"%") + + db = db.Where("log_type = ?", entity.LogTypeAdjustment) + + if query.Search != "" { + db = db.Where("note ILIKE ?", "%"+query.Search+"%") } - return db.Order("created_at DESC").Order("updated_at DESC") + + if query.TransactionType != "" { + db = db.Where("transaction_type = ?", strings.ToUpper(query.TransactionType)) + } + + return db.Order("created_at DESC") }) if err != nil { s.Log.Errorf("Failed to get adjustments: %+v", err) - return nil, 0, err + return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to get adjustment history") } - return adjustments, total, nil -} - -func (s adjustmentService) GetOne(c *fiber.Ctx, id uint) (*entity.Adjustment, error) { - adjustment, err := s.Repository.GetByID(c.Context(), id, s.withRelations) - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") - } - if err != nil { - s.Log.Errorf("Failed get adjustment by id: %+v", err) - return nil, err - } - return adjustment, nil -} - -func (s *adjustmentService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.Adjustment, error) { - if err := s.Validate.Struct(req); err != nil { - return nil, err - } - - createBody := &entity.Adjustment{ - Name: req.Name, - } - - if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil { - s.Log.Errorf("Failed to create adjustment: %+v", err) - return nil, err - } - - return s.GetOne(c, createBody.Id) -} - -func (s adjustmentService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Adjustment, error) { - if err := s.Validate.Struct(req); err != nil { - return nil, err - } - - updateBody := make(map[string]any) - - if req.Name != nil { - updateBody["name"] = *req.Name - } - - if len(updateBody) == 0 { - return s.GetOne(c, id) - } - - if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") - } - s.Log.Errorf("Failed to update adjustment: %+v", err) - return nil, err - } - - return s.GetOne(c, id) -} - -func (s adjustmentService) DeleteOne(c *fiber.Ctx, id uint) error { - if err := s.Repository.DeleteOne(c.Context(), id); err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return fiber.NewError(fiber.StatusNotFound, "Adjustment not found") - } - s.Log.Errorf("Failed to delete adjustment: %+v", err) - return err - } - return nil + + // Convert to pointer slice + result := make([]*entity.StockLog, len(stockLogs)) + for i, v := range stockLogs { + result[i] = &v + } + + return result, total, nil } diff --git a/internal/modules/inventory/adjustments/validations/adjustment.validation.go b/internal/modules/inventory/adjustments/validations/adjustment.validation.go index 95505746..1245108a 100644 --- a/internal/modules/inventory/adjustments/validations/adjustment.validation.go +++ b/internal/modules/inventory/adjustments/validations/adjustment.validation.go @@ -1,15 +1,18 @@ package validation type Create struct { - Name string `json:"name" validate:"required_strict,min=3"` -} - -type Update struct { - Name *string `json:"name,omitempty" validate:"omitempty"` + ProductID uint `json:"product_id" validate:"required"` + WarehouseID uint `json:"warehouse_id" validate:"required"` + TransactionType string `json:"transaction_type" validate:"required,oneof=increase decrease"` + Quantity float64 `json:"quantity" validate:"required,gt=0"` + Note string `json:"note" validate:"omitempty,max=255"` } type Query struct { - Page int `query:"page" validate:"omitempty,number,min=1"` - Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` - Search string `query:"search" validate:"omitempty,max=50"` + Page int `query:"page" validate:"omitempty,min=1"` + Limit int `query:"limit" validate:"omitempty,min=1,max=100"` + Search string `query:"search" validate:"omitempty"` + ProductID int `query:"product_id" validate:"omitempty,min=0"` + WarehouseID int `query:"warehouse_id" validate:"omitempty,min=0"` + TransactionType string `query:"transaction_type" validate:"omitempty,oneof=increase decrease"` } diff --git a/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go b/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go index 4e526184..5e0ea423 100644 --- a/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go +++ b/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go @@ -26,7 +26,6 @@ func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ Page: c.QueryInt("page", 1), Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), ProductId: uint(c.QueryInt("product_id", 0)), WarehouseId: uint(c.QueryInt("warehouse_id", 0)), } diff --git a/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go b/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go index 0d7ef471..2260e834 100644 --- a/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go +++ b/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go @@ -13,7 +13,7 @@ type ProductWarehouseBaseDTO struct { Id uint `json:"id"` ProductId uint `json:"product_id"` WarehouseId uint `json:"warehouse_id"` - Quantity int `json:"quantity"` + Quantity float64 `json:"quantity"` } type ProductWarehouseListDTO struct { diff --git a/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go b/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go index 053592da..0398a825 100644 --- a/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go +++ b/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go @@ -14,6 +14,7 @@ type ProductWarehouseRepository interface { IsProductExist(ctx context.Context, productId uint) (bool, error) IsWarehouseExist(ctx context.Context, warehouseId uint) (bool, error) ExistsByID(ctx context.Context, id uint) (bool, error) + GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error) } type ProductWarehouseRepositoryImpl struct { @@ -51,3 +52,11 @@ func (r *ProductWarehouseRepositoryImpl) IsWarehouseExist(ctx context.Context, w func (r *ProductWarehouseRepositoryImpl) ExistsByID(ctx context.Context, id uint) (bool, error) { return repository.Exists[entity.ProductWarehouse](ctx, r.db, id) } + +func (r *ProductWarehouseRepositoryImpl) GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error) { + var productWarehouse entity.ProductWarehouse + if err := r.DB().WithContext(ctx).Where("product_id = ? AND warehouse_id = ?", productId, warehouseId).First(&productWarehouse).Error; err != nil { + return nil, err + } + return &productWarehouse, nil +} diff --git a/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go b/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go index 21675ef1..03c7c9a1 100644 --- a/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go +++ b/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go @@ -58,13 +58,6 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) db = db.Where("warehouse_id = ?", params.WarehouseId) } - // Search in related product or warehouse names - if params.Search != "" { - db = db.Joins("LEFT JOIN products ON products.id = product_warehouse.product_id"). - Joins("LEFT JOIN warehouses ON warehouses.id = product_warehouse.warehouse_id"). - Where("products.name ILIKE ? OR warehouses.name ILIKE ?", "%"+params.Search+"%", "%"+params.Search+"%") - } - return db.Order("created_at DESC").Order("updated_at DESC") }) diff --git a/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go b/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go index 1031595b..02648300 100644 --- a/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go +++ b/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go @@ -1,21 +1,20 @@ package validation type Create struct { - ProductId uint `json:"product_id" validate:"required,number,min=1"` - WarehouseId uint `json:"warehouse_id" validate:"required,number,min=1"` - Quantity int `json:"quantity" validate:"required,number,min=0"` + ProductId uint `json:"product_id" validate:"required,number,min=1"` + WarehouseId uint `json:"warehouse_id" validate:"required,number,min=1"` + Quantity float64 `json:"quantity" validate:"required,number,min=0"` } type Update struct { - ProductId *uint `json:"product_id,omitempty" validate:"omitempty,number,min=1"` - WarehouseId *uint `json:"warehouse_id,omitempty" validate:"omitempty,number,min=1"` - Quantity *int `json:"quantity,omitempty" validate:"omitempty,number,min=0"` + ProductId *uint `json:"product_id,omitempty" validate:"omitempty,number,min=1"` + WarehouseId *uint `json:"warehouse_id,omitempty" validate:"omitempty,number,min=1"` + Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,number,min=0"` } type Query struct { - Page int `query:"page" validate:"omitempty,number,min=1"` - Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` - Search string `query:"search" validate:"omitempty,max=50"` - ProductId uint `query:"product_id" validate:"omitempty,number,min=1"` - WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"` + Page int `query:"page" validate:"omitempty,number,min=1"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` + ProductId uint `query:"product_id" validate:"omitempty,number,min=1"` + WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"` } diff --git a/internal/modules/master/products/controllers/product.controller.go b/internal/modules/master/products/controllers/product.controller.go index 2406cd9c..ee2c95f8 100644 --- a/internal/modules/master/products/controllers/product.controller.go +++ b/internal/modules/master/products/controllers/product.controller.go @@ -24,9 +24,10 @@ func NewProductController(productService service.ProductService) *ProductControl func (u *ProductController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + ProductCategoryID: c.QueryInt("product_category_id", 0), } result, totalResults, err := u.ProductService.GetAll(c, query) diff --git a/internal/modules/master/products/services/product.service.go b/internal/modules/master/products/services/product.service.go index 076cabf9..fb1fe00f 100644 --- a/internal/modules/master/products/services/product.service.go +++ b/internal/modules/master/products/services/product.service.go @@ -72,6 +72,9 @@ func (s productService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity if params.Search != "" { return db.Where("name LIKE ?", "%"+params.Search+"%") } + if params.ProductCategoryID != 0 { + return db.Where("product_category_id = ?", params.ProductCategoryID) + } return db.Order("created_at DESC").Order("updated_at DESC") }) diff --git a/internal/modules/master/products/validations/product.validation.go b/internal/modules/master/products/validations/product.validation.go index 07f4c005..3e00aa8a 100644 --- a/internal/modules/master/products/validations/product.validation.go +++ b/internal/modules/master/products/validations/product.validation.go @@ -29,7 +29,8 @@ type Update struct { } type Query struct { - Page int `query:"page" validate:"omitempty,number,min=1"` - Limit int `query:"limit" validate:"omitempty,number,min=1"` - Search string `query:"search" validate:"omitempty,max=50"` + Page int `query:"page" validate:"omitempty,number,min=1"` + Limit int `query:"limit" validate:"omitempty,number,min=1"` + Search string `query:"search" validate:"omitempty,max=50"` + ProductCategoryID int `query:"product_category_id" validate:"omitempty,number,min=1"` } diff --git a/internal/modules/shared/stock-logs/repositories/stock-logs.repository.go b/internal/modules/shared/stock-logs/repositories/stock-logs.repository.go new file mode 100644 index 00000000..c93db2b1 --- /dev/null +++ b/internal/modules/shared/stock-logs/repositories/stock-logs.repository.go @@ -0,0 +1,88 @@ +package repository + +import ( + "context" + + "gitlab.com/mbugroup/lti-api.git/internal/common/repository" + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + "gorm.io/gorm" +) + +type StockLogRepository interface { + repository.BaseRepository[entity.StockLog] + GetByFlaggable(ctx context.Context, logType string, logId uint) ([]*entity.StockLog, error) + GetByProductWarehouse(ctx context.Context, productWarehouseId uint, limit int) ([]*entity.StockLog, error) + GetByTransactionType(ctx context.Context, transactionType string, limit int) ([]*entity.StockLog, error) +} + +type StockLogRepositoryImpl struct { + *repository.BaseRepositoryImpl[entity.StockLog] +} + +func NewStockLogRepository(db *gorm.DB) StockLogRepository { + return &StockLogRepositoryImpl{ + BaseRepositoryImpl: repository.NewBaseRepository[entity.StockLog](db), + } +} + +func (r *StockLogRepositoryImpl) GetByFlaggable(ctx context.Context, logType string, logId uint) ([]*entity.StockLog, error) { + var stockLogs []*entity.StockLog + + err := r.DB().WithContext(ctx). + Where("log_type = ? AND log_id = ?", logType, logId). + Preload("ProductWarehouse"). + Preload("ProductWarehouse.Product"). + Preload("ProductWarehouse.Warehouse"). + Order("created_at DESC"). + Find(&stockLogs).Error + + if err != nil { + return nil, err + } + + return stockLogs, nil +} + +func (r *StockLogRepositoryImpl) GetByProductWarehouse(ctx context.Context, productWarehouseId uint, limit int) ([]*entity.StockLog, error) { + var stockLogs []*entity.StockLog + + query := r.DB().WithContext(ctx). + Where("product_warehouse_id = ?", productWarehouseId). + Preload("ProductWarehouse"). + Preload("ProductWarehouse.Product"). + Preload("ProductWarehouse.Warehouse"). + Order("created_at DESC") + + if limit > 0 { + query = query.Limit(limit) + } + + err := query.Find(&stockLogs).Error + if err != nil { + return nil, err + } + + return stockLogs, nil +} + +func (r *StockLogRepositoryImpl) GetByTransactionType(ctx context.Context, transactionType string, limit int) ([]*entity.StockLog, error) { + var stockLogs []*entity.StockLog + + query := r.DB().WithContext(ctx). + Where("transaction_type = ?", transactionType). + Preload("ProductWarehouse"). + Preload("ProductWarehouse.Product"). + Preload("ProductWarehouse.Warehouse"). + Order("created_at DESC") + + if limit > 0 { + query = query.Limit(limit) + } + + err := query.Find(&stockLogs).Error + if err != nil { + return nil, err + } + + return stockLogs, nil +} diff --git a/internal/route/route.go b/internal/route/route.go index 7acab04a..82b48166 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -9,9 +9,9 @@ import ( "gorm.io/gorm" constants "gitlab.com/mbugroup/lti-api.git/internal/modules/constants" + inventory "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory" master "gitlab.com/mbugroup/lti-api.git/internal/modules/master" users "gitlab.com/mbugroup/lti-api.git/internal/modules/users" - inventory "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory" // MODULE IMPORTS ) @@ -25,7 +25,7 @@ func Routes(app *fiber.App, db *gorm.DB) { users.UserModule{}, master.MasterModule{}, constants.ConstantModule{}, - inventory.InventoryModule{}, + inventory.InventoryModule{}, // MODULE REGISTRY }