<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: life</title>
    <description>The latest articles on DEV Community by life (@fulfillnexa).</description>
    <link>https://dev.to/fulfillnexa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4131202%2F67222f3b-ed90-43ff-a7d2-f8cdbbb06320.png</url>
      <title>DEV Community: life</title>
      <link>https://dev.to/fulfillnexa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fulfillnexa"/>
    <language>en</language>
    <item>
      <title>A Damage Claim Is a Table With Evidence Columns and a Clock</title>
      <dc:creator>life</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:00:38 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-damage-claim-is-a-table-with-evidence-columns-and-a-clock-523p</link>
      <guid>https://dev.to/fulfillnexa/a-damage-claim-is-a-table-with-evidence-columns-and-a-clock-523p</guid>
      <description>&lt;p&gt;A claim does not fail because the product broke. It fails because the record of what was packed was never stored anywhere retrievable, and by the time anyone wants it, the only remaining source is the memory of whoever was on that bench eleven days ago.&lt;/p&gt;

&lt;p&gt;So the feature is a table with the right columns, written at the moment the carton closes, plus a clock attached to it. Not a nicer support workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has to be captured at pack-out
&lt;/h2&gt;

&lt;p&gt;The scan that seals a carton is already producing most of this. It is usually thrown away.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;pack_record&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;carton_id&lt;/span&gt;        &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_ref&lt;/span&gt;        &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;station_id&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;operator_id&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;packed_at&lt;/span&gt;        &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;scale_weight_g&lt;/span&gt;   &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;label_id&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;label_voided_at&lt;/span&gt;  &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;manifest_scan_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dunnage_profile&lt;/span&gt;  &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- resolved from the product record, not typed&lt;/span&gt;
  &lt;span class="n"&gt;building&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;pack_item&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;carton_id&lt;/span&gt;   &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;pack_record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;sku&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;qty&lt;/span&gt;         &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;unit_scan&lt;/span&gt;   &lt;span class="nb"&gt;boolean&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carton_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The column people skip is &lt;code&gt;dunnage_profile&lt;/code&gt;. Packaging choice is a property of the product, and if it is not recorded per carton then a claim cannot distinguish "this bottle was packed wrong" from "this bottle was packed to spec and still broke", which is exactly the distinction the carrier and the supplier both want.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;unit_scan&lt;/code&gt; matters for a different reason. A carton packed by weight alone can contain the wrong unit and still be correct on every other field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The claim row, and the clock that lives on it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;damage_claim&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;carton_id&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;pack_record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opened_at&lt;/span&gt;      &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;filed_by&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;-- shipper_of_record | seller | partner&lt;/span&gt;
  &lt;span class="n"&gt;evidence&lt;/span&gt;       &lt;span class="n"&gt;jsonb&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s1"&gt;'[]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;disposition&lt;/span&gt;    &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;written_off_qty&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;deadline_at&lt;/span&gt;    &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;-- carrier window, from the dated rule row&lt;/span&gt;
  &lt;span class="n"&gt;closed_at&lt;/span&gt;      &lt;span class="n"&gt;timestamptz&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two design notes that decide whether this works.&lt;/p&gt;

&lt;p&gt;The first is &lt;code&gt;filed_by&lt;/code&gt;. The party with standing is normally whoever is named on the label, and in a third-party setup that is a real question with a real answer that has to be written down before the parcel moves. Left blank, both sides wait for the other to file and the claim never exists. This column is the difference between a process and a rumor.&lt;/p&gt;

&lt;p&gt;The second is &lt;code&gt;deadline_at&lt;/code&gt;, and where its value comes from. Carrier claim windows and default liability ceilings are published terms that change, and declared value coverage is bought when the label is purchased rather than retrofitted afterwards. Store the deadline as data with a source date, in the same dated-rule-table shape you would use for any other external constraint, and never compute it from a constant in code. A hard-coded window is a claim that quietly expires six months before anybody notices the terms moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that outlives the claim
&lt;/h2&gt;

&lt;p&gt;Here is the part that keeps costing money after the refund is issued. A damaged unit that is refunded and never dispositioned is still sitting in your available quantity.&lt;/p&gt;

&lt;p&gt;The write-off has to be a transaction, not a status on the claim row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;begin
  update damage_claim set disposition='written_off', closed_at=now() where id=$1
  insert into stock_adjustment (sku, building, qty, reason, claim_id)
       values ($2, $3, -$4, 'damage', $1)
  -- available recomputes from on_hand + committed + adjustment
commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the adjustment is a note instead of a row, the storefront will sell the phantom unit, the next order will fail at pick, and the exception will be filed under a completely different problem. Every damage claim needs a disposition with a quantity effect, and the quantity effect has to reach the number your channels are allowed to sell.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ask a partner, in order
&lt;/h2&gt;

&lt;p&gt;Can you pull the pack record for this carton, by carton id, ten days later. Does it include the dunnage profile and the weight the scale read. Who is named as shipper of record on my labels. Who files, and what does the evidence pack contain when it leaves your building. And when a unit is written off, which system's available number changes.&lt;/p&gt;

&lt;p&gt;Those five questions map one to one onto the columns above, which is the point. A partner who can answer them is describing a schema. A partner who answers with a promise is describing a habit, and habits do not survive a busy week or a staff change.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) keeps the pack record with the carton and the write-off with the claim, on floors in Suzhou with 13,000 square meters, Shenzhen with 3,000 and Dongguan with 8,000. We do not publish rates or transit commitments, and we do not decide disposition policy on a seller's behalf. What we will commit to is that the row exists, that it names the building the carton left, and that a damaged unit does not stay sellable because nobody pressed a button.&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>ecommerce</category>
      <category>logistics</category>
    </item>
    <item>
      <title>A Cancel Is a State Transition, Not a Boolean</title>
      <dc:creator>life</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:00:11 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-cancel-is-a-state-transition-not-a-boolean-2ejn</link>
      <guid>https://dev.to/fulfillnexa/a-cancel-is-a-state-transition-not-a-boolean-2ejn</guid>
      <description>&lt;p&gt;Almost every fulfillment integration I have seen handles a cancellation by flipping a flag on the order row and hoping the warehouse worker process polls often enough. It works in demos and in quiet months. It fails in the specific way that costs money. The flag says canceled, the parcel leaves anyway, and both sides of the integration are technically correct about what they did.&lt;/p&gt;

&lt;p&gt;The fix is not a faster poll. It is admitting that a cancel is an intent competing with a state machine, and that the state machine has to be the thing that resolves the race.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two tables
&lt;/h2&gt;

&lt;p&gt;The order record already has a status, and Shopify's own vocabulary for it is open, closed, canceled and archived, with a separate fulfillment axis of unfulfilled, partially fulfilled and fulfilled. Copying those into your database is fine. Treating them as one field is the bug, because they change for different reasons and at different speeds.&lt;/p&gt;

&lt;p&gt;What is missing is the intent record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;cancel_intent&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;              &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_ref&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;source&lt;/span&gt;          &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;-- storefront | support | fraud | customer&lt;/span&gt;
  &lt;span class="n"&gt;reason_code&lt;/span&gt;     &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;received_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;observed_state&lt;/span&gt;  &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;-- what the resolver found&lt;/span&gt;
  &lt;span class="n"&gt;outcome&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="c1"&gt;-- stopped | too_late | unwind_required | failed&lt;/span&gt;
  &lt;span class="n"&gt;resolved_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The column that makes the whole design honest is &lt;code&gt;observed_state&lt;/code&gt;. It forces the system to record what was true at the moment the cancel arrived, instead of pretending that a cancel is a command. A cancel is a request to move a parcel backwards through states that may already be behind you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolving the race
&lt;/h2&gt;

&lt;p&gt;The resolver runs once per intent, takes a lock on the fulfillment row, and branches on the current state. The important detail is that the pick confirmation and the cancel have to serialize on the same row, otherwise you get the classic interleaving where the picker reads "not canceled", the cancel writes, the picker writes "picked", and the parcel is on a truck with a canceled order attached to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lock fulfillment_row
switch row.state:
  case 'queued':        state = 'canceled'; outcome = 'stopped'
  case 'picked':        emit walk_back_task;  state = 'canceled';
                        outcome = 'unwind_required'
  case 'packed':        void_label; emit unpack_task;
                        state = 'canceled'; outcome = 'unwind_required'
  case 'manifested':    outcome = 'too_late';  -&amp;gt; return_path
  case 'shipped':       outcome = 'too_late';  -&amp;gt; return_path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two of these branches are not cancellations at all. They are unwind operations that happen to have been triggered by a cancellation, and they need their own tasks, their own completion signal, and their own effect on the stock record. Modeling them as the same event is how you end up with a canceled order and an on-hand quantity that never came back.&lt;/p&gt;

&lt;p&gt;The last two branches are where the honest system says no. Once a carton is on a manifest, the parcel belongs to a carrier schedule, and the remaining option is whatever intercept product that carrier sells, which is time-boxed and explicitly not guaranteed. Your state machine should not pretend otherwise, because the next person to read that record will be reconciling money.&lt;/p&gt;

&lt;h2&gt;
  
  
  The acknowledgment is the product
&lt;/h2&gt;

&lt;p&gt;Support tickets die here. The storefront gets &lt;code&gt;200 OK&lt;/code&gt; from a cancel endpoint and reports "canceled" to the customer, while the warehouse got the message at 14:38 and the carton was sealed at 14:36. Both are true.&lt;/p&gt;

&lt;p&gt;An ack that carries the observed state turns that argument into a log line. "Cancel received at pack-out. Label voided, unit walked back to the shelf, count reconciled." That is a sentence a support agent can send, and it cannot be written unless the resolver actually ran. The difference between those two replies is the entire service level.&lt;/p&gt;

&lt;p&gt;The same event should carry a reason code. Cancels arrive from customers, from fraud review, from support goodwill and from marketplace rules, and they have different follow-up obligations. A cancel that came from a marketplace deadline has to be visible to whoever watches channel compliance, while a customer change of mind does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this fixes that a flag does not
&lt;/h2&gt;

