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:
@@ -3,132 +3,165 @@
|
||||
{% block nav_logs %}active{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0">Jurnale Import</h4>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<select class="form-select form-select-sm" id="runSelector" style="min-width: 320px;">
|
||||
<option value="">-- Selecteaza un sync run --</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="loadRuns()" title="Reincarca lista">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mb-4">Jurnale Import</h4>
|
||||
|
||||
<!-- Sync Runs Table (always visible) -->
|
||||
<!-- Sync Run Selector -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Sync Runs</span>
|
||||
<div id="runsTablePagination" class="d-flex align-items-center gap-2"></div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
<th>Status</th>
|
||||
<th>Total</th>
|
||||
<th>OK</th>
|
||||
<th>Fara mapare</th>
|
||||
<th>Erori</th>
|
||||
<th>Durata</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="runsTableBody">
|
||||
<tr><td colspan="7" class="text-center text-muted py-3">Se incarca...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-body py-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<label class="form-label mb-0 fw-bold text-nowrap">Sync Run:</label>
|
||||
<select class="form-select form-select-sm" id="runsDropdown" onchange="selectRun(this.value)">
|
||||
<option value="">Se incarca...</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline-secondary text-nowrap" onclick="loadRuns()" title="Reincarca lista"><i class="bi bi-arrow-clockwise"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Run Detail Section (shown when run selected or live sync) -->
|
||||
<div id="runDetailSection" style="display:none;">
|
||||
|
||||
<!-- Run Summary Bar -->
|
||||
<div class="row g-3 mb-3" id="runSummary">
|
||||
<div class="col-auto">
|
||||
<div class="card stat-card px-3 py-2">
|
||||
<div class="stat-value text-primary" id="sum-total" style="font-size:1.25rem;">-</div>
|
||||
<div class="stat-label">Total</div>
|
||||
<!-- Detail Viewer (shown when run selected) -->
|
||||
<div id="logViewerSection" style="display:none;">
|
||||
<!-- Filter bar -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Run: <code id="logRunId"></code> <span class="badge bg-secondary" id="logStatusBadge">-</span></span>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="autoRefreshToggle" checked>
|
||||
<label class="form-check-label small" for="autoRefreshToggle">Auto-refresh</label>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="btnShowTextLog" onclick="toggleTextLog()">
|
||||
<i class="bi bi-file-text"></i> Log text brut
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="card stat-card px-3 py-2">
|
||||
<div class="stat-value text-success" id="sum-imported" style="font-size:1.25rem;">-</div>
|
||||
<div class="stat-label">Importate</div>
|
||||
<div class="card-body py-2">
|
||||
<div class="btn-group" role="group" id="orderFilterBtns">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="filterOrders('all')">
|
||||
Toate <span class="badge bg-light text-dark ms-1" id="countAll">0</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-success" onclick="filterOrders('IMPORTED')">
|
||||
Importate <span class="badge bg-light text-dark ms-1" id="countImported">0</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-warning" onclick="filterOrders('SKIPPED')">
|
||||
Omise <span class="badge bg-light text-dark ms-1" id="countSkipped">0</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="filterOrders('ERROR')">
|
||||
Erori <span class="badge bg-light text-dark ms-1" id="countError">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="card stat-card px-3 py-2">
|
||||
<div class="stat-value text-warning" id="sum-skipped" style="font-size:1.25rem;">-</div>
|
||||
<div class="stat-label">Omise</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="card stat-card px-3 py-2">
|
||||
<div class="stat-value text-danger" id="sum-errors" style="font-size:1.25rem;">-</div>
|
||||
<div class="stat-label">Erori</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="card stat-card px-3 py-2">
|
||||
<div class="stat-value text-secondary" id="sum-duration" style="font-size:1.25rem;">-</div>
|
||||
<div class="stat-label">Durata</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live Feed (visible only during active sync) -->
|
||||
<div class="card mb-3" id="liveFeedCard" style="display:none;">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-broadcast"></i> Live Feed
|
||||
<span class="badge bg-danger ms-2 live-pulse">LIVE</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="live-feed" id="liveFeed"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter buttons -->
|
||||
<div class="mb-3" id="filterRow">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<button type="button" class="btn btn-outline-secondary active" data-filter="all">
|
||||
<i class="bi bi-list-ul"></i> Toate
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-success" data-filter="IMPORTED">
|
||||
<i class="bi bi-check-circle"></i> Importate
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-warning" data-filter="SKIPPED">
|
||||
<i class="bi bi-skip-forward"></i> Fara Mapare
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" data-filter="ERROR">
|
||||
<i class="bi bi-x-circle"></i> Erori
|
||||
</button>
|
||||
</div>
|
||||
<small class="text-muted ms-3" id="filterCount"></small>
|
||||
</div>
|
||||
|
||||
<!-- Orders table -->
|
||||
<div class="card">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0" id="logsTable">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:140px;">Nr. Comanda</th>
|
||||
<th>Client</th>
|
||||
<th style="width:100px;" class="text-center">Nr. Articole</th>
|
||||
<th style="width:120px;">Status</th>
|
||||
<th>Eroare / Detalii</th>
|
||||
<th>#</th>
|
||||
<th class="sortable" onclick="sortOrdersBy('order_date')">Data comanda <span class="sort-icon" data-col="order_date"></span></th>
|
||||
<th class="sortable" onclick="sortOrdersBy('order_number')">Nr. comanda <span class="sort-icon" data-col="order_number"></span></th>
|
||||
<th class="sortable" onclick="sortOrdersBy('customer_name')">Client <span class="sort-icon" data-col="customer_name"></span></th>
|
||||
<th class="sortable" onclick="sortOrdersBy('items_count')">Articole <span class="sort-icon" data-col="items_count"></span></th>
|
||||
<th class="sortable" onclick="sortOrdersBy('status')">Status <span class="sort-icon" data-col="status"></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logsBody">
|
||||
<tbody id="runOrdersBody">
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Selecteaza un sync run</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted" id="ordersPageInfo"></small>
|
||||
<div id="ordersPagination" class="d-flex align-items-center gap-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible text log -->
|
||||
<div id="textLogSection" style="display:none;">
|
||||
<div class="card">
|
||||
<div class="card-header">Log text brut</div>
|
||||
<pre class="log-viewer" id="logContent">Se incarca...</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Detail Modal -->
|
||||
<div class="modal fade" id="orderDetailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Comanda <code id="detailOrderNumber"></code></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted">Client:</small> <strong id="detailCustomer"></strong><br>
|
||||
<small class="text-muted">Data comanda:</small> <span id="detailDate"></span><br>
|
||||
<small class="text-muted">Status:</small> <span id="detailStatus"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted">ID Comanda ROA:</small> <span id="detailIdComanda">-</span><br>
|
||||
<small class="text-muted">ID Partener:</small> <span id="detailIdPartener">-</span><br>
|
||||
<small class="text-muted">ID Adr. Facturare:</small> <span id="detailIdAdresaFact">-</span><br>
|
||||
<small class="text-muted">ID Adr. Livrare:</small> <span id="detailIdAdresaLivr">-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>SKU</th>
|
||||
<th>Produs</th>
|
||||
<th>Cant.</th>
|
||||
<th>Pret</th>
|
||||
<th>TVA</th>
|
||||
<th>CODMAT</th>
|
||||
<th>Status</th>
|
||||
<th>Actiune</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="detailItemsBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="detailError" class="alert alert-danger mt-3" style="display:none;"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Inchide</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Map Modal (used from order detail) -->
|
||||
<div class="modal fade" id="quickMapModal" tabindex="-1" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Mapeaza SKU: <code id="qmSku"></code></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-2">
|
||||
<small class="text-muted">Produs web:</small> <strong id="qmProductName"></strong>
|
||||
</div>
|
||||
<div id="qmCodmatLines">
|
||||
<!-- Dynamic CODMAT lines -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary mt-2" onclick="addQmCodmatLine()">
|
||||
<i class="bi bi-plus"></i> Adauga CODMAT
|
||||
</button>
|
||||
<div id="qmPctWarning" class="text-danger mt-2" style="display:none;"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuleaza</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveQuickMapping()">Salveaza</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user