Fix: reso gia' esistente -> stato 'exists' + messaggio corretto al merchant

Se l'ordine ha gia' un reso (order.returns), createShopifyReturn ritorna 'exists'
invece di fallire: la notifica dice 'esiste gia' un reso, gestiscilo dall'ordine'
al posto del fuorviante 'reso non creato, fallo manualmente'. Audit shopify_return_exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1
This commit is contained in:
2026-07-07 16:54:43 +02:00
parent 5fb9bf494c
commit ce36f1a657
3 changed files with 25 additions and 5 deletions

View File

@@ -118,7 +118,7 @@ export async function sendMerchantNotification(params: {
customerEmail: string;
orderUrl: string;
transmittedAt: string;
returnStatus: "created" | "no_returnable" | "error";
returnStatus: "created" | "no_returnable" | "exists" | "error";
}): Promise<ReceiptResult> {
const transport = buildTransport();
if (!transport) {
@@ -128,9 +128,11 @@ export async function sendMerchantNotification(params: {
const actionLine =
params.returnStatus === "created"
? "E' stato creato un reso nell'ordine: gestiscilo dalla pagina dell'ordine."
: params.returnStatus === "no_returnable"
? "L'ordine non risulta evaso: valuta annullamento o rimborso."
: "Reso non creato automaticamente: verifica manualmente l'ordine.";
: params.returnStatus === "exists"
? "Esiste gia' un reso per questo ordine: gestiscilo dalla pagina dell'ordine."
: params.returnStatus === "no_returnable"
? "L'ordine non risulta evaso: valuta annullamento o rimborso."
: "Reso non creato automaticamente: verifica manualmente l'ordine.";
const orderBtn = /^https?:\/\//i.test(params.orderUrl)
? `<p style="margin:16px 0 0;"><a href="${escM(params.orderUrl)}" style="display:inline-block;padding:10px 18px;background:#1a1a1a;color:#fff;text-decoration:none;border-radius:8px;font-size:14px;font-weight:600;">Apri l'ordine</a></p>`

View File

@@ -356,6 +356,9 @@ export async function getShopInfo(admin: AdminApiContext): Promise<ShopInfo> {
const RETURNABLE_QUERY = `#graphql
query recessoOrderFulfillments($orderId: ID!) {
order(id: $orderId) {
returns(first: 1) {
edges { node { id } }
}
fulfillments(first: 10) {
fulfillmentLineItems(first: 50) {
edges {
@@ -380,6 +383,7 @@ const RETURN_CREATE_MUTATION = `#graphql
interface ReturnableGraphQL {
data?: {
order?: {
returns?: { edges?: Array<unknown> | null } | null;
fulfillments?: Array<{
fulfillmentLineItems?: {
edges?: Array<{
@@ -402,6 +406,7 @@ interface ReturnCreateGraphQL {
export type ReturnCreation =
| { status: "created"; returnId: string }
| { status: "no_returnable" }
| { status: "exists" } // esiste gia' un reso per l'ordine
| { status: "error"; error: string };
/**
@@ -418,6 +423,10 @@ export async function createShopifyReturn(
variables: { orderId: orderGid },
});
const qBody = (await qRes.json()) as ReturnableGraphQL;
// Se esiste gia' un reso per l'ordine, non crearne un altro (evita errore fuorviante).
if ((qBody.data?.order?.returns?.edges ?? []).length > 0) {
return { status: "exists" };
}
const returnLineItems: Array<{
fulfillmentLineItemId: string;
quantity: number;

View File

@@ -426,7 +426,8 @@ export const action = async ({ request }: ActionFunctionArgs) => {
// (best-effort; il recesso legale e' gia' registrato). Ordini annullati/
// rimborsati -> skip (G5: evita doppio reso/rimborso). Non evaso -> il
// merchant gestisce annullo/rimborso.
let returnStatus: "created" | "no_returnable" | "error" = "error";
let returnStatus: "created" | "no_returnable" | "exists" | "error" =
"error";
const orderClosed =
!!match.cancelledAt ||
match.financialStatus === "REFUNDED" ||
@@ -464,6 +465,14 @@ export const action = async ({ request }: ActionFunctionArgs) => {
detail: "ordine non evaso o nulla da rendere",
},
});
} else if (ret.status === "exists") {
await db.auditLog.create({
data: {
shop,
event: "shopify_return_exists",
detail: match.orderName,
},
});
} else {
console.error("[recesso] returnCreate:", ret.error);
await db.auditLog.create({