GWM : tax calculation : using taxable amount column now
This commit is contained in:
parent
6ba17a3a6f
commit
67b2c32884
@ -247,6 +247,8 @@ Each sub-master supports: `GET /` (list), `GET /:id`, `POST /`, `PUT /:id`, `DEL
|
||||
| [x] | GET | `/purchase-orders/:poId/attachments/:attachmentId/download` | view | Download file (authenticated) |
|
||||
| [x] | DELETE | `/purchase-orders/:poId/attachments/:attachmentId` | delete | Delete attachment + file |
|
||||
|
||||
**Totals:** `taxable_amount = sub_total + freight + other − discount`; GST (`tax_total` / CGST+SGST / IGST) is calculated on `taxable_amount` (not on `sub_total`). Column persisted via `scripts/patch-po-taxable-amount.sql`.
|
||||
|
||||
---
|
||||
|
||||
### GRN (`/grn`) — module: `GRN`
|
||||
|
||||
@ -698,6 +698,7 @@ model purchase_orders {
|
||||
expected_delivery_date DateTime? @db.Date
|
||||
sub_total Decimal @default(0) @db.Decimal(15, 4)
|
||||
discount_amount Decimal @default(0) @db.Decimal(15, 4)
|
||||
taxable_amount Decimal @default(0) @db.Decimal(15, 4)
|
||||
tax_total Decimal @default(0) @db.Decimal(15, 4)
|
||||
cgst Decimal @default(0) @db.Decimal(15, 4)
|
||||
sgst Decimal @default(0) @db.Decimal(15, 4)
|
||||
|
||||
21
scripts/patch-po-taxable-amount.sql
Normal file
21
scripts/patch-po-taxable-amount.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- Persist PO header taxable amount and support GST-on-taxable calc
|
||||
-- taxable_amount = sub_total + freight_charges + other_charges - discount_amount
|
||||
|
||||
ALTER TABLE purchase_orders
|
||||
ADD COLUMN IF NOT EXISTS taxable_amount NUMERIC(15, 4) NOT NULL DEFAULT 0;
|
||||
|
||||
UPDATE purchase_orders
|
||||
SET taxable_amount = ROUND(
|
||||
COALESCE(sub_total, 0)
|
||||
+ COALESCE(freight_charges, 0)
|
||||
+ COALESCE(other_charges, 0)
|
||||
- COALESCE(discount_amount, 0),
|
||||
4
|
||||
)
|
||||
WHERE taxable_amount = 0
|
||||
AND (
|
||||
COALESCE(sub_total, 0) <> 0
|
||||
OR COALESCE(freight_charges, 0) <> 0
|
||||
OR COALESCE(other_charges, 0) <> 0
|
||||
OR COALESCE(discount_amount, 0) <> 0
|
||||
);
|
||||
@ -172,6 +172,10 @@ paths:
|
||||
get:
|
||||
tags: [Purchase Orders]
|
||||
summary: Get purchase order detail
|
||||
description: >
|
||||
Includes line items and amount summary. Header taxable_amount =
|
||||
sub_total + freight_charges + other_charges - discount_amount.
|
||||
tax_total / cgst+sgst / igst are calculated on taxable_amount.
|
||||
parameters:
|
||||
- { name: id, in: path, required: true, schema: { type: string, example: '1' } }
|
||||
responses:
|
||||
|
||||
@ -33,7 +33,7 @@ const computeHeaderTotals = (
|
||||
) => {
|
||||
// Sub total = sum of line taxable amounts (after line-level discounts).
|
||||
const subTotal = round4(lines.reduce((sum, line) => sum + toNum(line.taxable_amount), 0));
|
||||
const taxTotal = round4(lines.reduce((sum, line) => sum + toNum(line.tax_amount), 0));
|
||||
const lineTaxTotal = round4(lines.reduce((sum, line) => sum + toNum(line.tax_amount), 0));
|
||||
const headerDiscountAmount = round4(headerDiscount);
|
||||
const freightCharges = round4(freight);
|
||||
const otherCharges = round4(other);
|
||||
@ -41,6 +41,12 @@ const computeHeaderTotals = (
|
||||
// Taxable amount = Sub Total + Freight + Other − Discount (header-level).
|
||||
const taxableAmount = round4(subTotal + freightCharges + otherCharges - headerDiscountAmount);
|
||||
|
||||
// GST is applied on header taxable amount (not on sub_total alone).
|
||||
// Scale line taxes proportionally so multi-rate POs stay correct:
|
||||
// each line's share of freight/other/discount is taxed at that line's GST %.
|
||||
const taxTotal =
|
||||
subTotal > 0 ? round4((lineTaxTotal * taxableAmount) / subTotal) : 0;
|
||||
|
||||
// TDS is calculated on sub total (line taxable), not on header taxable.
|
||||
const tdsAmount = tdsApplicable ? round4((subTotal * toNum(tdsSectionPct)) / 100) : 0;
|
||||
const adjustmentAmount = round4(adjustment);
|
||||
|
||||
@ -21,7 +21,6 @@ const {
|
||||
computeHeaderTotals,
|
||||
splitGst,
|
||||
toNum,
|
||||
round4,
|
||||
} = require('./purchase-orders.calculations');
|
||||
const repository = require('./purchase-orders.repository');
|
||||
const { assertAnyLocation } = require('../../utils/locations');
|
||||
@ -144,13 +143,6 @@ const sanitizePo = (po) => {
|
||||
|
||||
return {
|
||||
...rest,
|
||||
// Header taxable = sub_total + freight + other − discount (not a DB column).
|
||||
taxable_amount: round4(
|
||||
toNum(rest.sub_total) +
|
||||
toNum(rest.freight_charges) +
|
||||
toNum(rest.other_charges) -
|
||||
toNum(rest.discount_amount)
|
||||
),
|
||||
vendor: vendors || null,
|
||||
billing: billing_location || null,
|
||||
shipping: shipping_location || null,
|
||||
@ -320,10 +312,8 @@ const buildHeaderData = async (payload, builtItems, userId, { applyDefaultTerms
|
||||
adjustment: payload.adjustment,
|
||||
}
|
||||
);
|
||||
// taxable_amount is derived for API/PDF; not a persisted PO column.
|
||||
const { taxable_amount: _taxableAmount, ...persistedTotals } = totals;
|
||||
|
||||
const gstSplit = splitGst(persistedTotals.tax_total, isInterStateSupply(billing, vendor));
|
||||
const gstSplit = splitGst(totals.tax_total, isInterStateSupply(billing, vendor));
|
||||
|
||||
let termsAndConditions =
|
||||
payload.terms_and_conditions !== undefined && payload.terms_and_conditions !== null
|
||||
@ -349,7 +339,7 @@ const buildHeaderData = async (payload, builtItems, userId, { applyDefaultTerms
|
||||
remarks: payload.remarks || null,
|
||||
tds_applicable: tdsApplicable,
|
||||
tds_section_pct: tdsApplicable ? tdsSectionPct : null,
|
||||
...persistedTotals,
|
||||
...totals,
|
||||
...gstSplit,
|
||||
updated_by: userId ? BigInt(userId) : null,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user