&lt;p&gt;Three things show up in the data afterwards. Late notices stop being invisible, because &lt;code&gt;too_late&lt;/code&gt; is a countable outcome with a stage attached. The unwind tasks stop being forgotten, because they have a row that is either complete or not. And the cost of a cancel becomes measurable per stage rather than per month, which is the only version of that number that changes anybody's behavior.&lt;/p&gt;

&lt;p&gt;We run this shape across our Chinese sites, 13,000 square meters in Suzhou, 3,000 in Shenzhen and 8,000 in Dongguan, under the FulfillNexa by SBT (fulfillnexa.com) name, and the reason it is modeled as transitions rather than flags is not architectural taste. It is that a cancel can arrive while a human is holding the unit, and the record has to say what happened next in a language that survives the shift change.&lt;/p&gt;

&lt;p&gt;The status names and the rule that a partially fulfilled order has to have its fulfillment canceled before the order itself becomes eligible are Shopify's published documentation. Check their current version the day you build, because the field that saves you is the one that still exists.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>ecommerce</category>
      <category>fulfillment</category>
    </item>
    <item>
      <title>A Fulfillment Agreement Is Three Documents, Not a Dashboard</title>
      <dc:creator>life</dc:creator>
      <pubDate>Wed, 07 Oct 2026 19:10:02 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-fulfillment-agreement-is-three-documents-not-a-dashboard-2b9f</link>
      <guid>https://dev.to/fulfillnexa/a-fulfillment-agreement-is-three-documents-not-a-dashboard-2b9f</guid>
      <description>&lt;p&gt;Most fulfillment conversations end with somebody sharing a dashboard. It shows orders, inventory, maybe a shipment status column, and it feels like the deliverable. It is not the deliverable. A dashboard is a view, and a view can be changed, re-pointed or switched off by whoever owns the underlying system.&lt;/p&gt;

&lt;p&gt;What you actually signed up for is three documents. If they do not exist as documents, the service has no interface, and every disagreement later becomes a negotiation about memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document one: a dated rule sheet for the channel you ship into
&lt;/h2&gt;

&lt;p&gt;This is the one that sounds like paperwork and behaves like engineering. The rule sheet says: for channel X, item class Y, here is what must be true before the parcel leaves our building, and here is the date the rule was read from the source.&lt;/p&gt;

&lt;p&gt;The reason the date is the load-bearing part is that these rules move. Amazon's bagging requirements, expiration-date presentation, carton envelope and case-pack caps are all published by Amazon and all subject to change without notice to you. A rule sheet without dates is a claim about the present that will silently become false.&lt;/p&gt;

&lt;p&gt;A shape that works:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;rule_code&lt;/th&gt;
&lt;th&gt;channel&lt;/th&gt;
&lt;th&gt;applies_to&lt;/th&gt;
&lt;th&gt;expected&lt;/th&gt;
&lt;th&gt;source_seen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;suffocation_warning&lt;/td&gt;
&lt;td&gt;amazon-fba&lt;/td&gt;
&lt;td&gt;bag opening &amp;gt;= 5in&lt;/td&gt;
&lt;td&gt;prescribed warning printed, film &amp;gt;= 1.5 mil&lt;/td&gt;
&lt;td&gt;2026-10-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;expiration_format&lt;/td&gt;
&lt;td&gt;amazon-fba&lt;/td&gt;
&lt;td&gt;shelf-life items&lt;/td&gt;
&lt;td&gt;MM-DD-YYYY or MM-YYYY, 36pt+, unit and box&lt;/td&gt;
&lt;td&gt;2026-10-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;carton_envelope&lt;/td&gt;
&lt;td&gt;amazon-fba&lt;/td&gt;
&lt;td&gt;standard box&lt;/td&gt;
&lt;td&gt;36.00 x 25.00 x 25.00 in, 50.00 lb&lt;/td&gt;
&lt;td&gt;2026-10-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;preexisting_barcode&lt;/td&gt;
&lt;td&gt;amazon-fba&lt;/td&gt;
&lt;td&gt;inbound carton&lt;/td&gt;
&lt;td&gt;prior carrier barcode removed or rendered unscannable&lt;/td&gt;
&lt;td&gt;2026-10-01&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two properties make this a document rather than a wiki page. Closing a rule never changes what an old shipment was judged against, because the version is dated. And every row names a source, so when the platform changes the requirement, the fix is one row, not an archaeology project.&lt;/p&gt;

&lt;p&gt;We keep our prep rules in exactly this shape, and the reason is not tidiness. It is that an operator answering "why was this treated differently from last month" needs a version, not a recollection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document two: an exception log with an owner and a disposition
&lt;/h2&gt;

&lt;p&gt;Anything that did not go as planned should exist as a row with four fields filled in: what triggered it, what the decision was, who owns the next action, and what it cost.&lt;/p&gt;

&lt;p&gt;The field people skip is the owner, and it is the only one that determines whether the exception closes. "Held pending documentation" with no name attached is a status, not a work item. Compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-10-07  hold        carton 88213  rule: expiration_format
            owner: &amp;lt;named person&amp;gt;     next: request re-code from supplier
            cost: null                close: null

2026-10-07  hold        carton 88213  rule: expiration_format
            owner: nobody             next: waiting
            cost: unknown             close: unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one is what most provider relationships actually look like, and it is why sellers discover a problem three weeks late rather than three days late.&lt;/p&gt;

&lt;p&gt;A useful property to ask for: the log must be exportable and readable after you stop being a customer. If the only copy lives in a dashboard you lose access to on termination, the log is a feature of the vendor's product rather than a record of your operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document three: named responsibilities
&lt;/h2&gt;

&lt;p&gt;Three questions, answered in writing, with a party named for each:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who is importer of record on a direct-to-consumer parcel?&lt;/strong&gt; For a parcel leaving a Chinese warehouse addressed to a consumer in Germany, the party accountable at the border is often the recipient. Whoever arranged the shipment should be explicit about who is named and why, because that is the difference between a tax line and a customer service incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who pays a preparation defect fee when the defect was created on the provider's side?&lt;/strong&gt; A rule sheet with no cost owner is advice. The answer does not have to be generous, it has to exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who holds the evidence, and for how long?&lt;/strong&gt; If a carton is reworked, the only surviving proof of what arrived is what was captured before the rework. Ask where those files live and whether you can get them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What deliberately is not in these documents
&lt;/h2&gt;

&lt;p&gt;Rates and transit promises, for one. We do not publish either, and no rule above depends on them. A quoted per-parcel number and a delivery window are both forecasts, and forecasts do not belong in the same artifact as a dated rule that determines whether a shipment is compliant.&lt;/p&gt;

&lt;p&gt;The other thing that does not belong is a service-level number nobody can recompute. If a provider states a percentage, ask which denominator and which window, and ask to see the same query run on last month's data. A metric you cannot reproduce is decoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read all three in ten minutes
&lt;/h2&gt;

&lt;p&gt;Ask for one closed exception from last quarter and follow it. The rule sheet should tell you which version applied on that date. The log should tell you who owned it and how long it stayed open. Named responsibilities should tell you who paid and who still has the photographs.&lt;/p&gt;

&lt;p&gt;If those three line up, the dashboard is a convenience. If they do not, the dashboard is the whole product, and you are buying someone else's memory of your business.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) runs inbound, storage and outbound across three warehouses in China, with 3,000 square meters in Shenzhen, 13,000 in Suzhou and 8,000 in Dongguan, and the three documents above are the shape we think a fulfillment relationship should be auditable through. We do not publish rates or transit commitments, and none of the above depends on either.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>ecommerce</category>
      <category>fulfillment</category>
    </item>
    <item>
      <title>A Prep Defect Should Come Back as a Record, Not a Story</title>
      <dc:creator>life</dc:creator>
      <pubDate>Wed, 07 Oct 2026 19:07:24 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-prep-defect-should-come-back-as-a-record-not-a-story-1720</link>
      <guid>https://dev.to/fulfillnexa/a-prep-defect-should-come-back-as-a-record-not-a-story-1720</guid>
      <description>&lt;p&gt;A carton gets pulled aside at a receiving center. Two days later someone asks what happened. The answer arrives as a sentence: "the bags were wrong." Nobody can say which bag, against which rule, or what it cost, and the conversation quietly becomes about who to believe.&lt;/p&gt;

&lt;p&gt;That is the failure mode I want to write down, because it is not a packing failure. The packing failure already happened and was fixable in ninety seconds. The failure that costs money is that the evidence was destroyed by the fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a defect actually is
&lt;/h2&gt;

&lt;p&gt;Almost every prep defect on goods coming from China is a measurement, not a judgment. A bag opening of five inches or more needs a suffocation warning printed on it, and the film has to be at least 1.5 mil. Expiration dates have to read MM-DD-YYYY or MM-YYYY, at 36 point or larger, on the unit and on the box. A standard carton stops at 36.00 by 25.00 by 25.00 inches and 50.00 pounds. Case packs cap at 150 units. A supplier carton whose previous carrier barcode is still scannable is a defect even when everything inside it is perfect.&lt;/p&gt;

&lt;p&gt;Each of those is a number, an observed value, and a source. That is a record. A record can be replayed, audited and argued with. "The bags were wrong" cannot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The record has to be captured before the rework
&lt;/h2&gt;

&lt;p&gt;This is the part that is easy to get wrong for an understandable reason: fixing the item feels like the job, so the operator fixes it and then reports that it was fixed.&lt;/p&gt;

&lt;p&gt;Rework is irreversible. The moment the opaque poly bag is cut open and the item goes into a compliant one, the only surviving proof of what arrived is whatever was captured first. So the capture has to be a step that gates the rework, not a form filled in afterwards.&lt;/p&gt;

