Files
pcrt-legal-return/app/app/lib/recesso.copy.ts
tommaso aa0c77289d Ambito del recesso esplicito (intero ordine) + avviso auto-annullo + A6-ter in roadmap
Base legale (ANALISI-REQUISITI-LEGALI.md §5): il considerando 37 della Dir. (UE)
2023/2673 dice che il professionista *can* offrire il recesso su parte del
contratto, non che *deve*. Un pulsante a livello di ordine e' conforme. Ma il form
non lo diceva, mentre le automazioni agivano in modo totale.

- statementTemplate: 'relativo all'INTERO ordine #X' (la dichiarazione ora
  corrisponde a cio' che l'app fa davvero).
- SCOPE_HINT sotto la dichiarazione: il recesso riguarda l'intero ordine, per il
  parziale contattare il negozio (canale modulo tipo/email, sempre valido).
- Admin: banner quando autoCancelUnfulfilled e' attivo -> annulla e rimborsa
  l'INTERO ordine, irreversibile; con ordini multi-articolo meglio tenerlo spento.
- PLAN: A6-ter (recesso parziale) come attivita' OPZIONALE con toggle per-shop
  partialWithdrawalEnabled, default OFF. Include la nota che l'idempotenza
  (shop, orderId) di fa8ee9c andra' riportata a (shop, orderId, articoli).
- ANALISI-REQUISITI-LEGALI.md §5: punto chiarito + panorama concorrenti (Revize
  fa il parziale, Rescindly no, Shopify non ha pulsante nativo, LegalBlink non e'
  un concorrente ma un generatore di documenti).
2026-07-10 10:56:50 +02:00

150 lines
6.5 KiB
TypeScript

