#!/usr/bin/env python3 """Append a new freight record and regenerate CSV/summary outputs. Usage: python3 append_load.py new_record.json The input JSON should be a single record object with fields matching the ledger schema. """ import csv import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parent LEDGER_PATH = ROOT / 'truck_freight_ledger.json' CSV_PATH = ROOT / 'truck_freight_ledger.csv' SUMMARY_PATH = ROOT / 'truck_freight_summary.md' FIELDNAMES = [ 'id','date','status','vehicle','from','via','to','shipper','load_type','item','deliveries', 'freight_charged','advance_received','paid_to_driver','commission','driver_freight', 'pending_from_shipper','pending_to_driver','notes' ] def join_list(v): if v is None: return '' if isinstance(v, list): return ' | '.join(str(x) for x in v) return str(v) def load_ledger(): return json.loads(LEDGER_PATH.read_text()) def write_csv(records): with CSV_PATH.open('w', newline='') as f: writer = csv.DictWriter(f, fieldnames=FIELDNAMES) writer.writeheader() for r in records: row = {k: '' for k in FIELDNAMES} for k in ['id','date','status','vehicle','from','to','shipper','load_type','item','freight_charged','advance_received','paid_to_driver','commission','driver_freight','pending_from_shipper','pending_to_driver','notes']: row[k] = r.get(k, '') if r.get(k, '') is not None else '' row['via'] = join_list(r.get('via')) row['deliveries'] = join_list(r.get('deliveries')) writer.writerow(row) def write_summary(records): def total(key): s = 0 for r in records: v = r.get(key) if isinstance(v, (int, float)): s += v return s num_records = len(records) settled = sum(1 for r in records if r.get('status') == 'settled') pending_or_partial = sum(1 for r in records if 'pending' in (r.get('status') or '') or r.get('status') == 'partial') lines = [] lines.append('# Truck Freight Ledger Summary') lines.append('') lines.append('Overview') lines.append(f'- Records: {num_records}') lines.append(f'- Settled loads: {settled}') lines.append(f'- Pending/partial loads: {pending_or_partial}') lines.append('') lines.append('Totals') lines.append(f'- Freight charged: {total("freight_charged")}') lines.append(f'- Advance received: {total("advance_received")}') lines.append(f'- Paid to driver: {total("paid_to_driver")}') lines.append(f'- Commission: {total("commission")}') lines.append(f'- Pending from shipper: {total("pending_from_shipper")}') lines.append(f'- Pending to driver: {total("pending_to_driver")}') lines.append('') lines.append('Record snapshot') lines.append('| Date | Vehicle | Shipper | Route | Freight | Advance | Paid driver | Commission | Pending shipper | Status |') lines.append('|---|---|---|---|---:|---:|---:|---:|---:|---|') for r in sorted(records, key=lambda x: (x.get('date') or '', x.get('vehicle') or '', x.get('id') or '')): route = ' → '.join([p for p in [r.get('from'), r.get('to')] if p]) if r.get('via'): route = f"{r.get('from')} via {join_list(r.get('via'))} → {r.get('to')}" lines.append( f"| {r.get('date','')} | {r.get('vehicle','') or '-'} | {r.get('shipper','') or '-'} | {route or '-'} | {r.get('freight_charged','') if r.get('freight_charged') is not None else '-'} | {r.get('advance_received','') if r.get('advance_received') is not None else '-'} | {r.get('paid_to_driver','') if r.get('paid_to_driver') is not None else '-'} | {r.get('commission','') if r.get('commission') is not None else '-'} | {r.get('pending_from_shipper','') if r.get('pending_from_shipper') is not None else '-'} | {r.get('status','') or '-'} |" ) SUMMARY_PATH.write_text('\n'.join(lines) + '\n') def main(): if len(sys.argv) != 2: print('Usage: python3 append_load.py new_record.json', file=sys.stderr) raise SystemExit(2) new_record = json.loads(Path(sys.argv[1]).read_text()) ledger = load_ledger() ledger.setdefault('records', []).append(new_record) ledger['records'] = sorted(ledger['records'], key=lambda x: (x.get('date') or '', x.get('vehicle') or '', x.get('id') or '')) LEDGER_PATH.write_text(json.dumps(ledger, indent=2, ensure_ascii=False) + '\n') write_csv(ledger['records']) write_summary(ledger['records']) print(f"Appended record {new_record.get('id', '(no id)')} and regenerated CSV + summary.") if __name__ == '__main__': main()