&lt;p&gt;The shape we keep it in is deliberately boring. One row per finding, and the row is written before anything is touched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"findingId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f-2026-10-07-0142"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cartonRef"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inbound-carton-88213"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ruleCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"suffocation_warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amazon-fba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warning text present on bags with opening &amp;gt;= 5in, film &amp;gt;= 1.5 mil"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"observed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"opaque bag, no printed warning, opening 8in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://sellercentral.example/help/prep/bagging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceSeen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"88213-bag-front.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"88213-bag-scale.jpg"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"measured"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"openingIn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"filmMil"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"weightLb"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"disposition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rework"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reworkedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"costBearer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three fields are the ones people leave out and later need.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sourceSeen&lt;/code&gt; is the date somebody actually read the rule. Without it, a rule change silently rewrites history and you cannot say what standard the item was judged against at the time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;evidence&lt;/code&gt; is an array, not a boolean. One photo of a bag on a bench proves the bag exists. The second photo is the one that shows the tape measure across the opening. If the finding is a measurement, the evidence has to contain the measurement.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;costBearer&lt;/code&gt; stays null until someone with authority decides who pays a preparation defect fee. Leaving it blank is a fact. Guessing is a policy you did not agree with anybody.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test that makes this real
&lt;/h2&gt;

&lt;p&gt;Write it as a dispute test rather than a unit test:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Someone claims a carton was rejected on 14 March. Open the record for that carton. Can you state the rule, the observed value, the version of the rule that was in force on 14 March, and what was done, without asking anyone who was on shift that day?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, your prep process is a memory system with a database attached. The database stores the outcome. The memory stores why, and memory does not survive staff turnover.&lt;/p&gt;

&lt;p&gt;The corollary is uncomfortable but useful: &lt;strong&gt;a fix you cannot see is a service you cannot audit.&lt;/strong&gt; Any provider who reports "handled" without a row behind it is asking you to take the relationship on trust, and trust is the wrong instrument for a per-carton process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes upstream
&lt;/h2&gt;

&lt;p&gt;Once defects are records instead of stories, the aggregate becomes readable. Five rows with &lt;code&gt;ruleCode: "expiration_date_format"&lt;/code&gt; from the same manufacturer is not a prep problem, it is a coding-convention problem at the factory, and the fix belongs in the purchase order rather than at a packing bench. A row with &lt;code&gt;ruleCode: "preexisting_barcode"&lt;/code&gt; on every carton from one supplier means the inbound is arriving in retail-ready packaging that has to be broken down, which is a decision to make before you buy, not after.&lt;/p&gt;

&lt;p&gt;That is the actual value of the record. It is not that you can win an argument, although you can. It is that the defect log tells you which upstream decision to change, and a log of "bags were wrong" tells you nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ask your provider
&lt;/h2&gt;

&lt;p&gt;Ask what they capture before reworking, and ask to see one row. Ask whether the rule they applied is versioned with the date they read it. Ask who is named as the payer when the defect is theirs, and whether that decision is in the row or in an email. Ask what happens to the evidence file if you leave after six months.&lt;/p&gt;

&lt;p&gt;Four questions, and the answers are either records or stories.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) runs inbound, storage and outbound across three warehouses in China, with 3,000 square meters in Shenzhen, 13,000 in Suzhou and 8,000 in Dongguan, and the reason we keep writing these shapes down is that a prep dispute is settled by the record and by nothing else. We do not publish rates or transit commitments, and none of the above depends on either.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>ecommerce</category>
      <category>fulfillment</category>
      <category>logistics</category>
    </item>
    <item>
      <title>A Shopify Order From a China Warehouse Is Six Decision Points, Not One API Call</title>
      <dc:creator>life</dc:creator>
      <pubDate>Tue, 06 Oct 2026 18:08:58 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-shopify-order-from-a-china-warehouse-is-six-decision-points-not-one-api-call-2ohj</link>
      <guid>https://dev.to/fulfillnexa/a-shopify-order-from-a-china-warehouse-is-six-decision-points-not-one-api-call-2ohj</guid>
      <description>&lt;p&gt;The mental model most sellers carry into a China fulfillment arrangement is a single arrow: an order lands in Shopify, a webhook fires, a warehouse prints a label. Everything after that is a tracking number.&lt;/p&gt;

&lt;p&gt;That arrow hides six decisions. The reason it is worth naming them one by one is that each one has a place where it can be made automatically, a place where it has to be made by a person, and a place where it is currently being made by nobody. The third category is where parcels get stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  One: intake, keyed on something stable
&lt;/h2&gt;

&lt;p&gt;An order arrives. Before anything physical happens, the question is whether this is a new instruction or a repeat of one you already acted on. Any intake path that can deliver the same event twice has to be keyed on a stable identifier rather than on arrival time, and the key has to be unique in the store, not just in the queue.&lt;/p&gt;

&lt;p&gt;The failure looks like this: two warehouse tasks for one order, two labels, two charges, and a customer who receives the same item twice and is charged once. The fix is a constraint, not a check. If the intake table has a unique index on the order identifier, a duplicate cannot be created; if the only protection is a worker noticing, it will eventually not be noticed.&lt;/p&gt;

&lt;p&gt;We subscribe to Shopify's &lt;code&gt;orders/create&lt;/code&gt; topic, and the topic name is the easy part. The part that decides whether the integration survives is what happens to the second delivery of the same event.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two: the address, before anything is printed
&lt;/h2&gt;

&lt;p&gt;An address that will fail at the destination should fail at the origin. Shopify already models this as a first-class reason: &lt;code&gt;INCORRECT_ADDRESS&lt;/code&gt; is one of the enumerated hold reasons on a fulfillment order, alongside &lt;code&gt;INVENTORY_OUT_OF_STOCK&lt;/code&gt;, &lt;code&gt;AWAITING_PAYMENT&lt;/code&gt;, &lt;code&gt;HIGH_RISK_OF_FRAUD&lt;/code&gt;, &lt;code&gt;AWAITING_RETURN_ITEMS&lt;/code&gt;, &lt;code&gt;UNKNOWN_DELIVERY_DATE&lt;/code&gt;, &lt;code&gt;ONLINE_STORE_POST_PURCHASE_CROSS_SELL&lt;/code&gt; and &lt;code&gt;OTHER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That list is worth reading once, because it tells you what the platform expects a fulfillment flow to be able to stop for. A warehouse integration that can only report "shipped" and "not shipped" has nowhere to put six of those eight states, so it puts them in email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three: inventory that is promised, not merely counted
&lt;/h2&gt;

&lt;p&gt;A single stock pool shared by several sales channels needs a reservation ledger rather than a computed "available" number, because the number you compute at read time is already stale by the time the picker sees it. For a Shopify-only setup the same logic applies at the location level: the quantity you decrement is a statement about what a specific location owes, and the timing of that decrement determines whether two channels can both sell the last unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four: the packing decision, which is a customs decision
&lt;/h2&gt;

&lt;p&gt;Two items that would each clear a destination's low-value threshold become one consignment that does not when a picker puts them in the same box. The check therefore belongs before the parcel is created, in the packing decision, and not in a report afterwards.&lt;/p&gt;

&lt;p&gt;This is the decision most often made by whoever is standing at the bench. It is also the one that is cheapest to encode, because it needs exactly two inputs: the declared value already captured on the order, and the destination's current threshold from a dated rule table. We keep our prep and packing rules in that shape, one row per rule with the date the rule was read from its source, because a threshold that moved last month should not silently change what a shipment from March was judged against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five: the hold state, which Shopify already gives you
&lt;/h2&gt;

&lt;p&gt;This is the decision point that separates a real integration from a queue with a label printer attached.&lt;/p&gt;

&lt;p&gt;A fulfillment order in Shopify has statuses, and two of them exist precisely for "do not ship this yet": &lt;code&gt;ON_HOLD&lt;/code&gt;, and &lt;code&gt;SCHEDULED&lt;/code&gt;, which carries a &lt;code&gt;fulfill_at&lt;/code&gt; timestamp for deferred release. The mutations are &lt;code&gt;fulfillmentOrderHold&lt;/code&gt; (it takes the fulfillment order id and a hold object, and the reason comes from the enumeration above) and &lt;code&gt;fulfillmentOrderReleaseHold&lt;/code&gt;, which returns the order to &lt;code&gt;OPEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now compare what happens in a typical China-side arrangement when an order cannot go out. The order sits in the warehouse system's own "pending" bucket. The merchant's Shopify admin shows nothing unusual. The seller finds out from a customer message.&lt;/p&gt;

&lt;p&gt;The mapping that has to exist is short:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What stopped the order&lt;/th&gt;
&lt;th&gt;Where it should be visible&lt;/th&gt;
&lt;th&gt;Shopify state&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Payment or fraud review&lt;/td&gt;
&lt;td&gt;Store and warehouse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ON_HOLD&lt;/code&gt; with &lt;code&gt;AWAITING_PAYMENT&lt;/code&gt; / &lt;code&gt;HIGH_RISK_OF_FRAUD&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address failed validation&lt;/td&gt;
&lt;td&gt;Store and warehouse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ON_HOLD&lt;/code&gt; with &lt;code&gt;INCORRECT_ADDRESS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stock committed elsewhere&lt;/td&gt;
&lt;td&gt;Store and warehouse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ON_HOLD&lt;/code&gt; with &lt;code&gt;INVENTORY_OUT_OF_STOCK&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Awaiting a restock date&lt;/td&gt;
&lt;td&gt;Store and warehouse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SCHEDULED&lt;/code&gt; with &lt;code&gt;fulfill_at&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents incomplete&lt;/td&gt;
&lt;td&gt;Store and warehouse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ON_HOLD&lt;/code&gt; with &lt;code&gt;OTHER&lt;/code&gt; plus a note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ready&lt;/td&gt;
&lt;td&gt;Warehouse queue&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OPEN&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The row that matters is the last-but-one. &lt;code&gt;OTHER&lt;/code&gt; is not a junk slot; it is where a provider's own reasons go, and a note attached to a hold in the merchant's store is a different service level from the same fact arriving in a weekly email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six: handoff and the loop back
&lt;/h2&gt;

&lt;p&gt;A tracking number written into Shopify is not the end of the flow. The exception loop is what makes the previous five decisions improve: a parcel returned undelivered, a shipment held at a border, a carton rejected at a receiving center. Each of those has to come back as a row with an owner, not as a message in a thread.&lt;/p&gt;

&lt;p&gt;If a provider cannot show you one closed exception from last quarter, with the rule that triggered it, who owned it, and how long it stayed open, then the exception log is a feature of their dashboard rather than a record of your operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should stay human
&lt;/h2&gt;

&lt;p&gt;Three cases, and it is worth being explicit because over-automating them is how sellers get surprised:&lt;/p&gt;

