home · contact · privacy
Record/preserve BookingLine indentations via edit form.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 2 Feb 2026 03:33:42 +0000 (04:33 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 2 Feb 2026 03:33:42 +0000 (04:33 +0100)
src/ledgplom/http.py
src/ledgplom/ledger.py
src/templates/edit_structured.js
src/tests/full.edit_structured.0
src/tests/full.edit_structured.3
src/tests/full.edit_structured.4

index a5a62fea4285f7f805040c75ead2f8d2e5c32416..0c8d437f1a94844842d89f9f0395fd695d28cd03 100644 (file)
@@ -5,7 +5,7 @@ from typing import Any
 # plomlib
 from plomlib.web import PlomHttpHandler, PlomHttpServer, PlomQueryMap
 # ourselves
-from ledgplom.ledger import DEFAULT_INDENT, Ledger
+from ledgplom.ledger import Ledger
 
 
 _PATH_TEMPLATES = Path('templates')
@@ -72,10 +72,11 @@ class _Handler(PlomHttpHandler):
                 inputs = {key: self.postvars.first(f'line_{lineno}_{key}')
                           for key in input_names}
                 if 0 == lineno:
-                    code = f'{inputs["date"]} {inputs["target"]}'
+                    code = inputs["date"] + ' ' + inputs["target"]
                 else:
-                    code = f'{DEFAULT_INDENT}{inputs["account"]} ' +\
-                            f'{inputs["amount"]} {inputs["currency"]}'
+                    code = (' ' * int(inputs["len_indent"])
+                            + inputs["account"] + ' ' * 2
+                            + inputs["amount"] + ' ' + inputs["currency"])
                 new_lines += [f'{code} ; {inputs["comment"]}']
         new_lines += self.postvars.first('raw_lines').splitlines()
         new_id = self.server.ledger.rewrite_block(old_id, new_lines)
index 7d26eae5c8904f587a3b6a39916b7fc2a3da2415..72b5fe886a0de23ccdde8db5b97d5d4eab794af0 100644 (file)
@@ -10,7 +10,6 @@ from typing import Any, Iterator, Optional, Self
 _INDENT_CHARS = {' ', '\t'}
 _SEP_COMMENTS = ';'
 _PREFIX_DEF = '#def '
-DEFAULT_INDENT = 2 * ' '
 
 
 class _Wealth():
@@ -120,15 +119,15 @@ class _DatLine:
     def __init__(self, text: str) -> None:
         self._raw = text
 
-    def _into_parts(self) -> tuple[str, str, str]:
+    def _into_parts(self) -> tuple[int, str, str]:
         stage_count = 0
-        indent = ''
+        len_indent = 0
         code = ''
         comment = ''
         for c in self._raw:
             if stage_count == 0:
                 if c in _INDENT_CHARS:
-                    indent += c
+                    len_indent += 1
                 else:
                     stage_count += 1
             if stage_count == 1:
@@ -138,11 +137,11 @@ class _DatLine:
                     stage_count += 1
             if stage_count == 2:
                 comment += c
-        return indent, code, comment
+        return len_indent, code, comment
 
     @property
-    def indent(self) -> str:
-        'All (maybe zero) the chars of whitespace line starts with.'
+    def len_indent(self) -> int:
+        'Number (maybe zero) of chars of whitespace line starts with.'
         return self._into_parts()[0]
 
     @property
@@ -224,7 +223,7 @@ class _IntroLine(_BookingLine):
     def errors(self) -> tuple[str, ...]:
         errors = [f'{name} empty' for name in self._field_names
                   if not getattr(self, name)]
-        if self.indent:
+        if self.len_indent:
             errors += ['intro line indented']
         try:
             dt_date.fromisoformat(self.date)
@@ -235,7 +234,7 @@ class _IntroLine(_BookingLine):
 
 class _TransferLine(_BookingLine):
     'Non-first _Booking line, expected to carry value movement.'
-    _field_names = {'account', 'amount', 'currency'}
+    _field_names = {'account', 'amount', 'currency', 'len_indent'}
 
     @property
     def account(self) -> str:
@@ -261,7 +260,7 @@ class _TransferLine(_BookingLine):
     @property
     def errors(self) -> tuple[str, ...]:
         errors = []
-        # if not self.indent:
+        # if not self.len_indent:
         #     errors += ['transfer line not indented']
         if not self.account:
             errors += ['account missing']
@@ -392,7 +391,7 @@ class _DatBlock(_LinesBlock):
         i_block: _DatBlock = cls()
         blocks = []
         for dat_line in (_DatLine(line) for line in lines):
-            if (not dat_line.indent) and i_block.indented:
+            if (not dat_line.len_indent) and i_block.indented:
                 blocks += [i_block]
                 i_block.next = _DatBlock()
                 i_block = i_block.next
@@ -402,13 +401,13 @@ class _DatBlock(_LinesBlock):
     @property
     def indented(self) -> bool:
         'Does the block contain any indented lines?'
-        return bool([line for line in self.lines if line.indent])
+        return bool([line for line in self.lines if line.len_indent])
 
     @property
     def gap_lines(self) -> tuple[_DatLine, ...]:
         'Sequence of all included DatLines without code and indent.'
         return tuple(line for line in self.lines
-                     if not line.indent + line.code)
+                     if not (line.len_indent or line.code))
 
     @property
     def redundant_empty_lines(self) -> tuple[_DatLine, ...]:
