usawa

Signed, immutable accounting.
Log | Files | Refs | Submodules | LICENSE

commit 9596246ed4ae2428e404f21b132c681d839cc7a6
parent 895a86e94f07ceffb2cd63eb1a67aaf659c59680
Author: lash <dev@holbrook.no>
Date:   Sat,  4 Apr 2026 18:02:25 -0600

Implement accumulator in balancer, correct adjustments

Diffstat:
Mdummy/tests/balance.py | 96++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mdummy/usawa/balance.py | 51+++++++++++++++++++++++++++++++--------------------
2 files changed, 112 insertions(+), 35 deletions(-)

diff --git a/dummy/tests/balance.py b/dummy/tests/balance.py @@ -13,6 +13,7 @@ testdir = os.path.realpath(os.path.dirname(__file__)) class TestBalancer(unittest.TestCase): + def setUp(self): self.parent = bytes.fromhex('0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6') self.ref = '1bda7dfa-b8fd-400d-8b42-1d2861ad7f70' @@ -22,16 +23,6 @@ class TestBalancer(unittest.TestCase): self.dtreg = datetime.datetime.now() -# def test_balancer_process(self): -# dst = EntryPart('FOO', 'asset', 'foo', 1337) -# src = EntryPart('FOO', 'income', 'baz', 1337, debit=True) -# entry = Entry(42, datetime.datetime.strptime('2025-11-11', '%Y-%m-%d'), parent=self.parent, ref=self.ref, description=self.description, tx_datereg=self.dtreg) -# entry.add_part(src) -# entry.add_part(dst) -# o = Balancer(self.uidx) -# self.assertTrue(o.balanced()) -# - def test_balancer_parts_simple(self): o = Balancer(self.uidx) src = EntryPart('FOO', 'income', 'baz', 1337, debit=True) @@ -41,7 +32,7 @@ class TestBalancer(unittest.TestCase): self.assertTrue(o.balanced()) - def test_balancer_parts_simple(self): + def test_balancer_parts_exchange(self): o = Balancer(self.uidx) self.uidx.set_rate('BAR', 230000000) src = EntryPart('BAR', 'income', 'baz', 420000, debit=True) @@ -51,14 +42,89 @@ class TestBalancer(unittest.TestCase): self.assertTrue(o.balanced()) - def test_balancer_parts_simple(self): + def test_balance_accumulate_simple(self): o = Balancer(self.uidx) - self.uidx.set_rate('BAR', 230000000) - src = EntryPart('BAR', 'income', 'baz', 420000, debit=True) + src = EntryPart('FOO', 'asset', 'baz', -1337, debit=True) o.apply_part(src) - dst = EntryPart('FOO', 'asset', 'foo', 9660) + dst = EntryPart('FOO', 'asset', 'foo', 1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'liability', 'baz', 1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'asset', 'foo', 1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'liability', 'baz', 1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'liability', 'foo', -1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'asset', 'baz', -1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'liability', 'foo', -1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + + def test_flow_accumulate_simple(self): + o = Balancer(self.uidx) + src = EntryPart('FOO', 'asset', 'baz', -1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'expense', 'foo', 1337) o.apply_part(dst) self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'income', 'baz', 1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'asset', 'foo', 1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'income', 'baz', 1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'expense', 'foo', 1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + o = Balancer(self.uidx) + src = EntryPart('FOO', 'income', 'baz', 1337, debit=True) + o.apply_part(src) + dst = EntryPart('FOO', 'liability', 'foo', -1337) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + + + def test_balance_running(self): + o = Balancer(self.uidx) + src = EntryPart('FOO', 'liability', 'foo', 1337, debit=True) + o.apply_part(src) + self.assertEqual(o.balance(), -1337) + + dst = EntryPart('FOO', 'asset', 'bar', 42) + o.apply_part(dst) + self.assertEqual(o.balance(), -1295) + + dst = EntryPart('FOO', 'expense', 'baz', 1295) + o.apply_part(dst) + self.assertTrue(o.balanced()) + self.assertEqual(o.value(), 1337) + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/balance.py b/dummy/usawa/balance.py @@ -9,13 +9,14 @@ class Balancer: self.uidx = unitindex self.r = 0 self.m = 0 + self.z = 0 def apply_part(self, part): amount = self.uidx.val(part.unit, part.amount) - fn = getattr(self, 'handle_' + part.typ) - fn(amount[0], part.isdebit) - self.m += amount[1] + fn = getattr(self, '_handle_' + part.typ) + v = fn(amount[0], part.isdebit) + self.z += abs(amount[0]) logg.debug('after {} {} => {} = {}'.format(part.unit, part.amount, amount[0], self.r)) @@ -23,29 +24,39 @@ class Balancer: return self.r == 0 - def handle_income(self, amount, issrc=False): - if issrc: - self.r += amount - else: - self.r -= amount + def balance(self): + return self.r + + + def value(self): + return int(self.z / 2) + + + def _handle_income(self, amount, issrc=False): + if not issrc: + raise TypeError('income can only be src') + self.r -= amount + return amount - def handle_expense(self, amount, issrc=False): + def _handle_expense(self, amount, issrc=False): if issrc: - self.r -= amount - else: - self.r += amount + raise TypeError('expense can only be dst') + self.r += amount + return amount - def handle_asset(self, amount, issrc=False): + def _handle_asset(self, amount, issrc=False): if issrc: - self.r += amount - else: - self.r -= amount + if amount >= 0: + raise ValueError('positive asset can only be dst') + self.r += amount + return amount - def handle_liability(self, amount, issrc=False): + def _handle_liability(self, amount, issrc=False): if issrc: - self.r -= amount - else: - self.r += amount + if amount < 0: + raise ValueError('negative liability can only be dst') + self.r -= amount + return amount