1+ update shipping_shipments s
2+ set status = ' needs_attention' ,
3+ lease_owner = null ,
4+ lease_expires_at = null ,
5+ next_attempt_at = null ,
6+ last_error_code = coalesce(s .last_error_code , ' ORDER_NOT_FULFILLABLE' ),
7+ last_error_message = coalesce(
8+ s .last_error_message ,
9+ ' Shipping pipeline closed by DB backfill: order is not shippable.'
10+ ),
11+ updated_at = now()
12+ from orders o
13+ where o .id = s .order_id
14+ and s .status in (' queued' , ' processing' )
15+ and (
16+ o .shipping_required is not true
17+ or o .payment_status <> ' paid'
18+ or o .status <> ' PAID'
19+ or o .inventory_status <> ' reserved'
20+ );
21+ -- > statement-breakpoint
22+
23+ update orders o
24+ set shipping_status = ' cancelled' ::shipping_status,
25+ updated_at = now()
26+ where (
27+ o .payment_status in (' failed' , ' refunded' )
28+ or o .status in (' CANCELED' , ' INVENTORY_FAILED' )
29+ )
30+ and o .shipping_status is not null
31+ and o .shipping_status not in (' cancelled' ::shipping_status, ' delivered' ::shipping_status);
32+ -- > statement-breakpoint
33+
34+ alter table " orders"
35+ add constraint " orders_terminal_shipping_status_chk"
36+ check (
37+ (
38+ " orders" ." payment_status" not in (' failed' , ' refunded' )
39+ and " orders" ." status" not in (' CANCELED' , ' INVENTORY_FAILED' )
40+ )
41+ or (
42+ " orders" ." shipping_status" is null
43+ or " orders" ." shipping_status" in (' cancelled' , ' delivered' )
44+ )
45+ )
46+ not valid;
47+ -- > statement-breakpoint
48+
49+ alter table " orders"
50+ validate constraint " orders_terminal_shipping_status_chk" ;
51+ -- > statement-breakpoint
52+
53+ create or replace function shop_orders_close_shipping_pipeline_guardrail ()
54+ returns trigger
55+ language plpgsql
56+ as $$
57+ declare
58+ was_shippable boolean := false;
59+ is_shippable boolean := false;
60+ is_terminal boolean := false;
61+ begin
62+ is_shippable := (
63+ new .shipping_required is true
64+ and new .payment_status = ' paid'
65+ and new .status = ' PAID'
66+ and new .inventory_status = ' reserved'
67+ );
68+
69+ if tg_op = ' UPDATE' then
70+ was_shippable := (
71+ old .shipping_required is true
72+ and old .payment_status = ' paid'
73+ and old .status = ' PAID'
74+ and old .inventory_status = ' reserved'
75+ );
76+ end if;
77+
78+ is_terminal := (
79+ new .payment_status in (' failed' , ' refunded' )
80+ or new .status in (' CANCELED' , ' INVENTORY_FAILED' )
81+ );
82+
83+ if is_terminal or (was_shippable and not is_shippable) then
84+ if new .shipping_status is not null
85+ and new .shipping_status not in (' cancelled' , ' delivered' )
86+ then
87+ new .shipping_status := ' cancelled' ::shipping_status;
88+ end if;
89+
90+ update shipping_shipments s
91+ set status = ' needs_attention' ,
92+ lease_owner = null ,
93+ lease_expires_at = null ,
94+ next_attempt_at = null ,
95+ last_error_code = coalesce(s .last_error_code , ' ORDER_NOT_FULFILLABLE' ),
96+ last_error_message = coalesce(
97+ s .last_error_message ,
98+ ' Shipping pipeline closed by DB guardrail: order became non-shippable.'
99+ ),
100+ updated_at = now()
101+ where s .order_id = new .id
102+ and s .status in (' queued' , ' processing' );
103+ end if;
104+
105+ return new;
106+ end;
107+ $$;
108+ -- > statement-breakpoint
109+
110+ drop trigger if exists trg_orders_close_shipping_pipeline_guardrail on orders;
111+ -- > statement-breakpoint
112+
113+ create trigger trg_orders_close_shipping_pipeline_guardrail
114+ before update of payment_status, status, inventory_status, shipping_required
115+ on orders
116+ for each row
117+ execute function shop_orders_close_shipping_pipeline_guardrail();
118+ -- > statement-breakpoint
119+
120+ create or replace function shop_shipping_shipments_require_shippable_order_guardrail ()
121+ returns trigger
122+ language plpgsql
123+ as $$
124+ begin
125+ if new .status in (' queued' , ' processing' ) then
126+ if not exists (
127+ select 1
128+ from orders o
129+ where o .id = new .order_id
130+ and o .shipping_required is true
131+ and o .payment_status = ' paid'
132+ and o .status = ' PAID'
133+ and o .inventory_status = ' reserved'
134+ ) then
135+ raise exception
136+ using errcode = ' 23514' ,
137+ constraint = ' shipping_shipments_shippable_order_chk' ,
138+ message = ' shipping_shipments queued/processing rows require a shippable paid order' ;
139+ end if;
140+ end if;
141+
142+ return new;
143+ end;
144+ $$;
145+ -- > statement-breakpoint
146+
147+ drop trigger if exists trg_shipping_shipments_require_shippable_order_guardrail on shipping_shipments;
148+ -- > statement-breakpoint
149+
150+ create trigger trg_shipping_shipments_require_shippable_order_guardrail
151+ before insert or update of status, order_id
152+ on shipping_shipments
153+ for each row
154+ execute function shop_shipping_shipments_require_shippable_order_guardrail();
0 commit comments