Script pour import Sage 100 et i7 (et Ciel ?)

Répondre
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

sam. mars 10, 2018 1:22 pm

Je me suis fait un script Python de conversion d'un fichier comptable provenant de Sage 100 et i7 vers le format CSV accepté par OpenConcerto.
Outre la conversion pure et dure des écritures comptables, il me permet :
- de contourner le bug de comptes multiples décrit dans le deuxième message de ce topic.
- de faire des ajustements personnels sur la numérotation des codes comptables.
- de contourner un bug assez vicieux sur les lettres accentuées qui passaient parfois mal. L'écriture "magique" rajoutée en début de fichier CSV m'a l'air de résoudre ce problème, mais je ne peux apporter aucune garantie.

Par contre, je n'ai pas trouvé de contournement sur le bug des codes journaux non présents.. pourtant bien présents.

Comment utiliser ce script :
- enregistrer le script dans un fichier pnmtocsv.py
- lancer le script avec en paramètre le fichier provenant de Sage. Exempe :

Code : Tout sélectionner

python3 pnmtocsv.py societeA.pnm
- le script va produire 2 fichiers : societeA.csv et societeA_court.csv
- importer d'abord dans OpenConcerto le fichier societeA_court.csv
- supprimer ces mêmes écritures qui viennent d'être importées.
- importer le fichier complet societeA.csv

