Skip to content

Commit

Permalink
todo: add statements
Browse files Browse the repository at this point in the history
  • Loading branch information
epogrebnyak committed Jan 21, 2024
1 parent ba7b88f commit 00920e3
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 37 deletions.
40 changes: 26 additions & 14 deletions core/test_uncore.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ def test_chart_contra_pairs():

def test_chart_creation():
chart0 = (
Chart("income_summary_account", "retained_earnings")
Chart(
income_summary_account="income_summary_account",
retained_earnings_account="retained_earnings",
)
.add(T.Income, "sales", contra_names=["refunds", "voids"])
.add(T.Asset, "cash")
.add(T.Capital, "equity", contra_names=["buyback"])
Expand All @@ -75,9 +78,8 @@ def test_chart_creation():


def test_journal_creation():
assert Journal.new(
ChartDict(cash=T.Asset, contra_cash=Reference("cash")), "isa", "re"
) == Journal(
j = Journal.new(ChartDict(cash=T.Asset, contra_cash=Reference("cash")))
assert j.set_isa("isa").set_re("re") == Journal(
{
"cash": Account(Regular(T.Asset)),
"contra_cash": Account(Contra(T.Asset)),
Expand Down Expand Up @@ -125,7 +127,7 @@ def journal(chart):

def test_journal_has_retained_earnings(chart):
j = Journal.from_chart(chart)
assert j[chart.retained_earnings_account]
assert j[chart.retained_earnings_account].account_type == Regular(T.Capital)


def test_balances(journal):
Expand Down Expand Up @@ -170,29 +172,27 @@ def pipeline(chart, journal):


def test_moves_income(pipeline):
assert pipeline.close_contra([T.Income]).moves == [
assert pipeline.close_contra(T.Income).moves == [
Move(frm="refunds", to="sales"),
Move(frm="voids", to="sales"),
]


def test_moves_capital(pipeline):
assert pipeline.close_contra([T.Capital]).moves == [
Move(frm="buyback", to="equity")
]
assert pipeline.close_contra(T.Capital).moves == [Move(frm="buyback", to="equity")]


def test_moves_close_temp(pipeline):
assert pipeline.close_contra(
[T.Income, T.Expense]
T.Income, T.Expense
).flush().close_temporary().moves == [
Move(frm="sales", to="income_summary_account"),
Move(frm="salary", to="income_summary_account"),
]


def test_eq(pipeline, chart):
p = pipeline.close_contra([T.Income, T.Expense]).close_temporary()
def test_equate_close_isa_entry(pipeline, chart):
p = pipeline.close_contra(T.Income, T.Expense).close_temporary()
a = p.flush().close_isa().closing_entries
b = [
double_entry(
Expand All @@ -204,11 +204,23 @@ def test_eq(pipeline, chart):
assert a == b


def test_ledger_does_not_change_after_pipeline(chart, journal):
def test_ledger_does_not_change_after_pipeline_application(chart, journal):
assert journal.balances["retained_earnings"] == 0
assert close(chart, journal).balances["retained_earnings"] == 12
assert journal.balances["retained_earnings"] == 0
assert journal.tuples["retained_earnings"] == (0, 0)


def test_equity_nets_out(chart, journal):
def test_equity_nets_out_to_1000(chart, journal):
assert close(chart, journal).balances["equity"] == 1000


def test_close_pipeline_logic(chart, journal):
assert close(chart, journal) == (
Pipeline(chart, journal)
.close_contra(T.Income, T.Expense)
.close_temporary()
.close_isa()
.close_contra(T.Asset, T.Capital, T.Liability)
.journal
)
60 changes: 37 additions & 23 deletions core/uncore.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,17 @@ def which(t: T) -> Side:


def reverse(side: Side):
match side:
case Side.Debit:
return Side.Credit
case Side.Credit:
return Side.Debit
if side == Side.Debit:
return Side.Credit
elif side == Side.Credit:
return Side.Debit


@dataclass
class Reference:
"""Reference class is used for contra account definition.
`points_to` refers to 'parent' account name."""

points_to: str


Expand Down Expand Up @@ -98,7 +100,8 @@ def offset(self, name: str, contra_name: str):
return self

def contra_pairs(self, t: T) -> list[tuple[str, str]]:
"""List contra accounts, similar to `[('sales', 'refunds')]`."""
"""List contra accounts, result should similar to
`[('sales', 'refunds'), ('sales', 'voids')]`."""
return [
(value.points_to, name)
for name, value in self.items()
Expand All @@ -111,11 +114,12 @@ def regular_names(self, *ts: T) -> list[str]:

def account_type(self, name) -> Contra | Regular:
"""Return regular or contra account type based on `name`."""
match self[name]:
case Reference(links_to):
return Contra(t=self[links_to])
case t:
return Regular(t)
what = self[name]
if isinstance(what, Reference):
t: T = self[what.points_to] # type: ignore
return Contra(t)
elif isinstance(what, T):
return Regular(what)


@dataclass
Expand Down Expand Up @@ -185,9 +189,12 @@ def credit(self, amount: Amount) -> None:
self.credits.append(amount)

def tuple(self):
"""Return account debits and credit side balance."""
return sum(self.debits), sum(self.credits)

def condense(self):
"""Return new account of same type with account balance
on proper side debit or credit side."""
match self.account_type.side:
case Side.Debit:
a, b = [self.balance()], []
Expand All @@ -196,6 +203,7 @@ def condense(self):
return self.__class__(self.account_type, a, b)

def balance(self):
"""Return account balance."""
a, b = self.tuple()
match self.account_type.side:
case Side.Debit:
Expand All @@ -205,31 +213,37 @@ def balance(self):


class Journal(UserDict[str, Account]):
"""Ledger that holds T-accounts. Each T-account is referenced by unique name."""

@classmethod
def new(
cls,
chart_dict: ChartDict,
income_summary_account: str,
retained_earnings_account: str,
):
journal = cls()
for key in chart_dict.keys():
journal[key] = Account(chart_dict.account_type(key))
journal[retained_earnings_account] = Account(Regular(T.Capital))
journal[income_summary_account] = Account(Intermediate(Side.Credit))
return journal

@classmethod
def from_chart(
cls,
chart: Chart,
):
return cls.new(
chart_dict=chart.dict,
income_summary_account=chart.income_summary_account,
retained_earnings_account=chart.retained_earnings_account,
return (
cls.new(chart.dict)
.set_isa(chart.income_summary_account)
.set_re(chart.retained_earnings_account)
)

def set_isa(self, name):
self[name] = Account(Intermediate(Side.Credit))
return self

def set_re(self, name):
self[name] = Account(Regular(T.Capital))
return self

def post(self, entry: Entry):
if not is_balanced(entry):
raise ValueError(entry)
Expand Down Expand Up @@ -263,7 +277,7 @@ def subset(self, t: T):

@dataclass
class Move:
"""Indicate where from and where to transfer the account balances.
"""Indicate how to transfer the account balances.
Helps to trace closing entries."""

frm: str
Expand Down Expand Up @@ -299,7 +313,7 @@ def move(self, frm: str, to: str):
self.journal.post(entry)
return self

def close_contra(self, ts: list[T]):
def close_contra(self, *ts: T):
"""Close contra accounts that offset accounts of types `ts`."""
for t in ts:
for name, contra_name in self.chart.dict.contra_pairs(t):
Expand All @@ -322,7 +336,7 @@ def close_isa(self):
def close_first(self):
"""Close contra accounts to income and expenses.
Makes journal ready for income statement."""
return self.close_contra([T.Income, T.Expense])
return self.close_contra(T.Income, T.Expense)

def close_second(self):
"""Close income summary account and move balance to retained earnings.
Expand All @@ -331,7 +345,7 @@ def close_second(self):

def close_last(self):
"""Do netting (close contra accounts) for permanent accounts."""
return self.close_contra([T.Asset, T.Capital, T.Liability])
return self.close_contra(T.Asset, T.Capital, T.Liability)

def flush(self):
self.closing_entries = []
Expand Down

0 comments on commit 00920e3

Please sign in to comment.