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.
Querying Order.fulfillmentOrders.deliveryMethod requires the
read_merchant_managed_fulfillment_orders scope (not read_orders, despite
what shopify.dev claims) and was failing with 'Access denied for
fulfillmentOrders field' against real stores.
Adding that scope would force every install to re-grant permissions, so
instead we rely on shippingLine alone:
- Shopify Local Pickup app: shippingLine.code = 'Pickup' (caught by the
regex) and shippingLine.title is the chosen location name itself (e.g. 'Lager Graz') \u2014 perfect as pickupLocationName.
- Custom-rate pickup ('Abholung im Lager'): regex matches title/code,
title is used as the location hint.
Removes RawDeliveryMethod, the deliveryMethods field on RawOrderForInvoice,
and the fulfillmentOrders edges from RawAdminResponse.
- 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.
- discounts: read discountedUnitPriceSet (per-line) and discountCode/discountCodes
(order-level) from Shopify; render discounted unit price with strikethrough
original on the invoice line and add a 'Rabattcode'/'Discount code' meta row
when codes were used.
- delivery date: pick the latest fulfillment.createdAt for §11 UStG instead of
hard-coding processedAt; fall back to invoice date when unfulfilled.
- pickup: detect Shopify Local Pickup (and 'Abholung'/'Pickup' custom rates) via
shippingLine.source/code/title; suppress the pickup-location 'shipping address'
block and render localized 'Abholung'/'Pick-up' as the shipping method.
- layout: move the company logo to the top-left and the meta block to the
top-right, putting recipient (and any separate delivery address) on its own
row below; drop the standalone invoice-/order-number meta rows and surface
them inside the title (e.g. 'Rechnung Nr. RE-1004 · Bestellnummer: #1004') to
recover vertical space.
- tests: smoke fixtures cover discount, pickup, and fulfillment-date variants
without disturbing the AT B2B totals.
- Query Order.shippingLine and Order.fulfillments.trackingInfo from Admin GraphQL.
- Surface orderName (#1004) so customers recognise their order alongside the sequential invoice number.
- Render shipping cost as a synthetic line item (folds into the VAT breakdown).
- Show shipping method (Versandart / Shipping method) and tracking numbers (clickable when URL present) in the meta block.
- Render a separate delivery-address block when the shipping address differs from billing.
- DE strings stay informal (Versandart / Sendungsnummer / Lieferadresse / Versand).
- i18n.de: switch Sie/Ihren to du/dein for salutation, thank-you line,
customer-VAT label and payment-terms paragraph. Closing line was
already informal.
- i18n: add paymentMethodLabel/paymentStatusLabel + per-status labels
(paid/unpaid/partial/refunded) for both DE and EN, plus
derivePaymentStatus helper that condenses Shopify's
displayFinancialStatus (PAID, PARTIALLY_PAID, REFUNDED, …) into a
4-value enum.
- loadOrderForInvoice: query Order.paymentGatewayNames and propagate it
on the raw view-model.
- composeInvoice + types: expose paymentStatus + paymentGatewayNames on
InvoiceViewModel (filtered/trimmed). loadDraftOrderForOffer keeps
paymentGatewayNames empty (drafts have no gateway yet).
- InvoiceDocument: render two new meta rows on real invoices —
'Zahlart / Payment method' (joined, prettified gateway names) and
'Zahlstatus / Payment status' (translated label). Storno + offer kinds
intentionally omit them.
- scripts/render-sample.ts: extend smoke checks to assert the informal
DE wording, the new payment-method/status rows and the
paymentStatus/paymentGatewayNames composer outputs.