fix(dashboard): update sync card after completion + use Bucharest timezone

Sync card was showing previous run data after sync completed because the
last_run query excluded the current run_id even after it finished. Now only
excludes during active running state.

All datetime.now() and SQLite datetime('now') replaced with Europe/Bucharest
timezone to fix times displayed 2 hours behind (was using UTC).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-03-17 13:02:18 +00:00
parent f5ef9e0811
commit f74322beab
3 changed files with 34 additions and 16 deletions

View File

@@ -68,12 +68,14 @@ async def sync_status():
# Build last_run from most recent completed/failed sync_runs row # Build last_run from most recent completed/failed sync_runs row
current_run_id = status.get("run_id") current_run_id = status.get("run_id")
is_running = status.get("status") == "running"
last_run = None last_run = None
try: try:
from ..database import get_sqlite from ..database import get_sqlite
db = await get_sqlite() db = await get_sqlite()
try: try:
if current_run_id: if current_run_id and is_running:
# Only exclude current run while it's actively running
cursor = await db.execute(""" cursor = await db.execute("""
SELECT * FROM sync_runs SELECT * FROM sync_runs
WHERE status IN ('completed', 'failed') AND run_id != ? WHERE status IN ('completed', 'failed') AND run_id != ?

View File

@@ -1,8 +1,16 @@
import json import json
import logging import logging
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo
from ..database import get_sqlite, get_sqlite_sync from ..database import get_sqlite, get_sqlite_sync
_tz_bucharest = ZoneInfo("Europe/Bucharest")
def _now_str():
"""Return current Bucharest time as ISO string."""
return datetime.now(_tz_bucharest).replace(tzinfo=None).isoformat()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -12,8 +20,8 @@ async def create_sync_run(run_id: str, json_files: int = 0):
try: try:
await db.execute(""" await db.execute("""
INSERT INTO sync_runs (run_id, started_at, status, json_files) INSERT INTO sync_runs (run_id, started_at, status, json_files)
VALUES (?, datetime('now'), 'running', ?) VALUES (?, ?, 'running', ?)
""", (run_id, json_files)) """, (run_id, _now_str(), json_files))
await db.commit() await db.commit()
finally: finally:
await db.close() await db.close()
@@ -28,7 +36,7 @@ async def update_sync_run(run_id: str, status: str, total_orders: int = 0,
try: try:
await db.execute(""" await db.execute("""
UPDATE sync_runs SET UPDATE sync_runs SET
finished_at = datetime('now'), finished_at = ?,
status = ?, status = ?,
total_orders = ?, total_orders = ?,
imported = ?, imported = ?,
@@ -38,7 +46,7 @@ async def update_sync_run(run_id: str, status: str, total_orders: int = 0,
already_imported = ?, already_imported = ?,
new_imported = ? new_imported = ?
WHERE run_id = ? WHERE run_id = ?
""", (status, total_orders, imported, skipped, errors, error_message, """, (_now_str(), status, total_orders, imported, skipped, errors, error_message,
already_imported, new_imported, run_id)) already_imported, new_imported, run_id))
await db.commit() await db.commit()
finally: finally:

View File

@@ -3,6 +3,14 @@ import json
import logging import logging
import uuid import uuid
from datetime import datetime, timedelta from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
_tz_bucharest = ZoneInfo("Europe/Bucharest")
def _now():
"""Return current time in Bucharest timezone (naive, for display/storage)."""
return datetime.now(_tz_bucharest).replace(tzinfo=None)
from . import order_reader, validation_service, import_service, sqlite_service, invoice_service, gomag_client from . import order_reader, validation_service, import_service, sqlite_service, invoice_service, gomag_client
from ..config import settings from ..config import settings
@@ -22,7 +30,7 @@ def _log_line(run_id: str, message: str):
"""Append a timestamped line to the in-memory log buffer.""" """Append a timestamped line to the in-memory log buffer."""
if run_id not in _run_logs: if run_id not in _run_logs:
_run_logs[run_id] = [] _run_logs[run_id] = []
ts = datetime.now().strftime("%H:%M:%S") ts = _now().strftime("%H:%M:%S")
_run_logs[run_id].append(f"[{ts}] {message}") _run_logs[run_id].append(f"[{ts}] {message}")
@@ -62,11 +70,11 @@ async def prepare_sync(id_pol: int = None, id_sectie: int = None) -> dict:
if _sync_lock.locked(): if _sync_lock.locked():
return {"error": "Sync already running", "run_id": _current_sync.get("run_id") if _current_sync else None} return {"error": "Sync already running", "run_id": _current_sync.get("run_id") if _current_sync else None}
run_id = datetime.now().strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6] run_id = _now().strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6]
_current_sync = { _current_sync = {
"run_id": run_id, "run_id": run_id,
"status": "running", "status": "running",
"started_at": datetime.now().isoformat(), "started_at": _now().isoformat(),
"finished_at": None, "finished_at": None,
"phase": "starting", "phase": "starting",
"phase_text": "Starting...", "phase_text": "Starting...",
@@ -142,11 +150,11 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
async with _sync_lock: async with _sync_lock:
# Use provided run_id or generate one # Use provided run_id or generate one
if not run_id: if not run_id:
run_id = datetime.now().strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6] run_id = _now().strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6]
_current_sync = { _current_sync = {
"run_id": run_id, "run_id": run_id,
"status": "running", "status": "running",
"started_at": datetime.now().isoformat(), "started_at": _now().isoformat(),
"finished_at": None, "finished_at": None,
"phase": "reading", "phase": "reading",
"phase_text": "Reading JSON files...", "phase_text": "Reading JSON files...",
@@ -157,7 +165,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
_update_progress("reading", "Reading JSON files...") _update_progress("reading", "Reading JSON files...")
started_dt = datetime.now() started_dt = _now()
_run_logs[run_id] = [ _run_logs[run_id] = [
f"=== Sync Run {run_id} ===", f"=== Sync Run {run_id} ===",
f"Inceput: {started_dt.strftime('%d.%m.%Y %H:%M:%S')}", f"Inceput: {started_dt.strftime('%d.%m.%Y %H:%M:%S')}",
@@ -322,9 +330,9 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
try: try:
min_date = datetime.strptime(min_date_str[:10], "%Y-%m-%d") - timedelta(days=1) min_date = datetime.strptime(min_date_str[:10], "%Y-%m-%d") - timedelta(days=1)
except (ValueError, TypeError): except (ValueError, TypeError):
min_date = datetime.now() - timedelta(days=90) min_date = _now() - timedelta(days=90)
else: else:
min_date = datetime.now() - timedelta(days=90) min_date = _now() - timedelta(days=90)
existing_map = await asyncio.to_thread( existing_map = await asyncio.to_thread(
validation_service.check_orders_in_roa, min_date, conn validation_service.check_orders_in_roa, min_date, conn
@@ -673,14 +681,14 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
"already_imported": already_imported_count, "cancelled": cancelled_count}) "already_imported": already_imported_count, "cancelled": cancelled_count})
if _current_sync: if _current_sync:
_current_sync["status"] = status _current_sync["status"] = status
_current_sync["finished_at"] = datetime.now().isoformat() _current_sync["finished_at"] = _now().isoformat()
logger.info( logger.info(
f"Sync {run_id} completed: {imported_count} new, {already_imported_count} already imported, " f"Sync {run_id} completed: {imported_count} new, {already_imported_count} already imported, "
f"{len(skipped)} skipped, {error_count} errors, {cancelled_count} cancelled" f"{len(skipped)} skipped, {error_count} errors, {cancelled_count} cancelled"
) )
duration = (datetime.now() - started_dt).total_seconds() duration = (_now() - started_dt).total_seconds()
_log_line(run_id, "") _log_line(run_id, "")
cancelled_text = f", {cancelled_count} anulate" if cancelled_count else "" cancelled_text = f", {cancelled_count} anulate" if cancelled_count else ""
_run_logs[run_id].append( _run_logs[run_id].append(
@@ -696,7 +704,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
await sqlite_service.update_sync_run(run_id, "failed", 0, 0, 0, 1, error_message=str(e)) await sqlite_service.update_sync_run(run_id, "failed", 0, 0, 0, 1, error_message=str(e))
if _current_sync: if _current_sync:
_current_sync["status"] = "failed" _current_sync["status"] = "failed"
_current_sync["finished_at"] = datetime.now().isoformat() _current_sync["finished_at"] = _now().isoformat()
_current_sync["error"] = str(e) _current_sync["error"] = str(e)
return {"run_id": run_id, "status": "failed", "error": str(e)} return {"run_id": run_id, "status": "failed", "error": str(e)}
finally: finally: