Abonnements — modifications à porter dans l'analyse

Neuf modifications. Aucun changement du fonctionnement actuel, aucune réécriture du code existant.
Auteur Michael  ·  Modifications de l'analyse Serge  ·  Adaptation du code Franck
Date 25 août 2026  ·  État constaté analyse WEBDEV 2024, captures du 25 août 2026
Cadrage La mécanique de facturation en place ne change pas : le prorata continue d'être calculé au moment du mouvement et stocké dans StripeSubscriptionsObjectsIoT, la consolidation continue de s'appuyer sur ce fichier. Deux fichiers sont créés, tous deux demandés en mai. Les neuf modifications complètent l'analyse : elles ajoutent ce qui manque, elles ne remplacent rien.

1. D'où vient chaque modification

Cinq modifications sur neuf sont des demandes de Franck, formulées en mai et reprises ici dans ses termes. Les quatre autres sont des propositions issues d'une relecture du modèle de données.

Demandé par Franck

Franck — 21 mai, 13 h 31 « oui il faut créer un fichier historique »
→ modification 9, StripeSubscriptionsHistory
Franck — 22 mai, 13 h 58 « pour l'analyse je pense qu'il y aura juste un fichier qui va alimenter les détails avec IDStripeSubscriptions, Descriptions, IDPrix, Qte, PeriodeDebut, PeriodeFin »
→ modification 8, StripeInvoicesDetails — nom choisi par Franck le même jour à 14 h 54, structure complétée à 14 h 52 et validée par lui
Franck — 22 mai, 15 h 01 « il faut ajouter un flag dans SSO pour les soft delete, parce que si il y a résiliation, les jauges ne devraient plus être dans SSO après pour ne pas être pris en compte dans la prochaine facturation »
→ modification 3, IsResilie
Franck — 22 mai, 15 h 17 « je pense qu'il faut quand même ajouter l'IDPrix dans SSO sans ça on ne sait pas l'historique de changement de plan »
→ modification 2, IDStripePrices
Franck — 22 mai, 15 h 25 « notre liaison objet vers SSO est 1,1 il faut mettre à 1,n »
→ modification 1, la cardinalité

Proposé à partir d'une relecture du modèle

ObjetMotif
4SSO.IDStripeInvoices Sans elle, une facture contestée n'est pas justifiable a posteriori. Seule modification qui n'était pas envisagée en mai.
5StripeSubscriptions.EndDate nullable Absence de date = abonnement en cours sans terme connu.
6StripeInvoices.IDStripeSubscriptions nullable Une facture de pack SMS n'a pas d'abonnement.
7Index composé sur SSO C'est le parcours exact de la facturation mensuelle.

2. Ce que ces modifications apportent

3. Les neuf modifications

Dans l'ordre d'exécution. Les modifications portant sur StripeSubscriptionsObjectsIoT servent les deux modèles de client — gestionnaire mensuel et client final annuel — ce fichier étant commun aux deux.

1 Cardinalité ObjectsIoT ↔ StripeSubscriptionsObjectsIoT à modifier

État actuel
Dans la fenêtre de description de la liaison, la réponse à « chaque objectsiot peut avoir plusieurs stripesubscriptionsobjectsiot » est Non, soit (0,1) côté ObjectsIoT.
Cible
Basculer cette réponse sur Oui. Côté ObjectsIoT, la cardinalité passe de (0,1) à (0,n) ; côté StripeSubscriptionsObjectsIoT elle reste (1,1). La clé IDObjectsIoT passe d'unique à clé avec doublons.
Minimum
Le minimum reste à zéro, pas à un : une sonde en stock, en test ou non assemblée n'a aucune ligne d'abonnement, et c'est normal.
Motif
Une jauge accumule plusieurs lignes au fil de sa vie : une à l'activation, une résiliation à la sortie, une nouvelle à chaque changement de plan. Avec la cardinalité actuelle, seule la dernière peut exister.

2 Ajouter StripeSubscriptionsObjectsIoT.IDStripePrices à créer

