feat(F-069): completed feature

This commit is contained in:
chattie
2026-08-19 18:09:23 +02:00
parent ba2ac939c7
commit ddcf2e0c28
14 changed files with 437 additions and 50 deletions

View File

@@ -28,6 +28,7 @@ const methodBodySchema = z.object({
name: z.string().min(1).max(120),
baseCostCents: z.number().int().min(0),
freeShippingThresholdCents: z.number().int().min(0).optional().nullable(),
description: z.string().max(500).optional().nullable(),
active: z.boolean().optional(),
});
@@ -74,13 +75,14 @@ export async function registerShippingRoutes(
requireRole(user, 'admin');
const input = parseJson(methodBodySchema, request.body);
const result = await deps.pool.query<{ id: string }>(
`INSERT INTO shipping_methods (zone_id, name, base_cost_cents, free_shipping_threshold_cents, active)
VALUES ($1, $2, $3, $4, $5) RETURNING id`,
`INSERT INTO shipping_methods (zone_id, name, base_cost_cents, free_shipping_threshold_cents, description, active)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`,
[
input.zoneId,
input.name,
input.baseCostCents,
input.freeShippingThresholdCents ?? null,
input.description ?? null,
input.active ?? true,
],
);
@@ -199,6 +201,7 @@ export async function registerShippingRoutes(
name: string;
base_cost_cents: number;
free_shipping_threshold_cents: number | null;
description: string | null;
active: boolean;
}>(
`SELECT sm.*, sz.name as zone_name FROM shipping_methods sm
@@ -213,6 +216,7 @@ export async function registerShippingRoutes(
name: r.name,
baseCostCents: r.base_cost_cents,
freeShippingThresholdCents: r.free_shipping_threshold_cents,
description: r.description,
active: r.active,
})),
});
@@ -259,6 +263,7 @@ export async function registerShippingRoutes(
name: z.string().min(1).max(120).optional(),
baseCostCents: z.number().int().min(0).optional(),
freeShippingThresholdCents: z.number().int().min(0).optional().nullable(),
description: z.string().max(500).optional().nullable(),
active: z.boolean().optional(),
}),
request.body,
@@ -278,6 +283,10 @@ export async function registerShippingRoutes(
sets.push(`free_shipping_threshold_cents = $${i++}`);
values.push(patch.freeShippingThresholdCents);
}
if (patch.description !== undefined) {
sets.push(`description = $${i++}`);
values.push(patch.description);
}
if (patch.active !== undefined) {
sets.push(`active = $${i++}`);
values.push(patch.active);
@@ -342,6 +351,53 @@ export async function registerShippingRoutes(
throw mapShippingError(error);
}
});
// ── Public list of active shipping methods ───────────────────────────────────────────────────
const publicMethodsSchema: FastifySchema = {
tags: ['Shipping'],
summary: 'List active shipping methods (public)',
querystring: {
type: 'object',
properties: {
country: { type: 'string', description: 'Filter by country (defaults to ES)' },
postalCode: { type: 'string', description: 'Filter by postal code prefix' },
},
},
response: { 200: { type: 'object' } },
};
app.get('/shipping/methods', { schema: publicMethodsSchema }, async (request, reply) => {
const query = request.query as { country?: string; postalCode?: string };
const country = query.country ?? 'ES';
const result = await deps.pool.query<{
id: string;
zone_id: string;
name: string;
base_cost_cents: number;
free_shipping_threshold_cents: number | null;
description: string | null;
}>(
`SELECT sm.id, sm.zone_id, sm.name, sm.base_cost_cents,
sm.free_shipping_threshold_cents, sm.description
FROM shipping_methods sm
JOIN shipping_zones sz ON sz.id = sm.zone_id
WHERE sm.active = true
AND sz.active = true
AND sz.country = $1
AND ($2::text IS NULL OR sz.postal_code_prefix IS NULL OR $2 LIKE sz.postal_code_prefix || '%')
ORDER BY sm.base_cost_cents ASC`,
[country, query.postalCode ?? null],
);
return reply.send({
items: result.rows.map((r) => ({
id: r.id,
zoneId: r.zone_id,
name: r.name,
baseCostCents: r.base_cost_cents,
freeShippingThresholdCents: r.free_shipping_threshold_cents,
description: r.description,
})),
});
});
}
function mapShippingError(error: unknown): Error {