feat(03-02): Collector service, remittance service, report service, APIs, and tests
- collector-service.ts: recordCollection (FIFO, zone enforcement, DR 1030/CR 1100 JE), voidCollection (reversing JE), getCollectionHistory - remittance-service.ts: createRemittance, verifyRemittance (DR 1010/CR 1030, variance non-blocking), listRemittances - collection-report-service.ts: getDailyCollectionSummary, getCollectorCollectionDetail - 6 API routes: POST/GET /collections, GET /collections/[id], POST /collections/[id]/void, POST/GET /remittances, POST /remittances/[id]/verify, GET /reports/collections - 26 integration tests: 13 collector (FIFO, zone enforcement, JE verification, void, cross-tenant) + 13 remittance (variance, JE accounts, double-verify rejection) - All 26 tests pass
This commit is contained in:
49
src/app/api/remittances/[id]/verify/route.ts
Normal file
49
src/app/api/remittances/[id]/verify/route.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* POST /api/remittances/[id]/verify — Verify a remittance (office staff counts total)
|
||||
*/
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { withPermission } from "@/lib/middleware/authorize";
|
||||
import { withTenantContext } from "@/lib/prisma-tenant";
|
||||
import { verifyRemittance } from "@/lib/services/remittance-service";
|
||||
|
||||
export function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
return withPermission("update", "Subscriber")(
|
||||
async (innerReq: NextRequest, { user }) => {
|
||||
if (!user.tenantId) {
|
||||
return NextResponse.json(
|
||||
{ error: "No tenant context — super-admins must use the admin API" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
let body: unknown;
|
||||
try {
|
||||
body = await innerReq.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
||||
}
|
||||
|
||||
const { verifiedTotal, notes } = body as Record<string, unknown>;
|
||||
|
||||
if (verifiedTotal === undefined || verifiedTotal === null) {
|
||||
return NextResponse.json({ error: "verifiedTotal is required" }, { status: 400 });
|
||||
}
|
||||
|
||||
const tenantPrisma = withTenantContext(user.tenantId);
|
||||
|
||||
try {
|
||||
const result = await verifyRemittance(tenantPrisma, user.tenantId, id, {
|
||||
verifiedById: user.id,
|
||||
verifiedTotal: verifiedTotal as string | number,
|
||||
notes: notes as string | undefined,
|
||||
});
|
||||
return NextResponse.json(result);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Failed to verify remittance";
|
||||
return NextResponse.json({ error: message }, { status: 400 });
|
||||
}
|
||||
}
|
||||
)(req);
|
||||
}
|
||||
Reference in New Issue
Block a user