refactor(status): introduce OrderStatus enum, replace string literals

Centralized order status values in api/app/constants.py via a
str-valued Enum so comparisons keep working. Replaced literals in:
- services: sync_service, sqlite_service, retry_service
- routers: sync, dashboard
- templates: dashboard.html, logs.html
- static JS: shared (ORDER_STATUS mirror), dashboard, logs
- tests: requirements, order_items_overwrite, business_rules

MALFORMED intentionally NOT added — introduced in follow-up PR2
(per-order failure isolation).

Full test suite: 231 unit + 33 e2e pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-22 08:45:32 +00:00
parent 51790accf9
commit f6d283b743
14 changed files with 171 additions and 133 deletions

View File

@@ -11,6 +11,16 @@
};
})();
// ── Order status constants (mirror of Python OrderStatus enum) ────────────
const ORDER_STATUS = Object.freeze({
IMPORTED: 'IMPORTED',
ALREADY_IMPORTED: 'ALREADY_IMPORTED',
SKIPPED: 'SKIPPED',
ERROR: 'ERROR',
CANCELLED: 'CANCELLED',
DELETED_IN_ROA: 'DELETED_IN_ROA',
});
// ── HTML escaping ─────────────────────────────────
function esc(s) {
if (s == null) return '';
@@ -503,12 +513,12 @@ function fmtNum(v) {
function orderStatusBadge(status) {
switch ((status || '').toUpperCase()) {
case 'IMPORTED': return '<span class="badge bg-success">Importat</span>';
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>';
case ORDER_STATUS.IMPORTED: return '<span class="badge bg-success">Importat</span>';
case ORDER_STATUS.ALREADY_IMPORTED: return '<span class="badge bg-info">Deja importat</span>';
case ORDER_STATUS.SKIPPED: return '<span class="badge bg-warning">Omis</span>';
case ORDER_STATUS.ERROR: return '<span class="badge bg-danger">Eroare</span>';
case ORDER_STATUS.CANCELLED: return '<span class="badge bg-secondary">Anulat</span>';
case ORDER_STATUS.DELETED_IN_ROA: return '<span class="badge bg-dark">Sters din ROA</span>';
default: return `<span class="badge bg-secondary">${esc(status)}</span>`;
}
}
@@ -844,7 +854,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
// Retry button (only for ERROR/SKIPPED orders)
const retryBtn = document.getElementById('detailRetryBtn');
if (retryBtn) {
const canRetry = ['ERROR', 'SKIPPED', 'DELETED_IN_ROA'].includes((order.status || '').toUpperCase());
const canRetry = [ORDER_STATUS.ERROR, ORDER_STATUS.SKIPPED, ORDER_STATUS.DELETED_IN_ROA].includes((order.status || '').toUpperCase());
retryBtn.style.display = canRetry ? '' : 'none';
if (canRetry) {
retryBtn.onclick = async () => {
@@ -879,7 +889,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
// Resync button (IMPORTED/ALREADY_IMPORTED only)
const resyncBtn = document.getElementById('detailResyncBtn');
if (resyncBtn) {
const canResync = ['IMPORTED', 'ALREADY_IMPORTED'].includes((order.status || '').toUpperCase());
const canResync = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes((order.status || '').toUpperCase());
resyncBtn.style.display = canResync ? '' : 'none';
if (canResync) {
const isInvoiced = !!(order.factura_numar);
@@ -930,7 +940,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
// Delete button (IMPORTED/ALREADY_IMPORTED only)
const deleteBtn = document.getElementById('detailDeleteBtn');
if (deleteBtn) {
const canDelete = ['IMPORTED', 'ALREADY_IMPORTED'].includes((order.status || '').toUpperCase());
const canDelete = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes((order.status || '').toUpperCase());
deleteBtn.style.display = canDelete ? '' : 'none';
if (canDelete) {
const isInvoiced = !!(order.factura_numar);
@@ -1015,20 +1025,20 @@ function inlineConfirmAction(btn, confirmText, actionFn, opts) {
// ── Dot helper ────────────────────────────────────
function statusDot(status) {
switch ((status || '').toUpperCase()) {
case 'IMPORTED':
case 'ALREADY_IMPORTED':
case ORDER_STATUS.IMPORTED:
case ORDER_STATUS.ALREADY_IMPORTED:
case 'COMPLETED':
case 'RESOLVED':
return '<span class="dot dot-green"></span>';
case 'SKIPPED':
case ORDER_STATUS.SKIPPED:
case 'UNRESOLVED':
case 'INCOMPLETE':
return '<span class="dot dot-yellow"></span>';
case 'ERROR':
case ORDER_STATUS.ERROR:
case 'FAILED':
return '<span class="dot dot-red"></span>';
case 'CANCELLED':
case 'DELETED_IN_ROA':
case ORDER_STATUS.CANCELLED:
case ORDER_STATUS.DELETED_IN_ROA:
return '<span class="dot dot-gray"></span>';
default:
return '<span class="dot dot-gray"></span>';
@@ -1168,7 +1178,7 @@ function _renderHeaderInfo(order) {
}
// ERROR orders: muted dashes for ROA fields
if (order.status === 'ERROR' && !order.id_comanda) {
if (order.status === ORDER_STATUS.ERROR && !order.id_comanda) {
document.getElementById('detailIdComanda').innerHTML = '<span class="text-muted">\u2014</span>';
document.getElementById('detailIdPartener').innerHTML = '<span class="text-muted">\u2014</span>';
}