/**
* Copy deck IT. Stringhe visibili del flusso di recesso + template email.
* Testi legali vincolanti (non modificare senza verifica). Placeholder {{...}}.
* i18n multi-lingua = fase A7.
*/
// Etichetta pulsante di avvio (Theme App Extension / A2). Dicitura di legge.
export const BUTTON_LABEL = "Recedere dal contratto qui";
// Etichetta funzione di conferma (step 3). Funzione dedicata, non checkbox.
export const CONFIRM_LABEL = "Conferma recesso";
// Titolo del flusso.
export const PAGE_TITLE = "Recesso dal contratto";
// Label dei campi + placeholder.
export const FIELD = {
name: { label: "Nome e cognome", placeholder: "Mario Rossi" },
orderName: { label: "Numero dell'ordine", placeholder: "es. 1234" },
email: { label: "Email", placeholder: "La tua email" },
statement: { label: "Dichiarazione di recesso" },
} as const;
// Dichiarazione precompilata (editabile), deriva dall'Allegato I-B.
export function statementTemplate(orderName: string): string {
return `Con la presente comunico il recesso dal contratto di vendita relativo all'intero ordine ${orderName}.`;
}
/**
* Ambito del recesso esercitato tramite questa funzione. La funzione dell'art.
* 54-bis puo' legittimamente operare a livello di CONTRATTO (ordine): offrire
* il recesso parziale e' una facolta' del professionista (considerando 37 Dir.
* UE 2023/2673, "the trader CAN provide..."), non un obbligo. Il diritto al
* recesso parziale resta comunque esercitabile dagli altri canali (modulo tipo,
* email), che non vanno mai disabilitati: per questo li indichiamo.
*/
export const SCOPE_HINT =
"Questo recesso riguarda l'intero ordine. Per restituire solo alcuni articoli, contatta il negozio.";
// Informazioni sul diritto di recesso (Art. 49) + alternative (coesistenza).
// Mostrate su richiesta dal pulsante info "i", per non appesantire il flusso.
export const INFO_TITLE = "Il tuo diritto di recesso";
export const INFO_BODY =
"Hai 14 giorni per recedere da questo contratto senza dover fornire una motivazione. Il termine decorre dalla consegna del bene (o dalla conclusione del contratto per i servizi).\n\n" +
"Confermando il modulo eserciti il recesso: riceverai via email una ricevuta con la data e l'ora di trasmissione.\n\n" +
"Questa funzione è aggiuntiva: puoi comunque usare il modulo tipo (Allegato I, parte B) o inviare una dichiarazione via email.";
// Messaggi di errore.
export const ERROR = {
// Anti-enumeration: ordine inesistente ed email non combaciante mostrano lo
// stesso messaggio, stesso testo e stesso status 200.
lookupNoMatch:
"Non abbiamo trovato un ordine con questi dati. Controlla il numero dell'ordine e l'email dell'acquisto.",
missingField: "Compila tutti i campi per continuare.",
invalidEmail: "Inserisci un indirizzo email valido.",
windowClosed:
"Il termine di 14 giorni per il recesso su questo ordine è terminato. Contattaci per altre richieste.",
generic: "Si è verificato un problema. Riprova tra poco.",
} as const;
// Avvisi informativi (non bloccanti).
export const NOTICE = {
orderClosed:
"Questo ordine risulta già annullato o rimborsato. Puoi comunque registrare il recesso; il negozio ti contatterà per eventuali dettagli.",
} as const;
// Schermata finale (dopo "Conferma recesso").
export function successMessage(
orderName: string,
transmittedAt: string,
email: string,
receiptSent = true,
): { line1: string; line2: string; line3: string } {
return {
line1: "Recesso trasmesso",
line2: `Registrato per l'ordine ${orderName} il ${transmittedAt}.`,
line3: receiptSent
? `Ti abbiamo inviato una ricevuta a ${email}.`
: `La ricevuta verrà inviata a ${email}. Se non la ricevi a breve, contattaci.`,
};
}
/**
* Schermata per un recesso GIA' esercitato su quest'ordine. Il diritto si
* esercita una volta sola: non registriamo un secondo atto, ma confermiamo il
* primo (con il suo timestamp legale) invece di far finta di nulla.
*/
export function duplicateMessage(
orderName: string,
transmittedAt: string,
email: string,
receiptResent: boolean,
): { line1: string; line2: string; line3: string } {
return {
line1: "Recesso già registrato",
line2: `Risulta trasmesso per l'ordine ${orderName} il ${transmittedAt}. Non serve inviarlo di nuovo.`,
line3: receiptResent
? `Ti abbiamo re-inviato la ricevuta a ${email}.`
: `La ricevuta è stata inviata a ${email}. Controlla anche la posta indesiderata.`,
};
}
// Template email ricevuta su supporto durevole (usato da A4).
export function receiptEmailSubject(orderName: string): string {
return `Ricevuta della tua richiesta di recesso - Ordine ${orderName}`;
}
export function receiptEmailBody(params: {
customerName: string;
orderName: string;
transmittedAt: string; // formato leggibile con fuso orario esplicito (Europe/Rome)
statementText: string;
shopName: string;
}): string {
const { customerName, orderName, transmittedAt, statementText, shopName } =
params;
return `Gentile ${customerName},
confermiamo di aver ricevuto la Sua dichiarazione di recesso relativa all'ordine ${orderName}, trasmessa tramite la funzione di recesso presente sul nostro sito.
Dettagli della richiesta:
- Data e ora di trasmissione: ${transmittedAt}
- Ordine: ${orderName}
- Nome del consumatore: ${customerName}
Testo integrale della dichiarazione di recesso trasmessa:
"${statementText}"
Questa comunicazione costituisce la ricevuta su supporto durevole della Sua dichiarazione di recesso, ai sensi dell'art. 54-bis del Codice del Consumo. La data e l'ora sopra indicate attestano il momento della trasmissione della dichiarazione.
Le invieremo separatamente le istruzioni per l'eventuale restituzione dei beni e i tempi di rimborso.
Restano validi anche gli altri mezzi per esercitare il recesso (modulo tipo di cui all'Allegato I, parte B, o qualsiasi altra dichiarazione esplicita, anche via email): questa funzione è aggiuntiva e non li sostituisce.
${shopName}`;
}
// Messaggio prodotto escluso (Art. 59). Copy pronto; la LOGICA è in A6.
export const EXCLUSION_REASON = {
CUSTOM: "prodotto realizzato su misura o personalizzato",
PERISHABLE: "prodotto deperibile o a rapida scadenza",
HYGIENE:
"prodotto sigillato, aperto dopo la consegna, non restituibile per motivi igienici o di salute",
OTHER: "prodotto escluso dal diritto di recesso",
} as const;
export function exclusionMessage(motivoEsclusione: string): string {
return `Per questo prodotto il diritto di recesso non è previsto (${motivoEsclusione}, ai sensi dell'art. 59 del Codice del Consumo). Per informazioni o altre richieste, contattaci.`;
}