&lt;p&gt;A hold whose reason is &lt;code&gt;OTHER&lt;/code&gt; and whose note is ambiguous. An address that fails validation a second time. An item whose remaining shelf life clears the requirement by a thin margin in a category that expects a buffer. In each case the correct system behavior is to stop and ask, and the correct provider behavior is to have a named person who gets asked.&lt;/p&gt;

&lt;p&gt;Automation in a fulfillment arrangement is not there to remove judgment. It is there to make judgment necessary only at the three or four places where judgment actually changes the outcome, and to make every one of those places visible in the same screen the seller is already looking at.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) runs inbound, storage and outbound for Shopify and marketplace orders from three warehouses in China, with 3,000 square meters in Shenzhen, 13,000 in Suzhou and 8,000 in Dongguan. We do not publish rates or transit commitments, and nothing above depends on either. The status names, hold reasons and mutation names quoted here are Shopify's own published Admin GraphQL vocabulary, and the version to work against is whatever their docs say on the day you build.&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>shopify</category>
      <category>fulfillment</category>
      <category>logistics</category>
    </item>
    <item>
      <title>Prep requirements are a rules table, not an if-statement</title>
      <dc:creator>life</dc:creator>
      <pubDate>Tue, 06 Oct 2026 16:42:40 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/prep-requirements-are-a-rules-table-not-an-if-statement-2g9g</link>
      <guid>https://dev.to/fulfillnexa/prep-requirements-are-a-rules-table-not-an-if-statement-2g9g</guid>
      <description>&lt;p&gt;Every fulfillment system ends up with a function that decides whether an item needs a poly bag, a label, a bubble wrap layer, or a "Sold as set" marking. It usually starts as ten lines of conditionals and it ends as the place where bugs go to hide, because the rules it encodes are not ours. They belong to the channel, and the channel changes them.&lt;/p&gt;

&lt;p&gt;When the rules live in code, a change in the channel's requirement becomes a deploy. When they live in a table, it becomes a row with an effective date, and the difference shows up the first time you have to explain what a shipment that moved in March was prepped under.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the rules actually look like
&lt;/h2&gt;

&lt;p&gt;Strip away the per-channel naming and a prep rule has the same shape everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;when  &amp;lt;item attributes&amp;gt;
require &amp;lt;one or more prep actions&amp;gt;
from    &amp;lt;date&amp;gt;
to      &amp;lt;date or open&amp;gt;
source  &amp;lt;where this came from&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attributes are things like whether the item is liquid, whether it ships in its own packaging, whether the manufacturer barcode is scannable, whether it is a multi-pack that must not be separated, whether it is fragile, whether it has a shelf life. The actions are things like bag it, label it, overlabel the old barcode, bundle it with a set marker, add a suffocation warning, or reject it as unshippable until the seller changes the packaging.&lt;/p&gt;

&lt;p&gt;Two of those are easy to miss and cause most of the rework. A bag can be required while the warning text is not, or the warning can be required only above a certain opening size. And an item that already carries a scannable barcode may still need an overlay label if that barcode encodes something the channel cannot resolve. Encoding either of those as a single boolean is how a shipment gets rejected at the door with a reason nobody in the building can reproduce.&lt;/p&gt;

&lt;h2&gt;
  
  
  The schema
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;prep_rule&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;            &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;channel&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- 'amazon-fba', 'walmart-wfs', 'dtc-marketplace'&lt;/span&gt;
  &lt;span class="n"&gt;rule_code&lt;/span&gt;     &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- stable name: 'poly_bag', 'suffocation_warning'&lt;/span&gt;
  &lt;span class="n"&gt;predicate&lt;/span&gt;     &lt;span class="n"&gt;jsonb&lt;/span&gt;       &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- attribute constraints&lt;/span&gt;
  &lt;span class="n"&gt;actions&lt;/span&gt;       &lt;span class="n"&gt;jsonb&lt;/span&gt;       &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- ordered list of prep actions to apply&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;        &lt;span class="n"&gt;jsonb&lt;/span&gt;       &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- thresholds for this rule version&lt;/span&gt;
  &lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;       &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;effective_to&lt;/span&gt;   &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;-- NULL means still in force&lt;/span&gt;
  &lt;span class="n"&gt;source_url&lt;/span&gt;    &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- where the channel published it&lt;/span&gt;
  &lt;span class="n"&gt;source_seen&lt;/span&gt;   &lt;span class="nb"&gt;date&lt;/span&gt;        &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- when we read it&lt;/span&gt;
  &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;effective_to&lt;/code&gt; is never edited once a successor row exists. When a channel changes a rule, you close the old row by inserting a new one with a later &lt;code&gt;effective_from&lt;/code&gt;, and you leave the old row alone. The history is the point. A seller disputing a March rejection needs the March rule, not today's rule with a comment attached.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;params&lt;/code&gt; holds the thresholds. Keeping them out of the predicate is deliberate: the predicate decides &lt;em&gt;whether&lt;/em&gt; the rule applies, the params decide &lt;em&gt;how&lt;/em&gt; the action is carried out. "Bag it" is the action; the opening-size threshold that triggers the extra warning text is a parameter of a different rule that may also apply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;prep_action&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt;        &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;label&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;labor_secs&lt;/span&gt;  &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- for costing, not for the customer-facing quote&lt;/span&gt;
  &lt;span class="n"&gt;consumable&lt;/span&gt;  &lt;span class="nb"&gt;text&lt;/span&gt;                &lt;span class="c1"&gt;-- 'poly_bag_6x8', 'thermal_label', 'bubble_wrap'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resolving a SKU at a point in time
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ItemAttrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isLiquid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;shipsInOwnPackaging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;barcodeScannable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;isSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;fragile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;shelfLifeDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="na"&gt;longestOpeningCm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requiredPrep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ItemAttrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;ruleCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT rule_code, actions, params FROM prep_rule
      WHERE channel = $1
        AND effective_from &amp;lt;= $2
        AND (effective_to IS NULL OR effective_to &amp;gt; $2)
      ORDER BY rule_code`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ruleCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rule_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;matches&lt;/code&gt; is a small predicate evaluator, not an expression engine. Something like &lt;code&gt;{ "isLiquid": true, "shipsInOwnPackaging": false }&lt;/code&gt; meaning both must hold, with arrays allowed on the right side for "any of". Resist the urge to let the predicate be arbitrary SQL or JavaScript. The moment it is, a bad row can do more than misclassify a SKU.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;asOf&lt;/code&gt; parameter is the whole design. Every caller from the receiving bench to the invoicing job passes the same date, and the answer is stable for that date forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three bugs this shape prevents
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Silent drift.&lt;/strong&gt; A channel updates its requirement and nobody tells engineering. With rules in code, the system keeps applying the old rule and the failure surfaces as rejected shipments weeks later. With rules in a table plus a &lt;code&gt;source_seen&lt;/code&gt; date, a periodic check that re-reads the published page and flags anything older than, say, ninety days is a fifteen-line job. It does not decide what the new rule is. It says "this one is old, go look".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unreproducible decisions.&lt;/strong&gt; "Why did we bag and label this twice?" is answerable when the resolution is a query: which rows were in force, what the item attributes were, what the actions produced. It is not answerable when the logic was an if-chain that has since been edited twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Costing that disagrees with the bench.&lt;/strong&gt; &lt;code&gt;labor_secs&lt;/code&gt; and &lt;code&gt;consumable&lt;/code&gt; per action mean the prep charge derived from the same rules the operators follow. When the two come from different places, the invoice is always the one that turns out wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rule that expired is not applied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requiredPrep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amazon-fba&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;liquidItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-06-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ruleCode&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poly_bag_old_threshold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;two rules can apply to one item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requiredPrep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amazon-fba&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wideOpeningLiquidItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-10-06&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ruleCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poly_bag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suffocation_warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closing a rule does not change historical answers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requiredPrep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amazon-fba&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;liquidItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-03-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nf"&gt;insertNewRuleVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poly_bag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-10-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requiredPrep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amazon-fba&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;liquidItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-03-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last one is the test that protects the design. If closing a rule can change what March resolves to, the table is a cache of today's opinion rather than a record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the numbers come from
&lt;/h2&gt;

&lt;p&gt;One honest caveat about &lt;code&gt;params&lt;/code&gt;. The thresholds belong to the channel and they move. Nothing in this schema makes your numbers correct; it only makes them dated, attributable and re-runnable. Every row carries a &lt;code&gt;source_url&lt;/code&gt; and a &lt;code&gt;source_seen&lt;/code&gt; date precisely so that a wrong number is a row you can find and replace, rather than a constant buried in a function nobody remembers touching.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) runs inbound, storage and outbound across Shenzhen, Suzhou and Dongguan, 24,000 square meters combined and 13,000 of it in Suzhou, and the prep rules we apply are kept in exactly this shape because our operators need to see why an item was treated a certain way, not just that it was. We do not publish rates or transit commitments, and none of the above depends on either.&lt;/p&gt;

</description>
      <category>data</category>
      <category>architecture</category>
      <category>ecommerce</category>
      <category>validation</category>
    </item>
    <item>
      <title>Shelf life is a column you query, not a number you decrement</title>
      <dc:creator>life</dc:creator>
      <pubDate>Tue, 06 Oct 2026 16:41:16 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/shelf-life-is-a-column-you-query-not-a-number-you-decrement-4858</link>
      <guid>https://dev.to/fulfillnexa/shelf-life-is-a-column-you-query-not-a-number-you-decrement-4858</guid>
      <description>&lt;p&gt;A seller with 900 units of a six-month-shelf-life product discovers the problem at the worst possible moment: the fulfillment center refuses the inbound at check-in because the remaining life is under whatever the channel's floor happens to be that year. Nothing in the seller's system disagreed with the delivery. The stock was there, the count was right, the labels scanned. The only thing wrong was a date nobody had been querying.&lt;/p&gt;

&lt;p&gt;Expiry handling looks like a small feature and turns into a data-model decision, because the number that matters is not "days left" but "was this acceptable at the moment it was received, and at the moment it shipped, and if we are asked next month, at those moments too".&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake is storing a countdown
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sku_stock: id, sku, quantity, expiry_days_left
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A countdown column has to be maintained by something. A nightly job decrements it, and the job runs late once, or the server was down for three days, or somebody backfills last November's receipts and the counter is now wrong in a direction that only shows up when a customer complains. Worse, the value is derived data that has been promoted to a source of truth, so you can no longer answer "what did we think on the 14th of March".&lt;/p&gt;

&lt;p&gt;The fix is boring and correct: store the date, derive the remaining life at read time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;lot&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;sku&lt;/span&gt;            &lt;span class="nb"&gt;text&lt;/span&gt;   &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lot_code&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;-- manufacturer batch code, if any&lt;/span&gt;
  &lt;span class="n"&gt;manufacture_dt&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;expiry_dt&lt;/span&gt;      &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;-- NULL for non-perishable&lt;/span&gt;
  &lt;span class="n"&gt;received_dt&lt;/span&gt;    &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt;       &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;warehouse_code&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;state&lt;/span&gt;          &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'available'&lt;/span&gt;
                 &lt;span class="c1"&gt;-- available | quarantined | expiring | expired | returned_hold&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remaining life at any instant is then arithmetic, and it is the same arithmetic everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;daysRemaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiry_dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;daysBetween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;startOfDay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;startOfDay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiry_dt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Acceptance is a policy question with a date attached
&lt;/h2&gt;

&lt;p&gt;The number that gets a shipment rejected is not a property of the goods. It is a policy of the channel, and it differs by category and by how much total life the item claims. A 30-day floor against a product with 180 days total life is a very different rule from a one-third-of-remaining-life rule against a product with two years.&lt;/p&gt;

&lt;p&gt;So the item record needs the total life, and the check needs both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ShelfLifePolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;minDaysAtReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;minFractionRemainingAtReceipt&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;totalLifeDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                           &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShelfLifePolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Verdict&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;daysRemaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no expiry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EXPIRED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minDaysAtReceipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BELOW_MIN_DAYS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;need&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minDaysAtReceipt&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minFractionRemainingAtReceipt&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalLifeDays&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalLifeDays&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frac&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minFractionRemainingAtReceipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BELOW_FRACTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;frac&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acceptable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;totalLifeDays&lt;/code&gt; is the field that is almost always missing, and without it a fraction-based rule cannot be evaluated at all. Ask for it when a perishable SKU is first set up, and make the setup fail loudly if it is not there. Discovering during a rejection that you never recorded the total life is the expensive version of the same conversation.&lt;/p&gt;

&lt;p&gt;The policy itself belongs somewhere with an effective date, because channels do revise these and a dispute about a shipment received in the spring is answered with the spring policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  FEFO, and the trap inside it
&lt;/h2&gt;

&lt;p&gt;First-expired-first-out is the obvious picking rule, and it is right most of the time. It goes wrong in two specific ways.&lt;/p&gt;

&lt;p&gt;The first is partial lots. If a lot of 400 has 45 days left and the order needs 60 units, consuming from that lot is correct. If the next order needs 400, you now have to decide whether to split the lot or push the short-dated units to the back and ship fresher stock, which quietly turns FEFO into LIFO for the remainder. Make the split decision explicit in the reservation layer rather than leaving it to whichever picker reaches the bin first.&lt;/p&gt;

&lt;p&gt;The second is the multi-channel case. A marketplace that requires 90 days of remaining life at receipt cannot be served from the same pool as a direct-to-consumer order with no such floor. FEFO across the whole pool will happily allocate the shortest-dated lot to the channel that cannot accept it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;allocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChannelConstraint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT * FROM lot
      WHERE sku = $1 AND state = 'available' AND quantity &amp;gt; 0
        AND (expiry_dt IS NULL OR expiry_dt &amp;gt;= $2)
      ORDER BY expiry_dt NULLS LAST, received_dt`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minExpiryDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;          &lt;span class="c1"&gt;// the floor is in the WHERE, not in memory&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        &lt;span class="c1"&gt;// throws SHORT_STOCK with the gap&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting the channel's floor into the &lt;code&gt;WHERE&lt;/code&gt; clause is the difference between a rule that is enforced and a rule that is hoped for. An application-side filter gets forgotten by the next caller that writes a new query path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expiry is an event, not a state you poll