État actuel
Absente.
Cible
Numérique (8), obligatoire, clé étrangère vers StripePrices.
Motif
Mémorise le prix appliqué à cette jauge sur cette période. Un prix Stripe étant immuable — toute évolution tarifaire crée un nouveau prix au lieu de modifier l'ancien — le pointeur suffit à garantir l'historique, sans recopier de montant.

3 Ajouter StripeSubscriptionsObjectsIoT.IsResilie à créer

État actuel
Absente.
Cible
Booléen, obligatoire, valeur par défaut FALSE.
Motif
Clôt une ligne sans la supprimer. À la sortie d'une jauge ou lors d'un changement de plan, la ligne existante reçoit sa date de fin et passe à TRUE ; une nouvelle ligne est créée le cas échéant.
Dépend de
La modification 1.

4 Ajouter StripeSubscriptionsObjectsIoT.IDStripeInvoices à créer

État actuel
Absente. Aucun lien n'existe entre une ligne d'abonnement et la facture qui l'a portée.
Cible
Numérique (8), valeur vide autorisée, clé étrangère vers StripeInvoices, renseignée à la consolidation en même temps que le passage du drapeau de traitement.
Motif
Permet de répondre à « quelles jauges composent la facture de juin ? ». Sans elle, une facture contestée n'est pas justifiable a posteriori : le drapeau de traitement indique qu'une ligne a été facturée, mais pas sur quelle facture.

5 StripeSubscriptions.EndDate — accepter la valeur vide à modifier

État actuel
Valeur vide non autorisée, pas de valeur par défaut.
Cible
Autoriser la valeur vide, sans valeur par défaut.
Motif
Cette date n'est renseignée que lorsqu'un client met fin à son abonnement. Absence de date = abonnement en cours sans terme connu. C'est la sémantique de l'API Stripe, dont le champ équivalent est nullable.
Impact
Aucun sur les données : la contrainte est élargie.

6 StripeInvoices.IDStripeSubscriptions — accepter la valeur vide à modifier

État actuel
Valeur vide non autorisée, valeur par défaut 0.
Cible
Autoriser la valeur vide et retirer la valeur par défaut à 0. L'absence d'abonnement se code par une valeur vide, pas par un zéro qui pointerait vers un abonnement inexistant.
Motif
Une facture peut exister sans abonnement associé : cas du pack SMS one-shot, rattaché par IDStripePurchaseSMS, avec IDCustomer en lien direct vers le client.
Impact
Aucun sur les données : la contrainte est élargie.

7 Index composé sur StripeSubscriptionsObjectsIoT à créer

État actuel
Absent. Toutes les clés du fichier portent sur une seule rubrique.
Cible
Index composé sur (IDStripeSubscriptions, Processed).
Motif
C'est le filtre exact de la consolidation mensuelle : somme des montants non encore facturés pour un abonnement donné. Sans index, ce parcours se dégrade à mesure que le parc grandit.

8 Créer StripeInvoicesDetails à créer

Le détail d'une facture consolidée. Décidé le 22 mai sur proposition de Franck, qui l'a également nommé. Réaffirmé le 10 juin.

