← Retour au portfolio
ERP de gestion commerciale et de stock, produit SaaS multi-entreprises

SaraSoft

ERP de gestion commerciale et de stock, conçu pour des commerçants dont le réseau tombe pendant des jours, puis recadré en produit international vendu en SaaS. Trois déploiements pour une seule base : backend Django/DRF, application de gestion, boutique en ligne. Environ 60 000 lignes côté backend, 48 000 pour la gestion, 10 000 pour la vitrine, 176 migrations et 107 fichiers de tests.

Type de projet
ERP en SaaS multi-entreprises, en développement actif
Périmètre
Backend Django/DRF, application de gestion, boutique en ligne — trois déploiements, une seule base
Volume
~60 000 lignes backend · ~48 000 lignes de gestion · ~10 000 lignes de vitrine · 176 migrations · 107 fichiers de tests
Stack technique
Django 5, DRF, Next.js 16 / React 19, PostgreSQL (Neon), Tigris, WebAuthn, IndexedDB, service worker, Lengo Pay, Fly.io
SaraSoft

Rien ne se configure

Les utilisateurs visés sont peu à l'aise avec un téléphone, parfois analphabètes : tout se déduit des données plutôt que de se régler. Un client mono-site ne voit jamais le mot « site ». Et aucune décision d'un client particulier n'est figée dans le code — devise, fuseau, TVA et numérotation sont des attributs, dont le défaut reproduit l'existant.

Périmètre fonctionnel

Douze applications Django : catalogue et conditionnements, stock multi-sites avec lots, transferts et inventaires, chaîne de vente complète (devis → commande → bon de livraison → facture → règlement → avoir), achats, clients, dépenses, trésorerie, comptabilité OHADA (exercices, journaux, écritures, lettrage), et l'abonnement au produit lui-même avec paiement mobile Orange Money / MTN.

Trois axes d'accès, jamais mélangés

Ai-je le droit ? Quelles données me concernent ? L'entreprise a-t-elle payé ? Trois questions distinctes, trois mécanismes, trois réponses : 403 pour une permission refusée, 404 ou liste filtrée pour un périmètre, 402 pour un module hors formule. Répondre 403 à un module non souscrit enverrait l'administrateur cocher des cases sans effet. Le périmètre s'applique en écriture autant qu'en lecture, et un périmètre vide ne donne accès à rien.

Cloisonnement à quatre verrous

Un gestionnaire qui filtre par entreprise, un refus en écriture hors entreprise, la sécurité au niveau ligne posée par migration PostgreSQL, et un test de surface qui remonte de chaque route au modèle qu'elle sert pour exiger la clé d'entreprise. Le quatrième existe parce qu'un modèle dépourvu de cette clé ne donnait aux trois premiers rien à quoi s'accrocher — sans qu'aucun ne proteste.

Hors ligne, sans rien demander

La clé de données est tirée à 256 bits, jamais dérivée d'un mot de passe, et enveloppée par une clé non extractible propre à l'appareil : un profil de navigateur copié ne rend que du chiffré. Vingt-deux ressources sont répliquées chiffrées sur l'appareil, et rien n'est demandé à l'utilisateur pour travailler hors ligne — la protection ne doit rien lui coûter. L'empreinte digitale sert de portail, pas d'identifiant.

Un journal d'intentions, pas d'états

La file de synchronisation transporte ce que l'utilisateur a voulu faire : créer puis supprimer une fiche hors ligne s'annule de soi-même, le numéro de pièce sert de clé d'idempotence, un refus attend une décision humaine, et un silence du réseau n'est jamais interprété comme un refus.

Une boutique sans panier

La vitrine ne gère ni panier ni commande : elle met l'acheteur en relation avec le vendeur par un lien WhatsApp pré-rempli, et la transaction se noue dans la conversation. Le numéro de l'acheteur n'atteint donc jamais le serveur — aucune donnée personnelle d'acheteur n'existe dans ce produit. Ce qui se compte est le geste : vu, puis contacté. Une mise en relation n'est pas une vente, et aucun écran ne laisse croire le contraire.

Django 5DRFNext.js 16React 19PostgreSQL / NeonRLSRBACComptabilité OHADAWebAuthnIndexedDBService workerTigrisLengo PayFly.io
SaraSoft — diagramme / capture