Files
ArchiTools/src/app/api/ancpi/orders/route.ts
T
Claude VM aa246c2d91 fix(epay-ui): show localitate + judet on intern extracts; hide cancelled rows
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>
2026-06-05 21:25:23 +03:00

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 });
}
}