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:
@@ -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;
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user