État actuel
Absent de l'analyse.
Motif
Conserver la composition de chaque facture émise : les lignes de plan, le forfait device et son nombre de jauges, l'abonnement de base, l'ajustement de prorata. Aucun de ces éléments n'est stocké aujourd'hui — le détail n'existe que dans le PDF Stripe, donc consultable mais non exploitable. Sert également d'historique de facturation par gestionnaire.
Structure
Les 10 rubriques arrêtées le 22 mai, inchangées :
IDStripeInvoicesDetails   Id. automatique, clé primaire
IDStripeSubscriptions       FK → abonnement du gestionnaire
IDStripePrices              FK → prix appliqué
Description                 Texte  (libellé interne de la ligne)
Qte                         Numérique SIGNÉ
PeriodeDebut                Date   (début du cycle facturé)
PeriodeFin                  Date   (fin du cycle facturé)
MontantProrata              Monétaire SIGNÉ
Processed                   Booléen, défaut FALSE
IDStripeInvoices            FK, valeur vide autorisée → facture émise
Granularité
Une ligne par ligne de facture pour tout ce qui n'est pas lié à une jauge — abonnement de base, forfait device. Pour le prorata, la facture Stripe ne porte qu'une seule ligne consolidée, mais ce fichier reçoit une ligne par jauge, avec son prix, sa période et son montant. Le numéro de série s'obtient par jointure, il n'y a pas de rubrique à ajouter pour cela. C'est ce qui permet de répondre à « ce montant de prorata correspond à quoi ? » sans reconstituer le calcul.
Index
IDStripeSubscriptions pour retrouver l'historique d'un gestionnaire, et IDStripeInvoices pour retrouver le détail d'une facture.
Mode d'alimentation — la seule différence avec le plan de mai Le plan de mai prévoyait d'écrire une ligne à chaque mouvement : ajout de jauge, retour en stock, changement de plan. Cela impliquait de modifier le code de mouvement, ce qui n'a jamais été fait. Ici, le fichier est écrit une fois par cycle, au moment de la facturation, comme copie du détail émis. Le fichier, sa structure et sa valeur d'audit sont identiques ; seul le moment de l'écriture change, et le code de mouvement reste inchangé. Dans ce mode, Processed vaut TRUE dès l'écriture et IDStripeInvoices est renseignée immédiatement. Les rubriques sont maintenues telles quelles afin de garder ouverte la possibilité de passer plus tard à une alimentation au mouvement.

9 Créer StripeSubscriptionsHistory à créer

L'historique au niveau abonnement. Demandé par Franck le 21 mai, structure arrêtée le même jour, jamais créé.

État actuel
Absent de l'analyse.
Motif
Répondre à « quel abonnement ce client a-t-il eu, à quel prix, de quand à quand, et pourquoi s'est-il terminé ». Le fichier StripeSubscriptionsObjectsIoT porte le détail jauge ↔ abonnement ; celui-ci porte le niveau au-dessus. Les deux sont complémentaires.
Structure
Les 7 rubriques arrêtées le 21 mai :
IDStripeSubscriptionsHistory   Id. automatique, clé primaire
IDCustomer                     FK → client
IDStripeSubscriptions          FK historique — peut pointer vers une ligne supprimée
IDStripePrices                 FK → prix appliqué
StartDate                      Date
EndDate                        Date
EndReason                      Texte libre  (trop cher, plus besoin, déménagement…)
Alimentation
Par la tâche planifiée, au moment où un abonnement se termine.
Pourquoi le créer maintenant plutôt que plus tard Ce fichier était classé « à faire en fin de chantier ». Sa structure est arrêtée, il est vide au départ et n'impacte aucune donnée existante : c'est la modification la moins coûteuse de la liste. Le reporter signifie rouvrir l'analyse une fois de plus, alors que l'objectif de cette passe est précisément de tout grouper en une seule ouverture.

4. Ce que cela implique dans le code

Une fois les modifications portées dans l'analyse. Rien de ce qui fonctionne aujourd'hui n'est remis en cause.

A · Code de mouvement — ajout, retrait, changement de plan d'une jauge

C'est le seul endroit où la logique change, du fait de la modification 1.

Aujourd'hui
Une jauge ne pouvant porter qu'une seule ligne, un changement de plan se fait nécessairement par mise à jour de la ligne existante. L'état précédent est écrasé.
Après
La ligne existante est close — sa date de fin est posée et IsResilie passe à TRUE — et une nouvelle ligne est créée. L'historique se conserve au lieu de s'écraser.
En plus
Renseigner IDStripePrices à chaque création de ligne, avec le prix effectivement appliqué.
Une garantie disparaît et doit être reprise dans le code Aujourd'hui, la clé unique sur IDObjectsIoT garantit à elle seule qu'une jauge n'a qu'une seule ligne d'abonnement. Après la modification 1, cette garantie n'existe plus au niveau du fichier. La règle à tenir devient : une jauge n'a jamais plus d'une ligne non résiliée à un instant donné — unicité sur (IDObjectsIoT, IsResilie = FALSE). Toute création de ligne doit donc être précédée de la clôture de la ligne active, dans la même opération. Sans ce contrôle, une jauge peut se retrouver facturée deux fois sur le même cycle sans qu'aucune erreur ne remonte.

