GWM : plan and warehouse merged as locations
This commit is contained in:
parent
58d281c6b9
commit
f04b917b76
@ -171,8 +171,9 @@ Each sub-master supports: `GET /` (list), `GET /:id`, `POST /`, `PUT /:id`, `DEL
|
|||||||
| [x] | Asset Categories | `/masters/asset-categories` |
|
| [x] | Asset Categories | `/masters/asset-categories` |
|
||||||
| [x] | Departments | `/masters/departments` |
|
| [x] | Departments | `/masters/departments` |
|
||||||
| [x] | Designations | `/masters/designations` |
|
| [x] | Designations | `/masters/designations` |
|
||||||
| [x] | Plants | `/masters/plants` |
|
| [x] | Locations | `/masters/locations` | Unified plants + warehouses (`type`: `plant` \| `warehouse`) |
|
||||||
| [x] | Warehouses | `/masters/warehouses` |
|
| [x] | Plants | `/masters/plants` | Alias — plant locations only |
|
||||||
|
| [x] | Warehouses | `/masters/warehouses` | Alias — warehouse locations only |
|
||||||
| [x] | Document Series | `/masters/document-series` |
|
| [x] | Document Series | `/masters/document-series` |
|
||||||
|
|
||||||
**Masters total:** 14 modules × 5 endpoints = **70 APIs** [x]
|
**Masters total:** 14 modules × 5 endpoints = **70 APIs** [x]
|
||||||
|
|||||||
@ -171,13 +171,13 @@ model asset_transfers {
|
|||||||
created_at DateTime @default(now()) @db.Timestamptz(6)
|
created_at DateTime @default(now()) @db.Timestamptz(6)
|
||||||
assets assets @relation(fields: [asset_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
assets assets @relation(fields: [asset_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
||||||
departments_asset_transfers_from_department_idTodepartments departments? @relation("asset_transfers_from_department_idTodepartments", fields: [from_department_id], references: [id], onUpdate: NoAction)
|
departments_asset_transfers_from_department_idTodepartments departments? @relation("asset_transfers_from_department_idTodepartments", fields: [from_department_id], references: [id], onUpdate: NoAction)
|
||||||
plants_asset_transfers_from_plant_idToplants plants? @relation("asset_transfers_from_plant_idToplants", fields: [from_plant_id], references: [id], onUpdate: NoAction)
|
from_plant locations? @relation("TransferFromPlant", fields: [from_plant_id], references: [id], onUpdate: NoAction)
|
||||||
users_asset_transfers_from_user_idTousers users? @relation("asset_transfers_from_user_idTousers", fields: [from_user_id], references: [id], onUpdate: NoAction)
|
users_asset_transfers_from_user_idTousers users? @relation("asset_transfers_from_user_idTousers", fields: [from_user_id], references: [id], onUpdate: NoAction)
|
||||||
warehouses_asset_transfers_from_warehouse_idTowarehouses warehouses? @relation("asset_transfers_from_warehouse_idTowarehouses", fields: [from_warehouse_id], references: [id], onUpdate: NoAction)
|
from_warehouse locations? @relation("TransferFromWarehouse", fields: [from_warehouse_id], references: [id], onUpdate: NoAction)
|
||||||
departments_asset_transfers_to_department_idTodepartments departments? @relation("asset_transfers_to_department_idTodepartments", fields: [to_department_id], references: [id], onUpdate: NoAction)
|
departments_asset_transfers_to_department_idTodepartments departments? @relation("asset_transfers_to_department_idTodepartments", fields: [to_department_id], references: [id], onUpdate: NoAction)
|
||||||
plants_asset_transfers_to_plant_idToplants plants? @relation("asset_transfers_to_plant_idToplants", fields: [to_plant_id], references: [id], onUpdate: NoAction)
|
to_plant locations? @relation("TransferToPlant", fields: [to_plant_id], references: [id], onUpdate: NoAction)
|
||||||
users_asset_transfers_to_user_idTousers users? @relation("asset_transfers_to_user_idTousers", fields: [to_user_id], references: [id], onUpdate: NoAction)
|
users_asset_transfers_to_user_idTousers users? @relation("asset_transfers_to_user_idTousers", fields: [to_user_id], references: [id], onUpdate: NoAction)
|
||||||
warehouses_asset_transfers_to_warehouse_idTowarehouses warehouses? @relation("asset_transfers_to_warehouse_idTowarehouses", fields: [to_warehouse_id], references: [id], onUpdate: NoAction)
|
to_warehouse locations? @relation("TransferToWarehouse", fields: [to_warehouse_id], references: [id], onUpdate: NoAction)
|
||||||
users_asset_transfers_transferred_byTousers users? @relation("asset_transfers_transferred_byTousers", fields: [transferred_by], references: [id], onUpdate: NoAction)
|
users_asset_transfers_transferred_byTousers users? @relation("asset_transfers_transferred_byTousers", fields: [transferred_by], references: [id], onUpdate: NoAction)
|
||||||
|
|
||||||
@@index([asset_id], map: "idx_asset_transfers_asset_id")
|
@@index([asset_id], map: "idx_asset_transfers_asset_id")
|
||||||
@ -232,11 +232,11 @@ model assets {
|
|||||||
departments departments? @relation(fields: [department_id], references: [id], onUpdate: NoAction)
|
departments departments? @relation(fields: [department_id], references: [id], onUpdate: NoAction)
|
||||||
grn grn? @relation(fields: [grn_id], references: [id], onUpdate: NoAction)
|
grn grn? @relation(fields: [grn_id], references: [id], onUpdate: NoAction)
|
||||||
grn_items grn_items? @relation(fields: [grn_item_id], references: [id], onUpdate: NoAction)
|
grn_items grn_items? @relation(fields: [grn_item_id], references: [id], onUpdate: NoAction)
|
||||||
plants plants @relation(fields: [plant_id], references: [id], onUpdate: NoAction)
|
plant locations @relation("AssetPlantLocation", fields: [plant_id], references: [id], onUpdate: NoAction)
|
||||||
purchase_orders purchase_orders? @relation(fields: [po_id], references: [id], onUpdate: NoAction)
|
purchase_orders purchase_orders? @relation(fields: [po_id], references: [id], onUpdate: NoAction)
|
||||||
users_assets_updated_byTousers users? @relation("assets_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
users_assets_updated_byTousers users? @relation("assets_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
||||||
vendors_assets_vendor_idTovendors vendors? @relation("assets_vendor_idTovendors", fields: [vendor_id], references: [id], onUpdate: NoAction)
|
vendors_assets_vendor_idTovendors vendors? @relation("assets_vendor_idTovendors", fields: [vendor_id], references: [id], onUpdate: NoAction)
|
||||||
warehouses warehouses? @relation(fields: [warehouse_id], references: [id], onUpdate: NoAction)
|
warehouse locations? @relation("AssetWarehouseLocation", fields: [warehouse_id], references: [id], onUpdate: NoAction)
|
||||||
|
|
||||||
@@index([asset_category_id], map: "idx_assets_category_id")
|
@@index([asset_category_id], map: "idx_assets_category_id")
|
||||||
@@index([department_id], map: "idx_assets_dept_id")
|
@@index([department_id], map: "idx_assets_dept_id")
|
||||||
@ -375,7 +375,7 @@ model grn {
|
|||||||
users_grn_received_byTousers users? @relation("grn_received_byTousers", fields: [received_by], references: [id], onUpdate: NoAction)
|
users_grn_received_byTousers users? @relation("grn_received_byTousers", fields: [received_by], references: [id], onUpdate: NoAction)
|
||||||
users_grn_updated_byTousers users? @relation("grn_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
users_grn_updated_byTousers users? @relation("grn_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
||||||
vendors vendors @relation(fields: [vendor_id], references: [id], onUpdate: NoAction)
|
vendors vendors @relation(fields: [vendor_id], references: [id], onUpdate: NoAction)
|
||||||
warehouses warehouses @relation(fields: [warehouse_id], references: [id], onUpdate: NoAction)
|
warehouse locations @relation("GRNWarehouseLocation", fields: [warehouse_id], references: [id], onUpdate: NoAction)
|
||||||
grn_attachments grn_attachments[]
|
grn_attachments grn_attachments[]
|
||||||
grn_items grn_items[]
|
grn_items grn_items[]
|
||||||
|
|
||||||
@ -576,30 +576,42 @@ model permissions {
|
|||||||
@@unique([module_id, action])
|
@@unique([module_id, action])
|
||||||
}
|
}
|
||||||
|
|
||||||
model plants {
|
model locations {
|
||||||
id BigInt @id @default(autoincrement())
|
id BigInt @id @default(autoincrement())
|
||||||
code String @unique @db.VarChar(20)
|
type String @db.VarChar(20)
|
||||||
name String @db.VarChar(150)
|
code String @db.VarChar(30)
|
||||||
gstin String? @db.VarChar(15)
|
name String @db.VarChar(150)
|
||||||
address String?
|
parent_id BigInt?
|
||||||
city String? @db.VarChar(100)
|
gstin String? @db.VarChar(15)
|
||||||
state String? @db.VarChar(100)
|
address String?
|
||||||
pincode String? @db.VarChar(10)
|
city String? @db.VarChar(100)
|
||||||
phone String? @db.VarChar(15)
|
state String? @db.VarChar(100)
|
||||||
is_active Boolean @default(true)
|
pincode String? @db.VarChar(10)
|
||||||
created_by BigInt?
|
phone String? @db.VarChar(15)
|
||||||
updated_by BigInt?
|
location String? @db.VarChar(200)
|
||||||
created_at DateTime @default(now()) @db.Timestamptz(6)
|
is_active Boolean @default(true)
|
||||||
updated_at DateTime @default(now()) @db.Timestamptz(6)
|
created_by BigInt?
|
||||||
deleted_at DateTime? @db.Timestamptz(6)
|
updated_by BigInt?
|
||||||
asset_transfers_asset_transfers_from_plant_idToplants asset_transfers[] @relation("asset_transfers_from_plant_idToplants")
|
created_at DateTime @default(now()) @db.Timestamptz(6)
|
||||||
asset_transfers_asset_transfers_to_plant_idToplants asset_transfers[] @relation("asset_transfers_to_plant_idToplants")
|
updated_at DateTime @default(now()) @db.Timestamptz(6)
|
||||||
assets assets[]
|
deleted_at DateTime? @db.Timestamptz(6)
|
||||||
users_plants_created_byTousers users? @relation("plants_created_byTousers", fields: [created_by], references: [id], onUpdate: NoAction, map: "fk_plants_created_by")
|
transfers_from_plant asset_transfers[] @relation("TransferFromPlant")
|
||||||
users_plants_updated_byTousers users? @relation("plants_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction, map: "fk_plants_updated_by")
|
transfers_to_plant asset_transfers[] @relation("TransferToPlant")
|
||||||
purchase_orders purchase_orders[]
|
transfers_from_wh asset_transfers[] @relation("TransferFromWarehouse")
|
||||||
users_users_plant_idToplants users[] @relation("users_plant_idToplants")
|
transfers_to_wh asset_transfers[] @relation("TransferToWarehouse")
|
||||||
warehouses warehouses[]
|
assets_plant assets[] @relation("AssetPlantLocation")
|
||||||
|
assets_warehouse assets[] @relation("AssetWarehouseLocation")
|
||||||
|
grn_warehouse grn[] @relation("GRNWarehouseLocation")
|
||||||
|
parent locations? @relation("LocationHierarchy", fields: [parent_id], references: [id], onUpdate: NoAction)
|
||||||
|
children locations[] @relation("LocationHierarchy")
|
||||||
|
po_plant purchase_orders[] @relation("POPlantLocation")
|
||||||
|
po_warehouse purchase_orders[] @relation("POWarehouseLocation")
|
||||||
|
users_plant users[] @relation("UserPlantLocation")
|
||||||
|
users_created users? @relation("locations_created_byTousers", fields: [created_by], references: [id], onUpdate: NoAction, map: "fk_locations_created_by")
|
||||||
|
users_updated users? @relation("locations_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction, map: "fk_locations_updated_by")
|
||||||
|
|
||||||
|
@@index([type], map: "idx_locations_type")
|
||||||
|
@@index([parent_id], map: "idx_locations_parent_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
||||||
@ -706,10 +718,10 @@ model purchase_orders {
|
|||||||
purchase_orders purchase_orders? @relation("purchase_ordersTopurchase_orders", fields: [parent_po_id], references: [id], onUpdate: NoAction)
|
purchase_orders purchase_orders? @relation("purchase_ordersTopurchase_orders", fields: [parent_po_id], references: [id], onUpdate: NoAction)
|
||||||
other_purchase_orders purchase_orders[] @relation("purchase_ordersTopurchase_orders")
|
other_purchase_orders purchase_orders[] @relation("purchase_ordersTopurchase_orders")
|
||||||
payment_terms payment_terms? @relation(fields: [payment_term_id], references: [id], onUpdate: NoAction)
|
payment_terms payment_terms? @relation(fields: [payment_term_id], references: [id], onUpdate: NoAction)
|
||||||
plants plants @relation(fields: [plant_id], references: [id], onUpdate: NoAction)
|
plant locations @relation("POPlantLocation", fields: [plant_id], references: [id], onUpdate: NoAction)
|
||||||
users_purchase_orders_updated_byTousers users? @relation("purchase_orders_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
users_purchase_orders_updated_byTousers users? @relation("purchase_orders_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
||||||
vendors vendors @relation(fields: [vendor_id], references: [id], onUpdate: NoAction)
|
vendors vendors @relation(fields: [vendor_id], references: [id], onUpdate: NoAction)
|
||||||
warehouses warehouses? @relation(fields: [warehouse_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
warehouse locations? @relation("POWarehouseLocation", fields: [warehouse_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||||
|
|
||||||
@@index([created_at], map: "idx_po_created_at")
|
@@index([created_at], map: "idx_po_created_at")
|
||||||
@@index([po_date], map: "idx_po_po_date")
|
@@index([po_date], map: "idx_po_po_date")
|
||||||
@ -846,8 +858,8 @@ model users {
|
|||||||
password_reset_tokens password_reset_tokens[]
|
password_reset_tokens password_reset_tokens[]
|
||||||
payment_terms_payment_terms_created_byTousers payment_terms[] @relation("payment_terms_created_byTousers")
|
payment_terms_payment_terms_created_byTousers payment_terms[] @relation("payment_terms_created_byTousers")
|
||||||
payment_terms_payment_terms_updated_byTousers payment_terms[] @relation("payment_terms_updated_byTousers")
|
payment_terms_payment_terms_updated_byTousers payment_terms[] @relation("payment_terms_updated_byTousers")
|
||||||
plants_plants_created_byTousers plants[] @relation("plants_created_byTousers")
|
locations_locations_created_byTousers locations[] @relation("locations_created_byTousers")
|
||||||
plants_plants_updated_byTousers plants[] @relation("plants_updated_byTousers")
|
locations_locations_updated_byTousers locations[] @relation("locations_updated_byTousers")
|
||||||
po_approvals po_approvals[]
|
po_approvals po_approvals[]
|
||||||
po_attachments po_attachments[]
|
po_attachments po_attachments[]
|
||||||
purchase_orders_purchase_orders_created_byTousers purchase_orders[] @relation("purchase_orders_created_byTousers")
|
purchase_orders_purchase_orders_created_byTousers purchase_orders[] @relation("purchase_orders_created_byTousers")
|
||||||
@ -862,7 +874,7 @@ model users {
|
|||||||
other_users_users_created_byTousers users[] @relation("users_created_byTousers")
|
other_users_users_created_byTousers users[] @relation("users_created_byTousers")
|
||||||
departments departments? @relation(fields: [department_id], references: [id], onUpdate: NoAction)
|
departments departments? @relation(fields: [department_id], references: [id], onUpdate: NoAction)
|
||||||
designations designations? @relation(fields: [designation_id], references: [id], onUpdate: NoAction)
|
designations designations? @relation(fields: [designation_id], references: [id], onUpdate: NoAction)
|
||||||
plants_users_plant_idToplants plants? @relation("users_plant_idToplants", fields: [plant_id], references: [id], onUpdate: NoAction)
|
plant_location locations? @relation("UserPlantLocation", fields: [plant_id], references: [id], onUpdate: NoAction)
|
||||||
users_users_reporting_toTousers users? @relation("users_reporting_toTousers", fields: [reporting_to], references: [id], onUpdate: NoAction)
|
users_users_reporting_toTousers users? @relation("users_reporting_toTousers", fields: [reporting_to], references: [id], onUpdate: NoAction)
|
||||||
other_users_users_reporting_toTousers users[] @relation("users_reporting_toTousers")
|
other_users_users_reporting_toTousers users[] @relation("users_reporting_toTousers")
|
||||||
users_users_updated_byTousers users? @relation("users_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
users_users_updated_byTousers users? @relation("users_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
||||||
@ -877,8 +889,6 @@ model users {
|
|||||||
vendor_item_mapping_vendor_item_mapping_updated_byTousers vendor_item_mapping[] @relation("vendor_item_mapping_updated_byTousers")
|
vendor_item_mapping_vendor_item_mapping_updated_byTousers vendor_item_mapping[] @relation("vendor_item_mapping_updated_byTousers")
|
||||||
vendors_vendors_created_byTousers vendors[] @relation("vendors_created_byTousers")
|
vendors_vendors_created_byTousers vendors[] @relation("vendors_created_byTousers")
|
||||||
vendors_vendors_updated_byTousers vendors[] @relation("vendors_updated_byTousers")
|
vendors_vendors_updated_byTousers vendors[] @relation("vendors_updated_byTousers")
|
||||||
warehouses_warehouses_created_byTousers warehouses[] @relation("warehouses_created_byTousers")
|
|
||||||
warehouses_warehouses_updated_byTousers warehouses[] @relation("warehouses_updated_byTousers")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
||||||
@ -995,25 +1005,3 @@ model vendors {
|
|||||||
|
|
||||||
@@index([vendor_name(ops: raw("gin_trgm_ops"))], map: "idx_vendors_name_trgm", type: Gin)
|
@@index([vendor_name(ops: raw("gin_trgm_ops"))], map: "idx_vendors_name_trgm", type: Gin)
|
||||||
}
|
}
|
||||||
|
|
||||||
model warehouses {
|
|
||||||
id BigInt @id @default(autoincrement())
|
|
||||||
code String @unique @db.VarChar(30)
|
|
||||||
name String @db.VarChar(150)
|
|
||||||
plant_id BigInt
|
|
||||||
location String? @db.VarChar(200)
|
|
||||||
is_active Boolean @default(true)
|
|
||||||
created_by BigInt?
|
|
||||||
updated_by BigInt?
|
|
||||||
created_at DateTime @default(now()) @db.Timestamptz(6)
|
|
||||||
updated_at DateTime @default(now()) @db.Timestamptz(6)
|
|
||||||
deleted_at DateTime? @db.Timestamptz(6)
|
|
||||||
asset_transfers_asset_transfers_from_warehouse_idTowarehouses asset_transfers[] @relation("asset_transfers_from_warehouse_idTowarehouses")
|
|
||||||
asset_transfers_asset_transfers_to_warehouse_idTowarehouses asset_transfers[] @relation("asset_transfers_to_warehouse_idTowarehouses")
|
|
||||||
assets assets[]
|
|
||||||
grn grn[]
|
|
||||||
purchase_orders purchase_orders[]
|
|
||||||
users_warehouses_created_byTousers users? @relation("warehouses_created_byTousers", fields: [created_by], references: [id], onUpdate: NoAction)
|
|
||||||
plants plants @relation(fields: [plant_id], references: [id], onUpdate: NoAction)
|
|
||||||
users_warehouses_updated_byTousers users? @relation("warehouses_updated_byTousers", fields: [updated_by], references: [id], onUpdate: NoAction)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ CREATE VIEW v_asset_expiry_alerts AS
|
|||||||
FROM asset_amc_contracts amc
|
FROM asset_amc_contracts amc
|
||||||
JOIN assets a ON a.id = amc.asset_id
|
JOIN assets a ON a.id = amc.asset_id
|
||||||
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
||||||
JOIN plants p ON p.id = a.plant_id
|
JOIN locations p ON p.id = a.plant_id AND p.type = 'plant'
|
||||||
LEFT JOIN departments d ON d.id = a.department_id
|
LEFT JOIN departments d ON d.id = a.department_id
|
||||||
LEFT JOIN vendors v ON v.id = amc.vendor_id
|
LEFT JOIN vendors v ON v.id = amc.vendor_id
|
||||||
WHERE amc.is_active = TRUE AND amc.deleted_at IS NULL AND a.deleted_at IS NULL
|
WHERE amc.is_active = TRUE AND amc.deleted_at IS NULL AND a.deleted_at IS NULL
|
||||||
@ -48,7 +48,7 @@ UNION ALL
|
|||||||
FROM asset_insurance_policies ins
|
FROM asset_insurance_policies ins
|
||||||
JOIN assets a ON a.id = ins.asset_id
|
JOIN assets a ON a.id = ins.asset_id
|
||||||
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
||||||
JOIN plants p ON p.id = a.plant_id
|
JOIN locations p ON p.id = a.plant_id AND p.type = 'plant'
|
||||||
LEFT JOIN departments d ON d.id = a.department_id
|
LEFT JOIN departments d ON d.id = a.department_id
|
||||||
WHERE ins.is_active = TRUE AND ins.deleted_at IS NULL AND a.deleted_at IS NULL
|
WHERE ins.is_active = TRUE AND ins.deleted_at IS NULL AND a.deleted_at IS NULL
|
||||||
AND (ins.policy_end_date - CURRENT_DATE) <= 90
|
AND (ins.policy_end_date - CURRENT_DATE) <= 90
|
||||||
@ -67,7 +67,7 @@ UNION ALL
|
|||||||
END
|
END
|
||||||
FROM assets a
|
FROM assets a
|
||||||
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
||||||
JOIN plants p ON p.id = a.plant_id
|
JOIN locations p ON p.id = a.plant_id AND p.type = 'plant'
|
||||||
LEFT JOIN departments d ON d.id = a.department_id
|
LEFT JOIN departments d ON d.id = a.department_id
|
||||||
WHERE a.warranty_expiry_date IS NOT NULL AND a.deleted_at IS NULL
|
WHERE a.warranty_expiry_date IS NOT NULL AND a.deleted_at IS NULL
|
||||||
AND (a.warranty_expiry_date - CURRENT_DATE) <= 90;
|
AND (a.warranty_expiry_date - CURRENT_DATE) <= 90;
|
||||||
@ -96,7 +96,7 @@ SELECT
|
|||||||
amc.end_date AS amc_end_date
|
amc.end_date AS amc_end_date
|
||||||
FROM assets a
|
FROM assets a
|
||||||
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
JOIN asset_categories ac_cat ON ac_cat.id = a.asset_category_id
|
||||||
JOIN plants p ON p.id = a.plant_id
|
JOIN locations p ON p.id = a.plant_id AND p.type = 'plant'
|
||||||
LEFT JOIN departments d ON d.id = a.department_id
|
LEFT JOIN departments d ON d.id = a.department_id
|
||||||
JOIN LATERAL (
|
JOIN LATERAL (
|
||||||
SELECT * FROM asset_service_visits
|
SELECT * FROM asset_service_visits
|
||||||
@ -145,9 +145,9 @@ SELECT
|
|||||||
a.created_at
|
a.created_at
|
||||||
FROM assets a
|
FROM assets a
|
||||||
JOIN asset_categories ac ON ac.id = a.asset_category_id
|
JOIN asset_categories ac ON ac.id = a.asset_category_id
|
||||||
JOIN plants p ON p.id = a.plant_id
|
JOIN locations p ON p.id = a.plant_id AND p.type = 'plant'
|
||||||
LEFT JOIN departments d ON d.id = a.department_id
|
LEFT JOIN departments d ON d.id = a.department_id
|
||||||
LEFT JOIN warehouses w ON w.id = a.warehouse_id
|
LEFT JOIN locations w ON w.id = a.warehouse_id AND w.type = 'warehouse'
|
||||||
LEFT JOIN users u ON u.id = a.assigned_to_user_id
|
LEFT JOIN users u ON u.id = a.assigned_to_user_id
|
||||||
LEFT JOIN vendors v ON v.id = a.vendor_id
|
LEFT JOIN vendors v ON v.id = a.vendor_id
|
||||||
LEFT JOIN asset_amc_contracts amc
|
LEFT JOIN asset_amc_contracts amc
|
||||||
|
|||||||
148
scripts/patch-locations-merge.sql
Normal file
148
scripts/patch-locations-merge.sql
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
-- Merge plants + warehouses into locations (type: plant | warehouse)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS locations (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
type VARCHAR(20) NOT NULL CHECK (type IN ('plant', 'warehouse')),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(150) NOT NULL,
|
||||||
|
parent_id BIGINT REFERENCES locations(id),
|
||||||
|
gstin VARCHAR(15),
|
||||||
|
address TEXT,
|
||||||
|
city VARCHAR(100),
|
||||||
|
state VARCHAR(100),
|
||||||
|
pincode VARCHAR(10),
|
||||||
|
phone VARCHAR(15),
|
||||||
|
location VARCHAR(200),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
created_by BIGINT,
|
||||||
|
updated_by BIGINT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_locations_code_active
|
||||||
|
ON locations (code)
|
||||||
|
WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_locations_type ON locations (type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_locations_parent_id ON locations (parent_id);
|
||||||
|
|
||||||
|
-- Migrate plants (preserve ids) when legacy table exists
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public' AND table_name = 'plants'
|
||||||
|
) THEN
|
||||||
|
INSERT INTO locations (
|
||||||
|
id, type, code, name, gstin, address, city, state, pincode, phone,
|
||||||
|
is_active, created_by, updated_by, created_at, updated_at, deleted_at
|
||||||
|
)
|
||||||
|
OVERRIDING SYSTEM VALUE
|
||||||
|
SELECT
|
||||||
|
id, 'plant', code, name, gstin, address, city, state, pincode, phone,
|
||||||
|
is_active, created_by, updated_by, created_at, updated_at, deleted_at
|
||||||
|
FROM plants
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Migrate warehouses (preserve ids; plant_id -> parent_id)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public' AND table_name = 'warehouses'
|
||||||
|
) THEN
|
||||||
|
INSERT INTO locations (
|
||||||
|
id, type, code, name, parent_id, location,
|
||||||
|
is_active, created_by, updated_by, created_at, updated_at, deleted_at
|
||||||
|
)
|
||||||
|
OVERRIDING SYSTEM VALUE
|
||||||
|
SELECT
|
||||||
|
id, 'warehouse', code, name, plant_id, location,
|
||||||
|
is_active, created_by, updated_by, created_at, updated_at, deleted_at
|
||||||
|
FROM warehouses
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
SELECT setval(
|
||||||
|
pg_get_serial_sequence('locations', 'id'),
|
||||||
|
GREATEST(COALESCE((SELECT MAX(id) FROM locations), 1), 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Drop FKs referencing plants / warehouses
|
||||||
|
DO $$
|
||||||
|
DECLARE r RECORD;
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'plants') THEN
|
||||||
|
FOR r IN
|
||||||
|
SELECT c.conname, c.conrelid::regclass AS tbl
|
||||||
|
FROM pg_constraint c
|
||||||
|
WHERE c.contype = 'f' AND c.confrelid = 'public.plants'::regclass
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %I', r.tbl, r.conname);
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'warehouses') THEN
|
||||||
|
FOR r IN
|
||||||
|
SELECT c.conname, c.conrelid::regclass AS tbl
|
||||||
|
FROM pg_constraint c
|
||||||
|
WHERE c.contype = 'f' AND c.confrelid = 'public.warehouses'::regclass
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %I', r.tbl, r.conname);
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Point existing FK columns to locations (idempotent)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_plant_id_fkey') THEN
|
||||||
|
ALTER TABLE users ADD CONSTRAINT users_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'assets_plant_id_fkey') THEN
|
||||||
|
ALTER TABLE assets ADD CONSTRAINT assets_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'assets_warehouse_id_fkey') THEN
|
||||||
|
ALTER TABLE assets ADD CONSTRAINT assets_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'purchase_orders_plant_id_fkey') THEN
|
||||||
|
ALTER TABLE purchase_orders ADD CONSTRAINT purchase_orders_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'purchase_orders_warehouse_id_fkey') THEN
|
||||||
|
ALTER TABLE purchase_orders ADD CONSTRAINT purchase_orders_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'grn_warehouse_id_fkey') THEN
|
||||||
|
ALTER TABLE grn ADD CONSTRAINT grn_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_from_plant_id_fkey') THEN
|
||||||
|
ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_from_plant_id_fkey FOREIGN KEY (from_plant_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_to_plant_id_fkey') THEN
|
||||||
|
ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_to_plant_id_fkey FOREIGN KEY (to_plant_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_from_warehouse_id_fkey') THEN
|
||||||
|
ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_from_warehouse_id_fkey FOREIGN KEY (from_warehouse_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_to_warehouse_id_fkey') THEN
|
||||||
|
ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_to_warehouse_id_fkey FOREIGN KEY (to_warehouse_id) REFERENCES locations(id);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Drop views that depend on plants/warehouses, then drop legacy tables
|
||||||
|
DROP VIEW IF EXISTS v_purchase_orders CASCADE;
|
||||||
|
DROP VIEW IF EXISTS v_grn CASCADE;
|
||||||
|
DROP VIEW IF EXISTS v_assets CASCADE;
|
||||||
|
DROP VIEW IF EXISTS v_asset_expiry_alerts CASCADE;
|
||||||
|
DROP VIEW IF EXISTS v_asset_next_service CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS warehouses;
|
||||||
|
DROP TABLE IF EXISTS plants;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
33
scripts/patch-locations-views.sql
Normal file
33
scripts/patch-locations-views.sql
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
-- Recreate PO/GRN views after locations merge
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS v_purchase_orders CASCADE;
|
||||||
|
DROP VIEW IF EXISTS v_grn CASCADE;
|
||||||
|
|
||||||
|
CREATE VIEW v_purchase_orders AS
|
||||||
|
SELECT
|
||||||
|
po.*,
|
||||||
|
pl.code AS plant_code,
|
||||||
|
pl.name AS plant_name,
|
||||||
|
wh.code AS warehouse_code,
|
||||||
|
wh.name AS warehouse_name,
|
||||||
|
v.vendor_code,
|
||||||
|
v.vendor_name
|
||||||
|
FROM purchase_orders po
|
||||||
|
JOIN locations pl ON pl.id = po.plant_id AND pl.type = 'plant'
|
||||||
|
LEFT JOIN locations wh ON wh.id = po.warehouse_id AND wh.type = 'warehouse'
|
||||||
|
JOIN vendors v ON v.id = po.vendor_id
|
||||||
|
WHERE po.deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE VIEW v_grn AS
|
||||||
|
SELECT
|
||||||
|
g.*,
|
||||||
|
po.po_number,
|
||||||
|
v.vendor_code,
|
||||||
|
v.vendor_name,
|
||||||
|
wh.code AS warehouse_code,
|
||||||
|
wh.name AS warehouse_name
|
||||||
|
FROM grn g
|
||||||
|
JOIN purchase_orders po ON po.id = g.po_id
|
||||||
|
JOIN vendors v ON v.id = g.vendor_id
|
||||||
|
JOIN locations wh ON wh.id = g.warehouse_id AND wh.type = 'warehouse'
|
||||||
|
WHERE g.deleted_at IS NULL;
|
||||||
@ -3,8 +3,7 @@ const ApiError = require('../../utils/ApiError');
|
|||||||
|
|
||||||
const SOFT_DELETE_TABLES = new Set([
|
const SOFT_DELETE_TABLES = new Set([
|
||||||
'vendors',
|
'vendors',
|
||||||
'plants',
|
'locations',
|
||||||
'warehouses',
|
|
||||||
'departments',
|
'departments',
|
||||||
'users',
|
'users',
|
||||||
'purchase_orders',
|
'purchase_orders',
|
||||||
|
|||||||
@ -4,12 +4,12 @@ const auditLog = require('../../utils/auditLog');
|
|||||||
const { getPagination } = require('../../utils/pagination');
|
const { getPagination } = require('../../utils/pagination');
|
||||||
const { nextDocumentNumber } = require('../../utils/generateCode');
|
const { nextDocumentNumber } = require('../../utils/generateCode');
|
||||||
const { DISPOSAL_STATUSES } = require('./assets.constants');
|
const { DISPOSAL_STATUSES } = require('./assets.constants');
|
||||||
|
const { assertPlant, assertWarehouse } = require('../../utils/locations');
|
||||||
const repository = require('./assets.repository');
|
const repository = require('./assets.repository');
|
||||||
|
|
||||||
const SOFT_DELETE_TABLES = new Set([
|
const SOFT_DELETE_TABLES = new Set([
|
||||||
'vendors',
|
'vendors',
|
||||||
'plants',
|
'locations',
|
||||||
'warehouses',
|
|
||||||
'departments',
|
'departments',
|
||||||
'users',
|
'users',
|
||||||
'purchase_orders',
|
'purchase_orders',
|
||||||
@ -20,9 +20,9 @@ const SOFT_DELETE_TABLES = new Set([
|
|||||||
|
|
||||||
const assetInclude = {
|
const assetInclude = {
|
||||||
asset_categories: { select: { id: true, code: true, name: true, code_prefix: true } },
|
asset_categories: { select: { id: true, code: true, name: true, code_prefix: true } },
|
||||||
plants: { select: { id: true, code: true, name: true } },
|
plant: { select: { id: true, code: true, name: true } },
|
||||||
departments: { select: { id: true, name: true } },
|
departments: { select: { id: true, name: true } },
|
||||||
warehouses: { select: { id: true, code: true, name: true } },
|
warehouse: { select: { id: true, code: true, name: true } },
|
||||||
users_assets_assigned_to_user_idTousers: {
|
users_assets_assigned_to_user_idTousers: {
|
||||||
select: { id: true, full_name: true, employee_code: true },
|
select: { id: true, full_name: true, employee_code: true },
|
||||||
},
|
},
|
||||||
@ -38,18 +38,14 @@ const assetDetailInclude = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const transferInclude = {
|
const transferInclude = {
|
||||||
plants_asset_transfers_from_plant_idToplants: { select: { id: true, code: true, name: true } },
|
from_plant: { select: { id: true, code: true, name: true } },
|
||||||
plants_asset_transfers_to_plant_idToplants: { select: { id: true, code: true, name: true } },
|
to_plant: { select: { id: true, code: true, name: true } },
|
||||||
departments_asset_transfers_from_department_idTodepartments: { select: { id: true, name: true } },
|
departments_asset_transfers_from_department_idTodepartments: { select: { id: true, name: true } },
|
||||||
departments_asset_transfers_to_department_idTodepartments: { select: { id: true, name: true } },
|
departments_asset_transfers_to_department_idTodepartments: { select: { id: true, name: true } },
|
||||||
users_asset_transfers_from_user_idTousers: { select: { id: true, full_name: true } },
|
users_asset_transfers_from_user_idTousers: { select: { id: true, full_name: true } },
|
||||||
users_asset_transfers_to_user_idTousers: { select: { id: true, full_name: true } },
|
users_asset_transfers_to_user_idTousers: { select: { id: true, full_name: true } },
|
||||||
warehouses_asset_transfers_from_warehouse_idTowarehouses: {
|
from_warehouse: { select: { id: true, code: true, name: true } },
|
||||||
select: { id: true, code: true, name: true },
|
to_warehouse: { select: { id: true, code: true, name: true } },
|
||||||
},
|
|
||||||
warehouses_asset_transfers_to_warehouse_idTowarehouses: {
|
|
||||||
select: { id: true, code: true, name: true },
|
|
||||||
},
|
|
||||||
users_asset_transfers_transferred_byTousers: { select: { id: true, full_name: true } },
|
users_asset_transfers_transferred_byTousers: { select: { id: true, full_name: true } },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,9 +61,9 @@ const sanitizeAsset = (asset) => {
|
|||||||
if (!asset) return null;
|
if (!asset) return null;
|
||||||
const {
|
const {
|
||||||
asset_categories,
|
asset_categories,
|
||||||
plants,
|
plant,
|
||||||
departments,
|
departments,
|
||||||
warehouses,
|
warehouse,
|
||||||
users_assets_assigned_to_user_idTousers,
|
users_assets_assigned_to_user_idTousers,
|
||||||
vendors_assets_vendor_idTovendors,
|
vendors_assets_vendor_idTovendors,
|
||||||
purchase_orders,
|
purchase_orders,
|
||||||
@ -80,9 +76,9 @@ const sanitizeAsset = (asset) => {
|
|||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
asset_category: asset_categories || null,
|
asset_category: asset_categories || null,
|
||||||
plant: plants || null,
|
plant: plant || null,
|
||||||
department: departments || null,
|
department: departments || null,
|
||||||
warehouse: warehouses || null,
|
warehouse: warehouse || null,
|
||||||
assigned_to_user: users_assets_assigned_to_user_idTousers || null,
|
assigned_to_user: users_assets_assigned_to_user_idTousers || null,
|
||||||
vendor: vendors_assets_vendor_idTovendors || null,
|
vendor: vendors_assets_vendor_idTovendors || null,
|
||||||
purchase_order: purchase_orders || null,
|
purchase_order: purchase_orders || null,
|
||||||
@ -90,9 +86,7 @@ const sanitizeAsset = (asset) => {
|
|||||||
created_by_user: users_assets_created_byTousers || null,
|
created_by_user: users_assets_created_byTousers || null,
|
||||||
updated_by_user: users_assets_updated_byTousers || null,
|
updated_by_user: users_assets_updated_byTousers || null,
|
||||||
asset_categories: undefined,
|
asset_categories: undefined,
|
||||||
plants: undefined,
|
|
||||||
departments: undefined,
|
departments: undefined,
|
||||||
warehouses: undefined,
|
|
||||||
users_assets_assigned_to_user_idTousers: undefined,
|
users_assets_assigned_to_user_idTousers: undefined,
|
||||||
vendors_assets_vendor_idTovendors: undefined,
|
vendors_assets_vendor_idTovendors: undefined,
|
||||||
purchase_orders: undefined,
|
purchase_orders: undefined,
|
||||||
@ -104,28 +98,28 @@ const sanitizeAsset = (asset) => {
|
|||||||
const sanitizeTransfer = (row) => {
|
const sanitizeTransfer = (row) => {
|
||||||
if (!row) return null;
|
if (!row) return null;
|
||||||
const {
|
const {
|
||||||
plants_asset_transfers_from_plant_idToplants,
|
from_plant,
|
||||||
plants_asset_transfers_to_plant_idToplants,
|
to_plant,
|
||||||
departments_asset_transfers_from_department_idTodepartments,
|
departments_asset_transfers_from_department_idTodepartments,
|
||||||
departments_asset_transfers_to_department_idTodepartments,
|
departments_asset_transfers_to_department_idTodepartments,
|
||||||
users_asset_transfers_from_user_idTousers,
|
users_asset_transfers_from_user_idTousers,
|
||||||
users_asset_transfers_to_user_idTousers,
|
users_asset_transfers_to_user_idTousers,
|
||||||
warehouses_asset_transfers_from_warehouse_idTowarehouses,
|
from_warehouse,
|
||||||
warehouses_asset_transfers_to_warehouse_idTowarehouses,
|
to_warehouse,
|
||||||
users_asset_transfers_transferred_byTousers,
|
users_asset_transfers_transferred_byTousers,
|
||||||
...rest
|
...rest
|
||||||
} = row;
|
} = row;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
from_plant: plants_asset_transfers_from_plant_idToplants || null,
|
from_plant: from_plant || null,
|
||||||
to_plant: plants_asset_transfers_to_plant_idToplants || null,
|
to_plant: to_plant || null,
|
||||||
from_department: departments_asset_transfers_from_department_idTodepartments || null,
|
from_department: departments_asset_transfers_from_department_idTodepartments || null,
|
||||||
to_department: departments_asset_transfers_to_department_idTodepartments || null,
|
to_department: departments_asset_transfers_to_department_idTodepartments || null,
|
||||||
from_user: users_asset_transfers_from_user_idTousers || null,
|
from_user: users_asset_transfers_from_user_idTousers || null,
|
||||||
to_user: users_asset_transfers_to_user_idTousers || null,
|
to_user: users_asset_transfers_to_user_idTousers || null,
|
||||||
from_warehouse: warehouses_asset_transfers_from_warehouse_idTowarehouses || null,
|
from_warehouse: from_warehouse || null,
|
||||||
to_warehouse: warehouses_asset_transfers_to_warehouse_idTowarehouses || null,
|
to_warehouse: to_warehouse || null,
|
||||||
transferred_by_user: users_asset_transfers_transferred_byTousers || null,
|
transferred_by_user: users_asset_transfers_transferred_byTousers || null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -155,11 +149,10 @@ const normalizeAssetPayload = async (payload, { isCreate = false } = {}) => {
|
|||||||
payload.asset_category_id,
|
payload.asset_category_id,
|
||||||
'asset_category_id'
|
'asset_category_id'
|
||||||
);
|
);
|
||||||
await assertReference('plants', payload.plant_id, 'plant_id');
|
await assertPlant(payload.plant_id, 'plant_id');
|
||||||
if (payload.department_id)
|
if (payload.department_id)
|
||||||
await assertReference('departments', payload.department_id, 'department_id');
|
await assertReference('departments', payload.department_id, 'department_id');
|
||||||
if (payload.warehouse_id)
|
if (payload.warehouse_id) await assertWarehouse(payload.warehouse_id, 'warehouse_id');
|
||||||
await assertReference('warehouses', payload.warehouse_id, 'warehouse_id');
|
|
||||||
if (payload.assigned_to_user_id)
|
if (payload.assigned_to_user_id)
|
||||||
await assertReference('users', payload.assigned_to_user_id, 'assigned_to_user_id');
|
await assertReference('users', payload.assigned_to_user_id, 'assigned_to_user_id');
|
||||||
if (payload.vendor_id) await assertReference('vendors', payload.vendor_id, 'vendor_id');
|
if (payload.vendor_id) await assertReference('vendors', payload.vendor_id, 'vendor_id');
|
||||||
@ -402,14 +395,12 @@ const transferAsset = async (id, payload, userId, requestId) => {
|
|||||||
throw new ApiError(409, 'Cannot transfer disposed or scrapped assets');
|
throw new ApiError(409, 'Cannot transfer disposed or scrapped assets');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.to_plant_id) await assertReference('plants', payload.to_plant_id, 'to_plant_id');
|
if (payload.to_plant_id) await assertPlant(payload.to_plant_id, 'to_plant_id');
|
||||||
if (payload.to_department_id) {
|
if (payload.to_department_id) {
|
||||||
await assertReference('departments', payload.to_department_id, 'to_department_id');
|
await assertReference('departments', payload.to_department_id, 'to_department_id');
|
||||||
}
|
}
|
||||||
if (payload.to_user_id) await assertReference('users', payload.to_user_id, 'to_user_id');
|
if (payload.to_user_id) await assertReference('users', payload.to_user_id, 'to_user_id');
|
||||||
if (payload.to_warehouse_id) {
|
if (payload.to_warehouse_id) await assertWarehouse(payload.to_warehouse_id, 'to_warehouse_id');
|
||||||
await assertReference('warehouses', payload.to_warehouse_id, 'to_warehouse_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
const transfer = {
|
const transfer = {
|
||||||
transfer_date: toDateOnly(payload.transfer_date),
|
transfer_date: toDateOnly(payload.transfer_date),
|
||||||
|
|||||||
@ -110,7 +110,7 @@ const getMe = async (userId) => {
|
|||||||
},
|
},
|
||||||
departments: { select: { id: true, name: true } },
|
departments: { select: { id: true, name: true } },
|
||||||
designations: { select: { id: true, name: true } },
|
designations: { select: { id: true, name: true } },
|
||||||
plants_users_plant_idToplants: { select: { id: true, code: true, name: true } },
|
plant_location: { select: { id: true, code: true, name: true } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ const getMe = async (userId) => {
|
|||||||
role: roles[0] || null,
|
role: roles[0] || null,
|
||||||
department: user.departments,
|
department: user.departments,
|
||||||
designation: user.designations,
|
designation: user.designations,
|
||||||
plant: user.plants_users_plant_idToplants,
|
plant: user.plant_location,
|
||||||
permissions,
|
permissions,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,11 +5,12 @@ const { getPagination } = require('../../utils/pagination');
|
|||||||
const { nextDocumentNumber } = require('../../utils/generateCode');
|
const { nextDocumentNumber } = require('../../utils/generateCode');
|
||||||
const { buildSimplePdf } = require('../../utils/simplePdf');
|
const { buildSimplePdf } = require('../../utils/simplePdf');
|
||||||
const repository = require('./grn.repository');
|
const repository = require('./grn.repository');
|
||||||
|
const { assertWarehouse } = require('../../utils/locations');
|
||||||
|
|
||||||
const grnListInclude = {
|
const grnListInclude = {
|
||||||
purchase_orders: { select: { id: true, po_number: true, status: true } },
|
purchase_orders: { select: { id: true, po_number: true, status: true } },
|
||||||
vendors: { select: { id: true, vendor_code: true, vendor_name: true } },
|
vendors: { select: { id: true, vendor_code: true, vendor_name: true } },
|
||||||
warehouses: { select: { id: true, code: true, name: true } },
|
warehouse: { select: { id: true, code: true, name: true } },
|
||||||
users_grn_received_byTousers: { select: { id: true, full_name: true } },
|
users_grn_received_byTousers: { select: { id: true, full_name: true } },
|
||||||
users_grn_created_byTousers: { select: { id: true, full_name: true } },
|
users_grn_created_byTousers: { select: { id: true, full_name: true } },
|
||||||
};
|
};
|
||||||
@ -46,7 +47,7 @@ const sanitizeGrn = (row) => {
|
|||||||
const {
|
const {
|
||||||
purchase_orders,
|
purchase_orders,
|
||||||
vendors,
|
vendors,
|
||||||
warehouses,
|
warehouse,
|
||||||
users_grn_received_byTousers,
|
users_grn_received_byTousers,
|
||||||
users_grn_created_byTousers,
|
users_grn_created_byTousers,
|
||||||
users_grn_quality_checked_byTousers,
|
users_grn_quality_checked_byTousers,
|
||||||
@ -60,7 +61,7 @@ const sanitizeGrn = (row) => {
|
|||||||
...rest,
|
...rest,
|
||||||
purchase_order: purchase_orders || null,
|
purchase_order: purchase_orders || null,
|
||||||
vendor: vendors || null,
|
vendor: vendors || null,
|
||||||
warehouse: warehouses || null,
|
warehouse: warehouse || null,
|
||||||
received_by_user: users_grn_received_byTousers || null,
|
received_by_user: users_grn_received_byTousers || null,
|
||||||
created_by_user: users_grn_created_byTousers || null,
|
created_by_user: users_grn_created_byTousers || null,
|
||||||
quality_checked_by_user: users_grn_quality_checked_byTousers || null,
|
quality_checked_by_user: users_grn_quality_checked_byTousers || null,
|
||||||
@ -78,6 +79,7 @@ const sanitizeGrn = (row) => {
|
|||||||
purchase_orders: undefined,
|
purchase_orders: undefined,
|
||||||
vendors: undefined,
|
vendors: undefined,
|
||||||
warehouses: undefined,
|
warehouses: undefined,
|
||||||
|
warehouse: undefined,
|
||||||
users_grn_received_byTousers: undefined,
|
users_grn_received_byTousers: undefined,
|
||||||
users_grn_created_byTousers: undefined,
|
users_grn_created_byTousers: undefined,
|
||||||
users_grn_quality_checked_byTousers: undefined,
|
users_grn_quality_checked_byTousers: undefined,
|
||||||
@ -242,10 +244,7 @@ const buildHeaderData = (payload, po, userId) => ({
|
|||||||
|
|
||||||
const createGrn = async (payload, userId, requestId) => {
|
const createGrn = async (payload, userId, requestId) => {
|
||||||
const po = await getReceivablePoOrThrow(payload.po_id);
|
const po = await getReceivablePoOrThrow(payload.po_id);
|
||||||
const warehouse = await prisma.warehouses.findFirst({
|
await assertWarehouse(payload.warehouse_id, 'warehouse_id');
|
||||||
where: { id: BigInt(payload.warehouse_id), deleted_at: null, is_active: true },
|
|
||||||
});
|
|
||||||
if (!warehouse) throw new ApiError(422, 'Invalid warehouse_id');
|
|
||||||
|
|
||||||
const { builtItems, assetPlans } = await validateAndBuildItems(po, payload.items);
|
const { builtItems, assetPlans } = await validateAndBuildItems(po, payload.items);
|
||||||
const header = buildHeaderData(payload, po, userId);
|
const header = buildHeaderData(payload, po, userId);
|
||||||
@ -316,10 +315,7 @@ const updateGrn = async (id, payload, userId, requestId) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (payload.warehouse_id) {
|
if (payload.warehouse_id) {
|
||||||
const warehouse = await prisma.warehouses.findFirst({
|
await assertWarehouse(payload.warehouse_id, 'warehouse_id');
|
||||||
where: { id: BigInt(payload.warehouse_id), deleted_at: null, is_active: true },
|
|
||||||
});
|
|
||||||
if (!warehouse) throw new ApiError(422, 'Invalid warehouse_id');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ router.use('/designations', require('./designations/designations.routes'));
|
|||||||
router.use('/document-series', require('./document-series/document-series.routes'));
|
router.use('/document-series', require('./document-series/document-series.routes'));
|
||||||
router.use('/item-subcategories', require('./item-subcategories/item-subcategories.routes'));
|
router.use('/item-subcategories', require('./item-subcategories/item-subcategories.routes'));
|
||||||
router.use('/items', require('./items/items.routes'));
|
router.use('/items', require('./items/items.routes'));
|
||||||
|
router.use('/locations', require('./locations/locations.routes'));
|
||||||
router.use('/warehouses', require('./warehouses/warehouses.routes'));
|
router.use('/warehouses', require('./warehouses/warehouses.routes'));
|
||||||
router.use('/plants', require('./plants/plants.routes'));
|
router.use('/plants', require('./plants/plants.routes'));
|
||||||
|
|
||||||
|
|||||||
30
src/modules/masters/locations/locations.controller.js
Normal file
30
src/modules/masters/locations/locations.controller.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const asyncHandler = require('../../../utils/asyncHandler');
|
||||||
|
const ApiResponse = require('../../../utils/ApiResponse');
|
||||||
|
const service = require('./locations.service');
|
||||||
|
|
||||||
|
const create = asyncHandler(async (req, res) => {
|
||||||
|
const data = await service.createLocation(req.body, req.user?.id, req.id);
|
||||||
|
res.status(201).json(new ApiResponse(201, data, 'Location created successfully'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const list = asyncHandler(async (req, res) => {
|
||||||
|
const result = await service.listLocations(req.query);
|
||||||
|
res.json(new ApiResponse(200, result.data, 'Locations list fetched', result.meta));
|
||||||
|
});
|
||||||
|
|
||||||
|
const getOne = asyncHandler(async (req, res) => {
|
||||||
|
const data = await service.getLocationById(req.params.id);
|
||||||
|
res.json(new ApiResponse(200, data, 'Location fetched'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = asyncHandler(async (req, res) => {
|
||||||
|
const data = await service.updateLocationById(req.params.id, req.body, req.user?.id, req.id);
|
||||||
|
res.json(new ApiResponse(200, data, 'Location updated successfully'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const remove = asyncHandler(async (req, res) => {
|
||||||
|
await service.deleteLocationById(req.params.id, req.user?.id, req.id);
|
||||||
|
res.json(new ApiResponse(200, null, 'Location deleted successfully'));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = { create, list, getOne, update, remove };
|
||||||
21
src/modules/masters/locations/locations.routes.js
Normal file
21
src/modules/masters/locations/locations.routes.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const authenticate = require('../../../middlewares/auth.middleware');
|
||||||
|
const authorize = require('../../../middlewares/rbac.middleware');
|
||||||
|
const validate = require('../../../middlewares/validate.middleware');
|
||||||
|
const controller = require('./locations.controller');
|
||||||
|
const {
|
||||||
|
createLocationSchema,
|
||||||
|
updateLocationSchema,
|
||||||
|
listQuerySchema,
|
||||||
|
} = require('./locations.validation');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.use(authenticate);
|
||||||
|
router.get('/', authorize('MASTERS', 'view'), validate(listQuerySchema, 'query'), controller.list);
|
||||||
|
router.get('/:id', authorize('MASTERS', 'view'), controller.getOne);
|
||||||
|
router.post('/', authorize('MASTERS', 'create'), validate(createLocationSchema), controller.create);
|
||||||
|
router.put('/:id', authorize('MASTERS', 'edit'), validate(updateLocationSchema), controller.update);
|
||||||
|
router.delete('/:id', authorize('MASTERS', 'delete'), controller.remove);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
274
src/modules/masters/locations/locations.service.js
Normal file
274
src/modules/masters/locations/locations.service.js
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
const prisma = require('../../../config/prisma');
|
||||||
|
const ApiError = require('../../../utils/ApiError');
|
||||||
|
const auditLog = require('../../../utils/auditLog');
|
||||||
|
const { getPagination } = require('../../../utils/pagination');
|
||||||
|
const {
|
||||||
|
LOCATION_TYPES,
|
||||||
|
assertPlant,
|
||||||
|
toPlantResponse,
|
||||||
|
toWarehouseResponse,
|
||||||
|
toLocationResponse,
|
||||||
|
} = require('../../../utils/locations');
|
||||||
|
|
||||||
|
const TABLE_NAME = 'locations';
|
||||||
|
|
||||||
|
const normalizeString = (value) => {
|
||||||
|
if (value === null || value === undefined) return value;
|
||||||
|
return String(value).trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildPlantData = (payload) => ({
|
||||||
|
type: LOCATION_TYPES.PLANT,
|
||||||
|
code: payload.code ? String(payload.code).trim().toUpperCase() : undefined,
|
||||||
|
name: normalizeString(payload.name),
|
||||||
|
gstin: normalizeString(payload.gstin),
|
||||||
|
address: normalizeString(payload.address),
|
||||||
|
city: normalizeString(payload.city),
|
||||||
|
state: normalizeString(payload.state),
|
||||||
|
pincode: normalizeString(payload.pincode),
|
||||||
|
phone: normalizeString(payload.phone),
|
||||||
|
is_active: payload.is_active,
|
||||||
|
parent_id: null,
|
||||||
|
location: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildWarehouseData = (payload) => ({
|
||||||
|
type: LOCATION_TYPES.WAREHOUSE,
|
||||||
|
code: payload.code ? String(payload.code).trim().toUpperCase() : undefined,
|
||||||
|
name: normalizeString(payload.name),
|
||||||
|
parent_id: payload.parent_id ?? payload.plant_id ?? null,
|
||||||
|
location: normalizeString(payload.location),
|
||||||
|
is_active: payload.is_active,
|
||||||
|
gstin: null,
|
||||||
|
address: null,
|
||||||
|
city: null,
|
||||||
|
state: null,
|
||||||
|
pincode: null,
|
||||||
|
phone: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const warehouseInclude = { parent: { select: { id: true, code: true, name: true } } };
|
||||||
|
|
||||||
|
const assertUniqueCode = async (code, excludeId = null) => {
|
||||||
|
if (!code) return;
|
||||||
|
const existing = await prisma.locations.findFirst({
|
||||||
|
where: {
|
||||||
|
code,
|
||||||
|
deleted_at: null,
|
||||||
|
...(excludeId ? { id: { not: BigInt(excludeId) } } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (existing) throw new ApiError(409, 'Location code already exists');
|
||||||
|
};
|
||||||
|
|
||||||
|
const createLocation = async (type, payload, userId, requestId) => {
|
||||||
|
const data =
|
||||||
|
type === LOCATION_TYPES.PLANT ? buildPlantData(payload) : buildWarehouseData(payload);
|
||||||
|
|
||||||
|
if (!data.code || !data.name) throw new ApiError(422, 'code and name are required');
|
||||||
|
await assertUniqueCode(data.code);
|
||||||
|
|
||||||
|
if (type === LOCATION_TYPES.WAREHOUSE) {
|
||||||
|
if (!data.parent_id) throw new ApiError(422, 'parent_id (plant) is required for warehouse');
|
||||||
|
await assertPlant(data.parent_id, 'parent_id');
|
||||||
|
data.parent_id = BigInt(data.parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.created_by = userId ? BigInt(userId) : null;
|
||||||
|
data.updated_by = userId ? BigInt(userId) : null;
|
||||||
|
|
||||||
|
const created = await prisma.locations.create({
|
||||||
|
data,
|
||||||
|
include: type === LOCATION_TYPES.WAREHOUSE ? warehouseInclude : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response =
|
||||||
|
type === LOCATION_TYPES.PLANT ? toPlantResponse(created) : toWarehouseResponse(created);
|
||||||
|
|
||||||
|
await auditLog({
|
||||||
|
tableName: TABLE_NAME,
|
||||||
|
recordId: created.id,
|
||||||
|
action: 'CREATE',
|
||||||
|
oldValue: null,
|
||||||
|
newValue: response,
|
||||||
|
userId,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listLocations = async (query, type = null) => {
|
||||||
|
const { page, limit, skip } = getPagination(query);
|
||||||
|
|
||||||
|
const where = {
|
||||||
|
deleted_at: null,
|
||||||
|
...(type ? { type } : {}),
|
||||||
|
...(query.parent_id ? { parent_id: BigInt(query.parent_id) } : {}),
|
||||||
|
...(query.is_active !== undefined ? { is_active: query.is_active } : {}),
|
||||||
|
...(query.search
|
||||||
|
? {
|
||||||
|
OR: [
|
||||||
|
{ code: { contains: query.search, mode: 'insensitive' } },
|
||||||
|
{ name: { contains: query.search, mode: 'insensitive' } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const [rows, total] = await Promise.all([
|
||||||
|
prisma.locations.findMany({
|
||||||
|
where,
|
||||||
|
include: type === LOCATION_TYPES.WAREHOUSE ? warehouseInclude : undefined,
|
||||||
|
orderBy: { created_at: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
|
}),
|
||||||
|
prisma.locations.count({ where }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mapRow = (row) => {
|
||||||
|
if (type === LOCATION_TYPES.PLANT) return toPlantResponse(row);
|
||||||
|
if (type === LOCATION_TYPES.WAREHOUSE) return toWarehouseResponse(row);
|
||||||
|
return toLocationResponse(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { data: rows.map(mapRow), meta: { page, limit, total } };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLocationById = async (id, type = null) => {
|
||||||
|
const row = await prisma.locations.findFirst({
|
||||||
|
where: {
|
||||||
|
id: BigInt(id),
|
||||||
|
deleted_at: null,
|
||||||
|
...(type ? { type } : {}),
|
||||||
|
},
|
||||||
|
include: warehouseInclude,
|
||||||
|
});
|
||||||
|
if (!row) throw new ApiError(404, type ? `${type} not found` : 'Location not found');
|
||||||
|
|
||||||
|
if (type === LOCATION_TYPES.PLANT) return toPlantResponse(row);
|
||||||
|
if (type === LOCATION_TYPES.WAREHOUSE) return toWarehouseResponse(row);
|
||||||
|
return toLocationResponse(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLocation = async (id, type, payload, userId, requestId) => {
|
||||||
|
const existing = await prisma.locations.findFirst({
|
||||||
|
where: { id: BigInt(id), deleted_at: null, type },
|
||||||
|
include: warehouseInclude,
|
||||||
|
});
|
||||||
|
if (!existing) throw new ApiError(404, `${type} not found`);
|
||||||
|
|
||||||
|
const data =
|
||||||
|
type === LOCATION_TYPES.PLANT ? buildPlantData(payload) : buildWarehouseData(payload);
|
||||||
|
Object.keys(data).forEach((key) => {
|
||||||
|
if (data[key] === undefined) delete data[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.code) await assertUniqueCode(data.code, id);
|
||||||
|
|
||||||
|
if (type === LOCATION_TYPES.WAREHOUSE && data.parent_id !== undefined) {
|
||||||
|
if (!data.parent_id) throw new ApiError(422, 'parent_id (plant) is required for warehouse');
|
||||||
|
await assertPlant(data.parent_id, 'parent_id');
|
||||||
|
data.parent_id = BigInt(data.parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.updated_by = userId ? BigInt(userId) : null;
|
||||||
|
|
||||||
|
const updated = await prisma.locations.update({
|
||||||
|
where: { id: BigInt(id) },
|
||||||
|
data,
|
||||||
|
include: type === LOCATION_TYPES.WAREHOUSE ? warehouseInclude : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldValue =
|
||||||
|
type === LOCATION_TYPES.PLANT ? toPlantResponse(existing) : toWarehouseResponse(existing);
|
||||||
|
const newValue =
|
||||||
|
type === LOCATION_TYPES.PLANT ? toPlantResponse(updated) : toWarehouseResponse(updated);
|
||||||
|
|
||||||
|
await auditLog({
|
||||||
|
tableName: TABLE_NAME,
|
||||||
|
recordId: id,
|
||||||
|
action: 'UPDATE',
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
userId,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return newValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteLocation = async (id, type, userId, requestId) => {
|
||||||
|
const existing = await prisma.locations.findFirst({
|
||||||
|
where: { id: BigInt(id), deleted_at: null, type },
|
||||||
|
include: warehouseInclude,
|
||||||
|
});
|
||||||
|
if (!existing) throw new ApiError(404, `${type} not found`);
|
||||||
|
|
||||||
|
await prisma.locations.update({
|
||||||
|
where: { id: BigInt(id) },
|
||||||
|
data: { deleted_at: new Date(), updated_by: userId ? BigInt(userId) : null },
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldValue =
|
||||||
|
type === LOCATION_TYPES.PLANT ? toPlantResponse(existing) : toWarehouseResponse(existing);
|
||||||
|
|
||||||
|
await auditLog({
|
||||||
|
tableName: TABLE_NAME,
|
||||||
|
recordId: id,
|
||||||
|
action: 'DELETE',
|
||||||
|
oldValue,
|
||||||
|
newValue: { deleted_at: new Date() },
|
||||||
|
userId,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteLocationById = async (id, userId, requestId) => {
|
||||||
|
const existing = await prisma.locations.findFirst({
|
||||||
|
where: { id: BigInt(id), deleted_at: null },
|
||||||
|
include: warehouseInclude,
|
||||||
|
});
|
||||||
|
if (!existing) throw new ApiError(404, 'Location not found');
|
||||||
|
return deleteLocation(id, existing.type, userId, requestId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLocationById = async (id, payload, userId, requestId) => {
|
||||||
|
const existing = await prisma.locations.findFirst({
|
||||||
|
where: { id: BigInt(id), deleted_at: null },
|
||||||
|
});
|
||||||
|
if (!existing) throw new ApiError(404, 'Location not found');
|
||||||
|
return updateLocation(id, existing.type, payload, userId, requestId);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
LOCATION_TYPES,
|
||||||
|
createPlant: (payload, userId, requestId) =>
|
||||||
|
createLocation(LOCATION_TYPES.PLANT, payload, userId, requestId),
|
||||||
|
listPlants: (query) => listLocations(query, LOCATION_TYPES.PLANT),
|
||||||
|
getPlantById: (id) => getLocationById(id, LOCATION_TYPES.PLANT),
|
||||||
|
updatePlant: (id, payload, userId, requestId) =>
|
||||||
|
updateLocation(id, LOCATION_TYPES.PLANT, payload, userId, requestId),
|
||||||
|
deletePlant: (id, userId, requestId) =>
|
||||||
|
deleteLocation(id, LOCATION_TYPES.PLANT, userId, requestId),
|
||||||
|
createWarehouse: (payload, userId, requestId) =>
|
||||||
|
createLocation(LOCATION_TYPES.WAREHOUSE, payload, userId, requestId),
|
||||||
|
listWarehouses: (query) => listLocations(query, LOCATION_TYPES.WAREHOUSE),
|
||||||
|
getWarehouseById: (id) => getLocationById(id, LOCATION_TYPES.WAREHOUSE),
|
||||||
|
updateWarehouse: (id, payload, userId, requestId) =>
|
||||||
|
updateLocation(id, LOCATION_TYPES.WAREHOUSE, payload, userId, requestId),
|
||||||
|
deleteWarehouse: (id, userId, requestId) =>
|
||||||
|
deleteLocation(id, LOCATION_TYPES.WAREHOUSE, userId, requestId),
|
||||||
|
createLocation: (payload, userId, requestId) => {
|
||||||
|
if (!payload.type) throw new ApiError(422, 'type is required');
|
||||||
|
if (![LOCATION_TYPES.PLANT, LOCATION_TYPES.WAREHOUSE].includes(payload.type)) {
|
||||||
|
throw new ApiError(422, 'type must be plant or warehouse');
|
||||||
|
}
|
||||||
|
return createLocation(payload.type, payload, userId, requestId);
|
||||||
|
},
|
||||||
|
listLocations: (query) => listLocations(query),
|
||||||
|
getLocationById: (id) => getLocationById(id),
|
||||||
|
updateLocationById,
|
||||||
|
deleteLocationById,
|
||||||
|
};
|
||||||
94
src/modules/masters/locations/locations.validation.js
Normal file
94
src/modules/masters/locations/locations.validation.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
const locationFields = {
|
||||||
|
code: Joi.string().max(30).optional(),
|
||||||
|
name: Joi.string().max(150).optional(),
|
||||||
|
gstin: Joi.string().max(15).allow(null, '').optional(),
|
||||||
|
address: Joi.string().allow(null, '').optional(),
|
||||||
|
city: Joi.string().max(100).allow(null, '').optional(),
|
||||||
|
state: Joi.string().max(100).allow(null, '').optional(),
|
||||||
|
pincode: Joi.string().max(10).allow(null, '').optional(),
|
||||||
|
phone: Joi.string().max(15).allow(null, '').optional(),
|
||||||
|
location: Joi.string().max(200).allow(null, '').optional(),
|
||||||
|
parent_id: Joi.number().integer().positive().optional(),
|
||||||
|
plant_id: Joi.number().integer().positive().optional(),
|
||||||
|
is_active: Joi.boolean().optional(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPlantSchema = Joi.object({
|
||||||
|
code: Joi.string().max(30).required(),
|
||||||
|
name: Joi.string().max(150).required(),
|
||||||
|
gstin: locationFields.gstin,
|
||||||
|
address: locationFields.address,
|
||||||
|
city: locationFields.city,
|
||||||
|
state: locationFields.state,
|
||||||
|
pincode: locationFields.pincode,
|
||||||
|
phone: locationFields.phone,
|
||||||
|
is_active: Joi.boolean().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatePlantSchema = createPlantSchema.fork(['code', 'name'], (schema) => schema.optional()).min(1);
|
||||||
|
|
||||||
|
const createWarehouseSchema = Joi.object({
|
||||||
|
code: Joi.string().max(30).required(),
|
||||||
|
name: Joi.string().max(150).required(),
|
||||||
|
plant_id: Joi.number().integer().positive().required(),
|
||||||
|
location: locationFields.location,
|
||||||
|
is_active: Joi.boolean().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateWarehouseSchema = createWarehouseSchema
|
||||||
|
.fork(['code', 'name', 'plant_id'], (schema) => schema.optional())
|
||||||
|
.min(1);
|
||||||
|
|
||||||
|
const createLocationSchema = Joi.object({
|
||||||
|
type: Joi.string().valid('plant', 'warehouse').required(),
|
||||||
|
code: Joi.string().max(30).required(),
|
||||||
|
name: Joi.string().max(150).required(),
|
||||||
|
parent_id: Joi.when('type', {
|
||||||
|
is: 'warehouse',
|
||||||
|
then: Joi.number().integer().positive().required(),
|
||||||
|
otherwise: Joi.number().integer().positive().allow(null).optional(),
|
||||||
|
}),
|
||||||
|
gstin: locationFields.gstin,
|
||||||
|
address: locationFields.address,
|
||||||
|
city: locationFields.city,
|
||||||
|
state: locationFields.state,
|
||||||
|
pincode: locationFields.pincode,
|
||||||
|
phone: locationFields.phone,
|
||||||
|
location: locationFields.location,
|
||||||
|
is_active: Joi.boolean().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateLocationSchema = Joi.object({
|
||||||
|
code: Joi.string().max(30).optional(),
|
||||||
|
name: Joi.string().max(150).optional(),
|
||||||
|
parent_id: Joi.number().integer().positive().optional(),
|
||||||
|
gstin: locationFields.gstin,
|
||||||
|
address: locationFields.address,
|
||||||
|
city: locationFields.city,
|
||||||
|
state: locationFields.state,
|
||||||
|
pincode: locationFields.pincode,
|
||||||
|
phone: locationFields.phone,
|
||||||
|
location: locationFields.location,
|
||||||
|
is_active: Joi.boolean().optional(),
|
||||||
|
}).min(1);
|
||||||
|
|
||||||
|
const listQuerySchema = Joi.object({
|
||||||
|
page: Joi.number().integer().min(1).default(1),
|
||||||
|
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||||
|
search: Joi.string().allow('').optional(),
|
||||||
|
is_active: Joi.boolean().optional(),
|
||||||
|
type: Joi.string().valid('plant', 'warehouse').optional(),
|
||||||
|
parent_id: Joi.number().integer().positive().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createPlantSchema,
|
||||||
|
updatePlantSchema,
|
||||||
|
createWarehouseSchema,
|
||||||
|
updateWarehouseSchema,
|
||||||
|
createLocationSchema,
|
||||||
|
updateLocationSchema,
|
||||||
|
listQuerySchema,
|
||||||
|
};
|
||||||
@ -1,86 +1,9 @@
|
|||||||
const { buildMasterService } = require('../_shared/master.factory');
|
const service = require('../locations/locations.service');
|
||||||
|
|
||||||
const config = {
|
|
||||||
modelName: 'plants',
|
|
||||||
tableName: 'plants',
|
|
||||||
uniqueField: 'code',
|
|
||||||
softDelete: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'code',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: true,
|
|
||||||
searchable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gstin',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'address',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'city',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'state',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pincode',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'phone',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'is_active',
|
|
||||||
type: 'boolean',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'created_by',
|
|
||||||
type: 'int',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updated_by',
|
|
||||||
type: 'int',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const service = buildMasterService(config);
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createPlants: service.createOne,
|
createPlants: service.createPlant,
|
||||||
listPlants: service.list,
|
listPlants: service.listPlants,
|
||||||
getPlantsById: service.getOne,
|
getPlantsById: service.getPlantById,
|
||||||
updatePlants: service.updateOne,
|
updatePlants: service.updatePlant,
|
||||||
deletePlants: service.removeOne,
|
deletePlants: service.deletePlant,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,62 +1,9 @@
|
|||||||
const { buildMasterService } = require('../_shared/master.factory');
|
const service = require('../locations/locations.service');
|
||||||
|
|
||||||
const config = {
|
|
||||||
modelName: 'warehouses',
|
|
||||||
tableName: 'warehouses',
|
|
||||||
uniqueField: 'code',
|
|
||||||
softDelete: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'code',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: true,
|
|
||||||
searchable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'plant_id',
|
|
||||||
type: 'int',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'location',
|
|
||||||
type: 'string',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'is_active',
|
|
||||||
type: 'boolean',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'created_by',
|
|
||||||
type: 'int',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updated_by',
|
|
||||||
type: 'int',
|
|
||||||
uppercase: false,
|
|
||||||
searchable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const service = buildMasterService(config);
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createWarehouses: service.createOne,
|
createWarehouses: service.createWarehouse,
|
||||||
listWarehouses: service.list,
|
listWarehouses: service.listWarehouses,
|
||||||
getWarehousesById: service.getOne,
|
getWarehousesById: service.getWarehouseById,
|
||||||
updateWarehouses: service.updateOne,
|
updateWarehouses: service.updateWarehouse,
|
||||||
deleteWarehouses: service.removeOne,
|
deleteWarehouses: service.deleteWarehouse,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,11 +14,12 @@ const {
|
|||||||
} = require('./purchase-orders.constants');
|
} = require('./purchase-orders.constants');
|
||||||
const { computeLineAmounts, computeHeaderTotals } = require('./purchase-orders.calculations');
|
const { computeLineAmounts, computeHeaderTotals } = require('./purchase-orders.calculations');
|
||||||
const repository = require('./purchase-orders.repository');
|
const repository = require('./purchase-orders.repository');
|
||||||
|
const { assertPlant, assertWarehouse } = require('../../utils/locations');
|
||||||
|
|
||||||
const poListInclude = {
|
const poListInclude = {
|
||||||
vendors: { select: { id: true, vendor_code: true, vendor_name: true } },
|
vendors: { select: { id: true, vendor_code: true, vendor_name: true } },
|
||||||
plants: { select: { id: true, code: true, name: true } },
|
plant: { select: { id: true, code: true, name: true } },
|
||||||
warehouses: { select: { id: true, code: true, name: true } },
|
warehouse: { select: { id: true, code: true, name: true } },
|
||||||
brands: { select: { id: true, code: true, name: true } },
|
brands: { select: { id: true, code: true, name: true } },
|
||||||
users_purchase_orders_created_byTousers: { select: { id: true, full_name: true } },
|
users_purchase_orders_created_byTousers: { select: { id: true, full_name: true } },
|
||||||
};
|
};
|
||||||
@ -57,8 +58,8 @@ const sanitizePo = (po) => {
|
|||||||
if (!po) return null;
|
if (!po) return null;
|
||||||
const {
|
const {
|
||||||
vendors,
|
vendors,
|
||||||
plants,
|
plant,
|
||||||
warehouses,
|
warehouse,
|
||||||
brands,
|
brands,
|
||||||
payment_terms,
|
payment_terms,
|
||||||
delivery_terms,
|
delivery_terms,
|
||||||
@ -73,8 +74,8 @@ const sanitizePo = (po) => {
|
|||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
vendor: vendors || null,
|
vendor: vendors || null,
|
||||||
plant: plants || null,
|
plant: plant || null,
|
||||||
warehouse: warehouses || null,
|
warehouse: warehouse || null,
|
||||||
brand: brands || null,
|
brand: brands || null,
|
||||||
payment_term: payment_terms || null,
|
payment_term: payment_terms || null,
|
||||||
delivery_term: delivery_terms || null,
|
delivery_term: delivery_terms || null,
|
||||||
@ -102,7 +103,9 @@ const sanitizePo = (po) => {
|
|||||||
po_approvals: undefined,
|
po_approvals: undefined,
|
||||||
vendors: undefined,
|
vendors: undefined,
|
||||||
plants: undefined,
|
plants: undefined,
|
||||||
|
plant: undefined,
|
||||||
warehouses: undefined,
|
warehouses: undefined,
|
||||||
|
warehouse: undefined,
|
||||||
brands: undefined,
|
brands: undefined,
|
||||||
payment_terms: undefined,
|
payment_terms: undefined,
|
||||||
delivery_terms: undefined,
|
delivery_terms: undefined,
|
||||||
@ -120,8 +123,7 @@ const assertStatus = (po, allowedStatuses, action) => {
|
|||||||
|
|
||||||
const SOFT_DELETE_TABLES = new Set([
|
const SOFT_DELETE_TABLES = new Set([
|
||||||
'vendors',
|
'vendors',
|
||||||
'plants',
|
'locations',
|
||||||
'warehouses',
|
|
||||||
'payment_terms',
|
'payment_terms',
|
||||||
'delivery_terms',
|
'delivery_terms',
|
||||||
'items',
|
'items',
|
||||||
@ -206,9 +208,8 @@ const validateAndBuildItems = async (items) => {
|
|||||||
|
|
||||||
const buildHeaderData = async (payload, builtItems, userId) => {
|
const buildHeaderData = async (payload, builtItems, userId) => {
|
||||||
await assertReference('vendors', payload.vendor_id, 'vendor_id');
|
await assertReference('vendors', payload.vendor_id, 'vendor_id');
|
||||||
await assertReference('plants', payload.plant_id, 'plant_id');
|
await assertPlant(payload.plant_id, 'plant_id');
|
||||||
if (payload.warehouse_id)
|
if (payload.warehouse_id) await assertWarehouse(payload.warehouse_id, 'warehouse_id');
|
||||||
await assertReference('warehouses', payload.warehouse_id, 'warehouse_id');
|
|
||||||
if (payload.brand_id)
|
if (payload.brand_id)
|
||||||
await assertReference('brands', payload.brand_id, 'brand_id', { requireActive: false });
|
await assertReference('brands', payload.brand_id, 'brand_id', { requireActive: false });
|
||||||
if (payload.payment_term_id)
|
if (payload.payment_term_id)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const ApiError = require('../../utils/ApiError');
|
|||||||
const auditLog = require('../../utils/auditLog');
|
const auditLog = require('../../utils/auditLog');
|
||||||
const { getPagination } = require('../../utils/pagination');
|
const { getPagination } = require('../../utils/pagination');
|
||||||
const { encrypt, decrypt, blindIndex } = require('../../utils/encryption');
|
const { encrypt, decrypt, blindIndex } = require('../../utils/encryption');
|
||||||
|
const { assertPlant } = require('../../utils/locations');
|
||||||
const { extractUserRoles } = require('../../utils/userPermissions');
|
const { extractUserRoles } = require('../../utils/userPermissions');
|
||||||
|
|
||||||
const userInclude = {
|
const userInclude = {
|
||||||
@ -13,7 +14,7 @@ const userInclude = {
|
|||||||
},
|
},
|
||||||
departments: { select: { id: true, name: true } },
|
departments: { select: { id: true, name: true } },
|
||||||
designations: { select: { id: true, name: true } },
|
designations: { select: { id: true, name: true } },
|
||||||
plants_users_plant_idToplants: { select: { id: true, code: true, name: true } },
|
plant_location: { select: { id: true, code: true, name: true } },
|
||||||
users_users_reporting_toTousers: { select: { id: true, full_name: true, employee_code: true } },
|
users_users_reporting_toTousers: { select: { id: true, full_name: true, employee_code: true } },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ const sanitizeUser = (user) => {
|
|||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
const {
|
const {
|
||||||
user_roles,
|
user_roles,
|
||||||
plants_users_plant_idToplants,
|
plant_location,
|
||||||
users_users_reporting_toTousers,
|
users_users_reporting_toTousers,
|
||||||
departments,
|
departments,
|
||||||
designations,
|
designations,
|
||||||
@ -44,7 +45,7 @@ const sanitizeUser = (user) => {
|
|||||||
role: roles[0] || null,
|
role: roles[0] || null,
|
||||||
department: departments || null,
|
department: departments || null,
|
||||||
designation: designations || null,
|
designation: designations || null,
|
||||||
plant: plants_users_plant_idToplants || null,
|
plant: plant_location || null,
|
||||||
reporting_manager: users_users_reporting_toTousers || null,
|
reporting_manager: users_users_reporting_toTousers || null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -129,10 +130,7 @@ const assertFk = async (payload) => {
|
|||||||
if (!row) throw new ApiError(422, 'Invalid designation_id');
|
if (!row) throw new ApiError(422, 'Invalid designation_id');
|
||||||
}
|
}
|
||||||
if (payload.plant_id) {
|
if (payload.plant_id) {
|
||||||
const row = await prisma.plants.findFirst({
|
await assertPlant(payload.plant_id, 'plant_id');
|
||||||
where: { id: BigInt(payload.plant_id), deleted_at: null },
|
|
||||||
});
|
|
||||||
if (!row) throw new ApiError(422, 'Invalid plant_id');
|
|
||||||
}
|
}
|
||||||
if (payload.reporting_to) {
|
if (payload.reporting_to) {
|
||||||
const row = await prisma.users.findFirst({
|
const row = await prisma.users.findFirst({
|
||||||
|
|||||||
96
src/utils/locations.js
Normal file
96
src/utils/locations.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
const prisma = require('../config/prisma');
|
||||||
|
const ApiError = require('./ApiError');
|
||||||
|
|
||||||
|
const LOCATION_TYPES = Object.freeze({
|
||||||
|
PLANT: 'plant',
|
||||||
|
WAREHOUSE: 'warehouse',
|
||||||
|
});
|
||||||
|
|
||||||
|
const assertLocation = async (id, expectedType, label, { requireActive = true } = {}) => {
|
||||||
|
if (!id) return null;
|
||||||
|
|
||||||
|
const row = await prisma.locations.findFirst({
|
||||||
|
where: {
|
||||||
|
id: BigInt(id),
|
||||||
|
type: expectedType,
|
||||||
|
deleted_at: null,
|
||||||
|
},
|
||||||
|
include: expectedType === LOCATION_TYPES.WAREHOUSE
|
||||||
|
? { parent: { select: { id: true, code: true, name: true } } }
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!row) throw new ApiError(422, `Invalid ${label}`);
|
||||||
|
if (requireActive && !row.is_active) throw new ApiError(422, `${label} is inactive`);
|
||||||
|
return row;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertPlant = (id, label = 'plant_id') =>
|
||||||
|
assertLocation(id, LOCATION_TYPES.PLANT, label);
|
||||||
|
|
||||||
|
const assertWarehouse = (id, label = 'warehouse_id') =>
|
||||||
|
assertLocation(id, LOCATION_TYPES.WAREHOUSE, label);
|
||||||
|
|
||||||
|
const locationListSelect = { id: true, code: true, name: true, type: true };
|
||||||
|
|
||||||
|
const plantListSelect = { id: true, code: true, name: true };
|
||||||
|
|
||||||
|
const warehouseListSelect = {
|
||||||
|
id: true,
|
||||||
|
code: true,
|
||||||
|
name: true,
|
||||||
|
parent_id: true,
|
||||||
|
location: true,
|
||||||
|
parent: { select: plantListSelect },
|
||||||
|
};
|
||||||
|
|
||||||
|
const toPlantResponse = (row) => ({
|
||||||
|
id: row.id,
|
||||||
|
code: row.code,
|
||||||
|
name: row.name,
|
||||||
|
gstin: row.gstin,
|
||||||
|
address: row.address,
|
||||||
|
city: row.city,
|
||||||
|
state: row.state,
|
||||||
|
pincode: row.pincode,
|
||||||
|
phone: row.phone,
|
||||||
|
is_active: row.is_active,
|
||||||
|
created_by: row.created_by,
|
||||||
|
updated_by: row.updated_by,
|
||||||
|
created_at: row.created_at,
|
||||||
|
updated_at: row.updated_at,
|
||||||
|
deleted_at: row.deleted_at,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toWarehouseResponse = (row) => ({
|
||||||
|
id: row.id,
|
||||||
|
code: row.code,
|
||||||
|
name: row.name,
|
||||||
|
plant_id: row.parent_id,
|
||||||
|
plant: row.parent || null,
|
||||||
|
location: row.location,
|
||||||
|
is_active: row.is_active,
|
||||||
|
created_by: row.created_by,
|
||||||
|
updated_by: row.updated_by,
|
||||||
|
created_at: row.created_at,
|
||||||
|
updated_at: row.updated_at,
|
||||||
|
deleted_at: row.deleted_at,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toLocationResponse = (row) => {
|
||||||
|
if (row.type === LOCATION_TYPES.PLANT) return { ...toPlantResponse(row), type: row.type };
|
||||||
|
return { ...toWarehouseResponse(row), type: row.type, parent_id: row.parent_id };
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
LOCATION_TYPES,
|
||||||
|
assertLocation,
|
||||||
|
assertPlant,
|
||||||
|
assertWarehouse,
|
||||||
|
locationListSelect,
|
||||||
|
plantListSelect,
|
||||||
|
warehouseListSelect,
|
||||||
|
toPlantResponse,
|
||||||
|
toWarehouseResponse,
|
||||||
|
toLocationResponse,
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user