fix(invoice): detect pickup via DeliveryMethodType and show 'Abholort: <location>' meta row

- Use Order.fulfillmentOrders.deliveryMethod.methodType === 'PICK_UP' as the
  primary signal (Shopify Local Pickup app exposes this reliably; the
  shippingLine title is just the location name with no 'pickup' keyword).
  Keep the legacy shippingLine string heuristic as a fallback for custom
  shipping rates merchants name 'Abholung'/'Pickup'.
- Surface assignedLocation.name as pickupLocationName on the view model.
- Replace the 'Versandart: <location name>' row with 'Abholort: <location>'
  (DE) / 'Pick-up location: <location>' (EN); falls back to plain
  'Abholung'/'Pick-up' when the location name is unavailable.
This commit is contained in:
Gerhard Scheikl
2026-05-15 14:46:55 +02:00
parent 415a9dd462
commit d742e75419
7 changed files with 124 additions and 26 deletions
@@ -25,6 +25,11 @@ export interface RawOrderForInvoice {
taxLines: RawTaxLine[];
shippingLine: RawShippingLine | null;
fulfillments: RawFulfillment[];
/** Delivery methods declared on the order's fulfillment orders. Used to
* reliably detect local pickup (`methodType === "PICK_UP"`) and to
* surface the pickup-location name. May be empty for unfulfilled or
* digital orders. */
deliveryMethods: RawDeliveryMethod[];
/** Discount codes applied to the cart (e.g. `["SUMMER10"]`). Empty when
* no codes were used. Manual / automatic discounts without a code are
* not exposed here. */
@@ -102,6 +107,17 @@ export interface RawFulfillment {
trackingInfo: RawTrackingInfo[];
}
/** Subset of Shopify's `DeliveryMethod` we care about. `methodType` is one
* of the enum values from `DeliveryMethodType` — e.g. `SHIPPING`,
* `PICK_UP`, `LOCAL`, `RETAIL`, `PICKUP_POINT`, `NONE`. */
export interface RawDeliveryMethod {
methodType: string | null;
/** Name of the location the customer chose to pick up from (when
* `methodType === "PICK_UP"`). Comes from the assigned location of the
* fulfillment order. */
locationName: string | null;
}
const QUERY = `#graphql
query OrderForInvoice($id: ID!) {
order(id: $id) {
@@ -173,6 +189,18 @@ const QUERY = `#graphql
company
}
}
fulfillmentOrders(first: 20) {
edges {
node {
deliveryMethod {
methodType
}
assignedLocation {
name
}
}
}
}
lineItems(first: 250) {
edges {
node {
@@ -240,6 +268,14 @@ interface RawAdminResponse {
discountCodes: string[] | null;
shippingLine: RawShippingLine | null;
fulfillments: RawFulfillment[] | null;
fulfillmentOrders: {
edges: {
node: {
deliveryMethod: { methodType: string | null } | null;
assignedLocation: { name: string | null } | null;
};
}[];
} | null;
lineItems: { edges: { node: RawLineItem }[] };
purchasingEntity: {
company?: { name: string } | null;
@@ -293,6 +329,10 @@ export async function loadOrderForInvoice(
: (order.discountCode ? [order.discountCode] : []),
shippingLine: order.shippingLine ?? null,
fulfillments: order.fulfillments ?? [],
deliveryMethods: (order.fulfillmentOrders?.edges ?? []).map((e) => ({
methodType: e.node.deliveryMethod?.methodType ?? null,
locationName: e.node.assignedLocation?.name ?? null,
})),
lineItems: (order.lineItems?.edges || []).map((e) => {
const node = e.node as unknown as RawLineItem & { image?: { url: string | null } | null };
return {