refactor: restructure purchase receipts module and related workflows

- Removed old DTOs for creating and updating purchase receipts.
- Updated purchase receipts controller and service to use new DTOs and workflows.
- Introduced transaction helper for managing database transactions.
- Added new workflows for handling purchase receipt payments and supplier ledgers.
- Implemented new logic for managing purchase receipt items and payments.
- Enhanced error handling for payment processing in workflows.
- Updated supplier ledger management to reflect changes in purchase receipts.
This commit is contained in:
2026-01-05 10:07:23 +03:30
parent a2db2daa70
commit fda190f902
38 changed files with 617 additions and 486 deletions
+112 -381
View File
@@ -1,34 +1,19 @@
-- AUTO-GENERATED MYSQL TRIGGER DUMP -- AUTO-GENERATED MYSQL TRIGGER DUMP
-- Generated at: 2026-01-04T09:46:30.365Z -- Generated at: 2026-01-04T17:29:18.092Z
-- ------------------------------------------ -- ------------------------------------------
-- index: 1 -- index: 1
-- Trigger: trg_bank_account_transaction_after_insert
-- Event: INSERT
-- Table: Bank_Account_Transactions
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_bank_account_transaction_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_bank_account_transaction_after_insert` AFTER INSERT ON `Bank_Account_Transactions` FOR EACH ROW BEGIN
IF NEW.type = 'DEPOSIT' THEN
UPDATE Bank_Account_Balance SET balance = balance + NEW.amount WHERE bankAccountId = NEW.bankAccountId;
ELSEIF NEW.type = 'WITHDRAWAL' THEN
UPDATE Bank_Account_Balance SET balance = balance - NEW.amount WHERE bankAccountId = NEW.bankAccountId;
END IF;
END;
-- ------------------------------------------
-- index: 2
-- Trigger: trg_bank_account_transaction_after_delete -- Trigger: trg_bank_account_transaction_after_delete
-- Event: DELETE -- Event: DELETE
-- Table: Bank_Account_Transactions -- Table: Bank_Account_Transactions
-- ------------------------------------------ -- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_bank_account_transaction_after_delete`; DROP TRIGGER IF EXISTS `trg_bank_account_transaction_after_delete`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_bank_account_transaction_after_delete` AFTER DELETE ON `Bank_Account_Transactions` FOR EACH ROW BEGIN CREATE DEFINER=`pos`@`%` TRIGGER `trg_bank_account_transaction_after_delete` AFTER DELETE ON `Bank_Account_Transactions` FOR EACH ROW BEGIN
UPDATE Bank_Accounts SET balance = balance - OLD.amount; UPDATE `Bank_Account_Balance` SET balance = balance - OLD.amount WHERE `bankAccountId` = OLD.bankAccountId;
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 3 -- index: 2
-- Trigger: trg_transfer_item_after_insert -- Trigger: trg_transfer_item_after_insert
-- Event: INSERT -- Event: INSERT
-- Table: Inventory_Transfer_Items -- Table: Inventory_Transfer_Items
@@ -66,7 +51,7 @@ DECLARE fromInv INT;
end; end;
-- ------------------------------------------ -- ------------------------------------------
-- index: 4 -- index: 3
-- Trigger: trg_order_item_after_insert -- Trigger: trg_order_item_after_insert
-- Event: INSERT -- Event: INSERT
-- Table: Order_Items -- Table: Order_Items
@@ -78,7 +63,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_insert` AFTER INSERT ON `
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 5 -- index: 4
-- Trigger: trg_order_item_after_update -- Trigger: trg_order_item_after_update
-- Event: UPDATE -- Event: UPDATE
-- Table: Order_Items -- Table: Order_Items
@@ -91,7 +76,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_update` AFTER UPDATE ON `
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 6 -- index: 5
-- Trigger: trg_order_item_after_delete -- Trigger: trg_order_item_after_delete
-- Event: DELETE -- Event: DELETE
-- Table: Order_Items -- Table: Order_Items
@@ -104,7 +89,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_item_after_delete` AFTER DELETE ON `
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 7 -- index: 6
-- Trigger: trg_order_after_cancel -- Trigger: trg_order_after_cancel
-- Event: UPDATE -- Event: UPDATE
-- Table: Orders -- Table: Orders
@@ -118,7 +103,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_order_after_cancel` AFTER UPDATE ON `Order
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 8 -- index: 7
-- Trigger: trg_purchase_receipt_item_after_insert -- Trigger: trg_purchase_receipt_item_after_insert
-- Event: INSERT -- Event: INSERT
-- Table: Purchase_Receipt_Items -- Table: Purchase_Receipt_Items
@@ -181,222 +166,7 @@ DECLARE invId INT;
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 9 -- index: 8
-- Trigger: trg_pr_payment_before_insert
-- Event: INSERT
-- Table: Purchase_Receipt_Payments
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_pr_payment_before_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_pr_payment_before_insert` BEFORE INSERT ON `Purchase_Receipt_Payments` FOR EACH ROW BEGIN
DECLARE receiptTotal DECIMAL(14,2);
DECLARE paid DECIMAL(14,2);
SELECT totalAmount, paidAmount
INTO receiptTotal, paid
FROM Purchase_Receipts
WHERE id = NEW.receiptId
FOR UPDATE;
IF NEW.type = 'PAYMENT' AND paid + NEW.amount > receiptTotal THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'مجموع مبلغ پرداختی بیشتر از مبلغ فاکتور است.';
END IF;
END;
-- ------------------------------------------
-- index: 10
-- Trigger: trg_purchase_payment_update_receipt
-- Event: INSERT
-- Table: Purchase_Receipt_Payments
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_purchase_payment_update_receipt`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_purchase_payment_update_receipt` AFTER INSERT ON `Purchase_Receipt_Payments` FOR EACH ROW BEGIN
DECLARE paid DECIMAL(15,2);
DECLARE total DECIMAL(15,2);
SELECT
COALESCE(SUM(
CASE WHEN type = 'PAYMENT' THEN amount ELSE -amount END
),0)
INTO paid
FROM Purchase_Receipt_Payments
WHERE receiptId = NEW.receiptId;
SELECT totalAmount INTO total
FROM Purchase_Receipts
WHERE id = NEW.receiptId;
UPDATE Purchase_Receipts
SET
paidAmount = paid,
status = CASE
WHEN paid = 0 THEN 'UNPAID'
WHEN paid < total THEN 'PARTIALLY_PAID'
ELSE 'PAID'
END
WHERE id = NEW.receiptId;
END;
-- ------------------------------------------
-- index: 11
-- Trigger: trg_purchase_payment_after_insert
-- Event: INSERT
-- Table: Purchase_Receipt_Payments
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_purchase_payment_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_purchase_payment_after_insert` AFTER INSERT ON `Purchase_Receipt_Payments` FOR EACH ROW BEGIN
DECLARE currentBalance DECIMAL(15, 2);
SELECT balance INTO currentBalance
FROM Bank_Account_Balance
WHERE
bankAccountId = NEW.bankAccountId FOR
UPDATE;
IF currentBalance IS NULL THEN SET currentBalance = 0;
INSERT INTO
Bank_Account_Balance (bankAccountId, balance, updatedAt)
VALUES (NEW.bankAccountId, 0, NOW());
END IF;
IF NEW.type = 'PAYMENT' THEN
SET
currentBalance = currentBalance - NEW.amount;
INSERT INTO
Bank_Account_Transactions (
bankAccountId,
type,
amount,
balanceAfter,
referenceType,
referenceId
)
VALUES (
NEW.bankAccountId,
'WITHDRAWAL',
NEW.amount,
currentBalance,
'PURCHASE_PAYMENT',
NEW.id
);
ELSE SET currentBalance = currentBalance + NEW.amount;
INSERT INTO
Bank_Account_Transactions (
bankAccountId,
type,
amount,
balanceAfter,
referenceType,
referenceId
)
VALUES (
NEW.bankAccountId,
'DEPOSIT',
NEW.amount,
currentBalance,
'PURCHASE_REFUND',
NEW.id
);
END IF;
UPDATE Bank_Account_Balance
SET
balance = currentBalance
WHERE
bankAccountId = NEW.bankAccountId;
END;
-- ------------------------------------------
-- index: 12
-- Trigger: trg_pr_payment_after_insert
-- Event: INSERT
-- Table: Purchase_Receipt_Payments
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_pr_payment_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_pr_payment_after_insert` AFTER INSERT ON `Purchase_Receipt_Payments` FOR EACH ROW BEGIN
DECLARE receiptTotal DECIMAL(14,2) Default 0;
DECLARE newPaid DECIMAL(14,2) Default 0;
DECLARE _supplierId INT;
DECLARE lastBalance DECIMAL(14,2) Default 0;
-- Lock receipt row
SELECT COALESCE(totalAmount, 0), COALESCE(paidAmount, 0), supplierId
INTO receiptTotal, newPaid, _supplierId
FROM Purchase_Receipts
WHERE id = NEW.receiptId
FOR UPDATE;
-- Apply payment or refund
IF NEW.type = 'PAYMENT' THEN
SET newPaid = newPaid + NEW.amount;
ELSE
SET newPaid = newPaid - NEW.amount;
END IF;
-- Update receipt
UPDATE Purchase_Receipts
SET
paidAmount = newPaid,
status =
CASE
WHEN newPaid = 0 THEN 'UNPAID'
WHEN newPaid < receiptTotal THEN 'PARTIALLY_PAID'
ELSE 'PAID'
END
WHERE id = NEW.receiptId;
-- Get last supplier balance
SELECT IFNULL(balance, 0)
INTO lastBalance
FROM Supplier_Ledger
WHERE supplierId = _supplierId
ORDER BY id DESC
LIMIT 1;
-- Insert supplier ledger
INSERT INTO Supplier_Ledger
(
supplierId,
debit,
credit,
balance,
sourceType,
sourceId,
createdAt
)
VALUES
(
_supplierId,
IF(NEW.type = 'REFUND', NEW.amount, 0),
IF(NEW.type = 'PAYMENT', NEW.amount, 0),
lastBalance
+ IF(NEW.type = 'PAYMENT', NEW.amount, 0)
- IF(NEW.type = 'REFUND', NEW.amount, 0),
'PAYMENT',
NEW.id,
NOW()
);
END;
-- ------------------------------------------
-- index: 13
-- Trigger: trg_pr_payment_after_delete -- Trigger: trg_pr_payment_after_delete
-- Event: DELETE -- Event: DELETE
-- Table: Purchase_Receipt_Payments -- Table: Purchase_Receipt_Payments
@@ -431,46 +201,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_pr_payment_after_delete` AFTER DELETE ON `
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 14 -- index: 9
-- Trigger: trg_purchase_receipt_after_insert
-- Event: INSERT
-- Table: Purchase_Receipts
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_purchase_receipt_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_purchase_receipt_after_insert` AFTER INSERT ON `Purchase_Receipts` FOR EACH ROW BEGIN
DECLARE lastBalance DECIMAL(14,2) DEFAULT 0;
SELECT COALESCE(balance, 0)
INTO lastBalance
FROM Supplier_Ledger
WHERE supplierId = NEW.supplierId
ORDER BY id DESC
LIMIT 1;
INSERT INTO Supplier_Ledger
(
supplierId,
debit,
credit,
balance,
sourceType,
sourceId,
createdAt
)
VALUES
(
NEW.supplierId,
NEW.totalAmount,
0,
lastBalance - NEW.totalAmount,
'PURCHASE',
NEW.id,
NOW()
);
END;
-- ------------------------------------------
-- index: 15
-- Trigger: trg_sales_invoice_items_before_insert -- Trigger: trg_sales_invoice_items_before_insert
-- Event: INSERT -- Event: INSERT
-- Table: Sales_Invoice_Items -- Table: Sales_Invoice_Items
@@ -503,7 +234,7 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_sales_invoice_items_before_insert` BEFORE
end; end;
-- ------------------------------------------ -- ------------------------------------------
-- index: 16 -- index: 10
-- Trigger: trg_sales_invoice_items_after_insert -- Trigger: trg_sales_invoice_items_after_insert
-- Event: INSERT -- Event: INSERT
-- Table: Sales_Invoice_Items -- Table: Sales_Invoice_Items
@@ -576,7 +307,43 @@ DECLARE pos_id INT;
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 17 -- index: 11
-- Trigger: trg_pos_account_payment_after_insert
-- Event: INSERT
-- Table: Sales_Invoice_Payments
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_pos_account_payment_after_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_pos_account_payment_after_insert` AFTER INSERT ON `Sales_Invoice_Payments` FOR EACH ROW BEGIN
DECLARE _bankAccountId INT;
IF(NEW.paymentMethod != 'CASH') THEN
SELECT cashBankAccountId INTO _bankAccountId
FROM Pos_Accounts pa
INNER JOIN Sales_Invoices si ON pa.id = si.posAccountId
WHERE si.id = NEW.invoiceId;
End IF;
INSERT INTO Bank_Account_Transactions (
bankAccountId,
type,
amount,
balanceAfter,
referenceType,
referenceId
)
VALUES(
_bankAccountId,
'DEPOSIT',
NEW.amount,
0,
'POS_SALE',
NEW.id
);
END;
-- ------------------------------------------
-- index: 12
-- Trigger: trg_sales_invoice_payment_after_insert -- Trigger: trg_sales_invoice_payment_after_insert
-- Event: INSERT -- Event: INSERT
-- Table: Sales_Invoice_Payments -- Table: Sales_Invoice_Payments
@@ -615,43 +382,77 @@ CREATE DEFINER=`pos`@`%` TRIGGER `trg_sales_invoice_payment_after_insert` AFTER
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 18 -- index: 13
-- Trigger: trg_pos_account_payment_after_insert -- Trigger: trg_stock_sale_insert
-- Event: INSERT -- Event: INSERT
-- Table: Sales_Invoice_Payments -- Table: Stock_Movements
-- ------------------------------------------ -- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_pos_account_payment_after_insert`; DROP TRIGGER IF EXISTS `trg_stock_sale_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_pos_account_payment_after_insert` AFTER INSERT ON `Sales_Invoice_Payments` FOR EACH ROW BEGIN CREATE DEFINER=`pos`@`%` TRIGGER `trg_stock_sale_insert` AFTER INSERT ON `Stock_Movements` FOR EACH ROW BEGIN IF NEW.referenceType = 'SALES' THEN
DECLARE _bankAccountId INT; INSERT INTO
Stock_Balance (
IF(NEW.paymentMethod != 'CASH') THEN productId,
SELECT cashBankAccountId INTO _bankAccountId quantity,
FROM Pos_Accounts pa avgCost,
INNER JOIN Sales_Invoices si ON pa.id = si.posAccountId totalCost,
WHERE si.id = NEW.invoiceId; inventoryId,
End IF; updatedAt
INSERT INTO Bank_Account_Transactions (
bankAccountId,
type,
amount,
balanceAfter,
referenceType,
referenceId
) )
VALUES( VALUES (
_bankAccountId, NEW.productId,
'DEPOSIT', NEW.quantity,
NEW.amount, NEW.unitPrice,
0, NEW.totalCost,
'POS_SALE', NEW.inventoryId,
NEW.id NOW()
); )
ON DUPLICATE KEY UPDATE
quantity = quantity - NEW.quantity,
totalCost = totalCost - NEW.totalCost,
avgCost = totalCost / quantity;
END IF;
END; END;
-- ------------------------------------------ -- ------------------------------------------
-- index: 19 -- index: 14
-- Trigger: trg_stock_purchase_insert
-- Event: INSERT
-- Table: Stock_Movements
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_stock_purchase_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_stock_purchase_insert` AFTER INSERT ON `Stock_Movements` FOR EACH ROW BEGIN IF NEW.referenceType = 'PURCHASE' THEN
INSERT INTO
Stock_Balance (
productId,
quantity,
avgCost,
totalCost,
inventoryId,
updatedAt
)
VALUES (
NEW.productId,
NEW.quantity,
NEW.unitPrice,
NEW.totalCost,
NEW.inventoryId,
NOW()
)
ON DUPLICATE KEY UPDATE
quantity = quantity + NEW.quantity,
totalCost = totalCost + NEW.totalCost,
avgCost = totalCost / quantity;
END IF;
END;
-- ------------------------------------------
-- index: 15
-- Trigger: trg_stock_transfer -- Trigger: trg_stock_transfer
-- Event: INSERT -- Event: INSERT
-- Table: Stock_Movements -- Table: Stock_Movements
@@ -733,73 +534,3 @@ END IF;
END; END;
-- ------------------------------------------
-- index: 20
-- Trigger: trg_stock_purchase_insert
-- Event: INSERT
-- Table: Stock_Movements
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_stock_purchase_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_stock_purchase_insert` AFTER INSERT ON `Stock_Movements` FOR EACH ROW BEGIN IF NEW.referenceType = 'PURCHASE' THEN
INSERT INTO
Stock_Balance (
productId,
quantity,
avgCost,
totalCost,
inventoryId,
updatedAt
)
VALUES (
NEW.productId,
NEW.quantity,
NEW.unitPrice,
NEW.totalCost,
NEW.inventoryId,
NOW()
)
ON DUPLICATE KEY UPDATE
quantity = quantity + NEW.quantity,
totalCost = totalCost + NEW.totalCost,
avgCost = totalCost / quantity;
END IF;
END;
-- ------------------------------------------
-- index: 21
-- Trigger: trg_stock_sale_insert
-- Event: INSERT
-- Table: Stock_Movements
-- ------------------------------------------
DROP TRIGGER IF EXISTS `trg_stock_sale_insert`;
CREATE DEFINER=`pos`@`%` TRIGGER `trg_stock_sale_insert` AFTER INSERT ON `Stock_Movements` FOR EACH ROW BEGIN IF NEW.referenceType = 'SALES' THEN
INSERT INTO
Stock_Balance (
productId,
quantity,
avgCost,
totalCost,
inventoryId,
updatedAt
)
VALUES (
NEW.productId,
NEW.quantity,
NEW.unitPrice,
NEW.totalCost,
NEW.inventoryId,
NOW()
)
ON DUPLICATE KEY UPDATE
quantity = quantity - NEW.quantity,
totalCost = totalCost - NEW.totalCost,
avgCost = totalCost / quantity;
END IF;
END;
+26 -2
View File
@@ -1,26 +1,31 @@
-- Stored Procedures equivalent to triggers -- Stored Procedures equivalent to triggers
DELIMITER // DELIMITER / /
-- Procedure for trg_bank_account_transaction_after_insert -- Procedure for trg_bank_account_transaction_after_insert
CREATE PROCEDURE update_bank_balance(IN p_bankAccountId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2)) CREATE PROCEDURE update_bank_balance(IN p_bankAccountId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2))
BEGIN BEGIN
START TRANSACTION;
IF p_type = 'DEPOSIT' THEN IF p_type = 'DEPOSIT' THEN
UPDATE Bank_Account_Balance SET balance = balance + p_amount WHERE bankAccountId = p_bankAccountId; UPDATE Bank_Account_Balance SET balance = balance + p_amount WHERE bankAccountId = p_bankAccountId;
ELSEIF p_type = 'WITHDRAWAL' THEN ELSEIF p_type = 'WITHDRAWAL' THEN
UPDATE Bank_Account_Balance SET balance = balance - p_amount WHERE bankAccountId = p_bankAccountId; UPDATE Bank_Account_Balance SET balance = balance - p_amount WHERE bankAccountId = p_bankAccountId;
END IF; END IF;
COMMIT;
END // END //
-- Procedure for trg_bank_account_transaction_after_delete -- Procedure for trg_bank_account_transaction_after_delete
CREATE PROCEDURE update_bank_balance_on_delete(IN p_bankAccountId INT, IN p_amount DECIMAL(15,2)) CREATE PROCEDURE update_bank_balance_on_delete(IN p_bankAccountId INT, IN p_amount DECIMAL(15,2))
BEGIN BEGIN
START TRANSACTION;
UPDATE Bank_Accounts SET balance = balance - p_amount WHERE id = p_bankAccountId; UPDATE Bank_Accounts SET balance = balance - p_amount WHERE id = p_bankAccountId;
COMMIT;
END // END //
-- Procedure for trg_transfer_item_after_insert -- Procedure for trg_transfer_item_after_insert
CREATE PROCEDURE process_transfer_item(IN p_transferId INT, IN p_productId INT, IN p_count DECIMAL(10,2)) CREATE PROCEDURE process_transfer_item(IN p_transferId INT, IN p_productId INT, IN p_count DECIMAL(10,2))
BEGIN BEGIN
START TRANSACTION;
DECLARE fromInv INT; DECLARE fromInv INT;
DECLARE toInv INT; DECLARE toInv INT;
DECLARE _avgCost DECIMAL(10,2); DECLARE _avgCost DECIMAL(10,2);
@@ -47,42 +52,52 @@ BEGIN
(type, quantity, unitPrice, totalCost, avgCost, referenceType, referenceId, productId, inventoryId, counterInventoryId, createdAt, remainedInStock) (type, quantity, unitPrice, totalCost, avgCost, referenceType, referenceId, productId, inventoryId, counterInventoryId, createdAt, remainedInStock)
VALUES VALUES
('IN', p_count, _avgCost, _avgCost*p_count, _avgCost, 'INVENTORY_TRANSFER', p_transferId, p_productId, toInv, fromInv, NOW(), latestQuantityInOrigin-p_count); ('IN', p_count, _avgCost, _avgCost*p_count, _avgCost, 'INVENTORY_TRANSFER', p_transferId, p_productId, toInv, fromInv, NOW(), latestQuantityInOrigin-p_count);
COMMIT;
END // END //
-- Procedure for trg_order_item_after_insert -- Procedure for trg_order_item_after_insert
CREATE PROCEDURE update_stock_reservation_insert(IN p_orderId INT, IN p_productId INT, IN p_quantity DECIMAL(10,2)) CREATE PROCEDURE update_stock_reservation_insert(IN p_orderId INT, IN p_productId INT, IN p_quantity DECIMAL(10,2))
BEGIN BEGIN
START TRANSACTION;
UPDATE Stock_Reservations SET quantity = quantity + p_quantity UPDATE Stock_Reservations SET quantity = quantity + p_quantity
WHERE orderId = p_orderId AND productId = p_productId; WHERE orderId = p_orderId AND productId = p_productId;
COMMIT;
END // END //
-- Procedure for trg_order_item_after_update -- Procedure for trg_order_item_after_update
CREATE PROCEDURE update_stock_reservation_update(IN p_orderId INT, IN p_productId INT, IN p_old_quantity DECIMAL(10,2), IN p_new_quantity DECIMAL(10,2)) CREATE PROCEDURE update_stock_reservation_update(IN p_orderId INT, IN p_productId INT, IN p_old_quantity DECIMAL(10,2), IN p_new_quantity DECIMAL(10,2))
BEGIN BEGIN
START TRANSACTION;
UPDATE Stock_Reservations UPDATE Stock_Reservations
SET quantity = quantity - p_old_quantity + p_new_quantity SET quantity = quantity - p_old_quantity + p_new_quantity
WHERE orderId = p_orderId AND productId = p_productId; WHERE orderId = p_orderId AND productId = p_productId;
COMMIT;
END // END //
-- Procedure for trg_order_item_after_delete -- Procedure for trg_order_item_after_delete
CREATE PROCEDURE update_stock_reservation_delete(IN p_orderId INT, IN p_productId INT, IN p_quantity DECIMAL(10,2)) CREATE PROCEDURE update_stock_reservation_delete(IN p_orderId INT, IN p_productId INT, IN p_quantity DECIMAL(10,2))
BEGIN BEGIN
START TRANSACTION;
UPDATE Stock_Reservations SET quantity = quantity - p_quantity UPDATE Stock_Reservations SET quantity = quantity - p_quantity
WHERE orderId = p_orderId AND productId = p_productId; WHERE orderId = p_orderId AND productId = p_productId;
COMMIT;
END // END //
-- Procedure for trg_order_after_cancel -- Procedure for trg_order_after_cancel
CREATE PROCEDURE cancel_order_stock(IN p_orderId INT, IN p_status VARCHAR(20)) CREATE PROCEDURE cancel_order_stock(IN p_orderId INT, IN p_status VARCHAR(20))
BEGIN BEGIN
START TRANSACTION;
IF p_status = 'CANCELED' OR p_status = 'REJECTED' OR p_status = 'DONE' THEN IF p_status = 'CANCELED' OR p_status = 'REJECTED' OR p_status = 'DONE' THEN
UPDATE Stock_Reservations sr SET quantity = 0 UPDATE Stock_Reservations sr SET quantity = 0
WHERE sr.orderId = p_orderId; WHERE sr.orderId = p_orderId;
END IF; END IF;
COMMIT;
END // END //
-- Procedure for trg_purchase_receipt_item_after_insert -- Procedure for trg_purchase_receipt_item_after_insert
CREATE PROCEDURE process_purchase_item(IN p_receiptId INT, IN p_productId INT, IN p_count DECIMAL(10,2), IN p_unitPrice DECIMAL(15,2), IN p_totalAmount DECIMAL(15,2)) CREATE PROCEDURE process_purchase_item(IN p_receiptId INT, IN p_productId INT, IN p_count DECIMAL(10,2), IN p_unitPrice DECIMAL(15,2), IN p_totalAmount DECIMAL(15,2))
BEGIN BEGIN
START TRANSACTION;
DECLARE latestQuantity DECIMAL(10, 2) DEFAULT 0; DECLARE latestQuantity DECIMAL(10, 2) DEFAULT 0;
DECLARE invId INT; DECLARE invId INT;
DECLARE suppId INT; DECLARE suppId INT;
@@ -133,11 +148,13 @@ BEGIN
latestQuantity + p_count, latestQuantity + p_count,
NOW() NOW()
); );
COMMIT;
END // END //
-- Procedure for trg_pr_payment_before_insert -- Procedure for trg_pr_payment_before_insert
CREATE PROCEDURE validate_payment_before_insert(IN p_receiptId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2)) CREATE PROCEDURE validate_payment_before_insert(IN p_receiptId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2))
BEGIN BEGIN
START TRANSACTION;
DECLARE receiptTotal DECIMAL(14,2); DECLARE receiptTotal DECIMAL(14,2);
DECLARE paid DECIMAL(14,2); DECLARE paid DECIMAL(14,2);
@@ -151,11 +168,13 @@ BEGIN
SIGNAL SQLSTATE '45000' SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'مجموع مبلغ پرداختی بیشتر از مبلغ فاکتور است.'; SET MESSAGE_TEXT = 'مجموع مبلغ پرداختی بیشتر از مبلغ فاکتور است.';
END IF; END IF;
COMMIT;
END // END //
-- Procedure for trg_purchase_payment_update_receipt -- Procedure for trg_purchase_payment_update_receipt
CREATE PROCEDURE update_receipt_payment(IN p_receiptId INT) CREATE PROCEDURE update_receipt_payment(IN p_receiptId INT)
BEGIN BEGIN
START TRANSACTION;
DECLARE paid DECIMAL(15,2); DECLARE paid DECIMAL(15,2);
DECLARE total DECIMAL(15,2); DECLARE total DECIMAL(15,2);
@@ -180,11 +199,13 @@ BEGIN
ELSE 'PAID' ELSE 'PAID'
END END
WHERE id = p_receiptId; WHERE id = p_receiptId;
COMMIT;
END // END //
-- Procedure for trg_purchase_payment_after_insert -- Procedure for trg_purchase_payment_after_insert
CREATE PROCEDURE process_purchase_payment(IN p_bankAccountId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2), IN p_id INT) CREATE PROCEDURE process_purchase_payment(IN p_bankAccountId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2), IN p_id INT)
BEGIN BEGIN
START TRANSACTION;
DECLARE currentBalance DECIMAL(15, 2); DECLARE currentBalance DECIMAL(15, 2);
SELECT balance INTO currentBalance SELECT balance INTO currentBalance
@@ -237,11 +258,13 @@ BEGIN
UPDATE Bank_Account_Balance UPDATE Bank_Account_Balance
SET balance = currentBalance SET balance = currentBalance
WHERE bankAccountId = p_bankAccountId; WHERE bankAccountId = p_bankAccountId;
COMMIT;
END // END //
-- Procedure for trg_pr_payment_after_insert -- Procedure for trg_pr_payment_after_insert
CREATE PROCEDURE update_supplier_ledger(IN p_receiptId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2), IN p_id INT) CREATE PROCEDURE update_supplier_ledger(IN p_receiptId INT, IN p_type VARCHAR(20), IN p_amount DECIMAL(15,2), IN p_id INT)
BEGIN BEGIN
START TRANSACTION;
DECLARE receiptTotal DECIMAL(14,2) DEFAULT 0; DECLARE receiptTotal DECIMAL(14,2) DEFAULT 0;
DECLARE newPaid DECIMAL(14,2) DEFAULT 0; DECLARE newPaid DECIMAL(14,2) DEFAULT 0;
DECLARE _supplierId INT; DECLARE _supplierId INT;
@@ -304,6 +327,7 @@ BEGIN
p_id, p_id,
NOW() NOW()
); );
COMMIT;
END // END //
-- Procedure for trg_pr_payment_after_delete -- Procedure for trg_pr_payment_after_delete
@@ -630,4 +654,4 @@ BEGIN
avgCost = totalCost / quantity; avgCost = totalCost / quantity;
END // END //
DELIMITER ; DELIMITER;
+1 -5
View File
@@ -11,7 +11,7 @@ import { BanksModule } from './modules/banks/banks.module'
import { CardexModule } from './modules/cardex/cardex.module' import { CardexModule } from './modules/cardex/cardex.module'
import { InventoriesModule } from './modules/inventories/inventories.module' import { InventoriesModule } from './modules/inventories/inventories.module'
import { PosModule } from './modules/pos/pos.module' import { PosModule } from './modules/pos/pos.module'
import { PurchaseReceiptPaymentsModule } from './modules/purchase-receipt-payments/purchase-receipt-payments.module' import { PurchaseReceiptsModule } from './modules/purchase-receipts/purchase-receipts.module'
import { StatisticsModule } from './modules/statistics/statistics.module' import { StatisticsModule } from './modules/statistics/statistics.module'
import { SuppliersModule } from './modules/suppliers/suppliers.module' import { SuppliersModule } from './modules/suppliers/suppliers.module'
import { PrismaModule } from './prisma/prisma.module' import { PrismaModule } from './prisma/prisma.module'
@@ -19,8 +19,6 @@ import { ProductBrandsModule } from './product-brands/product-brands.module'
import { ProductCategoriesModule } from './product-categories/product-categories.module' import { ProductCategoriesModule } from './product-categories/product-categories.module'
import { ProductVariantsModule } from './product-variants/product-variants.module' import { ProductVariantsModule } from './product-variants/product-variants.module'
import { ProductsModule } from './products/products.module' import { ProductsModule } from './products/products.module'
import { PurchaseReceiptItemsModule } from './purchase-receipt-items/purchase-receipt-items.module'
import { PurchaseReceiptsModule } from './purchase-receipts/purchase-receipts.module'
import { RolesModule } from './roles/roles.module' import { RolesModule } from './roles/roles.module'
import { SalesInvoiceItemsModule } from './sales-invoice-items/sales-invoice-items.module' import { SalesInvoiceItemsModule } from './sales-invoice-items/sales-invoice-items.module'
import { SalesInvoicesModule } from './sales-invoices/sales-invoices.module' import { SalesInvoicesModule } from './sales-invoices/sales-invoices.module'
@@ -43,8 +41,6 @@ import { UsersModule } from './users/users.module'
CustomersModule, CustomersModule,
InventoriesModule, InventoriesModule,
PurchaseReceiptsModule, PurchaseReceiptsModule,
PurchaseReceiptItemsModule,
PurchaseReceiptPaymentsModule,
SalesInvoicesModule, SalesInvoicesModule,
SalesInvoiceItemsModule, SalesInvoiceItemsModule,
InventoryTransfersModule, InventoryTransfersModule,
+11
View File
@@ -0,0 +1,11 @@
// src/common/database/transaction.helper.ts
import { PrismaService } from '../../prisma/prisma.service'
export async function withTransaction<T>(
prisma: PrismaService,
fn: (tx: PrismaService) => Promise<T>,
): Promise<T> {
return prisma.$transaction(async tx => {
return fn(tx as PrismaService)
})
}
@@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'
import { PrismaModule } from '../../prisma/prisma.module' import { PrismaModule } from '../../prisma/prisma.module'
import { BankAccountsController } from './bank-accounts.controller' import { BankAccountsController } from './bank-accounts.controller'
import { BankAccountsService } from './bank-accounts.service' import { BankAccountsService } from './bank-accounts.service'
import { BankAccountsWorkflow } from './bank-accounts.workflow'
@Module({ @Module({
imports: [PrismaModule], imports: [PrismaModule],
controllers: [BankAccountsController], controllers: [BankAccountsController],
providers: [BankAccountsService], providers: [BankAccountsService, BankAccountsWorkflow],
exports: [BankAccountsWorkflow],
}) })
export class BankAccountsModule {} export class BankAccountsModule {}
@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { withTransaction } from '../../common/database/transaction.helper'
import { ResponseMapper } from '../../common/response/response-mapper' import { ResponseMapper } from '../../common/response/response-mapper'
import { PrismaService } from '../../prisma/prisma.service' import { PrismaService } from '../../prisma/prisma.service'
@@ -6,8 +7,16 @@ import { PrismaService } from '../../prisma/prisma.service'
export class BankAccountsService { export class BankAccountsService {
constructor(private prisma: PrismaService) {} constructor(private prisma: PrismaService) {}
async create(data: any) { async create(data: any) {
const item = await this.prisma.bankAccount.create({ data }) return withTransaction(this.prisma, async tx => {
const item = await tx.bankAccount.create({ data })
await tx.bankAccountBalance.create({
data: {
bankAccountId: item.id,
balance: 0,
},
})
return ResponseMapper.create(item) return ResponseMapper.create(item)
})
} }
async findAll() { async findAll() {
@@ -0,0 +1,65 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '../../generated/prisma/client'
import { AddTransactionToBankAccountDto } from './dto/add-transaction.dto'
@Injectable()
export class BankAccountsWorkflow {
async addTransaction(
tx: Prisma.TransactionClient,
payload: AddTransactionToBankAccountDto,
) {
const item = await tx.bankAccountBalance.findUnique({
where: {
bankAccountId: payload.bankAccountId,
},
})
const balance = item ? Number(item.balance) : 0
const newBalance = item
? payload.type === 'DEPOSIT'
? balance + payload.amount
: balance - payload.amount
: payload.type === 'DEPOSIT'
? payload.amount
: -payload.amount
await tx.bankAccountTransaction.create({
data: {
bankAccount: { connect: { id: payload.bankAccountId } },
amount: payload.amount,
type: payload.type,
balanceAfter: newBalance,
referenceId: payload.referenceId,
referenceType: payload.referenceType,
},
})
console.log('first')
const bankAccountBalanceItem = await tx.bankAccountBalance.findUnique({
where: {
bankAccountId: payload.bankAccountId,
},
})
if (!bankAccountBalanceItem) {
return await tx.bankAccountBalance.create({
data: {
bankAccount: { connect: { id: payload.bankAccountId } },
balance: newBalance,
},
})
}
return await tx.bankAccountBalance.update({
where: {
bankAccountId: payload.bankAccountId,
},
data: {
balance: newBalance,
},
})
}
}
@@ -0,0 +1,22 @@
import { IsEnum, IsInt } from 'class-validator'
import {
BankAccountTransactionType,
BankTransactionRefType,
} from '../../../generated/prisma/enums'
export class AddTransactionToBankAccountDto {
@IsInt()
bankAccountId: number
@IsInt()
amount: number
@IsInt()
referenceId: number
@IsEnum(BankTransactionRefType)
referenceType: BankTransactionRefType
@IsEnum(BankAccountTransactionType)
type: BankAccountTransactionType
}
@@ -1,12 +0,0 @@
import { Module } from '@nestjs/common'
import { PrismaModule } from '../../prisma/prisma.module'
import { PurchaseReceiptPaymentsController } from './purchase-receipt-payments.controller'
import { PurchaseReceiptPaymentsService } from './purchase-receipt-payments.service'
@Module({
imports: [PrismaModule],
controllers: [PurchaseReceiptPaymentsController],
providers: [PurchaseReceiptPaymentsService],
})
export class PurchaseReceiptPaymentsModule {}
@@ -1,12 +1,17 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { ResponseMapper } from '../common/response/response-mapper' import { withTransaction } from '../../../common/database/transaction.helper'
import { Prisma } from '../generated/prisma/client' import { ResponseMapper } from '../../../common/response/response-mapper'
import { PrismaService } from '../prisma/prisma.service' import { Prisma } from '../../../generated/prisma/client'
import { PrismaService } from '../../../prisma/prisma.service'
import { CreatePurchaseReceiptDto } from './dto/create-purchase-receipt.dto' import { CreatePurchaseReceiptDto } from './dto/create-purchase-receipt.dto'
import { PurchaseReceiptWorkflow } from './purchase-receipts.workflow'
@Injectable() @Injectable()
export class PurchaseReceiptsService { export class PurchaseReceiptsService {
constructor(private prisma: PrismaService) {} constructor(
private prisma: PrismaService,
private purchaseReceiptWorkflow: PurchaseReceiptWorkflow,
) {}
async create(dto: CreatePurchaseReceiptDto) { async create(dto: CreatePurchaseReceiptDto) {
const data: Prisma.PurchaseReceiptCreateInput = { const data: Prisma.PurchaseReceiptCreateInput = {
@@ -31,7 +36,9 @@ export class PurchaseReceiptsService {
} }
: undefined, : undefined,
} }
const item = await this.prisma.purchaseReceipt.create({
return withTransaction(this.prisma, async tx => {
const item = await tx.purchaseReceipt.create({
data, data,
include: { items: true }, include: { items: true },
omit: { omit: {
@@ -39,7 +46,14 @@ export class PurchaseReceiptsService {
inventoryId: true, inventoryId: true,
}, },
}) })
await this.purchaseReceiptWorkflow.onCreatePurchaseReceipt(
tx,
dto.supplierId,
dto.totalAmount,
item.id,
)
return ResponseMapper.create(item) return ResponseMapper.create(item)
})
} }
async findAll() { async findAll() {
@@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '../../../generated/prisma/client'
import { SupplierLedgerWorkflow } from '../../suppliers/ledger/supplier-ledger.workflow'
@Injectable()
export class PurchaseReceiptWorkflow {
constructor(private supplierLedgerWorkflow: SupplierLedgerWorkflow) {}
async onCreatePurchaseReceipt(
tx: Prisma.TransactionClient,
supplierId: number,
amount: number,
sourceId: number,
) {
await this.supplierLedgerWorkflow.updateLedgerBalance(
tx,
supplierId,
amount,
sourceId,
'PURCHASE',
)
}
}
@@ -1,5 +1,5 @@
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { PrismaModule } from '../prisma/prisma.module' import { PrismaModule } from '../../../prisma/prisma.module'
import { PurchaseReceiptItemsController } from './purchase-receipt-items.controller' import { PurchaseReceiptItemsController } from './purchase-receipt-items.controller'
import { PurchaseReceiptItemsService } from './purchase-receipt-items.service' import { PurchaseReceiptItemsService } from './purchase-receipt-items.service'
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { ResponseMapper } from '../common/response/response-mapper' import { ResponseMapper } from '../../../common/response/response-mapper'
import { PrismaService } from '../prisma/prisma.service' import { PrismaService } from '../../../prisma/prisma.service'
import { CreatePurchaseReceiptItemDto } from './dto/create-purchase-receipt-item.dto' import { CreatePurchaseReceiptItemDto } from './dto/create-purchase-receipt-item.dto'
@Injectable() @Injectable()
@@ -1,5 +1,5 @@
import { IsEnum, IsInt, IsNumber, IsOptional, IsString } from 'class-validator' import { IsEnum, IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
import { PaymentMethodType, PaymentType } from '../../../generated/prisma/enums' import { PaymentMethodType, PaymentType } from '../../../../generated/prisma/enums'
export class CreatePurchaseReceiptPaymentDto { export class CreatePurchaseReceiptPaymentDto {
@IsNumber() @IsNumber()
@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common'
import { PrismaModule } from '../../../prisma/prisma.module'
import { BankAccountsModule } from '../../bank-accounts/bank-accounts.module'
import { PurchaseReceiptPaymentsController } from './purchase-receipt-payments.controller'
import { PurchaseReceiptPaymentsService } from './purchase-receipt-payments.service'
import { PurchaseReceiptPaymentsWorkflow } from './purchase-receipt-payments.workflow'
@Module({
imports: [PrismaModule, BankAccountsModule],
controllers: [PurchaseReceiptPaymentsController],
providers: [PurchaseReceiptPaymentsService, PurchaseReceiptPaymentsWorkflow],
exports: [PurchaseReceiptPaymentsWorkflow],
})
export class PurchaseReceiptPaymentsModule {}
@@ -1,14 +1,29 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { ResponseMapper } from '../../common/response/response-mapper' import { withTransaction } from '../../../common/database/transaction.helper'
import { PrismaService } from '../../prisma/prisma.service' import { ResponseMapper } from '../../../common/response/response-mapper'
import { PrismaService } from '../../../prisma/prisma.service'
import { CreatePurchaseReceiptPaymentDto } from './dto/create-purchase-receipt-payment.dto'
import { PurchaseReceiptPaymentsWorkflow } from './purchase-receipt-payments.workflow'
@Injectable() @Injectable()
export class PurchaseReceiptPaymentsService { export class PurchaseReceiptPaymentsService {
constructor(private prisma: PrismaService) {} constructor(
private prisma: PrismaService,
private purchaseReceiptPaymentsWorkflow: PurchaseReceiptPaymentsWorkflow,
) {}
async create(data: any) { async create(data: CreatePurchaseReceiptPaymentDto) {
const item = await this.prisma.purchaseReceiptPayments.create({ data }) return withTransaction(this.prisma, async tx => {
const item = await tx.purchaseReceiptPayments.create({ data })
await this.purchaseReceiptPaymentsWorkflow.onAddPayment(
tx,
data.bankAccountId,
data.receiptId,
data.amount,
data.type,
)
return ResponseMapper.create(item) return ResponseMapper.create(item)
})
} }
async findAll() { async findAll() {
@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '../../../generated/prisma/client'
import { PaymentType } from '../../../generated/prisma/enums'
import { BankAccountsWorkflow } from '../../bank-accounts/bank-accounts.workflow'
@Injectable()
export class PurchaseReceiptPaymentsWorkflow {
constructor(private bankAccountsWorkflow: BankAccountsWorkflow) {}
async onAddPayment(
tx: Prisma.TransactionClient,
bankAccountId: number,
receiptId: number,
amount: number,
type: PaymentType,
) {
const { paidAmount, totalAmount } = await tx.purchaseReceipt.findUniqueOrThrow({
where: { id: receiptId },
})
if (type === 'PAYMENT' && Number(paidAmount) + Number(amount) > Number(totalAmount)) {
throw new Error('مجموع مبلغ پرداختی بیشتر از مبلغ فاکتور است.')
}
if (type === 'REFUND' && Number(paidAmount) - Number(amount) < 0) {
throw new Error(
'مجموع مبلغ بازپرداختی بیشتر از مبلغ پرداخت‌ شده در این فاکتور است.',
)
}
await this.bankAccountsWorkflow.addTransaction(tx, {
bankAccountId: bankAccountId,
amount: amount,
type: type === 'PAYMENT' ? 'WITHDRAWAL' : 'DEPOSIT',
referenceId: receiptId,
referenceType: type === 'PAYMENT' ? 'PURCHASE_PAYMENT' : 'PURCHASE_REFUND',
})
const newPaidAmount =
type === 'PAYMENT'
? Number(paidAmount) + Number(amount)
: Number(paidAmount) - Number(amount)
const newStatus =
newPaidAmount === 0
? 'UNPAID'
: newPaidAmount >= Number(totalAmount)
? 'PAID'
: 'PARTIALLY_PAID'
await tx.purchaseReceipt.update({
where: { id: receiptId },
data: {
paidAmount: newPaidAmount,
status: newStatus,
},
})
}
}
@@ -0,0 +1,21 @@
import { Module } from '@nestjs/common'
import { PrismaModule } from '../../prisma/prisma.module'
import { SupplierLedgerModule } from '../suppliers/ledger/supplier-ledger.module'
import { PurchaseReceiptsController } from './index/purchase-receipts.controller'
import { PurchaseReceiptsService } from './index/purchase-receipts.service'
import { PurchaseReceiptWorkflow } from './index/purchase-receipts.workflow'
import { PurchaseReceiptItemsModule } from './purchase-receipt-items/purchase-receipt-items.module'
import { PurchaseReceiptPaymentsModule } from './purchase-receipt-payments/purchase-receipt-payments.module'
@Module({
imports: [
PrismaModule,
PurchaseReceiptItemsModule,
PurchaseReceiptPaymentsModule,
SupplierLedgerModule,
],
controllers: [PurchaseReceiptsController],
providers: [PurchaseReceiptsService, PurchaseReceiptWorkflow],
exports: [PurchaseReceiptWorkflow],
})
export class PurchaseReceiptsModule {}
@@ -22,11 +22,6 @@ export class SuppliersController {
return this.suppliersService.findOne(Number(supplierId)) return this.suppliersService.findOne(Number(supplierId))
} }
@Get(':supplierId/ledger')
getLedger(@Param('supplierId') supplierId: string) {
return this.suppliersService.getLedger(Number(supplierId))
}
@Patch(':id') @Patch(':id')
update(@Param('id') id: string, @Body() dto: UpdateSupplierDto) { update(@Param('id') id: string, @Body() dto: UpdateSupplierDto) {
return this.suppliersService.update(Number(id), dto) return this.suppliersService.update(Number(id), dto)
@@ -37,21 +37,6 @@ export class SuppliersService {
}) })
} }
async getLedger(supplierId: number) {
const items = await this.prisma.supplierLedger.findMany({
where: {
supplierId,
},
omit: {
supplierId: true,
},
orderBy: {
createdAt: 'desc',
},
})
return ResponseMapper.list(items)
}
async update(id: number, data: any) { async update(id: number, data: any) {
const item = await this.prisma.supplier.update({ where: { id }, data }) const item = await this.prisma.supplier.update({ where: { id }, data })
return ResponseMapper.update(item) return ResponseMapper.update(item)
@@ -21,10 +21,15 @@ export class SupplierInvoicesController {
@Post(':invoiceId/pay') @Post(':invoiceId/pay')
createPayment( createPayment(
@Param('supplierId') supplierId: string,
@Param('invoiceId') invoiceId: string, @Param('invoiceId') invoiceId: string,
@Body() data: CreateReceiptPaymentDto, @Body() data: CreateReceiptPaymentDto,
) { ) {
return this.supplierInvoicesService.createPayment(Number(invoiceId), data) return this.supplierInvoicesService.createPayment(
Number(supplierId),
Number(invoiceId),
data,
)
} }
@Get(':invoiceId/payments') @Get(':invoiceId/payments')
@@ -1,11 +1,14 @@
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { PrismaModule } from '../../../prisma/prisma.module' import { PrismaModule } from '../../../prisma/prisma.module'
import { PurchaseReceiptPaymentsModule } from '../../purchase-receipts/purchase-receipt-payments/purchase-receipt-payments.module'
import { SupplierLedgerModule } from '../ledger/supplier-ledger.module'
import { SupplierInvoicesController } from './invoices.controller' import { SupplierInvoicesController } from './invoices.controller'
import { SupplierInvoicesService } from './invoices.service' import { SupplierInvoicesService } from './invoices.service'
import { SupplierInvoicesWorkflow } from './invoices.workflow'
@Module({ @Module({
imports: [PrismaModule], imports: [PrismaModule, PurchaseReceiptPaymentsModule, SupplierLedgerModule],
controllers: [SupplierInvoicesController], controllers: [SupplierInvoicesController],
providers: [SupplierInvoicesService], providers: [SupplierInvoicesService, SupplierInvoicesWorkflow],
}) })
export class SupplierInvoicesModule {} export class SupplierInvoicesModule {}
@@ -1,11 +1,16 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { withTransaction } from '../../../common/database/transaction.helper'
import { ResponseMapper } from '../../../common/response/response-mapper' import { ResponseMapper } from '../../../common/response/response-mapper'
import { PrismaService } from '../../../prisma/prisma.service' import { PrismaService } from '../../../prisma/prisma.service'
import { CreateReceiptPaymentDto } from './dto/create-receipt-payment.dto' import { CreateReceiptPaymentDto } from './dto/create-receipt-payment.dto'
import { SupplierInvoicesWorkflow } from './invoices.workflow'
@Injectable() @Injectable()
export class SupplierInvoicesService { export class SupplierInvoicesService {
constructor(private prisma: PrismaService) {} constructor(
private prisma: PrismaService,
private supplierInvoicesWorkflow: SupplierInvoicesWorkflow,
) {}
async findAll(supplierId: number) { async findAll(supplierId: number) {
const items = await this.prisma.purchaseReceipt.findMany({ const items = await this.prisma.purchaseReceipt.findMany({
@@ -147,11 +152,23 @@ export class SupplierInvoicesService {
return ResponseMapper.list(items) return ResponseMapper.list(items)
} }
async createPayment(invoiceId: number, data: CreateReceiptPaymentDto) { async createPayment(
supplierId: number,
invoiceId: number,
data: CreateReceiptPaymentDto,
) {
const { bankAccountId, ...rest } = data const { bankAccountId, ...rest } = data
console.log(data)
const payment = await this.prisma.purchaseReceiptPayments.create({ return withTransaction(this.prisma, async tx => {
await this.supplierInvoicesWorkflow.onPaymentCreated(
tx,
supplierId,
data.amount,
invoiceId,
bankAccountId,
)
const payment = await tx.purchaseReceiptPayments.create({
data: { data: {
...rest, ...rest,
payedAt: new Date(data.payedAt), payedAt: new Date(data.payedAt),
@@ -174,5 +191,6 @@ export class SupplierInvoicesService {
}, },
}) })
return ResponseMapper.create(payment) return ResponseMapper.create(payment)
})
} }
} }
@@ -0,0 +1,36 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '../../../generated/prisma/client'
import { PurchaseReceiptPaymentsWorkflow } from '../../purchase-receipts/purchase-receipt-payments/purchase-receipt-payments.workflow'
import { SupplierLedgerWorkflow } from '../ledger/supplier-ledger.workflow'
@Injectable()
export class SupplierInvoicesWorkflow {
constructor(
private supplierLedgerWorkflow: SupplierLedgerWorkflow,
private purchaseReceiptPaymentWorkflow: PurchaseReceiptPaymentsWorkflow,
) {}
async onPaymentCreated(
tx: Prisma.TransactionClient,
supplierId: number,
amount: number,
invoiceId: number,
bankAccountId: number,
) {
await this.supplierLedgerWorkflow.updateLedgerBalance(
tx,
supplierId,
amount,
invoiceId,
'PAYMENT',
)
await this.purchaseReceiptPaymentWorkflow.onAddPayment(
tx,
bankAccountId,
invoiceId,
amount,
'PAYMENT',
)
}
}
@@ -0,0 +1,12 @@
import { Controller, Get, Param } from '@nestjs/common'
import { SupplierLedgerService } from './supplier-ledger.service'
@Controller('suppliers/:supplierId/ledger')
export class SupplierLedgerController {
constructor(private readonly supplierLedgerService: SupplierLedgerService) {}
@Get('')
getLedger(@Param('supplierId') supplierId: string) {
return this.supplierLedgerService.findAll(Number(supplierId))
}
}
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common'
import { PrismaModule } from '../../../prisma/prisma.module'
import { SupplierLedgerController } from './supplier-ledger.controller'
import { SupplierLedgerService } from './supplier-ledger.service'
import { SupplierLedgerWorkflow } from './supplier-ledger.workflow'
@Module({
imports: [PrismaModule],
controllers: [SupplierLedgerController],
providers: [SupplierLedgerService, SupplierLedgerWorkflow],
exports: [SupplierLedgerWorkflow],
})
export class SupplierLedgerModule {}
@@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from '../../../common/response/response-mapper'
import { PrismaService } from '../../../prisma/prisma.service'
@Injectable()
export class SupplierLedgerService {
constructor(private prisma: PrismaService) {}
async findAll(supplierId: number) {
const items = await this.prisma.supplierLedger.findMany({
where: {
supplierId,
},
omit: {
supplierId: true,
},
orderBy: {
createdAt: 'desc',
},
})
return ResponseMapper.list(items)
}
}
@@ -0,0 +1,57 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '../../../generated/prisma/client'
import { LedgerSourceType } from '../../../generated/prisma/enums'
@Injectable()
export class SupplierLedgerWorkflow {
async updateLedgerBalance(
tx: Prisma.TransactionClient,
supplierId: number,
amount: number,
sourceId: number,
sourceType: LedgerSourceType,
) {
const lastSupplierLedgerBalance = await tx.supplierLedger
.findFirstOrThrow({
where: { supplierId },
orderBy: { createdAt: 'desc' },
})
.then(sl => (sl ? sl.balance : 0))
let newBalance = Number(lastSupplierLedgerBalance)
let credit = 0
let debit = 0
let description = ''
switch (sourceType) {
case 'PURCHASE':
newBalance += amount
credit = amount
description = `خرید به مبلغ ${amount} بابت خرید شماره ${sourceId}`
break
case 'REFUND':
case 'PAYMENT':
newBalance -= amount
debit = amount
description = `پرداخت به مبلغ ${amount} بابت پرداخت شماره ${sourceId}`
break
case 'ADJUSTMENT':
newBalance = amount
description = `اصلاح حساب به مبلغ ${amount} بابت پرداخت شماره ${sourceId}`
break
}
return await tx.supplierLedger.create({
data: {
supplierId,
debit,
credit,
balance: newBalance,
sourceId,
sourceType,
description,
},
})
}
}
+2 -1
View File
@@ -3,9 +3,10 @@ import { PrismaModule } from '../../prisma/prisma.module'
import { SuppliersController } from './index/suppliers.controller' import { SuppliersController } from './index/suppliers.controller'
import { SuppliersService } from './index/suppliers.service' import { SuppliersService } from './index/suppliers.service'
import { SupplierInvoicesModule } from './invoices/invoices.module' import { SupplierInvoicesModule } from './invoices/invoices.module'
import { SupplierLedgerModule } from './ledger/supplier-ledger.module'
@Module({ @Module({
imports: [PrismaModule, SupplierInvoicesModule], imports: [PrismaModule, SupplierInvoicesModule, SupplierLedgerModule],
controllers: [SuppliersController], controllers: [SuppliersController],
providers: [SuppliersService], providers: [SuppliersService],
}) })
@@ -1,11 +0,0 @@
import { Module } from '@nestjs/common'
import { PrismaModule } from '../prisma/prisma.module'
import { PurchaseReceiptsController } from './purchase-receipts.controller'
import { PurchaseReceiptsService } from './purchase-receipts.service'
@Module({
imports: [PrismaModule],
controllers: [PurchaseReceiptsController],
providers: [PurchaseReceiptsService],
})
export class PurchaseReceiptsModule {}