fix(release): 0.2.8 harden admin proxy, cart stock caps, selfpay and refund timeline
- admin proxy: 25s timeout, body guards, structured failure logs - cart: addItem enforces stock cap (409 INSUFFICIENT_STOCK), UI clamps qty - tpv selfpay: hide sidebar/discounts/save-pending, rename button, receipt-settings 400 fix - pos admin: quick products slot count aligned to 8 - returns: human-readable history message + metadata jsonb (migrations 064-065) + admin fallback formatter - storefront: product card white background - product page: remove duplicate stock label under add-to-cart button
This commit is contained in:
20
project/migrations/064_orders_history_metadata.js
Normal file
20
project/migrations/064_orders_history_metadata.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* F-138: orders_order_history.metadata holds the structured payload for
|
||||
* machine-readable events (returns, refunds, etc.) so we can keep the
|
||||
* human-visible `message` column short and friendly while preserving all the
|
||||
* detail that internal tooling needs.
|
||||
* @param {import('node-pg-migrate').MigrationBuilder} pgm
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE orders_order_history
|
||||
ADD COLUMN IF NOT EXISTS metadata jsonb NULL
|
||||
`);
|
||||
};
|
||||
|
||||
export const down = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE orders_order_history
|
||||
DROP COLUMN IF EXISTS metadata
|
||||
`);
|
||||
};
|
||||
65
project/migrations/065_backfill_return_history_messages.js
Normal file
65
project/migrations/065_backfill_return_history_messages.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* F-138 backfill: convert any legacy `orders_order_history` refund entries that
|
||||
* were stored as raw JSON `message` strings into the new human-readable format
|
||||
* introduced by `apply-pos-return.ts`. We also move the original payload into
|
||||
* the new `metadata` column so internal tooling still has the structured data.
|
||||
* @param {import('node-pg-migrate').MigrationBuilder} pgm
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
pgm.sql(`
|
||||
WITH parsed AS (
|
||||
SELECT id,
|
||||
(message::jsonb) AS payload,
|
||||
(
|
||||
CASE (message::jsonb->>'status')
|
||||
WHEN 'refund' THEN 'Devolución total'
|
||||
ELSE 'Devolución parcial'
|
||||
END
|
||||
) AS action
|
||||
FROM orders_order_history
|
||||
WHERE event_type = 'RETURNED'
|
||||
AND message ~ '^\\s*\\{'
|
||||
),
|
||||
formatted AS (
|
||||
SELECT id,
|
||||
action || ' de ' ||
|
||||
(round(((payload->>'refundedCents')::numeric) / 100)::numeric(12,2)::text) ||
|
||||
' €' ||
|
||||
COALESCE(
|
||||
(SELECT ' — ' || string_agg(
|
||||
((l->>'returnedQuantity')::int)::text || ' × ' ||
|
||||
COALESCE(oi.name, 'Artículo'),
|
||||
', '
|
||||
ORDER BY ord)
|
||||
FROM jsonb_array_elements(payload->'lines') WITH ORDINALITY AS x(l, ord)
|
||||
LEFT JOIN orders_items oi ON oi.id = (l->>'orderItemId')::uuid
|
||||
WHERE (l->>'returnedQuantity')::int > 0
|
||||
),
|
||||
''
|
||||
) ||
|
||||
CASE
|
||||
WHEN NULLIF(payload->>'reason', '') IS NOT NULL
|
||||
THEN ' (motivo: ' || (payload->>'reason') || ')'
|
||||
ELSE ''
|
||||
END AS readable
|
||||
FROM parsed
|
||||
)
|
||||
UPDATE orders_order_history h
|
||||
SET message = f.readable,
|
||||
metadata = h.message::jsonb
|
||||
FROM formatted f
|
||||
WHERE h.id = f.id
|
||||
`);
|
||||
};
|
||||
|
||||
export const down = (pgm) => {
|
||||
// Best-effort: restore the JSON message from metadata. We can't know whether
|
||||
// a row was originally a JSON message or already human-readable, so the
|
||||
// down migration just clears the column.
|
||||
pgm.sql(`
|
||||
UPDATE orders_order_history
|
||||
SET message = metadata::text,
|
||||
metadata = NULL
|
||||
WHERE event_type = 'RETURNED' AND metadata IS NOT NULL
|
||||
`);
|
||||
};
|
||||
Reference in New Issue
Block a user