&lt;/h2&gt;

&lt;p&gt;Stock does not become expired because a cron job noticed it. It becomes expired on a date. Model it that way and the reporting stops lying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;lot_status&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;daysRemaining_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiry_dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;days_left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;CASE&lt;/span&gt;
         &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiry_dt&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'none'&lt;/span&gt;
         &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiry_dt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;  &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'expired'&lt;/span&gt;
         &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;daysRemaining_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiry_dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'expiring'&lt;/span&gt;
         &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'ok'&lt;/span&gt;
       &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;life_state&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;lot&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the operational report is a query, and the "how much stock is about to become unsellable" number is the same number in the dashboard, the email alert and the reconciliation export, because all three read the same view instead of each recomputing it slightly differently.&lt;/p&gt;

&lt;p&gt;The one thing to avoid is a nightly job that mutates &lt;code&gt;state&lt;/code&gt; to &lt;code&gt;'expired'&lt;/code&gt;. That gives you two truths, and they will disagree for whatever window the job has not covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this buys in practice
&lt;/h2&gt;

&lt;p&gt;Three things show up within a month. Rejections at the channel's door drop, because the floor is enforced at allocation rather than discovered at check-in. Write-offs get attributed, because the lot row records when a quantity was destroyed and under which state, so "we lost 4% of this SKU" becomes "3.1% expired before sale, 0.9% damaged at inbound". And the argument with the supplier about which batch is short-dated ends, because the receipt recorded &lt;code&gt;manufacture_dt&lt;/code&gt; and &lt;code&gt;expiry_dt&lt;/code&gt; at the dock instead of reconstructing them from a carton later.&lt;/p&gt;

&lt;p&gt;None of that is exotic. It is a date column instead of a countdown, a policy row with an effective date instead of a constant, and a &lt;code&gt;WHERE&lt;/code&gt; clause instead of a hope.&lt;/p&gt;

&lt;p&gt;FulfillNexa by SBT (fulfillnexa.com) receives, stores and ships across Shenzhen, Suzhou and Dongguan, 24,000 square meters in total with 13,000 of that in Suzhou, and lot dates are captured at the dock because a rejection discovered 12,000 kilometers from the buyer is a far more expensive conversation than one at the pallet. We do not publish rates or transit commitments, and none of the above depends on either.&lt;/p&gt;

</description>
      <category>database</category>
      <category>ecommerce</category>
      <category>inventory</category>
      <category>design</category>
    </item>
    <item>
      <title>Splitting one order into two parcels without lying to anyone</title>
      <dc:creator>life</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:52:23 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/splitting-one-order-into-two-parcels-without-lying-to-anyone-1b9b</link>
      <guid>https://dev.to/fulfillnexa/splitting-one-order-into-two-parcels-without-lying-to-anyone-1b9b</guid>
      <description>&lt;p&gt;A support ticket arrives as a screenshot. The customer bought three items, received two, and the storefront still says the order is pending. The warehouse says both shipments went out on time. Nobody is wrong, and everything is broken, which is the signature of a split order handled at the wrong layer.&lt;/p&gt;

&lt;p&gt;Splitting is not an exception path in fulfillment software. It is the normal path for any warehouse holding mixed stock depths, and the systems that treat it as a corner case fail in exactly the ways support teams describe as mysterious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model mistake
&lt;/h2&gt;

&lt;p&gt;Most order systems store an order and a shipment as the same object, because at launch every order was one parcel. The moment a split happens, that object has to be two things at once: the commercial promise the customer bought, and the physical thing a carrier label was printed for.&lt;/p&gt;

&lt;p&gt;Keep them separate from the start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderLine&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;               &lt;span class="c1"&gt;// quantity bought, allocated, shipped&lt;/span&gt;
  &lt;span class="nl"&gt;freightChargedOnce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// what the customer paid, billed a single time&lt;/span&gt;
  &lt;span class="nl"&gt;splits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Shipment&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Shipment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShipmentLine&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;       &lt;span class="c1"&gt;// subset of order lines with quantities&lt;/span&gt;
  &lt;span class="nl"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomsEntry&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// one per shipment, never shared&lt;/span&gt;
  &lt;span class="nl"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;picked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;packed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tendered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inTransit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
       &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The invariant that prevents the screenshot is the first one: the customer is charged for shipping once, on the order, and a split never creates a second charge. Systems get this wrong when the shipping calculation is attached to the shipment record and the split inherits whatever the pricing engine last returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;shipment&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;            &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt;      &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="nv"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;seq&lt;/span&gt;           &lt;span class="nb"&gt;int&lt;/span&gt;  &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;unique&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="k"&gt;replace&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;assert_single_freight_charge&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;"order"&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;shipping_charged_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="s1"&gt;'order % charged freight more than once'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deciding the split
&lt;/h2&gt;

&lt;p&gt;Allocation runs before split logic, not after. Attempt to satisfy each line from one location and one availability window, then partition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;planShipments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StockIndex&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ShipmentPlan&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ShipmentLine&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantityShipped&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;allocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availabilityBucket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dangerousGoods&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;splitReason&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// 'availability' | 'location' | 'hazmat' | 'size'&lt;/span&gt;
    &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;availabilityBucket&lt;/code&gt; is the one people leave out. Two lines both in stock but one held for a pending quality check should not ship together, and a split driven only by location will happily put both in the same box and then hold the box. The split key has to contain every dimension on which "together or not" can change, and those dimensions are: physical location, availability status, handling class, size or weight band, and any destination rule that binds only some items.&lt;/p&gt;

&lt;p&gt;Store the reason. A split without a recorded reason cannot be audited, and the first question your operations lead asks during a spike is why the split rate doubled this week. If the answer lives only in code, it will be guessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every shipment gets its own downstream objects
&lt;/h2&gt;

&lt;p&gt;Once the partition exists, three things have to be per shipment rather than per order, and each of them is a place where a naive implementation leaks.&lt;/p&gt;

&lt;p&gt;Labels and tracking. One label per shipment, one tracking number per shipment, and the storefront shows one delivery per box instead of a single progress bar. A progress bar for a split order is a lie by construction.&lt;/p&gt;

