Transformation de fichiers Quicken vers GNU Cash

le contexte

J'utilise Quicken depuis 25 ans; d'abord la version 2001, puis la version 2006. Comme je suis sous Linux, Quicken tourne dans Wine.

La seule chose qui ne fonctionnait pas sont les backups; la fonction "Backup" se plantait, mais je pouvais toujours copier le fichier courant, ainsi que l'exporter.

Malheureusement, depuis une mise à jour de Wine, je constate des comportements bizarre; erreur au lancement (table de "Memorized transactions" tronquée), à l'affichage (transactions dupliquées), mais plus inquiétant, transactions manquantes.

C'est le temps de passer à une solution plus moderne, et surtout, native sous Linux. Mon choix c'est porté sur GNU Cash, dont l'ergonomie est assez similaire à Quicken.

Naturellement, je veux importer mon historique de transactions qui, je dois dire, est peut être plutôt complexe: 110 catégories distinctes, une vingtaine de comptes, amortissement hypothécaire, comptes d'investissement (avec des "parts" et un prix par part).

Premier essai

A priori assez simple: exporter de Quicken au format QIF, puis importer le QIF dans GNU Cash.

Le format QIF est un format texte, donc modifiable avec un simple éditeur de texte (j'utilise vim), mais aussi exploitable par des commandes de traitement de texte comme sed ou awk / nawk.

Le premier problème était dans Quicken qui plante à l'écriture du fichier d'export. J'ai fini par exporter année par année, soit 25 fichiers, pour un total de 180500 lignes. J'ai une année problématique qui semble corrompue; je ferai un export sous mon ancienne version de Wine pour celle là.

Un autre problème est que Quicken exporte au format Windows; les fins de lignes sont donc "\r\n" au lieu de "\n" pour Linux; facile à traiter avec sed:

$ sed -e 's/\r//' Export2010.QIF

Cependant, l'import n'a pas fonctionné: GNU Cash se plaint d'un format de date incorrect. Une des raisons que j'ai identifié est la présence de slash (/) dans des descriptions de compte. En effet, la description d'un compte est un champ de type D ce qui est également le préfixe pour une date.

DCda/Que Pension Plan  <-- Ici le champ D correspond à une description
...
D10/ 1' 9              <-- Mais ici, il correspond à une date
...

Pour être certain que toutes les dates sont correctes, j'ai fait un petit script awk qui les corrige:

$ cat date.awk
/^D/	{D=substr($0,2,length($0)-1);
		sub("' ","/200",D);
		sub("'","/20",D);
		sub("/ ","/0",D);
		sub(/^[1-9]\//,"0&",D);
	}
$ sed -e 's/\r//' Export2010.QIF | nawk -f date.awk 
...
D01/01/2009
...
D01/12/2009
...

Le format QIF

Mon idée était d'importer les fichiers QIF dans LibreOffice Calc (l'équivalent de Excel sous Linux) afin de vérifier les transactions: éliminer les doublons et recréer les transactions manquantes. Comme les fichiers QIF sont des fichiers texte, il semblait "facile" de les passer à travers un script awk pour les ressortir en CSV (comma-separated values).

Les fichiers QIF sont organisés sections ("chunks"), avec un début et une fin de section. Le nombre de sections dépend de ce qui a été sélectionné à exporter. Malheureusement le format manque de cohérence, les mêmes balises servant à plusieurs usages; ce qui rend l'exploitation par les commandes de traitement de stream (sed, awk ou nawk) difficile.

Rappel: la commande sed (stream editor) permet d'effectuer des commandes basiques (filtrage, remplacement, ...) sur un flot de caractères. Les commandes awk-like (awk, gawk = Gnu awk, et nawk = new awk), qui prennent leurs noms des 3 créateurs Alfred Aho, Peter Weinberger et Brian Kernighan, sont des commandes de traitement de flot plus élaborées, comportant un langage de programmation.

Les chunks

Un chunk débute par ! et se termine par un autre chunk ou la fin du fichier.

La balise "Autoswitch"

Cette balise est utilisée pour lister les comptes, si l'option d'exporter la liste des comptes a été sélectionnée.

$ sed -e 's/\r//' Export*.QIF | grep "^!" | sort -u
!Option:AutoSwitch  <-- Début de la liste des comptes
...
!Clear:AutoSwitch   <-- Fin de la liste des comptes
...

On s'attendrait donc à ne voir cette balise qu'une seule fois; et bien non! Elle apparaît 2 fois dans l'export, comme on le voit sur cet exemple:

$ egrep '^!' -n Export2010.QIF 
1:!Type:Cat             <-- on liste les catégories
770:!Option:AutoSwitch  <-- début d'une liste..
771:!Account            <-- c'est une liste de comptes
931:!Clear:AutoSwitch   <-- fin de liste
932:!Option:AutoSwitch  <-- de nouveau, début d'une liste..
933:!Account            <-- c'est une liste de comptes...
937:!Type:Bank          <-- ... bancaires
...
938:!Account
942:!Type:Bank
943:!Account
...                     <-- à partir d'ici, on trouve également des transactions
3820:!Type:CCard        <-- puis vient la liste des cartes de crédit
3821:!Account
...
5291:!Type:Cash         <-- puis la liste des cartes des comptes liquides
5292:!Account
...                     <-- etc, mais pas de fin de liste !

Voici les identifiants et leur description (source: https://github.com/Gnucash/gnucash/blob/stable/gnucash/import-export/qif-imp/file-format.txt)

$ sed -e 's/\r//' Export*.QIF | grep "^!" | sort -u
!Account            <-- le compte concerné
!Type:Bank          <-- compte bancaire
!Type:Cash          <-- argent liquide
!Type:Cat           <-- catégories de transactions
!Type:CCard         <-- carte de crédit
!Type:Invst         <-- compte d'investissement
!Type:Memorized     <-- transactions mémorisées, pour faciliter la saisie
!Type:Oth A         <-- autre compte d'actifs
!Type:Oth L         <-- autre compte de passifs (dettes, hypothèques)
!Type:Security      <-- compte de placements (actions, obligations)
!Type:Prices        <-- prix pour les compte de placements (par exemple prix unitaire de l'action)
Le premier chunk, cat, décrit les catégories de transaction, c'est-à-dire les différentes "case" dans lesquelles vous pouvez classer les sommes:
$ head -800 Export2010.QIF | sed -e 's/\r//'
!Type:Cat
NASS GRP             <-- N (name) = nom de la catégorie
DAssurance Groupe    <-- D = description
E                    <-- type de catégorie; dépense (E = expense) ou gain (I  = income)
^                    <-- séparateur de fin
...
NAuto                <-- nom de la catégorie
DAutomobile Expenses <-- description
E                    <-- type de catégorie
^                    <-- séparateur de fin
NAuto:Fuel           <-- nom de la catégorie (ici une sous-catégorie)
DAuto Gas & Oil
T                    <-- T (tax): cette catégorie est reliée à un compte de taxes
R5728                <-- Compte de taxes relié
E
^
...
Certaines catégories comporte un buget mensuel; je pense qu'il est ajouté automatiquement à la première saisie. Personnellement, je ne l'utilise pas. On voit donc 12 balises B qui se suivent:
NAuto:Insurance
DAuto Insurance
B-74.17              <-- B (budget mensuel): somme pour le premier mois
B0.00                <-- mois suivant
B0.00                <-- etc...
B0.00
B0.00
B0.00
B0.00
B0.00
B0.00
B0.00
B0.00
B0.00
T
R5760
E
^
...
Le chunk suivant, !Account, décrit les différents comptes:
!Account
N0000000-ES1 Épargne stable REER  <-- nom du compte
TBank                             <-- type de compte (Bank = compte bancaire)
^                                 <-- séparateur de fin
NCELI
TBank
DCompte d'épargne CELI            <-- description
^                                 <-- séparateur de fin
...
NMasterCard
TCCard                            <-- type de compte (CCard = carte de crédit)
L0.00                             <-- Limite de crédit
^
...
Viennent ensuite les transactions qui se présente sous la forme suivante:
!Account                          <-- On identifie le compte duquel les transaction suivent
NCELI
DCompte d'Épargne CELI
TBank
^
!Type:Bank                        <-- Ensuite viennent les transactions:
D3/22'10                          <-- Date de transaction: 22 mars 2010
U0.00                             <-- montant
T0.00                             <-- montant ?? (je n'ai jamais vu de différences)
CX                                <-- C = Cleared; concilliation bancaire
POpening Balance                  <-- P = Payee; dans ce cas, description
L[CELI]                           <-- Catégorie ou compte (*)
^
(*) Quand il s'agit d'une catégorie, elle apparaît directement:
LInterest Inc
Quand il s'agit d'un transfert vers un autre compte, c'est le compte destination qui apparaît entre les crochets:
L[CELI]

Comments

Popular Posts