B · Consolidation mensuelle

Trois écritures à ajouter dans un traitement qui existe déjà. Aucune logique existante n'est modifiée.

Ajout 1
Renseigner IDStripeInvoices sur les lignes facturées, au même moment que le passage du drapeau de traitement.
Ajout 2
Écrire les lignes du fichier de détails. La tâche parcourt déjà les lignes d'abonnement une par une pour calculer la somme des proratas : il s'agit d'écrire une ligne au passage plutôt que le seul total. Aucun appel Stripe supplémentaire, la facture émise ne change pas.
Ajout 3
Écrire une ligne d'historique d'abonnement lorsqu'un abonnement se termine.

C · Ce qui ne bouge pas

5. Deux points à clarifier

Aucun des deux ne bloque les neuf modifications ci-dessus.

Qu'est-ce qui tourne aujourd'hui en production ?question à Franck

Le passage à la facture consolidée du gestionnaire était en développement le 10 juin, une partie en local. Trois points restent à confirmer avant d'écrire quoi que ce soit :

Traitement du prorata à la sortie d'une jaugedécision commerciale

Une jauge retirée en cours de cycle donne-t-elle lieu à une correction proratisée, ou le cycle entamé est-il dû en entier ? La question a été posée le 22 mai et n'a jamais été tranchée. La seconde règle supprimerait les lignes négatives et le calcul associé, le prorata restant appliqué à l'entrée. Sans effet sur les neuf modifications ; à trancher avant d'engager du développement sur ce point.

6. Hors périmètre de cette passe

Identifié, non traité ici.

SujetNatureStatut
Supervision de la tâche planifiée — alerte en cas de non-exécution, réconciliation, rattrapage d'un cycle manquéDéveloppement Ouvert depuis le 10 juin. Voir encadré ci-dessous.
Rejeu de la tâche planifiée — relancer la tâche ne doit pas produire une seconde factureDéveloppement Se règle en vérifiant qu'aucune facture n'existe déjà pour ce gestionnaire sur cette période — les dates sont déjà présentes dans StripeInvoices
Écran de consultation du détail d'une facture au portailDéveloppement Rendu possible par les modifications 4 et 8. À planifier séparément — aucune page du portail n'est touchée par cette passe.
Transfert d'une sonde d'un client à un autreRègle métier Soulevé le 21 mai, jamais traité. La modification 1 le rend techniquement possible.
Fichier StripePlansAnalyse Il stocke le prix qu'utilise un abonnement. La modification 2 fait la même chose à un grain plus fin. Laissé en l'état, mais ne rien construire dessus.
Point le plus lourd du dossier, sans lien avec l'analyse Dans la version cible, seul l'abonnement de base est prélevé automatiquement : le forfait device, les plans et les proratas dépendent entièrement de la tâche planifiée. Si elle échoue ou ne tourne pas, la facturation est silencieusement incomplète — sans erreur visible et sans réclamation client. Trois garde-fous à concevoir : une alerte, une réconciliation par cycle, un rattrapage. StripeInvoices portant déjà les dates de début et de fin de période, vérifier qu'une facture n'existe pas déjà pour ce gestionnaire sur cette période suffit à rendre la tâche rejouable sans risque de double facturation.

7. Récapitulatif

FichierObjetNature
1ObjectsIoT ↔ StripeSubscriptionsObjectsIoTCardinalité et unicitéModification
2StripeSubscriptionsObjectsIoTIDStripePricesCréation
3StripeSubscriptionsObjectsIoTIsResilieCréation
4StripeSubscriptionsObjectsIoTIDStripeInvoicesCréation
5StripeSubscriptionsEndDate — valeur vide autoriséeModification
6StripeInvoicesIDStripeSubscriptions — valeur vide autoriséeModification
7StripeSubscriptionsObjectsIoTIndex (IDStripeSubscriptions, Processed)Création
8StripeInvoicesDetailsDétail de facture (10 rubriques)Création
9StripeSubscriptionsHistoryHistorique d'abonnement (7 rubriques)Création

Trois rubriques, un index et deux fichiers à créer ; trois propriétés à modifier. L'analyse est ouverte une seule fois.