@@ -433,7 +432,7 @@ class _DatBlock(_LinesBlock):
     def booking(self) -> Optional[_Booking]:
         'Booking made from lines indented or with code.'
         booking_lines = tuple(_DatLine(line.raw) for line in self.lines
-                              if line.indent or line.code)
+                              if line.len_indent or line.code)
         if not booking_lines:
             return None
         booking = _Booking()
index 00f0932ff42a9ece315c2e13cfec4009943bb096..4e2da3e3d1cecbb0a24d064a816ce3d460012f61 100644 (file)
@@ -21,11 +21,11 @@ eslint
 ],
 "max-lines": [
     "error",
-    {"max": 355, "skipBlankLines": true, "skipComments": true}
+    {"max": 373, "skipBlankLines": true, "skipComments": true}
 ],
 "max-lines-per-function": [
     "error",
-    238
+    249
 ],
 "max-params": [
     "error",
@@ -33,7 +33,7 @@ eslint
 ],
 "max-statements": [
     "error",
-    38
+    42
 ],
 "multiline-comment-style": [
     "error",
@@ -56,6 +56,7 @@ import {
 } from "/taint.js";
 
 const
+    DEFAULT_INPUT_LEN_INDENT = 2,
     IDX_LAST = -1,
     IDX_PAST_INTRO_LINE = 1,
     IDX_START = 0,
@@ -67,9 +68,12 @@ const
     LEN_CURRENCY = 3,
     LEN_DATE = 10,
     LEN_EMPTY = 0,
-    LEN_INTRO_LINE = 3,
+    LEN_INTRO_LINE = 4,
+    LEN_LEN_INDENT = 1,
     LEN_LINE_STEP = 0,
+    LEN_STEP_INPUT_LEN_INDENT = 1,
     LEN_TARGET = 37,
+    MINIMUM_INPUT_LEN_INDENT = 1,
     bookingLines = {{ booking_lines|tojson|safe }},
     rawGapLines = {{ raw_gap_lines|tojson|safe }};
 
@@ -82,7 +86,8 @@ const newBookingLine = (
     amount,
     "comment": "",
     currency,
-    "errors": []
+    "errors": [],
+    "len_indent": DEFAULT_INPUT_LEN_INDENT
 });
 
 const taintAndUpdateForm = () => {
@@ -96,7 +101,7 @@ const updateForm = () => {
         textarea = document.getElementById("gap_lines");
 
     // empty and redo gapLines textarea, empty bookingLines table
-    textarea.innerHTML = ""
+    textarea.innerHTML = "";
     rawGapLines.forEach((line) => {
         textarea.innerHTML += `${line}\n`;
     });
@@ -176,7 +181,10 @@ const updateForm = () => {
             td.appendChild(input);
             input.name = `line_${idx}_${name}`;
             input.size = size;
-            input.setAttribute("value", value.trim());
+            input.setAttribute(
+                "value",
+                value.trim()
+            );
             input.oninput = taint;
             return input;
         };
@@ -249,6 +257,14 @@ const updateForm = () => {
                 LEN_TARGET
             );
         } else {
+            const indentInput = addTdInput(
+                "len_indent",
+                `${bookingLine.len_indent}`,
+                LEN_LEN_INDENT
+            );
+            indentInput.type = "number";
+            indentInput.min = MINIMUM_INPUT_LEN_INDENT;
+            indentInput.step = LEN_STEP_INPUT_LEN_INDENT;
             const accInput = addTdInput(
                 "account",
                 bookingLine.account,
index ea6ffe1d77f3cb512d5444094d87d5ec3318771a..5519d394b9f812303052011258b99dfaabc2a250 100644 (file)
@@ -111,7 +111,7 @@ to
     <tr>
         <td colspan="1">
         </td>
-        <td colspan="3">
+        <td colspan="4">
             <input name="line_0_date" size="10" value="2001-01-01" id="date_input">
             <input name="line_0_target" size="37" value="test">
         </td>
@@ -127,6 +127,9 @@ to
             <button type="button" disabled="">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_1_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_1_account" size="30" value="foo" list="all_accounts" autocomplete="off">
         </td>
@@ -149,6 +152,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_2_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_2_account" size="30" value="bar" list="all_accounts" autocomplete="off">
         </td>
index 469bcbb7f8a1b6a7fbaafd4c9d0eca3778c5eefb..ba00d207f910967f053598512dc466f2a1c70c30 100644 (file)
@@ -110,7 +110,7 @@ to
     <tr>
         <td colspan="1">
         </td>
-        <td colspan="3">
+        <td colspan="4">
             <input name="line_0_date" size="10" value="2001-01-03" id="date_input">
             <input name="line_0_target" size="37" value="test">
         </td>
@@ -126,6 +126,9 @@ to
             <button type="button" disabled="">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_1_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_1_account" size="30" value="foo:x" list="all_accounts" autocomplete="off">
         </td>
@@ -148,6 +151,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_2_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_2_account" size="30" value="foo:x" list="all_accounts" autocomplete="off">
         </td>
@@ -170,6 +176,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_3_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_3_account" size="30" value="bar:x:y" list="all_accounts" autocomplete="off">
         </td>
@@ -192,6 +201,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_4_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_4_account" size="30" value="bar:z" list="all_accounts" autocomplete="off">
         </td>
index c7097a3d043d52f117ae25afb2e9b263bad2fe1f..6529f528426ea3a7b5c541507651844160be5521 100644 (file)
@@ -116,7 +116,7 @@ to
     <tr>
         <td colspan="1">
         </td>
-        <td colspan="3">
+        <td colspan="4">
             <input name="line_0_date" size="10" value="2001-01-03" id="date_input">
             <input name="line_0_target" size="37" value="test">
         </td>
@@ -132,6 +132,9 @@ to
             <button type="button" disabled="">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_1_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_1_account" size="30" value="foo:x" list="all_accounts" autocomplete="off">
         </td>
@@ -154,6 +157,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_2_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_2_account" size="30" value="foo:x" list="all_accounts" autocomplete="off">
         </td>
@@ -176,6 +182,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_3_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_3_account" size="30" value="bar:x:y" list="all_accounts" autocomplete="off">
         </td>
@@ -198,6 +207,9 @@ to
             <button type="button">^</button>
             <button type="button">v</button>
         </td>
+        <td colspan="1">
+            <input name="line_4_len_indent" size="1" value="4" type="number" min="1" step="1">
+        </td>
         <td colspan="1">
             <input name="line_4_account" size="30" value="bar:z" list="all_accounts" autocomplete="off">
         </td>