5-Layer commitment model that tracks every rupee from budget allocation through final payment — ensuring real-time visibility into financial obligations at any project phase.
InfraTraq implements a 5-Layer Commitment Accounting model that tracks every rupee from budget allocation through final payment. This ensures real-time visibility into financial obligations at any project phase, preventing budget overruns and enabling proactive cash-flow management.
| Status | Description | Allowed Actions | Next States |
|---|---|---|---|
| Draft | Budget/commitment created but not yet approved | Edit, Submit for Approval, Delete | Pending Approval |
| Pending Approval | Awaiting budget holder or finance manager approval | Approve, Reject, Return | Approved, Rejected |
| Approved | Budget allocated or commitment confirmed | Amend, Consume (via PO/GRN) | Open, Amended |
| Open | Active commitment with remaining balance | Invoice Match, Part-Pay, Amend | Partial, Closed |
| Partial | Commitment partially invoiced/paid | Invoice Match, Pay, Short-Close | Closed |
| Closed | Fully delivered and paid; no remaining balance | View, Audit | — |
| Cancelled | Budget or commitment cancelled; balance released | View, Audit | — |
| Rejected | Approval denied; returned to originator | Edit, Resubmit, Delete | Draft |
Annual/project budget allocation per cost code. Finance team creates budget lines with monthly phasing (m1-m12) for cash-flow forecasting. Approved budget sets the spending ceiling.
PO/WO value committed to vendors/subcontractors. Created automatically when PO or WO is approved. Budget availability check runs before approval.
GRN/DMB received but not yet invoiced. Creates provisional journal entries debiting expense and crediting accrual liability.
Invoice booking after 3-way match reverses accruals and posts final JE. Payment updates commitment and posts bank entries. TDS tracked separately.
Budget ≥ Commitment ≥ Accrual ≥ Invoice ≥ Paymentbudget_id — PK, budget header per project/entity/periodproject_id — FK → project.projectentity_id — FK → organization.company_entitybudget_number, budget_type, fiscal_yeartotal_amount, currency, statusid — PK, budget line items per account/cost codebudget_id — FK → finance.budgetaccount_id — FK → finance.gl_accountcost_code_id — FK → master_data.cost_codeannual_amount, m1..m12 — Monthly phasingrevised_amount, actual_amount, variancecommitment_id — PK, financial commitment trackingproject_id — FK → project.projectpo_id — FK → procurement.purchase_orderwo_id — FK → subcontractor.work_ordercommitment_type — PO or WOoriginal_amount, revised_amount, invoiced_amount, paid_amount, balancestatus — open, partial, closed, cancelledid — PK, commitment line items per GL accountcommitment_id — FK → finance.commitmentaccount_id — FK → finance.gl_accountdescription, amount, invoiced, paid, balanceid — PK, month-end provisional (accrual) journal entriesentity_id — FK → organization.company_entityentry_type — accrual, reversalreference_type, reference_id — Source documentamount, je_id — FK → finance.journal_entryis_reversed, statusje_id — PK, GL journal entries for all commitment layer postingsje_type — commitment, accrual, reversal, invoice, paymentsource_module, reference_type, reference_idtotal_debit, total_credit, statusaccount_id — FK → finance.gl_account (on journal_line)cost_code_id — FK → master_data.cost_code (on journal_line)Finance team creates budget + budget_line records per project, cost code, and GL account. Monthly phasing (m1-m12) enables cash-flow forecasting. Approved budget sets the ceiling.
When a Purchase Order or Work Order is approved, the system auto-creates a commitment record with original_amount = PO/WO total. Each line maps to a GL account via commitment_line. Budget availability check runs before approval — if insufficient budget, the PO/WO is blocked.
When goods are received (GRN) or work is measured (DMB), the system creates a provisional_entry debiting expense and crediting accrual liability. This captures the obligation before the invoice arrives.
When the vendor's AP Invoice is booked after 3-way match (PO-GRN-Invoice), the provisional entry is reversed and a final journal_entry is posted. The commitment record's invoiced_amount is updated. Tax entries (GST/TDS) are auto-generated.
Payment Voucher creation updates commitment.paid_amount and posts bank/cash journal entries. TDS deducted is tracked separately. The commitment balance = revised_amount - paid_amount shows remaining obligation.
When PO/WO is fully delivered and paid, or cancelled with partial delivery, the commitment is closed. Any remaining balance is released back to the available budget. Short-close entries reverse the uncommitted portion.
class CommitmentService { /** Check if budget has sufficient balance for a new commitment */ async checkBudgetAvailability(projectId, costCodeId, amount) { const budget = await BudgetLine.findOne({ project_id: projectId, cost_code_id: costCodeId, status: 'approved' }); const committed = await Commitment.sum('revised_amount', { project_id: projectId, cost_code_id: costCodeId, status: ['open', 'partial'] }); const available = budget.revised_amount - committed; if (amount > available) throw new InsufficientBudgetError(available, amount); return { available, requested: amount, remaining: available - amount }; } /** Create commitment when PO/WO is approved */ async createCommitment(referenceType, referenceId) { const doc = await (referenceType === 'PO' ? PurchaseOrder.findById(referenceId) : WorkOrder.findById(referenceId)); await this.checkBudgetAvailability(doc.project_id, doc.cost_code_id, doc.total_amount); const commitment = await Commitment.create({ project_id: doc.project_id, commitment_type: referenceType, po_id: referenceType === 'PO' ? referenceId : null, wo_id: referenceType === 'WO' ? referenceId : null, original_amount: doc.total_amount, revised_amount: doc.total_amount, invoiced_amount: 0, paid_amount: 0, balance: doc.total_amount, status: 'open' }); // Post commitment journal: Dr Commitment Control, Cr Commitment Liability await JournalService.postCommitment(commitment); return commitment; } /** Create accrual when GRN/DMB is approved */ async createAccrual(commitmentId, grnOrDmbId, amount) { const entry = await ProvisionalEntry.create({ entity_id, entry_type: 'accrual', reference_type: 'GRN', reference_id: grnOrDmbId, amount, status: 'active' }); // Post provisional JE: Dr Expense, Cr Accrual Liability await JournalService.postAccrual(entry); return entry; } /** Update commitment when invoice is booked */ async onInvoiceBooked(commitmentId, invoiceAmount) { await Commitment.update(commitmentId, { invoiced_amount: literal('invoiced_amount + ' + invoiceAmount), balance: literal('revised_amount - paid_amount') }); await ProvisionalEntry.reverse(commitmentId); } /** Update commitment when payment is released */ async onPaymentReleased(commitmentId, paidAmount) { await Commitment.update(commitmentId, { paid_amount: literal('paid_amount + ' + paidAmount), balance: literal('revised_amount - paid_amount') }); const c = await Commitment.findById(commitmentId); if (c.balance <= 0) await c.update({ status: 'closed' }); } }
| Rule | Condition | Action |
|---|---|---|
| Budget Ceiling Check | New PO/WO amount > available budget | Block approval, notify finance manager |
| Over-commitment Alert | Committed > 90% of budget | Warning notification to PM and Finance |
| Tolerance Variance | Invoice amount > PO amount + tolerance% | Require additional approval level |
| Period Lock | Transaction date in locked period | Reject entry, suggest next open period |
| Duplicate Commitment | Same PO/WO already has active commitment | Block creation, show existing record |
| Amendment Recheck | PO/WO amendment changes value | Rerun budget check for delta amount |
| Event | Source Table | Auto Action |
|---|---|---|
| PO Approved | procurement.purchase_order | Create commitment + post JE |
| WO Approved | subcontractor.work_order | Create commitment + post JE |
| GRN Accepted | receiving.grn | Create accrual provisional entry + JE |
| DMB Approved | subcontractor.dmb | Create accrual provisional entry + JE |
| Invoice 3-Way Matched | accounts_payable.ap_invoice | Reverse accrual + final JE + update commitment |
| Payment Released | accounts_payable.payment_voucher | Update commitment.paid_amount + bank JE |
| PO/WO Amended | procurement.po_amendment | Update commitment.revised_amount + recheck budget |
| PO/WO Cancelled | procurement.purchase_order | Close commitment + release budget |
| Period Close | finance.fiscal_period | Generate all pending accruals for the period |
CHECK (paid_amount ≤ invoiced_amount ≤ revised_amount)commitment on (project_id, cost_code_id, status) for fast availability queriesaudit.audit_trail for all commitment state changes