226 lines
9.8 KiB
Nix
226 lines
9.8 KiB
Nix
{ core, ... }:
|
||
{ balance, balance', balanceToTransaction, from, outcome, ... }:
|
||
let
|
||
inherit(core) list set string time;
|
||
inherit(time) formatDate;
|
||
|
||
concatLines'
|
||
= function: lines: string.concatLines (list.filter (value: value != null) (list.map function lines));
|
||
|
||
lineLength = 111;
|
||
|
||
padNameValue#: string -> string
|
||
= name:
|
||
value:
|
||
currency:
|
||
extra:
|
||
let
|
||
value' = string.from value;
|
||
nameLength = string.lengthUTF8 name;
|
||
valueLength = ( string.length value' ) - 4;
|
||
currencyLength = string.lengthUTF8 currency;
|
||
length = lineLength - nameLength - valueLength - currencyLength - extra - 2;
|
||
padding = string.repeat " " length;
|
||
theValue = string.slice 0 valueLength value';
|
||
theValue' = if theValue == "-0.00" then " 0.00" else theValue;
|
||
in
|
||
"${name}:${padding}${theValue'} ${currency}";
|
||
|
||
listSectionTotal'#: int -> T -> string
|
||
# where T: { title: string, accounts: [ T | { name: string, ... } ], ... }
|
||
= { ... } @ self:
|
||
negative:
|
||
depth:
|
||
{ title, accounts, ... } @ this:
|
||
let
|
||
entries = listSectionTotal self negative ( depth + 1 ) accounts;
|
||
in
|
||
if entries != ""
|
||
then
|
||
" ${string.repeat " " depth}${title}:\n${entries}"
|
||
else
|
||
null;
|
||
|
||
listSectionTotal#: int -> T -> string
|
||
# where T: { title: string, accounts: [ T | { name: string, ... } ], ... }
|
||
= { currency, events ? {}, ... } @ self:
|
||
negative:
|
||
depth:
|
||
concatLines'
|
||
(
|
||
{ ... } @ entry:
|
||
let
|
||
filterSection = events.filterSection or ({ ... }: true);
|
||
total
|
||
= if negative
|
||
then
|
||
-entry.total
|
||
else
|
||
entry.total;
|
||
in
|
||
if entry ? name
|
||
then
|
||
" ${padNameValue "${string.repeat " " depth}${entry.name}" total currency 2}"
|
||
else if filterSection entry
|
||
then
|
||
listSectionTotal' self negative depth entry
|
||
else
|
||
" ${padNameValue "${string.repeat " " depth}${entry.title}" total currency 2}"
|
||
);
|
||
|
||
formatBalance#: Self -> Date -> string
|
||
= dateTime:
|
||
self:
|
||
formatBalance' ( balance dateTime self );
|
||
|
||
formatBalance'#: Self -> string
|
||
= { name, currency, dateTime, assets, liabilities, outcome, journal, events ? {}, ... } @ self:
|
||
let
|
||
formatBalanceTitle
|
||
= events.formatBalanceTitle
|
||
or (
|
||
{ name, dateTime, ...}:
|
||
"Consolidated Statement Finance Positions of ${name} as of ${formatDate dateTime "eng"}"
|
||
);
|
||
title = formatBalanceTitle { inherit name dateTime; };
|
||
formatAccountNames
|
||
= events.formatAccountNames
|
||
or (
|
||
{ ... }:
|
||
{
|
||
balance = "Assets, Liabilities and Equity";
|
||
outcome = "Expenses and Revenues";
|
||
revenues = "Revenues";
|
||
expenses = "Expenses";
|
||
}
|
||
);
|
||
formatBalanceNames
|
||
= events.formatBalanceNames
|
||
or (
|
||
{ ... }:
|
||
{
|
||
credit = "Credit";
|
||
debit = "Debit";
|
||
total = "Total";
|
||
}
|
||
);
|
||
filterSection = events.filterSection or ( { ... }: true );
|
||
accountNames = formatAccountNames {};
|
||
balanceNames = formatBalanceNames {};
|
||
|
||
listSections'#: [ Transaction ] -> bool -> int -> T -> string
|
||
# where T: { title: string, accounts: [ T | { name: string, ... } ], ... }
|
||
= allTransactions:
|
||
depth:
|
||
{ title, accounts, ... }:
|
||
let
|
||
entries = listSections allTransactions depth accounts;
|
||
in
|
||
if entries != ""
|
||
then
|
||
"${string.repeat " " depth} ${title}:\n${entries}"
|
||
else
|
||
null;
|
||
|
||
listSections#: [ Transaction ] -> bool -> int -> T -> string
|
||
# where T: { title: string, accounts: [ T | { name: string, ... } ], ... }
|
||
= allTransactions:
|
||
depth:
|
||
let
|
||
padding = string.repeat " " depth;
|
||
formatAccount#: int -> { name, credit, debit, total, ... } -> string
|
||
= { name, credit, debit, total, ... }:
|
||
let
|
||
listTransactions#: [ Transaction ] -> [ { amount: Amount, reference: int, ... } ] -> string
|
||
= journal:
|
||
list.concatMap
|
||
(
|
||
{ amount, reference, ... }:
|
||
let
|
||
transaction
|
||
= list.get allTransactions reference;
|
||
in
|
||
"\n${padding} ${padNameValue transaction.description amount currency ( 8 + 2 * depth )}"
|
||
)
|
||
journal;
|
||
in
|
||
if debit.journal != []
|
||
|| credit.journal != []
|
||
then
|
||
''
|
||
${padding} ${name}
|
||
${padding} ${balanceNames.debit}:${listTransactions debit.journal}
|
||
${padding} ${string.repeat "–" (lineLength - 8 - 2 * depth)}
|
||
${padding} ${padNameValue balanceNames.total debit.total currency ( 8 + 2 * depth)}
|
||
${padding} ${balanceNames.credit}:${listTransactions credit.journal}
|
||
${padding} ${string.repeat "–" (lineLength - 8 - 2 * depth)}
|
||
${padding} ${padNameValue balanceNames.total credit.total currency ( 8 + 2 * depth)}
|
||
''
|
||
else
|
||
null;
|
||
in
|
||
concatLines'
|
||
(
|
||
{ ... } @ entry:
|
||
if entry ? name
|
||
then
|
||
formatAccount entry
|
||
else
|
||
listSections' allTransactions ( depth + 1 ) entry
|
||
);
|
||
|
||
expensesAndRevenues = list.partition ({ total, ... }: total >= 0) outcome.accounts;
|
||
expenses = { title = accountNames.expenses; accounts = expensesAndRevenues.wrong; };
|
||
revenues = { title = accountNames.revenues; accounts = expensesAndRevenues.right; };
|
||
in
|
||
''
|
||
== ${accountNames.balance} ==
|
||
=== ${assets.title} ===
|
||
${listSections journal 0 assets.accounts}
|
||
=== ${liabilities.title} ===
|
||
${listSections journal 0 liabilities.accounts}
|
||
== ${accountNames.outcome} ==
|
||
=== ${expenses.title} ===
|
||
${listSections journal 0 expenses.accounts}
|
||
=== ${revenues.title} ===
|
||
${listSections journal 0 revenues.accounts}
|
||
== ${title} ==
|
||
=== ${assets.title} ===
|
||
${listSectionTotal self false 0 assets.accounts}
|
||
${string.repeat "=" ( lineLength - 2)}
|
||
${padNameValue balanceNames.total assets.total currency 2}
|
||
|
||
=== ${liabilities.title} ===
|
||
${listSectionTotal self true 0 liabilities.accounts}
|
||
${string.repeat "=" ( lineLength - 2)}
|
||
${padNameValue balanceNames.total (- liabilities.total) currency 2}
|
||
'';
|
||
|
||
formatOutcome#: Self -> { from: D, till: D } -> string
|
||
# where D -> DateTime
|
||
= { from, till } @ period:
|
||
self:
|
||
formatOutcome' (outcome period self);
|
||
|
||
formatOutcome'#: Self -> { from: D, till: D } -> string
|
||
# where D -> DateTime
|
||
= { name, currency, from, till, events, outcome, ... } @ self:
|
||
let
|
||
formatOutcomeTitle
|
||
= events.formatOutcomeTitle
|
||
or (
|
||
{ name, from, till, ...}:
|
||
"Income statement of ${name} between ${time.formatDate from "eng"} and ${time.formatDate till "eng"}"
|
||
);
|
||
title = formatOutcomeTitle { inherit name from till; };
|
||
total = -1.0 * outcome.total;
|
||
in
|
||
''
|
||
${formatBalance' self}
|
||
== ${title} ==
|
||
${listSectionTotal self true 0 outcome.accounts}
|
||
${string.repeat "=" ( lineLength - 2)}
|
||
${padNameValue outcome.title total currency 2}
|
||
'';
|
||
in
|
||
{ inherit formatBalance formatOutcome; } |