from __future__ import annotations from pathlib import Path import re from datetime import date DEFAULT_PATTERN = r"(?P\d{4})-(?P\d{4})" def suggest_next_invoice_number(invoices_root: Path, year: int | None = None, pattern: str = DEFAULT_PATTERN) -> str: invoices_root = invoices_root.expanduser().resolve() y = year or date.today().year rx = re.compile(pattern) max_seq = 0 if invoices_root.exists(): for d in invoices_root.iterdir(): if not d.is_dir(): continue m = rx.search(d.name) if not m: continue try: yr = int(m.group("year")) seq = int(m.group("seq")) except Exception: continue if yr == y and seq > max_seq: max_seq = seq return f"{y}-{max_seq+1:04d}" from contextlib import contextmanager @contextmanager def invoice_root_lock(root: Path): """Serialize local number allocation and folder creation on Linux.""" import fcntl root = root.expanduser().resolve() root.mkdir(parents=True, exist_ok=True) with (root / '.jr-einvoice.lock').open('a') as lock: fcntl.flock(lock, fcntl.LOCK_EX) try: yield finally: fcntl.flock(lock, fcntl.LOCK_UN)