_SEP_ACCNAME_STEPS = ':'
SEP_COMMENTS = ';'
_PREFIX_INSTRUCTION = '#'
-_ACC_MOD_INSTRUCTORS = {'description', 'balancer'}
+_ACC_MOD_INSTRUCTORS = {'description', 'balancers'}
class _Wealth():
class _Account:
'Combine name, position in tree of owner, and wealth of self + children.'
description = ''
- balancer = ''
+ _balancers: tuple[str, ...] = tuple()
def __init__(self, parent: Optional[Self], basename: str) -> None:
self._wealth_diffs: dict[int, _Wealth] = {}
+ step_name)
yield rebuilt_path, step_name
+ @property
+ def balancers(self) -> tuple[str, ...]:
+ "Accounts among which this account's amount be balanced out."
+ return self._balancers
+
+ @balancers.setter
+ def balancers(self, balancers_str: str) -> None:
+ self._balancers = tuple(balancers_str.split())
+
class _DatLine:
'Line of .dat file parsed into comments and machine-readable data.'
accounts = self._calc_accounts(id_)
if lines:
return {'raw_gap_lines': [dl.raw for dl in block.gap_lines],
- 'account_balancers': {k: v.balancer
+ 'account_balancers': {k: v.balancers
for k, v in accounts.items()},
'booking_lines': ([line.as_dict
for line in block.booking.booking_lines]
],
"max-lines": [
"error",
- {"max": 394, "skipBlankLines": true, "skipComments": true}
+ {"max": 427, "skipBlankLines": true, "skipComments": true}
],
"max-lines-per-function": [
"error",
const
BALANCERS_DEFAULT = {"": "?"},
+ FACTOR_CENTS = 100,
IDX_LAST = -1,
IDX_PAST_INTRO_LINE = 1,
IDX_START = 0,
LEN_INDENT_INPUT_DEFAULT = 2,
LEN_INDENT_INPUT_MINIMUM = 1,
LEN_INTRO_LINE = 4,
+ LEN_INVERSION_DECIMAL = 2,
LEN_LEN_INDENT = 1,
LEN_LINE_STEP = 1,
LEN_STEP_INPUT_LEN_INDENT = 1,
};
const balance = (bookingLine) => {
- let invertedAmount = TOK_NONE;
- if (bookingLine.amount !== TOK_NONE) {
- invertedAmount = `-${bookingLine.amount}`;
- const doubleMinus = "--";
- if (invertedAmount.startsWith(doubleMinus)) {
- invertedAmount = invertedAmount.slice(doubleMinus.length);
- }
+ const
+ nameSteps = bookingLine.account.split(SEP_ACCNAME_STEPS),
+ toReturn = [];
+ let
+ balancers = [],
+ leftToInvert = null,
+ partInverted = TOK_NONE;
+ while (balancers.length === LEN_EMPTY) {
+ balancers = accountBalancers[nameSteps.join(SEP_ACCNAME_STEPS)] || [];
+ nameSteps.pop();
}
- let balancer = "";
- const accNameSteps = bookingLine.account.split(SEP_ACCNAME_STEPS);
- while (balancer === "") {
- balancer = accountBalancers[accNameSteps.join(SEP_ACCNAME_STEPS)];
- accNameSteps.pop();
+ if (bookingLine.amount && bookingLine.amount !== TOK_NONE) {
+ leftToInvert = Math.abs(bookingLine.amount);
+ partInverted = leftToInvert / balancers.length;
}
- return newBookingLine(
- balancer,
- invertedAmount,
- bookingLine.currency,
- LEN_INDENT_INPUT_BALANCED
+ const centsRound = (amt) => Math.round(FACTOR_CENTS * amt) / FACTOR_CENTS;
+ const signFixed = (amt
+ ) => (-Math.sign(bookingLine.amount) * amt).toFixed(LEN_INVERSION_DECIMAL);
+ Array.from(balancers).forEach(
+ (balancer, idx) => {
+ let amountInput = TOK_NONE;
+ if (partInverted !== TOK_NONE) {
+ if (idx === balancers.length + IDX_LAST) {
+ amountInput = signFixed(centsRound(leftToInvert));
+ } else {
+ const rounded = centsRound(partInverted);
+ leftToInvert -= rounded;
+ amountInput = signFixed(rounded);
+ }
+ }
+ toReturn.push(
+ newBookingLine(
+ balancer,
+ amountInput,
+ bookingLine.currency,
+ LEN_INDENT_INPUT_BALANCED
+ )
+ );
+ }
);
+ return toReturn;
};
const updateForm = () => {
() => bookingLines.splice(
idx + LEN_LINE_STEP,
LEN_EMPTY,
- balance(bookingLine)
+ ...balance(bookingLine)
)
);
addButton(
bookingLines.splice(
idx,
LEN_EMPTY,
- balance(bookingLines[idx - LEN_LINE_STEP])
+ ...balance(bookingLines[idx - LEN_LINE_STEP])
);
}
taintAndUpdateForm();