Compare commits

..

84 Commits

Author SHA1 Message Date
08ce6f9151 Mise à jour des tableurs Ratio 2026-05-31 09:28:11 +02:00
e50ea5c22c reorganisation ouvrture 2026-05-26 12:41:59 +02:00
a037ac5ea7 refonte générale des requêtes 2026-05-14 15:52:14 +02:00
9e06f7257b Modification des requêtes 2026-05-13 17:36:40 +02:00
c2b75076d0 Modification du versionnage auto 2026-05-10 16:12:23 +02:00
703d2ecda3 Réglages feuille Catalogue 2026-05-08 17:40:18 +02:00
fc0e813b2b Réglages power query pour tout le classeur 2026-05-07 19:30:32 +02:00
e1623d8007 Consolidation des calculs de ratio 2026-05-04 11:17:27 +02:00
2930fd4e50 Maj centrale 2 2026-05-03 15:40:49 +02:00
f26b3d6816 Maj centrale 2026-05-03 10:00:55 +02:00
659c93f3a5 Correction bug UF Articles 2026-05-02 15:01:05 +02:00
422799ece0 maj 2026-04-27 18:33:58 +02:00
5f398fd1f2 Ajout de l'utilisateur dans les inventaires 2026-04-24 08:53:42 +02:00
aadbb77cbc Refonte feuille catalogue 2026-04-23 18:11:18 +02:00
090aa78a59 refonte page Catalogue 2026-04-23 11:49:52 +02:00
b5ab432fa9 Ajout du numéro de facture 2026-04-02 09:26:34 +02:00
c04c7e848b Mise en place maj auto 2026-03-31 17:13:19 +02:00
9f3928e584 Mise en place maj auto 2026-03-21 15:00:07 +01:00
61178ceb9f Maj tempo 2026-03-18 13:46:46 +01:00
c510e166d0 Recalcul des ratios 2026-03-06 09:56:07 +01:00
4cf67865b8 Sécurité de la feuille Catalogue 2026-02-19 16:43:31 +01:00
539d1bdce2 Refonte des fichiers Factures et macros 2026-02-05 12:07:49 +01:00
505b89f696 Chois articles hors ou in Stock 2026-02-04 16:14:15 +01:00
4e97d0cb02 Unification connexions requêtes2 2026-02-03 10:10:12 +01:00
b89280d856 Unification connexions requêtes 2026-02-02 13:36:24 +01:00
792b301314 MEF des graphiques 2026-02-02 09:31:18 +01:00
3c6cc9041e Barre d'alerte Ca 2026-01-31 09:19:20 +01:00
0214e41ee2 RAZ app 2 2026-01-30 18:45:35 +01:00
bb3729f4ed RAZ app 2026-01-30 18:32:28 +01:00
b14265418f Accès à la requète 2026-01-30 11:46:42 +01:00
1119986fc7 Cosmétique TDB 2026-01-25 10:41:03 +01:00
4fc444d380 Rafraichissement des tables données 4 2026-01-23 17:11:58 +01:00
096c44ba79 Rafraichissement des tables données 3 2026-01-23 10:32:21 +01:00
61de6db5b5 Rafraichissement des tables données 2 2026-01-22 11:08:35 +01:00
ebf6dc59b0 Rafraichissement des tables données 2026-01-21 11:43:35 +01:00
720894eb2c Ajout d'une version Old pour Cuisine 2026-01-21 05:56:54 +01:00
3cf4ddd12d Remise en état de l'app 2026-01-17 09:22:31 +01:00
27ea802619 Modif tables factures Ca et Frais 2026-01-12 20:22:18 +01:00
8fc8199a6d Remise en état numérotation 2026-01-03 14:57:02 +01:00
0b61a1df8f Remise en état partie Cuisine & salle 2026-01-02 10:37:26 +01:00
8c92460cc2 Remise en état partie Cuisine 2026-01-02 02:27:10 +01:00
4b2f046a0d Rapidité des macros sur Mysql 2025-12-22 11:35:01 +01:00
e97f518d68 Traitement des 100% perso 2025-12-17 23:43:37 +01:00
0f9f011d99 Maj insertion Designation 2025-12-17 13:34:52 +01:00
b635fe1626 Rappel CA non saisis 2025-12-13 13:14:21 +01:00
2c54583a0b Création DemoCuisine 2025-12-09 13:31:21 +01:00
e38aa7595a Raz app 2025-12-08 13:25:09 +01:00
f7bf4f29eb Calcul des Autres Frais 2025-12-08 09:19:52 +01:00
85321437ed Mise en ordre UF Frais 2025-12-07 17:21:42 +01:00
4992c73359 Mise en ordre des frais sur cuisine et salle 2025-12-05 11:13:51 +01:00
86c9683f0d Création des édits et selection 2025-12-04 14:23:00 +01:00
1cb7e7dd66 Création des édits 2025-12-03 10:39:28 +01:00
05db391f1b Refonte de la parte retro 2025-12-01 16:13:44 +01:00
f2ca1ffe77 Création page Edits 2025-11-30 10:33:08 +01:00
155f882f34 Petits bugs 2025-11-30 08:55:49 +01:00
44cda34d49 Oter bt dangereux 2025-11-28 13:24:25 +01:00
222dd979a5 Surlignage feuille Catalogue 2025-11-28 08:42:38 +01:00
f609c09ca9 Epure classeur 2025-11-28 08:10:43 +01:00
bc11cd004b Créeation et gestion Rétrocessions 2025-11-26 16:14:21 +01:00
2939bd41ac Reconstruction de la sauvegarde et du versionnage 2025-11-26 11:00:34 +01:00
3d3c43f684 Construction UF Frais et sécurité 2025-11-25 08:52:55 +01:00
0f246e9acc Construction UF Frais 2025-11-25 08:49:00 +01:00
37393c602d Réparation des tables 2025-11-21 12:01:42 +01:00
c1f25eb61d Mise en production : mise à jour des classeurs Cuisine/Restauration, scripts de mise à jour, versioning 2025-11-17 15:55:30 +01:00
37465a9ece Numérotation auto des versions 2025-11-15 16:33:00 +01:00
1d769e2f2e Modif gitignore 2025-11-15 11:47:13 +01:00
137b5ccc71 Supprimer Excel/prod/InventairesCache/qte_cache.csv 2025-11-15 10:44:12 +00:00
9f453b95c3 Supprimer Excel/prod/InventairesCache/qte_cache_Ratio_prod.csv 2025-11-15 10:44:02 +00:00
322f32d499 Supprimer Excel/prod/Desktop/carte été transition.xlsx 2025-11-15 10:43:45 +00:00
09bc28bec9 Supprimer Excel/prod/Desktop/ALLERGENES HW - fevrier 2025.xlsx 2025-11-15 10:43:37 +00:00
e14309725f Séparation restauration de Cuisine 2025-11-15 11:37:00 +01:00
6db1a8de28 Bugs mineur sur articles 2025-11-13 20:30:24 +01:00
81818eb39b Nouvel interface 2025-11-12 15:17:34 +01:00
977be7dd80 Refonte complète du classeur 2025-11-11 10:31:36 +01:00
4d6a1e3cf4 Oter fichiers outils 2025-11-09 09:16:33 +01:00
b638d333b5 Ajout notification par mail à la création d'user 2025-11-09 00:48:01 +01:00
498d04b1d1 Cosmétique app users 2025-11-08 17:14:32 +01:00
bd27b02a11 Refonte fichier users 2025-11-08 08:36:41 +01:00
18c08d5c84 optimisation userform Factures 2025-11-06 01:20:03 +01:00
9f60606f5c encart renseignements sur userform Articles 2025-11-05 15:39:58 +01:00
6c15f7efe6 remise en place connexion externe pour users 2025-11-04 06:22:43 +01:00
5b9c48ad20 Page3 dans Factures 2025-10-31 10:47:40 +01:00
ebb844fc13 Merge branch 'master' of https://mj91.fr:448/Michel/Inventaire-gestion 2025-10-30 10:40:52 +01:00
61dc6c5e34 Filtrage des fournisseurs Actifs 2025-10-30 10:38:31 +01:00
40 changed files with 1108 additions and 823 deletions

34
.gitignore vendored
View File

