Initialisation du dépôt NQFS

This commit is contained in:
Daniel Allaire 2026-03-09 13:26:16 -04:00
commit f303ba0fb1
21 changed files with 433 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
__pycache__/
*.pyc
.venv/
env/
dist/
build/
*.png
*.pdf

5
CHANGELOG.md Normal file
View file

@ -0,0 +1,5 @@
# Journal des modifications
## 1.0.0
- première publication de la structure du projet
- première version de la spécification NQFS

8
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,8 @@
# Contribution
Les contributions sont bienvenues.
Principes :
- préserver la simplicité
- préserver la compatibilité ascendante autant que possible
- éviter les ajouts qui alourdissent inutilement la norme

1
LICENSE Normal file
View file

@ -0,0 +1 @@
Creative Commons Attribution 4.0 International (CC BY 4.0)

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# Norme québécoise de facturation simple
La **NQFS** est une proposition de norme ouverte permettant dencoder, dans un code QR, les données essentielles dune facture afin de faciliter son importation automatique dans un progiciel de gestion intégré ou un logiciel comptable.
## Objectif
Réduire la ressaisie manuelle des factures fournisseurs en fournissant un format de données simple, structuré et directement exploitable.
## Principe
Chaque facture peut contenir un code QR dont le contenu commence par :
NQFS:
suivi dun objet JSON compact contenant les champs utiles à limportation comptable.
## Portée
La norme couvre :
- lencodage de données essentielles de facture
- le format JSON
- le format de contenu du code QR
La norme ne couvre pas :
- les lignes détaillées de facture
- les paiements
- la signature cryptographique
- les réseaux déchange

19
demonstration/README.md Normal file
View file

@ -0,0 +1,19 @@
# Démonstration
## Préparer lenvironnement
python3 -m venv .venv
source .venv/bin/activate
pip install -r outils/requirements.txt
## Valider le JSON
./outils/valider-json.py demonstration/facture-demo.json
## Générer le QR
./outils/generer-qr.py demonstration/facture-demo.json demonstration/facture-demo-qr.png
## Générer une facture PDF de démonstration
./demonstration/generer-facture-pdf.py demonstration/facture-demo.json demonstration/facture-demo.pdf

View file

@ -0,0 +1,19 @@
{
"spec": "NQFS-1.0",
"supplier_name": "Chezlepro Inc",
"supplier_tax_id": "1234567890",
"supplier_email": "facturation@chezlepro.ca",
"supplier_phone": "+1-450-555-1234",
"invoice_number": "INV-2026-00312",
"invoice_date": "2026-03-09",
"due_date": "2026-04-08",
"currency": "CAD",
"subtotal": 1000.00,
"tps_rate": 0.05,
"tps_amount": 50.00,
"tvq_rate": 0.09975,
"tvq_amount": 99.75,
"total": 1149.75,
"reference": "BC-2026-0312",
"description": "Services informatiques"
}

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
import qrcode
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
if len(sys.argv) != 3:
print("Usage: generer-facture-pdf.py <entree.json> <sortie.pdf>")
sys.exit(1)
src = Path(sys.argv[1])
pdf_out = Path(sys.argv[2])
qr_out = pdf_out.with_suffix(".png")
with open(src, "r", encoding="utf-8") as f:
data = json.load(f)
payload = "NQFS:" + json.dumps(data, ensure_ascii=False, separators=(",", ":"))
img = qrcode.make(payload)
img.save(qr_out)
c = canvas.Canvas(str(pdf_out), pagesize=letter)
w, h = letter
c.setFont("Helvetica-Bold", 14)
c.drawString(50, h - 50, data["supplier_name"])
c.setFont("Helvetica", 11)
c.drawString(50, h - 90, f"Facture : {data['invoice_number']}")
c.drawString(50, h - 110, f"Date : {data['invoice_date']}")
c.drawString(50, h - 130, f"Échéance : {data.get('due_date', '')}")
c.drawString(50, h - 150, f"Devise : {data['currency']}")
c.drawString(50, h - 170, f"Sous-total : {data['subtotal']:.2f}")
c.drawString(50, h - 190, f"TPS : {data['tps_amount']:.2f}")
c.drawString(50, h - 210, f"TVQ : {data['tvq_amount']:.2f}")
c.drawString(50, h - 230, f"Total : {data['total']:.2f}")
c.drawString(50, h - 250, f"Référence : {data.get('reference', '')}")
c.drawString(50, h - 270, f"Description : {data.get('description', '')}")
c.drawImage(str(qr_out), w - 180, h - 240, width=120, height=120)
c.save()
print(f"PDF généré : {pdf_out}")

View file

