diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 11:08:05 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 11:08:05 +0800 |
| commit | 9818daebaf6fb99124a3196f880b3b8a7998d3a6 (patch) | |
| tree | 0b027703ff1ff164a89965723cfd9d5a3fee5081 /src/FundLab.Api/App.fs | |
| parent | 8a68f4a98f51ff05b00bb7c14da7bca2a8c6eb98 (diff) | |
| download | fund-lab-9818daebaf6fb99124a3196f880b3b8a7998d3a6.tar.gz | |
Add cash ledger event-source breakdown and reconciliation API/UI (3d-31 B4c)
Diffstat (limited to 'src/FundLab.Api/App.fs')
| -rw-r--r-- | src/FundLab.Api/App.fs | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/FundLab.Api/App.fs b/src/FundLab.Api/App.fs index d5c3a50..3d89dae 100644 --- a/src/FundLab.Api/App.fs +++ b/src/FundLab.Api/App.fs @@ -487,6 +487,35 @@ type CapitalDepositResponse = createdAt: string } +type CashLedgerEventResponse = + { + source: string + eventDate: string + amount: string + referenceType: string + referenceId: string + note: string option + } + +type CashLedgerSourceResponse = + { + source: string + netAmount: string + eventCount: int + } + +type CashReconciliationResponse = + { + fundId: Guid + openingCash: string + netInflow: string + closingCash: string + ledgerBalance: string + difference: string + sources: CashLedgerSourceResponse list + events: CashLedgerEventResponse list + } + type FundReturnsPointResponse = { date: string @@ -846,6 +875,39 @@ module App = createdAt = timestampText deposit.CreatedAt } + let private cashLedgerEventResponse (event: CashLedger.CashLedgerEvent) : CashLedgerEventResponse = + { + source = CashLedger.sourceText event.Source + eventDate = dateText event.EventDate + amount = cashText event.Amount + referenceType = event.ReferenceType + referenceId = event.ReferenceId + note = event.Note + } + + let private cashReconciliationResponse + (fundId: Guid) + (reconciliation: CashLedger.CashReconciliation) + (events: CashLedger.CashLedgerEvent list) + : CashReconciliationResponse = + { + fundId = fundId + openingCash = cashText reconciliation.OpeningCash + netInflow = cashText reconciliation.NetInflow + closingCash = cashText reconciliation.ClosingCash + ledgerBalance = cashText reconciliation.LedgerBalance + difference = cashText reconciliation.Difference + sources = + reconciliation.Sources + |> List.map (fun total -> + { + source = CashLedger.sourceText total.Source + netAmount = cashText total.NetAmount + eventCount = total.EventCount + }) + events = events |> List.map cashLedgerEventResponse + } + let private sipPlanResponse (plan: SipPlanRecord) : SipPlanResponse = { id = plan.Id @@ -1763,6 +1825,20 @@ module App = with _ -> errorResponse 500 "PERSISTENCE_ERROR" "capital deposit persistence failed" next ctx + let private getCashReconciliation (repository: FundRepository) (fundIdText: string) : HttpHandler = + fun next ctx -> + match Guid.TryParse fundIdText with + | false, _ -> errorResponse 400 "INVALID_FUND_ID" "fund id must be a UUID" next ctx + | true, fundId -> + try + match repository.GetCashReconciliation fundId with + | Error _ -> errorResponse 404 "FUND_NOT_FOUND" "fund was not found" next ctx + | Ok reconciliation -> + let events = repository.GetCashLedger fundId + json (cashReconciliationResponse fundId reconciliation events) next ctx + with _ -> + errorResponse 500 "PERSISTENCE_ERROR" "cash reconciliation failed" next ctx + let private createSipPlan (repository: FundRepository) (fundIdText: string) : HttpHandler = fun next ctx -> task { @@ -3555,6 +3631,7 @@ module App = GET >=> routef "/funds/%s/positions" (getPositions repository) POST >=> routef "/funds/%s/capital/deposit" (createCapitalDeposit repository) GET >=> routef "/funds/%s/capital/deposits" (getCapitalDeposits repository) + GET >=> routef "/funds/%s/cash-reconciliation" (getCashReconciliation repository) POST >=> routef "/funds/%s/sip/plans" (createSipPlan repository) GET >=> routef "/funds/%s/sip/plans" (getSipPlans repository) POST >=> routef "/funds/%s/sip/advance" (advanceSipPlans repository probes) |
