home · contact · privacy
Do not treat sink warnings as line-specific.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 23 Mar 2025 12:38:53 +0000 (13:38 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 23 Mar 2025 12:38:53 +0000 (13:38 +0100)
src/ledgplom/http.py
src/ledgplom/ledger.py
src/templates/_macros.tmpl
src/templates/edit_raw.tmpl
src/templates/edit_structured.tmpl

index b1734f5903e0bff8fb5da51aba76e170428c90f7..232b427fac4b7359177576cf2ac86a2948785906 100644 (file)
@@ -182,6 +182,7 @@ class _Handler(PlomHttpHandler):
         ctx['id'] = id_
         ctx['dat_lines'] = [dl if raw else dl.as_dict
                             for dl in booking.booked_lines]
+        ctx['sink_error'] = booking.sink_error
         ctx['valid'] = self.server.ledger.bookings_valid_up_incl(id_)
         if not raw:
             ctx['all_accounts'] = sorted(self.server.ledger.accounts.keys())
index 798596af8aeb0fa7f142a804867805e2adaec081..450c6d8d777273cca93eec6b6f90ce55fe8ec022 100644 (file)
@@ -200,11 +200,6 @@ class DatLine(_Dictable):
         """Return error if registered on attempt to parse into BookingLine."""
         return '; '.join(self.booked.errors) if self.booked else ''
 
-    @property
-    def is_questionable(self) -> bool:
-        """Return whether line be questionable per associated _Booking."""
-        return bool(self.booking and self.booking.is_questionable)
-
     @property
     def raw_nbsp(self) -> str:
         """Return .raw but ensure whitespace as &nbsp;, and at least one."""
@@ -341,6 +336,7 @@ class _Booking:
         # calculate .account_changes
         changes = _Wealth()
         sink_account = None
+        self.sink_error = ''
         self.account_changes: dict[str, _Wealth] = {}
         for transfer_line in [tl for tl in self._transfer_lines
                               if not tl.errors]:
@@ -348,8 +344,9 @@ class _Booking:
                 self.account_changes[transfer_line.account] = _Wealth()
             if transfer_line.amount is None:
                 if sink_account:
-                    transfer_line.errors += ['too many sinks']
-                sink_account = transfer_line.account
+                    self.sink_error = 'too many sinks'
+                else:
+                    sink_account = transfer_line.account
                 continue
             change = _Wealth({transfer_line.currency: transfer_line.amount})
             self.account_changes[transfer_line.account] += change
@@ -357,7 +354,7 @@ class _Booking:
         if sink_account:
             self.account_changes[sink_account] += changes.as_sink
         elif not changes.sink_empty:
-            self._transfer_lines[-1].errors += ['needed sink missing']
+            self.sink_error = 'needed sink missing'
 
     def recalc_prev_next(self, bookings: list[Self]) -> None:
         """Assuming .id_ to be index in bookings, link prev + next bookings."""
@@ -408,7 +405,9 @@ class _Booking:
 
     @property
     def is_questionable(self) -> bool:
-        """Whether lines count any errors."""
+        """Whether lines count any errors, or add up to a .sink_error."""
+        if self.sink_error:
+            return True
         for _ in [bl for bl in [self.intro_line] + self._transfer_lines
                   if bl.errors]:
             return True
index 9135e51c743e6188b0386a12bbf2f21e476d376c..84be211e5d2d79c0590f398c2f36bec42752894a 100644 (file)
@@ -13,13 +13,13 @@ td.balance.curr { width: 3em; }
 
 
 {% macro css_errors() %}
-td.invalid, tr.warning td.invalid { background-color: #ff0000; }
+span.sink_error, td.invalid, tr.warning td.invalid { background-color: #ff0000; }
 {% endmacro %}
 
 
 
 {% macro css_ledger_index_col() %}
-table.ledger tr > td:first-child { background-color: white; text-align: right; }
+table.ledger tr > td:first-child { text-align: right; }
 {% endmacro %}
 
 
@@ -37,9 +37,9 @@ table.ledger tr > td:first-child { background-color: white; text-align: right; }
     {% if (not raw) and dat_line.prev_line_empty %}
       <tr ><td>&nbsp;</td></tr>
     {% endif %}
-    <tr class="alternating{% if dat_line.is_questionable %} warning{% endif %}">
+    <tr class="alternating">
 
-    <td{% if dat_line.is_intro %} id="{{dat_line.booking_id}}"{% endif %}>
+    <td{% if dat_line.is_intro %} id="{{dat_line.booking_id}}"{% endif %} {% if dat_line.booking.sink_error %}class="invalid"{% endif %}>
     {% 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)) }}
@@ -129,7 +129,7 @@ function taint() {
 
 
 
-{% macro edit_bar(target, id) %}
+{% macro edit_bar(target, id, sink_error) %}
 <span class="disable_on_change">
 <a href="/bookings/{{id-1}}">prev</a> · <a href="/bookings/{{id+1}}">next</a>
 </span>
@@ -139,6 +139,10 @@ function taint() {
 <a href="/edit_{{target}}/{{id}}">switch to {{target}}</a> · <a href="/balance?up_incl={{id}}">balance after</a> · <a href="/ledger_structured/#{{id}}">in ledger</a>
 </span>
 <hr />
+{% if sink_error %}
+<span class="sink_error">balancing error: {{ sink_error }}</span>
+<hr />
+{% endif %}
 {% endmacro %}
 
 
index adfccbca07980e5e19738e9d979658db08e2e391..fb7b8043f2445a53ac11126946a27290a2052c36 100644 (file)
@@ -15,7 +15,7 @@
 
 {% block content %}
 <form action="/edit_raw/{{id}}" method="POST">
-{{ macros.edit_bar("structured", id) }}
+{{ macros.edit_bar("structured", id, sink_error) }}
 <textarea name="booking" cols=100 rows=100 oninput="taint()">
 {% for dat_line in dat_lines %}{{ dat_line.raw }}
 {% endfor %}</textarea>
index 3250ae4b7418bec65c273ca7829dd086b192dd3a..dad2e0ad9429bb9c2775c6c8fc07e2839832e74d 100644 (file)
@@ -256,7 +256,7 @@ window.onload = update_form;
 
 {% block content %}
 <form action="/edit_structured/{{id}}" method="POST">
-{{ macros.edit_bar("raw", id) }}
+{{ macros.edit_bar("raw", id, sink_error) }}
 <input type="button" onclick="mirror()" value="mirror" class="disable_on_change">
 <input type="button" onclick="fill_sink()" value="fill sink" class="disable_on_change">
 |