Abonnements — modifications à porter dans l'analyse

Huit modifications. Aucune rubrique existante n’est supprimée, renommée ni retypée ; aucune contrainte n’est resserrée.
Auteur Michael  ·  Modifications de l'analyse Serge  ·  Adaptation du code Franck
Date 2 septembre 2026  ·  État constaté analyse WEBDEV 2024, captures du 25 août 2026
Révisions 26 août — remplissage différé du lien vers la facture  ·  2 septembre — StripeSubscriptionsObjectsIoT.IDStripeInvoices retirée sur objection de Franck, IDObjectsIoT ajoutée au fichier de détails, alimentation au mouvement
Cadrage Le prorata reste calculé au moment du mouvement et stocké dans StripeSubscriptionsObjectsIoT. Deux fichiers sont créés, tous deux demandés en mai. Les huit modifications complètent l’analyse : elles ajoutent ce qui manque, elles ne remplacent rien.

Côté code, une seule chose change vraiment : le fichier de détails est écrit par le portail à chaque mouvement, là où l’information existe. La tâche planifiée ne fait plus que lire et consolider — elle ne recalcule aucun prorata.

1. D'où vient chaque modification

Cinq modifications sur huit sont des demandes de Franck, formulées en mai et reprises ici dans ses termes. Les trois 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 8, 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, Quantity, StartDate, EndDate »
→ modification 7, 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, Cancelled
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
4StripeSubscriptions.EndDate nullable Absence de date = abonnement en cours sans terme connu.
5StripeInvoices.IDStripeSubscriptions nullable Une facture de pack SMS n'a pas d'abonnement.
6Index composé sur SSO C'est le parcours exact de la facturation mensuelle.

2. Ce que ces modifications apportent

3. Les huit 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.Cancelled à 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 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.

5 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.

6 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.

7 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
Onze rubriques : les dix arrêtées le 22 mai, plus IDObjectsIoT ajoutée le 27 août.
IDStripeInvoicesDetails   Id. automatique, clé primaire
IDStripeSubscriptions     FK → abonnement du gestionnaire
IDStripePrices            FK → prix appliqué
IDObjectsIoT              FK, valeur vide autorisée → la jauge concernée
Description               Texte  (libellé interne de la ligne)
Quantity                  Numérique SIGNÉ
StartDate                 Date   (début de la période couverte)
EndDate                   Date   (fin de la période couverte)
Amount                    Monétaire SIGNÉ
Processed                 Booléen, défaut FALSE
IDStripeInvoices          FK, valeur vide autorisée → facture émise
Granularité
IDObjectsIoT est renseignée quand la ligne concerne une jauge — plan de connectivité, prorata — et vide sinon : abonnement de base, dépassement consolidé, forfait portant sur tout le parc. Pour le prorata, la facture Stripe ne porte qu’une seule ligne consolidée, là où ce fichier reçoit une ligne par jauge, avec son prix, sa période et son montant. C’est ce qui permet de répondre à « ce montant de prorata correspond à quoi ? » sans reconstituer le calcul.

Le pointeur vise ObjectsIoT et non la ligne d’abonnement, parce que le forfait device se facture aussi sur les jauges en stock, qui n’ont aucune ligne dans StripeSubscriptionsObjectsIoT.

Clés
IDStripeSubscriptions, IDStripeInvoices et IDObjectsIoT, toutes avec doublons, plus deux clés composées : (IDStripeSubscriptions, StartDate) pour le rapprochement différé de la facture, et (IDStripeSubscriptions, Processed) pour retrouver les cycles non traités.
Mode d’alimentation — écrit par le portail, à chaque mouvement Le portail écrit une ligne au moment où le mouvement a lieu : souscription, activation d’un plan, changement de palier, retour en stock. C’est le plan de mai, et c’est ce que décrit le workflow portail de Franck du 28 août.

Trois raisons le justifient. On écrit au moment où l’information existe, sans la reconstituer plus tard. La tâche planifiée — la pièce fragile du dispositif — s’en trouve allégée : elle lit et consolide, une requête suffit, et elle ne recalcule jamais un prorata. Enfin, si un cycle n’est pas facturé, les lignes existent déjà en base : le manque devient constatable et rattrapable au lieu de disparaître.

La règle qui va avec : le prorata est calculé une fois, au mouvement. Toute consolidation qui le recalculerait le compterait deux fois.

8 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 le portail, à la souscription : la ligne naît avec l’abonnement. La date de fin et le motif sont renseignés plus tard, quand l’abonnement se termine. Une ligne créée au départ ne peut pas être oubliée par une tâche qui ne s’exécute pas.
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 là que se concentre le travail : la modification 1 change la logique de clotûre, et c’est aussi ici que le fichier de détails est alimenté.

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 Cancelled 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é.
Et écrire le détail
Chaque mouvement produit sa ligne dans StripeInvoicesDetailsProcessed à FALSE, IDStripeInvoices vide, IDObjectsIoT renseignée quand une jauge est concernée. C’est l’écriture qui remplace le travail de reconstitution à la facturation.
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, Cancelled = 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 · Tâche planifiée — consolidation à l’anniversaire

Elle s’allège : les lignes qu’elle devait produire existent déjà en base.

Lire
Relever les lignes de détail non traitées de l’abonnement pour la période échue. Une requête suffit — c’est le rôle de l’index composé de la modification 6.
Consolider
Sommer, sans jamais recalculer un prorata. Le calcul a eu lieu une fois, au mouvement.
Pousser
Envoyer les lignes sur l’abonnement Stripe, puis basculer Processed à TRUE. IDStripeInvoices reste vide : la facture naît de cet envoi et se rapproche plus tard, sur l’abonnement et la période.
Clore
Renseigner la date de fin et le motif sur la ligne d’historique lorsqu’un abonnement se termine.
Ce qu’elle écrit encore
Le dépassement de messages, qui n’est connu qu’à l’échéance : c’est la seule ligne de détail qu’elle produit elle-même.

C · Ce qui ne bouge pas

5. Deux points à clarifier

Aucun des deux ne bloque les huit 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 huit 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 la modification 7. À planifier séparément : sans cet écran, le détail écrit en base n’est consultable par personne.
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.

Le risque est atténué par l’écriture au mouvement : les lignes d’un cycle manqué sont déjà en base, datées et complètes. Le manque reste invisible pour le client, mais il devient constatable et rattrapable. Il n’est pas supprimé pour autant : les trois garde-fous restent à concevoir.

7. Récapitulatif

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

Deux rubriques, un index et deux fichiers à créer ; deux propriétés à élargir et une cardinalité à modifier. Aucune contrainte n’est resserrée, donc aucun contrôle de données préalable. L’analyse est ouverte une seule fois.