Compare commits
6 Commits
3f42d80768
...
core/route
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de3bcbaea2 | ||
|
|
b1ee077754 | ||
|
|
d82ae9f313 | ||
|
|
27de44d895 | ||
|
|
00949c60c5 | ||
|
|
2ca6dc31a9 |
87
package-lock.json
generated
87
package-lock.json
generated
@@ -10,10 +10,13 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1"
|
"express": "^5.2.1",
|
||||||
|
"formidable": "^3.5.4",
|
||||||
|
"pdf-parse-fork": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
|
"@types/formidable": "^3.5.1",
|
||||||
"@types/node": "^25.9.3",
|
"@types/node": "^25.9.3",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
@@ -61,6 +64,27 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.21.3 || >=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@paralleldrive/cuid2": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "^1.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tsconfig/node10": {
|
"node_modules/@tsconfig/node10": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||||
@@ -135,6 +159,16 @@
|
|||||||
"@types/send": "*"
|
"@types/send": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/formidable": {
|
||||||
|
"version": "3.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-3.5.1.tgz",
|
||||||
|
"integrity": "sha512-aFQijSGbD8JCeEST2LEbwR7faHynbt43lojLIcTM/QhB2U06h41ZVRFVEql+Z1xdL+aKGIzm69V/P/uSW9N6XA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/http-errors": {
|
"node_modules/@types/http-errors": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||||
@@ -147,7 +181,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz",
|
||||||
"integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==",
|
"integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": ">=7.24.0 <7.24.7"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
@@ -247,6 +280,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/asap": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
@@ -456,6 +495,16 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dezalgo": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"asap": "^2.0.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||||
@@ -629,6 +678,23 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/formidable": {
|
||||||
|
"version": "3.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
|
||||||
|
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
|
"dezalgo": "^1.0.4",
|
||||||
|
"once": "^1.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -970,6 +1036,11 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-ensure": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
|
||||||
|
"integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.1.14",
|
"version": "3.1.14",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
||||||
@@ -1061,6 +1132,18 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pdf-parse-fork": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf-parse-fork/-/pdf-parse-fork-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ovXkJaTtw8PfLNhBThKHKsZlT6WrCkVKY/QgsDK5GiD/tuL2qzezSlHpEgqHCnq3r/0GOq3NZ+won78KL/dAjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"node-ensure": "^0.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1"
|
"express": "^5.2.1",
|
||||||
|
"formidable": "^3.5.4",
|
||||||
|
"pdf-parse-fork": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
|
"@types/formidable": "^3.5.1",
|
||||||
"@types/node": "^25.9.3",
|
"@types/node": "^25.9.3",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|||||||
102
src/modules/pdf-parse.ts
Normal file
102
src/modules/pdf-parse.ts
Normal 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;
|
||||||
@@ -3,13 +3,17 @@
|
|||||||
/login : page de connexion
|
/login : page de connexion
|
||||||
|
|
||||||
/create-account : page pour que l'admin crée des comptes renseignant son niveau d'accès
|
/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
|
/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
|
/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 : page de déclaration de casse/perte
|
||||||
/product-report/new : page pour déclarer la casse/perte d'un produit
|
/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
|
/stock : page de visualisation du stock virtuel
|
||||||
|
|
||||||
@@ -17,8 +21,4 @@
|
|||||||
/shopping-list/${list_id} : page de gestion d'une liste de course
|
/shopping-list/${list_id} : page de gestion d'une liste de course
|
||||||
|
|
||||||
|
|
||||||
post /new-purchase : extraire les données d'une facture et la mettre dans la DB / mettre les produits saisie manullemment dans la DB
|
|
||||||
post /create-account : ajouter les informations de compte dans la DB
|
|
||||||
post /product-report/new : modifier les informations du stock dans la DB
|
|
||||||
|
|
||||||
/webhooks/sumup : Lorsque un client à payé modifie les informations du stock dans la DB
|
/webhooks/sumup : Lorsque un client à payé modifie les informations du stock dans la DB
|
||||||
74
src/routes/purchase.routes.ts
Normal file
74
src/routes/purchase.routes.ts
Normal 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;
|
||||||
@@ -15,4 +15,4 @@ router.get("/", (req, res) => {
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Export router
|
// Export router
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
module.exports = router;
|
export default router;
|
||||||
@@ -15,7 +15,11 @@ app.use(express.urlencoded( {extended: false }));
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Routes import
|
// Routes import
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
app.use("/status", require("./routes/status.routes"));
|
import statusRoutes from "./routes/status.routes";
|
||||||
|
import purchaseRoutes from "./routes/purchase.routes";
|
||||||
|
|
||||||
|
app.use("/status", statusRoutes);
|
||||||
|
app.use("/new-purchase", purchaseRoutes);
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Starting Server
|
// Starting Server
|
||||||
|
|||||||
Reference in New Issue
Block a user