class DatLine(_Dictable):
"""Line of .dat file parsed into comments and machine-readable data."""
- dictables = {'booking_line', 'code', 'comment', 'error', 'is_intro'}
+ dictables = {'booked', 'code', 'comment', 'error', 'is_intro'}
prev_line_empty: bool
def __init__(self, line: str) -> None:
halves = [t.rstrip() for t in line.split(';', maxsplit=1)]
self.comment = halves[1] if len(halves) > 1 else ''
self.code = halves[0]
- self.booking_line: Optional[_BookingLine] = None
+ self.booking: Optional['_Booking'] = None
+ self.booked: Optional[_BookingLine] = None
@property
def comment_instructions(self) -> dict[str, str]:
@property
def is_intro(self) -> bool:
"""Return if intro line of a _Booking."""
- return isinstance(self.booking_line, _IntroLine)
+ return isinstance(self.booked, _IntroLine)
@property
def booking_id(self) -> int:
- """If .booking_line, its .booking_id, else -1."""
+ """If .booking, its .booking_id, else -1."""
return self.booking.id_ if self.booking else -1
- @property
- def booking(self) -> Optional['_Booking']:
- """If .booking_line, matching _Booking, else None."""
- return self.booking_line.booking if self.booking_line else None
-
@property
def error(self) -> str:
"""Return error if registered on attempt to parse into _BookingLine."""
- return '; '.join(self.booking_line.errors) if self.booking_line else ''
+ return '; '.join(self.booked.errors) if self.booked else ''
@property
def is_questionable(self) -> bool:
"""Return whether line be questionable per associated _Booking."""
- return (self.booking_line.booking.is_questionable if self.booking_line
- else False)
+ return self.booking.is_questionable if self.booking else False
@property
def raw_nbsp(self) -> str:
class _BookingLine(_Dictable):
"""Parsed code part of a DatLine belonging to a _Booking."""
- def __init__(self, booking: '_Booking') -> None:
+ def __init__(self) -> None:
self.errors: list[str] = []
- self.booking = booking
self.idx = 0
"""First line of a _Booking, expected to carry date etc."""
dictables = {'date', 'target'}
- def __init__(self, booking: '_Booking', code: str) -> None:
- super().__init__(booking)
+ def __init__(self, code: str) -> None:
+ super().__init__()
if code[0].isspace():
self.errors += ['intro line indented']
toks = code.lstrip().split(maxsplit=1)
"""Non-first _Booking line, expected to carry value movement."""
dictables = {'amount', 'account', 'currency'}
- def __init__(self, booking: '_Booking', code: str, idx: int) -> None:
- super().__init__(booking)
+ def __init__(self, code: str, idx: int) -> None:
+ super().__init__()
self.idx = idx
self.currency = ''
self.amount: Optional[Decimal] = None
self.id_, self.booked_lines = id_, booked_lines[:]
self._gap_lines = gap_lines[:] if gap_lines else []
# parse booked_lines into Intro- and _TransferLines
- self.intro_line = _IntroLine(self, self.booked_lines[0].code)
- self._transfer_lines = [
- _TransferLine(self, b_line.code, i+1) for i, b_line
- in enumerate(self.booked_lines[1:])]
- self.booked_lines[0].booking_line = self.intro_line
+ for line in booked_lines:
+ line.booking = self
+ self.intro_line = _IntroLine(self.booked_lines[0].code)
+ self._transfer_lines = [_TransferLine(b_line.code, i+1) for i, b_line
+ in enumerate(self.booked_lines[1:])]
+ self.booked_lines[0].booked = self.intro_line
for i, b_line in enumerate(self._transfer_lines):
- self.booked_lines[i + 1].booking_line = b_line
+ self.booked_lines[i + 1].booked = b_line
# calculate .account_changes
changes = _Wealth()
sink_account = None
{% if dat_line.is_intro %}
<a href="#{{dat_line.booking_id}}">[#]</a>
{{ table_dat_lines_action_button(dat_line, "moveup", "^", dat_line.booking.can_move(1)) }}
- {% elif dat_line.booking_line.idx == 1 %}
+ {% elif dat_line.booked.idx == 1 %}
<a href="/balance?up_incl={{dat_line.booking_id}}">[b]</a>
{{ table_dat_lines_action_button(dat_line, "movedown", "v", dat_line.booking.can_move(0)) }}
- {% elif dat_line.booking_line.idx == 2 %}
+ {% elif dat_line.booked.idx == 2 %}
{{ table_dat_lines_action_button(dat_line, "copy", "C") }}
{% endif %}
</td>
{% elif dat_line.error %}
<td class="invalid" colspan=3>{{dat_line.code}}</td>
<td>{{dat_line.comment_in_ledger}}</td>
- {% elif dat_line.booking_line %}
- <td class="amt">{{dat_line.booking_line.amount_short}}</td>
- <td class="curr">{{dat_line.booking_line.currency|truncate(4,true,"…")}}</td>
- <td>{{dat_line.booking_line.account}}</td>
+ {% elif dat_line.booked %}
+ <td class="amt">{{dat_line.booked.amount_short}}</td>
+ <td class="curr">{{dat_line.booked.currency|truncate(4,true,"…")}}</td>
+ <td>{{dat_line.booked.account}}</td>
<td>{{dat_line.comment_in_ledger}}</td>
{% else %}
<td colspan=2></td>
} else if (input.name.endsWith('error')) {
line_to_update.code = input.value;
} else if (input.name.endsWith('date')) {
- line_to_update.booking_line.date = input.value;
+ line_to_update.booked.date = input.value;
} else if (input.name.endsWith('target')) {
- line_to_update.booking_line.target = input.value;
+ line_to_update.booked.target = input.value;
} else if (input.name.endsWith('account')) {
- line_to_update.booking_line.account = input.value;
+ line_to_update.booked.account = input.value;
} else if (input.name.endsWith('amount')) {
- line_to_update.booking_line.amount = input.value;
+ line_to_update.booked.amount = input.value;
} else if (input.name.endsWith('currency')) {
- line_to_update.booking_line.currency = input.value;
+ line_to_update.booked.currency = input.value;
}
}
}
// actual input lines
if (dat_line.is_intro) {
const td = setup_input_td(tr, 3);
- const date_input = add_input(td, 'date', dat_line.booking_line.date, 10)
+ const date_input = add_input(td, 'date', dat_line.booked.date, 10)
date_input.classList.add('date_input');
- add_input(td, 'target', dat_line.booking_line.target, 37)
+ add_input(td, 'target', dat_line.booked.target, 37)
} else if (!dat_line.error) { // i.e. valid TransferLine
- const acc_input = add_td_input('account', dat_line.booking_line.account, 30);
+ const acc_input = add_td_input('account', dat_line.booked.account, 30);
acc_input.setAttribute ('list', 'all_accounts');
acc_input.autocomplete = 'off';
// not using input[type=number] cuz no minimal step size, therefore regex test instead
- const amt_input = add_td_input('amount', dat_line.booking_line.amount == 'None' ? '' : dat_line.booking_line.amount, 12);
+ const amt_input = add_td_input('amount', dat_line.booked.amount == 'None' ? '' : dat_line.booked.amount, 12);
amt_input.pattern = '^-?[0-9]+(\.[0-9]+)?$';
amt_input.classList.add("number_input");
// ensure integer amounts at least line up with double-digit decimals
if (amt_input.value.match(/^-?[0-9]+$/)) { amt_input.value += '.00'; }
// imply that POST handler will set '€' currency if unset, but amount set
- const curr_input = add_td_input('currency', dat_line.booking_line.currency, 3);
+ const curr_input = add_td_input('currency', dat_line.booked.currency, 3);
curr_input.placeholder = '€';
} else {
add_td_input('error', dat_line.code, 20, 3)
// line deletion and addition buttons
td_add_del = add_td(tr);
add_button(td_add_del, 'add new', false, function() {
- new_line = {error: '', comment: '', booking_line: {account: '', amount: '', currency: ''}};
+ new_line = {error: '', comment: '', booked: {account: '', amount: '', currency: ''}};
dat_lines.splice(i + 1, 0, new_line);
});
if (i > 0) {
dat_line.code = dat_line.code.replaceAll(from, to);
}
['date', 'target', 'account', 'amount', 'currency'].forEach((key) => {
- if (key in dat_line.booking_line) {
- dat_line.booking_line[key] = dat_line.booking_line[key].replaceAll(from, to);
+ if (key in dat_line.booked) {
+ dat_line.booked[key] = dat_line.booked[key].replaceAll(from, to);
}
});
});
function mirror() {
dat_lines.slice(1).forEach((dat_line) => {
- let inverted_amount = `-${dat_line.booking_line.amount}`;
+ let inverted_amount = `-${dat_line.booked.amount}`;
if (inverted_amount.startsWith('--')) {
inverted_amount = inverted_amount.slice(2);
}
dat_lines.push({
error: '',
comment: '',
- booking_line: {
+ booked: {
account: '?',
amount: inverted_amount,
- currency: dat_line.booking_line.currency
+ currency: dat_line.booked.currency
}
});
})