Optionnel :
Entre les 2 imports, je fais quelques étapes. Lorsque je crée une société sous OpenConcerto, je prends le plan comptable de base. Donc je n'ai pas forcément tous les comptes lorsque je vais importer les écritures en provenance de Sage. Je pourrais partir sur un plan complet, mais je préfère n'avoir que les comptes vraiment utiles.
Donc après le premier import, je sors une balance pour voir les comptes qu'Openconcerto a dû créer (libellé "Création automatique"). Je supprime ensuite les écritures du premier import puis pour chaque compte "Création automatique", je regarde s'il existe dans le plan comptable complet. Si oui, je supprime le compte dans le PCE (le plan comptable de l'entreprise) puis j'importe ce compte depuis le plan comptable général. Cela me permet juste d'avoir le bon libellé pour le compte.

Je mets le code ci-dessous car le forum interdit en pièce jointe des .py ou même des .txt

Code : Tout sélectionner

# Convertit un/des fichier(s) d'écritures comptables .pnm en fichier CSV pour
# l'erp OpenConcerto
#
# Le format .pnm est généré par Sage Comptabilité Ligne 100 et i7 en exportant
# au "Format Trésorerie Sage et Ciel"
#
# En sortie, le fichier sera au format CSV accepté par OpenConcerto
#
# Le chemin et le ficher .pnm doit être indiqué après l'appel du script :
# ex : pnmtocsv.py socA.pnm
#      pnmtocsv.py chemin/socB.pnm
#
# Plusieurs fichiers peuvent être indiqués :
# ex : pnmtocsv.py socA.pnm chemin/socB.pnm
#
# En raison d'un bug dans la fonction d'importation dans OpenConcerto 5.2, le
# script va générer deux fichiers CSV :
# - socA.csv est le fichier "normal" contenant toutes les écritures
# - socA_court.csv est un fichier "réduit" contenant le minimum d'écritures (au
# maximum une par code comptable) avec un montant symbolique
# C'est ce fichier court qu'il faut en premier lieu importer. Comme cela,
# OpenConcerto ne va créer qu'une seule fois les codes comptables qu'il ne
# connait pas.
# Ensuite, il faut supprimer ces écritures importées, puis importer le premier
# fichier CSV.
#
# Le script a été testé sous environnement Linux et Python 3.5


import sys
from pathlib import Path

# liste des codecs qui serviront à produire les fichiers en sortie
#codecs = ['utf_8','iso8859_15', 'cp1252', 'cp850', 'cp858']
codecs = ['utf8']

# Récupération et vérification des fichiers données en ligne de commandes
societes = []
for a in sys.argv[1:] :
    p = Path(a)
    if p.is_file() and p.suffix == '.pnm' :
        societes.append(p)
    else :
        print("le fichier", p, "sera ignoré, ce n'est pas un fichier .pnm")


for societe in societes :
    set_codes_comptables = set()
    for codec in codecs :
        with open(str(societe), 'r', encoding='cp850') as file_pnm :
            date_fichier_court = '01/01/1970'
            with open(str(societe.with_suffix('.csv')), 'w', encoding=codec) as file_export :
                for n, line in enumerate(file_pnm) : # on lit les lignes une par une
                    # La première ligne du fichier .pnm contient un entête inutile
                    if n == 0 :
                        continue
                    # Pour chaque ligne, on découpe et récupère les éléments nécessaires :
                    # Code journal
                    journal = line[0:2].strip()
                    # Date au format JJ/MM/AAAA (en contradiction avec la boite de dialogue d'import)
                    date = line[3:9].strip()
                    date = list(date)
                    date.insert(4,'/20')
                    date.insert(2,'/')
                    date = ''.join(date)
                    # Compte comptable suit plusieurs règles personnelles
                    # - suppression des zéros en fin de compte sauf si le compte ne tient que sur 2 chiffres
                    #   (ex: on ne veut pas 51210000 mais 5121, on ne veut pas 11 mais 110)
                    # - des exceptions sont gérées (par exemple, on veut le compte 44860 et non 4486)
                    # - dans le cas d'un compte de tiers, c'est celui-ci qui est pris en compte
                    # - pour les comptes de tiers, on fait une conversion en rajoutant un 1 entre le 40 (et 41)
                    #   et le nom du compte (ex : 40TOTO devient 401TOTO)
                    compte_comptable = line[11:24].strip().rstrip('0') #on enlève les 0 à la fin du compte
                    if len(compte_comptable) < 3 : # On augmente la longueur du compte comptable à 3 chiffres
                        compte_comptable = compte_comptable + '0'
                    if compte_comptable == '4486' : # On doit garder le compte 44860 qui est le compte de la taxe d'apprentissage
                    	compte_comptable = '44860'
                    compte_tiers = line[24:38].strip() # On enlève les blancs
                    if compte_tiers : # le compte tiers est prioritaire au compte comptable
                        compte_comptable = compte_tiers[1:3] + '1' + compte_tiers[3:]
                    # Numéro de la pièce
                    num_piece = line[38:51].strip()
                    # Libellé de l'écriture
                    libelle = line[51:77].strip()
                    # Le champs 'sens' indique si l'écriture lue est à mettre au débit ou crédit
                    sens = line[83:84].strip()
                    montant = line[84:104].strip()
                    if sens == 'D'  :
                        debit = montant
                        credit = '0'
                    else :
                        debit = '0'
                        credit = montant
                    # on mémorise dans un set les comptes comptables qui seront utilisés (ils seront donc uniques)
                    set_codes_comptables.add(compte_comptable)
                    # Avant la première ligne du fichier CSV, on insère une écriture spéciale.
                    # Cette écriture, d'un montant nul mais avec des accents semble suffisante
                    # pour que le reste des écritures s'importent correctement avec les accents
                    if n == 1 :
                        file_export.write(date + ';OD;471;1;éàôâ;0;0\n')
                        date_fichier_court = date
                    # On formate la ligne à produire puis on l'enregistre dans le fichier CSV
                    ligne_export = [date, journal, compte_comptable, num_piece, libelle, debit, credit]
                    file_export.write(';'.join(ligne_export)+'\n')


                # Génère un fichier CSV avec seulement une écriture par compte
                # pour contourner le bug de la création multiple des comptes
                # inconnues lors de l'importation.
                # Les écritures sont non nulles pour sortir une balance de
                # vérification des comptes utilisées.
                # Toutes les écritures un 1 au débit, sauf la dernière pour
                # l'équilibrage
                with open(str(p.with_name(p.stem + '_court.csv')), 'w', encoding=codec) as file_pre_export :
                    date = date_fichier_court
                    journal = 'OD'
                    num_piece = ''
                    libelle = ''
                    debit = '1'
                    credit = '0'
                    list_comptes = list(set_codes_comptables)
                    list_comptes.sort()
                    nb_compte = len(list_comptes)
                    last_compte = list_comptes.pop() # on met de coté le dernier compte pour l'équilibrage
                    for compte_comptable in list_comptes :
                        ligne_export = [date, '"' + journal + '"', compte_comptable, num_piece, libelle, debit, credit]
                        file_pre_export.write(';'.join(ligne_export)+'\n')
                    # Dernière ligne pour l'équilibrage des écritures
                    ligne_export = [date, '"' + journal + '"', last_compte, num_piece, libelle, '0', str(nb_compte - 1)]
                    file_pre_export.write(';'.join(ligne_export)+'\n')

Utilisateur Cloud avec un contrat de maintenance.
Avatar du membre
guillaume
Messages : 2429
Enregistré le : ven. févr. 11, 2011 7:15 pm

sam. mars 10, 2018 3:33 pm

Bonjour,

Merci pour votre contribution.
A noter que le bug d'import de la 1.5.2 est corrigé dans la future 1.5.3.

Cordialement,
Directeur technique d'OpenConcerto qui dans son temps libre s'occupe du forum.
Pour une assistance pro, nous sommes joignables à ILM Informatique contre quelques jetons.
Pensez aussi à lire le manuel !
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

sam. mars 10, 2018 4:33 pm

De rien, si cela peut être utile à d'autres.

Par contre, de quel bug parles tu, car j'en compte 3 pour l'instant :
- les journaux importés non reconnus du premier coup
- la création multiple des comptes comptables
- les caractères accentués qui ne passent pas toujours
Utilisateur Cloud avec un contrat de maintenance.
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

sam. mars 10, 2018 9:35 pm

Et un petit 4ème, à savoir que la fenêtre de sélection de fichiers pour l'import impose un filtre .ods donc on ne peut voir les fichiers .csv.
Utilisateur Cloud avec un contrat de maintenance.
bfilloux
Messages : 1
Enregistré le : mar. mars 13, 2018 11:12 am

mar. mars 13, 2018 11:16 am

Bonjour,
je suis nouvel utilisateur d'openConcerto et j'ai besoin d'intégrer une compta antérieurement exécutée sur Sage... Toutefois j'ai des fichiers .mae et non .pnm. Votre script serait-il adapté à cette "reprise de compta" ou y a-t'il une autre procédure ? (et désolé si cette discussion n'est pas la place de ce message...)
Bien à vous
BF
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

