exchangealgebra 0.4.1.1 → 0.4.1.2
raw patch · 5 files changed
+61/−11 lines, 5 files
Files
- ChangeLog.md +14/−0
- exchangealgebra.cabal +3/−3
- src/ExchangeAlgebra/Algebra/Transfer.hs +6/−4
- src/ExchangeAlgebra/Journal/Transfer.hs +6/−4
- test/Spec.hs +32/−0
ChangeLog.md view
@@ -1,5 +1,19 @@ # Changelog for ExchangeAlgebra +## 0.4.1.2 - 2026-06-11++### Fixed+- `incomeSummaryAccount` (both `ExchangeAlgebra.Algebra.Transfer` and+ `ExchangeAlgebra.Journal.Transfer`) crashed with `Non-exhaustive patterns in+ case` on a **balanced ledger** (credit == debit, i.e. zero net income). In that+ case `diffRL` reports the wildcard `Side` constructor, which the+ `case dc of { Credit -> …; Debit -> … }` did not handle. The fix adds a `Side`+ branch that returns the input ledger unchanged (no `NetIncome` / `NetLoss`+ posting is appended when net income is zero). Note that appending a `Zero`+ posting is **not** a correct alternative for the Journal version, since it is+ not an identity there. Covered by the new `testIncomeSummaryBalancedAlg` and+ `testIncomeSummaryBalancedJournal` regression tests.+ ## 0.4.1.1 - 2026-06-07 ### Fixed
exchangealgebra.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.39.1. -- -- see: https://github.com/sol/hpack ----- hash: fe5fab919197e65dce7b698dc174e0447987248ecd7a81108d260e187a1a248f+-- hash: d3a05562cbe70211374afd6c39a40fd7f8ff7e7b23b90f74716875f339171ea4 name: exchangealgebra-version: 0.4.1.1+version: 0.4.1.2 synopsis: Exchange Algebra for bookkeeping and economic simulation description: Please see the README on GitHub at <https://github.com/yakagika/ExchangeAlgebra#readme> category: Accounting, Finance, Math
src/ExchangeAlgebra/Algebra/Transfer.hs view
@@ -535,12 +535,14 @@ -- * Closing transfer entries -- | Income Summary Account: compute net income for the current period.+-- When the ledger is balanced (credit == debit, net income is zero), @diffRL@+-- reports the wildcard 'Side'; in that case the input is returned unchanged. incomeSummaryAccount :: (HatVal n, ExBaseClass b) => Alg n b -> Alg n b incomeSummaryAccount alg = let (dc,diff) = diffRL alg- in let x = case dc of- Credit -> diff :@ (toNot wiledcard) .~ NetIncome- Debit -> diff :@ (toNot wiledcard) .~ NetLoss- in alg .+ x+ in case dc of+ Credit -> alg .+ (diff :@ (toNot wiledcard) .~ NetIncome)+ Debit -> alg .+ (diff :@ (toNot wiledcard) .~ NetLoss)+ Side -> alg -- | Net income transfer. Transfers NetIncome/NetLoss to RetainedEarnings. --
src/ExchangeAlgebra/Journal/Transfer.hs view
@@ -97,14 +97,16 @@ -- | Compute net income for the current period (Income Summary Account). -- Calculate the debit-credit difference and add it as NetIncome or NetLoss to the plank Note.+-- When the ledger is balanced (credit == debit, net income is zero), @diffRL@ reports the+-- wildcard 'Side'; in that case the journal is returned unchanged. -- -- Complexity: O(s) (s = total number of scalar entries) incomeSummaryAccount :: (Note n, HatVal v, ExBaseClass b) => Journal n v b -> Journal n v b incomeSummaryAccount js = let (dc,diff) = diffRL js- in let x = case dc of- Credit -> diff :@ (toNot wiledcard) .~ NetIncome- Debit -> diff :@ (toNot wiledcard) .~ NetLoss- in js .+ ( x .| plank)+ in case dc of+ Credit -> js .+ ((diff :@ (toNot wiledcard) .~ NetIncome) .| plank)+ Debit -> js .+ ((diff :@ (toNot wiledcard) .~ NetLoss) .| plank)+ Side -> js -- | Net income transfer (Journal version). Transfer NetIncome/NetLoss to RetainedEarnings for each Note. --
test/Spec.hs view
@@ -381,6 +381,36 @@ actual = EJT.finalStockTransfer transferJournalSample assertEqual "Journal.finalStockTransfer matches composed transfer" (EJ.toMap ref) (EJ.toMap actual) +-- A ledger whose Debit and Credit sides are equal (net income is zero).+-- @diffRL@ reports the wildcard 'Side' here, which used to crash+-- @incomeSummaryAccount@ with "Non-exhaustive patterns" (regression for 0.4.1.2).+balancedTransferAlg :: TransferAlg+balancedTransferAlg = EA.fromList+ [ 5 :@ Not :<(Cash, 1, 1, Yen) -- Assets -> Debit side+ , 5 :@ Not :<(Sales, 2, 1, Yen) -- Revenue -> Credit side+ ]++balancedTransferJournal :: TransferJournal+balancedTransferJournal = balancedTransferAlg .| "balanced"++-- | Regression: a balanced ledger (credit == debit) must not crash+-- @incomeSummaryAccount@; it returns the input unchanged.+testIncomeSummaryBalancedAlg :: IO ()+testIncomeSummaryBalancedAlg = do+ let actual = EAT.incomeSummaryAccount balancedTransferAlg+ assertEqual+ "Algebra.incomeSummaryAccount on balanced ledger returns input unchanged"+ (EA.toList balancedTransferAlg)+ (EA.toList actual)++testIncomeSummaryBalancedJournal :: IO ()+testIncomeSummaryBalancedJournal = do+ let actual = EJT.incomeSummaryAccount balancedTransferJournal+ assertEqual+ "Journal.incomeSummaryAccount on balanced ledger returns input unchanged"+ (EJ.toMap balancedTransferJournal)+ (EJ.toMap actual)+ type SpillRestoreJournal = EJ.Journal (String, Int) Double (HatBase CountUnit) testRestoreJournalFromBinarySpill :: IO ()@@ -802,6 +832,8 @@ testFilterByAxisWithDeltaUpdates testFinalStockTransferAlgEquivalence testFinalStockTransferJournalEquivalence+ testIncomeSummaryBalancedAlg+ testIncomeSummaryBalancedJournal testRestoreJournalFromBinarySpill testSimulateEx1Default testCsvTranspose