&lt;p&gt;Idempotent status ingestion. Carrier webhooks arrive twice and out of order, and with two shipments from one order you now also get interleaving. Key the state machine on &lt;code&gt;shipmentId + status + occurredAt&lt;/code&gt;, reject stale transitions, and never derive order status by reading the last event. Order status should be a fold over its shipments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Shipment&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;partiallyDelivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tendered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inTransit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inTransit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preparing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customs entries. This is the one that stopped being theoretical in 2026. A shipment crossing into the United States is now declared, not waved through, and a declaration belongs to a shipment. Splitting an order therefore splits the declared value across two filings, and each filing needs its own line-level classification and its own value. Two consequences follow that a pure logistics model never sees.&lt;/p&gt;

&lt;p&gt;The first is threshold sensitivity. Value bands decide which entry procedure applies, so a split can move a parcel from one procedural bucket to another, and the cost of the split is not just the second box and the second label. If your pricing code assumes freight cost scales with weight only, it will be wrong on the day someone splits an order to save a delivery.&lt;/p&gt;

&lt;p&gt;The second is that arbitrary re-splitting to sit under a threshold is not an optimization. It is misrepresentation, because the underlying transaction is a single sale to a single person. Encode that as a hard rule, not a guideline, and let the split planner see the constraint instead of discovering it in a compliance review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canSplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShipmentPlan&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EntryRule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;sameBuyerSameDay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aggregatesByBuyerDay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value_aggregation_required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tests worth writing before the traffic does it for you
&lt;/h2&gt;

&lt;p&gt;A two-line order with one line out of stock produces two shipments, one freight charge, and an order status of &lt;code&gt;partiallyDelivered&lt;/code&gt; once the first is delivered. A webhook replayed after a later status does not regress the shipment. A split where the second shipment is cancelled returns the reserved stock without touching the first shipment's label. A carrier event that arrives with a shipment id belonging to a different order is rejected loudly instead of matched fuzzily. And the fold above returns &lt;code&gt;delivered&lt;/code&gt; only when every shipment is delivered, which sounds obvious and is the assertion that catches an implementation where the last event wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the customer should see
&lt;/h2&gt;

&lt;p&gt;Nothing exotic. Two deliveries, each with its own contents, its own tracking link, and its own expected arrival if you can compute one honestly. The order page should make the split visible when it happens, not after the first parcel arrives, because a customer who knows a second box is coming never files the ticket that a confused customer files on day three.&lt;/p&gt;

&lt;p&gt;The engineering version of the same advice is smaller. Model the order and the shipment as different objects, charge freight once and enforce it, give every shipment its own declaration, and record why the split happened. The ticket in the screenshot was not a picker error. It was a data model that had never been asked to describe two boxes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written from the operations side of FulfillNexa by SBT (fulfillnexa.com), a China-based cross-border fulfillment provider. Stock sits across three sites in mainland China, with the 13,000 square meter Suzhou warehouse handling supplier consolidation, 8,000 square meters at Dongguan for ecommerce and single-unit orders, and 3,000 square meters at Shenzhen for oversized cargo. Charges are quoted per shipment and per order value band rather than published as a rate card.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>ecommerce</category>
      <category>logistics</category>
    </item>
    <item>
      <title>Landed cost is a join against a dated rule table, not a column</title>
      <dc:creator>life</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:52:08 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/landed-cost-is-a-join-against-a-dated-rule-table-not-a-column-2p96</link>
      <guid>https://dev.to/fulfillnexa/landed-cost-is-a-join-against-a-dated-rule-table-not-a-column-2p96</guid>
      <description>&lt;p&gt;Most fulfillment systems store the duty assumption where it cannot survive contact with reality. A field on the product row. Sometimes a percentage in a shipping settings screen, entered the day the account was set up and never revisited. It looks like a reasonable denormalization until the first time a rule changes underneath it and forty thousand historical orders quietly become wrong.&lt;/p&gt;

&lt;p&gt;The reason that shape fails is that duty is not an attribute of a product. It is the result of a lookup against several keys at a point in time, and every one of those keys can change independently of your catalog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;landed cost = f(jurisdiction, hs_code, country_of_origin, entry_path, declared_value, date)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The date is the part people leave out, and it is the part that bites.&lt;/p&gt;

&lt;h2&gt;
  
  
  A timeline that is really a table
&lt;/h2&gt;

&lt;p&gt;The United States small-parcel exemption makes a good worked example because it moved four times in two years and every move was dated in the record that made it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2016-02-24  value cap raised from 200 to 800 USD (TFTEA, Pub. L. 114-125, sec. 901(d))
2025-05-02  exemption suspended for China and Hong Kong origin (E.O. 14256)
2025-08-29  exemption suspended for all countries (E.O. 14324, signed 2025-07-30)
2026-06-24  suspension written into CBP regulation, non-postal modes, indefinite
2027-07-01  statutory termination of the exemption (Pub. L. 119-21, sec. 70531(b))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your model stores a boolean called &lt;code&gt;de_minimis_eligible&lt;/code&gt; on the destination country, that boolean has been wrong at least twice and you have no way to know what it said on the day an order shipped. If instead eligibility is a row with a validity interval and a citation, the same query that prices today's order can price March 2025 and tell you which authority it used.&lt;/p&gt;

&lt;h2&gt;
  
  
  The schema
&lt;/h2&gt;

&lt;p&gt;Two tables carry almost all of this. The first is the rule side, which is reference data with history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;              &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;jurisdiction&lt;/span&gt;    &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;-- 'US', 'EU', 'GB'&lt;/span&gt;
  &lt;span class="n"&gt;rule_kind&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;-- 'de_minimis' | 'informal_entry_ceiling'&lt;/span&gt;
                                                   &lt;span class="c1"&gt;-- | 'ad_valorem' | 'fee'&lt;/span&gt;
  &lt;span class="n"&gt;hs_prefix&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;-- null = applies to all codes&lt;/span&gt;
  &lt;span class="n"&gt;origin_country&lt;/span&gt;  &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;-- null = applies to all origins&lt;/span&gt;
  &lt;span class="n"&gt;entry_path&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;-- 'postal' | 'non_postal' | null&lt;/span&gt;
  &lt;span class="n"&gt;threshold_cents&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;-- for eligibility rules&lt;/span&gt;
  &lt;span class="n"&gt;rate&lt;/span&gt;            &lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                        &lt;span class="c1"&gt;-- for ad valorem rules&lt;/span&gt;
  &lt;span class="n"&gt;currency&lt;/span&gt;        &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;effective_from&lt;/span&gt;  &lt;span class="nb"&gt;date&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;effective_to&lt;/span&gt;    &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;-- null = still in force&lt;/span&gt;
  &lt;span class="n"&gt;authority&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;-- citation, never a paraphrase&lt;/span&gt;
  &lt;span class="n"&gt;source_url&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;captured_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;rescinded_at&lt;/span&gt;    &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second is the snapshot side, written once per shipment and never updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;landed_cost_snapshot&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;shipment_id&lt;/span&gt;    &lt;span class="nb"&gt;bigint&lt;/span&gt;       &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;as_of&lt;/span&gt;          &lt;span class="nb"&gt;date&lt;/span&gt;         &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;entry_path&lt;/span&gt;     &lt;span class="nb"&gt;text&lt;/span&gt;         &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;duty_cents&lt;/span&gt;     &lt;span class="nb"&gt;bigint&lt;/span&gt;       &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;fees_cents&lt;/span&gt;     &lt;span class="nb"&gt;bigint&lt;/span&gt;       &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;fx_rate&lt;/span&gt;        &lt;span class="nb"&gt;numeric&lt;/span&gt;      &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;rule_ids&lt;/span&gt;       &lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;     &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;-- which rows produced this&lt;/span&gt;
  &lt;span class="n"&gt;recompute_of&lt;/span&gt;   &lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="c1"&gt;-- previous snapshot, if restated&lt;/span&gt;
  &lt;span class="n"&gt;reason&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                            &lt;span class="c1"&gt;-- 'initial' | 'rule_change' | 'claim'&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt;  &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;landed_cost_snapshot&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shipment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rule_ids&lt;/code&gt; is the column that saves you later. A snapshot with the numbers but not the rules used is an opinion. With the rules listed, it is evidence, and you can rebuild it, dispute it, or restate it against a different rule set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolving a price
&lt;/h2&gt;

&lt;p&gt;The lookup is an as-of join, and the ordering rules need to be explicit instead of left to whichever index the planner picked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;resolve_entry_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;p_jurisdiction&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_value_cents&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_ship_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_postal&lt;/span&gt; &lt;span class="nb"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="k"&gt;sql&lt;/span&gt; &lt;span class="k"&gt;stable&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threshold_cents&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
         &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;p_value_cents&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threshold_cents&lt;/span&gt;
         &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;p_postal&lt;/span&gt;
    &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s1"&gt;'postal_informal'&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;p_value_cents&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;ceiling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threshold_cents&lt;/span&gt;
    &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s1"&gt;'informal'&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'formal'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;lateral&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold_cents&lt;/span&gt;
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;jurisdiction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_jurisdiction&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;rule_kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'de_minimis'&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_path&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'postal'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_postal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;p_ship_date&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p_ship_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
    &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
  &lt;span class="k"&gt;cross&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="k"&gt;lateral&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;threshold_cents&lt;/span&gt;
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;jurisdiction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_jurisdiction&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;rule_kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'informal_entry_ceiling'&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;p_ship_date&lt;/span&gt;
      &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p_ship_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
    &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ceiling&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the United States the informal ceiling is 2,500 dollars, so nearly every single-parcel shipment lands in informal entry after June 2026 rather than in the exemption it used to claim. That distinction is invisible in a pricing model that only knows "duty or no duty", and it is exactly the thing that determines whether a broker, a bond, or a carrier filing arrangement has to exist before the parcel moves. Treating entry path as a derived output of the rule table instead of a hardcoded string is what lets the same code keep working when a jurisdiction reorganizes its low-value channel, which the postal side of that June rule did in a separate document the same day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invariant worth guarding
&lt;/h2&gt;

