Guide utilisateur de l'application web. Cette plateforme gère toute l'activité d'une entreprise artisanale : clients, chantiers, catalogue, devis (avec PDF et envoi par email), relevés de terrain, et administration. Les données sont isolées par entreprise.
http://localhost:8000/).Sur cette page de connexion : liens publics « Confidentialité » (politique RGPD) et « Mentions légales » — accessibles sans compte.
Les comptes sont créés par l'administrateur de l'entreprise (voir §14) ou via la
commande de démonstration seed_demo. Les identifiants des comptes de
démonstration partagés sont communiqués par l'éditeur sur demande —
ils ne figurent ni dans cette documentation, ni dans sa version en ligne
(enregistrez-les dans votre gestionnaire de mots de passe).
Comptes de démonstration verrouillés : le mot de passe de ces comptes reste fixe et ne peut pas être réinitialisé (ni depuis Administration → Membres, ni depuis l'espace éditeur). Le verrou est porté par le champ
User.is_demo(posé parseed_demo) et appliqué parset_user_password, unique point de réinitialisation de l'application. Ainsi la démo reste utilisable par les futurs testeurs.Démo isolée pour un prospect : au lieu du compte partagé ci-dessus, créez une entreprise dédiée (métier activé, scénario de projection — chantier daté et adressé, donc navigation Waze/Google Maps au planning, relevé guidé, devis à relancer, admin au vrai email du prospect, mot de passe de première connexion) :
manage.py seed_prospect --company "Dupont Menuiserie" --trade menuiserie --email j.dupont@dupont.fr --pdf— 1 prospect = 1 commande (entreprise isolée) ; l'optiondocs/QUOTELUI-demo-<entreprise>.pdf: accès, parcours de découverte, métier) à joindre au message d'envoi. Avec--send-email, le PDF est généré et l'email d'accès part immédiatement au prospect (pièce jointe), envoyé depuis la boîte configurée dans Réglages → Envoi (option--smtp-from "Nom entreprise"pour choisir la boîte, sinon la première active) — journalisé dans le journal des emails. Ajoutez--resetpour repartir d'un scénario neuf sans changer les identifiants ;manage.py demo_brief --company … [--password …]régénère le PDF à tout moment. La boîte d'envoi est choisie dans cet ordre :--smtp-from "Nom entreprise", sinon la boîte éditeur (Éditeur → Réglages, si active), sinon la première boîte d'entreprise active. Si le verrou de licences est actif (LICENSE_SECRET), le seed génère automatiquement la clé du métier (valable 1 an) — le module reste actif dans l'administration sans saisie manuelle. Voirdocs/deployment.md§3 ter (livraison / SaaS multi-client).
QUOTELUI est une application web installable (Progressive Web App) :
localhost).
Sur le réseau local en http://, le service worker n'est pas activé — utilisez
la version déployée en HTTPS (§ déploiement).Un même compte peut appartenir à plusieurs entreprises.
Le rôle affiché dans le menu correspond à celui que vous avez dans l'entreprise sélectionnée (le rôle global ne s'applique que si aucun rôle d'entreprise n'est défini).
Les actions sont contrôlées côté serveur (masquer un bouton ne suffit jamais). La hiérarchie des rôles est cumulative.
| Rôle | Peut faire |
|---|---|
| LECTEUR | Consulter le catalogue, les clients, les chantiers, les relevés et les devis. |
| TECHNICIEN | Tout « LECTEUR » + saisir les relevés de terrain (mesures, champs spécifiques, photos, observations) et les valider. |
| COMMERCIAL | Tout « TECHNICIEN » + créer / modifier / gérer les clients, créer, éditer, envoyer et réviser les devis, créer/envoyer les factures (V2.1), les bons de livraison (V2.10) et le pipeline d'affaires / relances (V2.11). |
| MANAGER | Tout « COMMERCIAL » + gérer le catalogue (types de produits, attributs), les prix/TVA, le stock, les fournisseurs et les commandes fournisseurs, et consulter l'audit. |
| ADMIN | Tout « MANAGER » + gérer les membres, les modules métiers et les réglages de l'entreprise. |
Le menu s'adapte à votre rôle : un Technicien ne voit pas « Clients » ni « Devis », mais voit « Relevés », « Catalogue » et ses alertes.
| Entrée | Rôles | Description |
|---|---|---|
| Tableau de bord | Tous | Vue adaptée au rôle (voir §5). |
| Devis | ≠ Technicien | Liste et création des devis (§10). |
| Factures | ≠ Technicien | Devis acceptés → factures, états, PDF/email (§10 bis). |
| Clients | ≠ Technicien | Liste et gestion des clients (§7). |
| Chantiers | ≠ Technicien | Liste et gestion des chantiers/projets (§8). |
| Relevés | Tous | Relevés de chantier, workflow terrain → bureau (§11). |
| Catalogue | Tous | Produits et types de produits (§9). |
| Stock | Tous (mouvements : Manager+) | Suivi des quantités, seuils, mouvements (§13 quater). |
| Fournisseurs | Tous (gestion : Manager+) | Fiches fournisseurs et tarifs d'achat (§13 quinquies). |
| Commandes | Tous (gestion : Manager+) | Commandes fournisseurs, réception → stock (§13 quinquies). |
| Livraisons | Tous (gestion : Commercial+) | Bons de livraison client, sortie de stock (§13 sexies). |
| Affaires | Tous (gestion : Commercial+) | Pipeline commercial, relances CRM (§13 septies). |
| Planning | Tous | Calendrier semaine des chantiers, relevés, RDV et livraisons, avec conflits par technicien (§13 quattuordecies). |
| Recherche | Tous | Recherche globale multi-objets (§6). |
| Alertes (+ badge) | Tous | Rappels automatiques (§6 bis). |
| Statistiques | Manager+ | Pilotage commercial (§5 bis). |
| Historique | Manager+ | Journal d'audit (§15). |
| Réglages | Admin | Paramètres de l'entreprise (§13). |
| Administration → Membres | Admin | Comptes et rôles de l'entreprise (§14). |
| Administration → Modules | Admin | Activation des modules métiers (§14 bis). |
En bas du menu : la liste des entreprises, votre nom et votre rôle, et le bouton de déconnexion (↪).
Sur mobile, le menu se replie ; le bouton ☰ en haut à droite l'ouvre.
Vue d'accueil adaptée au rôle :
Chaque bloc renvoie vers la liste filtrée qu'il annonce (§23) : « CA devis » ouvre les devis envoyés + acceptés avec le Total TTC du filtre affiché, « Montant en attente » les seuls devis envoyés, « Brouillons » tous les états de travail (brouillon, à compléter, en révision, prêt), « Expirent sous 7 j » les devis dont la validité se termine dans la semaine, « À relancer » vos devis envoyés depuis plus de 30 jours, « Relevés incomplets » les relevés sans aucun élément et « Photos manquantes » ceux sans photo — les compteurs affichés sont donc toujours les mêmes d'un côté comme de l'autre. La liste des devis affiche, quand un filtre est actif, son Total TTC ; les puces de statut se combinent avec « Mes devis » (portée commerciale).
Statistiques dans le menu (Manager+) agrège l'activité commerciale de l'entreprise sur les 12 derniers mois :
Détails techniques : les brouillons, modes intermédiaires et devis annulés / expirés sont exclus des CA et des ratios ; les devis refusés sont comptés dans la transformation mais jamais dans le CA.
Une seule zone de recherche interroge tous les objets : clients, contacts, chantiers, devis, produits, documents et relevés.
L'application surveille en continu et affiche un badge orange avec le nombre de rappels. Rien à configurer : les règles sont celles du cahier des charges.
| Type d'alerte | Règle |
|---|---|
| Devis à relancer | Devis envoyé depuis plus de 30 jours sans réponse. |
| Devis expirant | Validité qui arrive à échéance (< 7 jours). |
| Devis expiré | Devis envoyé dont la date de validité est dépassée (ou déjà passé à EXPIRE). Un brouillon n'expire jamais. |
| Factures non réglées | Facture dont l'échéance est dépassée (montant affiché). |
| Stock bas / rupture | Produit suivi dont le solde est ≤ seuil (rouge si rupture, ≤ 0). |
| Relances en retard | Activité commerciale (appel, email…) à faire dont l'échéance est dépassée. |
| Conflit de planning | Un technicien a 2+ interventions le même jour sur les 14 prochains jours (chantier + relevé + RDV…). |
| Relevé incomplet | Relevé en TERRAIN sans éléments. |
| Photos manquantes | Relevé en TERRAIN/VALIDE sans photo. |
Ouvrez Alertes : chaque ligne mène à la liste filtrée correspondante (devis expirés, factures en retard, factures en attente d'acquittement ou rejetées par la plateforme, stock à réapprovisionner, relevés sans photos…), qui affiche exactement le nombre annoncé — le compteur de l'alerte, le KPI du tableau de bord et la liste ont la même définition.
Un devis envoyé dont la validité est dépassée apparaît en rouge dans la
liste (badge « expiré ») et sur sa fiche (bandeau « Validité dépassée ») :
pour prolonger la validité, remettez-le en révision (nouvelle version) ou
relancez-le tel quel (même PDF figé). La commande périodique
manage.py expire_quotes (facultative, §48) aligne le statut sur EXPIRE —
l'affichage est identique avant et après.
Address
lié en billing_address), n° TVA intracommunautaire (normalisé en
majuscules) et SIRET optionnel (Customer.siret, migration
customers/0002 — validé : exactement 14 chiffres).set_billing_address(customer, …) crée l'adresse à la demande, la met à
jour sur place (pas de doublon) et la retire si tous les champs sont
vides._snapshot_customer) emporte
address / vat_number / siret : les PDF de devis et de facture
affichent « TVA : … » / « SIRET : … » quand renseignés — figés à l'envoi,
jamais rétro-impactés sur les versions antérieures.La gestion des clients est réservée au Commercial et au-dessus.
Un chantier est un projet rattaché à un client : lieu d'intervention, dates.
Le catalogue est entièrement dynamique : chaque type de produit (fenêtre, chaudière, radiateur…) possède ses propres attributs (largeur, débit, puissance, matériau, énergie…).
nom, reference, type, unite, prix_achat, prix_vente,
ref_fournisseur, description (en-têtes tolérants aux accents et à la
casse ; séparateur ; ou ,). Un modèle CSV est téléchargeable. Options :
mettre à jour les produits existants (même référence), créer les types de
produits manquants, et enregistrer le prix d'achat chez un fournisseur
choisi. Un rapport indique les créations, mises à jour et erreurs par ligne.DEMO-…) ; « Retirer »
les archive (les devis déjà établis restent intacts). Équivalent en
ligne de commande : manage.py seed_catalog_demo --company … [--module …]
(ou --remove).Catalogue ≠ stock : le catalogue est un référentiel d'offre (ce que vous pouvez proposer, y compris des produits fournisseurs, avec leurs prix). Il ne représente pas votre inventaire : pour suivre ce que vous détenez réellement, utilisez le Stock (§13 quater), qui ne suit que les produits que vous choisissez de suivre.
Les données de base (types, attributs, champs de relevé) sont injectées automatiquement à l'activation d'un module métier (§14 bis). Vous pouvez aussi créer vos propres types de produits sans aucune programmation.
DEV-2026-0001, séquence propre à l'entreprise).update_line recalcule les
totaux ; la TVA figée et le produit lié restent intacts. Les mêmes
opérations existent côté API (set_line_discount, remove_line).prix unitaire × quantité = total HT), donc traitée comme les autres par
la TVA, les remises, la facturation et l'export comptable.Le devis suit une machine d'état stricte :
BROUILLON → A_COMPLETER → EN_REVISION → PRET → ENVOYE → ACCEPTE / REFUSE / EXPIRE
↘ (révision) → nouvelle version
| État | Signification |
|---|---|
| BROUILLON | Créé, en cours de saisie. |
| A_COMPLETER | Vous signalez qu'il manque des informations. |
| EN_REVISION | Correction en cours. |
| PRET | Complet, prêt à l'envoi. |
| ENVOYE | Envoyé au client : le contenu est figé. |
| ACCEPTE / REFUSE / EXPIRE | Issue finale du devis. |
| ANNULE | Devis abandonné. |
Règle d'or : les états modifiables sont BROUILLON, A_COMPLETER, EN_REVISION, PRET. Dès l'envoi, le contenu est gelé (aucune modification silencieuse) ; pour le changer, repassez en EN_REVISION : une nouvelle version est créée (v1 → v2 → …), les versions précédentes restent consultables.
À l'envoi, remarque : l'email du destinataire et les coordonnées de l'entreprise sont pris dans la version figée, jamais dans les données vivantes — c'est une garantie de traçabilité.
Après envoi (contenu figé) :
Tant que le devis est en brouillon ou en état intermédiaire, le bouton PDF n'apparaît pas : son contenu n'est pas encore figé (§10.4). Une fois envoyé, le bouton devient disponible. Si l'URL du PDF est saisie directement sur un devis non envoyé, un message l'indique (aucune erreur serveur).
Sur la fiche du devis, l'historique des versions (v1, v2…) est consultable : chaque version montre le contenu exact envoyé à ce moment-là.
Bouton « Quantitatif / achat » sur la fiche du devis : le détail des quantités d'achat pour préparer les commandes fournisseurs — distinct du prix de vente.
Pour chaque ligne liée à un produit :
surface × couches ÷ rendement (m²/L) → litres ;60x120 → 0,72 m²/pièce) → m² ;nombre_au_m2, joints compris) → nombre de blocs ; ou surface ×
épaisseur (mm) → volume de béton (m³) pour une dalle ;recouvrement, éléments/m² posés) → nombre de tuiles / ardoises.
Sans la donnée métier (rendement, format, éléments/m², épaisseur,
recouvrement), la quantité du devis est conservée et la raison
s'affiche : aucune conversion inventée ;%) appliquée à la quantité d'achat —
jamais à la quantité facturée au client ;contenu d'un conditionnement, ex. 1,44 m² par carton,
2,5 L par pot) : le nombre de conditionnements est arrondi au supérieur
(un conditionnement partiel n'est pas vendu) ;Kits / ensembles : un produit composé (voir fiche produit → « Composition (kit) ») est éclaté dans la liste d'achat — chaque composant apparaît avec sa quantité (× quantité du kit), ses propres pertes et conditionnements, et le kit lui-même n'est pas commandé. Une fourniture présente à la fois dans un kit et sur une ligne séparée est signalée « déjà dans un kit » pour éviter la double commande (spec §5.2). Idéal pour une salle de bain complète, un tableau électrique ou un ensemble de chauffage.
Le tableau affiche théorique / perte / avec perte / conditionnements / à acheter / facturé, puis la liste des commandes à prévoir, agrégée par produit. Les lignes sans produit (prestations libres) sont listées mais n'entraînent aucune commande. Aucun montant ni quantité n'est inventé : sans rendement renseigné, la quantité de la ligne est conservée et la raison s'affiche.
Conformité comptable : les pertes, conditionnements, kits et conversions
métier ne concernent que les quantités d'achat. La quantité facturée
reste celle des lignes du devis (prix unitaire × quantité), et les exports
comptables (export factures, synoptique annuel, déclaration de TVA, bilan
recettes/dépenses) ne contiennent que des montants facturés, ventilés par taux
de TVA — jamais une quantité d'achat. Modifier un tarif produit ou un
conditionnement après l'établissement d'un devis/facture ne change aucun
montant déjà établi.
Ces paramètres se renseignent sur la fiche produit (formulaire de création)
ou à l'import catalogue (colonnes conditionnement, unite_achat,
perte, minimum). La composition d'un kit se gère sur la fiche du
produit : section « Composition (kit) » → choisir un composant + sa quantité
(le produit devient alors de nature composé).
La facturation est le prolongement naturel d'un devis accepté.
Menu Factures → + Nouvelle facture :
BROUILLON → PRETE → ENVOYEE → EN_RETARD → PAYEE
↘ (retour) ↘
BROUILLON ⇄ PRETE ANNULEE (à tout moment)
| État | Signification |
|---|---|
| BROUILLON | En cours de fabrication, modifiable. |
| PRETE | Prête à envoyer, modifiable. |
| ENVOYEE | Envoyée au client : le contenu est figé. |
| EN_RETARD | Échéance dépassée (passage automatique). |
| PAYEE | Règlement reçu. |
| ANNULEE | Annulée. |
Règle d'or : identique au devis. Editable uniquement en BROUILLON/PRETE. Dès ENVOYEE, le contenu est figé (PDF, totaux, TVA, destinataire). Pour la modifier, annulez et créez une nouvelle facture — c'est la garantie de traçabilité d'un document financier.
Après envoi : boutons PDF (téléchargement du document figé, mention d'échéance, numéro du devis d'origine) et Envoyer par email (PDF en pièce jointe, destinataire = la version figée, jamais le client vivant). Chaque envoi est journalisé (Emails) et tracé dans l'historique.
Le bouton PDF n'apparaît que lorsque le contenu est figé (dès ENVOYEE) : tant que la facture est en brouillon ou prête, elle n'a pas de PDF associé (même règle que le devis, §10.3).
Une facture EN_RETARD est mise en évidence (bandeau « RAPPEL » sur le PDF). La bascule ENVOYEE → EN_RETARD est automatique lorsque la date d'échéance est dépassée (commande/boucle de maintenance).
Pour une facture Envoyée ou En retard, le bouton Relancer (fiche ou liste de factures) envoie au client le même PDF figé avec l'objet « Relance de paiement : FAC-… ». La date de dernière relance est affichée sur la fiche, et l'envoi est journalisé dans Emails (historique de relances).
Si la facture n'est pas EN_RETARD alors que l'échéance est passée, la commande périodique s'en charge :
À planifier quotidiennement côté serveur (cron / systemd timer). Seules les entreprises actives sont affectées ; aucun ré-envoi intempestif grâce au délai.
Un point d'export réunit les documents destinés au comptable, accessible depuis Factures → Export comptable :
| Élément | Où | Contenu |
|---|---|---|
| Synoptique annuel (PDF) | bouton « Synoptique PDF » | Total HT/TVA/TTC par mois, ventilation par taux de TVA (base + TVA), encaissement (payées / en attente / en retard, montant TTC). Choisir l'année en haut de page. |
| Export CSV | bouton « CSV des factures » | Une ligne par facture : numéro, date émission, échéance, client, statut, HT, TVA, TTC, devis d'origine, devise. Séparateur ;, UTF-8 (compatible tableur). |
Règles : les factures annulées sont comptées mais exclues des montants
(coches * dans le PDF) ; les montants sont en Decimal arrondis à deux
décimales ; chaque ligne figeant sa TVA, une modification ultérieure d'un taux
ne change pas l'historique.
Périmètre : cet écran ne montre que le chiffre d'affaires émis par
l'application (factures QuoteliL). Un bandeau le rappelle et renvoie vers le
bilan complet (Documents tiers → Bilan, §18 quater) qui ajoute les pièces
importées et les factures fournisseurs — le calcul des factures est partagé
(annual_data) : sans pièce importée, le total TTC de l'export est égal au
sous-total « factures » des recettes du bilan. Sélecteurs d'année alignés sur
les deux écrans (helper _export_years()), liens croisés dans les deux sens.
Depuis Factures → Déclaration TVA, sélectionnez une période (mois ou trimestre) pour afficher la synthèse :
Les factures annulées et les commandes annulées / brouillon sont exclues. Les taux sont ceux figés sur chaque ligne au moment de l'émission (§9) — la modification ultérieure d'un taux ne change pas cet historique.
Exports :
- CSV : bouton « CSV » — format comptable (;, UTF-8 avec BOM).
- PDF : bouton « PDF » — document A4 synthétique avec les deux tableaux
et la TVA nette en encadré.
Réglages → Facture électronique connecte QUOTELUI à la plateforme de dématérialisation (PDP) choisie par l'entreprise :
https://…), identifiant expéditeur, case
activée, et soumission automatique à l'envoi (best effort : un
échec est tracé mais ne bloque jamais l'envoi de la facture).GET {base}/health (test de connexion),
POST {base}/invoices (soumission), GET {base}/invoices/{id}
(acquittement).POST {base}/oauth2/token (si OAuth),
GET {base}/v1.beta/companies/me (test de connexion — renvoie aussi votre
dénomination sociale, preuve que les identifiants sont bons),
POST {base}/v1.beta/invoices avec le document XML brut, puis
GET {base}/v1.beta/invoices/{id} pour l'acquittement.
Saisissez l'URL de base (SUPER PDP : https://api.superpdp.tech, déjà
pré-remplie) — l'application ajoute elle-même les chemins. Vous pouvez même
coller une URL complète (…/oauth2/token, …/health, …/invoices,
…/v1.beta) : la fin inutile est retirée automatiquement, jamais de chemin
en double. En cas de 404, le message affiche l'URL exacte testéeet la
liste des chemins réellement appelés : c'est presque toujours une URL de base
erronée.docs/deployment.md §5 bis, jamais dans
l'interface) : manage.py update_compliance installe le pack sans
redéploiement de l'image (données seules, contrôle sha256) et
manage.py check_updates annonce une nouvelle version de l'image.
Hors-ligne : cache local, sinon pack intégré à l'image.Qui pilote la connexion ? Deux options :
companies/me) : en cas d'écart elle s'arrête avec un message citant
les deux identifiants, sans même appeler la plateforme.urn:cen.eu:en16931:2017 et porte par la même occasion tous les
contrôles français du schéma (notes légales PMT/PMD/AAB, cadre de
facturation BT-23, SIREN avec schéma 0002, ventilation TVA en euros).
Tout est automatique : un rapport validation_reports de la plateforme
confirme is_valid avant l'envoi réel.0225:…), enregistre automatiquement le client sous votre
préfixe (suffixe = son SIREN) et n'attend que quelques secondes que
l'entrée devienne active. À la première facture d'un client, si la
plateforme met un instant à la voir, une reprise automatique est faite
(une seule) ; en cas d'échec, le refus « receiver address … does not
exist » se résout en relançant Soumettre à la plateforme. Les adresses
e-mail (schéma EM) ne servent plus qu'en repli si l'annuaire est
injoignable.Le mode bac à sable et le mode production utilisent la même
adresse d'API : c'est la clé (ou le couple client_id/client_secret)
qui détermine l'environnement (doc SUPER PDP — « La clé API utilisée
détermine si l'API est utilisée en mode production ou bac à sable »). Aucun
changement d'URL, aucun redéploiement.
Avant de basculer, côté SUPER PDP (console) :
Dans l'application (Réglages → Facture électronique) : saisir les identifiants
de production, décocher « Mode bac à sable », activer la connexion,
puis cliquer Tester la connexion (la dénomination affichée doit être la
vôtre en production). Les adresses d'annuaire deviennent alors les adresses
réelles : l'adresse du destinataire est son SIREN (0225:SIREN) et
l'application n'inscrit jamais de ligne d'annuaire pour un client.
Les factures déjà transmises en bac à sable restent tracées comme telles
(badge « bac à sable »).
Sur la fiche d'une facture (figée / envoyée) : un bloc « Facture électronique (PDP) » liste les soumissions avec le retour de la plateforme : SOUMISE / EN_ATTENTE / ACCEPTEE / MISE_A_DISPOSITION (mise à disposition chez le destinataire) / REJETEE / ECHEC, plus l'identifiant message PDP, la date de soumission, un badge « bac à sable » si la transmission a eu lieu en environnement de tests, et le détail (couleur : vert = accepté, violet = remis au destinataire, rouge = rejet/échec, ambre = en attente) avec :
invoice.send) ;invoice.view).GET invoice_events avec un curseur d'identifiants strictement
croissants : aucun événement ne peut se perdre, aucun n'est traité deux
fois. C'est la commande serveur manage.py pdp_refresh (planifiée en cron
par l'opérateur — docs/deployment.md §11) qui la pilote pour toutes les
sociétés prêtes ; la page de réglages affiche « Statuts récupérés le … ».
Le bouton Actualiser reste disponible pour un contrôle immédiat,
facture par facture.Lire un refus de la plateforme — le message affiché indique la conduite à tenir :
fr:213 avec des règles BR-10 / BR-11 (contrôle de
conformité EN 16931, après dépôt) : la facture électronique exige
l'adresse postale complète du client (adresse de facturation de sa
fiche client, pays compris) — c'est la cause la plus fréquente. Complétez
la fiche client, puis créez une facture de remplacement : le contenu
d'une facture déjà envoyée est figé et ne se corrige pas. Les autres
règles BR-… du même refus désignent une donnée obligatoire manquante
dans le document ; la soumission apparaît en Rejetée (rouge) et le
détail BR-… s'affiche dans le tableau des soumissions de la fiche
facture (texte complet au survol).La colonne « retour » affiche, sous le message, la réponse telle quelle de
la plateforme (plateforme : …) : c'est l'état déclaré par la plateforme, même
si notre vocabulaire ne le connaît pas encore (codes métier propres au fournisseur).
API : POST /api/invoices/{pk}/electronic/ et
POST /api/invoices/{pk}/electronic-status/.
La réforme 2026 n'impose la facture électronique (échange via une plateforme agréée) qu'aux opérations B2B domestiques. Les ventes à des particuliers (ou à des clients étrangers) relèvent de l'e-reporting : les données de transaction et de paiement sont transmises à l'administration.
Ce que fait la plateforme agréée pour vous (contrat SUPER PDP, article « E-reporting ») : les factures B2C sont déposées comme les factures B2B ; la plateforme ne les achemine à personne (un particulier n'a pas de plateforme) et en extrait automatiquement l'e-reporting. Deux conditions :
BAR = B2C — l'application l'ajoute
automatiquement dès que le client figé n'a pas d'identifiant fiscal
français exploitable (SIREN/SIRET) ;fr:212), que l'application envoie au passage de
la facture en « payée » (aucune action de votre part).Ce que fait l'application :
B2C) : complétez sa fiche, puis établissez une
facture de remplacement (le contenu d'une facture envoyée est figé).fr:212 (montant TTC par taux de TVA, à la date d'encaissement) ; la
fiche facture l'affiche (« Encaissement déclaré à la plateforme le … »).
Une panne de la plateforme ne bloque jamais l'encaissement (l'échec
est tracé dans le journal et la resoumission reste possible).b2c_transactions, b2c_payments).bash
python manage.py e_report --company 1 # mois courant
python manage.py e_report --company 1 --from 2026-10-01 --to 2026-10-31
python manage.py e_report --company 1 --out /tmp/ereporting.json
Sortie JSON : transactions (jour d'émission × taux de TVA → HT, TVA,
nombre de factures) + payments (jour de paiement × taux → TTC) pour les
seules factures B2C émises (Envoyée / En retard / Payée — hors
Annulée et hors brouillons). Montants en texte à 2 décimales (jamais de
flottants), période par défaut = mois en cours, entreprises actives
uniquement.
- Ne pas déclarer deux fois : pour les factures B2C émises dans
QUOTELUI, l'e-reporting est fait par la plateforme — n'envoyez pas en
plus les données de e_report pour ces mêmes ventes (réservé aux ventes
sans facture).
Le relevé (métier) est le document de terrain réalisé par le technicien avant
élaboration du devis. Il est cloisonné par métier : Survey.module_key
(choisi à la création, parmi les modules activés) impose côté serveur les
types/produits acceptés — un relevé menuiserie refuse un produit plomberie,
web comme API. Les champs proposés dépendent du métier car ils viennent des
schémas d'attributs du catalogue (ProductType → AttributeSchema) :
CHOICE → boutons radio, BOOLEAN → Oui/Non,
numérique → saisie nombre, TEXT/DATE en dernier recours.hauteur→H, largeur→L,
diametre→Ø, mm déjà posés) ; les types ronds (gouttière,
fenêtre ronde) n'exposent que Ø.Measurement (clés canoniques, quantité
du devis V2.6 intacte), reste → CustomField, produit → SurveyItem.product
et SurveyItem.product_type. Obligatoires vérifiés ; l'ajout est atomique
(un refus ne laisse aucun élément).TERRAIN → VALIDE → BUREAU → EN_VERIFICATION → CLOTURE
↕ ↕ ↕
retours possibles (VALIDE → TERRAIN, BUREAU → VALIDE)
| État | Qui | Rôle |
|---|---|---|
| TERRAIN | Technicien | Saisie autorisée : éléments, mesures, champs, photos, observations. |
| VALIDE | Technicien | Relevé relu et validé par le technicien. |
| BUREAU | Commercial/Manager | Traitement au bureau (élaboration du devis). |
| EN_VERIFICATION | Bureau | Vérification finale. |
| CLOTURE | Tout le flux | Terminé — plus aucune modification. |
| ANNULE | — | Relevé abandonné. |
Règle : seul un relevé au TERRAIN est modifiable. Les retours permettent de renvoyer un relevé en arrière sans jamais perdre son historique.
TEXT). L'unité vient du schéma : jamais d'unité à taper.update_dimensions, refus des clés inconnues, champ vide =
inchangé). Aucune mesure libre sur un relevé cloisonné.
Le formulaire Mesures libres (libellé + valeur + unité, unité résolue
et créée à la demande, clé facultative — §11.3) reste réservé aux relevés
génériques.Les relevés génériques (créés sans module, data legacy ou API) gardent le comportement historique : libellé libre, aucun filtre.
La suppression est gérée en « corbeille » : un relevé supprimé reste traçable dans l'historique et restauré si besoin par l'administrateur (il garde son numéro).
Chaque mesure possède un libellé (« Hauteur baie ») et une clé
technique : un mot court en minuscules, sans accents ni espaces (ex. largeur,
hauteur, surface).
largeur, hauteur, diametre) — aucune saisie ;
la quantité du devis (V2.6) retrouve la mesure sans ambiguïté.largeur,
« Hauteur baie » → hauteur-baie). Si une telle clé existe déjà sur le même
élément, la seconde reçoit un suffixe (largeur_2) — jamais d'erreur.surface, ml, debit).Le rôle de la clé est de relier la mesure au reste de l'application :
surface pour un tarif au
m², ml pour un tarif au mètre linéaire).| Vous saisissez | Clé produite (si vide) | Effet sur le devis |
|---|---|---|
| « Surface » 1,5 m² | surface | devient la quantité d'une ligne tarifée en m² |
| « Longueur » 4 ml | longueur | devient la quantité d'une ligne au mètre linéaire |
| « Remarque » 2 | remarque | descriptive uniquement |
Astuce : pour forcer la liaison avec un tarif, utilisez précisément la clé de l'attribut de quantité définie dans Réglages → Tarifs (pose) (§13 bis).
Chaque liste (devis, clients, chantiers, relevés, produits) peut être parcourue via Recherche (§6) ou directement sur les cartes du tableau de bord.
Réglages (menu) permet de configurer l'entreprise, avec effet immédiat sur les nouveaux devis (l'envoi fige ces informations dans la version) :
| Paramètre | Effet |
|---|---|
| Logo de l'entreprise | Votre propre logo d'artisan, affiché en en-tête des PDF de devis (et uniquement là — jamais dans les emails, et distinct du logo applicatif QUOTELUI). Sans logo, le PDF affiche à la place une case avec le nom de l'entreprise. Il est figé dans le devis à l'envoi. |
| Adresse (rue, code, ville, pays) | Affichée sur les PDF et les emails. |
| Nom commercial / SIRET / TVA intracom. | Identité légale de l'éditeur. |
| Conditions de règlement | Figées sur le devis. |
| Conditions générales (CGV) | Textes des CGV figurant sur le devis. |
| Numérotation des devis | Modèle par défaut DEV-{year}-{seq} (modifiable). |
| TVA par défaut des lignes | Préremplie sur les nouvelles lignes. |
| Durée de validité | Date de validité proposée automatiquement. |
| Couleur de marque | Utilisée dans l'interface. |
| Email de contact (PDF) | Pied de page des devis/factures/livraisons (« Toute question sur ce devis : … »). Défaut contact@quotelui.fr ; figé à l'envoi. |
Configuration par entreprise : chaque société conserve ses réglages. Un réglage ne modifie jamais un devis déjà envoyé (celui-ci est figé).
À noter : si l'entreprise ne possède encore aucun taux de TVA, QUOTELUI crée automatiquement les taux standards français (20 %, 10 %, 5,5 %, 2,1 %) à l'ouverture des Réglages et fixe la TVA par défaut à 20 % — cela évite le message « Chaque ligne doit figer son taux de TVA » lors de la création d'un devis depuis un relevé (§13 ter).
Réglages → Notifications (V2.13) configure un digest d'alertes par email : destinataires (séparés par des virgules) et catégories à inclure (devis, factures en retard, stock bas, relances commerciales, relevés, licences). L'envoi est déclenché par une commande périodique (voir §13 octies).
Réglages → Tarifs (pose) (Admin) permet de constituer un référentiel de prix par type de produit : pour chaque type du catalogue (fenêtre, porte, pose…), vous enregistrez une prestation type avec son prix unitaire HT.
Quand un commercial ajoute une ligne de devis et choisit un produit du catalogue, l'application propose automatiquement :
Le tarif proposé reste modifiable : le commercial peut valider, corriger le prix ou la quantité avant l'ajout de la ligne. La proposition ne modifie jamais le devis elle-même.
Points de fonctionnement :
Un relevé de chantier peut devenir un devis en un clic :
link_item_product — produit actif de la
même entreprise, type cohérent avec l'élément, métier du relevé sur un
relevé cloisonné). L'élément affiche alors « · produit : … ». Un élément
typé sans produit (« Pas au catalogue ») reste chiffrable via la
règle du type (rule_for_product_type).surface en m²),
sinon l'attribut du référentiel, sinon 1 ;Points importants :
rule_for_product_type), sinon 0 € « à tarifer ».
Le message de création rappelle le nombre de lignes à tarifer.Stock dans le menu permet de suivre les quantités par produit (Manager+ pour les mouvements, tous les rôles en lecture).
Le stock est organisé en dépôts (dépôt principal, atelier, camion…) :
Fournisseurs (menu) : fiches d'achat (coordonnées, conditions de règlement, délai indicatif) et tarifs d'achat par produit. La gestion est réservée aux rôles Manager+ ; les autres rôles consultent. Une fiche affiche aussi l'historique de ses commandes.
Nature du prix (spec §6) — sur chaque tarif :
Remises négociées — section dédiée de la fiche fournisseur : remise par famille (type de produit), par catégorie catalogue, de volume (« à partir de N unités ») ou par défaut sur la fiche. Résolution déterministe et jamais cumulée : la règle la plus spécifique gagne (remise produit → volume/famille → famille/catégorie → défaut). Les dates de validité bornent chaque règle ; une règle retirée reste traçable.
Historique & source : chaque changement de tarif (prix, nature, remise, validité) est historisé (ancien prix jamais écrasé en silence) et la source du prix peut être saisie (« fichier 2026 », « contrat cadre »…). Les lignes de commande fournisseur reprennent automatiquement le prix d'achat net résolu pour la quantité commandée.
Modules métier : une fiche peut préciser les métiers concernés (Menuiserie, Plomberie…) ; vide = le fournisseur est proposé pour tous les métiers.
Commandes (menu) gère les bons de commande (BC-YYYY-NNNN) :
BROUILLON → ENVOYEE → CONFIRMEE → PARTIELLEMENT_RECUE → RECUE
ANNULÉE (depuis tout état non reçu)
Livraisons (menu) gère les bons de livraison (BL-YYYY-NNNN) — utiles
pour tracer ce qui est réellement remis au client, indépendamment de la
facturation.
BROUILLON → PARTIELLEMENT_LIVREE → LIVREE
ANNULÉE (depuis BROUILLON / PARTIELLEMENT_LIVREE)
Affaires (menu) pilote le pipeline commercial avant le devis.
Réglages → Notifications (Admin) active un digest d'alertes envoyé par email : destinataires et catégories à inclure (devis, factures en retard, stock bas, relances commerciales, relevés, licences).
Sous-traitants (menu) gère les artisans externes à qui vous confiez des parties de chantier.
Le système supporte 6 devises : EUR (€), CHF (franc suisse), USD ($), GBP (£), CAD (dollar canadien), XOF (franc CFA).
currency disponibles.Règle : la devise est un label d'affichage. Tous les calculs (restes à payer, reporting, totaux) sont dans la devise d'origine du document. Le change sera une phase ultérieure si besoin.
Chaque chantier dispose d'un espace documents & photos accessible depuis la fiche chantier.
/chantiers/<pk>/documents/) : liste, upload (PDF, Excel,
Word…), catégorie (Devis signé, Facture, PV, Assurance, Autre),
suppression. Chaque document est versionné (V1 → V2 → …)./chantiers/<pk>/photos/) : galerie avec aperçu (thumbnails
auto-générés), upload (JPEG/PNG/WebP), suppression.Aucune migration — tout repose sur le GFK existant (Document + MediaItem).
Planning dans le menu ouvre un calendrier hebdomadaire qui regroupe automatiquement tout ce qui a une date, sans rien à saisir en plus :
| Source | Date utilisée |
|---|---|
| Chantiers | Date de début du chantier (les personnes affectées sont les techniciens). |
| Relevés | Date de relevé prévue (TERRAIN). |
| Activités | RDV, visites, appels à faire (CRM). |
| Livraisons | Date de livraison (non annulées). |
Fonctionnement :
?start=AAAA-MM-JJ.Le planning est en lecture seule : il se remplit à partir de la date de début des chantiers (§8), de la date des relevés (§11) et des activités (§13 septies). Pour faire vivre l'agenda, renseignez ces dates au quotidien.
Aucune migration : le planning ne fait que lire des objets existants.
Le Tarif Fabricant chiffre une menuiserie selon le catalogue du fabricant, sans quitter le devis : bouton ⚡ Tarif Fabricant (menuiserie) sur la fiche d'un devis (§10) ou d'un relevé menuiserie (§11). La page s'ouvre dans une fenêtre intégrée (même site, sans perte de contexte).
Ouverture sur le bon type : le configurateur s'ouvre directement sur le type menuiserie sélectionné dans le formulaire de devis.
Choix guidés : type d'ouvrage (fenêtre, porte, porte-fenêtre, baie, portail, volet, store…), matière, vitrage, ouverture, coloris RAL, nombre de vantaux, hauteur × largeur en mm.
TARIF_PROVIDER=api + TARIF_API_URL (et TARIF_API_KEY pour
la clé d'accès) — jamais de clé en dur dans le code.Import d'une grille fabricant — Réglages → Tarifs fabricant (Admin et Manager) :
.xls doit être réenregistré en .xlsx, .csv ou .json.{"type", "label", "cells": [{width_mm, height_mm, price_ht}], "options":
[{key, label, mode, value, length_base?, included_in_base?}]} — les modes
sont ceux du CSV (coef, fixe, m2, ml, percent, unite, inclus).
Le modèle JSON complet est fourni dans le code
(apps/pricing/services.py → GRID_JSON_SCHEMA) et une grille existante
peut être ré-exportée dans ce format (grid_export_json).; (ou tabulation/virgule — détecté automatiquement).type;largeur;hauteur;prix_ht (l'en-tête est tolérant),
puis les sections option;clé;libellé;mode;valeur (clé au format
domaine:valeur, ex. option;matiere:alu;Aluminium;coef;1.10,
option;coloris:ral 7016;Thermolaquage RAL 7016;percent;8).coef (multiplicatif), fixe (montant HT),
m2 (montant × surface réelle), ml (montant × longueur facturable —
base perimetre par défaut, ou largeur / hauteur en 6ᵉ colonne),
percent (pourcentage du prix de base de la grille — jamais l'ensemble
du devis), unite (montant × quantité d'options saisie), inclus
(option déjà comprise dans la case : affichée, jamais facturée — aucune
double facturation).requiert en 7ᵉ colonne du fichier, requires en JSON) — par
exemple la motorisation ne s'applique qu'au type « volet »
(…;fixe;145.00;;type:volet). Tant que la clé requise n'est pas
sélectionnée, l'option est ignorée (jamais facturée à tort) et signalée
comme incompatible dans le détail du calcul.Accès : Admin et Manager (gestion des prix). Le tarif est une proposition : la ligne reste modifiable dans le devis.
Administration → Membres gère les comptes de l'entreprise.
Chaque ajout/retrait/changement de rôle est journalisé dans l'audit.
Mode d'équipe : en mode Solo, l'entreprise n'a qu'un seul compte (l'administrateur) — le formulaire d'ajout est remplacé par un bandeau expliquant de contacter l'éditeur. Le mode se pilote depuis l'espace Éditeur (§14 quater).
Modes d'équipe : chaque entreprise est en mode Solo (un seul compte :
l'administrateur — tous les autres rôles bloqués à 0), Standard
(multi-utilisateurs, défaut) ou Personnalisé (quotas édités à la main).
Le basculement se fait depuis Espace Éditeur → fiche entreprise →
« Membres → », boutons « Passer en Solo » / « Passer en Standard » ; il ne
supprime aucun membre existant (les ajouts sont simplement bloqués, et un
dépassement éventuel reste visible). Chaque bascule est audité
(PERMISSION_CHANGE, acteur = l'éditeur). Le mode est dérivé des quotas
(Company.member_limits, JSON) — aucune migration, aucun champ « plan ».
Quotas par rôle : par entreprise, le nombre maximal de comptes actifs
est limité par défaut à 1 ADMIN, 1 MANAGER, 1 COMMERCIAL, 3 TECHNICIEN,
LECTEUR illimité (EDITEUR toujours hors quotas). La règle est appliquée
côté serveur dans add_member (ajouts ET changements de rôle) — un
dépassement produit un message clair, jamais une erreur 500.
editeur/<pk>/membres/, PUBLISHER_LICENSE_MANAGE) : boutons de mode +
champ Maximum par rôle (vide = défaut, nombre = quota, case Illimité
= aucun quota). Stockage : Company.member_limits (JSON, migration
companies/0006), lecture via
apps/companies/limits.py (membership_mode / apply_membership_mode /
member_limit / check_member_limit / set_member_limits).Administration → Modules active les spécialités de l'entreprise.
| Module | Types de produits injectés |
|---|---|
| Menuiserie | Fenêtres, portes, portes-fenêtres, baies vitrées, portails, volets, stores, accessoires, pose. |
| Plomberie | Robinets, mitigeurs, lavabos, WC, chauffe-eau, chaudières, radiateurs, pose plomberie. |
| Chauffage | Pompes à chaleur, poêles, sèche-serviettes, planchers chauffants, ventilo-convecteurs, ballons thermodynamiques, régulation, pose chauffage. |
| Électricité | Tableaux, disjoncteurs, interrupteurs, prises, luminaires, détecteurs ; prestations : préparation et passage des câbles, pose des appareillages, compteur et tableau électrique, mise en sécurité et fin de chantier. |
| Couverture | Tuiles, ardoises, bac acier, zinc, gouttières, isolation, étanchéité ; prestations : dépose, pose, reprise faîtages/noues. |
| Carrelage | Carrelage sol/mur, faïence, tomettes, parquet, plinthes, joints, préparation des supports ; prestations : dépose, pose, joints. |
| Peinture / Plaquiste | Plaques de plâtre, doublage, cloisons, plafonds, enduits, peintures, papiers peints ; prestations : pose, enduits, peinture. |
| Maçonnerie | Parpaings, bétons, dalles, fondations, murs, crépis, clôtures, terrasses ; prestations : fondations, élévation, dalles, crépis. |
Selon l'installation, les modules peuvent être verrouillés : seuls les métiers livrés d'office (Menuiserie) se passent de clé. Pour activer un autre métier (Plomberie, …), il faut la clé d'activation fournie par ton installateur :
L'éditeur dispose de trois actions par module (route
editeur/<pk>/licences/) :
L'état affiché est sans ambiguïté : Actif — licence valide jusqu'au …, Actif sans licence valide (sera reverrouillé), Licence enregistrée — module inactif, Expirée le …, Pas encore de clé.
Après la phase de démonstration et la signature du client, l'éditeur crée son espace définitif en une passe depuis Éditeur → Livrer un client :
https://client1.quotelui.fr) — c'est le lien que recevra le
client par email.La page de succès montre les identifiants une seule fois : notez-les avant de partir. Un lien mène ensuite aux licences de la société livrée (pour renouveler plus tard).
Historique (menu, Manager+) journalise toutes les opérations importantes de l'entreprise :
Filtres et export :
Le journal est filtré par entreprise : rien ne fuite entre deux sociétés.
Emails (menu, Manager+) montre qui a reçu quoi et quand, avec le statut de chaque envoi.
Envoyé / Échec + erreur détaillée en rouge).Tant que la case « Activer cette boîte » est cochée, les envois passent
par cette boîte. Sinon, l'application utilise la configuration serveur
(variables d'environnement EMAIL_*, ou journal console en développement).
| Variable | Exemple (Gmail) | Exemple (OVH hébergement) |
|---|---|---|
EMAIL_BACKEND | django.core.mail.backends.smtp.EmailBackend | idem |
EMAIL_HOST | smtp.gmail.com | ssl0.ovh.net |
EMAIL_PORT | 587 | 587 |
EMAIL_HOST_USER | mon-artisan@gmail.com | contact@mondomaine.fr |
EMAIL_HOST_PASSWORD | mot de passe d'application | mot de passe de la boîte |
EMAIL_USE_TLS | True | True |
DEFAULT_FROM_EMAIL | QUOTELUI <mon-artisan@gmail.com> | QUOTELUI <contact@mondomaine.fr> |
Gmail impose « mot de passe d'application » (2 étapes l'activation requise). Sans SMTP configuré (mode développement/démo), les emails sont imprimés dans le journal du serveur et marqués « Envoyé » — rien ne part réellement.
Les emails envoyés avec le devis (PDF en pièce jointe) utilisent l'email du client figé dans la version envoyée, jamais le client mis à jour ensuite.
Parcours de bout en bout sur l'entreprise de démonstration « Artisan Démo SARL » (compte de démonstration communiqué par l'éditeur), montrant qu'un même devis cumule des cotations de plusieurs modules métier.
http://<IP-du-PC>:8000, même Wi-Fi, §1).Produits (menu). Les 8 modules sont activés
sur cette entreprise (Administration → Modules) ; vous y retrouvez les produits
de chaque métier : fenêtres (Menuiserie), PAC (Chauffage), disjoncteurs
(Électricité), tuiles (Couverture), parpaings (Maçonnerie), grès cérame
(Carrelage), peinture (Peinture).Devis → bouton Nouveau devis avec le chantier
visé. La création reprend l'exemple prêt à l'emploi DEV-2026-0007 :
11 lignes couvrant 7 modules, chacune avec sa TVA figée (fenêtre 5,5 %,
PAC et disjoncteurs 20 %, tuiles/parpaings/carrelage/peinture/main-d'œuvre 10 %),
totaux HT / TVA / TTC affichés (17 580 € TTC).<BROUILLON> (ou A_COMPLETER) vers
PRET, puis Envoyer : le contenu est figé dans une version (v1 → v2 …),
le PDF est généré (bouton PDF) et envoi par email possible (Envoyer).
Une fois envoyé, plus aucune ligne n'est modifiable sans créer une nouvelle
version (retour EN_REVISION).Relance : depuis un devis ENVOYE, le bouton Relancer renvoie le même
PDF figé au même destinataire (objet « Relance : … »), journalisé dans
Emails (§15 bis).
la relance peut être automatisée par la commande périodique :
| Commande | Effet |
|---|---|
| Relance automatique tout devis envoyé il y a plus de 30 jours sans réponse, sauf si relancé depuis moins de 7 jours (délais ajustables). Mode simulation disponible. |
Seules les entreprises actives sont affectées ; aucune version n'est créée (le PDF figé d'origine est renvoyé).
7. Relevé de chantier : ouvrez Relevés → RELEV-2026-0003 (statut VALIDE,
workflow §11) : l'item « PAC air-eau » porte des mesures (surface 85 m²)
et des champs spécifiques au métier Chauffage §15 (pièces, énergie,
mode d'émission) ; l'item « Tableau existant » porte des champs
Électricité (prise de terre, puissance souscrite). Même relevé, deux
métiers : les champs proviennent des schémas de chaque module actif.
8. Historique : ouvrez Historique (§15) : l'envoi du devis, le changement
d'état du relevé, l'activation des modules et la création des produits y sont
tracés.
À retenir : rien n'est codé « métier » dans le moteur. Les modules contribuent uniquement des données (types de produits, attributs, champs de relevé). On peut donc librement mélanger Menuiserie, Plomberie, Chauffage, Électricité, Couverture, Carrelage, Peinture, Maçonnerie — sur le même devis comme sur le même relevé — et désactiver un module sans jamais perdre l'historique.
| Problème | Solution |
|---|---|
| « Connexion refusée » | Vérifiez l'adresse email et le mot de passe (majuscules/symboles compris). |
| Impossible de modifier un devis | Le devis est envoyé (figé) : repassez en EN_REVISION pour créer une nouvelle version. |
| Un champ métier manque dans le relevé | Le module du métier est peut-être inactif : activez-le (Administration → Modules). |
| Le PDF/email ne fonctionne pas | Le devis doit d'abord être envoyé (contenu figé). Pour l'email, le client doit avoir une adresse email. |
| Un devis est « expiré » | Sa validité est dépassée ; relancez via EN_REVISION (nouvelle version) avec une nouvelle date. |
| Bouton d'administration absent | Vous n'avez pas le rôle ADMIN dans cette entreprise. |
Lorsque vous envoyez un devis par email, un lien de signature est inclus dans le message (vert, bien visible). Le client peut :
Comment ça marche :
| Étape | Action |
|---|---|
| Envoi | Le lien est ajouté automatiquement à l'email de devis (ou relance). |
| Fiche devis | Le bloc Signature électronique affiche le statut (« En attente » / « Accepté le … » / « Refusé le … ») et un bouton Copier le lien. |
| Décision | Le lien mène à une page claire : résumé du devis + 2 boutons (Accepter / Refuser). La page est lisible même sur smartphone. |
| Après | Le statut passe à ACCEPTE ou REFUSE. L'historique montre « Accepté le 12/09/2026 à 14:30 · IP 203.0.113.7 ». |
Points importants :
QUOTELUI est une plateforme métier : chaque entreprise utilise son propre compte et reste responsable de traitement des données qu'elle y enregistre (clients, chantiers, devis…).
Ce qui est réellement mis en œuvre (mesures techniques) :
Droits des personnes (RGPD) : accès, rectification, effacement, limitation, portabilité et opposition s'exercent auprès de l'entreprise qui traite vos données (votre artisan/client) ; l'éditeur QUOTELUI est sous-traitant et applique ces demandes transmises par l'entreprise.
Politique complète : application → « Confidentialité » (page
/confidentialite/, visible dès la page de connexion, sans compte) et
Mentions légales (/mentions-legales/).
Module apps.tiers — import de pièces comptables reçues des clients
(recettes) et fournisseurs (dépenses), rattachement aux fiches
tiers, bilan annuel harmonisé et transmission à l'expert-comptable.
Aucune logique de métier particulier : module 100 % CORE (§45).
ThirdPartyDocument — sens RECETTE/DEPENSE, type DEVIS/FACTURE
(recette) ou + COMMANDE (dépense), tiers unique (client ou
fournisseur, contrôle croisé), montants HT/TVA saisis → TTC calculé en
Decimal (jamais de float), statut A_RAPPROCHER/RAPPROCHE, fichier
rattaché via le GFK documents.Document existant (upload_document).
Suppression logique (fiche et fichier). Migration
tiers/0001_initial + companies/0009_companysettings_accountant_email
(CompanySettings.accountant_email, email expert-comptable pré-rempli).
apps/tiers/services.py)create_document(company, *, direction, kind, document_date, amount_ht,
vat_amount, customer, supplier, file, …) — validation complète
(sens/type/tiers, extension parmi ALLOWED_EXTENSIONS, 25 Mo max),
création atomique fiche + fichier ; update_document,
delete_document, mark_reconciled, documents_for (filtres) ;send_to_accountant(company, *, year, recipient, by) — envoie le bilan
en CSV joint via send_email (journalisé EmailLog), horodate
sent_at sur les pièces de l'année.apps/tiers/reporting.py, pur)accounting_summary(company, year) : recettes = factures émises (hors
annulées) + pièces de recette ; dépenses = pièces de dépense + factures
fournisseurs suivies sur les commandes (supplier_invoice_*) ; net TTC,
pièces à rapprocher, dernier envoi. La clé facturation embarque le
résultat de annual_data (mois, TVA par taux, encaissement) — source
unique avec l'écran Export comptable (§10 bis.5) : les deux affichent les
mêmes chiffres par construction. Chaque ligne porte sa devise
(invoice.currency / order.currency ; pièces importées = EUR, le modèle
n'a pas de champ devise). bilan_csv : format comptable FR (;, BOM UTF-8,
décimales à la virgule), colonnes Sens;Source;Pièce;Tiers;Date;Devise;HT;TVA;TTC;Détail.
/documents-tiers/)Liste (filtres sens/type/statut/année/texte), import (drag & drop +
bouton Parcourir), fiche/édition, rapprochement, suppression, bilan
(années, cartes recettes/dépenses/net, facturation par mois + TVA par taux +
encaissement repris de l'export, CSV, envoi à l'expert-comptable ; lien
croisé « Synoptique factures » ↔ l'export renvoie « Bilan complet »).
Capacités : THIRDDOC_VIEW (LECTEUR+) / THIRDDOC_MANAGE (MANAGER+),
même matrice web et API. Carte « Documents tiers » sur /statistiques/ ;
provider pièces tiers dans la recherche globale.
GET/POST /api/documents-tiers/ (+ PATCH/DELETE, action
rapprocher/) ; création multipart avec le champ file ; mêmes
validations que les services (THIRDDOC_VIEW/THIRDDOC_MANAGE).
Depuis le bilan : destinataire saisi ou repris de Réglages → Email de
l'expert-comptable ; objet « Bilan comptable AAAA — entreprise » ; pièce
jointe bilan-comptable-AAAA.csv ; trace EmailLog (SENT/FAILED) et
sent_at sur les pièces. Email de transport = boîte configurée Phase 10 /
auto-gérée (§20) ; aucun envoi automatique — action explicite uniquement.
Suite du module apps.tiers — aucun modèle ni migration : lecture
locale d'un fichier déposé, proposition de saisie, puis rapprochement des
fiches tiers en doublon. Tout reste sous contrôle de l'utilisateur
(l'extraction propose, jamais n'écrit tout seul).
apps/tiers/extracting.py, pur)extract_text(file) → (texte, moteur) : couche texte du PDF
(pypdf, prioritaire quand elle existe) ; sinon rasterisation
(pypdfium2, 5 pages max, sans dépendance système) + OCR Tesseract
fra (pytesseract) ; images → OCR direct (Pillow, EXIF, 3500 px) ;
.txt/.csv décodés (UTF-8/UTF-8-sig/latin-1) ; format non pris en
charge (.docx…) ou échec → ExtractionError à message FR
(« saisie manuelle »). Borne fichiers = _clean_file existant
(25 Mo, extensions contrôlées). Le curseur fichier est réinitialisé.parse_text(text) → dict JSON-able sans objet métier : kind
(FACTURE/DEVIS/COMMANDE — priorité à l'en-tête, une ligne « Facture
n° … bon de commande » reste une facture), reference, date
(FR/ISO, priorité à la date d'émission), amount_ht/vat_amount/
amount_ttc (Decimal en texte, virgule française, NBSP tolérés ;
TVA déduite HT→TTC si absente ; HT+TVA≠TTC → warning), party
(émetteur : nom forme juridique, SIRET, TVA FR, email, tel, rue/CP/ville)recipient (bloc « Facturé à ») + warnings. Aucun montant
inventé : ce qui n'est pas trouvé reste vide.
Robustesse mise en page réelle (retours terrain V4.5 bis) :n°/no jamais à l'intérieur d'un mot (le « no »
de « l'innovation » produisait « VATION ») et valeur candidate doit
contenir un chiffre → « Référence du devis : DEV-2026-0043 » est
lu correctement ;apps/tiers/parties.py)normalize_name (NFKD→ASCII, casse, S.A.S.→SAS, formes juridiques
retirées), siret_key (SIRET 14 → SIREN, TVA FR 11 → décalage 2,
9 déjà SIREN, sinon "").resolve_party : si l'émetteur est notre entreprise (SIREN ou nom
normalisé) → le tiers est le destinataire (pièce qu'on a émise) ;
si le destinataire est déjà un client connu (SIREN/nom) → pièce
traitée en recette côté ce client, même si l'émetteur n'est pas
reconnu comme nous (avertissement affiché, sens restant modifiable à
la main) ; sinon l'émetteur. choose_direction : COMMANDE→DEPENSE,
émetteur = nous ou inconnu→RECETTE, reçu→DEPENSE.issuer/recipient/
recipient_match (party_match_payload), la case restaure l'état
initial en se décochant (tiers_extract.js → applyIssuerIsMe).match_party : correspondance côté client et fournisseur par SIREN
puis nom normalisé — renvoie mixed quand l'identité existe des deux
côtés (profil mixte : rattachable, jamais fusionné §45).create_proposed_party : création de la fiche proposée au moment de
l'import (new_party=1 côté web) — capacité CUSTOMER_MANAGE
(client : kind ENTREPRISE si forme juridique/SIRET, sinon PARTICULIER
prénom/nom + set_billing_address) ou SUPPLIER_MANAGE
(fournisseur) vérifiée côté serveur.apps/tiers/duplicates.py)duplicate_groups(company) — lecture seule : union-find par
SIREN identique / même nom sur clients actifs puis fournisseurs
actifs + liste mixed (identité des deux côtés).merge_customers / merge_suppliers — la maîtresse absorbe les
dossiers FK du doublon (_meta.related_objects, filtrés par company) :
pièces tiers, contacts, devis, factures, chantiers, affaires, commandes…
puis soft_delete du doublon (audit SOFT_DELETE). Unicité SQL
unique_supplier_price_per_product : prix déjà maîtrisé → la ligne du
doublon est archivée (dropped), jamais supprimée. Capacités
CUSTOMER_MANAGE/SUPPLIER_MANAGE (par by) — les montants ne sont
jamais modifiés, seuls les rattachements changent./documents-tiers/)POST /documents-tiers/analyser/ (THIRDDOC_VIEW) → JSON
{engine, kind, direction, reference, date, amounts, party, match,
warnings} — aucune écriture ;tiers_extract.js (fetch vanilla, enclenché par
Alpine setFile après contrôle format/taille) remplit le DOM et
dispatch change (selects x-model suivent) ; bannières statut +
correspondance ; bloc « Créer ce tiers maintenant »
(new_party + new_party_*) affiché si match.type=new, refermé si
l'utilisateur choisit lui-même un tiers ;tiers_extract.js émet tiers:extracted /
tiers:failed sur le formulaire, Alpine bascule les drapeaux
fileChosen/readOk/readFailed/partial/editDetails et
x-show="detailsVisible()" ne révèle les champs détaillés (sens, type,
n°, date, montants) qu'en échec/partial/édition manuelle ; en cas de
lecture complète, un résumé vert (« Lu automatiquement — … ») remplace
les champs avec boutons Modifier/Masquer les champs ; les sélecteurs
client/fournisseur restent toujours visibles (action requise si aucun
match) ; garde-fou @submit : montant HT manquant sous champs cachés →
révélation + message ;GET /documents-tiers/doublons/ (THIRDDOC_VIEW) : groupes par
section (clients / fournisseurs) + profils mixtes (jamais
fusionnés) ; POST /documents-tiers/doublons/fusionner/
(THIRDDOC_MANAGE) : radio « fiche à garder » + bouton
« Fusionner ce doublon » par membre (plusieurs clics pour N doublons) ;Dépendances : pypdf, pypdfium2, pytesseract (pyproject) + paquets
système tesseract-ocr tesseract-ocr-fra (Dockerfile). Tests :
tests/test_thirdparty_extraction.py (72) — parseur FR, PDF à couche
texte, OCR réel skipUnless(tesseract), rattachement/directions,
groupes/fusions (y c. prix en conflit), écrans + capacités + isolation.
Piège de déploiement (NAS) : en prod le static vit dans le volume
static_data (nginx le sert en ro) — collectstatic est à lancer
après chaque déploiement (docker-compose exec web python manage.py
collectstatic --noinput, rappelé en commentaire de
compose.synology.yml) : sans lui, tiers_extract.js est servi vide
et l'extraction ne se déclenche jamais (constaté sur V2). Pire : une
copie 0 octet reste collée par le Cache-Control: max-age=604800 de
nginx sur /static/et par le cache cache-first du service worker —
d'où le triple remède actuel (SW quotelui-static-v2 réseau d'abord +
no-cache, ?v= sur l'URL du script, message explicite si le script
d'extraction est absent au lieu d'un no-op silencieux).
Suite du module apps/tiers — machine d'états des pièces + deux scénarios
de facturation, sans toucher au core (les services factures existants
sont appelés, jamais modifiés). migration tiers/0002.
BROUILLON (Brouillon) → EN_VALIDATION (En validation) →
A_RAPPROCHER (À rapprocher) → RAPPROCHE (Rapproché) ; états
terminaux FACTURE (Facturé) et LIE (Lié — facture externe).
Les pièces importées restent par défaut en À rapprocher (zéro rupture :
comptabilisées tout de suite). Brouillon / En validation sont des états
optionnels (création manuelle ou API, bouton « Envoyer pour validation »).
send_for_validation(document) : BROUILLON|A_RAPPROCHER → EN_VALIDATION
(idempotent) ;approve_validation(document) : EN_VALIDATION → A_RAPPROCHER (la pièce
compte dans le bilan) ;mark_reconciled et update_document sont refusés, message explicite.generate_invoice_from_piece(document, by=) → crée une facture interne
via create_invoice + add_line (services factures inchangés) : ligne
unique (libellé = n° de pièce ou type, PU = HT de la pièce, qté 1, taux
TVA déduit de la TVA de la pièce, arrondi 0,01 %), facture née en
BROUILLON dans le module Factures (numéro FAC-YYYY-NNNN auto) —
l'artisan la revoit puis l'envoie avec l'existant. La pièce bascule en
FACTURE avec FK invoice (PROTECT) : elle sort des pièces du bilan et
compte via la facture (zéro trou, zéro double compte — même total TTC).
Le type de la pièce n'est pas bloquant : un devis importé depuis un
autre outil peut aussi devenir une facture (utile quand le devis client
sort d'une autre application et qu'il faut l'encaisser ici).
link_external_invoice(document, number, date, amount, source) : la
facture est émise par une autre application → champs
external_number/date/amount/source/linked_at, pièce en LIE. La pièce
reste dans le bilan (choix produit : le lien est une simple
annotation — la facture externe n'existe pas comme objet Quotelui, donc
zéro double compte, bilan complet) et est dispensée du rapprochement.
unlink_external_invoice(document) retire la liaison (champs effacés,
retour à « À rapprocher ») en cas d'erreur de saisie. Une pièce LIE
ne peut plus devenir une facture interne (jamais de deux factures
pour une même opération) : déliez d'abord.
reporting._piece_rows)Entrent dans les recettes et dépenses du bilan : A_RAPPROCHER,
RAPPROCHEet LIE (annotation d'une facture externe — comptée
puisqu'elle n'existe pas ici) ; exclus : brouillon / en validation /
FACTURE (comptée via sa facture générée). annual_data (factures
émises) intact ; les cartes statistiques héritent via
accounting_summary.
LIE ; liste :
badge couleur aligné ;POST /api/documents-tiers/{pk}/send-validation/ · approve/ ·
generate-invoice/ (répond generated_invoice +
generated_invoice_number) · link-external/ (number, date,
amount, source) · unlink-external/ ; invoice + external_*
exposés en lecture.Tests : tests/test_tiers_workflow.py (22) — transitions, exclusions
bilan, génération (taux déduit, zéro trou), refus (DEPENSE, brouillon,
HT nul, déjà facturée, liaison sur terminal), édition refusée en terminal,
écrans web (boutons par statut, redirection facture, 403 LECTEUR) +
API bout en bout.