feat(sync): handle cancelled GoMag orders (status Anulata / statusId 7)
- Add web_status column to orders table (generic name for platform status) - Filter cancelled orders during sync, record as CANCELLED in SQLite - Soft-delete previously-imported cancelled orders in Oracle (if not invoiced) - Add CANCELLED filter pill + badge in dashboard UI - New soft_delete_order_in_roa() and mark_order_cancelled() functions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,7 +109,8 @@ CREATE TABLE IF NOT EXISTS orders (
|
||||
invoice_checked_at TEXT,
|
||||
order_total REAL,
|
||||
delivery_cost REAL,
|
||||
discount_total REAL
|
||||
discount_total REAL,
|
||||
web_status TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_date ON orders(order_date);
|
||||
@@ -316,6 +317,7 @@ def init_sqlite():
|
||||
("order_total", "REAL"),
|
||||
("delivery_cost", "REAL"),
|
||||
("discount_total", "REAL"),
|
||||
("web_status", "TEXT"),
|
||||
]:
|
||||
if col not in order_cols:
|
||||
conn.execute(f"ALTER TABLE orders ADD COLUMN {col} {typedef}")
|
||||
|
||||
@@ -294,3 +294,51 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def soft_delete_order_in_roa(id_comanda: int) -> dict:
|
||||
"""Soft-delete an order in Oracle ROA (set sters=1 on comenzi + comenzi_detalii).
|
||||
Returns {"success": bool, "error": str|None, "details_deleted": int}
|
||||
"""
|
||||
result = {"success": False, "error": None, "details_deleted": 0}
|
||||
|
||||
if database.pool is None:
|
||||
result["error"] = "Oracle pool not initialized"
|
||||
return result
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = database.pool.acquire()
|
||||
with conn.cursor() as cur:
|
||||
# Soft-delete order details
|
||||
cur.execute(
|
||||
"UPDATE comenzi_detalii SET sters = 1 WHERE id_comanda = :1 AND sters = 0",
|
||||
[id_comanda]
|
||||
)
|
||||
result["details_deleted"] = cur.rowcount
|
||||
|
||||
# Soft-delete the order itself
|
||||
cur.execute(
|
||||
"UPDATE comenzi SET sters = 1 WHERE id_comanda = :1 AND sters = 0",
|
||||
[id_comanda]
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
result["success"] = True
|
||||
logger.info(f"Soft-deleted order ID={id_comanda} in Oracle ROA ({result['details_deleted']} details)")
|
||||
except Exception as e:
|
||||
result["error"] = str(e)
|
||||
logger.error(f"Error soft-deleting order ID={id_comanda}: {e}")
|
||||
if conn:
|
||||
try:
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
if conn:
|
||||
try:
|
||||
database.pool.release(conn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
@@ -52,7 +52,8 @@ async def upsert_order(sync_run_id: str, order_number: str, order_date: str,
|
||||
shipping_name: str = None, billing_name: str = None,
|
||||
payment_method: str = None, delivery_method: str = None,
|
||||
order_total: float = None,
|
||||
delivery_cost: float = None, discount_total: float = None):
|
||||
delivery_cost: float = None, discount_total: float = None,
|
||||
web_status: str = None):
|
||||
"""Upsert a single order — one row per order_number, status updated in place."""
|
||||
db = await get_sqlite()
|
||||
try:
|
||||
@@ -62,8 +63,8 @@ async def upsert_order(sync_run_id: str, order_number: str, order_date: str,
|
||||
id_comanda, id_partener, error_message, missing_skus, items_count,
|
||||
last_sync_run_id, shipping_name, billing_name,
|
||||
payment_method, delivery_method, order_total,
|
||||
delivery_cost, discount_total)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
delivery_cost, discount_total, web_status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(order_number) DO UPDATE SET
|
||||
customer_name = excluded.customer_name,
|
||||
status = CASE
|
||||
@@ -87,13 +88,14 @@ async def upsert_order(sync_run_id: str, order_number: str, order_date: str,
|
||||
order_total = COALESCE(excluded.order_total, orders.order_total),
|
||||
delivery_cost = COALESCE(excluded.delivery_cost, orders.delivery_cost),
|
||||
discount_total = COALESCE(excluded.discount_total, orders.discount_total),
|
||||
web_status = COALESCE(excluded.web_status, orders.web_status),
|
||||
updated_at = datetime('now')
|
||||
""", (order_number, order_date, customer_name, status,
|
||||
id_comanda, id_partener, error_message,
|
||||
json.dumps(missing_skus) if missing_skus else None,
|
||||
items_count, sync_run_id, shipping_name, billing_name,
|
||||
payment_method, delivery_method, order_total,
|
||||
delivery_cost, discount_total))
|
||||
delivery_cost, discount_total, web_status))
|
||||
await db.commit()
|
||||
finally:
|
||||
await db.close()
|
||||
@@ -118,7 +120,8 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
Each dict must have: sync_run_id, order_number, order_date, customer_name, status,
|
||||
id_comanda, id_partener, error_message, missing_skus (list|None), items_count,
|
||||
shipping_name, billing_name, payment_method, delivery_method, status_at_run,
|
||||
items (list of item dicts), delivery_cost (optional), discount_total (optional).
|
||||
items (list of item dicts), delivery_cost (optional), discount_total (optional),
|
||||
web_status (optional).
|
||||
"""
|
||||
if not orders_data:
|
||||
return
|
||||
@@ -131,8 +134,8 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
id_comanda, id_partener, error_message, missing_skus, items_count,
|
||||
last_sync_run_id, shipping_name, billing_name,
|
||||
payment_method, delivery_method, order_total,
|
||||
delivery_cost, discount_total)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
delivery_cost, discount_total, web_status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(order_number) DO UPDATE SET
|
||||
customer_name = excluded.customer_name,
|
||||
status = CASE
|
||||
@@ -156,6 +159,7 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
order_total = COALESCE(excluded.order_total, orders.order_total),
|
||||
delivery_cost = COALESCE(excluded.delivery_cost, orders.delivery_cost),
|
||||
discount_total = COALESCE(excluded.discount_total, orders.discount_total),
|
||||
web_status = COALESCE(excluded.web_status, orders.web_status),
|
||||
updated_at = datetime('now')
|
||||
""", [
|
||||
(d["order_number"], d["order_date"], d["customer_name"], d["status"],
|
||||
@@ -165,7 +169,8 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
d.get("shipping_name"), d.get("billing_name"),
|
||||
d.get("payment_method"), d.get("delivery_method"),
|
||||
d.get("order_total"),
|
||||
d.get("delivery_cost"), d.get("discount_total"))
|
||||
d.get("delivery_cost"), d.get("discount_total"),
|
||||
d.get("web_status"))
|
||||
for d in orders_data
|
||||
])
|
||||
|
||||
@@ -619,6 +624,7 @@ async def get_run_orders_filtered(run_id: str, status_filter: str = "all",
|
||||
"skipped": status_counts.get("SKIPPED", 0),
|
||||
"error": status_counts.get("ERROR", 0),
|
||||
"already_imported": status_counts.get("ALREADY_IMPORTED", 0),
|
||||
"cancelled": status_counts.get("CANCELLED", 0),
|
||||
"total": sum(status_counts.values())
|
||||
}
|
||||
}
|
||||
@@ -715,6 +721,7 @@ async def get_orders(page: int = 1, per_page: int = 50,
|
||||
"imported_all": status_counts.get("IMPORTED", 0) + status_counts.get("ALREADY_IMPORTED", 0),
|
||||
"skipped": status_counts.get("SKIPPED", 0),
|
||||
"error": status_counts.get("ERROR", 0),
|
||||
"cancelled": status_counts.get("CANCELLED", 0),
|
||||
"total": sum(status_counts.values()),
|
||||
"uninvoiced_sqlite": uninvoiced_sqlite,
|
||||
}
|
||||
@@ -860,6 +867,32 @@ async def mark_order_deleted_in_roa(order_number: str):
|
||||
await db.close()
|
||||
|
||||
|
||||
async def mark_order_cancelled(order_number: str, web_status: str = "Anulata"):
|
||||
"""Mark an order as cancelled from GoMag. Clears id_comanda and invoice cache."""
|
||||
db = await get_sqlite()
|
||||
try:
|
||||
await db.execute("""
|
||||
UPDATE orders SET
|
||||
status = 'CANCELLED',
|
||||
id_comanda = NULL,
|
||||
id_partener = NULL,
|
||||
factura_serie = NULL,
|
||||
factura_numar = NULL,
|
||||
factura_total_fara_tva = NULL,
|
||||
factura_total_tva = NULL,
|
||||
factura_total_cu_tva = NULL,
|
||||
factura_data = NULL,
|
||||
invoice_checked_at = NULL,
|
||||
web_status = ?,
|
||||
error_message = 'Comanda anulata in GoMag',
|
||||
updated_at = datetime('now')
|
||||
WHERE order_number = ?
|
||||
""", (web_status, order_number))
|
||||
await db.commit()
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
|
||||
# ── App Settings ─────────────────────────────────
|
||||
|
||||
async def get_app_settings() -> dict:
|
||||
|
||||
@@ -72,7 +72,7 @@ async def prepare_sync(id_pol: int = None, id_sectie: int = None) -> dict:
|
||||
"phase_text": "Starting...",
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"counts": {"imported": 0, "skipped": 0, "errors": 0, "already_imported": 0},
|
||||
"counts": {"imported": 0, "skipped": 0, "errors": 0, "already_imported": 0, "cancelled": 0},
|
||||
}
|
||||
return {"run_id": run_id, "status": "starting"}
|
||||
|
||||
@@ -152,7 +152,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
"phase_text": "Reading JSON files...",
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"counts": {"imported": 0, "skipped": 0, "errors": 0, "already_imported": 0},
|
||||
"counts": {"imported": 0, "skipped": 0, "errors": 0, "already_imported": 0, "cancelled": 0},
|
||||
}
|
||||
|
||||
_update_progress("reading", "Reading JSON files...")
|
||||
@@ -212,6 +212,104 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
summary = {"run_id": run_id, "status": "completed", "message": "No orders found", "json_files": json_count}
|
||||
return summary
|
||||
|
||||
# ── Separate cancelled orders (GoMag status "Anulata" / statusId "7") ──
|
||||
cancelled_orders = [o for o in orders if o.status_id == "7" or (o.status and o.status.lower() == "anulata")]
|
||||
active_orders = [o for o in orders if o not in cancelled_orders]
|
||||
cancelled_count = len(cancelled_orders)
|
||||
|
||||
if cancelled_orders:
|
||||
_log_line(run_id, f"Comenzi anulate in GoMag: {cancelled_count}")
|
||||
|
||||
# Record cancelled orders in SQLite
|
||||
cancelled_batch = []
|
||||
for order in cancelled_orders:
|
||||
shipping_name, billing_name, customer, payment_method, delivery_method = _derive_customer_info(order)
|
||||
order_items_data = [
|
||||
{"sku": item.sku, "product_name": item.name,
|
||||
"quantity": item.quantity, "price": item.price, "vat": item.vat,
|
||||
"mapping_status": "unknown", "codmat": None,
|
||||
"id_articol": None, "cantitate_roa": None}
|
||||
for item in order.items
|
||||
]
|
||||
cancelled_batch.append({
|
||||
"sync_run_id": run_id, "order_number": order.number,
|
||||
"order_date": order.date, "customer_name": customer,
|
||||
"status": "CANCELLED", "status_at_run": "CANCELLED",
|
||||
"id_comanda": None, "id_partener": None,
|
||||
"error_message": "Comanda anulata in GoMag",
|
||||
"missing_skus": None,
|
||||
"items_count": len(order.items),
|
||||
"shipping_name": shipping_name, "billing_name": billing_name,
|
||||
"payment_method": payment_method, "delivery_method": delivery_method,
|
||||
"order_total": order.total or None,
|
||||
"delivery_cost": order.delivery_cost or None,
|
||||
"discount_total": order.discount_total or None,
|
||||
"web_status": order.status or "Anulata",
|
||||
"items": order_items_data,
|
||||
})
|
||||
_log_line(run_id, f"#{order.number} [{order.date or '?'}] {customer} → ANULAT in GoMag")
|
||||
|
||||
await sqlite_service.save_orders_batch(cancelled_batch)
|
||||
|
||||
# Check if any cancelled orders were previously imported
|
||||
from ..database import get_sqlite as _get_sqlite
|
||||
db_check = await _get_sqlite()
|
||||
try:
|
||||
cancelled_numbers = [o.number for o in cancelled_orders]
|
||||
placeholders = ",".join("?" for _ in cancelled_numbers)
|
||||
cursor = await db_check.execute(f"""
|
||||
SELECT order_number, id_comanda FROM orders
|
||||
WHERE order_number IN ({placeholders})
|
||||
AND id_comanda IS NOT NULL
|
||||
AND status = 'CANCELLED'
|
||||
""", cancelled_numbers)
|
||||
previously_imported = [dict(r) for r in await cursor.fetchall()]
|
||||
finally:
|
||||
await db_check.close()
|
||||
|
||||
if previously_imported:
|
||||
_log_line(run_id, f"Verificare {len(previously_imported)} comenzi anulate care erau importate in Oracle...")
|
||||
# Check which have invoices
|
||||
id_comanda_list = [o["id_comanda"] for o in previously_imported]
|
||||
invoice_data = await asyncio.to_thread(
|
||||
invoice_service.check_invoices_for_orders, id_comanda_list
|
||||
)
|
||||
|
||||
for o in previously_imported:
|
||||
idc = o["id_comanda"]
|
||||
order_num = o["order_number"]
|
||||
if idc in invoice_data:
|
||||
# Invoiced — keep in Oracle, just log warning
|
||||
_log_line(run_id,
|
||||
f"#{order_num} → ANULAT dar FACTURAT (factura {invoice_data[idc].get('serie_act', '')}"
|
||||
f"{invoice_data[idc].get('numar_act', '')}) — NU se sterge din Oracle")
|
||||
# Update web_status but keep CANCELLED status (already set by batch above)
|
||||
else:
|
||||
# Not invoiced — soft-delete in Oracle
|
||||
del_result = await asyncio.to_thread(
|
||||
import_service.soft_delete_order_in_roa, idc
|
||||
)
|
||||
if del_result["success"]:
|
||||
# Clear id_comanda via mark_order_cancelled
|
||||
await sqlite_service.mark_order_cancelled(order_num, "Anulata")
|
||||
_log_line(run_id,
|
||||
f"#{order_num} → ANULAT + STERS din Oracle (ID: {idc}, "
|
||||
f"{del_result['details_deleted']} detalii)")
|
||||
else:
|
||||
_log_line(run_id,
|
||||
f"#{order_num} → ANULAT dar EROARE la stergere Oracle: {del_result['error']}")
|
||||
|
||||
orders = active_orders
|
||||
|
||||
if not orders:
|
||||
_log_line(run_id, "Nicio comanda activa dupa filtrare anulate.")
|
||||
await sqlite_service.update_sync_run(run_id, "completed", cancelled_count, 0, 0, 0)
|
||||
_update_progress("completed", f"No active orders ({cancelled_count} cancelled)")
|
||||
summary = {"run_id": run_id, "status": "completed",
|
||||
"message": f"No active orders ({cancelled_count} cancelled)",
|
||||
"json_files": json_count, "cancelled": cancelled_count}
|
||||
return summary
|
||||
|
||||
_update_progress("validation", f"Validating {len(orders)} orders...", 0, len(orders))
|
||||
|
||||
# ── Single Oracle connection for entire validation phase ──
|
||||
@@ -351,6 +449,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
"order_total": order.total or None,
|
||||
"delivery_cost": order.delivery_cost or None,
|
||||
"discount_total": order.discount_total or None,
|
||||
"web_status": order.status or None,
|
||||
"items": order_items_data,
|
||||
})
|
||||
_log_line(run_id, f"#{order.number} [{order.date or '?'}] {customer} → DEJA IMPORTAT (ID: {id_comanda_roa})")
|
||||
@@ -381,6 +480,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
"order_total": order.total or None,
|
||||
"delivery_cost": order.delivery_cost or None,
|
||||
"discount_total": order.discount_total or None,
|
||||
"web_status": order.status or None,
|
||||
"items": order_items_data,
|
||||
})
|
||||
_log_line(run_id, f"#{order.number} [{order.date or '?'}] {customer} → OMIS (lipsa: {', '.join(missing_skus)})")
|
||||
@@ -437,6 +537,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
order_total=order.total or None,
|
||||
delivery_cost=order.delivery_cost or None,
|
||||
discount_total=order.discount_total or None,
|
||||
web_status=order.status or None,
|
||||
)
|
||||
await sqlite_service.add_sync_run_order(run_id, order.number, "IMPORTED")
|
||||
# Store ROA address IDs (R9)
|
||||
@@ -465,6 +566,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
order_total=order.total or None,
|
||||
delivery_cost=order.delivery_cost or None,
|
||||
discount_total=order.discount_total or None,
|
||||
web_status=order.status or None,
|
||||
)
|
||||
await sqlite_service.add_sync_run_order(run_id, order.number, "ERROR")
|
||||
await sqlite_service.add_order_items(order.number, order_items_data)
|
||||
@@ -548,13 +650,14 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
"run_id": run_id,
|
||||
"status": status,
|
||||
"json_files": json_count,
|
||||
"total_orders": len(orders),
|
||||
"total_orders": len(orders) + cancelled_count,
|
||||
"new_orders": len(truly_importable),
|
||||
"imported": total_imported,
|
||||
"new_imported": imported_count,
|
||||
"already_imported": already_imported_count,
|
||||
"skipped": len(skipped),
|
||||
"errors": error_count,
|
||||
"cancelled": cancelled_count,
|
||||
"missing_skus": len(validation["missing"]),
|
||||
"invoices_updated": invoices_updated,
|
||||
"invoices_cleared": invoices_cleared,
|
||||
@@ -562,24 +665,25 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
}
|
||||
|
||||
_update_progress("completed",
|
||||
f"Completed: {imported_count} new, {already_imported_count} already, {len(skipped)} skipped, {error_count} errors",
|
||||
f"Completed: {imported_count} new, {already_imported_count} already, {len(skipped)} skipped, {error_count} errors, {cancelled_count} cancelled",
|
||||
len(truly_importable), len(truly_importable),
|
||||
{"imported": imported_count, "skipped": len(skipped), "errors": error_count,
|
||||
"already_imported": already_imported_count})
|
||||
"already_imported": already_imported_count, "cancelled": cancelled_count})
|
||||
if _current_sync:
|
||||
_current_sync["status"] = status
|
||||
_current_sync["finished_at"] = datetime.now().isoformat()
|
||||
|
||||
logger.info(
|
||||
f"Sync {run_id} completed: {imported_count} new, {already_imported_count} already imported, "
|
||||
f"{len(skipped)} skipped, {error_count} errors"
|
||||
f"{len(skipped)} skipped, {error_count} errors, {cancelled_count} cancelled"
|
||||
)
|
||||
|
||||
duration = (datetime.now() - started_dt).total_seconds()
|
||||
_log_line(run_id, "")
|
||||
cancelled_text = f", {cancelled_count} anulate" if cancelled_count else ""
|
||||
_run_logs[run_id].append(
|
||||
f"Finalizat: {imported_count} importate, {already_imported_count} deja importate, "
|
||||
f"{len(skipped)} nemapate, {error_count} erori din {len(orders)} comenzi | Durata: {int(duration)}s"
|
||||
f"{len(skipped)} nemapate, {error_count} erori{cancelled_text} din {len(orders) + cancelled_count} comenzi | Durata: {int(duration)}s"
|
||||
)
|
||||
|
||||
return summary
|
||||
|
||||
@@ -289,6 +289,7 @@ body {
|
||||
.fc-red { color: #dc2626; }
|
||||
.fc-neutral { color: #6b7280; }
|
||||
.fc-blue { color: #2563eb; }
|
||||
.fc-dark { color: #374151; }
|
||||
|
||||
/* ── Log viewer (dark theme — keep as-is) ────────── */
|
||||
.log-viewer {
|
||||
|
||||
@@ -283,6 +283,7 @@ async function loadDashOrders() {
|
||||
if (el('cntErr')) el('cntErr').textContent = c.error || c.errors || 0;
|
||||
if (el('cntFact')) el('cntFact').textContent = c.facturate || 0;
|
||||
if (el('cntNef')) el('cntNef').textContent = c.nefacturate || c.uninvoiced || 0;
|
||||
if (el('cntCanc')) el('cntCanc').textContent = c.cancelled || 0;
|
||||
|
||||
const tbody = document.getElementById('dashOrdersBody');
|
||||
const orders = data.orders || [];
|
||||
@@ -340,7 +341,8 @@ async function loadDashOrders() {
|
||||
{ label: 'Omise', count: c.skipped || 0, value: 'SKIPPED', active: activeStatus === 'SKIPPED', colorClass: 'fc-yellow' },
|
||||
{ label: 'Erori', count: c.error || c.errors || 0, value: 'ERROR', active: activeStatus === 'ERROR', colorClass: 'fc-red' },
|
||||
{ label: 'Fact.', count: c.facturate || 0, value: 'INVOICED', active: activeStatus === 'INVOICED', colorClass: 'fc-green' },
|
||||
{ label: 'Nefact.', count: c.nefacturate || c.uninvoiced || 0, value: 'UNINVOICED', active: activeStatus === 'UNINVOICED', colorClass: 'fc-red' }
|
||||
{ label: 'Nefact.', count: c.nefacturate || c.uninvoiced || 0, value: 'UNINVOICED', active: activeStatus === 'UNINVOICED', colorClass: 'fc-red' },
|
||||
{ label: 'Anulate', count: c.cancelled || 0, value: 'CANCELLED', active: activeStatus === 'CANCELLED', colorClass: 'fc-dark' }
|
||||
], (val) => {
|
||||
document.querySelectorAll('.filter-pill[data-status]').forEach(b => b.classList.remove('active'));
|
||||
const pill = document.querySelector(`.filter-pill[data-status="${val}"]`);
|
||||
@@ -438,6 +440,7 @@ function orderStatusBadge(status) {
|
||||
case 'ALREADY_IMPORTED': return '<span class="badge bg-info">Deja importat</span>';
|
||||
case 'SKIPPED': return '<span class="badge bg-warning">Omis</span>';
|
||||
case 'ERROR': return '<span class="badge bg-danger">Eroare</span>';
|
||||
case 'CANCELLED': return '<span class="badge bg-secondary">Anulat</span>';
|
||||
case 'DELETED_IN_ROA': return '<span class="badge bg-dark">Sters din ROA</span>';
|
||||
default: return `<span class="badge bg-secondary">${esc(status)}</span>`;
|
||||
}
|
||||
|
||||
@@ -219,6 +219,9 @@ function statusDot(status) {
|
||||
case 'ERROR':
|
||||
case 'FAILED':
|
||||
return '<span class="dot dot-red"></span>';
|
||||
case 'CANCELLED':
|
||||
case 'DELETED_IN_ROA':
|
||||
return '<span class="dot dot-gray"></span>';
|
||||
default:
|
||||
return '<span class="dot dot-gray"></span>';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css" rel="stylesheet">
|
||||
{% set rp = request.scope.get('root_path', '') %}
|
||||
<link href="{{ rp }}/static/css/style.css?v=10" rel="stylesheet">
|
||||
<link href="{{ rp }}/static/css/style.css?v=11" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top Navbar -->
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<script>window.ROOT_PATH = "{{ rp }}";</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="{{ rp }}/static/js/shared.js?v=10"></script>
|
||||
<script src="{{ rp }}/static/js/shared.js?v=11"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<button class="filter-pill d-none d-md-inline-flex" data-status="ERROR">Erori <span class="filter-count fc-red" id="cntErr">0</span></button>
|
||||
<button class="filter-pill d-none d-md-inline-flex" data-status="INVOICED">Facturate <span class="filter-count fc-green" id="cntFact">0</span></button>
|
||||
<button class="filter-pill d-none d-md-inline-flex" data-status="UNINVOICED">Nefacturate <span class="filter-count fc-red" id="cntNef">0</span></button>
|
||||
<button class="filter-pill d-none d-md-inline-flex" data-status="CANCELLED">Anulate <span class="filter-count fc-dark" id="cntCanc">0</span></button>
|
||||
<button class="btn btn-sm btn-outline-secondary d-none d-md-inline-flex align-items-center gap-1" id="btnRefreshInvoices" onclick="refreshInvoices()" title="Actualizeaza status facturi din Oracle">↻ Facturi</button>
|
||||
<!-- Search (integrated, end of row) -->
|
||||
<input type="search" id="orderSearch" placeholder="Cauta..." class="search-input">
|
||||
@@ -199,5 +200,5 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=13"></script>
|
||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=14"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user