@ -0,0 +1,19 @@
{
"spec": "NQFS-1.0",
"supplier_name": "Chezlepro Inc",
"supplier_tax_id": "1234567890",
"supplier_email": "facturation@chezlepro.ca",
"supplier_phone": "+1-450-555-1234",
"invoice_number": "INV-2026-00312",
"invoice_date": "2026-03-09",
"due_date": "2026-04-08",
"currency": "CAD",
"subtotal": 1000.00,
"tps_rate": 0.05,
"tps_amount": 50.00,
"tvq_rate": 0.09975,
"tvq_amount": 99.75,
"total": 1149.75,
"reference": "BC-2026-0312",
"description": "Services informatiques"
}

View file

@ -0,0 +1,15 @@
# Exemple dimportation ERP
À partir du QR, un ERP peut créer automatiquement :
- fournisseur : Chezlepro Inc
- numéro de facture : INV-2026-00312
- date facture : 2026-03-09
- échéance : 2026-04-08
- devise : CAD
- montant avant taxes : 1000.00
- TPS : 50.00
- TVQ : 99.75
- total : 1149.75
- référence : BC-2026-0312
- description : Services informatiques

View file

@ -0,0 +1,3 @@
{
"qr_payload": "NQFS:{\"spec\":\"NQFS-1.0\",\"supplier_name\":\"Chezlepro Inc\",\"supplier_tax_id\":\"1234567890\",\"supplier_email\":\"facturation@chezlepro.ca\",\"supplier_phone\":\"+1-450-555-1234\",\"invoice_number\":\"INV-2026-00312\",\"invoice_date\":\"2026-03-09\",\"due_date\":\"2026-04-08\",\"currency\":\"CAD\",\"subtotal\":1000,\"tps_rate\":0.05,\"tps_amount\":50,\"tvq_rate\":0.09975,\"tvq_amount\":99.75,\"total\":1149.75,\"reference\":\"BC-2026-0312\",\"description\":\"Services informatiques\"}"
}

9
gouvernance/evolution.md Normal file
View file

@ -0,0 +1,9 @@
# Évolution de la norme
Les changements proposés doivent :
- préserver la simplicité
- justifier leur utilité concrète
- éviter de compromettre la lisibilité du QR
- éviter les dépendances inutiles
Les évolutions incompatibles DOIVENT entraîner une nouvelle version majeure.

7
gouvernance/principes.md Normal file
View file

@ -0,0 +1,7 @@
# Principes directeurs
- simplicité dimplantation
- utilité comptable immédiate
- absence de dépendance à une plateforme centrale
- compatibilité avec les factures actuelles
- lisibilité humaine et machine

View file

@ -0,0 +1,5 @@
# Proposition
La NQFS est proposée comme norme ouverte, simple et volontaire visant à réduire la ressaisie manuelle des factures fournisseurs.
Elle est conçue pour être implantée rapidement dans les logiciels de facturation, les ERP et les logiciels comptables.

21
outils/generer-qr.py Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
import qrcode
if len(sys.argv) != 3:
print("Usage: generer-qr.py <entree.json> <sortie.png>")
sys.exit(1)
src = Path(sys.argv[1])
dst = Path(sys.argv[2])
with open(src, "r", encoding="utf-8") as f:
data = json.load(f)
payload = "NQFS:" + json.dumps(data, ensure_ascii=False, separators=(",", ":"))
img = qrcode.make(payload)
img.save(dst)
print(f"QR généré : {dst}")

3
outils/requirements.txt Normal file
View file

@ -0,0 +1,3 @@
jsonschema
qrcode[pil]
reportlab

22
outils/valider-json.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
from jsonschema import validate
if len(sys.argv) != 2:
print("Usage: valider-json.py <fichier.json>")
sys.exit(1)
base = Path(__file__).resolve().parent.parent
schema_path = base / "specification" / "schema-nqfs-1.0.json"
data_path = Path(sys.argv[1]).resolve()
with open(schema_path, "r", encoding="utf-8") as f:
schema = json.load(f)
with open(data_path, "r", encoding="utf-8") as f:
data = json.load(f)
validate(instance=data, schema=schema)
print("JSON valide selon NQFS-1.0")

View file

@ -0,0 +1,19 @@
# Décisions de conception
## Choix retenus
- JSON plat
- préfixe NQFS:
- taux et montants de taxes
- exclusion des lignes de facture
## Raisons
- simplicité dimplantation
- compatibilité ERP
- robustesse du code QR
- réduction de la ressaisie
## Choix explicitement rejetés pour la version 1
- items détaillés
- signature cryptographique
- mécanisme de paiement
- registre central

View file

