SPACE = ' '
_INDENT_CHARS = {SPACE, '\t'}
SEP_COMMENTS = ';'
-_PREFIX_DEF = '#def '
+_PREFIX_DEF = '#desc '
+_PREFIX_BAL = '#balancer '
class _Wealth():
class _Account:
'Combine name, position in tree of owner, and wealth of self + children.'
+ desc = ''
+ balancer = ''
def __init__(self, parent: Optional[Self], basename: str) -> None:
self._wealth_diffs: dict[int, _Wealth] = {}
self.basename = basename
- self.desc = ''
self.children: list[Self] = []
self.parent = parent
if self.parent:
return self._raw[:]
@property
- def comment_instructions(self) -> dict[str, str]:
+ def comment_instructions(self) -> dict[str, dict[str, str]]:
'Parse .comment into _Account modification instructions.'
- instructions = {}
- if self.comment.startswith(_PREFIX_DEF):
- parts = [part.strip() for part
- in self.comment[len(_PREFIX_DEF):].split(SEP_COMMENTS)]
- first_part_parts = parts[0].split(maxsplit=1)
- account_name = first_part_parts[0]
- desc = first_part_parts[1] if len(first_part_parts) > 1 else ''
- instructions[account_name] = desc
+ instructions: dict[str, dict[str, str]] = {}
+ for prefix in (_PREFIX_DEF, _PREFIX_BAL):
+ attr_name = prefix[1:].rstrip()
+ instructions[attr_name] = {}
+ if self.comment.startswith(prefix):
+ parts = tuple(part.strip() for part
+ in self.comment[len(prefix):].split(maxsplit=1))
+ if not parts:
+ continue
+ instructions[attr_name][parts[0]] = ''.join(parts[1:])
return instructions
for block in self.blocks:
collect_more_than_names = id_ < 0 or block.id_ <= id_
for line in block.lines:
- for acc_name, desc in line.comment_instructions.items():
- ensure_accounts(acc_name)
- if collect_more_than_names:
- accounts[acc_name].desc = desc
+ for attr_name, defs in line.comment_instructions.items():
+ for acc_name, definition in defs.items():
+ ensure_accounts(acc_name)
+ if collect_more_than_names and definition:
+ setattr(accounts[acc_name], attr_name, definition)
if block.booking:
for acc_name, wealth in block.booking.diffs_targeted.items():
ensure_accounts(acc_name)
def view_ctx_edit(self, id_: int, raw=True, lines=False) -> dict[str, Any]:
'All context data relevant for rendering an edit view.'
block = self.blocks[id_]
+ accounts = self._calc_accounts(id_)
if lines:
return {'raw_gap_lines': [dl.raw for dl in block.gap_lines],
+ 'account_balancers': {k: v.balancer
+ for k, v in accounts.items()},
'booking_lines': ([line.as_dict
for line in block.booking.booking_lines]
if block.booking else tuple())}
- accounts = self._calc_accounts(id_)
roots: list[dict[str, Any]] = []
for full_path in sorted(block.booking.diffs_targeted.keys()
if block.booking else []):
],
"max-lines": [
"error",
- {"max": 376, "skipBlankLines": true, "skipComments": true}
+ {"max": 394, "skipBlankLines": true, "skipComments": true}
],
"max-lines-per-function": [
"error",
- 250
+ 259
],
"max-params": [
"error",
],
"max-statements": [
"error",
- 42
+ 43
],
"multiline-comment-style": [
"error",
} from "/taint.js";
const
- DEFAULT_INPUT_LEN_INDENT = 2,
+ BALANCERS_DEFAULT = {"": "?"},
IDX_LAST = -1,
IDX_PAST_INTRO_LINE = 1,
IDX_START = 0,
LEN_CURRENCY = 3,
LEN_DATE = 10,
LEN_EMPTY = 0,
+ LEN_INDENT_INPUT_BALANCED = 4,
+ LEN_INDENT_INPUT_DEFAULT = 2,
LEN_INTRO_LINE = 4,
LEN_LEN_INDENT = 1,
LEN_LINE_STEP = 1,
LEN_STEP_INPUT_LEN_INDENT = 1,
LEN_TARGET = 37,
MINIMUM_INPUT_LEN_INDENT = 1,
+ accountBalancers = Object.assign(
+ BALANCERS_DEFAULT,
+ {{ account_balancers|tojson|safe }}
+ ),
bookingLines = {{ booking_lines|tojson|safe }},
rawGapLines = {{ raw_gap_lines|tojson|safe }};
const newBookingLine = (
account = "",
amount = "None",
- currency = ""
+ currency = "",
+ lenIndent = LEN_INDENT_INPUT_DEFAULT
) => ({
account,
amount,
"comment": "",
currency,
"errors": [],
- "len_indent": DEFAULT_INPUT_LEN_INDENT
+ "len_indent": lenIndent
});
const taintAndUpdateForm = () => {
updateForm(); // eslint-disable-line no-use-before-define
};
+const balance = (bookingLine) => {
+ let invertedAmount = "None";
+ if (bookingLine.amount !== "None") {
+ invertedAmount = `-${bookingLine.amount}`;
+ const doubleMinus = "--";
+ if (invertedAmount.startsWith(doubleMinus)) {
+ invertedAmount = invertedAmount.slice(doubleMinus.length);
+ }
+ }
+ let balancer = "";
+ const accNameSteps = bookingLine.account.split(":");
+ while (balancer === "") {
+ balancer = accountBalancers[accNameSteps.join(":")];
+ accNameSteps.pop();
+ }
+ return newBookingLine(
+ balancer,
+ invertedAmount,
+ bookingLine.currency,
+ LEN_INDENT_INPUT_BALANCED
+ );
+};
+
const updateForm = () => {
const
table = document.getElementById("booking_lines"),
const addButton = (
parentTd,
label,
- disabled,
- onclick
+ onclick,
+ disabled = false
) => {
/* add button to td to run onclick (after updating bookingLines from
from inputs, and followed by calling taint and updateForm) */
].forEach((kwargs) => addButton(
tdBtnsUpdown,
kwargs.label,
- !kwargs.enabled,
() => {
const otherLine = bookingLines[kwargs.earlierIdx];
bookingLines.splice(
LEN_EMPTY,
otherLine
);
- }
+ },
+ !kwargs.enabled
));
}
addButton(
tdAddDel,
"add new",
- false,
() => bookingLines.splice(
idx + LEN_LINE_STEP,
LEN_EMPTY,
)
);
if (idx > IDX_START) {
+ addButton(
+ tdAddDel,
+ "balance",
+ () => bookingLines.splice(
+ idx + LEN_LINE_STEP,
+ LEN_EMPTY,
+ balance(bookingLine)
+ );
+ );
addButton(
tdAddDel,
"delete",
- idx <= IDX_START,
() => bookingLines.splice(
idx,
LEN_LINE_STEP
};
const mirror = () => {
- bookingLines.slice(IDX_PAST_INTRO_LINE).forEach((bookingLine) => {
- let invertedAmount = "None";
- if (bookingLine.amount !== "None") {
- invertedAmount = `-${bookingLine.amount}`;
- const doubleMinus = "--";
- if (invertedAmount.startsWith(doubleMinus)) {
- invertedAmount = invertedAmount.slice(doubleMinus.length);
- }
- }
- bookingLines.push(
- newBookingLine(
- "?",
- invertedAmount,
- bookingLine.currency
- )
- );
- });
+ bookingLines.slice(IDX_PAST_INTRO_LINE).forEach(
+ (line) => bookingLines.push(balance(line))
+ );
taintAndUpdateForm();
};
-; #def bar:x bla bla bla
+; #desc bar:x bla bla bla
2001-01-01 test ; foo
; in-body comment 1
bar:x:y -10 €
bar:z -1 USD
-; #def bar:x bla foo bla
+; #desc bar:x bla foo bla
2001-01-03 test
foo:x 10 €
</span>
<hr>
-<textarea name="raw_lines" class="tainter" cols="100" rows="9">; #def bar:x bla bla bla
+<textarea name="raw_lines" class="tainter" cols="100" rows="9">; #desc bar:x bla bla bla
2001-01-01 test ; foo
; in-body comment 1
</span>
<hr>
<div>Gap:</div>
-<textarea id="gap_lines" name="raw_lines" cols="100" rows="3" class="tainter">; #def bar:x bla bla bla
+<textarea id="gap_lines" name="raw_lines" cols="100" rows="3" class="tainter">; #desc bar:x bla bla bla
</textarea>
<EXPANDED>
<hr>
<div>Gap:</div>
<textarea id="gap_lines" name="raw_lines" cols="100" rows="4" class="tainter">
-; #def bar:x bla foo bla
+; #desc bar:x bla foo bla
</textarea>
<EXPANDED>
[<a href="/edit_raw/0">e</a>]
</td>
</tr>
- <tr><td>; #def bar:x bla bla bla </td></tr>
+ <tr><td>; #desc bar:x bla bla bla </td></tr>
<tr><td> </td></tr>
<tr><td>2001-01-01 test ; foo </td></tr>
<tr><td> ; in-body comment 1 </td></tr>
</td>
</tr>
<tr><td> </td></tr>
- <tr><td>; #def bar:x bla foo bla </td></tr>
+ <tr><td>; #desc bar:x bla foo bla </td></tr>
<tr><td> </td></tr>
<tr><td>2001-01-03 test </td></tr>
<tr><td> foo:x 10 € </td></tr>
</td>
</tr>
<tr>
- <td colspan="4">; #def bar:x bla bla bla </td>
+ <td colspan="4">; #desc bar:x bla bla bla </td>
</tr>
<tr>
<td colspan="4"> </td>
<td colspan="4"> </td>
</tr>
<tr>
- <td colspan="4">; #def bar:x bla foo bla </td>
+ <td colspan="4">; #desc bar:x bla foo bla </td>
</tr>
<tr>
<td colspan="4"> </td>