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.
- convertecritures-1.0.1.dist-info/METADATA +101 -0
- convertecritures-1.0.1.dist-info/RECORD +40 -0
- convertecritures-1.0.1.dist-info/WHEEL +5 -0
- convertecritures-1.0.1.dist-info/entry_points.txt +2 -0
- convertecritures-1.0.1.dist-info/top_level.txt +1 -0
- py_convert/__about__.py +6 -0
- py_convert/__init__.py +3 -0
- py_convert/cli.py +8 -0
- py_convert/error.py +80 -0
- py_convert/format_export/__init__.py +26 -0
- py_convert/format_export/base_export.py +128 -0
- py_convert/format_export/clipboard_cador_export.py +56 -0
- py_convert/format_export/excel_export.py +50 -0
- py_convert/format_export/fec_export.py +67 -0
- py_convert/format_export/trs_export.py +121 -0
- py_convert/format_import/__init__.py +26 -0
- py_convert/format_import/base_import.py +105 -0
- py_convert/format_import/clipboard_cador_import.py +92 -0
- py_convert/format_import/fec_import.py +135 -0
- py_convert/format_import/quadra_import.py +178 -0
- py_convert/format_import/sage20_import.py +139 -0
- py_convert/format_import/sekur_import.py +115 -0
- py_convert/format_settings/__init__.py +37 -0
- py_convert/format_settings/azurcar_settings.py +366 -0
- py_convert/format_settings/base_settings.py +277 -0
- py_convert/format_settings/cash_settings.py +48 -0
- py_convert/format_settings/cave_settings.py +133 -0
- py_convert/format_settings/egs_settings.py +16 -0
- py_convert/format_settings/haillot_settings.py +21 -0
- py_convert/format_settings/mgecobat_settings.py +21 -0
- py_convert/format_settings/vignobles_settings.py +181 -0
- py_convert/gui/__init__.py +7 -0
- py_convert/gui/about.py +76 -0
- py_convert/gui/animated_toggle.py +140 -0
- py_convert/gui/app.py +78 -0
- py_convert/gui/cash.py +76 -0
- py_convert/gui/main.py +276 -0
- py_convert/gui/menu.py +39 -0
- py_convert/gui/settings.py +178 -0
- 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
|
+
[](https://pypi.org/project/ConvertEcritures)
|
|
24
|
+
<a href="https://opensource.org/license/mit"></a>
|
|
25
|
+
<a href="https://github.com/Atem83/ConvertEcritures/archive/refs/heads/main.zip"></a>
|
|
26
|
+

|
|
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 @@
|
|
|
1
|
+
py_convert
|
py_convert/__about__.py
ADDED
py_convert/__init__.py
ADDED
py_convert/cli.py
ADDED
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
|
+
)
|