@ -0,0 +1,20 @@
# Format du code QR
## Règle générale
Le contenu du code QR DOIT respecter la forme suivante :
NQFS:<json compact>
## Encodage
- UTF-8
- JSON compacté sans espaces inutiles
## Exemple
NQFS:{"spec":"NQFS-1.0","supplier_name":"Chezlepro Inc","invoice_number":"INV-2026-00312","invoice_date":"2026-03-09","currency":"CAD","subtotal":1000,"tps_rate":0.05,"tps_amount":50,"tvq_rate":0.09975,"tvq_amount":99.75,"total":1149.75}
## Recommandation
Le code QR DEVRAIT rester suffisamment compact pour assurer une lecture fiable sur facture papier et PDF.

119
specification/nqfs-1.0.md Normal file
View file

@ -0,0 +1,119 @@
# NQFS-1.0
## 1. Objet
La NQFS définit un format simple pour encoder, dans un code QR, les données essentielles dune facture afin de permettre leur ingestion automatique par un ERP ou un logiciel comptable.
## 2. Portée
La norme couvre :
- le contenu logique minimal
- le format JSON
- le préfixe didentification
- les règles de validation de base
La norme ne couvre pas :
- les lignes détaillées de facture
- les paiements
- la signature cryptographique
- la transmission réglementaire
- larchivage documentaire
## 3. Préfixe de détection
Le contenu du QR DOIT commencer par :
NQFS:
Le texte qui suit DOIT être un document JSON valide encodé en UTF-8.
## 4. Champs obligatoires
- spec
- supplier_name
- invoice_number
- invoice_date
- currency
- subtotal
- tps_rate
- tps_amount
- tvq_rate
- tvq_amount
- total
## 5. Champs recommandés
- supplier_tax_id
- supplier_email
- supplier_phone
- due_date
- reference
- description
## 6. Contraintes
### 6.1 spec
Chaîne. Exemple : NQFS-1.0
### 6.2 supplier_name
Chaîne non vide.
### 6.3 invoice_number
Chaîne non vide.
### 6.4 invoice_date
Date au format ISO 8601 : YYYY-MM-DD
### 6.5 due_date
Date au format ISO 8601 : YYYY-MM-DD
### 6.6 currency
Code alphabétique de devise sur trois caractères. Exemple : CAD
### 6.7 subtotal
Nombre décimal supérieur ou égal à 0.
### 6.8 tps_rate
Nombre décimal. Exemple : 0.05
### 6.9 tps_amount
Nombre décimal supérieur ou égal à 0.
### 6.10 tvq_rate
Nombre décimal. Exemple : 0.09975
### 6.11 tvq_amount
Nombre décimal supérieur ou égal à 0.
### 6.12 total
Nombre décimal supérieur ou égal à 0.
## 7. Règles de cohérence
- total DEVRAIT être égal à subtotal + tps_amount + tvq_amount
- tps_amount DEVRAIT être cohérent avec subtotal × tps_rate
- tvq_amount DEVRAIT être cohérent avec subtotal × tvq_rate
Une tolérance darrondi de deux cents est acceptable.
## 8. Exemple
{
"spec": "NQFS-1.0",
"supplier_name": "Chezlepro Inc",
"supplier_tax_id": "1234567890",
"supplier_email": "facturation@chezlepro.ca",
"supplier_phone": "+1-450-555-1234",
"invoice_number": "INV-2026-00312",
"invoice_date": "2026-03-09",
"due_date": "2026-04-08",
"currency": "CAD",
"subtotal": 1000.00,
"tps_rate": 0.05,
"tps_amount": 50.00,
"tvq_rate": 0.09975,
"tvq_amount": 99.75,
"total": 1149.75,
"reference": "BC-2026-0312",
"description": "Services informatiques"
}

View file

@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NQFS 1.0",
"type": "object",
"required": [
"spec",
"supplier_name",
"invoice_number",
"invoice_date",
"currency",
"subtotal",
"tps_rate",
"tps_amount",
"tvq_rate",
"tvq_amount",
"total"
],
"properties": {
"spec": { "type": "string" },
"supplier_name": { "type": "string", "minLength": 1 },
"supplier_tax_id": { "type": "string" },
"supplier_email": { "type": "string" },
"supplier_phone": { "type": "string" },
"invoice_number": { "type": "string", "minLength": 1 },
"invoice_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"due_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
"subtotal": { "type": "number", "minimum": 0 },
"tps_rate": { "type": "number", "minimum": 0 },
"tps_amount": { "type": "number", "minimum": 0 },
"tvq_rate": { "type": "number", "minimum": 0 },
"tvq_amount": { "type": "number", "minimum": 0 },
"total": { "type": "number", "minimum": 0 },
"reference": { "type": "string" },
"description": { "type": "string" }
},
"additionalProperties": false
}