...
Updating Allocated/Unavailable/Available and Other Running Totals
Option A: Have the business logic module perform calculations as money is moved
- Harder to guarantee consistency/accuracy - is eventual consistency OK for finances?
- Keeps business logic out of the storage module, consistent with approach taken in other acquisition apps
Option B: Perform summary/total calculations in the storage module and update all tables within a transaction. Either they all succeed, or the entire operation fails
- Much better control over consistency/accuracy
- Bleeds business logic into the storage module
- Other business logic remains in mod-finance - e.g. acquisitionUnits, what can be modified vs what can't, etc.
Option C: Calculate these numbers on-the-fly in the business logic module
- Difficult/inefficient to do if these numbers are needed to be shown in search results (they are)
- Likely means we'll need a more complicated data model with more views and schemas
- Since we're calculating these numbers on the fly, consistency is less of a problem... the numbers only live in one place
Option D: Hybrid of B and C
- Calculate most of these totals in the storage module and update all tables in a transaction
- For some special cases, we need to perform calculations on the fly
- e.g. GET groups for a ledger/FY - Only sum the amounts from the funds in those groups which are part of the ledger for this FY...
...
The "Remaining Allowable Exp." in the flowchart is calculated as follows:
[remaining amount we can expend] = (allocated * allowableExpenditure) - (allocated - (unavailable + available)) - (awaitingPayment + expended)
[remaining amount we can expend] = (totalFunding * allowableExpenditure) - unavailable + relatedEncumbranceForPendingPayment
The "restrictExpenditures" flag actually lives in the ledger, so we'll need to find traverse: budget → fund → ledger.restrictExpenditures
...
The "Remaining Allowable Enc." in the flowchar is calculated as follows:
[remaining amount we can encumber] = (allocated * allowableEncumbered) - (encumbered + awaitingPayment + expended)
[remaining amount we can encumber] = (totalFunding * allowableEncumbered) - unavailable
The "restrictEncumbrances" flag actually lives in the ledger, so we'll need to find traverse: budget → fund → ledger.restrictEncumbrances
...
Property | Type | Default | Required | Updated On Transaction | Notes | Requirements tickets | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id | string | N | N | UUID - system generated if not specified | ||||||||||
name | string | Y | N | = <fund.code>-<fiscal_year.code>, eg. AFRICAHIST-FY19. Unique | ||||||||||
budgetStatus | string | Y | N | enum: Active, Frozen, Planned, Closed | ||||||||||
allowableEncumbrance | number | N | N | percentage | ||||||||||
allowableExpenditure | number | N | N | percentage | ||||||||||
initialAllocation | number | N | Y | First allocation transaction amount | ||||||||||
allocationTo | number | N | Y | Total of allocations to this budget | ||||||||||
allocationFrom | number | N | Y | Total of allocations from this budget | ||||||||||
allocated | number | 0 | YN | Y | Running total of all the allocation transactions against the budget. = initialAllocation + allocationTo - allocationFrom | |||||||||
totalFunding | number | N | Y | = allocated + netTransfers | ||||||||||
cashBalance | number | N | Y | = totalFunding - expenditures | ||||||||||
awaitingPayment | number | N | Y (encumbrance only) | Running total of all the invoiced amounts (i.e. waiting to be paid) | ||||||||||
available | number | 0 | N | Y | = (allocation + <transfers>) - awaiting_payment - encumbered - expendituresmax(totalFunding - unavailable) NOTE: Doesn't include allowableEncumbered amount | |||||||||
encumbered | number | N | Y (encumbrance only) | Running total of money set aside for purchases | ||||||||||
expenditures | number | 0 | N | Y (encumbrance only) | Running total of all the payments recorded by the fund | |||||||||
unavailable | number | N | Y | = awaiting_payment encumbered + encumbered awaitingPayment + expenditures NOTE: Doesn't include overEncumbered amount | ||||||||||
overEncumbrance | number | N | Y (encumbrance only) | Amount the budget is over encumbered. i.e. overEncumbered = MAXmax(0, encumbered - max(MAXmax(totalFunding - expenditures, 0, (allocated - awaitingPayment - expended)) - awaytingPayment, 0), 0) | ||||||||||
overExpended | number | N | Y (encumbrance only) | Amount the budget is over expended. i.e. overExpended = max(0, expended awaytingPayment + awaitingPayment expenditures - allocatedtotalFunding, 0) | ||||||||||
netTransfers | number | 0 | N | Y | This would actually be calculated by summing all the "Transfers" on this budget. |
| ||||||||
fundId | string | Y | N | UUID of fund | ||||||||||
fiscalYearId | string | Y | N | UUID of fiscal year | ||||||||||
ledgerId | string | Y | N | UUID of ledger | ||||||||||
acquisitionUnits | array<string> | N | N | Array of UUIDs corresponding to the acquisition units assigned to this budget. | ||||||||||
tags | tags | N | N | inherited from fund | ||||||||||
metadata | metadata | N | Y | System generated metadata (createdBy/updatedBy/createdOn/etc.) |
...