@@ -23,18 +23,30 @@ dist/
# Fichiers temporaires d'Excel (~$fichier.xlsm)
~$*.xls*
~$*.xlsm
# On versionne 1 seul fichier prod "pivot" ; on ignore les exports datés
Excel/prod/Ratio_prod_20*.xlsm
!Excel/prod/Ratio_prod.xlsm
Excel/dev/**
!Excel/dev/.gitkeep
# ----- Dossiers Auth livrables -----
# On nembarque pas les exe ni .env dans le repo (tu livreras ça en zip)
Excel/**/Auth/*.exe
Excel/**/Auth/*.dll
Excel/**/Auth/*.pdb
Excel/**/Auth/.env
# Dossiers générés par Excel — à ne jamais versionner
Excel/dev/Desktop/
Excel/prod/Desktop/
Excel/dev/InventairesCache/
Excel/prod/InventairesCache/
# Archives prod : on les ignore (elles sont recréées par le .bat)
Excel/prod/Ratio_Cuisine_20*.xlsm
Excel/prod/Ratio_Restauration_20*.xlsm
# On versionne uniquement les fichiers prod "pivots"
!Excel/prod/Ratio_Cuisine.xlsm
!Excel/prod/Ratio_Cuisine_VERSION.txt
!Excel/prod/Ratio_Restauration.xlsm
!Excel/prod/Ratio_Restauration_VERSION.txt
Excel/backup/
Softs/
Excel/Tableurs modèles/*NEW*.xlsx
# On ignore tout le dev (fichiers de travail)
Excel/dev/
# (Optionnel si tu veux garder le dossier vide dans Git)
# !Excel/dev/.gitkeep
# ----- Systèmes / IDE -----
.DS_Store

Binary file not shown.

0
Docs/Logo.bmp Normal file
View File

BIN
Docs/Logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,28 +0,0 @@
mois;ref;designation;qte
2025-10;1336;Vinaigre yuzu ;1,1
2025-10;1039;Boursin ;1,1
2025-10;1042;Confiture bonne maman abricot 4 pces;1,1
2025-10;543;Smoothie Ananas Mangue;2,3
2025-10;1322;Smoothie fraise ;2,3
2025-10;1321;smoothie mangue ;2,3
2025-10;455;Cafe Grains Gourmet;2,3
2025-10;458;Cappucino En Poudre;2,3
2025-10;457;Decafeine en poudre ;2,3
2025-10;1055;Confiture bm framboise ;5,87
2025-10;1167;Lasagne ;5,87
2025-10;1166;Mousseline ;5,87
2025-10;1119;Orechiete ;5,87
2025-10;1168;Salade mexicaine ;5,87
2025-10;1151;Sirop d erable ;5,87
2025-10;1118;Tagliatelle ;5,87
2025-10;1120;Trofie ;5,87
2025-10;1587;CORIANDRE BOTTE;5,87
2025-10;1593;ROMARIN BOTTE;5,87
2025-10;1532;ANANAS SWEET BATEAU CAT1 KG;5,87
2025-10;1524;BANANE CAL.MOYEN KG;5,87
2025-10;1565;BLANC POIREAUX KG;5,87
2025-10;1573;CAROTTE PLATEAU CE2 KG;5,87
2025-10;1564;CERFEUIL BOTTE;5,87
2025-10;1521;CHAMP ERENGY-ERYNGII;5,87
2025-10;1575;CHOU BOCK CHOY SHANGAI (PETIT VERT) KG;5,87
2025-10;1545;CHOU CHINOIS CAT 1;5,87
1 mois ref designation qte
2 2025-10 1336 Vinaigre yuzu 1,1
3 2025-10 1039 Boursin 1,1
4 2025-10 1042 Confiture bonne maman abricot 4 pces 1,1
5 2025-10 543 Smoothie Ananas Mangue 2,3
6 2025-10 1322 Smoothie fraise 2,3
7 2025-10 1321 smoothie mangue 2,3
8 2025-10 455 Cafe Grains Gourmet 2,3
9 2025-10 458 Cappucino En Poudre 2,3
10 2025-10 457 Decafeine en poudre 2,3
11 2025-10 1055 Confiture bm framboise 5,87
12 2025-10 1167 Lasagne 5,87
13 2025-10 1166 Mousseline 5,87
14 2025-10 1119 Orechiete 5,87
15 2025-10 1168 Salade mexicaine 5,87
16 2025-10 1151 Sirop d erable 5,87
17 2025-10 1118 Tagliatelle 5,87
18 2025-10 1120 Trofie 5,87
19 2025-10 1587 CORIANDRE BOTTE 5,87
20 2025-10 1593 ROMARIN BOTTE 5,87
21 2025-10 1532 ANANAS SWEET BATEAU CAT1 KG 5,87
22 2025-10 1524 BANANE CAL.MOYEN KG 5,87
23 2025-10 1565 BLANC POIREAUX KG 5,87
24 2025-10 1573 CAROTTE PLATEAU CE2 KG 5,87
25 2025-10 1564 CERFEUIL BOTTE 5,87
26 2025-10 1521 CHAMP ERENGY-ERYNGII 5,87
27 2025-10 1575 CHOU BOCK CHOY SHANGAI (PETIT VERT) KG 5,87
28 2025-10 1545 CHOU CHINOIS CAT 1 5,87

View File

@@ -1 +0,0 @@
mois;ref;designation;qte
1 mois ref designation qte

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1 +0,0 @@
2.26

BIN
Images/Logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

BIN
Images/cuisine.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
Images/restauration.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

537
README.md
View File

@@ -1,203 +1,428 @@
# Inventaire-Gestion
# Ratio Inventaires
Ce projet permet de gérer les inventaires et les ratios de plusieurs sites à partir dun fichier Excel central et dune base MySQL.
Projet Excel/VBA permettant de gérer les inventaires, les articles, les fournisseurs, les factures, le chiffre daffaires et les ratios de consommation pour plusieurs activités.
## Fonctionnalités
* Mise à jour du pilote ODBC sur machine client
* Authentification par site
* Lecture automatique du fichier `ratio.xlsm`
* Insertion des données dans la base correspondante
* Gestion des articles et inventaire par code barres
* Affichage dans Streamlit (à venir)
# 🚀 Installation du pilote MySQL ODBC 8.3
## ✅ Objectif
Assurer une compatibilité totale entre les fichiers Excel connectés à MySQL et le pilote ODBC, en uniformisant tous les postes avec la **version 8.3.0 du connecteur MySQL ODBC**.
Le projet est organisé pour travailler proprement avec PyCharm, Git/Gitea et des scripts de mise en production automatisés.
---
## 📥 Étapes d'installation
## Objectif du projet
1. Télécharger le fichier `mysql-connector-odbc-8.3.0-winx64.msi` depuis le site officiel :
Le projet permet de maintenir plusieurs classeurs Excel métier :
👉 [https://dev.mysql.com/downloads/connector/odbc/](https://dev.mysql.com/downloads/connector/odbc/)
- `Ratio_Cuisine.xlsm`
- `Ratio_Restauration.xlsm`
- `Démo_Ratio_Cuisine.xlsm`
2. Copier le fichier dans ce dossier sur chaque poste cible :
Les fichiers de développement sont conservés dans `Excel/dev`, puis copiés vers `Excel/prod` par des scripts de mise en production.
```
C:\Installers\
```
3. Créer un fichier `Installer_ODBC_93.bat` contenant :
```bat
@echo off
echo ===============================
echo Installation MySQL ODBC 8.3.0
echo ===============================
SET MYPATH=C:\Installers
SET INSTALLER=%MYPATH%\mysql-connector-odbc-8.3.0-winx64.msi
IF EXIST "%INSTALLER%" (
echo >> Démarrage de l'installation silencieuse...
msiexec /i "%INSTALLER%" /qn
echo >> Installation terminée avec succès.
pause
) ELSE (
echo >> Fichier MSI non trouvé :
echo >> %INSTALLER%
pause
)
```
4. Lancer le script :
* clic droit sur `Installer_ODBC_83.bat`
* puis **"Exécuter en tant quadministrateur"**
Le numéro de version est géré automatiquement par fichier `.txt` interne, puis écrit directement dans le classeur Excel livré.
---
## 🔁 (Optionnel) Désinstallation d'une version précédente
## Organisation des dossiers
Pour supprimer proprement une version antérieure (ex : 8.2), vous pouvez ajouter :
```text
Ratio_Inventaires/
├── Docs/
├── Excel/
│ ├── backup/
│ │ └── anciennes versions sauvegardées
│ │
│ ├── dev/
│ │ ├── Ratio_Cuisine_dev.xlsm
│ │ ├── Ratio_Cuisine_VERSION.txt
│ │ ├── Ratio_Restauration_dev.xlsm
│ │ └── Ratio_Restauration_VERSION.txt
│ │
│ └── prod/
│ ├── Démo_Ratio_Cuisine.xlsm
│ ├── Ratio_Cuisine.xlsm
│ └── Ratio_Restauration.xlsm
└── Scripts/
├── Maj_prod_Cuisine.bat
├── Maj_prod_Restauration.bat
├── maj_version.py
└── set_cell_silent.vbs
```
---
## Rôle des dossiers
### `Excel/dev`
Contient les fichiers de travail.
```text
Ratio_Cuisine_dev.xlsm
Ratio_Restauration_dev.xlsm
```
Ces fichiers sont les fichiers modifiables.
Les fichiers `*_VERSION.txt` restent uniquement dans `dev`. Ils servent de compteurs internes pour les scripts de versionnage.
---
### `Excel/prod`
Contient les fichiers livrables.
```text
Ratio_Cuisine.xlsm
Ratio_Restauration.xlsm
Démo_Ratio_Cuisine.xlsm
```
Les fichiers de production ne nécessitent plus de fichier `.txt` à côté. La version est directement écrite dans le classeur Excel.
---
### `Excel/backup`
Contient les anciennes versions de production sauvegardées automatiquement avant remplacement.
Exemple :
```text
Ratio_Cuisine_Vers1.0.15.xlsm
Ratio_Restauration_Vers2.1.3.xlsm
```
Le nombre de sauvegardes conservées est limité dans les scripts `.bat`.
---
### `Scripts`
Contient les scripts dautomatisation :
```text
Maj_prod_Cuisine.bat
Maj_prod_Restauration.bat
maj_version.py
set_cell_silent.vbs
```
---
## Versionnage
Le projet utilise deux familles de versions :
```text
Cuisine → 1.x.x
Restauration → 2.x.x
```
Les fichiers de version à conserver sont uniquement :
```text
Excel/dev/Ratio_Cuisine_VERSION.txt
Excel/dev/Ratio_Restauration_VERSION.txt
```
Les fichiers `.txt` de version ne doivent plus être copiés dans `Excel/prod`.
---
## Fonctionnement du script de version
Le script :
```text
Scripts/maj_version.py
```
fait les opérations suivantes :
1. lit le fichier `*_VERSION.txt` dans `Excel/dev` ;
2. force le numéro majeur selon le classeur ;
3. incrémente le patch ;
4. réécrit le fichier `.txt` ;
5. écrit la version directement dans le classeur Excel de production.
Exemple de version écrite dans Excel :
```text
Version : 1.0.12
Version : 2.1.4
```
Lécriture dans le classeur Excel est faite par :
```text
Scripts/set_cell_silent.vbs
```
Ce script ouvre Excel en arrière-plan, modifie la cellule de version, sauvegarde et ferme le classeur.
---
## Cellule de version dans Excel
La version est écrite automatiquement dans la feuille :
```text
Tableau de bord
```
La cellule utilisée doit être réservée à la version.
Recommandation actuelle :
```text
E1 = Version : x.x.x
```
Attention : `C1` est utilisé par le contexte du site, notamment `SiteCanonique`, et ne doit donc pas être utilisé pour la version.
---
## Mise en production Cuisine
Depuis le dossier `Scripts`, lancer :
```bat
msiexec /x {GUID-DE-LA-VERSION-8.2} /qn
Maj_prod_Cuisine.bat CUISINE
```
*(à compléter avec l'identifiant de produit si nécessaire)*
Le script effectue :
1. sauvegarde de lancienne version de production ;
2. copie de `Excel/dev/Ratio_Cuisine_dev.xlsm` vers `Excel/prod/Ratio_Cuisine.xlsm` ;
3. incrément de `Excel/dev/Ratio_Cuisine_VERSION.txt` ;
4. écriture de la version dans le classeur de production ;
5. nettoyage des anciennes sauvegardes.
---
## 🧩 Conseils
## Mise en production Restauration
* Intégrez ces fichiers dans votre dépôt (ex : dossier `Installers/`)
* Versionnez votre script dans Git (Gitea) pour faciliter le déploiement sur tous les sites
* Utilisez RustDesk ou accès direct pour l'installation sur les PC distants
Depuis le dossier `Scripts`, lancer :
# 🔧 Gestion et distribution du fichier Excel `Ratio_prod.xlsm`
Ce document décrit le fonctionnement mis en place pour garantir une version stable et toujours à jour du fichier Excel `Ratio_prod.xlsm`, utilisé dans le cadre du projet **Ratio & Inventaires**.
---
## 🗃️ Organisation des fichiers
### Structure dans le projet (Git)
/Excel/
├── dev/
│ └── Ratio\_dev.xlsm ← Fichier de travail
├── prod/
│ └── Ratio\_prod.xlsm ← Fichier de production (non suivi par Git)
* `Ratio_dev.xlsm` : version de développement modifiable, suivie par Git.
* `Ratio_prod.xlsm` : version validée, protégée (ajoutée au `.gitignore` pour éviter tout push accidentel).
---
## 🖥️ Synchronisation automatique avec le NAS Synology
Le fichier `Ratio_prod.xlsm` est copié automatiquement sur le NAS Synology, dans un dossier partagé :
### ⚙️ Configuration
* **Synology Drive Server** est activé sur le NAS.
* **Synology Drive Client** est installé sur le poste de travail.
* Dossier synchronisé : `Partage_Ratio`
* Mode de synchronisation recommandé : `Téléchargement uniquement`.
---
## 🔌 Copie automatique vers clé USB (via NAS)
### Prérequis
* Application **USB Copy** installée et activée sur le NAS.
### Fonctionnement
1. Brancher une clé USB sur le port en façade du NAS.
2. Le NAS copie automatiquement `Ratio_prod.xlsm` sur la clé USB si une version plus récente est disponible.
3. Le fichier est copié dans le dossier racine de la clé, ou dans un dossier `Ratio/`.
### Avantages
* Pas besoin dordinateur pour copier à la main.
* Copie toujours à jour dès que la clé est branchée.
---
## 🛠️ Installation manuelle depuis une clé USB
Contenu du dossier USB :
```
INSTALL_RATIO/
├── Ratio_prod.xlsm
└── Installer_Prod.bat
```bat
Maj_prod_Restauration.bat RESTAURATION
```
### Étapes :
Le script effectue :
1. Brancher la clé USB sur le poste utilisateur.
2. Lancer `Installer_Prod.bat` **en tant quadministrateur**.
3. Le fichier sera copié dans :
```
C:\Program Files\Ratio\Ratio.xlsm
```
et protégé en lecture seule.
1. sauvegarde de lancienne version de production ;
2. copie de `Excel/dev/Ratio_Restauration_dev.xlsm` vers `Excel/prod/Ratio_Restauration.xlsm` ;
3. incrément de `Excel/dev/Ratio_Restauration_VERSION.txt` ;
4. écriture de la version dans le classeur de production ;
5. nettoyage des anciennes sauvegardes.
---
## ♻️ Restaurer la version de production dans le projet (dev)
## Fichier de démonstration
Si la version de travail (`dev`) a été corrompue ou modifiée par erreur :
Le fichier :
```text
Excel/prod/Démo_Ratio_Cuisine.xlsm
```
est destiné à présenter le fonctionnement sans installation réseau obligatoire.
Il peut contenir des données locales dans des feuilles de type :
```text
DATA_Articles
DATA_Fournisseurs
DATA_Factures
DATA_Ca
DATA_Inventaires
```
La version démo peut fonctionner sans connexion MySQL, sans ODBC et sans fichier `.txt` distribué.
Recommandations pour la version démo :
- données locales uniquement ;
- pas didentifiants MySQL ;
- pas de requêtes Power Query actives ;
- pas de fichier de version `.txt` livré ;
- limitation volontaire des ajouts pour éviter un usage réel non maîtrisé.
---
## Connexions MySQL et ODBC
Les classeurs de production connectés peuvent utiliser MySQL via ODBC.
La version recommandée du pilote est :
```text
MySQL ODBC 8.3 Unicode Driver
```
Sur les postes clients connectés, le pilote doit être installé avant utilisation.
La version démo locale, elle, ne doit pas dépendre du pilote ODBC.
---
## Installation du pilote MySQL ODBC 8.3
Télécharger le pilote officiel :
```text
mysql-connector-odbc-8.3.0-winx64.msi
```
Puis le placer dans :
```text
C:\Installers\
```
Exemple de script dinstallation silencieuse :
```bat
@echo off
echo ===============================
echo Installation MySQL ODBC 8.3.0
echo ===============================
set MYPATH=C:\Installers
set INSTALLER=%MYPATH%\mysql-connector-odbc-8.3.0-winx64.msi
if exist "%INSTALLER%" (
echo Démarrage de l'installation silencieuse...
msiexec /i "%INSTALLER%" /qn
echo Installation terminée.
) else (
echo Fichier MSI non trouvé :
echo %INSTALLER%
)
```
À lancer en administrateur.
---
## Bonnes pratiques Git/Gitea
À versionner :
```text
Excel/dev/Ratio_Cuisine_dev.xlsm
Excel/dev/Ratio_Restauration_dev.xlsm
Excel/dev/*_VERSION.txt
Scripts/*.bat
Scripts/*.py
Scripts/*.vbs
README.md
```
À ignorer ou éviter de versionner :
```text
Excel/backup/
~$*.xlsm
*.tmp
*.bak
```
Selon la stratégie choisie, les fichiers de `Excel/prod` peuvent être versionnés ou non. Dans ce projet, ils servent de fichiers livrables générés par les scripts.
---
## Exemple de `.gitignore`
```gitignore
# Fichiers temporaires Excel
~$*.xlsm
~$*.xlsx
*.tmp
*.bak
# Sauvegardes générées
Excel/backup/
# Cache Python
__pycache__/
*.pyc
# Environnements virtuels
.venv/
venv/
```
---
## Checklist avant mise en production
Avant de lancer un script de mise en production :
1. fermer le classeur Excel concerné ;
2. vérifier quaucun fichier `~$Ratio_*.xlsm` nest présent ;
3. sauvegarder les modifications dans le fichier `dev` ;
4. lancer le script `.bat` correspondant ;
5. vérifier que la version est bien écrite dans le classeur `prod` ;
6. faire un commit Git/Gitea.
---
## Commandes utiles
Vérifier létat Git :
```bash
cp Excel/prod/Ratio_prod.xlsm Excel/dev/Ratio_dev.xlsm
git status
```
Ou sous Windows :
Ajouter les modifications :
```powershell
Copy-Item -Path "Excel\prod\Ratio_prod.xlsm" -Destination "Excel\dev\Ratio_dev.xlsm" -Force
```bash
git add README_old.md Scripts/ Excel/dev/
```
Créer un commit :
```bash
git commit -m "Normalise versioning and prod deployment scripts"
```
Envoyer vers Gitea :
```bash
git push
```
---
## 🧾 Notes complémentaires
## Dépannage
* Le fichier `Ratio_prod.xlsm` nest **pas suivi par Git**, car exclu via `.gitignore`.
* Il est **protégé en lecture seule** sur le poste utilisateur.
* Une sauvegarde automatique est possible via **Hyper Backup** ou les **snapshots** du NAS.
### Erreur : fichier utilisé par un autre processus
Cause probable : le classeur est encore ouvert dans Excel ou un fichier temporaire existe.
Solution :
```bat
taskkill /F /IM excel.exe
```
Puis vérifier que le fichier temporaire a disparu :
```text
~$Ratio_Cuisine.xlsm
~$Ratio_Restauration.xlsm
```
---
# 📋 Checklist Release DEV → PROD
### Erreur dans `set_cell_silent.vbs` au moment de fermer Excel
Pour garantir que DEV reste ouvert et que PROD soit protégé :
Cause probable : Excel est resté en arrière-plan.
1. **Travailler dans DEV** : `Excel/dev/Ratio_dev.xlsm` (projet VBA non verrouillé).
2. **Lancer le script batch** (`build_prod.bat`).
* Copie DEV → PROD
* Application des protections (feuilles, structure, horodatage)
* Vérification/verrouillage du projet VBA PROD (manuel si nécessaire)
3. **Vérifier PROD** :
* Feuilles protégées
* Structure du classeur protégée
* Projet VBA verrouillé (mot de passe, déjà posé une fois)
4. **Distribuer PROD** : via NAS, USB ou dossier client.
👉 DEV reste toujours libre, PROD reste verrouillé et sûr.
Solution : utiliser la version robuste de `set_cell_silent.vbs`, qui ferme explicitement le classeur et lapplication Excel après sauvegarde.
---

206
README_old.md Normal file
View File

@@ -0,0 +1,206 @@
# Inventaire-Gestion
Ce projet permet de gérer les inventaires et les ratios de plusieurs sites à partir dun fichier Excel central et dune base MySQL.
## Fonctionnalités
* Mise à jour du pilote ODBC sur machine client
* Authentification par site
* Lecture automatique du fichier `ratio.xlsm`
* Insertion des données dans la base correspondante
* Gestion des articles et inventaire par code barres
* Affichage dans Streamlit (à venir)
# 🚀 Installation du pilote MySQL ODBC 8.3
## ✅ Objectif
Assurer une compatibilité totale entre les fichiers Excel connectés à MySQL et le pilote ODBC, en uniformisant tous les postes avec la **version 8.3.0 du connecteur MySQL ODBC**.
---
## 📥 Étapes d'installation
1. Télécharger le fichier `mysql-connector-odbc-8.3.0-winx64.msi` depuis le site officiel :
👉 [https://dev.mysql.com/downloads/connector/odbc/](https://dev.mysql.com/downloads/connector/odbc/)
2. Copier le fichier dans ce dossier sur chaque poste cible :
```
C:\Installers\
```
3. Créer un fichier `Installer_ODBC_93.bat` contenant :
```bat
@echo off
echo ===============================
echo Installation MySQL ODBC 8.3.0
echo ===============================
SET MYPATH=C:\Installers
SET INSTALLER=%MYPATH%\mysql-connector-odbc-8.3.0-winx64.msi
IF EXIST "%INSTALLER%" (
echo >> Démarrage de l'installation silencieuse...
msiexec /i "%INSTALLER%" /qn
echo >> Installation terminée avec succès.
pause
) ELSE (
echo >> Fichier MSI non trouvé :
echo >> %INSTALLER%
pause
)
```
4. Lancer le script :
* clic droit sur `Installer_ODBC_83.bat`
* puis **"Exécuter en tant quadministrateur"**
---
## 🔁 (Optionnel) Désinstallation d'une version précédente
Pour supprimer proprement une version antérieure (ex : 8.2), vous pouvez ajouter :
```bat
msiexec /x {GUID-DE-LA-VERSION-8.2} /qn
```
*(à compléter avec l'identifiant de produit si nécessaire)*
---
## 🧩 Conseils
* Intégrez ces fichiers dans votre dépôt (ex : dossier `Installers/`)
* Versionnez votre script dans Git (Gitea) pour faciliter le déploiement sur tous les sites
* Utilisez RustDesk ou accès direct pour l'installation sur les PC distants
# 🔧 Gestion et distribution du fichier Excel `Ratio_prod.xlsm`
Ce document décrit le fonctionnement mis en place pour garantir une version stable et toujours à jour du fichier Excel `Ratio_prod.xlsm`, utilisé dans le cadre du projet **Ratio & Inventaires**.
---
## 🗃️ Organisation des fichiers
### Structure dans le projet (Git)
/Excel/
├── dev/
│ └── Ratio\_dev.xlsm ← Fichier de travail
├── prod/
│ └── Ratio\_prod.xlsm ← Fichier de production (non suivi par Git)
* `Ratio_dev.xlsm` : version de développement modifiable, suivie par Git.
* `Ratio_prod.xlsm` : version validée, protégée (ajoutée au `.gitignore` pour éviter tout push accidentel).
---
## 🖥️ Synchronisation automatique avec le NAS Synology
Le fichier `Ratio_prod.xlsm` est copié automatiquement sur le NAS Synology, dans un dossier partagé :
### ⚙️ Configuration
* **Synology Drive Server** est activé sur le NAS.
* **Synology Drive Client** est installé sur le poste de travail.
* Dossier synchronisé : `Partage_Ratio`
* Mode de synchronisation recommandé : `Téléchargement uniquement`.
---
## 🔌 Copie automatique vers clé USB (via NAS)
### Prérequis
* Application **USB Copy** installée et activée sur le NAS.
### Fonctionnement
1. Brancher une clé USB sur le port en façade du NAS.
2. Le NAS copie automatiquement `Ratio_prod.xlsm` sur la clé USB si une version plus récente est disponible.
3. Le fichier est copié dans le dossier racine de la clé, ou dans un dossier `Ratio/`.
### Avantages
* Pas besoin dordinateur pour copier à la main.
* Copie toujours à jour dès que la clé est branchée.
---
## 🛠️ Installation manuelle depuis une clé USB
Contenu du dossier USB :
```
INSTALL_RATIO/
├── Ratio_prod.xlsm
└── Installer_Prod.bat
```
### Étapes :
1. Brancher la clé USB sur le poste utilisateur.
2. Lancer `Installer_Prod.bat` **en tant quadministrateur**.
3. Le fichier sera copié dans :
```
C:\Program Files\Ratio\Ratio.xlsm
```
et protégé en lecture seule.
---
## ♻️ Restaurer la version de production dans le projet (dev)
Si la version de travail (`dev`) a été corrompue ou modifiée par erreur :
```bash
cp Excel/prod/Ratio_Cuisine.xlsm Excel/dev/Ratio_Cuisine_dev_old.xlsm
```
Ou sous Windows :
```powershell
Copy-Item -Path "Excel\prod\Ratio_prod.xlsm" -Destination "Excel\dev\Ratio_dev.xlsm" -Force
```
---
## 🧾 Notes complémentaires
* Le fichier `Ratio_prod.xlsm` nest **pas suivi par Git**, car exclu via `.gitignore`.
* Il est **protégé en lecture seule** sur le poste utilisateur.
* Une sauvegarde automatique est possible via **Hyper Backup** ou les **snapshots** du NAS.
---
# 📋 Checklist Release DEV → PROD
Pour garantir que DEV reste ouvert et que PROD soit protégé :
1. **Travailler dans DEV** : `Excel/dev/Ratio_dev.xlsm` (projet VBA non verrouillé).
2. **Lancer le script batch** (`build_prod.bat`).
* Copie DEV → PROD
* Application des protections (feuilles, structure, horodatage)
* Vérification/verrouillage du projet VBA PROD (manuel si nécessaire)
3. **Vérifier PROD** :
* Feuilles protégées
* Structure du classeur protégée
* Projet VBA verrouillé (mot de passe, déjà posé une fois)
4. **Distribuer PROD** : via NAS, USB ou dossier client.
👉 DEV reste toujours libre, PROD reste verrouillé et sûr.
---
## Auteur
Michel

View File

@@ -1,92 +0,0 @@
@echo off
chcp 1252 >nul
setlocal EnableExtensions
REM ===================== PARAMS =====================
set "ROOT=C:\Users\miche\PycharmProjects\Ratio_Inventaires"
set "DEV_XLSM=%ROOT%\Excel\dev\Ratio_dev.xlsm"
set "PROD_DIR=%ROOT%\Excel\prod"
set "PROD_XLSM=%PROD_DIR%\Ratio_prod.xlsm"
set "VERSION_TXT=%ROOT%\Excel\dev\VERSION.txt"
set "SHEET_NAME=Tableau de bord"
set "CELL_ADDR=U1"
echo.
echo === [1/4] Prep ===============================================
if not exist "%PROD_DIR%" md "%PROD_DIR%"
if not exist "%DEV_XLSM%" goto ERR_NO_DEV
if not exist "%VERSION_TXT%" (
>"%VERSION_TXT%" echo 1.0.0
echo [INFO] VERSION.txt created with 1.0.0
)
for /f %%i in ('powershell -NoProfile -Command "(Get-Date).ToString('yyyy_MM_dd')"') do set "DATESTAMP=%%i"
set "ARCHIVE_FILE=%PROD_DIR%\Ratio_prod_%DATESTAMP%.xlsm"
echo.
echo === [2/4] Lire VERSION.txt et maj cellule =====================
for /f "usebackq delims=" %%v in ("%VERSION_TXT%") do set "NEW_VER=%%v"
set "NEW_VER=%NEW_VER: =%"
if "%NEW_VER%"=="" goto ERR_EMPTY_VER
echo Version: %NEW_VER%
REM Mise a jour de P1 dans DEV via VBS (macros & evenements OFF)
cscript //nologo "%ROOT%\Scripts\set_cell_silent.vbs" "%DEV_XLSM%" "%SHEET_NAME%" "%CELL_ADDR%" "%NEW_VER%"
if errorlevel 1 goto U1_FAIL
echo [OK] U1 updated on DEV: %NEW_VER%
goto AFTER_U1
:U1_FAIL
echo [WARN] U1 update failed on DEV (continuing).
:AFTER_U1
echo.
echo === [3/4] Backup PROD et purge archives ======================
if not exist "%PROD_XLSM%" goto NO_BACKUP
echo Backup -> "%ARCHIVE_FILE%"
copy /Y "%PROD_XLSM%" "%ARCHIVE_FILE%" >nul
goto AFTER_BACKUP
:NO_BACKUP
echo Pas de PROD a sauvegarder.
:AFTER_BACKUP
REM Garder uniquement les 5 dernieres archives
powershell -NoProfile -Command "Get-ChildItem -Path '%PROD_DIR%' -Filter 'Ratio_prod_20*_??_??.xlsm' | Sort-Object LastWriteTime -Descending | Select-Object -Skip 5 | Remove-Item -Force -ErrorAction SilentlyContinue"
echo.
echo === [4/4] Copier DEV -> PROD =================================
copy /Y "%DEV_XLSM%" "%PROD_XLSM%"
if errorlevel 1 goto ERR_COPY
>"%PROD_DIR%\VERSION.txt" echo %NEW_VER%
echo.
echo [OK] Termine :
echo Version : %NEW_VER%
echo PROD : "%PROD_XLSM%"
echo VERSION : "%PROD_DIR%\VERSION.txt"
echo.
endlocal & exit /b 0
REM ===================== ERRORS =====================
:ERR_NO_DEV
echo [ERROR] DEV file not found: "%DEV_XLSM%"
endlocal & exit /b 1
:ERR_EMPTY_VER
echo [ERROR] VERSION.txt is empty. Put a version like 1.8.3 and rerun.
endlocal & exit /b 1
:ERR_COPY
echo [ERROR] Copy DEV -> PROD failed.
endlocal & exit /b 1

View File

@@ -0,0 +1,115 @@
@echo off
setlocal ENABLEDELAYEDEXPANSION
cd /d "%~dp0"
echo --- Mise en prod Ratio_Cuisine ---
set "ROOT=%~dp0.."
set "VERSION_FILE=%ROOT%\Excel\dev\Ratio_Cuisine_VERSION.txt"
set "SRC=%ROOT%\Excel\dev\Ratio_Cuisine_dev.xlsm"
set "DST_DIR=%ROOT%\Excel\prod"
set "DST=%DST_DIR%\Ratio_Cuisine.xlsm"
set "BACKUP_DIR=%ROOT%\Excel\backup"
set KEEP_BACKUPS=10
echo.
echo ROOT = %ROOT%
echo SRC = %SRC%
echo DST = %DST%
echo BACKUP_DIR = %BACKUP_DIR%
echo VERSION_FILE= %VERSION_FILE%
echo.
set "OLD_VERSION="
if exist "%VERSION_FILE%" (
set /p OLD_VERSION=<"%VERSION_FILE%"
)
if not exist "%SRC%" (
echo ERREUR : fichier source introuvable :
echo %SRC%
exit /b 1
)
if not exist "%DST_DIR%" (
echo ERREUR : dossier de destination introuvable :
echo %DST_DIR%
exit /b 1
)
if not exist "%BACKUP_DIR%" (
echo Creation du dossier backup :
echo %BACKUP_DIR%
mkdir "%BACKUP_DIR%"
)
if exist "%DST_DIR%\~$Ratio_Cuisine.xlsm" (
echo ERREUR : Ratio_Cuisine.xlsm est ouvert dans Excel.
echo Ferme le classeur de production avant de relancer la mise en prod.
exit /b 1
)
if exist "%DST%" (
if "%OLD_VERSION%"=="" (
set "BACKUP=%BACKUP_DIR%\Ratio_Cuisine_sansVersion.xlsm"
) else (
set "BACKUP=%BACKUP_DIR%\Ratio_Cuisine_Vers%OLD_VERSION%.xlsm"
)
echo Sauvegarde de l'ancienne production...
echo De : %DST%
echo Vers : !BACKUP!
copy /Y "%DST%" "!BACKUP!"
if errorlevel 1 (
echo ERREUR : la sauvegarde de l'ancienne production a echoue.
exit /b 1
)
if not exist "!BACKUP!" (
echo ERREUR : le fichier de sauvegarde n'a pas ete cree.
echo Attendu : !BACKUP!
exit /b 1
)
echo Sauvegarde OK : !BACKUP!
) else (
echo Aucun ancien fichier de production trouve, pas de sauvegarde a faire.
)
echo.
echo Copie vers la version de production...
copy /Y "%SRC%" "%DST%"
if errorlevel 1 (
echo ERREUR lors de la copie vers production.
exit /b 1
)
if not exist "%DST%" (
echo ERREUR : le fichier de production n'a pas ete cree.
exit /b 1
)
echo.
echo Mise a jour de version...
python "%~dp0maj_version.py" "%VERSION_FILE%" "%DST%"
if errorlevel 1 (
echo ERREUR lors de la mise a jour de version.
exit /b 1
)
echo.
echo Nettoyage des anciennes sauvegardes Cuisine...
for /f "skip=%KEEP_BACKUPS% delims=" %%F in ('dir /b /a-d /o-d "%BACKUP_DIR%\Ratio_Cuisine_Vers*.xlsm" 2^>nul') do (
echo Suppression ancienne sauvegarde : %%F
del /q "%BACKUP_DIR%\%%F"
)
echo.
echo --- Mise en prod terminee ---
exit /b 0

View File

@@ -0,0 +1,114 @@
@echo off
setlocal ENABLEDELAYEDEXPANSION
cd /d "%~dp0"
echo --- Mise en prod Ratio_Restauration ---
set "ROOT=%~dp0.."
set "VERSION_FILE=%ROOT%\Excel\dev\Ratio_Restauration_VERSION.txt"
set "SRC=%ROOT%\Excel\dev\Ratio_Restauration_dev.xlsm"
set "DST_DIR=%ROOT%\Excel\prod"
set "DST=%DST_DIR%\Ratio_Restauration.xlsm"
set "BACKUP_DIR=%ROOT%\Excel\backup"
set KEEP_BACKUPS=10
echo.
echo ROOT = %ROOT%
echo SRC = %SRC%
echo DST = %DST%
echo BACKUP_DIR = %BACKUP_DIR%
echo VERSION_FILE= %VERSION_FILE%
echo.
set "OLD_VERSION="
if exist "%VERSION_FILE%" (
set /p OLD_VERSION=<"%VERSION_FILE%"
)
if not exist "%SRC%" (
echo ERREUR : fichier source introuvable :
echo %SRC%
exit /b 1
)
if not exist "%DST_DIR%" (
echo ERREUR : dossier de destination introuvable :
echo %DST_DIR%
exit /b 1
)
if not exist "%BACKUP_DIR%" (
echo Creation du dossier backup :
echo %BACKUP_DIR%
mkdir "%BACKUP_DIR%"
)
if exist "%DST_DIR%\~$Ratio_Restauration.xlsm" (
echo ERREUR : Ratio_Restauration.xlsm est ouvert dans Excel.
exit /b 1
)
if exist "%DST%" (
if "%OLD_VERSION%"=="" (
set "BACKUP=%BACKUP_DIR%\Ratio_Restauration_sansVersion.xlsm"
) else (
set "BACKUP=%BACKUP_DIR%\Ratio_Restauration_Vers%OLD_VERSION%.xlsm"
)
echo Sauvegarde de l'ancienne production...
echo De : %DST%
echo Vers : !BACKUP!
copy /Y "%DST%" "!BACKUP!"
if errorlevel 1 (
echo ERREUR : la sauvegarde de l'ancienne production a echoue.
exit /b 1
)
if not exist "!BACKUP!" (
echo ERREUR : le fichier de sauvegarde n'a pas ete cree.
echo Attendu : !BACKUP!
exit /b 1
)
echo Sauvegarde OK : !BACKUP!
) else (
echo Aucun ancien fichier de production trouve, pas de sauvegarde a faire.
)
echo.
echo Copie vers la version de production...
copy /Y "%SRC%" "%DST%"
if errorlevel 1 (
echo ERREUR lors de la copie vers production.
exit /b 1
)
if not exist "%DST%" (
echo ERREUR : le fichier de production n'a pas ete cree.
exit /b 1
)
echo.
echo Mise a jour de version...
python "%~dp0maj_version.py" "%VERSION_FILE%" "%DST%"
if errorlevel 1 (
echo ERREUR lors de la mise a jour de version.
exit /b 1
)
echo.
echo Nettoyage des anciennes sauvegardes Restauration...
for /f "skip=%KEEP_BACKUPS% delims=" %%F in ('dir /b /a-d /o-d "%BACKUP_DIR%\Ratio_Restauration_Vers*.xlsm" 2^>nul') do (
echo Suppression ancienne sauvegarde : %%F
del /q "%BACKUP_DIR%\%%F"
)
echo.
echo --- Mise en prod terminee ---
exit /b 0

View File

@@ -1 +0,0 @@
=== [4/4] Copier DEV - =================================

View File

@@ -0,0 +1,2 @@
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0Maj_Ratio_Cuisine.ps1"

View File

@@ -0,0 +1,42 @@
$source = "\\mj91.fr\PyCharm\Ratio_Inventaires\Excel\prod"
$dest = "C:\Users\$env:USERNAME\Domo91"
$nomFichier = "Ratio_Cuisine.xlsm"
$nomVersion = "Ratio_Cuisine_VERSION.txt"
$sourceXlsm = Join-Path $source $nomFichier
$sourceVersion = Join-Path $source $nomVersion
$destXlsm = Join-Path $dest $nomFichier
$destVersion = Join-Path $dest $nomVersion
if (!(Test-Path $dest)) {
New-Item -ItemType Directory -Path $dest -Force | Out-Null
}
if (!(Test-Path $sourceVersion)) {
Add-Type -AssemblyName PresentationFramework
[System.Windows.MessageBox]::Show("Le dossier de mise à jour sur le NAS est inaccessible.", "Mise à jour Ratio Cuisine")
exit
}
$versionServeur = (Get-Content $sourceVersion -ErrorAction Stop).Trim()
if (Test-Path $destVersion) {
$versionLocale = (Get-Content $destVersion -ErrorAction SilentlyContinue).Trim()
} else {
$versionLocale = ""
}
if ($versionServeur -ne $versionLocale) {
$excel = Get-Process EXCEL -ErrorAction SilentlyContinue
if ($excel) {
$excel | Stop-Process -Force
Start-Sleep -Seconds 2
}
Copy-Item -Path $sourceXlsm -Destination $destXlsm -Force
Copy-Item -Path $sourceVersion -Destination $destVersion -Force
}
Start-Process $destXlsm

View File

@@ -0,0 +1,2 @@
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0Maj_Ratio_Restauration.ps1"

View File

@@ -0,0 +1,42 @@
$source = "\\mj91.fr\PyCharm\Ratio_Inventaires\Excel\prod"
$dest = "C:\Users\$env:USERNAME\Domo91"
$nomFichier = "Ratio_Restauration.xlsm"
$nomVersion = "Ratio_Restauration_VERSION.txt"
$sourceXlsm = Join-Path $source $nomFichier
$sourceVersion = Join-Path $source $nomVersion
$destXlsm = Join-Path $dest $nomFichier
$destVersion = Join-Path $dest $nomVersion
if (!(Test-Path $dest)) {
New-Item -ItemType Directory -Path $dest -Force | Out-Null
}
if (!(Test-Path $sourceVersion)) {
Add-Type -AssemblyName PresentationFramework
[System.Windows.MessageBox]::Show("Le dossier de mise à jour sur le NAS est inaccessible.", "Mise à jour Ratio Restauration")
exit
}
$versionServeur = (Get-Content $sourceVersion -ErrorAction Stop).Trim()
if (Test-Path $destVersion) {
$versionLocale = (Get-Content $destVersion -ErrorAction SilentlyContinue).Trim()
} else {
$versionLocale = ""
}
if ($versionServeur -ne $versionLocale) {
$excel = Get-Process EXCEL -ErrorAction SilentlyContinue
if ($excel) {
$excel | Stop-Process -Force
Start-Sleep -Seconds 2
}
Copy-Item -Path $sourceXlsm -Destination $destXlsm -Force
Copy-Item -Path $sourceVersion -Destination $destVersion -Force
}
Start-Process $destXlsm

135
Scripts/maj_version.py Normal file
View File

@@ -0,0 +1,135 @@
import sys
import subprocess
from pathlib import Path
from datetime import date
# =========================================================
# CONFIG
# =========================================================
VBS_SCRIPT = Path(__file__).parent / "set_cell_silent.vbs"
# =========================================================
# PREFIXE VERSION
# =========================================================
def detecter_major(path: Path) -> int:
nom = path.name.lower()
if "restauration" in nom:
return 2
return 1
# =========================================================
# LECTURE VERSION
# =========================================================
def lire_version(path: Path, major: int):
if not path.exists():
return [major, 0, 0]
contenu = path.read_text(encoding="utf-8").strip().splitlines()
if not contenu:
return [major, 0, 0]
try:
version = [int(x) for x in contenu[0].split(".")]
while len(version) < 3:
version.append(0)
version = version[:3]
# force le major correct
version[0] = major
return version
except Exception:
return [major, 0, 0]
# =========================================================
# INCREMENT
# =========================================================
def increment_patch(version):
version[2] += 1
return version
# =========================================================
# ECRITURE TXT
# =========================================================
def ecrire_version_txt(path: Path, version):
version_str = ".".join(str(x) for x in version)
contenu = f"{version_str}\n{date.today().isoformat()}\n"
path.write_text(contenu, encoding="utf-8")
return version_str
# =========================================================
# ECRITURE EXCEL
# =========================================================
def ecrire_version_excel(classeur: Path, version_str: str):
if not classeur.exists():
print(f"Classeur introuvable : {classeur}")
return
valeur = f"Version : {version_str}"
cmd = [
"cscript",
"//nologo",
str(VBS_SCRIPT),
str(classeur),
"Tableau de bord",
"C1",
valeur
]
subprocess.run(cmd, check=True)
# =========================================================
# MAIN
# =========================================================
def main():
if len(sys.argv) < 3:
print("Usage :")
print("python maj_version.py VERSION.txt classeur.xlsm")
sys.exit(1)
version_file = Path(sys.argv[1])
classeur = Path(sys.argv[2])
major = detecter_major(version_file)
version = lire_version(version_file, major)
version = increment_patch(version)
version_str = ecrire_version_txt(version_file, version)
ecrire_version_excel(classeur, version_str)
print(f"Nouvelle version : {version_str}")
print(f"Classeur mis à jour : {classeur.name}")
if __name__ == "__main__":
main()

View File

@@ -1,68 +1,71 @@
' set_cell_silent.vbs
' Usage: cscript //nologo set_cell_silent.vbs "C:\chemin\fichier.xlsm" "NomFeuille" "A1" "Valeur"
Option Explicit
Dim f, sheetName, addr, val
Dim xl, wb, ws
Dim xl, wb
Dim filePath, sheetName, cellAddress, cellValue
If WScript.Arguments.Count < 4 Then
WScript.Echo "[ERR] Args: set_cell_silent.vbs <fichier.xlsm> <feuille> <cellule> <valeur>"
WScript.Echo "[ERR] Usage : set_cell_silent.vbs fichier.xlsm feuille cellule valeur"
WScript.Quit 1
End If
f = WScript.Arguments(0)
filePath = WScript.Arguments(0)
sheetName = WScript.Arguments(1)
addr = WScript.Arguments(2)
val = WScript.Arguments(3)
cellAddress = WScript.Arguments(2)
cellValue = WScript.Arguments(3)
On Error Resume Next
Set xl = CreateObject("Excel.Application")
If Err.Number <> 0 Then
WScript.Echo "[ERR] Excel non disponible (" & Err.Description & ")"
WScript.Echo "[ERR] Creation Excel : " & Err.Description
WScript.Quit 1
End If
On Error GoTo 0
xl.Visible = False
xl.DisplayAlerts = False
' Désactiver macros et événements AVANT douvrir
On Error Resume Next
xl.AutomationSecurity = 3 ' msoAutomationSecurityForceDisable
xl.EnableEvents = False
xl.ScreenUpdating = False
On Error GoTo 0
xl.AskToUpdateLinks = False
xl.AutomationSecurity = 3
Err.Clear
Set wb = xl.Workbooks.Open(filePath, 0, False)
On Error Resume Next
Set wb = xl.Workbooks.Open(f, False, False)
If Err.Number <> 0 Then
xl.Quit
WScript.Echo "[ERR] Ouverture classeur: " & Err.Description
xl.Quit
Set xl = Nothing
WScript.Quit 1
End If
On Error GoTo 0
On Error Resume Next
Set ws = wb.Worksheets.Item(sheetName)
Err.Clear
wb.Worksheets(sheetName).Range(cellAddress).Value = cellValue
If Err.Number <> 0 Then
WScript.Echo "[ERR] Ecriture cellule: " & Err.Description
wb.Close False
xl.Quit
WScript.Echo "[ERR] Feuille introuvable: " & sheetName
Set wb = Nothing
Set xl = Nothing
WScript.Quit 1
End If
On Error GoTo 0
On Error Resume Next
ws.Range(addr).Value2 = val
If Err.Number <> 0 Then
wb.Close False
xl.Quit
WScript.Echo "[ERR] Ecriture cellule " & addr & " : " & Err.Description
WScript.Quit 1
End If
On Error GoTo 0
Err.Clear
wb.Save
If Err.Number <> 0 Then
WScript.Echo "[ERR] Sauvegarde: " & Err.Description
wb.Close False
xl.Quit
Set wb = Nothing
Set xl = Nothing
WScript.Quit 1
End If
Err.Clear
wb.Close False
Set wb = Nothing
xl.Quit
Set xl = Nothing
WScript.Quit 0

Binary file not shown.

View File

@@ -1,17 +0,0 @@
import os, mysql.connector
from dotenv import load_dotenv
from pathlib import Path
# charge le .env **avec chemin absolu**
load_dotenv(Path(__file__).resolve().parent.joinpath(".env"))
conn = mysql.connector.connect(
host=os.getenv("DB_HOST"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
database=os.getenv("DB_NAME"),
connection_timeout=5,
)
conn.ping(reconnect=True, attempts=3, delay=1)
print("CONNECTED" if conn.is_connected() else "KO")
conn.close()

View File

@@ -1,427 +0,0 @@
# app_users.py — création + modification de champs (wide + onglets + grille)
import os
import re
from datetime import date, datetime
import bcrypt
import mysql.connector
from mysql.connector import errorcode
import streamlit as st
from dotenv import load_dotenv
# -----------------------
# Auth minimale
# -----------------------
def require_login():
st.markdown(
"""
<style>
body {
background-color: #f2f2f2;
}
</style>
""",
unsafe_allow_html=True
)
load_dotenv()
admin_user = os.getenv("ADMIN_USER")
admin_hash = os.getenv("ADMIN_PASS_HASH")
if not admin_hash:
st.error("ADMIN_PASS_HASH manquant dans .env")
st.stop()
if "auth_ok" not in st.session_state:
st.session_state.auth_ok = False
if not st.session_state.auth_ok:
st.markdown("<h2 style='text-align:center;'>🔐 Accès restreint</h2>", unsafe_allow_html=True)
# conteneur centré
login_left, login_center, login_right = st.columns([1, 2, 1])
with login_center:
st.markdown(
"""
<div style='border:1px solid #ddd; border-radius:12px; padding:2rem; background:#fafafa;'>
""",
unsafe_allow_html=True
)
u = st.text_input("Utilisateur", key="login_user")
p = st.text_input("Mot de passe", type="password", key="login_pass")
if st.button("Se connecter", use_container_width=True):
if u == admin_user and bcrypt.checkpw(p.encode(), admin_hash.encode()):
st.session_state.auth_ok = True
st.rerun()
else:
st.error("Identifiants invalides")
st.markdown("</div>", unsafe_allow_html=True)
st.stop()
require_login()
# -----------------------
# Connexion MySQL
# -----------------------
@st.cache_resource
def get_connection():
load_dotenv()
host = os.getenv("DB_HOST")
port = int(os.getenv("MYSQL_PORT", "3306"))
user = os.getenv("DB_USER")
pwd = os.getenv("DB_PASSWORD")
db = os.getenv("DB_NAME")
missing = [k for k, v in {
"MYSQL_USER": user, "MYSQL_PASSWORD": pwd, "MYSQL_HOST": host, "MYSQL_PORT": port, "MYSQL_DATABASE": db
}.items() if v in (None, "")]
if missing:
raise RuntimeError(f"Variables manquantes dans .env : {', '.join(missing)}")
return mysql.connector.connect(
host=host, port=port, user=user, password=pwd, database=db, autocommit=True
)
# -----------------------
# Utilitaires
# -----------------------
EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
def normalize_phone(phone: str | None) -> str | None:
if not phone:
return None
return re.sub(r"[^\d+]", "", phone)
def to_sql_date(d: date | str) -> str:
if isinstance(d, date):
return d.strftime("%Y-%m-%d")
for fmt in ("%Y-%m-%d", "%d/%m/%Y"):
try:
return datetime.strptime(d, fmt).strftime("%Y-%m-%d")
except ValueError:
continue
raise ValueError("Date invalide (attendu: YYYY-MM-DD ou JJ/MM/AAAA)")
def hash_password(plain: str, rounds: int = 12) -> str:
salt = bcrypt.gensalt(rounds=rounds)
return bcrypt.hashpw(plain.encode("utf-8"), salt).decode("utf-8")
def user_exists(cursor, username: str, email: str) -> bool:
cursor.execute(
"SELECT COUNT(*) FROM Utilisateurs WHERE NomUtilisateur=%s OR email=%s",
(username, email),
)
(count,) = cursor.fetchone()
return count > 0
def insert_user(cnx, username, full_name, site, password, expires, phone, email, store_plain=False):
if not EMAIL_RE.match(email):
raise ValueError("Email invalide.")
phone_norm = normalize_phone(phone)
exp_sql = to_sql_date(expires)
pwd_hash = hash_password(password)
with cnx.cursor() as cur:
if user_exists(cur, username, email):
raise RuntimeError("Nom d'utilisateur ou email déjà existant.")
if store_plain:
cur.execute(
"""
INSERT INTO Utilisateurs
(NomUtilisateur, Nom_complet, Site, MotDePasse, MotDePasseHash, DateExpiration, Telephone, email)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
""",
(username, full_name, site, password, pwd_hash, exp_sql, phone_norm, email),
)
else:
cur.execute(
"""
INSERT INTO Utilisateurs
(NomUtilisateur, Nom_complet, Site, MotDePasse, MotDePasseHash, DateExpiration, Telephone, email)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
""",
(username, full_name, site, None, pwd_hash, exp_sql, phone_norm, email),
)
return pwd_hash
def list_users(cnx, limit=200):
with cnx.cursor(dictionary=True) as cur:
cur.execute(
"""
SELECT NomUtilisateur, Nom_complet, Site, DateExpiration, Telephone, email
FROM Utilisateurs
ORDER BY NomUtilisateur ASC
LIMIT %s
""",
(limit,),
)
return cur.fetchall()
def get_user(cnx, username: str):
with cnx.cursor(dictionary=True) as cur:
cur.execute(
"""
SELECT NomUtilisateur, Nom_complet, Site, DateExpiration, Telephone, email
FROM Utilisateurs
WHERE NomUtilisateur=%s
""",
(username,),
)
return cur.fetchone()
def update_field(cnx, username: str, field: str, value):
allowed = {
"Nom_complet": "Nom_complet",
"Site": "Site",
"Telephone": "Telephone",
"DateExpiration": "DateExpiration",
"email": "email",
}
if field not in allowed:
raise ValueError("Champ non autorisé.")
sql_field = allowed[field]
if field == "email":
if not EMAIL_RE.match(str(value)):
raise ValueError("Email invalide.")
if field == "Telephone":
value = normalize_phone(str(value)) if value else None
if field == "DateExpiration":
value = to_sql_date(value)
with cnx.cursor() as cur:
cur.execute(
f"UPDATE Utilisateurs SET {sql_field}=%s WHERE NomUtilisateur=%s",
(value, username),
)
def update_password(cnx, username: str, new_password: str):
pwd_hash = hash_password(new_password)
with cnx.cursor() as cur:
cur.execute(
"UPDATE Utilisateurs SET MotDePasse=NULL, MotDePasseHash=%s WHERE NomUtilisateur=%s",
(pwd_hash, username),
)
return pwd_hash
# -----------------------
# UI
# -----------------------
st.set_page_config(page_title="Acces.Utilisateurs", page_icon="👤", layout="wide")
# CSS doux
st.markdown(
"""
<style>
.block-container {padding-top: 1.25rem; padding-bottom: 2rem; max-width: 1400px;}
button[kind="primary"], .stButton>button {height: 2.6rem;}
.stForm {border: 1px solid #eee; padding: 1rem; border-radius: 12px;}
.soft-card {border:1px solid #eee; padding:0.75rem 1rem; border-radius:12px; background:#fafafa;}
</style>
""",
unsafe_allow_html=True
)
st.title("👤 Acces.Utilisateurs")
# Connexion DB (badge)
try:
cnx = get_connection()
st.caption("Connexion MySQL via `.env` : **OK** ✅")
except Exception as e:
st.error(f"Connexion MySQL impossible : {e}")
st.stop()
# Bouton Sortie / Exit
hdr_l, hdr_r = st.columns([1, 0.18])
with hdr_r:
if st.button("🔒 SORTIE / EXIT", use_container_width=True, help="Se déconnecter et verrouiller l'accès"):
st.session_state.auth_ok = False
st.rerun()
# -----------------------
# Onglets
# -----------------------
tab_list, tab_create, tab_edit, tab_security = st.tabs(
["📋 Liste", "🆕 Créer", "✏️ Modifier", "🔐 Sécurité"]
)
# --- Onglet Liste ---
with tab_list:
st.subheader("Utilisateurs récents")
try:
import pandas as pd
rows = list_users(cnx, limit=1000)
df = pd.DataFrame(rows)
if not df.empty:
df["DateExpiration"] = pd.to_datetime(df["DateExpiration"], errors="coerce").dt.date
from datetime import date as _date
today = _date.today()
expired_mask = df["DateExpiration"] <= today
col_a, col_b = st.columns(2)
col_a.metric("Total utilisateurs", len(df))
col_b.metric("Comptes périmés", int(expired_mask.sum()))
def style_expired(row):
if row["DateExpiration"] <= today:
return ["color: white; background-color: #d32f2f;"] * len(row)
return [""] * len(row)
styled = df.style.apply(style_expired, axis=1)
visible_rows = len(df)
row_px = 36
header_px = 48
padding_px = 24
height = min(1000, header_px + row_px * visible_rows + padding_px)
st.dataframe(
styled,
use_container_width=True,
hide_index=True,
height=height,
)
else:
st.info("Aucun utilisateur à afficher.")
except Exception as e:
st.warning(f"Impossible de lister les utilisateurs : {e}")
# --- Onglet Créer ---
with tab_create:
with st.form("create_user_form", clear_on_submit=False):
st.subheader("Nouveau compte")
c1, c2, c3 = st.columns([1.2, 1.5, 1])
username = c1.text_input("NomUtilisateur", placeholder="ex: cjaquier")
full_name = c2.text_input("Nom_complet", placeholder="Clément JAQUIER")
site = c3.text_input("Site", placeholder="Roissy", value="Roissy")
c4, c5, c6 = st.columns([1.4, 1, 1])
email = c4.text_input("email", placeholder="prenom.nom@domaine.com")
phone = c5.text_input("Téléphone", placeholder="06 12 12 35 32")
expires = c6.date_input("DateExpiration", value=date.today())
c7, c8 = st.columns(2)
password = c7.text_input("Mot de passe (saisie)", type="password")
confirm = c8.text_input("Confirmer mot de passe", type="password")
store_plain = st.checkbox("Remplir aussi MotDePasse en clair (déconseillé)", value=False)
submitted = st.form_submit_button("Créer lutilisateur", use_container_width=True)
if submitted:
if not username or not full_name or not site or not email or not password:
st.error("Merci de remplir tous les champs obligatoires.")
elif password != confirm:
st.error("Les mots de passe ne correspondent pas.")
else:
try:
pwd_hash = insert_user(
cnx,
username=username.strip(),
full_name=full_name.strip(),
site=site.strip(),
password=password,
expires=expires,
phone=phone.strip() if phone else None,
email=email.strip(),
store_plain=store_plain,
)
st.success("Utilisateur créé avec succès ✅")
with st.expander("Voir le hash généré (MotDePasseHash)"):
st.code(pwd_hash)
except mysql.connector.Error as db_err:
if db_err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
st.error("Identifiants MySQL invalides.")
else:
st.error(f"Erreur MySQL : {db_err}")
except Exception as e:
st.error(f"Erreur : {e}")
# --- Onglet Modifier ---
with tab_edit:
st.subheader("Modifier un utilisateur existant")
try:
users = list_users(cnx, limit=1000)
usernames = [u["NomUtilisateur"] for u in users]
except Exception as e:
users = []
usernames = []
st.warning(f"Impossible de charger la liste des utilisateurs : {e}")
top_left, top_right = st.columns([1.2, 2])
with top_left:
sel_user = st.selectbox("Utilisateur", usernames, placeholder="Choisir un utilisateur")
field = st.selectbox(
"Champ à modifier",
["Nom_complet", "Site", "Telephone", "DateExpiration", "email"]
)
if field == "DateExpiration":
new_value = st.date_input("Nouvelle valeur (date)", value=date.today(), key="new_date")
elif field == "Telephone":
new_value = st.text_input("Nouvelle valeur (téléphone)", key="new_tel")
else:
new_value = st.text_input("Nouvelle valeur", key="new_text")
update_btn = st.button(
"Mettre à jour le champ",
type="primary",
use_container_width=True,
disabled=not sel_user,
)
with top_right:
if sel_user:
details = get_user(cnx, sel_user)
if details:
c1, c2, c3, c4, c5 = st.columns(5, gap="small")
c1.markdown(f"<div class='soft-card'><b>Nom</b><br>{details['Nom_complet'] or ''}</div>", unsafe_allow_html=True)
c2.markdown(f"<div class='soft-card'><b>Site</b><br>{details['Site'] or ''}</div>", unsafe_allow_html=True)
c3.markdown(f"<div class='soft-card'><b>Expire</b><br>{details['DateExpiration'] or ''}</div>", unsafe_allow_html=True)
c4.markdown(f"<div class='soft-card'><b>Tél</b><br>{details['Telephone'] or ''}</div>", unsafe_allow_html=True)
c5.markdown(f"<div class='soft-card'><b>Email</b><br>{details['email'] or ''}</div>", unsafe_allow_html=True)
if update_btn and sel_user:
try:
update_field(cnx, sel_user, field, new_value)
st.success(f"{field} mis à jour pour {sel_user}")
except mysql.connector.Error as db_err:
st.error(f"Erreur MySQL : {db_err}")
except Exception as e:
st.error(f"Erreur : {e}")
# --- Onglet Sécurité ---
with tab_security:
st.subheader("Réinitialiser le mot de passe (bcrypt)")
try:
if 'user_cache_for_pw' not in st.session_state:
st.session_state.user_cache_for_pw = [u["NomUtilisateur"] for u in list_users(cnx, limit=1000)]
pw_user_list = st.session_state.user_cache_for_pw
except Exception:
pw_user_list = []
user_pw = st.selectbox("Utilisateur", pw_user_list, key="pw_user", placeholder="Choisir un utilisateur")
colp1, colp2 = st.columns(2)
new_pw = colp1.text_input("Nouveau mot de passe", type="password")
new_pw2 = colp2.text_input("Confirmer", type="password")
if st.button("Mettre à jour le mot de passe", disabled=not user_pw, use_container_width=True):
if not new_pw:
st.error("Mot de passe vide.")
elif new_pw != new_pw2:
st.error("Les mots de passe ne correspondent pas.")
else:
try:
h = update_password(cnx, user_pw, new_pw)
st.success("Mot de passe mis à jour ✅")
st.caption("Hash (MotDePasseHash) :")
st.code(h)
except Exception as e:
st.error(f"Erreur : {e}")

View File

@@ -1,6 +0,0 @@
import bcrypt
password = input("Entre le mot de passe admin à hacher : ")
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
print("\nHash généré :")
print(hashed.decode())

View File

@@ -1,40 +0,0 @@
altair==5.5.0
attrs==25.4.0
blinker==1.9.0
cachetools==6.2.1
certifi==2025.10.5
charset-normalizer==3.4.4
click==8.3.0
gitdb==4.0.12
GitPython==3.1.45
idna==3.11
Jinja2==3.1.6
jsonschema==4.25.1
jsonschema-specifications==2025.9.1
MarkupSafe==3.0.3
mysql-connector-python==9.5.0
narwhals==2.9.0
numpy==2.3.4
packaging==25.0
pandas==2.3.3
pillow==11.3.0
protobuf==6.33.0
pyarrow==22.0.0
pydeck==0.9.1
python-dateutil==2.9.0.post0
python-dotenv==1.1.1
pytz==2025.2
referencing==0.37.0
requests==2.32.5
rpds-py==0.28.0
six==1.17.0
smmap==5.0.2
streamlit==1.50.0
tenacity==9.1.2
toml==0.10.2
tornado==6.5.2
typing_extensions==4.15.0
tzdata==2025.2
urllib3==2.5.0
watchdog==6.0.0
bcrypt==5.0.0