78 lines
3.5 KiB
Python
Executable File
78 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Sync external systems into reconciliation candidates.
|
|
|
|
Examples:
|
|
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all
|
|
PYTHONPATH=. python scripts/sync_external_reconciliation.py --jasmin --limit 50 --days 3
|
|
PYTHONPATH=. python scripts/sync_external_reconciliation.py --odoo --days 3
|
|
PYTHONPATH=. python scripts/sync_external_reconciliation.py --customers --limit 200
|
|
|
|
The full --all pipeline first creates/updates fiscal customers from Jasmin/Odoo
|
|
and then stages reconciliation_items. It never creates opportunities or confirms
|
|
payments, so the operator can link/create/ignore in the UI.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
from typing import Any, Dict, List
|
|
|
|
from app.external_reconciliation_sync import (
|
|
sync_all_external_reconciliation_candidates,
|
|
sync_external_fiscal_customers_for_reconciliation,
|
|
sync_jasmin_reconciliation_candidates,
|
|
sync_odoo_reconciliation_candidates,
|
|
sync_packlink_reconciliation_candidates,
|
|
)
|
|
|
|
|
|
def _print(result: Dict[str, Any]) -> None:
|
|
print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
|
|
|
|
async def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Sync external APIs into ClientFlow reconciliation candidates.")
|
|
parser.add_argument("--all", action="store_true", help="sync all enabled external systems")
|
|
parser.add_argument("--jasmin", action="store_true", help="sync Jasmin quotations/invoices")
|
|
parser.add_argument("--odoo", action="store_true", help="sync Odoo sale orders")
|
|
parser.add_argument("--packlink", action="store_true", help="sync Packlink shipments")
|
|
parser.add_argument("--customers", action="store_true", help="seed fiscal customers from Jasmin/Odoo before document reconciliation")
|
|
parser.add_argument("--limit", type=int, default=50, help="maximum records per source")
|
|
parser.add_argument("--days", type=int, default=3, help="lookback window for Jasmin/Odoo/Packlink syncs")
|
|
args = parser.parse_args()
|
|
|
|
if args.all or not (args.jasmin or args.odoo or args.packlink or args.customers):
|
|
_print(await sync_all_external_reconciliation_candidates(limit=args.limit, days=args.days))
|
|
return
|
|
|
|
if args.customers and not (args.jasmin or args.odoo or args.packlink):
|
|
_print(await sync_external_fiscal_customers_for_reconciliation(limit=max(args.limit, 200)))
|
|
return
|
|
|
|
results: List[Dict[str, Any]] = []
|
|
customer_result = None
|
|
if args.customers:
|
|
customer_result = await sync_external_fiscal_customers_for_reconciliation(limit=max(args.limit, 200))
|
|
if args.jasmin:
|
|
results.append(await sync_jasmin_reconciliation_candidates(limit=args.limit, days=args.days))
|
|
if args.odoo:
|
|
# Odoo XML-RPC client is sync; the service itself stays sync for easier reuse.
|
|
results.append(sync_odoo_reconciliation_candidates(limit=args.limit, days=args.days))
|
|
if args.packlink:
|
|
results.append(await sync_packlink_reconciliation_candidates(limit=args.limit, days=args.days))
|
|
output = {
|
|
"seen": sum(int(r.get("seen") or 0) for r in results),
|
|
"created_or_updated": sum(int(r.get("created_or_updated") or 0) for r in results),
|
|
"results": results,
|
|
}
|
|
if customer_result is not None:
|
|
output["customer_seen"] = customer_result.get("seen", 0)
|
|
output["customers_created_or_updated"] = customer_result.get("created_or_updated", 0)
|
|
output["customer_results"] = customer_result.get("results", [])
|
|
_print(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|