Files
Zumri-Backend/migrations/20260903050000-phase-5-inventory-merchandising.js
Sathira Sri Sathara 5643d89236 feat: implement business pricing service and money utilities
- Added `businessPricing.service.js` to handle business customer pricing logic.
- Introduced `money.js` for money parsing, formatting, and calculations.
- Created `pricing.service.js` to manage variant quoting and promotion application.
- Updated validation schemas in `catalogue.schemas.js` and `inventoryMerchandising.schemas.js` for new pricing and inventory features.
- Implemented cron job for inventory reservation expiry in `inventoryReservationExpiry.cron.js`.
- Created database migrations for catalogue and inventory structures.
- Added unit tests for catalogue localization, validation, and inventory merchandising functionalities.
2026-09-03 19:25:13 +05:30

6 lines
4.5 KiB
JavaScript

"use strict";
module.exports={async up(q,S){const ID={type:S.STRING,allowNull:false,primaryKey:true},STRING={type:S.STRING},DATE={type:S.DATE},INT={type:S.INTEGER,allowNull:false,defaultValue:0},DEC={type:S.DECIMAL(15,2)},timestamps={createdAt:{type:S.DATE,allowNull:false},updatedAt:{type:S.DATE,allowNull:false}};await q.createTable("warehouses",{id:ID,code:{type:S.STRING(50),allowNull:false,unique:true},name:{type:S.STRING(150),allowNull:false},status:{type:S.ENUM("ACTIVE","INACTIVE"),allowNull:false},is_default:{type:S.BOOLEAN,allowNull:false,defaultValue:false},timezone:{type:S.STRING(80),allowNull:false},address_line_1:STRING,address_line_2:STRING,city:STRING,province:STRING,postal_code:STRING,country_code:{type:S.STRING(2),allowNull:false},...timestamps});await q.createTable("inventory_balances",{id:ID,warehouse_id:{type:S.STRING,allowNull:false},variant_id:{type:S.STRING,allowNull:false},on_hand:INT,reserved:INT,low_stock_threshold:INT,...timestamps});await q.addConstraint("inventory_balances",{fields:["warehouse_id","variant_id"],type:"unique",name:"uq_inventory_balance_warehouse_variant"});
await q.createTable("inventory_transactions",{id:ID,event_id:{type:S.STRING,allowNull:false,unique:true},warehouse_id:str(false),variant_id:str(false),type:str(false),quantity_delta:INT,reserved_delta:INT,reference_type:STRING,reference_id:STRING,reason:{type:S.STRING(500)},actor_user_id:STRING,request_id:STRING,metadata:{type:S.JSON},occurred_at:{type:S.DATE,allowNull:false},createdAt:{type:S.DATE,allowNull:false}});await q.createTable("inventory_reservations",{id:ID,reservation_key:{type:S.STRING(160),allowNull:false,unique:true},warehouse_id:str(false),variant_id:str(false),quantity:{type:S.INTEGER,allowNull:false},status:str(false),reference_type:STRING,reference_id:STRING,user_id:STRING,expires_at:{type:S.DATE,allowNull:false},released_at:DATE,consumed_at:DATE,...timestamps});await q.createTable("inventory_transfers",{id:ID,event_id:{type:S.STRING,allowNull:false,unique:true},transfer_number:{type:S.STRING(80),allowNull:false,unique:true},source_warehouse_id:str(false),destination_warehouse_id:str(false),variant_id:str(false),quantity:{type:S.INTEGER,allowNull:false},status:str(false),created_by:str(false),completed_by:str(false),completed_at:{type:S.DATE,allowNull:false},...timestamps});
await q.createTable("business_tiers",{id:ID,code:{type:S.STRING(60),allowNull:false,unique:true},name:str(false),description:{type:S.TEXT},status:str(false),sort_order:INT,...timestamps});await q.addColumn("business_customers","business_tier_id",STRING);await q.createTable("variant_business_prices",{id:ID,variant_id:str(false),business_tier_id:STRING,business_customer_id:STRING,currency:{type:S.STRING(3),allowNull:false},price:{...DEC,allowNull:false},minimum_quantity:{type:S.INTEGER,allowNull:false},status:str(false),starts_at:DATE,ends_at:DATE,...timestamps});await q.createTable("business_price_tiers",{id:ID,business_price_id:str(false),min_quantity:{type:S.INTEGER,allowNull:false},max_quantity:{type:S.INTEGER},unit_price:{...DEC,allowNull:false},...timestamps});
await q.createTable("promotions",{id:ID,name:str(false),type:str(false),status:str(false),starts_at:DATE,ends_at:DATE,priority:INT,stackable:{type:S.BOOLEAN,allowNull:false},discount_percent:{type:S.DECIMAL(5,2)},discount_amount:DEC,fixed_price:DEC,minimum_subtotal:DEC,minimum_quantity:{type:S.INTEGER},customer_type:STRING,created_by:str(false),updated_by:STRING,...timestamps});await q.createTable("promotion_targets",{id:ID,promotion_id:str(false),target_type:str(false),target_id:STRING,...timestamps});await q.createTable("coupons",{id:ID,code:{type:S.STRING(50),allowNull:false,unique:true},promotion_id:str(false),status:str(false),starts_at:DATE,expires_at:DATE,max_uses:{type:S.INTEGER},max_uses_per_user:{type:S.INTEGER},...timestamps});await q.createTable("banners",{id:ID,placement:str(false),status:str(false),starts_at:DATE,ends_at:DATE,image_upload_id:{type:S.INTEGER,allowNull:false},mobile_image_upload_id:{type:S.INTEGER},target_url:STRING,audience_type:str(false),business_tier_id:STRING,sort_order:INT,created_by:str(false),updated_by:STRING,...timestamps});await q.createTable("banner_translations",{id:ID,banner_id:str(false),locale:{type:S.STRING(5),allowNull:false},headline:STRING,subheadline:{type:S.TEXT},cta_text:STRING,alt_text:STRING,...timestamps});await q.addConstraint("banner_translations",{fields:["banner_id","locale"],type:"unique",name:"uq_banner_translation_locale"});},async down(){throw new Error("Phase 5 migration is forward-only; restore from a verified backup instead");}};