feat(sqlite): refactor orders schema + dashboard period filter

Replace import_orders (insert-per-run) with orders table (one row per
order, upsert on conflict). Eliminates dedup CTE on every dashboard
query and prevents unbounded row growth at 4-500 orders/sync.

Key changes:
- orders table: PK order_number, upsert via ON CONFLICT DO UPDATE;
  COALESCE preserves id_comanda once set; times_skipped auto-increments
- sync_run_orders: lightweight junction (sync_run_id, order_number)
  replaces sync_run_id column on orders
- order_items: PK changed to (order_number, sku), INSERT OR IGNORE
- Auto-migration in init_sqlite(): import_orders → orders on first boot,
  old table renamed to import_orders_bak
- /api/dashboard/orders: period_days param (3/7/30/0=all, default 7)
- Dashboard: period selector buttons in orders card header
- start.sh: stop existing process on port 5003 before restart;
  remove --reload (broken on WSL2 /mnt/e/)
- Add invoice_service, E2E Playwright tests, Oracle package updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 16:18:57 +02:00
parent 650e98539e
commit 82196b9dc0
32 changed files with 4164 additions and 1192 deletions

View File

@@ -44,9 +44,12 @@ def convert_web_date(date_str: str) -> datetime:
if not date_str:
return datetime.now()
try:
return datetime.strptime(date_str[:10], '%Y-%m-%d')
return datetime.strptime(date_str.strip(), '%Y-%m-%d %H:%M:%S')
except ValueError:
return datetime.now()
try:
return datetime.strptime(date_str.strip()[:10], '%Y-%m-%d')
except ValueError:
return datetime.now()
def format_address_for_oracle(address: str, city: str, region: str) -> str:
@@ -78,18 +81,26 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None) -> dic
success: bool
id_comanda: int or None
id_partener: int or None
id_adresa_facturare: int or None
id_adresa_livrare: int or None
error: str or None
"""
result = {
"success": False,
"id_comanda": None,
"id_partener": None,
"id_adresa_facturare": None,
"id_adresa_livrare": None,
"error": None
}
try:
order_number = clean_web_text(order.number)
order_date = convert_web_date(order.date)
logger.info(
f"Order {order.number}: raw date={order.date!r}"
f"parsed={order_date.strftime('%Y-%m-%d %H:%M:%S')}"
)
if database.pool is None:
raise RuntimeError("Oracle pool not initialized")
@@ -99,14 +110,14 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None) -> dic
id_partener = cur.var(oracledb.DB_TYPE_NUMBER)
if order.billing.is_company:
denumire = clean_web_text(order.billing.company_name)
denumire = clean_web_text(order.billing.company_name).upper()
cod_fiscal = clean_web_text(order.billing.company_code) or None
registru = clean_web_text(order.billing.company_reg) or None
is_pj = 1
else:
denumire = clean_web_text(
f"{order.billing.firstname} {order.billing.lastname}"
)
f"{order.billing.lastname} {order.billing.firstname}"
).upper()
cod_fiscal = None
registru = None
is_pj = 0
@@ -151,6 +162,11 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None) -> dic
])
addr_livr_id = id_adresa_livr.getvalue()
if addr_fact_id is not None:
result["id_adresa_facturare"] = int(addr_fact_id)
if addr_livr_id is not None:
result["id_adresa_livrare"] = int(addr_livr_id)
# Step 4: Build articles JSON and import order
articles_json = build_articles_json(order.items)