mar. mars 13, 2018 1:52 pm

Mes fichiers comptables sont également en .mae. Mais je me sers de la fonction exportation dans Sage i7 pour avoir le fichier .pnm.
Comme indiqué dans les commentaires du script, j'ai exporté au format "Format Trésorerie Sage et Ciel". Le logiciel Sage i7 produit alors le fichier .pnm.
Utilisateur Cloud avec un contrat de maintenance.
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

lun. mars 19, 2018 11:19 am

Bonjour,

J'ai mis à jour mon script :
- Tous les fichiers courts sont générés en cas de sélection de plusieurs fichiers.
- les caractères ";" sont éliminés dans les libellés des écritures

Vous pouvez le télécharger ici. Ou bien le copier coller ci dessous. (On ne peut pas attacher de fichiers .py ou même .txt aux posts)

Code : Tout sélectionner

# Convertit un/des fichier(s) d'écritures comptables .pnm en fichier CSV pour
# l'erp OpenConcerto
#
# Le format .pnm est généré par Sage Comptabilité Ligne 100 et i7 en exportant
# au "Format Trésorerie Sage et Ciel"
#
# En sortie, le fichier sera au format CSV accepté par OpenConcerto
#
# Le chemin et le ficher .pnm doit être indiqué après l'appel du script :
# ex : pnmtocsv.py socA.pnm
#      pnmtocsv.py chemin/socB.pnm
#
# Plusieurs fichiers peuvent être indiqués :
# ex : pnmtocsv.py socA.pnm chemin/socB.pnm
#
# En raison d'un bug dans la fonction d'importation dans OpenConcerto 5.2, le
# script va générer deux fichiers CSV :
# - socA.csv est le fichier "normal" contenant toutes les écritures
# - socA_court.csv est un fichier "réduit" contenant le minimum d'écritures (au
# maximum une par code comptable) avec un montant symbolique
# C'est ce fichier court qu'il faut en premier lieu importer. Comme cela,
# OpenConcerto ne va créer qu'une seule fois les codes comptables qu'il ne
# connait pas.
# Ensuite, il faut supprimer ces écritures importées, puis importer le premier
# fichier CSV.
#
# Le script a été testé sous environnement Linux et Python 3.5