&lt;p&gt;Rules for the same key must not overlap or leave gaps at a boundary, because both failures are silent. A gap returns no row, and the code that prices "no rule found" usually falls back to zero instead of raising. An overlap returns two rows and the &lt;code&gt;order by effective_from desc limit 1&lt;/code&gt; silently picks one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="k"&gt;replace&lt;/span&gt; &lt;span class="k"&gt;view&lt;/span&gt; &lt;span class="n"&gt;rule_overlap&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hs_prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;rule_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;rule_b&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt;  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_kind&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_kind&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hs_prefix&lt;/span&gt;    &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hs_prefix&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin_country&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin_country&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'infinity'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'infinity'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that view in CI against your seeded reference data, not only in production. Overlaps get introduced by the most natural operation in this domain, which is importing a competitor's rate table or accepting a broker's spreadsheet as if it were a dimension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restating instead of editing
&lt;/h2&gt;

&lt;p&gt;When a rule is rescinded with retroactive effect, which is what happened in February 2026 when the Supreme Court held that IEEPA does not authorize additional tariffs and the duties imposed under it were ended the same day, the tempting move is to update the rate. Do not. Historical shipments were priced under a rule that existed at the time, and the money actually moved accordingly.&lt;/p&gt;

&lt;p&gt;The correct sequence is mechanical.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt;
   &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;rescinded_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;effective_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'2026-02-19'&lt;/span&gt;
 &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1488&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effective_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ad_valorem'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'2026-02-24'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- placeholder rate and date,&lt;/span&gt;
        &lt;span class="s1"&gt;'Proclamation under section 122, Temp. Import Surcharge'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- load your own lookup&lt;/span&gt;
        &lt;span class="s1"&gt;'https://www.federalregister.gov/...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;landed_cost_snapshot&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shipment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;as_of&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duty_cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;fees_cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fx_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recompute_of&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shipment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_of&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entry_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* recomputed */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fees_cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fx_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;array_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rule_change'&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;landed_cost_snapshot&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
  &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;old_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;customs_rule&lt;/span&gt; &lt;span class="n"&gt;new_rule&lt;/span&gt;
    &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;new_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jurisdiction&lt;/span&gt;
   &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;new_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_kind&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_kind&lt;/span&gt;
   &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;new_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'2026-02-24'&lt;/span&gt;
 &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'1 day'&lt;/span&gt;
 &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every restatement keeps its parent in &lt;code&gt;recompute_of&lt;/code&gt;, which gives you a chain per shipment and lets finance see the original figure, the restated figure, and the authority that caused the difference. That chain is also the only sane way to build a refund claim list, because the claim has to be scoped by entry date and by which rule was in force on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tests that catch the actual bugs
&lt;/h2&gt;

&lt;p&gt;Three of them cover most of the pain. A boundary test that asserts a shipment dated at midnight on the day a rule changes resolves to the new rule and not the old one, since &lt;code&gt;effective_to&lt;/code&gt; exclusivity is easy to write wrong. A no-rule-found test that asserts the resolver raises instead of returning zero, because zero duty is a legitimate answer in one case and a catastrophe in another, and only an explicit exception distinguishes them. And a determinism test that re-prices a frozen historical shipment after a rule insert and asserts the stored snapshot is byte-identical, since the whole point of the design is that history does not move when policy does.&lt;/p&gt;

&lt;p&gt;Rates are not configuration. They are a foreign table with an effective date and a citation attached, and the moment the model accepts that, an announcement from a regulator becomes a data change instead of a code change.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;FulfillNexa by SBT (fulfillnexa.com) is the cross-border fulfillment arm of SBT, operating three warehouses in mainland China with 24,000 square meters combined: Suzhou at 13,000 for consolidation out of the Yangtze delta, Dongguan at 8,000 for ecommerce stock and one-piece orders, and Shenzhen at 3,000 for oversized and sea-air cargo. This article models the shape of the data, not any current duty rate, and rates are settled per shipment rather than published.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>logistics</category>
      <category>fulfillment</category>
    </item>
    <item>
      <title>Finding double-billed shipments before the carrier's invoice ages out</title>
      <dc:creator>life</dc:creator>
      <pubDate>Fri, 02 Oct 2026 11:24:14 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/finding-double-billed-shipments-before-the-carriers-invoice-ages-out-1668</link>
      <guid>https://dev.to/fulfillnexa/finding-double-billed-shipments-before-the-carriers-invoice-ages-out-1668</guid>
      <description>&lt;p&gt;Carrier invoices contain duplicates. Not many, and not usually in bad faith, but enough that any operation shipping across several carriers and several services will find some on any given month. A parcel gets relabeled and both movements get billed. A return leg is invoiced as a forward leg. A charge appears in this cycle and again in the next one because the first was disputed and re-presented.&lt;/p&gt;

&lt;p&gt;The reason nobody catches it is timing. You get about ninety days to raise a dispute on most carrier invoices, and by the time finance notices that total freight spend is drifting above expectation, the window has closed. So the detection has to be a job that runs against the invoice itself, on the day it lands, rather than a quarterly review.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as the same shipment
&lt;/h2&gt;

&lt;p&gt;The obvious key is the tracking number, and it is not enough. Carriers legitimately bill one tracking number more than once, for a forward leg and a return, or for a re-delivery attempt booked as a separate service. The key that works in practice is a tuple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trackingNo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billableWeight&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;round1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billableWeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;na&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billedAmountCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;normalizeDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shipDate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;// carrier timezone, not yours&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Weight belongs in the key precisely because it is the field carriers get wrong. If the same tracking number appears twice with different billable weights, that is not a duplicate, that is a re-weigh, and it is a different kind of exception worth its own bucket.&lt;/p&gt;

&lt;p&gt;Date needs normalising before anything else. Invoice lines arrive in the carrier's timezone, and a shipment that left a China warehouse at 23:00 will land on two different dates depending on which system generated the line. Truncate to the shipping date rather than the invoice date, and never compare timestamps across the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two passes, because the failure modes differ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Within one invoice.&lt;/strong&gt; Straightforward grouping. Any key with a count above one is a candidate, and the candidates are usually re-presents of the same charge under a slightly different reason code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracking_no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ship_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;billed_amount_cents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total_cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;array_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;reason_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;reasons&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;invoice_lines&lt;/span&gt;
 &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;invoice_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
 &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;having&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
 &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;total_cents&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Across cycles.&lt;/strong&gt; Harder, because the second copy arrives weeks later in a file you have no reason to compare against the first. This needs a persistent table of every line ever billed, keyed on the tuple, with the invoice it came from. A new line that matches an older one is a duplicate unless the older one was reversed, which is why reversals have to be recorded as rows rather than as edits.&lt;/p&gt;

&lt;p&gt;The distinction matters. Within-invoice duplicates are usually a data problem in the file you are holding. Cross-cycle duplicates are a billing behavior, and the ones that repeat across months are the ones worth a conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you do with a hit
&lt;/h2&gt;

&lt;p&gt;Do not auto-dispute. A duplicate detector that files its own disputes will eventually file a wrong one, and carriers remember. Route hits to a queue with the two lines side by side, plus the scan history for that tracking number, and let a person decide in seconds rather than minutes.&lt;/p&gt;

&lt;p&gt;The fields worth showing together, because they are what settles it: both invoice dates, both amounts, the service code on each, the first scan event, the delivery event, and whether either line has already been credited. Half of these get dismissed on sight once the scan history is visible, which is the point of putting it in the view.&lt;/p&gt;

&lt;p&gt;Track two numbers on the queue itself. How much was flagged, and how much was actually recovered. The gap between them is your false-positive rate wearing a different hat, and if recovery drops while flagged volume climbs, the key tuple has drifted and needs tightening rather than the carriers needing a lecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boring win
&lt;/h2&gt;

&lt;p&gt;The first month of this produces a small refund and a sense of accomplishment. The value is not in that month. It is that after six months you have a per-carrier history of how often their billing contradicts itself, which turns a vague feeling that one carrier is expensive into a specific number you can take into a rate conversation.&lt;/p&gt;

&lt;p&gt;That history is a byproduct of a job that runs on the day the invoice lands, and it is the reason to build the detection as a permanent table rather than as a script you remember to run.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;FulfillNexa by SBT (fulfillnexa.com) is a China-based cross-border 3PL running three China warehouses totalling 24,000 m², with Suzhou at 13,000 m² for supplier consolidation, Dongguan at 8,000 m² for ecommerce warehousing and one-piece fulfillment, and Shenzhen at 3,000 m² for oversized cargo and sea-air work. Rates, product acceptance and delivery arrangements are confirmed per shipment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>data</category>
      <category>sql</category>
      <category>logistics</category>
    </item>
    <item>
      <title>A hold-and-release queue for orders waiting on paperwork</title>
      <dc:creator>life</dc:creator>
      <pubDate>Fri, 02 Oct 2026 11:24:06 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/a-hold-and-release-queue-for-orders-waiting-on-paperwork-1bca</link>
      <guid>https://dev.to/fulfillnexa/a-hold-and-release-queue-for-orders-waiting-on-paperwork-1bca</guid>
      <description>&lt;p&gt;Every fulfillment system eventually meets the order that cannot ship for a boring reason. The battery datasheet has not arrived. The commercial invoice lists a country of origin the buyer disputes. The destination needs a declaration the seller never filed. The order is real, the stock is real, and nothing will move until a document shows up.&lt;/p&gt;

&lt;p&gt;Most implementations handle this badly. Somebody puts a note on the order, which means the order keeps flowing through the pick wave and gets pulled back at the pack bench. Somebody else adds a boolean called &lt;code&gt;on_hold&lt;/code&gt;, which is worse, because a boolean cannot tell you why, who is chasing it, or when it stops being acceptable to wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  A hold is a state with a reason, not a flag
&lt;/h2&gt;

