feat(F-069): completed feature
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user