-- LA FACTURATION DES CONCIERGERIES.
--
-- ================================================================================
-- LE PRIX REJOINT L'OFFRE ET L'ABONNEMENT, SUR LE MEME PRINCIPE QUE LES QUOTAS
-- ================================================================================
--
-- L'offre porte un prix de CATALOGUE ; l'abonnement porte le prix ENGAGE, copie a la
-- souscription. C'est la meme decision que pour les quatre plafonds, et pour la meme raison :
-- augmenter « Essentiel » de 40 a 50 euros ne doit pas augmenter retroactivement ceux qui ont
-- signe a 40, ni changer une facture deja emise.
--
-- EN CENTIMES ENTIERS, comme tout l'argent de ce projet. Un flottant a virgule accumule des
-- erreurs a chaque addition : sur douze lignes de facture, le total ne tombe plus juste, et
-- c'est le genre d'ecart qu'un comptable trouve avant nous.

ALTER TABLE offres
    ADD COLUMN prix_cents INT UNSIGNED NULL AFTER descriptif;

ALTER TABLE abonnements
    ADD COLUMN prix_cents INT UNSIGNED NULL AFTER offre_id;

-- ================================================================================
-- LES FACTURES
-- ================================================================================

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

    -- ============================================================================
    -- LE NUMERO : « 2026-0042 », ET IL EST UNIQUE EN BASE
    -- ============================================================================
    --
    -- L'UNICITE EST LA VRAIE GARANTIE, pas le calcul qui l'attribue. Deux administrateurs qui
    -- emettent au meme instant liraient le meme MAX et proposeraient le meme numero ; sans cet
    -- index, deux factures porteraient « 2026-0042 » et l'une des deux serait introuvable le
    -- jour ou un client la conteste.
    --
    -- Avec l'index, la seconde ECHOUE BRUYAMMENT — ce qui est le bon comportement : on la
    -- reemet, et rien n'est perdu. Un numero de facture est une reference legale, pas un
    -- identifiant technique qu'on peut laisser deriver.
    --
    -- CHAR ET NON UN ENTIER : le zero de tete fait partie du numero, et « 2026-0042 » n'est pas
    -- un nombre.
    numero          CHAR(9)         NOT NULL,

    conciergerie_id BIGINT UNSIGNED NOT NULL,

    -- L'abonnement facture, quand la facture en decoule. NULL pour une facture ponctuelle —
    -- une prestation d'accompagnement, une reprise de donnees.
    abonnement_id   BIGINT UNSIGNED NULL,

    -- ============================================================================
    -- LES DATES : TROIS, ET ELLES NE DISENT PAS LA MEME CHOSE
    -- ============================================================================
    --
    --   emise_le     la date qui figure sur le document. Elle fait foi comptablement ;
    --   echeance_le  la date a laquelle le paiement est du. C'est elle qui decide du retard ;
    --   payee_le     NULL tant que rien n'est encaisse. Une DATE et non un booleen : « quand
    --                a-t-il paye » est la question qu'on pose devant un litige, et un booleen
    --                n'y repond pas. Meme choix que utilisateurs.notifications_refus_le.
    emise_le        DATE            NOT NULL,
    echeance_le     DATE            NOT NULL,
    payee_le        DATE            NULL,

    -- ============================================================================
    -- LES MONTANTS SONT STOCKES, PAS RECALCULES
    -- ============================================================================
    --
    -- On pourrait les deduire des lignes a chaque lecture. On ne le fait PAS : une facture
    -- emise est un document FIGE. Si l'on recalculait, corriger une ligne changerait le total
    -- d'un document deja envoye, et deux exemplaires du meme numero circuleraient avec des
    -- montants differents.
    --
    -- Les lignes expliquent le total ; elles ne le definissent pas.
    montant_ht_cents  INT UNSIGNED  NOT NULL DEFAULT 0,

    -- ============================================================================
    -- LE TAUX DE TVA EN CENTIEMES DE POINT : 2000 = 20,00 %
    -- ============================================================================
    --
    -- Un entier plutot qu'un DECIMAL, pour la meme raison que les centimes : pas de flottant
    -- dans un calcul d'argent. Et sur la FACTURE plutot que dans une constante, parce qu'un
    -- taux change — 19,6 puis 20 — et qu'une facture de 2013 doit garder le sien.
    tva_taux          SMALLINT UNSIGNED NOT NULL DEFAULT 2000,
    montant_ttc_cents INT UNSIGNED  NOT NULL DEFAULT 0,

    -- ============================================================================
    -- LE STATUT : CE QU'ON A DECIDE, ET NON CE QU'ON EN DEDUIT
    -- ============================================================================
    --
    -- Quatre valeurs, et « en retard » n'en fait PAS partie : le retard se DEDUIT de l'echeance
    -- et de la date du jour, exactement comme l'etat d'un abonnement. Le stocker obligerait a
    -- le rafraichir chaque nuit, et une facture serait « a jour » jusqu'au passage de la tache.
    --
    --   brouillon  se modifie librement, ne compte pas ;
    --   emise      le document est parti. Il ne se modifie plus, il s'annule ;
    --   payee      encaissee ;
    --   annulee    emise puis annulee. On ne SUPPRIME pas une facture emise : son numero est
    --              consomme, et un trou dans une sequence est ce qu'un controle fiscal
    --              remarque en premier.
    statut          VARCHAR(12)     NOT NULL DEFAULT 'brouillon',

    notes           TEXT            NULL,

    cree_le         DATETIME        NOT NULL,
    modifie_le      DATETIME        NOT NULL,

    PRIMARY KEY (id),
    UNIQUE KEY uq_factures_reference (reference),
    UNIQUE KEY uq_factures_numero (numero),

    -- L'index qui sert les deux questions de l'onglet Commercial : « les factures de ce
    -- client » et « qu'est-ce qui n'est pas paye ».
    KEY idx_factures_conciergerie (conciergerie_id, emise_le DESC),
    KEY idx_factures_impayees (statut, echeance_le),

    CONSTRAINT fk_factures_conciergerie FOREIGN KEY (conciergerie_id)
        REFERENCES conciergeries (id) ON DELETE RESTRICT,

    -- SET NULL et non RESTRICT : supprimer un abonnement saisi par erreur ne doit pas etre
    -- bloque par une facture, ni emporter cette facture. Elle reste, et perd son rattachement —
    -- ce qui est exact : le document existe toujours.
    CONSTRAINT fk_factures_abonnement FOREIGN KEY (abonnement_id)
        REFERENCES abonnements (id) ON DELETE SET NULL,

    -- Une echeance anterieure a l'emission est une faute de saisie qui rendrait la facture en
    -- retard le jour meme de son emission.
    CONSTRAINT ck_factures_echeance CHECK (echeance_le >= emise_le)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ================================================================================
