feat: implement POS order management system

- Added CreateOrderDto and CreateOrderItemDto for order creation.
- Developed PosOrdersController to handle order-related endpoints.
- Created PosOrdersService for business logic related to orders.
- Implemented PosOrdersWorkflow for order processing workflows.
- Established Prisma integration for database operations in orders.
- Introduced SalesInvoicesModule and related DTOs for sales invoice management.
- Enhanced SalesInvoicesService and SalesInvoicesWorkflow for invoice processing.
This commit is contained in:
2026-01-07 15:25:59 +03:30
parent b05048e62f
commit 5fd6611aca
37 changed files with 1974 additions and 291 deletions
@@ -0,0 +1,17 @@
CREATE OR REPLACE VIEW Stock_Available_View AS
SELECT
sb.productId,
sb.inventoryId,
sb.quantity AS physicalQuantity,
COALESCE(SUM(sr.quantity), 0) AS reservedQuantity,
(
sb.quantity - COALESCE(SUM(sr.quantity), 0)
) AS availableQuantity
FROM
Stock_Balance sb
LEFT JOIN Stock_Reservations sr ON sr.productId = sb.productId
AND sr.inventoryId = sb.inventoryId
GROUP BY
sb.productId,
sb.inventoryId,
sb.quantity;
@@ -0,0 +1,21 @@
/*
Warnings:
- You are about to drop the column `expiresAt` on the `Stock_Reservations` table. All the data in the column will be lost.
- Added the required column `posAccountId` to the `Orders` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE `Orders` ADD COLUMN `posAccountId` INTEGER NOT NULL;
-- AlterTable
ALTER TABLE `Stock_Reservations` DROP COLUMN `expiresAt`;
-- CreateIndex
CREATE INDEX `Orders_posAccountId_idx` ON `Orders`(`posAccountId`);
-- AddForeignKey
ALTER TABLE `Orders` ADD CONSTRAINT `Orders_posAccountId_fkey` FOREIGN KEY (`posAccountId`) REFERENCES `Pos_Accounts`(`id`) ON DELETE RESTRICT ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE `Stock_Reservations` ADD CONSTRAINT `Stock_Reservations_orderId_fkey` FOREIGN KEY (`orderId`) REFERENCES `Orders`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
+1
View File
@@ -47,6 +47,7 @@ model PosAccount {
inventoryBankAccount InventoryBankAccount @relation(fields: [inventoryId, bankAccountId], references: [inventoryId, bankAccountId])
salesInvoices SalesInvoice[]
orders Order[]
@@index([inventoryId])
@@map("Pos_Accounts")
+17 -11
View File
@@ -1,16 +1,22 @@
model Order {
id Int @id @default(autoincrement())
orderNumber String @unique @db.VarChar(100)
status OrderStatus @default(PENDING)
totalAmount Decimal @db.Decimal(15, 2)
description String? @db.Text
createdAt DateTime @default(now()) @db.Timestamp(0)
updatedAt DateTime @updatedAt @db.Timestamp(0)
deletedAt DateTime? @db.Timestamp(0)
customerId Int?
customer Customer? @relation(fields: [customerId], references: [id], onUpdate: NoAction)
orderItems OrderItem[]
id Int @id @default(autoincrement())
orderNumber String @unique @db.VarChar(100)
status OrderStatus @default(PENDING)
totalAmount Decimal @db.Decimal(15, 2)
description String? @db.Text
createdAt DateTime @default(now()) @db.Timestamp(0)
updatedAt DateTime @updatedAt @db.Timestamp(0)
deletedAt DateTime? @db.Timestamp(0)
customerId Int?
posAccountId Int
customer Customer? @relation(fields: [customerId], references: [id], onUpdate: NoAction)
posAccount PosAccount @relation(fields: [posAccountId], references: [id], onUpdate: NoAction)
orderItems OrderItem[]
stockReservations StockReservation[]
@@index([posAccountId])
@@index([customerId])
@@map("Orders")
}
+4 -3
View File
@@ -1,7 +1,8 @@
generator client {
provider = "prisma-client"
output = "../../src/generated/prisma"
moduleFormat = "cjs"
provider = "prisma-client"
output = "../../src/generated/prisma"
moduleFormat = "cjs"
previewFeatures = ["views"]
}
datasource db {
+11 -1
View File
@@ -63,15 +63,25 @@ model StockAdjustment {
model StockReservation {
id Int @id @default(autoincrement())
quantity Decimal @db.Decimal(10, 0)
expiresAt DateTime @db.Timestamp(0)
createdAt DateTime @default(now()) @db.Timestamp(0)
productId Int
inventoryId Int
orderId Int
inventory Inventory @relation(fields: [inventoryId], references: [id])
product Product @relation(fields: [productId], references: [id])
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
@@index([inventoryId])
@@index([productId])
@@map("Stock_Reservations")
}
view StockAvailableView {
productId Int
inventoryId Int
physicalQuantity Decimal @db.Decimal(10, 0)
reservedQuantity Decimal @db.Decimal(10, 0)
availableQuantity Decimal @db.Decimal(10, 0)
@@map("Stock_Available_View")
}
+44 -32
View File
@@ -1,5 +1,5 @@
-- AUTO-GENERATED MYSQL TRIGGER DUMP
-- Generated at: 2026-01-04T17:29:18.092Z
-- Generated at: 2026-01-06T16:09:38.959Z
-- ------------------------------------------
-- index: 1
@@ -52,44 +52,35 @@ end;
-- ------------------------------------------
-- index: 3
-- Trigger: trg_order_item_after_insert
-- Event: INSERT
-- Table: Order_Items
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_order_item_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_insert` AFTER INSERT ON `Order_Items` FOR EACH ROW BEGIN
UPDATE Stock_Reservations SET quantity = quantity + NEW.quantity
WHERE orderId = NEW.orderId AND productId = NEW.productId;
END;
-- ------------------------------------------
-- index: 4
-- Trigger: trg_order_item_after_update
-- Event: UPDATE
-- Table: Order_Items
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_order_item_after_update`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_update` AFTER UPDATE ON `Order_Items` FOR EACH ROW BEGIN
UPDATE Stock_Reservations
SET quantity = quantity - OLD.quantity + NEW.quantity
WHERE orderId = NEW.orderId AND productId = NEW.productId;
UPDATE Stock_Reservations
SET quantity = quantity - OLD.quantity + NEW.quantity
WHERE orderId = NEW.orderId AND productId = NEW.productId;
END;
-- ------------------------------------------
-- index: 5
-- index: 4
-- Trigger: trg_order_item_after_delete
-- Event: DELETE
-- Table: Order_Items
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_order_item_after_delete`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_delete` AFTER DELETE ON `Order_Items` FOR EACH ROW BEGIN
UPDATE Stock_Reservations SET quantity = quantity - OLD.quantity
DELETE From Stock_Reservations
WHERE orderId = OLD.orderId AND productId = OLD.productId;
END;
-- ------------------------------------------
-- index: 6
-- index: 5
-- Trigger: trg_order_after_cancel
-- Event: UPDATE
-- Table: Orders
@@ -103,7 +94,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_after_cancel` AFTER UPDATE ON `Order
END;
-- ------------------------------------------
-- index: 7
-- index: 6
-- Trigger: trg_purchase_receipt_item_after_insert
-- Event: INSERT
-- Table: Purchase_Receipt_Items
@@ -166,7 +157,7 @@ DECLARE invId INT;
END;
-- ------------------------------------------
-- index: 8
-- index: 7
-- Trigger: trg_pr_payment_after_delete
-- Event: DELETE
-- Table: Purchase_Receipt_Payments
@@ -201,7 +192,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_pr_payment_after_delete` AFTER DELETE ON `
END;
-- ------------------------------------------
-- index: 9
-- index: 8
-- Trigger: trg_sales_invoice_items_before_insert
-- Event: INSERT
-- Table: Sales_Invoice_Items
@@ -220,10 +211,9 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_sales_invoice_items_before_insert` BEFORE
WHERE si.id = NEW.invoiceId;
SELECT COALESCE(quantity, 0) INTO current_stock
FROM Stock_Balance sb
WHERE productId = NEW.productId AND sb.inventoryId = inventory_id
LIMIT 1;
SELECT COALESCE(sav.availableQuantity, 0) INTO current_stock
FROM Stock_Available_View sav
WHERE productId = NEW.productId AND sav.inventoryId = inventory_id;
@@ -234,7 +224,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_sales_invoice_items_before_insert` BEFORE
end;
-- ------------------------------------------
-- index: 10
-- index: 9
-- Trigger: trg_sales_invoice_items_after_insert
-- Event: INSERT
-- Table: Sales_Invoice_Items
@@ -307,7 +297,7 @@ DECLARE pos_id INT;
END;
-- ------------------------------------------
-- index: 11
-- index: 10
-- Trigger: trg_pos_account_payment_after_insert
-- Event: INSERT
-- Table: Sales_Invoice_Payments
@@ -343,7 +333,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_pos_account_payment_after_insert` AFTER IN
END;
-- ------------------------------------------
-- index: 12
-- index: 11
-- Trigger: trg_sales_invoice_payment_after_insert
-- Event: INSERT
-- Table: Sales_Invoice_Payments
@@ -382,7 +372,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_sales_invoice_payment_after_insert` AFTER
END;
-- ------------------------------------------
-- index: 13
-- index: 12
-- Trigger: trg_stock_sale_insert
-- Event: INSERT
-- Table: Stock_Movements
@@ -417,7 +407,7 @@ END IF;
END;
-- ------------------------------------------
-- index: 14
-- index: 13
-- Trigger: trg_stock_purchase_insert
-- Event: INSERT
-- Table: Stock_Movements
@@ -452,7 +442,7 @@ END IF;
END;
-- ------------------------------------------
-- index: 15
-- index: 14
-- Trigger: trg_stock_transfer
-- Event: INSERT
-- Table: Stock_Movements
@@ -534,3 +524,25 @@ END IF;
END;
-- ------------------------------------------
-- index: 15
-- Trigger: trg_no_negative_available_stock
-- Event: INSERT
-- Table: Stock_Reservations
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_no_negative_available_stock`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_no_negative_available_stock` BEFORE INSERT ON `Stock_Reservations` FOR EACH ROW BEGIN
DECLARE available DECIMAL(14,3);
SELECT availableQuantity
INTO available
FROM Stock_Available_View
WHERE productId = NEW.productId
AND inventoryId = NEW.inventoryId;
IF available < NEW.quantity THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'موجودی کافی نیست';
END IF;
END;