# Ver 1.1 :
# - bugfix mauvaise creation "_court.csv" quand plusieurs fichiers
# - new : suppression des ";" éventuels dans le libellé

import sys
from pathlib import Path

# liste des codecs qui serviront à produire les fichiers en sortie
#codecs = ['utf_8','iso8859_15', 'cp1252', 'cp850', 'cp858']
codecs = ['utf8']

# Récupération et vérification des fichiers données en ligne de commandes
societes = []
for a in sys.argv[1:] :
    p = Path(a)
    if p.is_file() and p.suffix == '.pnm' :
        societes.append(p)
    else :
        print("le fichier", p, "sera ignoré, ce n'est pas un fichier .pnm")


for societe in societes :
    set_codes_comptables = set()
    for codec in codecs :
        with open(str(societe), 'r', encoding='cp850') as file_pnm :
            date_fichier_court = '01/01/1970'
            with open(str(societe.with_suffix('.csv')), 'w', encoding=codec) as file_export :
                for n, line in enumerate(file_pnm) : # on lit les lignes une par une
                    # La première ligne du fichier .pnm contient un entête inutile
                    if n == 0 :
                        continue
                    # Pour chaque ligne, on découpe et récupère les éléments nécessaires :
                    # Code journal
                    journal = line[0:2].strip()
                    # Date au format JJ/MM/AAAA (en contradiction avec la boite de dialogue d'import)
                    date = line[3:9].strip()
                    date = list(date)
                    date.insert(4,'/20')
                    date.insert(2,'/')
                    date = ''.join(date)
                    # Compte comptable suit plusieurs règles personnelles
                    # - suppression des zéros en fin de compte sauf si le compte ne tient que sur 2 chiffres
                    #   (ex: on ne veut pas 51210000 mais 5121, on ne veut pas 11 mais 110)
                    # - des exceptions sont gérées (par exemple, on veut le compte 44860 et non 4486)
                    # - dans le cas d'un compte de tiers, c'est celui-ci qui est pris en compte
                    # - pour les comptes de tiers, on fait une conversion en rajoutant un 1 entre le 40 (et 41)
                    #   et le nom du compte (ex : 40TOTO devient 401TOTO)
                    compte_comptable = line[11:24].strip().rstrip('0') #on enlève les 0 à la fin du compte
                    if len(compte_comptable) < 3 : # On augmente la longueur du compte comptable à 3 chiffres
                        compte_comptable = compte_comptable + '0'
                    if compte_comptable == '4486' : # On doit garder le compte 44860 qui est le compte de la taxe d'apprentissage
                    	compte_comptable = '44860'
                    compte_tiers = line[24:38].strip() # On enlève les blancs
                    if compte_tiers : # le compte tiers est prioritaire au compte comptable
                        compte_comptable = compte_tiers[1:3] + '1' + compte_tiers[3:]
                    # Numéro de la pièce
                    num_piece = line[38:51].strip()
                    # Libellé de l'écriture
                    libelle = line[51:77].strip()
                    libelle = libelle.replace(';','')
                    # Le champs 'sens' indique si l'écriture lue est à mettre au débit ou crédit
                    sens = line[83:84].strip()
                    montant = line[84:104].strip()
                    if sens == 'D'  :
                        debit = montant
                        credit = '0'
                    else :
                        debit = '0'
                        credit = montant
                    # on mémorise dans un set les comptes comptables qui seront utilisés (ils seront donc uniques)
                    set_codes_comptables.add(compte_comptable)
                    # Avant la première ligne du fichier CSV, on insère une écriture spéciale.
                    # Cette écriture, d'un montant nul mais avec des accents semble suffisante
                    # pour que le reste des écritures s'importent correctement avec les accents
                    if n == 1 :
                        file_export.write(date + ';OD;471;1;éàôâ;0;0\n')
                        date_fichier_court = date
                    # On formate la ligne à produire puis on l'enregistre dans le fichier CSV
                    ligne_export = [date, journal, compte_comptable, num_piece, libelle, debit, credit]
                    file_export.write(';'.join(ligne_export)+'\n')


                # Génère un fichier CSV avec seulement une écriture par compte
                # pour contourner le bug de la création multiple des comptes
                # inconnues lors de l'importation.
                # Les écritures sont non nulles pour sortir une balance de
                # vérification des comptes utilisées.
                # Toutes les écritures un 1 au débit, sauf la dernière pour
                # l'équilibrage
                with open(str(societe.with_name(societe.stem + '_court.csv')), 'w', encoding=codec) as file_pre_export :
                    date = date_fichier_court
                    journal = 'OD'
                    num_piece = ''
                    libelle = ''
                    debit = '1'
                    credit = '0'
                    list_comptes = list(set_codes_comptables)
                    list_comptes.sort()
                    nb_compte = len(list_comptes)
                    last_compte = list_comptes.pop() # on met de coté le dernier compte pour l'équilibrage
                    for compte_comptable in list_comptes :
                        ligne_export = [date, '"' + journal + '"', compte_comptable, num_piece, libelle, debit, credit]
                        file_pre_export.write(';'.join(ligne_export)+'\n')
                    # Dernière ligne pour l'équilibrage des écritures
                    ligne_export = [date, '"' + journal + '"', last_compte, num_piece, libelle, '0', str(nb_compte - 1)]
                    file_pre_export.write(';'.join(ligne_export)+'\n')
