self.server.load()
elif 'save_file' in self.postvars.as_dict:
self.server.save()
+ elif self.pagename.startswith('ledger_'):
+ for key in self.postvars.keys_prefixed('move_'):
+ toks = key.split('_')
+ id_ = int(toks[1])
+ self.server.move_booking(id_, up=toks[2] == 'up')
+ self.redirect(Path(self.pagename).joinpath(f'#{id_}'))
+ return
elif self.pagename == 'edit_structured':
if self.postvars.first('apply'):
line_keys = self.postvars.keys_prefixed('line_')
id_ = int(self.path_toks[2])
if self.pagename.startswith('edit_'):
ctx['id'] = id_
+ elif self.pagename.startswith('ledger_'):
+ ctx['max_id'] = self.server.bookings[-1].id_
if self.pagename == 'balance':
id_ = int(self.params.first('up_incl') or '-1')
valid, balance_roots = self.server.balance_roots(id_)
'\n'.join([line.raw for line in self.dat_lines]), encoding='utf8')
self.load()
- def rewrite_booking(self, id_: int, new_dat_lines: list[DatLine]) -> None:
- """Rewrite .dat_lines for Booking of .id_ with new_dat_lines."""
- old_booking = self.bookings[id_]
- start_idx = self.dat_lines.index(old_booking.dat_lines[0])
- end_idx = self.dat_lines.index(old_booking.dat_lines[-1])
+ def _margin_indices(self, booking: Booking) -> tuple[int, int]:
+ start_idx = self.dat_lines.index(booking.dat_lines[0])
+ end_idx = self.dat_lines.index(booking.dat_lines[-1])
+ return start_idx, end_idx
+
+ def _replace_from_to(self,
+ start_idx: int,
+ end_idx: int,
+ new_lines: list[DatLine]
+ ) -> None:
self.dat_lines = (self.dat_lines[:start_idx]
- + new_dat_lines + self.dat_lines[end_idx+1:])
+ + new_lines
+ + self.dat_lines[end_idx+1:])
self.load_bookings()
+ def move_booking(self, id_: int, up: bool) -> None:
+ """Move Booking of id_ one step up or downwards"""
+ id_other = id_ + (-1 if up else 1)
+ agent = self.bookings[id_]
+ other = self.bookings[id_other]
+ start_agent, end_agent = self._margin_indices(agent)
+ start_other, end_other = self._margin_indices(other)
+ gap_lines = self.dat_lines[(end_other if up else end_agent) + 1:
+ start_agent if up else start_other]
+ self._replace_from_to(start_other if up else start_agent,
+ end_agent if up else end_other,
+ (agent.dat_lines if up else other.dat_lines)
+ + gap_lines
+ + (other.dat_lines if up else agent.dat_lines))
+
+ def rewrite_booking(self, id_: int, new_dat_lines: list[DatLine]) -> None:
+ """Rewrite .dat_lines for Booking of .id_ with new_dat_lines."""
+ self._replace_from_to(*self._margin_indices(self.bookings[id_]),
+ new_dat_lines)
+
@property
def dat_lines_sans_empty(self) -> list[DatLine]:
"""Return only those .data_lines with .code or .comment."""
table.ledger tr > td:first-child { background-color: white; }
{% endmacro %}
-{% macro table_dat_lines(dat_lines, raw) %}
+{% macro table_dat_lines(dat_lines, max_id, raw) %}
+<form action="/ledger_{% if raw %}raw{% else %}structured{% endif %}" method="POST">
<table class="ledger">
{% for dat_line in dat_lines %}
{% if (not raw) and dat_line.is_intro and loop.index > 1 %}
{% endif %}
<tr class="alternating{% if dat_line.is_questionable %} warning{% endif %}">
{% if dat_line.is_intro %}
- <td id="{{dat_line.booking_id}}"><a href="#{{dat_line.booking_id}}">[#]</a></td>
+ <td id="{{dat_line.booking_id}}"><a href="#{{dat_line.booking_id}}">[#]</a><input type="submit" name="move_{{dat_line.booking_id}}_up" value="^"{% if dat_line.booking_id == 0 %} disabled{% endif %}/></td>
{% elif dat_line.booking_line.idx == 1 %}
- <td><a href="/balance?up_incl={{dat_line.booking_id}}">[b]</a></td>
+ <td><a href="/balance?up_incl={{dat_line.booking_id}}">[b]</a><input type="submit" name="move_{{dat_line.booking_id}}_down" value="v"{% if dat_line.booking_id == max_id %} disabled{% endif %}/></td>
{% else %}
<td></td>
{% endif %}
{% endif %}
{% endfor %}
</table>
+</form>
{% endmacro %}
{% macro taint_js() %}
add_td_input('account', dat_line.booking_line.account, 30);
// 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);
- amt_input.pattern = '^[0-9]+(\.[0-9]+)?$';
+ 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'; }
+ 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);
curr_input.placeholder = '€';