fix(invoice): detect pickup via missing shippingAddress (real signal for built-in 'Shop location' rate)
Reproduced against real dev order #1032: the built-in 'Shop location' shipping rate sets neither a pickup keyword nor deliveryCategory: shippingLine: { title: 'Shop location', code: 'Shop location', source: 'shopify', deliveryCategory: null } shippingAddress: null requiresShipping: true So neither v2 (string regex on title/code) nor v3 (deliveryCategory) caught it. The robust signal is 'requiresShipping && shippingAddress == null': Shopify rejects checkout for a normal shipping order without an address, so this combination is conclusive proof of pickup. - Query Order.requiresShipping (only needs read_orders). - detectPickup() now treats missing-address-but-requires-shipping as the primary signal; deliveryCategory + title/code regex remain as fallbacks for Local-Pickup-app installs and custom rates. - New fixture buildShopLocationPickupOrder() in render-sample.ts mirrors order #1032 exactly so we never regress on this shape.
This commit is contained in:
@@ -136,6 +136,7 @@ function buildAtB2BOrder(): RawOrderForInvoice {
|
||||
displayFinancialStatus: "PENDING",
|
||||
paymentGatewayNames: ["manual"],
|
||||
taxesIncluded: false,
|
||||
requiresShipping: true,
|
||||
discountCodes: [],
|
||||
customer: {
|
||||
firstName: "Lukas",
|
||||
@@ -329,6 +330,28 @@ function buildCategoryOnlyPickupOrder(): RawOrderForInvoice {
|
||||
return o;
|
||||
}
|
||||
|
||||
/** Pickup variant matching a REAL observed order on
|
||||
* linumiq-dev.myshopify.com (#1032): Shopify's built-in "Shop location"
|
||||
* rate. NO "pickup" string anywhere, deliveryCategory is `null`, and
|
||||
* shippingAddress is also `null` — detection must rely on
|
||||
* `requiresShipping && shippingAddress == null`. */
|
||||
function buildShopLocationPickupOrder(): RawOrderForInvoice {
|
||||
const o = buildAtB2BOrder();
|
||||
o.shippingLine = {
|
||||
title: "Shop location",
|
||||
code: "Shop location",
|
||||
source: "shopify",
|
||||
carrierIdentifier: null,
|
||||
deliveryCategory: null,
|
||||
originalPriceSet: { shopMoney: { amount: "0.00", currencyCode: "EUR" } },
|
||||
discountedPriceSet: { shopMoney: { amount: "0.00", currencyCode: "EUR" } },
|
||||
taxLines: [],
|
||||
};
|
||||
o.shippingAddress = null;
|
||||
o.requiresShipping = true;
|
||||
return o;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Run assertions
|
||||
// ------------------------------------------------------------------
|
||||
@@ -655,6 +678,21 @@ async function main() {
|
||||
assert("shippingMethod cleared in category-only pickup",
|
||||
categoryPickupVm.shippingMethod == null);
|
||||
|
||||
// Real-world "Shop location" pickup (matches dev order #1032): no
|
||||
// "pickup" keyword anywhere, deliveryCategory null, shippingAddress null.
|
||||
// The only signal is `requiresShipping && !shippingAddress`.
|
||||
const shopLocPickupVm = composeInvoice({
|
||||
order: buildShopLocationPickupOrder(),
|
||||
settings: settings as never,
|
||||
invoiceNumber: "RE-1034",
|
||||
});
|
||||
assert("isPickup detected from missing shippingAddress (Shop location rate)",
|
||||
shopLocPickupVm.isPickup);
|
||||
assertEq("pickupLocationName from shippingLine.title for Shop location",
|
||||
shopLocPickupVm.pickupLocationName, "Shop location");
|
||||
assert("shippingMethod cleared for Shop location pickup",
|
||||
shopLocPickupVm.shippingMethod == null);
|
||||
|
||||
// Fallback: when footerNoteEn is empty, English uses the German note.
|
||||
console.log("• Footer note fallback (en → de when EN empty)");
|
||||
const settingsNoEn = { ...(settings as object), footerNoteEn: "" } as never;
|
||||
|
||||
Reference in New Issue
Block a user