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>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
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";
|
||||
@@ -57,6 +58,10 @@ export async function GET(req: Request) {
|
||||
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();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getAuthSession } from "@/core/auth/require-auth";
|
||||
import { gisApi, GisApiError } from "@/lib/gis-api-client";
|
||||
import { enrichCfLocations } from "@/modules/parcel-sync/services/cf-enrich-location";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -30,9 +31,14 @@ export async function GET(request: Request) {
|
||||
const offset = Number.isFinite(offsetRaw) && offsetRaw >= 0 ? offsetRaw : 0;
|
||||
|
||||
try {
|
||||
return NextResponse.json(
|
||||
await gisApi.enrichment.cf.list({ limit, offset, status }),
|
||||
);
|
||||
const data = await gisApi.enrichment.cf.list({ limit, offset, status });
|
||||
// gis-api returns uatName + siruta but judetName is null there — fill the
|
||||
// county (and any missing UAT name) from the local GisUat table so the UI
|
||||
// can show "localitate, jud. X" on intern rows too.
|
||||
if (Array.isArray(data?.rows)) {
|
||||
await enrichCfLocations(data.rows);
|
||||
}
|
||||
return NextResponse.json(data);
|
||||
} catch (err) {
|
||||
if (err instanceof GisApiError) {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -55,6 +55,10 @@ export interface CfExtractRow {
|
||||
userId: string;
|
||||
nrCadastral: string;
|
||||
nrCF?: string;
|
||||
type?: string;
|
||||
siruta?: string | null;
|
||||
uatName?: string | null;
|
||||
judetName?: string | null;
|
||||
status:
|
||||
| "pending"
|
||||
| "queued"
|
||||
|
||||
@@ -99,10 +99,10 @@ function adaptLegacyRow(row: LegacyCfExtract): CfExtractRecord {
|
||||
}
|
||||
|
||||
// Convert a gisApi CfExtractRow → the UI-side CfExtractRecord shape.
|
||||
// gis-api currently doesn't surface uatName/siruta/judetName on the list
|
||||
// endpoint, so we leave them blank; the row type defaults to "intern"
|
||||
// because gis_core's CfExtract is the cf-intern store (the cutover plan
|
||||
// hasn't yet moved ePay writes here).
|
||||
// gis-api returns siruta + uatName (judetName is null there, but the
|
||||
// /api/cf/orders proxy fills it from the local GisUat by SIRUTA — see
|
||||
// enrichCfLocations). The row type defaults to "intern" because gis's
|
||||
// CfExtract is primarily the cf-intern store.
|
||||
export function adaptCfRow(row: CfExtractRow & { type?: string }): CfExtractRecord {
|
||||
return {
|
||||
id: row.id,
|
||||
@@ -110,9 +110,9 @@ export function adaptCfRow(row: CfExtractRow & { type?: string }): CfExtractReco
|
||||
orderId: row.orderId ?? null,
|
||||
nrCadastral: row.nrCadastral,
|
||||
nrCF: row.nrCF ?? null,
|
||||
siruta: null,
|
||||
judetName: "",
|
||||
uatName: "",
|
||||
siruta: row.siruta ?? null,
|
||||
judetName: row.judetName ?? "",
|
||||
uatName: row.uatName ?? "",
|
||||
status: row.status,
|
||||
epayStatus: row.epayStatus ?? null,
|
||||
documentName: row.documentName ?? null,
|
||||
@@ -167,12 +167,18 @@ async function fetchGisAc(
|
||||
// single timeline shows ePay + intern history together. Sort newest-
|
||||
// first; dedupe by id (in case the same record ever lands in both
|
||||
// stores during the cutover migration).
|
||||
// Cancelled rows are dead (payment refused / cleaned-up bad orders) and just
|
||||
// clutter the list — hide them. failed/review stay (they're actionable).
|
||||
const isListable = (r: CfExtractRecord): boolean => r.status !== "cancelled";
|
||||
|
||||
export async function fetchCfOrdersList(
|
||||
useGisAc: boolean,
|
||||
params: { limit?: number; nrCadastral?: string; status?: string } = {},
|
||||
): Promise<{ orders: CfExtractRecord[]; total: number }> {
|
||||
if (!useGisAc) {
|
||||
return fetchLegacy(params);
|
||||
const r = await fetchLegacy(params);
|
||||
const orders = r.orders.filter(isListable);
|
||||
return { orders, total: orders.length };
|
||||
}
|
||||
|
||||
// Pull more rows from each side than the caller asked for so that the
|
||||
@@ -191,6 +197,7 @@ export async function fetchCfOrdersList(
|
||||
|
||||
const seen = new Set<string>();
|
||||
const dedup = merged.filter((r) => {
|
||||
if (!isListable(r)) return false;
|
||||
if (seen.has(r.id)) return false;
|
||||
seen.add(r.id);
|
||||
return true;
|
||||
@@ -198,14 +205,8 @@ export async function fetchCfOrdersList(
|
||||
|
||||
dedup.sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
|
||||
|
||||
const total =
|
||||
(legacy.status === "fulfilled" ? legacy.value.total : 0) +
|
||||
(gisac.status === "fulfilled" ? gisac.value.total : 0);
|
||||
|
||||
return {
|
||||
orders: params.limit ? dedup.slice(0, params.limit) : dedup,
|
||||
total,
|
||||
};
|
||||
const orders = params.limit ? dedup.slice(0, params.limit) : dedup;
|
||||
return { orders, total: orders.length };
|
||||
}
|
||||
|
||||
// Existence check used by the per-parcel order button. We check both
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Fill in uatName + judetName on CF extract rows from their SIRUTA.
|
||||
//
|
||||
// Intern (cf-intern) extracts — and ePay rows on the gis-api side — often
|
||||
// arrive without a judetName (it's null in gis_enrichment) and sometimes
|
||||
// without a uatName. Both are derivable from `siruta` via the local GisUat
|
||||
// table. This batches one query for the whole page instead of N lookups.
|
||||
|
||||
import { prisma } from "@/core/storage/prisma";
|
||||
|
||||
type LocatableRow = {
|
||||
siruta?: string | null;
|
||||
uatName?: string | null;
|
||||
judetName?: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates rows in place: for any row with a SIRUTA whose uatName/judetName is
|
||||
* blank, fill it from GisUat. Best-effort — a missing SIRUTA or a DB error
|
||||
* leaves the row unchanged. Returns the same array for convenience.
|
||||
*/
|
||||
export async function enrichCfLocations<T extends LocatableRow>(
|
||||
rows: T[],
|
||||
): Promise<T[]> {
|
||||
const sirutas = Array.from(
|
||||
new Set(
|
||||
rows
|
||||
.map((r) => (r.siruta ? String(r.siruta).trim() : ""))
|
||||
.filter(Boolean),
|
||||
),
|
||||
);
|
||||
if (sirutas.length === 0) return rows;
|
||||
|
||||
try {
|
||||
const uats = await prisma.gisUat.findMany({
|
||||
where: { siruta: { in: sirutas } },
|
||||
select: { siruta: true, name: true, county: true },
|
||||
});
|
||||
const bySiruta = new Map(uats.map((u) => [u.siruta, u]));
|
||||
|
||||
for (const row of rows) {
|
||||
const s = row.siruta ? String(row.siruta).trim() : "";
|
||||
if (!s) continue;
|
||||
const uat = bySiruta.get(s);
|
||||
if (!uat) continue;
|
||||
if (!row.uatName && uat.name) row.uatName = uat.name;
|
||||
if (!row.judetName && uat.county) row.judetName = uat.county;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[cf-enrich-location] lookup failed:", error);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
Reference in New Issue
Block a user