-- LES LIGNES
-- ================================================================================
--
-- Elles EXPLIQUENT le total, elles ne le definissent pas — voir plus haut. Un document emis
-- est fige ; les lignes disent de quoi il est fait.
CREATE TABLE facture_lignes (
    id                  BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    facture_id          BIGINT UNSIGNED NOT NULL,

    -- « Abonnement Essentiel du 01/01/2026 au 31/12/2026 ». Le libelle est FIGE lui aussi :
    -- renommer l'offre plus tard ne doit pas reecrire une facture partie chez le client.
    libelle             VARCHAR(255)    NOT NULL,

    -- En millemes plutot qu'en entier : « 1,5 mois » et « 0,5 journee » existent. 1000 = une
    -- unite. Un flottant serait plus lisible et moins sur.
    quantite_millemes   INT UNSIGNED    NOT NULL DEFAULT 1000,
    prix_unitaire_cents INT UNSIGNED    NOT NULL DEFAULT 0,

    ordre               SMALLINT UNSIGNED NOT NULL DEFAULT 0,

    PRIMARY KEY (id),
    KEY idx_facture_lignes (facture_id, ordre, id),

    -- CASCADE, contrairement au reste du projet : une ligne n'a aucune existence hors de sa
    -- facture, et supprimer un brouillon doit emporter son contenu. Une facture EMISE, elle,
    -- ne se supprime pas — c'est le statut qui l'interdit, pas la cle etrangere.
    CONSTRAINT fk_facture_lignes_facture FOREIGN KEY (facture_id)
        REFERENCES factures (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
