aa246c2d91
The intern (and gis-api-sourced) rows showed an empty "jud." with no UAT name or county, and a few dead cancelled/test rows cluttered the list. - gis-api returns siruta + uatName but judetName is null there, and the CfExtractRow type didn't even declare those fields so adaptCfRow blanked them. Added the fields to the type; adaptCfRow now surfaces uatName + siruta. - New enrichCfLocations(rows) fills missing uatName/judetName from SIRUTA via the local GisUat table (batched, one query). Applied in both list proxies (/api/cf/orders for gis rows, /api/ancpi/orders for old legacy intern rows whose judetName was stored empty). So intern rows now read "LOCALITATE, jud. X". - Hide status='cancelled' rows from the Extrase CF list (dead — payment refused / cleaned-up bad orders, e.g. the old 354686 test). failed/review stay (actionable via Reincearca). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
150 lines
5.2 KiB
TypeScript
150 lines
5.2 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { prisma } from "@/core/storage/prisma";
|
|
import { requireCfAccess } from "@/core/auth/cf-access";
|
|
import { enrichCfLocations } from "@/modules/parcel-sync/services/cf-enrich-location";
|
|
|
|
export const runtime = "nodejs";
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/**
|
|
* GET /api/ancpi/orders — list all CF extract orders.
|
|
*
|
|
* Query params:
|
|
* ?nrCadastral=123 — single cadastral number
|
|
* ?nrCadastral=123,456 — comma-separated for batch status check
|
|
* ?status=completed — filter by status
|
|
* ?limit=50&offset=0 — pagination
|
|
*
|
|
* When nrCadastral contains commas, returns an extra `statusMap` field:
|
|
* { orders, total, statusMap: { "123": "valid", "456": "expired", "789": "none" } }
|
|
* - "valid" = completed + expiresAt > now
|
|
* - "expired" = completed + expiresAt <= now
|
|
* - "none" = no completed record
|
|
*/
|
|
export async function GET(req: Request) {
|
|
try {
|
|
const access = await requireCfAccess();
|
|
if (!access.ok) {
|
|
return NextResponse.json({ error: access.error }, { status: access.status });
|
|
}
|
|
|
|
const url = new URL(req.url);
|
|
const nrCadastralParam = url.searchParams.get("nrCadastral") || undefined;
|
|
const status = url.searchParams.get("status") || undefined;
|
|
const limit = Math.min(parseInt(url.searchParams.get("limit") ?? "50"), 200);
|
|
const offset = parseInt(url.searchParams.get("offset") ?? "0");
|
|
|
|
// Check if multi-cadastral query
|
|
const cadastralNumbers = nrCadastralParam
|
|
? nrCadastralParam.split(",").map((s) => s.trim()).filter(Boolean)
|
|
: [];
|
|
const isMulti = cadastralNumbers.length > 1;
|
|
|
|
const where: Record<string, unknown> = {};
|
|
if (cadastralNumbers.length === 1) {
|
|
where.nrCadastral = cadastralNumbers[0];
|
|
} else if (isMulti) {
|
|
where.nrCadastral = { in: cadastralNumbers };
|
|
}
|
|
if (status) where.status = status;
|
|
|
|
const [orders, total] = await Promise.all([
|
|
prisma.cfExtract.findMany({
|
|
where,
|
|
orderBy: { createdAt: "desc" },
|
|
take: limit,
|
|
skip: offset,
|
|
}),
|
|
prisma.cfExtract.count({ where }),
|
|
]);
|
|
|
|
// Fill missing uatName/judetName from SIRUTA (old intern rows stored an
|
|
// empty judetName) so the list shows localitate + judet for them too.
|
|
await enrichCfLocations(orders);
|
|
|
|
// Build statusMap for multi-cadastral queries (or single if requested)
|
|
if (cadastralNumbers.length > 0) {
|
|
const now = new Date();
|
|
// For status map, we need completed records for each cadastral number
|
|
const completedRecords = await prisma.cfExtract.findMany({
|
|
where: {
|
|
nrCadastral: { in: cadastralNumbers },
|
|
status: "completed",
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
select: {
|
|
id: true,
|
|
nrCadastral: true,
|
|
expiresAt: true,
|
|
completedAt: true,
|
|
minioPath: true,
|
|
},
|
|
});
|
|
|
|
const statusMap: Record<string, string> = {};
|
|
const latestById: Record<string, typeof completedRecords[number]> = {};
|
|
|
|
// Find latest completed record per cadastral number
|
|
for (const rec of completedRecords) {
|
|
const existing = latestById[rec.nrCadastral];
|
|
if (!existing) {
|
|
latestById[rec.nrCadastral] = rec;
|
|
}
|
|
}
|
|
|
|
for (const nr of cadastralNumbers) {
|
|
const rec = latestById[nr];
|
|
if (!rec) {
|
|
statusMap[nr] = "none";
|
|
} else if (rec.expiresAt && rec.expiresAt <= now) {
|
|
statusMap[nr] = "expired";
|
|
} else {
|
|
statusMap[nr] = "valid";
|
|
}
|
|
}
|
|
|
|
// QW6: surface terminal failure/review so the UI can flag them (a
|
|
// cadastral whose latest record failed used to show as "none"). Only
|
|
// applies where there's no valid extract — a fresh valid one wins.
|
|
const attentionRecords = await prisma.cfExtract.findMany({
|
|
where: {
|
|
nrCadastral: { in: cadastralNumbers },
|
|
status: { in: ["failed", "review"] },
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
select: { nrCadastral: true, status: true },
|
|
});
|
|
for (const rec of attentionRecords) {
|
|
if (statusMap[rec.nrCadastral] === "none") {
|
|
statusMap[rec.nrCadastral] = rec.status; // "failed" | "review"
|
|
}
|
|
}
|
|
|
|
// Active (in-progress) orders take priority over none/failed/review/
|
|
// expired — an in-flight re-order should read as "processing".
|
|
const activeRecords = await prisma.cfExtract.findMany({
|
|
where: {
|
|
nrCadastral: { in: cadastralNumbers },
|
|
status: {
|
|
in: ["pending", "queued", "cart", "searching", "ordering", "polling", "downloading"],
|
|
},
|
|
},
|
|
select: { nrCadastral: true },
|
|
});
|
|
|
|
for (const rec of activeRecords) {
|
|
if (statusMap[rec.nrCadastral] !== "valid") {
|
|
statusMap[rec.nrCadastral] = "processing";
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ orders, total, statusMap, latestById });
|
|
}
|
|
|
|
return NextResponse.json({ orders, total });
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "Eroare server";
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|