ConvertEcritures 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. convertecritures-1.0.1.dist-info/METADATA +101 -0
  2. convertecritures-1.0.1.dist-info/RECORD +40 -0
  3. convertecritures-1.0.1.dist-info/WHEEL +5 -0
  4. convertecritures-1.0.1.dist-info/entry_points.txt +2 -0
  5. convertecritures-1.0.1.dist-info/top_level.txt +1 -0
  6. py_convert/__about__.py +6 -0
  7. py_convert/__init__.py +3 -0
  8. py_convert/cli.py +8 -0
  9. py_convert/error.py +80 -0
  10. py_convert/format_export/__init__.py +26 -0
  11. py_convert/format_export/base_export.py +128 -0
  12. py_convert/format_export/clipboard_cador_export.py +56 -0
  13. py_convert/format_export/excel_export.py +50 -0
  14. py_convert/format_export/fec_export.py +67 -0
  15. py_convert/format_export/trs_export.py +121 -0
  16. py_convert/format_import/__init__.py +26 -0
  17. py_convert/format_import/base_import.py +105 -0
  18. py_convert/format_import/clipboard_cador_import.py +92 -0
  19. py_convert/format_import/fec_import.py +135 -0
  20. py_convert/format_import/quadra_import.py +178 -0
  21. py_convert/format_import/sage20_import.py +139 -0
  22. py_convert/format_import/sekur_import.py +115 -0
  23. py_convert/format_settings/__init__.py +37 -0
  24. py_convert/format_settings/azurcar_settings.py +366 -0
  25. py_convert/format_settings/base_settings.py +277 -0
  26. py_convert/format_settings/cash_settings.py +48 -0
  27. py_convert/format_settings/cave_settings.py +133 -0
  28. py_convert/format_settings/egs_settings.py +16 -0
  29. py_convert/format_settings/haillot_settings.py +21 -0
  30. py_convert/format_settings/mgecobat_settings.py +21 -0
  31. py_convert/format_settings/vignobles_settings.py +181 -0
  32. py_convert/gui/__init__.py +7 -0
  33. py_convert/gui/about.py +76 -0
  34. py_convert/gui/animated_toggle.py +140 -0
  35. py_convert/gui/app.py +78 -0
  36. py_convert/gui/cash.py +76 -0
  37. py_convert/gui/main.py +276 -0
  38. py_convert/gui/menu.py +39 -0
  39. py_convert/gui/settings.py +178 -0
  40. py_convert/settingsManager.py +250 -0
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: ConvertEcritures
3
+ Version: 1.0.1
4
+ Summary: Convertisseur d'écritures comptables d'un format à un autre format.
5
+ Home-page: https://github.com/Atem83/ConvertEcritures
6
+ Author: Michaël AUGUSTE
7
+ License: MIT
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: polars
13
+ Requires-Dist: PySide6
14
+ Requires-Dist: screeninfo
15
+ Requires-Dist: send2trash
16
+
17
+ <h1 align="center">ConvertEcritures</h1>
18
+
19
+ <h3 align="center">Convertisseur d'écritures comptables d'un format à un autre format.</h3>
20
+
21
+ <div align="center">
22
+
23
+ [![PyPI](https://img.shields.io/pypi/v/ConvertEcritures?style=flat)](https://pypi.org/project/ConvertEcritures)
24
+ <a href="https://opensource.org/license/mit">![License](https://img.shields.io/badge/License-MIT-blue)</a>
25
+ <a href="https://github.com/Atem83/ConvertEcritures/archive/refs/heads/main.zip">![Download](https://img.shields.io/badge/Source_Code-Download-blue)</a>
26
+ ![LOC](https://tokei.rs/b1/github/Atem83/ConvertEcritures?category=lines)
27
+
28
+ </div>
29
+
30
+ <div align="center">
31
+ <div style="display: flex; justify-content: space-around; gap: 100px;">
32
+ <img src="https://raw.githubusercontent.com/Atem83/ConvertEcritures/main/images/GUI.png" alt="GUI" style="width: 35%;">
33
+ <img src="https://raw.githubusercontent.com/Atem83/ConvertEcritures/main/images/GUI Settings.png" alt="GUI Settings" style="width: 35%;">
34
+ </div>
35
+ </div>
36
+
37
+ <h2 align="center"> Formats d'import </h2>
38
+
39
+ **FEC** : format .txt et les deux séparateurs sont reconnus
40
+
41
+ **QUADRA** : format ASCII sans extension
42
+
43
+ **SAGE 20** : format .txt
44
+
45
+ **PRESSE-PAPIER** : pas de fichier d'import, récupère le contenu du presse-papier qui doit au format du logiciel comptable ComptabiliteExpert (éditeur ACD)
46
+
47
+ **SEKUR** : journal des ventes au format .xlsx du logiciel de vente SEKUR
48
+
49
+ <h2 align="center"> Formats d'export </h2>
50
+
51
+ **FEC** : format .txt avec séparateur tabulation
52
+
53
+ **EXCEL** : format .xlsx
54
+
55
+ **PRESSE-PAPIER** : pas de fichier d'export, exporte les écritures dans le presse-papier au format reconnu par le logiciel comptable ComptabiliteExpert (éditeur ACD)
56
+
57
+ **TRS** : format .TRS
58
+
59
+ <h2 align="center"> Paramètres </h2>
60
+
61
+ **Paramètre "CAISSE" pour le format d'import "PRESSE-PAPIER"** : permet de convertir le grand livre d'un compte 580 dans le presse-papier en écritures comptables équilibrées avec la contrepartie en caisse pour solder le compte 580 opération par opération facilement et rapidement.
62
+
63
+ Aucun autre paramètre n'est pour le moment publiquement disponible mais il est possible d'en ajouter.
64
+
65
+ Les paramètres permettent de personnaliser et de modifier les écritures comptables importer avant leur export sous le nouveau format.
66
+ Par exemple, un fichier d'écriture utilisant des comptes lambda peut alors être converti pour remplacer les comptes lambda par le plan comptable d'un dossier en particulier.
67
+
68
+ <h2 align="center"> Installation </h2>
69
+
70
+ <div align="center">
71
+
72
+ ```
73
+ pip install ConvertEcritures
74
+ ```
75
+
76
+ [<img alt="GitHub repo size" src="https://img.shields.io/github/repo-size/Atem83/ConvertEcritures?&color=green&label=Source%20Code&logo=Python&logoColor=yellow&style=for-the-badge" width="300">](https://github.com/Atem83/ConvertEcritures/archive/refs/heads/main.zip)
77
+
78
+ </div>
79
+
80
+ <br>
81
+
82
+ <h1 align="center"> Interface graphique </h1>
83
+
84
+ <h2 align="center"> Depuis le logiciel </h2>
85
+
86
+ Vous pouvez télécharger le logiciel [ici](https://github.com/Atem83/ConvertEcritures/releases/latest) et le lancer.
87
+
88
+ <h2 align="center"> Depuis une CLI </h2>
89
+
90
+ ```bash
91
+ ConvertEcritures-gui
92
+ ```
93
+
94
+ <h2 align="center"> Depuis un script Python </h2>
95
+
96
+ ```python
97
+ from py_convert.gui import App
98
+
99
+ app = App()
100
+ app.run()
101
+ ```
@@ -0,0 +1,40 @@
1
+ py_convert/__about__.py,sha256=o7SuuhX6niOyRA0N030_4y-MWKo1U3x0lKjiMVXxziE,254
2
+ py_convert/__init__.py,sha256=qq7_e2IldhJgwK06L-Bj9akZWbcLc5bmF5FQwXdpUx8,90
3
+ py_convert/cli.py,sha256=-R5wOCjknhTzQDiFVBh5i7WHds7BioacE3XSVv1cfpk,113
4
+ py_convert/error.py,sha256=QtIa-BrnJGSbHPEYsPq7tDM1rs0-XnrAWVtb2j2nlkk,2990
5
+ py_convert/settingsManager.py,sha256=mC92uD5oStkpFiACZFxXrJyQxzVpo2qqr0hNvkJj_iM,9080
6
+ py_convert/format_export/__init__.py,sha256=jdtCfoPDYqKZbzzRELTTmYGLlzRn5LwHKcObzxdVhO8,1060
7
+ py_convert/format_export/base_export.py,sha256=6n-3Y81kQL687kh53LMCEO_floncbjnDVUPMfaUD1HA,4127
8
+ py_convert/format_export/clipboard_cador_export.py,sha256=tNeGDhy5L0o5Mgf0q63jx1QzTvbb8d-bXad-df-dF7k,2200
9
+ py_convert/format_export/excel_export.py,sha256=-GhRbmTXy66vc4NeSXrmw1kpiFZsg_AtVMytTAIVrrE,1767
10
+ py_convert/format_export/fec_export.py,sha256=qbYuEETQZeUk-qXli5hPMjnnOVeu3HwS2M0SBNk7jkA,1873
11
+ py_convert/format_export/trs_export.py,sha256=-gwHS3xx2IBu61cYJHTGk0UIbGJ8u4WY6t9ZA9AP8dg,4295
12
+ py_convert/format_import/__init__.py,sha256=-zORP_5VQ4jr7EIxVk3go8GYrlzmJkoPK2RF0963ZyI,1060
13
+ py_convert/format_import/base_import.py,sha256=rCxmY9kUF51yDRh3F5Mdg_DydIy5cPyg4Y_-3ixxQyc,3529
14
+ py_convert/format_import/clipboard_cador_import.py,sha256=KZfHuDZm0kHK798oTGgzGgrzn9Uof316MGup8LAruE4,3267
15
+ py_convert/format_import/fec_import.py,sha256=GpBHwRLuGAGgmImHqlqdvXOjSKllhTssnkZJbyRi56M,4650
16
+ py_convert/format_import/quadra_import.py,sha256=mndfUL1R6UF1U9qAsdasEOXofyrMvACPz3JQZA19-ns,6887
17
+ py_convert/format_import/sage20_import.py,sha256=9jlo45RJgPlBQxu1EhRGJaGYY_ezjlAWucQVyZqHW8A,5032
18
+ py_convert/format_import/sekur_import.py,sha256=44a18u-55JxInG4GrUs2XdkURPUJQNbxQa4vrIIf1x4,4631
19
+ py_convert/format_settings/__init__.py,sha256=2atuu5G24BE_Y_Coeu-f6hnyM-5LSSIJ-lL6ZcIz6IA,1516
20
+ py_convert/format_settings/azurcar_settings.py,sha256=CnLgfD6iv0B8OfO6BtVhZqYSPT1HHFK-N50T-ExfiOM,14534
21
+ py_convert/format_settings/base_settings.py,sha256=xMvXm-bhhNiCzROANh68N6EIp9viaLhIaLjBlgu1l_U,10727
22
+ py_convert/format_settings/cash_settings.py,sha256=iCaSiGXVO9rfWdR_MEWTZbUi_XeDO25TDYltA2u_9N0,1771
23
+ py_convert/format_settings/cave_settings.py,sha256=udNF3RrvgNJAz3SbzTyP9yI2WlK1We1qHfBQfULlGVc,4876
24
+ py_convert/format_settings/egs_settings.py,sha256=cdmv51uwFoIqarHwHARTtyKqpkKK8_O3d83XsTfkAD8,560
25
+ py_convert/format_settings/haillot_settings.py,sha256=9GDHtOkkIRwEBwZU7MwhqVCvoysVsXb9K5v8BLYvDjQ,683
26
+ py_convert/format_settings/mgecobat_settings.py,sha256=gl7YDofhfzCjYyW3Zhq5aQfXhYJo-j0-3QoTlOeMfTo,900
27
+ py_convert/format_settings/vignobles_settings.py,sha256=uiDqDpQSQpQggNxXk5RGXoI_Uq4XcZ0jyT-G8xCiRRY,6605
28
+ py_convert/gui/__init__.py,sha256=Hsni3zkMvmlgZzYQOOiMw772g5iZOPQItbaDOSavJBI,209
29
+ py_convert/gui/about.py,sha256=0R1kdhUIl7U0LoracQdNC6JzPeGcXWUG5cygqP6TEUg,3116
30
+ py_convert/gui/animated_toggle.py,sha256=pTb3haMKEXkoxQpUgnbPH-dhT6uW6LmC1HMGDtvWPDk,4790
31
+ py_convert/gui/app.py,sha256=eA9xlNbQpP0uF3DZexVdrDQHcNwtWTM1t9OwttWU0SI,2692
32
+ py_convert/gui/cash.py,sha256=E4ZyLA35T3ylzjb6SPvsgmNtI33NHIxQFNFC1EtMhh0,3038
33
+ py_convert/gui/main.py,sha256=lUhTbFkEdefTzBWxMAQLQ7JcPWYJtXPGs2Qb8vM-SkQ,11881
34
+ py_convert/gui/menu.py,sha256=UbnqfSz66St3DjVutOz2fXM58sgBL7D6Hiq0xetB5hs,1427
35
+ py_convert/gui/settings.py,sha256=UVQ_1Ui4_E0AVWu3sAa3jbSP-oC2qWRXGvrixEoDCsI,7411
36
+ convertecritures-1.0.1.dist-info/METADATA,sha256=-su7sx0B4bTJeD1Lb_BZ_RFdVC_vIMNsGARe7QwOBck,3874
37
+ convertecritures-1.0.1.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
38
+ convertecritures-1.0.1.dist-info/entry_points.txt,sha256=ynh73g_xWlqAXaHqzW9QIxQiqqS2RHZFFGdZ2xokdbI,67
39
+ convertecritures-1.0.1.dist-info/top_level.txt,sha256=K_UFl26PMmSvIxwaIXQlNVJcWQf-ie-frJN-oeAqDZM,11
40
+ convertecritures-1.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.4.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ConvertEcritures-gui = ConvertEcritures.cli:main
@@ -0,0 +1 @@
1
+ py_convert
@@ -0,0 +1,6 @@
1
+ __title__ = "ConvertEcritures"
2
+ __description__ = "Convertisseur d'écritures comptables d'un format à un autre format."
3
+ __version__ = "1.0.1"
4
+ __author__ = "Michaël AUGUSTE"
5
+ __license__ = "MIT"
6
+ __url__ = "https://github.com/Atem83/ConvertEcritures"
py_convert/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .settingsManager import Settings
2
+ from .error import run_error
3
+ from .cli import main
py_convert/cli.py ADDED
@@ -0,0 +1,8 @@
1
+ from .gui.app import App
2
+
3
+ def main():
4
+ app = App()
5
+ app.run()
6
+
7
+ if __name__ == "__main__":
8
+ main()
py_convert/error.py ADDED
@@ -0,0 +1,80 @@
1
+ import sys
2
+ from PySide6.QtWidgets import (QApplication, QDialog, QLabel, QPushButton,
3
+ QVBoxLayout, QHBoxLayout, QSizePolicy, QWidget,
4
+ QMessageBox)
5
+ from PySide6.QtCore import Qt
6
+
7
+ class ErrorWindow(QDialog):
8
+ def __init__(self, error_message:str = "Une erreur est survenue", error_details:str = ""):
9
+ super().__init__()
10
+
11
+ self.setWindowTitle("Erreur")
12
+ self.setModal(True)
13
+ self.layout = QVBoxLayout()
14
+
15
+ # Icone d'erreur
16
+ warning_icon = QMessageBox().standardIcon(QMessageBox.Warning)
17
+ icon_label = QLabel()
18
+ icon_label.setPixmap(warning_icon)
19
+ icon_label.setAlignment(Qt.AlignLeft)
20
+
21
+ message_layout = QHBoxLayout()
22
+ message_layout.addWidget(icon_label)
23
+
24
+ # Message d'erreur simplifié
25
+ error_label = QLabel(error_message)
26
+ error_label.setAlignment(Qt.AlignLeft)
27
+ message_layout.addWidget(error_label)
28
+ self.layout.addLayout(message_layout)
29
+
30
+ # Bouton pour afficher le message d'erreur
31
+ self.toggle_button = QPushButton("Afficher le message d'erreur")
32
+ self.toggle_button.clicked.connect(self.toggle_message)
33
+ if error_details != "":
34
+ self.layout.addWidget(self.toggle_button)
35
+
36
+ # Message d'erreur détaillé
37
+ self.error_details_label = QLabel(error_details)
38
+ self.error_details_label.setVisible(False)
39
+ self.layout.addWidget(self.error_details_label)
40
+
41
+ # Permet de dimensionner en petit le bouton "OK"
42
+ button_layout = QHBoxLayout()
43
+ spacer = QWidget()
44
+ spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
45
+ button_layout.addWidget(spacer)
46
+
47
+ # Bouton de fermeture
48
+ close_button = QPushButton("OK")
49
+ close_button.clicked.connect(self.close)
50
+ button_layout.addWidget(close_button)
51
+ self.layout.addLayout(button_layout)
52
+
53
+ self.setLayout(self.layout)
54
+ self.adjustSize()
55
+
56
+ # True = fenêtre détaillée, False = fenêtre simplifiée
57
+ self.expanded = False
58
+
59
+ def toggle_message(self):
60
+ """Affiche ou masque le message d'erreur."""
61
+
62
+ if self.expanded:
63
+ self.toggle_button.setText("Afficher le message d'erreur")
64
+ self.error_details_label.setVisible(False)
65
+ self.adjustSize()
66
+ else:
67
+ self.toggle_button.setText("Masquer le message d'erreur")
68
+ self.error_details_label.setVisible(True)
69
+ self.adjustSize()
70
+ self.expanded = not self.expanded
71
+
72
+ def run_error(message:str = "", details:str = ""):
73
+ """Lance la fenêtre d'erreur."""
74
+ app = QApplication.instance()
75
+ # Créez une nouvelle instance si nécessaire
76
+ if app is None:
77
+ app = QApplication(sys.argv)
78
+
79
+ window = ErrorWindow(str(message), str(details))
80
+ window.exec()
@@ -0,0 +1,26 @@
1
+ import pkgutil
2
+ import importlib
3
+ from abc import ABC
4
+
5
+ def _get_classes() -> list[type]:
6
+ """Récupère dynamiquement toutes les classes du dossier."""
7
+ classes = []
8
+ for _, module_name, _ in pkgutil.iter_modules(__path__):
9
+ try:
10
+ # Importe dynamiquement le module
11
+ module = importlib.import_module(f'.{module_name}', package=__name__)
12
+ # Récupère toutes les classes du module
13
+ for name in dir(module):
14
+ cls = getattr(module, name)
15
+ if (isinstance(cls, type)
16
+ and hasattr(cls, 'name')
17
+ and callable(getattr(cls, 'name'))
18
+ and ABC not in cls.__bases__):
19
+ classes.append(cls)
20
+ except Exception as e:
21
+ print(f"Erreur lors de l'import de {module_name}: {str(e)}")
22
+ return classes
23
+
24
+ export_classes = _get_classes()
25
+ export_names = [cls().name() for cls in export_classes]
26
+ __all__ = [cls.__name__ for cls in export_classes] + ['export_names']
@@ -0,0 +1,128 @@
1
+ import re
2
+ import polars as pl
3
+ from abc import ABC, abstractmethod
4
+ from pathlib import Path
5
+ from ..error import run_error
6
+
7
+ class BaseExport(ABC):
8
+ """Classe abstraite pour les méthodes d'exportation d'écritures."""
9
+ def __init__(
10
+ self,
11
+ df: pl.DataFrame | None = None,
12
+ dir: Path = Path.home() / "Desktop"
13
+ ):
14
+ self._entries = df
15
+ self._dir = dir
16
+
17
+ @abstractmethod
18
+ def name(self) -> str:
19
+ """Nom de la méthode d'export."""
20
+ pass
21
+
22
+ @abstractmethod
23
+ def extension(self) -> str:
24
+ """Extension du fichier exporté."""
25
+ pass
26
+
27
+ @abstractmethod
28
+ def process_file(self):
29
+ """Exporte les écritures dans le format souhaité."""
30
+ pass
31
+
32
+ @property
33
+ def entries(self) -> pl.DataFrame:
34
+ """Liste des écritures à exporter."""
35
+ return self._entries
36
+
37
+ @property
38
+ def name_export(self) -> Path:
39
+ """Nom du fichier d'export."""
40
+ return self._dir / ("Export" + self.extension())
41
+
42
+ @property
43
+ def path_export(self) -> Path:
44
+ """Chemin du fichier d'export."""
45
+ path = self.name_export
46
+
47
+ while path.exists():
48
+ # Sépare le nom du fichier et son extension
49
+ raw_name = path.stem
50
+ extension = path.suffix.lower()
51
+
52
+ # Chercher le motif de la forme "(x)" à la fin du nom de fichier
53
+ match = re.search(r'(\(\d+\))$', raw_name)
54
+
55
+ if match:
56
+ # Extraire le nombre entre parenthèses et l'incrémenter
57
+ number = int(match.group(1).strip('()')) + 1
58
+ # Remplacer l'ancien numéro par le nouveau
59
+ new_name = re.sub(r'\(\d+\)$', f'({number})', raw_name)
60
+ else:
61
+ # Ajouter "(1)" à la fin du nom du fichier
62
+ new_name = raw_name + " (1)"
63
+
64
+ path = self.name_export.parent / (new_name + extension)
65
+ return path
66
+
67
+ @property
68
+ def mandatory_cols(self) -> list[str]:
69
+ """Liste des colonnes obligatoires pour l'export."""
70
+ return [
71
+ "JournalCode",
72
+ "EcritureDate",
73
+ "PieceRef",
74
+ "CompteNum",
75
+ "EcritureLib",
76
+ "EcritureLet",
77
+ "Debit",
78
+ "Credit"
79
+ ]
80
+
81
+ @property
82
+ def mandatory_data(self) -> list[str]:
83
+ """Liste des données obligatoires pour l'export."""
84
+ return [
85
+ "EcritureDate",
86
+ "CompteNum",
87
+ "Debit",
88
+ "Credit"
89
+ ]
90
+
91
+ def validate_entries(self) -> bool:
92
+ """Vérifie si les écritures contiennent les données obligatoires pour l'export."""
93
+ for col in self.mandatory_cols:
94
+ if col not in self.entries.columns:
95
+ msg = f"Export impossible,\nLa colonne '{col}' est manquante."
96
+ run_error(msg)
97
+ return False
98
+
99
+ for col in self.mandatory_data:
100
+ if self.entries[col].is_null().any():
101
+ msg = f"Export impossible,\n"
102
+ msg += f"Des valeurs de la colonne '{col}' sont vides."
103
+ run_error(msg)
104
+ return False
105
+
106
+ return True
107
+
108
+ def export_data(self):
109
+ """Méthode principale d'export."""
110
+ if not self.validate_entries():
111
+ return
112
+
113
+ try:
114
+ self.process_file()
115
+ except Exception as e:
116
+ run_error("Une erreur est survenue.", details=str(e))
117
+ print(e)
118
+ return
119
+
120
+ def swapGenToAux(self, df: pl.DataFrame) -> pl.DataFrame:
121
+ """Remplace le numéro de compte général par
122
+ le numéro de compte auxiliaire s'il y en a un."""
123
+ return df.with_columns(
124
+ pl.when(pl.col("CompAuxNum").is_null() == False)
125
+ .then(pl.col("CompAuxNum"))
126
+ .otherwise(pl.col("CompteNum"))
127
+ .alias("CompteNum")
128
+ )
@@ -0,0 +1,56 @@
1
+ import polars as pl
2
+ from PySide6.QtCore import Qt
3
+ from PySide6.QtWidgets import QMessageBox
4
+ from .base_export import BaseExport
5
+
6
+ class ClipboardCadorExport(BaseExport):
7
+ """Gestion d'export au format presse-papier de CADOR."""
8
+ def name(self):
9
+ return "PRESSE-PAPIER"
10
+
11
+ def extension(self):
12
+ return None
13
+
14
+ def process_file(self):
15
+ # Message d'avertissement si plusieurs journaux sont trouvés
16
+ if self.entries["JournalCode"].n_unique() > 1:
17
+ msg_box = QMessageBox()
18
+ msg_box.setWindowFlags(msg_box.windowFlags() | Qt.WindowStaysOnTopHint)
19
+ msg_box.setWindowTitle("Confirmation")
20
+ msg = "Plusieurs journaux ont été détectés.\n"
21
+ msg += "Toutes les écritures seront mélangées dans le même journal.\n\n"
22
+ msg += "Êtes-vous sûr de vouloir continuer ?"
23
+ msg_box.setText(msg)
24
+ msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
25
+ msg_box.button(QMessageBox.Yes).setText("Oui")
26
+ msg_box.button(QMessageBox.No).setText("Non")
27
+ answer = msg_box.exec()
28
+ if answer == QMessageBox.No:
29
+ return
30
+
31
+ # Remplace le compte général par le compte auxiliaire
32
+ df = self.swapGenToAux(self.entries)
33
+
34
+ # Réorganise l'ordre des colonnes à exporter
35
+ df = df.select(
36
+ pl.col("EcritureDate"),
37
+ pl.col("PieceRef"),
38
+ pl.col("CompteNum"),
39
+ pl.col("EcritureLib"),
40
+ pl.col("EcritureLet"),
41
+ pl.lit(None).alias("Ext."),
42
+ pl.col("Debit"),
43
+ pl.col("Credit")
44
+ )
45
+
46
+ # Passage en string pour remplacer les points par des virgules
47
+ df = df.cast({"Debit": pl.String, "Credit": pl.String})
48
+ df = df.with_columns(pl.col("Debit").str.replace(".", ",", literal=True))
49
+ df = df.with_columns(pl.col("Credit").str.replace(".", ",", literal=True))
50
+
51
+ df.write_clipboard(
52
+ separator="\t",
53
+ include_header=False,
54
+ date_format="%d/%m/%Y",
55
+ float_precision=2
56
+ )
@@ -0,0 +1,50 @@
1
+ import polars as pl
2
+ from .base_export import BaseExport
3
+
4
+ class ExcelExport(BaseExport):
5
+ """Gestion d'export au format Excel."""
6
+ def name(self):
7
+ return "EXCEL"
8
+
9
+ def extension(self):
10
+ return ".xlsx"
11
+
12
+ def process_file(self):
13
+ # Remplace le compte général par le compte auxiliaire
14
+ df = self.swapGenToAux(self.entries)
15
+
16
+ # Réorganise l'ordre des colonnes à exporter et formate leur type
17
+ df = df.select(
18
+ pl.col("JournalCode").alias("Code Jrnl"),
19
+ pl.col("EcritureDate").alias("Date Ecriture"),
20
+ pl.col("PieceRef").alias("N° Piece"),
21
+ pl.col("CompteNum").alias("N° Compte"),
22
+ pl.col("EcritureLib").alias("Libellé"),
23
+ pl.col("EcritureLet").alias("Lett."),
24
+ pl.lit(None).alias("Ext."),
25
+ pl.col("Debit").alias("Débit"),
26
+ pl.col("Credit").alias("Crédit")
27
+ )
28
+
29
+ dict_col = {
30
+ "Code Jrnl": {"align": "center"},
31
+ "Date Ecriture": {"align": "center"},
32
+ "N° Piece": {"align": "center"},
33
+ "N° Compte": {"align": "center"},
34
+ "Libellé": {"align": "left"},
35
+ "Lett.": {"align": "center"},
36
+ "Ext.": {"align": "center"},
37
+ "Débit": {"align": "center"},
38
+ "Crédit": {"align": "center"}
39
+ }
40
+
41
+ df.write_excel(
42
+ workbook=self.path_export,
43
+ table_style="TableStyleMedium2",
44
+ dtype_formats={pl.Date:"dd/mm/yyyy"},
45
+ header_format={"bold":True, "align":"center"},
46
+ column_formats=dict_col,
47
+ float_precision=2,
48
+ include_header=True,
49
+ autofit=True
50
+ )
@@ -0,0 +1,67 @@
1
+ import polars as pl
2
+ from .base_export import BaseExport
3
+
4
+ class FECExport(BaseExport):
5
+ """Gestion d'export au format FEC."""
6
+ def name(self):
7
+ return "FEC"
8
+
9
+ def extension(self):
10
+ return ".txt"
11
+
12
+ @property
13
+ def mandatory_cols(self) -> list[str]:
14
+ return [
15
+ "JournalCode",
16
+ "JournalLib",
17
+ "EcritureNum",
18
+ "EcritureDate",
19
+ "CompteNum",
20
+ "CompteLib",
21
+ "CompAuxNum",
22
+ "CompAuxLib",
23
+ "PieceRef",
24
+ "PieceDate",
25
+ "EcritureLib",
26
+ "Debit",
27
+ "Credit",
28
+ "EcritureLet",
29
+ "DateLet",
30
+ "ValidDate",
31
+ "Montantdevise",
32
+ "Idevise"
33
+ ]
34
+
35
+ @property
36
+ def mandatory_data(self) -> list[str]:
37
+ return [
38
+ "JournalCode",
39
+ "EcritureDate",
40
+ "CompteNum",
41
+ "Debit",
42
+ "Credit"
43
+ ]
44
+
45
+ def process_file(self):
46
+ # Conserve uniquement les colonnes obligatoires
47
+ df = self.entries.select(self.mandatory_cols)
48
+
49
+ # Remplace les points par des virgules comme séparateur décimaux
50
+ currency_col = [
51
+ "Debit",
52
+ "Credit",
53
+ "Montantdevise"
54
+ ]
55
+ for column in currency_col:
56
+ df = df.with_columns(pl.col(column).round(2))
57
+ df = df.with_columns(pl.col(column).cast(pl.String))
58
+ df = df.with_columns(pl.col(column).str.replace(".", ",", literal=True))
59
+
60
+ # Génère mon fichier d'export FEC
61
+ df.write_csv(
62
+ self.path_export,
63
+ separator="\t",
64
+ include_header=True,
65
+ date_format="%Y%m%d",
66
+ float_precision=2
67
+ )