fix(parcel-sync): always run syncLayer for delta detection + no-geom freshness
- Always call syncLayer for TERENURI/CLADIRI (not gated by isFresh) so that quick-count + VALID_FROM delta actually run on daily syncs - syncLayer handles efficiency internally via quick-count match - Add 48h freshness check for no-geom import (skip if recent) - Admin layers: skip if synced within 24h - Log sync summary (new features, updated features) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -187,42 +187,38 @@ async function runBackground(params: {
|
|||||||
getLayerFreshness(siruta, "CLADIRI_ACTIVE"),
|
getLayerFreshness(siruta, "CLADIRI_ACTIVE"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const terenuriNeedsSync =
|
const terenuriNeedsFullSync =
|
||||||
forceSync ||
|
forceSync || terenuriStatus.featureCount === 0;
|
||||||
!isFresh(terenuriStatus.lastSynced) ||
|
const cladiriNeedsFullSync =
|
||||||
terenuriStatus.featureCount === 0;
|
forceSync || cladiriStatus.featureCount === 0;
|
||||||
const cladiriNeedsSync =
|
|
||||||
forceSync ||
|
|
||||||
!isFresh(cladiriStatus.lastSynced) ||
|
|
||||||
cladiriStatus.featureCount === 0;
|
|
||||||
|
|
||||||
if (terenuriNeedsSync) {
|
// Always call syncLayer — it handles quick-count + VALID_FROM delta internally.
|
||||||
phase = "Sincronizare terenuri";
|
// Only force full download when no local data or explicit forceSync.
|
||||||
push({});
|
phase = "Sincronizare terenuri";
|
||||||
const r = await syncLayer(username, password, siruta, "TERENURI_ACTIVE", {
|
push({});
|
||||||
forceFullSync: forceSync,
|
const terenuriResult = await syncLayer(username, password, siruta, "TERENURI_ACTIVE", {
|
||||||
jobId,
|
forceFullSync: terenuriNeedsFullSync,
|
||||||
isSubStep: true,
|
jobId,
|
||||||
});
|
isSubStep: true,
|
||||||
if (r.status === "error")
|
});
|
||||||
throw new Error(r.error ?? "Sync terenuri failed");
|
if (terenuriResult.status === "error")
|
||||||
}
|
throw new Error(terenuriResult.error ?? "Sync terenuri failed");
|
||||||
updateOverall(0.5);
|
updateOverall(0.5);
|
||||||
|
|
||||||
if (cladiriNeedsSync) {
|
phase = "Sincronizare clădiri";
|
||||||
phase = "Sincronizare clădiri";
|
push({});
|
||||||
push({});
|
const cladiriResult = await syncLayer(username, password, siruta, "CLADIRI_ACTIVE", {
|
||||||
const r = await syncLayer(username, password, siruta, "CLADIRI_ACTIVE", {
|
forceFullSync: cladiriNeedsFullSync,
|
||||||
forceFullSync: forceSync,
|
jobId,
|
||||||
jobId,
|
isSubStep: true,
|
||||||
isSubStep: true,
|
});
|
||||||
});
|
if (cladiriResult.status === "error")
|
||||||
if (r.status === "error")
|
throw new Error(cladiriResult.error ?? "Sync clădiri failed");
|
||||||
throw new Error(r.error ?? "Sync clădiri failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync admin layers (always, lightweight)
|
// Sync admin layers — skip if synced within 24h
|
||||||
for (const adminLayer of ["LIMITE_INTRAV_DYNAMIC", "LIMITE_UAT"]) {
|
for (const adminLayer of ["LIMITE_INTRAV_DYNAMIC", "LIMITE_UAT"]) {
|
||||||
|
const adminStatus = await getLayerFreshness(siruta, adminLayer);
|
||||||
|
if (!forceSync && isFresh(adminStatus.lastSynced, 24)) continue;
|
||||||
phase = `Sincronizare ${adminLayer === "LIMITE_UAT" ? "limite UAT" : "limite intravilan"}`;
|
phase = `Sincronizare ${adminLayer === "LIMITE_UAT" ? "limite UAT" : "limite intravilan"}`;
|
||||||
push({});
|
push({});
|
||||||
try {
|
try {
|
||||||
@@ -232,37 +228,65 @@ async function runBackground(params: {
|
|||||||
isSubStep: true,
|
isSubStep: true,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// Non-critical — don't fail the whole job
|
|
||||||
note = `Avertisment: ${adminLayer} nu s-a sincronizat`;
|
note = `Avertisment: ${adminLayer} nu s-a sincronizat`;
|
||||||
push({});
|
push({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!terenuriNeedsSync && !cladiriNeedsSync) {
|
const syncSummary = [
|
||||||
note = "Date proaspete — sync skip";
|
terenuriResult.newFeatures > 0 ? `${terenuriResult.newFeatures} terenuri noi` : null,
|
||||||
}
|
terenuriResult.validFromUpdated ? `${terenuriResult.validFromUpdated} terenuri actualizate` : null,
|
||||||
|
cladiriResult.newFeatures > 0 ? `${cladiriResult.newFeatures} cladiri noi` : null,
|
||||||
|
cladiriResult.validFromUpdated ? `${cladiriResult.validFromUpdated} cladiri actualizate` : null,
|
||||||
|
].filter(Boolean);
|
||||||
|
note = syncSummary.length > 0 ? syncSummary.join(", ") : "Fără schimbări";
|
||||||
finishPhase();
|
finishPhase();
|
||||||
|
|
||||||
/* ── Phase 2: No-geometry import (optional) ──────── */
|
/* ── Phase 2: No-geometry import (optional) ──────── */
|
||||||
if (hasNoGeom && weights.noGeom > 0) {
|
if (hasNoGeom && weights.noGeom > 0) {
|
||||||
setPhase("Import parcele fără geometrie", weights.noGeom);
|
setPhase("Verificare parcele fără geometrie", weights.noGeom);
|
||||||
const noGeomClient = await EterraClient.create(username, password, {
|
// Skip no-geom import if recently done (within 48h) and not forced
|
||||||
timeoutMs: 120_000,
|
const { PrismaClient } = await import("@prisma/client");
|
||||||
});
|
const _prisma = new PrismaClient();
|
||||||
const res = await syncNoGeometryParcels(noGeomClient, siruta, {
|
let skipNoGeom = false;
|
||||||
onProgress: (done, tot, ph) => {
|
try {
|
||||||
phase = ph;
|
const recentNoGeom = await _prisma.gisFeature.findFirst({
|
||||||
push({});
|
where: {
|
||||||
},
|
layerId: "TERENURI_ACTIVE",
|
||||||
});
|
siruta,
|
||||||
if (res.status === "error") {
|
geometrySource: "NO_GEOMETRY",
|
||||||
note = `Avertisment no-geom: ${res.error}`;
|
updatedAt: { gte: new Date(Date.now() - 48 * 60 * 60 * 1000) },
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
skipNoGeom = !forceSync && recentNoGeom != null;
|
||||||
|
} catch { /* proceed with import */ }
|
||||||
|
await _prisma.$disconnect();
|
||||||
|
|
||||||
|
if (skipNoGeom) {
|
||||||
|
note = "Parcele fără geometrie — actualizate recent, skip";
|
||||||
push({});
|
push({});
|
||||||
} else {
|
} else {
|
||||||
const cleanNote =
|
phase = "Import parcele fără geometrie";
|
||||||
res.cleaned > 0 ? `, ${res.cleaned} vechi șterse` : "";
|
|
||||||
note = `${res.imported} parcele noi importate${cleanNote}`;
|
|
||||||
push({});
|
push({});
|
||||||
|
const noGeomClient = await EterraClient.create(username, password, {
|
||||||
|
timeoutMs: 120_000,
|
||||||
|
});
|
||||||
|
const res = await syncNoGeometryParcels(noGeomClient, siruta, {
|
||||||
|
onProgress: (done, tot, ph) => {
|
||||||
|
phase = ph;
|
||||||
|
push({});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (res.status === "error") {
|
||||||
|
note = `Avertisment no-geom: ${res.error}`;
|
||||||
|
push({});
|
||||||
|
} else {
|
||||||
|
const cleanNote =
|
||||||
|
res.cleaned > 0 ? `, ${res.cleaned} vechi șterse` : "";
|
||||||
|
note = `${res.imported} parcele noi importate${cleanNote}`;
|
||||||
|
push({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finishPhase();
|
finishPhase();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user