9 Commits

Author SHA1 Message Date
Lazanimady Andrianirindrainy
de3bcbaea2 corriger bug fonction pour prendre un fichier à partir d'un form fonctionel #1 2026-06-19 12:43:54 +02:00
Lazanimady Andrianirindrainy
b1ee077754 Merge branch 'features/pdf-parse' into core/routes tester les routes 2026-06-19 09:41:03 +02:00
Lazanimady Andrianirindrainy
d82ae9f313 1ère tentative purchase.route #1 2026-06-19 09:38:45 +02:00
Lazanimady Andrianirindrainy
27de44d895 modification liste_routes.txt #1 2026-06-19 09:37:37 +02:00
Aurelien
00949c60c5 chore : modification du parse pour avoir la liste des produit #5 2026-06-18 20:46:12 +02:00
Lazanimady Andrianirindrainy
3f42d80768 1ère définition des routes dans un fichiers .txt #1 2026-06-18 17:17:22 +02:00
Aurelien
2ca6dc31a9 feat : ajout d'un parse pour facture #5 2026-06-17 12:30:39 +02:00
5ffb1c7983 Update: README.md
Update all the README with instructions
2026-06-14 19:38:57 +02:00
15fde72f08 Package, Server, Routes
Adding the base of the server program
2026-06-14 19:14:35 +02:00
10 changed files with 2011 additions and 1 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
.env

118
README.md
View File

