home · contact · privacy
Fix error calculation for lines without amount and currency, simplify code. master
authorChristian Heller <c.heller@plomlompom.de>
Thu, 29 Jan 2026 01:34:57 +0000 (02:34 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Thu, 29 Jan 2026 01:34:57 +0000 (02:34 +0100)
src/ledgplom/ledger.py

index ac6acffe43f2943844940383c37d6207339d4209..7d26eae5c8904f587a3b6a39916b7fc2a3da2415 100644 (file)
@@ -1,13 +1,10 @@
 'Actual ledger classes.'
 # standard libs
-from abc import ABC
+from abc import ABC, abstractmethod
 from datetime import date as dt_date
 from decimal import Decimal, InvalidOperation as DecimalInvalidOperation
 from pathlib import Path
-from typing import Any, Generic, Iterator, Optional, Self, TypeVar
-
-
-_TypeDatLine = TypeVar('_TypeDatLine', bound='_DatLine')
+from typing import Any, Iterator, Optional, Self
 
 
 _INDENT_CHARS = {' ', '\t'}
@@ -204,10 +201,9 @@ class _BookingLine(_DatLine, ABC):
                      for idx in range(maxsplit))
 
     @property
+    @abstractmethod
     def errors(self) -> tuple[str, ...]:
         'Whatever is wrong with the respective line.'
-        return tuple(f'{name} empty' for name in self._field_names
-                     if not getattr(self, name))
 
 
 class _IntroLine(_BookingLine):
@@ -226,7 +222,8 @@ class _IntroLine(_BookingLine):
 
     @property
     def errors(self) -> tuple[str, ...]:
-        errors = list(super().errors)
+        errors = [f'{name} empty' for name in self._field_names
+                  if not getattr(self, name)]
         if self.indent:
             errors += ['intro line indented']
         try:
@@ -263,9 +260,11 @@ class _TransferLine(_BookingLine):
 
     @property
     def errors(self) -> tuple[str, ...]:
-        errors = list(super().errors)
+        errors = []
         # if not self.indent:
         #     errors += ['transfer line not indented']
+        if not self.account:
+            errors += ['account missing']
         if isinstance(self.amount, str):
             errors += [f'improper amount value: {self.amount}']
         if len(self.currency.split()) > 1:
@@ -284,18 +283,18 @@ class _TransferLine(_BookingLine):
         return ''
 
 
-class _LinesBlock(Generic[_TypeDatLine]):
-    _lines: list[_TypeDatLine]
+class _LinesBlock:
+    _lines: list[_DatLine]
 
     def __init__(self) -> None:
         self._lines = []
 
     @property
-    def lines(self) -> tuple[_TypeDatLine, ...]:
+    def lines(self) -> tuple[_DatLine, ...]:
         'Return collected lines.'
         return tuple(self._lines)
 
-    def add(self, lines: tuple[_TypeDatLine, ...], at_end=True) -> None:
+    def add(self, lines: tuple[_DatLine, ...], at_end=True) -> None:
         'Grow block downwards by this one DatLine.'
         if at_end:
             self._lines += lines
@@ -303,7 +302,7 @@ class _LinesBlock(Generic[_TypeDatLine]):
             self._lines[0:0] = list(lines)
 
 
-class _Booking(_LinesBlock[_BookingLine]):
+class _Booking(_LinesBlock):
 
     @property
     def _sink_account(self) -> str:
@@ -382,7 +381,7 @@ class _Booking(_LinesBlock[_BookingLine]):
         return self.intro_line.target
 
 
-class _DatBlock(_LinesBlock[_DatLine]):
+class _DatBlock(_LinesBlock):
     'Unit of lines with optional .booking, and (possibly zero) .gap_lines.'
     _prev: Optional[Self] = None
     _next: Optional[Self] = None
@@ -433,7 +432,7 @@ class _DatBlock(_LinesBlock[_DatLine]):
     @property
     def booking(self) -> Optional[_Booking]:
         'Booking made from lines indented or with code.'
-        booking_lines = tuple(_BookingLine(line.raw) for line in self.lines
+        booking_lines = tuple(_DatLine(line.raw) for line in self.lines
                               if line.indent or line.code)
         if not booking_lines:
             return None