Projet 03 : Système de gestion et de facturation commerciale#
Dans le monde de l'entreprise et des systèmes ERP (Enterprise Resource Planning), la génération et la conservation des factures de vente représentent un module critique soumis à des règles comptables et légales strictes (calculs hors taxes, ventilation de TVA, totaux nets).
L'objectif de ce projet est de concevoir un moteur de facturation complet en langage C combinant structures composites imbriquées, calculs de remises, affichage de tickets marchands et double persistance (export d'un reçu client .txt et archivage dans un journal comptable .csv).
1. Cahier des charges et modélisation des données#
L'application doit gérer :
1. Les articles du catalogue : code produit, désignation commerciale, prix unitaire HT et taux de TVA applicable (ex: 20%).
2. Les lignes de facture : association d'un article et d'une quantité commandée, avec calcul des sous-totaux HT et TTC de la ligne.
3. La facture globale : référence unique (ex: FAC-2026-001), date d'émission, nom du client, liste des lignes, taux de remise éventuel, total brut HT, montant global de TVA et montant net TTC à payer.
4. La persistance des pièces :
- Génération d'une facture imprimable au format texte.
- Enregistrement récapitulatif dans un journal des ventes CSV.
classDiagram
class Article {
+int code
+char designation[40]
+double prix_ht
+double taux_tva
}
class LigneFacture {
+Article article
+int quantite
+double sous_total_ht
+double sous_total_ttc
}
class Facture {
+char reference[20]
+char date[12]
+char client[50]
+LigneFacture lignes[20]
+int nb_lignes
+double total_ht
+double total_tva
+double remise_pct
+double total_ttc
}
Facture *-- LigneFacture
LigneFacture *-- Article
2. Définition des structures en C#
#define MAX_LIGNES 20
typedef struct
{
int code;
char designation[40];
double prix_ht;
double taux_tva; // Ex: 0.20 pour 20%
} Article;
typedef struct
{
Article article;
int quantite;
double total_ht;
double total_ttc;
} LigneFacture;
typedef struct
{
char reference[20];
char date[12];
char client[50];
LigneFacture lignes[MAX_LIGNES];
int nb_lignes;
double total_ht_brut;
double montant_remise;
double total_ht_net;
double total_tva;
double total_ttc;
} Facture;
3. Implémentation complète en langage C#
Voici l'application complète, autonome et compilable sans avertissements :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_LIGNES 20
#define JOURNAL_CSV "journal_ventes.csv"
typedef struct
{
int code;
char designation[40];
double prix_ht;
double taux_tva; // Ex: 0.20 pour 20%
} Article;
typedef struct
{
Article article;
int quantite;
double total_ht;
double total_ttc;
} LigneFacture;
typedef struct
{
char reference[20];
char date[12];
char client[50];
LigneFacture lignes[MAX_LIGNES];
int nb_lignes;
double total_ht_brut;
double remise_pourcent;
double montant_remise;
double total_ht_net;
double total_tva;
double total_ttc;
} Facture;
// Initialise une nouvelle facture
Facture CreerFacture(const char *reference, const char *client, double remise_pct)
{
Facture f;
memset(&f, 0, sizeof(Facture));
strncpy(f.reference, reference, sizeof(f.reference) - 1);
strncpy(f.client, client, sizeof(f.client) - 1);
f.remise_pourcent = remise_pct;
// Date courante au format AAAA-MM-JJ
time_t t = time(NULL);
struct tm *tm = localtime(&t);
strftime(f.date, sizeof(f.date), "%Y-%m-%d", tm);
f.nb_lignes = 0;
return f;
}
// Ajoute un article et calcule les sous-totaux de la ligne
int AjouterLigne(Facture *f, Article art, int quantite)
{
if (f->nb_lignes >= MAX_LIGNES)
{
printf("Erreur : capacite maximale de lignes atteinte sur cette facture.\n");
return 0;
}
if (quantite <= 0)
{
printf("Erreur : la quantite doit etre strictement positive.\n");
return 0;
}
LigneFacture *ligne = &f->lignes[f->nb_lignes];
ligne->article = art;
ligne->quantite = quantite;
ligne->total_ht = art.prix_ht * quantite;
ligne->total_ttc = ligne->total_ht * (1.0 + art.taux_tva);
f->nb_lignes++;
return 1;
}
// Calcule l'ensemble des totaux financiers et taxes
void CloturerFacture(Facture *f)
{
f->total_ht_brut = 0.0;
f->total_tva = 0.0;
for (int i = 0; i < f->nb_lignes; i++)
{
f->total_ht_brut += f->lignes[i].total_ht;
double tva_ligne = f->lignes[i].total_ht * f->lignes[i].article.taux_tva;
f->total_tva += tva_ligne;
}
f->montant_remise = f->total_ht_brut * (f->remise_pourcent / 100.0);
f->total_ht_net = f->total_ht_brut - f->montant_remise;
// Ajustement de la TVA au prorata de la remise
double facteur_remise = (1.0 - (f->remise_pourcent / 100.0));
f->total_tva *= facteur_remise;
f->total_ttc = f->total_ht_net + f->total_tva;
}
// Rendu formaté d'un flux (vers stdout ou un fichier)
void ImprimerVersFlux(FILE *flux, const Facture *f)
{
fprintf(flux, "========================================================================\n");
fprintf(flux, " FACTURE COMMERCIALE \n");
fprintf(flux, "========================================================================\n");
fprintf(flux, "Reference : %-20s Date d'emission : %s\n", f->reference, f->date);
fprintf(flux, "Client : %-20s Statut : PAYEE\n", f->client);
fprintf(flux, "------------------------------------------------------------------------\n");
fprintf(flux, "%-6s | %-24s | %-4s | %-9s | %-5s | %-9s\n",
"Code", "Designation", "Qte", "P.U. HT", "TVA", "Total HT");
fprintf(flux, "------------------------------------------------------------------------\n");
for (int i = 0; i < f->nb_lignes; i++)
{
const LigneFacture *l = &f->lignes[i];
fprintf(flux, "%-6d | %-24s | %4d | %8.2f | %4.1f%%| %8.2f \n",
l->article.code,
l->article.designation,
l->quantite,
l->article.prix_ht,
l->article.taux_tva * 100.0,
l->total_ht);
}
fprintf(flux, "------------------------------------------------------------------------\n");
fprintf(flux, "%50s : %10.2f EUR\n", "Sous-total Brut HT", f->total_ht_brut);
if (f->remise_pourcent > 0.0)
{
fprintf(flux, "%50s (%4.1f%%) : -%9.2f EUR\n",
"Remise commerciale", f->remise_pourcent, f->montant_remise);
fprintf(flux, "%50s : %10.2f EUR\n", "Sous-total Net HT", f->total_ht_net);
}
fprintf(flux, "%50s : %10.2f EUR\n", "Montant total TVA", f->total_tva);
fprintf(flux, "========================================================================\n");
fprintf(flux, "%50s : %10.2f EUR\n", "NET A PAYER TTC", f->total_ttc);
fprintf(flux, "========================================================================\n\n");
}
// Exporte la facture dans un fichier texte dédié
int ExporterFichierTexte(const Facture *f)
{
char nom_fichier[64];
snprintf(nom_fichier, sizeof(nom_fichier), "facture_%s.txt", f->reference);
FILE *fichier = fopen(nom_fichier, "w");
if (fichier == NULL)
{
perror("Impossible de generer le fichier facture texte");
return 0;
}
ImprimerVersFlux(fichier, f);
fclose(fichier);
printf(">>> Fichier client genere : '%s'\n", nom_fichier);
return 1;
}
// Archive la facture dans le journal CSV des ventes
int ArchiverJournalVentes(const Facture *f)
{
// Mode "a" pour conserver l'historique chronologique
FILE *journal = fopen(JOURNAL_CSV, "a+");
if (journal == NULL)
{
perror("Impossible d'ouvrir le journal CSV");
return 0;
}
// Si le fichier est neuf (taille 0), écrire l'en-tête
fseek(journal, 0, SEEK_END);
if (ftell(journal) == 0)
{
fprintf(journal, "reference,date,client,total_ht,total_tva,total_ttc\n");
}
// Écriture de la ligne comptable
fprintf(journal, "%s,%s,%s,%.2f,%.2f,%.2f\n",
f->reference,
f->date,
f->client,
f->total_ht_net,
f->total_tva,
f->total_ttc);
fclose(journal);
printf(">>> Ecriture comptable enregistree dans '%s'\n", JOURNAL_CSV);
return 1;
}
int main(void)
{
// 1. Définition du catalogue d'articles disponibles
Article ordi_portable = {101, "PC Portable Pro 15\"", 1199.00, 0.20};
Article ecran_4k = {102, "Ecran 4K 27\" IPS", 349.50, 0.20};
Article souris_sans_fil= {103, "Souris Ergonomique BT", 45.00, 0.20};
Article licence_os = {104, "Licence OS Pro Entreprise", 180.00, 0.20};
// 2. Création d'une commande pour un client avec remise de 5%
Facture fac1 = CreerFacture("FAC-2026-001", "Acme Corporation", 5.0);
// 3. Ajout des articles commandés
AjouterLigne(&fac1, ordi_portable, 2);
AjouterLigne(&fac1, ecran_4k, 2);
AjouterLigne(&fac1, souris_sans_fil, 4);
AjouterLigne(&fac1, licence_os, 2);
// 4. Clôture des calculs
CloturerFacture(&fac1);
// 5. Affichage à l'écran
ImprimerVersFlux(stdout, &fac1);
// 6. Exports sur disque
ExporterFichierTexte(&fac1);
ArchiverJournalVentes(&fac1);
return EXIT_SUCCESS;
}
4. Points d'attention pour la certification C#
- Précision des types monétaires : En programmation C industrielle, les montants financiers utilisent souvent des entiers représentant des centimes (
long long centimes) ou des bibliothèques de calcul décimal arbitraire pour éviter les imprécisions d'arrondi inhérentes à la norme IEEE-754 des nombres flottants (float/double). - Buffer Overflow sur les références : L'utilisation de
strncpy()etsnprintf()protège nos tampons contre les dépassements de mémoire lors de la construction des noms de fichiers. - Sécurité d'ouverture : L'ouverture en mode
"a+"avec détection de taille (fseek/ftell) permet d'initialiser dynamiquement l'en-tête CSV uniquement lors de la toute première vente.
Commentaires