&lt;p&gt;The model that survives contact with a real warehouse is small. A hold has a reason code, an owner, a created time, an optional expiry, and a set of orders it applies to. Orders are not held, they carry holds, which sounds like a distinction without a difference until the first time one order needs two of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;holds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;            &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt;      &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;reason&lt;/span&gt;        &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;-- doc_missing, declaration_required, address_unverifiable&lt;/span&gt;
  &lt;span class="n"&gt;detail&lt;/span&gt;        &lt;span class="n"&gt;jsonb&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;owner&lt;/span&gt;         &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;-- who is chasing it&lt;/span&gt;
  &lt;span class="n"&gt;opened_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;expires_at&lt;/span&gt;    &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;-- what to do if nothing arrives&lt;/span&gt;
  &lt;span class="n"&gt;released_at&lt;/span&gt;   &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;released_by&lt;/span&gt;   &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;unique&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="n"&gt;one_open_hold_per_order_reason&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;holds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;released_at&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The partial unique index does the quiet work. Without it, the same missing document discovered by three different checks creates three open holds, and releasing one leaves the order stuck with no visible reason. With it, the second insert fails, and the code path that found the problem learns that somebody already found it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the check belongs
&lt;/h2&gt;

&lt;p&gt;Release the hold at the last moment that still allows a release to be cheap. Too early and you block orders that would have cleared anyway, which is what starves your pick rates. Too late and a picker walks a carton to the bench, scans it, and has it pulled back from them.&lt;/p&gt;

&lt;p&gt;In practice that means two gates rather than one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Cost of being wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wave release&lt;/td&gt;
&lt;td&gt;Can this order be picked at all?&lt;/td&gt;
&lt;td&gt;A wasted walk, recoverable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label purchase&lt;/td&gt;
&lt;td&gt;Will this order be allowed to leave the building?&lt;/td&gt;
&lt;td&gt;A label paid for, a parcel to retrieve&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The label gate is the expensive one, and it is exactly where most systems stop checking. If the label prints, the parcel is committed, so a document hold discovered after that point is already a reverse-logistics problem. Check holds immediately before calling the rating and label API, not when the order was imported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expiry is the part people skip
&lt;/h2&gt;

&lt;p&gt;A hold without an expiry is a decision you refused to make. Attach one and the queue becomes actionable, because now you can ask a question that has a real answer: what happens to this order if nothing arrives in five days?&lt;/p&gt;

&lt;p&gt;The answers are ordinary. Cancel and restock. Ship anyway, if the missing document turns out not to be legally required for that lane, which is a rules question rather than a judgment call. Escalate to a human with the clock already running. Whatever you pick, encode it as an action on the hold rather than as a habit, because the alternative is a queue that nobody trusts to be looked at.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// runs every few minutes; the point is that a hold cannot silently age&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sweepExpiredHolds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`select id, order_id, reason from holds
      where released_at is null and expires_at is not null and expires_at &amp;lt; $1
      for update skip locked`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;expired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;POLICIES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;onExpiry&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escalate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// cancel | ship_anyway | escalate&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hold_expired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;holdId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;for update skip locked&lt;/code&gt; matters if you run more than one worker. Without it, two sweeps pick up the same expired hold and the order gets cancelled twice, or cancelled and shipped, which is the worst of the combinations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reporting that makes it tolerable
&lt;/h2&gt;

&lt;p&gt;Two numbers, on one screen, refreshed continuously. How many orders are currently held, broken by reason. And how long each reason typically takes to clear, as a median over the last thirty days rather than a snapshot.&lt;/p&gt;

&lt;p&gt;The first number tells you whether you are meeting the day's volume. The second tells you which reason is actually a process problem. A reason code that clears in hours is a document you can chase. A reason code whose median is nine days is a supplier relationship or a data-collection step you have never fixed, and no amount of queue engineering will shorten it.&lt;/p&gt;

&lt;p&gt;That is the whole design. Holds as rows rather than flags, two gates instead of one, an expiry that forces a decision, and a median that tells you what to fix upstream.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;FulfillNexa by SBT (fulfillnexa.com) is a China-based cross-border 3PL running three China warehouses totalling 24,000 m², with Suzhou at 13,000 m² for supplier consolidation, Dongguan at 8,000 m² for ecommerce warehousing and one-piece fulfillment, and Shenzhen at 3,000 m² for oversized cargo and sea-air work. Rates, product acceptance and delivery arrangements are confirmed per shipment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>architecture</category>
      <category>logistics</category>
      <category>fulfillment</category>
    </item>
    <item>
      <title>Retrying a label API that already charged you</title>
      <dc:creator>life</dc:creator>
      <pubDate>Thu, 01 Oct 2026 07:43:21 +0000</pubDate>
      <link>https://dev.to/fulfillnexa/retrying-a-label-api-that-already-charged-you-eh5</link>
      <guid>https://dev.to/fulfillnexa/retrying-a-label-api-that-already-charged-you-eh5</guid>
      <description>&lt;p&gt;Most retry advice assumes the operation you are retrying is free. For a shipping label API that assumption is wrong in an expensive way: the call creates a real object, it bills you when it succeeds, and the failure mode you actually hit is a timeout after the carrier already did the work.&lt;/p&gt;

&lt;p&gt;Your client sees an error. The carrier has a label, a tracking number and a charge. Retry naively and now you have two of each, one of which will never be scanned, and at some point in the quarter that turns into a refund argument you cannot win because both labels are legitimately yours.&lt;/p&gt;

&lt;p&gt;Here is the pattern that survives that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three outcomes, not two
&lt;/h2&gt;

&lt;p&gt;Every money-spending call has three results, and code that models two is the source of the bug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LabelResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;labelUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RejectReason&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;        &lt;span class="c1"&gt;// carrier said no. Safe to retry with changes.&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;                              &lt;span class="c1"&gt;// timeout, 5xx after send, dropped connection.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rejected&lt;/code&gt; is a normal failure. Nothing was created, nothing was charged, you can retry.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;unknown&lt;/code&gt; is the dangerous one. Nothing in the response tells you whether the carrier side succeeded, because you never got a response. Treating &lt;code&gt;unknown&lt;/code&gt; as failure is what double-buys.&lt;/p&gt;

&lt;p&gt;So the first rule is: never retry an &lt;code&gt;unknown&lt;/code&gt; directly. Resolve it first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give every attempt a client reference you can look up
&lt;/h2&gt;

&lt;p&gt;Before you can resolve an unknown, you need something to search on. Carrier APIs differ, but most accept a client reference or an external order id on the label request, and most expose a lookup by that reference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createLabelWithResolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lane&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lane&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LabelResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lane&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;attemptWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByClientRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;labelUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelUrl&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createLabel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lane&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientRef&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timedOut&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is5xxAfterSend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;resolveAfterTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;labelUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveAfterTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LabelResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The carrier may still be committing. Poll with backoff before deciding.&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByClientRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;labelUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelUrl&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;   &lt;span class="c1"&gt;// still unresolved: escalate, do NOT create another label&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;attemptWindow&lt;/code&gt; in the reference matters. If you bake the order id alone into the reference, a legitimate second shipment for the same order (a split, a reshipment after a loss) will collide with the first lookup and get swallowed. A per-attempt window or an explicit shipment sequence number keeps the lookup honest.&lt;/p&gt;

&lt;p&gt;If the carrier supports a real idempotency key, use it instead of the reference and let the API do the deduplication. Reference lookup is the fallback for the many that do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persist the pending state before you call
&lt;/h2&gt;

&lt;p&gt;The resolve-after-timeout logic only helps if the process that runs it can be a different process, hours later. That means the intent has to be on disk before the network call, not after it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;label_attempt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;generated&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;identity&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;shipment_id&lt;/span&gt;    &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;shipment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;client_ref&lt;/span&gt;     &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lane_code&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;state&lt;/span&gt;          &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;-- pending | created | rejected | unresolved&lt;/span&gt;
  &lt;span class="n"&gt;tracking&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;attempts&lt;/span&gt;       &lt;span class="nb"&gt;int&lt;/span&gt;  &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;next_check_at&lt;/span&gt;  &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;     &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Insert &lt;code&gt;pending&lt;/code&gt;, then call. On success move to &lt;code&gt;created&lt;/code&gt; with the tracking number. On rejection move to &lt;code&gt;rejected&lt;/code&gt;. On unresolved, leave it &lt;code&gt;unresolved&lt;/code&gt; with a &lt;code&gt;next_check_at&lt;/code&gt;, and let a sweeper retry the lookup rather than the creation.&lt;/p&gt;

&lt;p&gt;The sweeper is the piece that makes this operationally boring, which is the goal. An &lt;code&gt;unresolved&lt;/code&gt; row is a question with a deadline: either the lookup eventually finds the label, or a window passes in which the carrier would certainly have committed if it had received the request, and then you can safely create a new one.&lt;/p&gt;

&lt;p&gt;That window is a business parameter, not a technical one. Set it from the carrier's own documented commit behavior, and if they have not documented it, ask. A guess here is the difference between one orphaned label and a pile of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle the label you no longer need
&lt;/h2&gt;

&lt;p&gt;Two more leaks are worth automating while you are in this code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voiding.&lt;/strong&gt; A label that is never scanned can usually be voided for a refund inside a carrier-specific window. A label whose shipment was cancelled after purchase is a refund you are leaving on the table if nothing calls void. Track it as a state, not as an exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orphan detection.&lt;/strong&gt; A label created with no live shipment behind it happens when your own process dies between the carrier call returning and your database write landing. The join that finds them is short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;label_attempt&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;
  &lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;shipment&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shipment_id&lt;/span&gt;
 &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'created'&lt;/span&gt;
   &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cancelled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'30 days'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that weekly and reconcile the results against the carrier's billing export. Anything in both lists is money you can ask for back, and the request is trivially evidenced because you have the tracking number and the cancellation timestamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that is not code
&lt;/h2&gt;

&lt;p&gt;None of this removes the double-charge risk entirely, because a lookup can fail for the same reason the create did. What it does is make the residual case visible, bounded and refundable instead of silent, unbounded and absorbed.&lt;/p&gt;

&lt;p&gt;That is the standard worth aiming at on any API that spends money: it is fine to be unsure, as long as unsure is a state you store, poll and report on rather than an error you catch and retry.&lt;/p&gt;

&lt;p&gt;The lanes this was written against are the small-parcel and one-piece fulfillment ones FulfillNexa by SBT (fulfillnexa.com) runs from three sites in China, Dongguan at 8,000 m², Suzhou at 13,000 m² and Shenzhen at 3,000 m², where a single order can trigger a label purchase several times a day across different carriers and each of them handles client references slightly differently.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
