fix(invoice): use shippingLine.deliveryCategory as primary pickup signal
Order 1032 on dev still rendered as 'Versandart: Lager Graz' because the shipping line's title/code/source contained no 'pickup' keyword — only `shippingLine.deliveryCategory == "pickup"` flagged it as a pickup. `shippingLine.deliveryCategory` only requires `read_orders` (already granted), so query and use it as the primary signal. Keep the regex on title/code/source/carrier as a fallback for custom rates without a proper pickup category.
This commit is contained in:
@@ -487,36 +487,49 @@ function mapTracking(order: RawOrderForInvoice): TrackingInfo[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the order is a "local pickup" order. Pickup is detected
|
||||
* heuristically from `shippingLine.{source,code,title,carrierIdentifier}`:
|
||||
* Detects whether the order is a "local pickup" order using two signals:
|
||||
*
|
||||
* - The Shopify Local Pickup app sets `shippingLine.code = "Pickup"` and
|
||||
* uses the chosen pickup-location name as the shipping-line title — so
|
||||
* the title doubles as the location name.
|
||||
* - Merchants who model pickup as a custom shipping rate typically include
|
||||
* "Abholung"/"Pickup" in the title or code.
|
||||
* 1. Primary: `shippingLine.deliveryCategory` (e.g. `"pickup"`,
|
||||
* `"local_pickup"`). This is what Shopify's Local Pickup app and any
|
||||
* properly-categorised custom pickup rate set, and only requires the
|
||||
* `read_orders` scope.
|
||||
* 2. Fallback: regex on `shippingLine.{source,code,title,carrierIdentifier}`
|
||||
* for merchants who model pickup as a custom shipping rate without a
|
||||
* pickup category (e.g. titled "Abholung im Lager").
|
||||
*
|
||||
* (We deliberately do NOT query `Order.fulfillmentOrders.deliveryMethod`
|
||||
* here: that field requires the `read_merchant_managed_fulfillment_orders`
|
||||
* scope, which would force every install to re-grant permissions.)
|
||||
* (We deliberately do NOT query `Order.fulfillmentOrders.deliveryMethod`:
|
||||
* that field requires the `read_merchant_managed_fulfillment_orders` scope,
|
||||
* which would force every install to re-grant permissions.)
|
||||
*
|
||||
* Returns the pickup descriptor (with location name when known) or `null`
|
||||
* when the order is a normal shipping order. Callers should not render the
|
||||
* pickup-location address as a separate "delivery address".
|
||||
* When pickup is detected, the location name is taken from
|
||||
* `shippingLine.title` — for Shopify Local Pickup the title IS the chosen
|
||||
* location name (e.g. "Lager Graz").
|
||||
*
|
||||
* Returns the pickup descriptor or `null` when the order is a normal
|
||||
* shipping order. Callers should not render the pickup-location address as
|
||||
* a separate "delivery address".
|
||||
*/
|
||||
function detectPickup(
|
||||
order: RawOrderForInvoice,
|
||||
): { locationName: string | null } | null {
|
||||
const sl = order.shippingLine;
|
||||
if (!sl) return null;
|
||||
// Primary signal: shippingLine.deliveryCategory is "pickup" / "local_pickup"
|
||||
// for any pickup-like fulfillment (set by Shopify's Local Pickup app and by
|
||||
// custom apps that use the proper category). Doesn't require any extra scope.
|
||||
const dc = (sl.deliveryCategory ?? "").toLowerCase();
|
||||
const isPickupCategory = dc.includes("pickup") || dc.includes("pick_up") || dc.includes("pick-up");
|
||||
// Fallback: string heuristic on title/code/source/carrier — covers
|
||||
// merchants who model pickup as a custom shipping rate without category.
|
||||
const haystack = [sl.source, sl.code, sl.title, sl.carrierIdentifier]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
if (!/pick[\s-]?up|abholung|abhol\b/.test(haystack)) return null;
|
||||
const isPickupString = /pick[\s-]?up|abholung|abhol\b/.test(haystack);
|
||||
if (!isPickupCategory && !isPickupString) return null;
|
||||
// For Shopify Local Pickup, `title` is the location name itself
|
||||
// (e.g. "Lager Graz"). For custom-rate pickup ("Abholung im Lager"),
|
||||
// it's a generic description — still better than nothing as a hint.
|
||||
// (e.g. "Lager Graz"). For custom-rate pickup, it's a generic description
|
||||
// — still better than nothing as a hint.
|
||||
return { locationName: sl.title?.trim() || null };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user