Utilisateur Cloud avec un contrat de maintenance.
bill
Messages : 49
Enregistré le : mar. mars 17, 2015 12:56 pm
Localisation : Cordes sur Ciel
Contact :

sam. mars 31, 2018 9:53 am

Bonjour,
J'avais le même problème avec à peu près la même erreur lors de l'export des à-nouveaux mono-poste pour import multi-poste. Voici comment j'ai procédé avec les infos prises dans le script python de SRI.

1. :roll: J'ai édité le fichier exporté avec calc pour en premier modifier les formats de date en jj/MM/AAAA.

2. :geek: J'ai supprimé des colonnes pour ne garder dans cet ordre que :
PiceceDate, JournalCode, CompteNum PieceRef, EcritureLib, Debit, Credit

3. ;) J'ai renommé ces colonnes dans la première ligne comme dans le script ci-dessus, c'est à dire, toujours dans le même ordre :
date, journal, compte_comptable, num_piece, libelle, debit, credit

4. :cry: Grr, toujours la même erreur "unparseable date...."

5. :idea: J'ai supprimé la première ligne pour voir... YES ! :D C'était donc ça ! Importation réussie du fichier .ods

L'aventure continue. Encore bravo à lékip OC

Bill 8-)
Bill OUT 8-)
Adepte du libre, du partage et de l'échange, qui économise son énergie grâce à OpenConcerto

Win 10 / Xubuntu - OpenConcerto mono et multi-postes - Postgres distant sur VM Proxmox
SRI
Messages : 257
Enregistré le : mer. mars 15, 2017 8:50 pm

mar. sept. 11, 2018 12:54 pm

Suite à quelques tests, l'utilisation du fichier CSV "court" généré par mon script n'est plus nécessaire à partir de la 1.5.4 (peut être même de la 1.5.3). Vous pouvez allez voir le topic donné dans le premier message.
Utilisateur Cloud avec un contrat de maintenance.
Répondre