-- ================================================================================
-- LES TENTATIVES DE PAIEMENT
-- ================================================================================
--
-- POURQUOI UNE TABLE, ALORS QUE « factures.payee_le » SUFFIT A DIRE QU'ON A ETE PAYE.
--
-- Parce qu'elle ne dit que le succes. Or c'est l'ECHEC qui demande du travail :
--
--   COMBIEN DE FOIS a-t-on essaye de prelever ce client ? Un impaye se relance selon un
--   calendrier ; sans compteur, on relance indefiniment ou pas du tout.
--
--   POURQUOI a-t-il echoue ? « Provision insuffisante » se retente dans cinq jours ;
--   « mandat revoque » ne se retentera jamais et demande d'appeler le client. Les traiter
--   pareil, c'est soit harceler, soit abandonner une creance.
--
--   QUAND, exactement ? C'est ce qui fait foi devant un client qui conteste, et ce qui permet
--   de rapprocher un releve bancaire.
--
-- UNE COLONNE « nombre_echecs » SUR LA FACTURE AURAIT SUFFI A COMPTER, et perdu tout le reste.
-- On ne saurait ni quand, ni pourquoi, ni comment rapprocher — et l'on ne peut pas reconstituer
-- apres coup ce qu'on n'a pas enregistre.

CREATE TABLE tentatives_paiement (
    id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    reference       CHAR(26)        NOT NULL,

    facture_id      BIGINT UNSIGNED NOT NULL,

    -- La date de la tentative. DATETIME et non DATE : deux tentatives le meme jour arrivent
    -- des qu'on relance a la main apres un echec, et il faut pouvoir les ordonner.
    tentee_le       DATETIME        NOT NULL,

    -- ------------------------------------------------------------------ L'issue
    --
    -- TROIS VALEURS, ET LA TROISIEME EST CELLE QU'ON OUBLIE :
    --
    --   reussie   l'argent est arrive ;
    --   echouee   le prelevement a ete refuse ;
    --   en_cours  emis, pas encore tranche.
    --
    -- « en_cours » EXISTE PARCE QUE LE SEPA N'EST PAS INSTANTANE. Un prelevement part et son
    -- sort se connait des jours plus tard : le rejet remonte apres coup. Sans cet etat, une
    -- tentative serait « echouee » tant qu'elle n'est pas confirmee — et l'on relancerait un
    -- client dont le paiement est simplement en route.
    issue           ENUM('en_cours','reussie','echouee') NOT NULL DEFAULT 'en_cours',

    -- Le motif du refus, tel que le fournisseur le donne. NULL quand il n'y a rien a dire.
    --
    -- EN TEXTE LIBRE ET NON EN ENUM : chaque fournisseur a son vocabulaire, et le figer ici
    -- obligerait a une migration a chaque code de rejet qu'on decouvre en production.
    motif           VARCHAR(190)    NULL,

    -- ------------------------------------------------------------ Le lien externe
    --
    -- L'IDENTIFIANT CHEZ LE FOURNISSEUR — « pi_3Nk... » chez Stripe. C'est LUI qui rend le
    -- rapprochement possible : sans lui, une notification entrante ne se rattache a aucune
    -- tentative, et l'on ne saurait pas si elle a deja ete traitee.
    --
    -- NULL EN SIMULATION : l'ecran d'essai n'a pas de fournisseur derriere lui. La colonne dit
    -- donc aussi, par son absence, qu'une tentative est fictive.
    reference_externe VARCHAR(190)  NULL,

    -- Le moyen employe. Texte libre, meme raison que le motif : « sepa_debit », « card »,
    -- « virement », « simulation ».
    moyen           VARCHAR(40)     NOT NULL DEFAULT 'simulation',

    -- Le montant tente, en centimes. Il est COPIE et non lu sur la facture : un prelevement
    -- partiel existe, et le total de la facture peut etre corrige apres coup par un avoir.
    montant_cents   INT UNSIGNED    NOT NULL DEFAULT 0,

    cree_le         DATETIME        NOT NULL,
    modifie_le      DATETIME        NOT NULL,

    PRIMARY KEY (id),
    UNIQUE KEY uq_tentatives_reference (reference),

    -- ------------------------------------------------------ L'idempotence du webhook
    --
    -- LE MEME EVENEMENT ARRIVE DEUX FOIS, ET C'EST LA REGLE CHEZ TOUS LES FOURNISSEURS : ils
    -- reemettent tant qu'ils n'ont pas recu un accuse. Sans cette unicite, un accuse perdu
    -- ferait enregistrer deux fois le meme paiement — et la facture passerait payee deux fois,
    -- ce qui fausse le rapprochement bancaire.
    --
    -- LES NULL NE GENENT PAS : MySQL en accepte autant qu'on veut dans un index unique. Les
    -- tentatives simulees, qui n'ont pas de reference externe, ne sont donc pas limitees a une.
    UNIQUE KEY uq_tentatives_externe (reference_externe),

    KEY idx_tentatives_facture (facture_id, tentee_le DESC),

    CONSTRAINT fk_tentatives_facture FOREIGN KEY (facture_id)
        REFERENCES factures (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
