diff --git a/VERSION b/VERSION index c3c2c0e..393b4c5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.94 +2.2.95 diff --git a/app/economy/backend/router.py b/app/economy/backend/router.py index be3dd01..65c5089 100644 --- a/app/economy/backend/router.py +++ b/app/economy/backend/router.py @@ -465,18 +465,18 @@ async def send_selected_to_invoices(payload: BulkSendRequest, request: Request): if not rows: raise HTTPException(status_code=400, detail="No eligible entries found") - # Ensure selected invoice candidates are approved and invoice-billable. - selected_invoice_ids = [ + # Local order creation must not depend on e-conomic data/mapping. + # We only require billable entries; billing_method can be invoice/prepaid/fixed_price/internal. + selected_order_ids = [ int(r["id"]) for r in rows if bool(r.get("billable", True)) - and (r.get("billing_method") or "invoice") == "invoice" ] - if not selected_invoice_ids: - raise HTTPException(status_code=400, detail="No selected entries are invoice-billable") + if not selected_order_ids: + raise HTTPException(status_code=400, detail="No selected entries are billable") - placeholders_invoice = ",".join(["%s"] * len(selected_invoice_ids)) + placeholders_invoice = ",".join(["%s"] * len(selected_order_ids)) execute_update( f""" UPDATE tmodule_times @@ -488,19 +488,35 @@ async def send_selected_to_invoices(payload: BulkSendRequest, request: Request): WHERE id IN ({placeholders_invoice}) AND status <> 'billed' """, - tuple(selected_invoice_ids), + tuple(selected_order_ids), ) rows_by_customer: Dict[int, List[Dict[str, Any]]] = defaultdict(list) + skipped_missing_customer: List[int] = [] for row in rows: - if int(row["id"]) in selected_invoice_ids: - rows_by_customer[int(row["customer_id"])].append(row) + if int(row["id"]) not in selected_order_ids: + continue + + cust_id = row.get("customer_id") + if cust_id is None: + skipped_missing_customer.append(int(row["id"])) + continue + + rows_by_customer[int(cust_id)].append(row) created_orders = [] for cust_id, cust_rows in rows_by_customer.items(): order_id = _create_order_from_selected(cust_id, cust_rows, user_id) created_orders.append({"customer_id": cust_id, "order_id": order_id}) + if not created_orders: + if skipped_missing_customer: + raise HTTPException( + status_code=400, + detail="No local orders created: selected entries are missing customer linkage", + ) + raise HTTPException(status_code=400, detail="No local orders created") + # Time queue must never push directly to e-conomic. # Orders are created locally and can be transferred manually from Orders page. order_ids = [o["order_id"] for o in created_orders] @@ -511,8 +527,9 @@ async def send_selected_to_invoices(payload: BulkSendRequest, request: Request): return { "success": True, "selected": len(ids), - "invoice_candidates": len(selected_invoice_ids), + "billable_candidates": len(selected_order_ids), "created_orders": created_orders, + "skipped_missing_customer": skipped_missing_customer, "orders_url": orders_url, "message": "Lokale ordrer oprettet. Overfoer til e-conomic fra Ordre-siden.", } diff --git a/app/economy/frontend/time_queue.html b/app/economy/frontend/time_queue.html index 3043ff2..1c73048 100644 --- a/app/economy/frontend/time_queue.html +++ b/app/economy/frontend/time_queue.html @@ -458,9 +458,11 @@ const orders = (result.created_orders || []).map((x) => { return `customer ${x.customer_id}, order ${x.order_id}`; }).join('\n'); + const skipped = (result.skipped_missing_customer || []); const orderMessage = orders || 'Ingen ordrer oprettet'; const nextStep = result.orders_url ? `\n\nAabn ordre: ${result.orders_url}` : ''; - alert(`Lokale ordrer oprettet:\n${orderMessage}${nextStep}`); + const skippedMsg = skipped.length ? `\n\nSprunget over (mangler kunde-link): ${skipped.join(', ')}` : ''; + alert(`Lokale ordrer oprettet:\n${orderMessage}${skippedMsg}${nextStep}`); await loadCustomers(); await loadEntries(); } catch (err) {