@@ -1 +1,117 @@
# Snacks Analytics backend
<h1>Snacks Analytics - Backend</h1>
<hr></hr>
<h2>Sommaire</h2>
- [Introduction](#introduction)
- [Technologies](#technologies)
- [Language](#language)
- [Autres](#autres)
- [Installation](#installation)
- [Utilisation](#utilisation)
- [Contribution](#contribution)
- [Licence](#licence)
- [Auteurs](#auteurs)
### Introduction
Ce projet vise à développer l'API backend de l'application Snacks Analytics pour l'association Label[i]. Il s'agit du service centralisé de notre application web, conçu pour répondre à deux besoins principaux :
Pour les adhérents : Fournir toutes les informations en temps réel concernant les snacks proposés par l'association, notamment l'état des stocks et les prix.
Pour les gérants de l'association : Offrir un outil de pilotage complet pour suivre la rentabilité, analyser les variations de prix, gérer les factures et automatiser le suivi des flux financiers en intégrant notamment l'API SumUp.
### Technologies
Les technologies utilisées pour ce projet sont :
#### Language
[![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![NodeJs](https://img.shields.io/badge/NodeJs-3776AB?style=for-the-badge&logo=nodejs&logoColor=white)](https://nodejs.org/fr) [![PostgreSql](https://img.shields.io/badge/MySQL-4479A1?style=for-the-badge&logo=mysql&logoColor=white)](https://www.mysql.com/)
#### Autres
[![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white)](https://www.docker.com/) [![Git](https://img.shields.io/badge/Git-F05032?style=for-the-badge&logo=git&logoColor=white)](https://git-scm.com/)
### Installation
L'installation du projet peut se faire de deux manières :
1. **Installation manuelle** : Cloner le dépôt GitHub et installer les dépendances nécessaires
```zsh
git clone ssh://git@git.labeli.org:2223/AlexLoup/Snacks-Analytics-backend.git
cd Snacks-Analytics-backend
npm install
```
L'API sera accessible à l'adresse [http://localhost:5000](http://localhost:5000) une fois le serveur démarré.
### Utilisation
Pour lancer le projet en mode développement, utiliser la commande suivante :
```zsh
npm run server
```
### Contribution
Afin de contribuer au projet, veuillez suivre les étapes suivantes, celle ci peuvent paraitre assez strictes mais elles permettent de garder une bonne qualité de code et une bonne organisation du projet.
1. **Branche principale** : La branche principale du projet est `main` (dites branche de production). Toutes les modifications doivent être fusionnées dans cette branche via des demandes de `pull request` donc le code devra être soumis à une revue avant d'être intégré.
2. **Branches de développement** : La branche de développement est `dev` (dites branche de développement). Toutes les nouvelles fonctionnalités, corrections de bugs et autres modifications doivent être développées dans des branches distinctes créées à partir de `dev`. Le nom des branches doit suivre le format suivant : `feature/nom-de-la-fonctionnalité`, `bugfix/description-du-bug`, `hotfix/description-du-hotfix`.
3. **Pull Requests** : Une fois les modifications terminées dans une branche de développement, créer une `pull request` pour fusionner les modifications dans la branche `develop`. Assurez-vous que la `pull request` est examinée et approuvée par au moins un autre membre de l'équipe avant de procéder à la fusion.
4. **Tests** : Avant de fusionner une `pull request`, assurez-vous que toutes les modifications ont été testées et qu'elles n'introduisent pas de régressions. Si des tests automatisés sont en place, assurez-vous qu'ils passent tous avec succès.
5. **Mises à jour de la documentation** : Si les modifications apportées nécessitent une mise à jour de la documentation, assurez-vous que cela est fait avant de fusionner la `pull request`.
6. **Revue de code** : Encouragez les membres de l'équipe à revoir le code des autres avant de fusionner les `pull requests`. Cela aide à maintenir la qualité du code et à partager les connaissances au sein de l'équipe.
7. **Gestion des conflits** : Si des conflits surviennent lors de la fusion d'une `pull request`, résolvez-les en local avant de procéder à la fusion. Assurez-vous que le code résultant est testé et fonctionne correctement.
Pour toute question ou problème, n'hésitez pas à contacter l'équipe de développement ou à ouvrir une issue sur le dépôt GitHub.
8. **Formatage des commits** : Pour la création de commits, veuillez suivre la convention de nommage suivante pour assurer une meilleure lisibilité de l'historique des commits :
```
<type>(<scope>): <subject>
```
- **type** : Le type de changement effectué. Les types couramment utilisés sont :
- `feat` : Une nouvelle fonctionnalité
- `fix` : Une correction de bug
- `docs` : Des modifications de la documentation
- `style` : Des modifications de style (formatage, espaces, etc.) qui
- `update` : Une mise à jour du code existant
- `refactor` : Un changement de code qui n'ajoute ni fonctionnalité ni corrige de bug
- `test` : Ajout ou modification de tests
- n'affectent pas le code
- **scope** : La portée du changement (par exemple, le nom du module ou de la fonctionnalité affectée). Cela peut être omis si ce n'est pas pertinent.
- **subject** : Une brève description du changement (utilisez l'impératif, par exemple "ajoute la fonctionnalité X" ou "corrige le bug Y").
<h4>PS : Il est conseillé d'ajouter un 2ème paragraphe dans la description du commit pour expliquer le pourquoi du changement si nécessaire.</h4>
Exemple de commit :
```
git commit -m "update (README): rework complet" -m "Ecrire complète du README.md pour expliquer comment contribuer au projet"
```
### Licence
Le projet ci présent est sous licence [MIT](https://opensource.org/license/mit/). La licence MIT est une licence permissive qui permet une grande liberté d'utilisation, de modification et de distribution du code source. Voici un résumé des principales caractéristiques de la licence MIT :
- Permission est accordée à quiconque d'utiliser, de copier, de modifier, de fusionner, de publier, de distribuer, de sous-licencier et/ou de vendre des copies du logiciel.
- Le logiciel est fourni "tel quel", sans garantie d'aucune sorte, expresse ou implicite, y compris mais sans s'y limiter les garanties de qualité marchande, d'adéquation à un usage particulier et de non-contrefaçon.
- Les avis de copyright et de licence doivent être inclus dans toutes les copies ou parties substantielles du logiciel.
![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
### Auteurs
- [Alexandre Lou-Poueyou](github.com/AlexLoup33) - a.loupoueyou@icloud.com
<p align="center">N'hésitez à compléter la liste des contributeurs</p>

1602
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "snacks-analytics-backend",
"version": "1.0.0",
"description": "",
"repository": {
"type": "git",
"url": "ssh://git@git.labeli.org:2223/AlexLoup/Snacks-Analitycs-backend.git"
},
"license": "ISC",
"author": "",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"server": "nodemon ./src/server.ts"
},
"dependencies": {
"dotenv": "^17.4.2",
"express": "^5.2.1",
"formidable": "^3.5.4",
"pdf-parse-fork": "^1.2.0"
},
"devDependencies": {
"@types/express": "^5.0.6",
"@types/formidable": "^3.5.1",
"@types/node": "^25.9.3",
"nodemon": "^3.1.14",
"ts-node": "^10.9.2",
"typescript": "^6.0.3"
}
}

102
src/modules/pdf-parse.ts Normal file
View File

@@ -0,0 +1,102 @@
const fs = require('fs');
const pdfParse = require('pdf-parse-fork');
function pdf_parse(travel: string){
interface PdfData {
text: string;
}
let fournisseur = '';
let date = '';
interface DetailProduit {
quantité: number;
prixHT: number;
TVA: string;
}
const productsList: { [key: string]: DetailProduit } = {};
interface tab_res {
marque: string;
date_achats: string;
products: {
[nomProduit: string]: DetailProduit;
};
}
const dataBuffer = fs.readFileSync(travel);
pdfParse(dataBuffer)
.then((data: PdfData) => {
// on regarde si c'est auchan ou metro
const regexAuchan = /auchan/i;
if (regexAuchan.test(data.text)) {
fournisseur = "Auchan"
// on regarde la date d'achat avec une expression reguliere pour auchan
const regexDate = /(\d{2})\/(\d{2})\/(\d{4})/;
const correspondance = data.text.match(regexDate);
if (correspondance) {
date = correspondance[0]
} else {
date = "non trouvé"
}
// on prend la liste des produits et leurs detail pour auchan
const lignes = data.text.split('\n');
// Expressions régulières pour détecter les lignes de produits Auchan
const regexLigneProduit = /^(\d{13})(?!\d)(.+?)(\d+,\d+)\s+(\d+)\s+(\d+,\d+)\s+(\d+,\d+)\s+(\d+,\d+)$/;
lignes.forEach((ligne: string) => {
const match = ligne.trim().match(regexLigneProduit);
if (match) {
const nomProduit = match[1].trim();
const quantite = parseInt(match[4], 10);
const prixHT = parseFloat(match[2].replace(',', '.'));
const tva = match[6].replace(',', '.') + "%";
productsList[nomProduit] = {
quantité: quantite,
prixHT: prixHT,
TVA: tva
};
}
});
} else {
fournisseur = "Metro"
// on regarde la date d'achat avec une expression reguliere pour metro
const regexDate = /(\d{2})-(\d{2})-(\d{4})/;
const correspondance = data.text.match(regexDate);
if (correspondance) {
date = correspondance[0]
} else {
date = "non trouvé"
}
}
// on renvoie les valeur avec un seul objet
const res : tab_res = {
marque: fournisseur,
date_achats: date,
products: productsList
}
console.log(res);
})
.catch((error: unknown) => {
console.error("Erreur :", error);
});
}
export default pdf_parse;

View File

@@ -0,0 +1,24 @@
/ : accueil pour le public avec la visualisation des produits dans le stock et la date prévisionelle des prochaines courses
/login : page de connexion
/create-account : page pour que l'admin crée des comptes renseignant son niveau d'accès
post /create-account : ajouter les informations de compte dans la DB
/dashboard : page recensant les indicateur clé des ventes pour les vendeur / bureaux
/new-purchase : page d'import de facture ou pour la sasie manulle des achats
post /new-purchase/file : extraire les données d'une facture et la mettre dans la DB
post /new-purchase/new : mettre les produits saisie manullemment dans la DB
/product-report : page de déclaration de casse/perte
/product-report/new : page pour déclarer la casse/perte d'un produit
post /product-report/new : modifier les informations du stock dans la DB
/stock : page de visualisation du stock virtuel
/shopping-list : gestion des listes de course (création, dupplication, suppression)
/shopping-list/${list_id} : page de gestion d'une liste de course
/webhooks/sumup : Lorsque un client à payé modifie les informations du stock dans la DB

View File

@@ -0,0 +1,74 @@
import {Router, Request as ExpressRequest} from "express";
import formidable from "formidable";
import type { Files, File } from "formidable";
// import pdf_parse from "../modules/pdf-parse";
// ----------------------------------------
// Router config
// ----------------------------------------
const router = Router();
// ----------------------------------------
// Routes
// ----------------------------------------
router.get("/", (req,res) => {
let html: string = `
<form action="/new-purchase/file" method="POST" enctype="multipart/form-data">
<input type="file" name="image">
<button type="submit">Envoyer</button>
</form>`;
res.send(html);
});
router.post("/file", async (req,res) => {
try {
const files = await file_form_handler(req);
const file_paths: string[] = [];
for (const file of Object.values(files) as any[]) {
if (Array.isArray(file)) {
for (const f of file) {
file_paths.push(f.filepath);
}
} else {
file_paths.push(file.filepath);
}
}
const results_of_parsing = [];
for (const path of file_paths) {
results_of_parsing.push(path);
}
res.json(results_of_parsing);
} catch (error) {
console.error(error);
res.status(500).json({
error: error instanceof Error ? error.message : "Unknown error"
});
}
});
// fonction pour gérer la récéption des fichier par un form
function file_form_handler(req: ExpressRequest): Promise<Files> {
return new Promise<Files>((resolve, reject) => {
const form = formidable({ multiples: true});
form.parse(req, (error: Error | null, fields: any, files: Files) => {
if (error){
reject(error);
return;
}
resolve({ ...fields, ...files})
})
})
}
// ----------------------------------------
// Export router
// ----------------------------------------
export default router;

View File

@@ -0,0 +1,18 @@
import {Router} from "express";
// ----------------------------------------
// Router config
// ----------------------------------------
const router = Router();
// ----------------------------------------
// Routes
// ----------------------------------------
router.get("/", (req, res) => {
res.json("Server Alive !")
})
// ----------------------------------------
// Export router
// ----------------------------------------
export default router;

27
src/server.ts Normal file
View File

@@ -0,0 +1,27 @@
import express from "express";
// ----------------------------------------
// Backend configuration
// ----------------------------------------
const PORT = 5000;
const app = express();
// ----------------------------------------
// Middleware configuration
// ----------------------------------------
app.use(express.json());
app.use(express.urlencoded( {extended: false }));
// ----------------------------------------
// Routes import
// ----------------------------------------
import statusRoutes from "./routes/status.routes";
import purchaseRoutes from "./routes/purchase.routes";
app.use("/status", statusRoutes);
app.use("/new-purchase", purchaseRoutes);
// ----------------------------------------
// Starting Server
// ----------------------------------------
app.listen(PORT, () => console.log("Server started at port : " + PORT));

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["dist", "node_modules"]
}