feat(automations): auto-email invoice on wire-transfer placed and on fulfillment

- New ShopSettings fields: autoEmailOnWireTransferPlaced,
  autoEmailOnFulfilledNonWireTransfer, wireTransferGatewayNames.
- New Automations section in settings with two toggles + gateway list.
- orders/create webhook now fires automation 1 (wire-transfer placed).
- New orders/fulfilled webhook fires automation 2 (non-wire-transfer fulfilled).
- Shared helper services/invoice/automations.server.ts handles classification
  and idempotent generate+send (skips if already sent).
- Webhook subscription for orders/fulfilled added to all 3 app tomls.

This is the non-Plus fallback for Shopify Flow, whose custom-app actions
are gated to Plus stores only.
This commit is contained in:
Gerhard Scheikl
2026-05-09 20:21:41 +02:00
parent a99dbc51c5
commit 0800d1160b
9 changed files with 244 additions and 4 deletions
+32
View File
@@ -175,6 +175,9 @@ export const action = async ({ request }: ActionFunctionArgs) => {
emailBodyHtmlDe: str("emailBodyHtmlDe"),
emailSubjectEn: str("emailSubjectEn"),
emailBodyHtmlEn: str("emailBodyHtmlEn"),
autoEmailOnWireTransferPlaced: bool("autoEmailOnWireTransferPlaced"),
autoEmailOnFulfilledNonWireTransfer: bool("autoEmailOnFulfilledNonWireTransfer"),
wireTransferGatewayNames: str("wireTransferGatewayNames"),
};
await db.shopSettings.upsert({
@@ -412,6 +415,35 @@ export default function SettingsRoute() {
</s-stack>
</s-section>
<s-section heading="Automations">
<s-stack direction="block" gap="base">
<s-paragraph>
These trigger directly from Shopify order webhooks no Shopify
Flow required (Flow is gated to Plus stores for custom apps).
When an automation fires, the invoice is generated (if it doesn't
already exist) and emailed to the customer using the SMTP and
email-template settings above. A copy is recorded in the email
log; failures are logged server-side.
</s-paragraph>
<Toggle
label='Auto-email the invoice when a wire-transfer order is placed (so the customer gets the bank details + GiroCode immediately).'
name="autoEmailOnWireTransferPlaced"
checked={settings.autoEmailOnWireTransferPlaced}
/>
<Toggle
label='Auto-email the invoice when an order is fulfilled and is NOT a wire-transfer order (e.g. the customer paid by card and we send the invoice with the shipment).'
name="autoEmailOnFulfilledNonWireTransfer"
checked={settings.autoEmailOnFulfilledNonWireTransfer}
/>
<Field
label="Wire-transfer payment gateway names (comma-separated, case-insensitive substring match)"
name="wireTransferGatewayNames"
defaultValue={settings.wireTransferGatewayNames}
helpText='Used to classify which orders count as "wire transfer". Leave empty to use the default: manual, Überweisung, Wire Transfer, Bank Transfer, Vorkasse, Bank Deposit.'
/>
</s-stack>
</s-section>
<s-section>
<s-stack direction="block" gap="base">
{actionData?.ok && (