-
Notifications
You must be signed in to change notification settings - Fork 115
Cost Calculation Framework in Procurement Bills
This article documents how costs, discounts, taxes, and expenses are recorded and calculated in procurement bills within the HMIS system. It describes both the financial rules and the technical safeguards that ensure consistency, auditability, and correctness.
-
Bill Represents a procurement transaction. Stores bill-level inputs and calculated aggregates. Related entity:
BillFinanceDetails. -
BillItem Represents each item/pack in the bill. Stores basic user inputs. Related entities:
BillItemFinanceDetails,PharmaceuticalBillItem. -
BillFinanceDetails Stores derived bill-level values (gross, net, totals).
-
BillItemFinanceDetails Stores detailed rates and totals for each line, including both user inputs and calculated values.
-
PharmaceuticalBillItem Records unit/pack details and ties BillItem to stock/medicine catalogue. One-to-one with BillItem for pharmacy.
billDiscountbillTax-
billExpensesIncluded(capitalizable into cost) -
billExpensesExcluded(not included in cost)
-
qty(purchased quantity in units/packs) -
freeQty(bonus units/packs at zero price) -
purchaseRate(PR) -
lineDiscountRate(DR) -
lineTaxRate(TR) -
lineExpenseRate(ER) -
retailRate(RSR) -
wholesaleRate(WSR)
-
LineGrossRate = PR
-
LineNetRate = PR + TR + ER – DR
-
LineCostRate = (LineNetTotal ÷ (QtyInUnits + FreeQtyInUnits))
- Always per unit
- For packs, multiply by
unitsPerPackif needed
-
LineGrossTotal = PR × Qty
-
LineDiscount = DR × Qty
-
LineTax = TR × Qty
-
LineExpense = ER × Qty
-
LineNetTotal = Gross + Tax + Expense – Discount
Bill-level values (discounts, taxes, expenses) are distributed across items proportional to each item’s LineNetTotal.
-
FreeQty is excluded from allocation base.
-
Stored separately as:
billDiscountValuebillExpenseValuebillTaxValuebillNetValue
- GrossTotal = LineGrossTotal
- TotalDiscount = LineDiscount + Allocated BillDiscount
- TotalTax = LineTax + Allocated BillTax
- TotalExpense = LineExpense + Allocated BillExpense
- NetTotal = LineNetTotal + BillNetValue
Per-unit rates = Total ÷ Qty.
- Cost Rate = (Paid Value ÷ (Qty + FreeQty))
- ValueAtRetailRate = RSR × (Qty + FreeQty)
- ValueAtWholesaleRate = WSR × (Qty + FreeQty)
- ValueAtPurchaseRate = PR × (Qty + FreeQty)
- ValueAtCostRate = CostRate × (Qty + FreeQty)
- Stock Inflow = Positive qty
- Stock Outflow = Negative qty
- Expenditure = Negative value
- Income = Positive value
-
Pack vs Unit Consistency
- Persist a snapshot of
unitsPerPackand tax regime in each BillItemFinanceDetails. - Prevents catalogue changes from altering historical bills.
- Persist a snapshot of
-
Allocation Precision
- Allocate with high-precision decimal.
- Round only at currency precision.
- Distribute rounding remainders deterministically (largest remainder first).
- Log allocations for audit.
-
Policy Toggles
- Inclusive vs exclusive tax.
- Bill expense capitalizable vs not.
- Persist these flags at Bill level.
-
Edge Cases
- Returns: reverse with historical cost rate.
- Donations: record zero cost unless fair-value policy applies.
- Mixed AMPs/AMPPs: calculate in units internally.
-
One-to-One Guarantees
- Enforce that each
BillItemhas exactly onePharmaceuticalBillItem(pharmacy) or onePatientInvestigation(lab). - Use DB unique constraints and data-repair routines.
- Enforce that each
-
Idempotence
- Calculation = pure function of persisted inputs.
- Re-running must not alter previous results.
- Stamp each bill with
calculationPolicyVersion.
- Order of Operations Document exact calculation sequence and rounding points.
- Test Suite Maintain canonical sample bills (with freeQty, mixed packs, returns) with expected outputs.
- Why Panel Expose in UI: for any BillItem, show inputs, allocated values, and rounding logic.