feat(berths): CM-2 — price-reconcile API (list + bulk apply)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 10:35:09 +02:00
parent 47778796ad
commit 9147f2857e
5 changed files with 110 additions and 1 deletions

View File

@@ -0,0 +1,36 @@
/**
* Route handler for `/api/v1/berths/price-reconcile/apply` (CM-2 Part A).
*
* Writes a rep-approved slice of parsed prices to the berths. In handlers.ts so
* integration tests can call it directly.
*/
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { type RouteHandler } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { errorResponse } from '@/lib/errors';
import { applyBulkBerthPrices } from '@/lib/services/berth-price-reconcile.service';
const bodySchema = z.object({
approvals: z
.array(
z.object({
berthId: z.string().min(1),
price: z.number().nonnegative(),
currency: z.string().min(1).max(8),
}),
)
.min(1),
});
export const postHandler: RouteHandler = async (req, ctx) => {
try {
const body = await parseBody(req, bodySchema);
const result = await applyBulkBerthPrices(ctx.portId, body.approvals, ctx.userId);
return NextResponse.json({ data: result });
} catch (error) {
return errorResponse(error);
}
};

View File

@@ -0,0 +1,5 @@
import { withAuth, withPermission } from '@/lib/api/helpers';
import { postHandler } from './handlers';
export const POST = withAuth(withPermission('berths', 'edit', postHandler));

View File

@@ -0,0 +1,21 @@
/**
* Route handlers for `/api/v1/berths/price-reconcile` (CM-2 Part A).
*
* In handlers.ts so integration tests can call them directly, bypassing the
* auth/permission middleware (per CLAUDE.md "Route handler exports").
*/
import { NextResponse } from 'next/server';
import { type RouteHandler } from '@/lib/api/helpers';
import { errorResponse } from '@/lib/errors';
import { listPriceReconciliation } from '@/lib/services/berth-price-reconcile.service';
export const getHandler: RouteHandler = async (_req, ctx) => {
try {
const data = await listPriceReconciliation(ctx.portId);
return NextResponse.json({ data });
} catch (error) {
return errorResponse(error);
}
};

View File

@@ -0,0 +1,5 @@
import { withAuth, withPermission } from '@/lib/api/helpers';
import { getHandler } from './handlers';
export const GET = withAuth(withPermission('berths', 'edit', getHandler));