Browse Source

Initial commit

Philippe Le Brouster 9 years ago
commit
c1ff4ce087
11 changed files with 884 additions and 0 deletions
  1. 3 0
      .gitignore
  2. 95 0
      bin/himport
  3. 157 0
      himport.conf.template
  4. 0 0
      himports/__init__.py
  5. 525 0
      himports/hledger.py
  6. 53 0
      himports/settings.py
  7. 2 0
      requirements.pip
  8. 6 0
      utils/pyenv-exec
  9. 6 0
      utils/pyenv-himport
  10. 23 0
      utils/pyenv-init
  11. 14 0
      utils/scripts.cfg

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+*.pyc
+.*.swp
+python-env

+ 95 - 0
bin/himport

@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import getopt
+import locale
+import logging
+import os
+import shutil
+import sys
+import csv
+import datetime
+import codecs
+import getpass
+
+from himports import settings
+from himports.hledger import *
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger('hreport')
+
+sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
+
+def process_args(argv):
+    options = {}
+    usage = u'himport -p' 
+    try:
+        opts, args = getopt.getopt(
+                argv, "hp:P:",
+            ["mysql-password=","mysql-port" ])
+    except getopt.GetoptError:
+        print usage 
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt == '-h':
+            print usage
+            sys.exit()
+        elif opt in ("-p", "--mysql-password"):
+            options['mysql_password'] = arg
+        elif opt in ("-P", "--mysql-port"):
+            options['mysql_port'] = arg
+
+    return options
+
+def do_mysql(options):
+
+    
+    # On récupère les données via la base de données de dolibarr
+    s = settings.get('MYSQL_SETTINGS')
+
+    password = s['password']
+    if 'mysql_password' in options:
+        password = options['mysql_password']
+    if password is None or password == "":
+        password = getpass.getpass("password for mysql user '%s': " % (s['user']))
+
+    port = s['port']
+    if 'mysql_port' in options:
+        port = options['mysql_port']
+
+    dolibarr = DolibarrSQL(s['host'],port, s['database'], s['user'], password)
+    dolibarr.connect()
+    
+    bank_entries = dolibarr.get_bank_entries()
+    sell_entries = dolibarr.get_sell_entries()
+    supplier_entries = dolibarr.get_supplier_entries()
+
+    dolibarr.disconnect()
+
+    # On vérifie s'il manque des postes comptables dans les écritures
+    pc_missing = set()
+    pc_missing.update(bank_entries.check_pc())
+    pc_missing.update(sell_entries.check_pc())
+    pc_missing.update(supplier_entries.check_pc())
+    if len(pc_missing) > 0:
+        print "WARNING: poste comptable manquant"
+        for pc in pc_missing:
+            sys.stdout.write("%s\n" % (pc))
+    
+    # On écrie les fichiers hledger
+    Writer.write("bank",bank_entries)            
+    Writer.write("sells",sell_entries)            
+    Writer.write("suppliers",supplier_entries)            
+    Writer.write_hreport_plan()
+
+def main(argv):
+    locale.setlocale(locale.LC_ALL, b'fr_FR.utf-8')
+    options = process_args(argv)
+    
+    do_mysql(options)
+
+
+if __name__ == "__main__":
+    main(sys.argv[1:])

+ 157 - 0
himport.conf.template

@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import datetime
+
+#
+# GENERAL
+#
+OUTPUT_DIR = "./out"
+
+OUTPUT_FILES = {
+    "bank":      "%year%/ecritures.d/banque.journal",
+    "sells":     "%year%/ecritures.d/vente.journal",
+    "suppliers": "%year%/ecritures.d/achat.journal",
+    "pc":        "plan.journal",
+}
+
+MYSQL_SETTINGS = {
+    "host":      "localhost",
+    "database":  "dolibarr_test",
+    "user":      "dolibarr_test",
+    "password":  "dae1ohCu",
+    "port":      3306,
+}
+
+#
+# Describe accounting year if not standard (from 1st january to 31 december)
+#
+ACCOUNTING_YEARS = {
+    ("2012", "2011/01/01", "2012/12/31")
+}
+
+TVA_REFS = {
+    'tva_deductible': '4456',
+    'tva_collecte': '4457',
+}
+
+#
+# Plan comptables (nom, description)
+#
+PC_NAMES = {
+    '1'         : 'capitaux',
+    '11'        : 'capitaux:report à nouveau',
+    '117'       : 'capitaux:report à nouveau:positif',
+    '119'       : 'capitaux:report à nouveau:négatif',
+    '12'        : 'capitaux:résultat',
+    '120'       : 'capitaux:résultat:positif',
+    '129'       : 'capitaux:résultat:négatif',
+    '2'         : 'immobilisations',
+    '201'       : "immobilisations:incorporelles:frais d'établissement",
+    '21'        : "immobilisations:corporelles",
+    '2183'      : "immobilisations:corporelles:matériel informatique",
+    '2184'      : "immobilisations:corporelles:mobilier",
+    '281'       : "immobilisations:amortissements",          
+    '2801'      : "immobilisations:amortissements:incorporelles",
+    '2818'      : "immobilisations:amortissements:corporelles",
+    '28183'     : "immobilisations:amortissements:corporelles:matériel informatique",
+    '28184'     : "immobilisations:amortissements:corporelles:mobilier", 
+    '4'         : "tiers",
+    '40'        : "tiers:fournisseurs",
+    '401101'    : "tiers:fournisseurs:telehouse",
+    '401102'    : "tiers:fournisseurs:Liazo",
+    '401103'    : "tiers:fournisseurs:Absolight",
+    '401104'    : "tiers:fournisseurs:Tata Communication",
+    '401105'    : "tiers:fournisseurs:Lost Oasis",
+    '401106'    : "tiers:fournisseurs:RIPE NCC",
+    '401107'    : "tiers:fournisseurs:Crédit Mutuel",
+    '401108'    : "tiers:fournisseurs:LCD International",
+    '401109'    : "tiers:fournisseurs:CICP",
+    '401110'    : "tiers:fournisseurs:Alturna Network",
+    '401111'    : "tiers:fournisseurs:GANDI SAS",
+    '401112'    : "tiers:fournisseurs:AS Info",
+    '41'        : "tiers:clients",
+    '411101'    : "tiers:clients:Altern B",
+    '411102'    : "tiers:clients:FDN",
+    '411103'    : "tiers:clients:Globenet",
+    '411104'    : "tiers:clients:Linagora",
+    '411105'    : "tiers:clients:Gixe",
+    '411106'    : "tiers:clients:LAutreNet",
+    '411107'    : "tiers:clients:Rézine",
+    '411108'    : "tiers:clients:Tetaneutral",
+    '411109'    : "tiers:clients:Grenode",
+    '411110'    : "tiers:clients:Franciliens",
+    '411111'    : "tiers:clients:Illyse",
+    '411112'    : "tiers:clients:Ilico",
+    '411113'    : "tiers:clients:Octopuce",
+    '411114'    : "tiers:clients:Artefact",
+    '411115'    : "tiers:clients:NDN",
+    '411116'    : "tiers:clients:LDN",
+    '411117'    : "tiers:clients:Neutrinet",
+    '411118'    : "tiers:clients:AssoDIUT",
+    '411119'    : "tiers:clients:Rhizome",
+    '411120'    : "tiers:clients:BeTech",
+    '411121'    : "tiers:clients:personne physique",
+    '445'       : "tiers:etat:tva",
+    '4456'      : "tiers:etat:tva:déductible",
+    '4457'      : "tiers:etat:tva:collecté",
+    '468'       : "tiers:divers",
+    '4686'      : "tiers:divers:charges à payer",
+    '5'         : "finances",
+    '512'       : "finances:banque",
+    '5121'      : "finances:banque:Crédit Mutuel",
+    '5122'      : "finances:banque:GIE",
+    '532'       : "finances:chèques à encaisser",
+    '6'         : "charges",
+    '60'        : "charges:achats",
+    '604'       : "charges:achats:prestation de services",
+    '606'       : "charges:achats:non-stockés",
+    '6061'      : "charges:achats:non-stockés:fournitures non stockables",
+    '6063'      : "charges:achats:non-stockés:fournitures d'entretien et petits équipements",
+    '6064'      : "charges:achats:non-stockés:fournitures administratives",
+    '6068'      : "charges:achats:non-stockés:autres matières et fournitures",
+    '61'        : "charges:services",
+    '613'       : "charges:services:locations",
+    '613001'    : "charges:services:locations:hosting",
+    '613002'    : "charges:services:locations:lir",
+    '616'       : "charges:services:assurances",
+    '62'        : "charges:autres services",
+    '6227'      : "charges:autres services:frais d'actes",
+    '626'       : "charges:autres services:pce",
+    '626001'    : "charges:autres services:pce:internet",
+    '625'       : "charges:autres services:déplacement, missions,réceptions",
+    '627'       : "charges:autres services:banque",
+    '628'       : "charges:autres services:divers",
+    '6281'      : "charges:autres services:divers:cotisations",
+    '651'       : "charges:redevances",
+    '672'       : "charges:charges sur exercices antérieur",
+    '6811'      : "charges:amortissements",
+    '68111'     : "charges:amortissements:incorporelles",
+    '68112'     : "charges:amortissements:corporelles",
+    '7'         : "produits",
+    '706'       : "produits:services", 
+    '706001'    : "produits:services:routage",
+    '706002'    : "produits:services:commutation",
+    '706003'    : "produits:services:lir",
+    '7060031'   : "produits:services:lir:pi",
+    '7060032'   : "produits:services:lir:pa",
+    '706004'    : "produits:services:hosting",
+    '706005'    : "produits:services:transit",
+    '7060050'   : "produits:services:transit:bp-0",
+    '7060051'   : "produits:services:transit:bp-a",
+    '7060052'   : "produits:services:transit:bp-b",
+    '7060053'   : "produits:services:transit:bp-c",
+    '7060054'   : "produits:services:transit:bp-d",
+    '756'       : "produits:cotisations",
+    '77'        : "produits:exceptionnels",
+    '7718'      : "produits:exceptionnels:dons manuels",
+
+
+}
+
+PC_DESCRIPTIONS = {
+    '41'  : 'Clients et comptes rattachés',
+    '5121': 'CC Crédit Mutuel',
+    '5122': 'Compte GIE',
+
+}

+ 0 - 0
himports/__init__.py


File diff suppressed because it is too large
+ 525 - 0
himports/hledger.py


+ 53 - 0
himports/settings.py

@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import os
+import datetime
+
+CONF_FILES = [
+    "himport.conf",
+    "himport.conf.local",
+]
+
+__settings = None
+
+
+def get(opt):
+    ret = __settings.get(opt)
+    if ret is None:
+        raise "Settings not found"
+    return ret
+
+
+def get_ledger_account(code):
+    account_names = __settings.get("PC_NAMES")
+    if code in account_names:
+        return account_names[code]
+    else:
+        return code
+
+
+__settings = {}
+for conf_file in CONF_FILES:
+    if os.path.isfile(conf_file):
+        execfile(conf_file, __settings)
+
+if __settings is None:
+    raise Exception(
+        "Need a configuration file. One of %s" % ','.join(CONF_FILES))
+
+if 'OUTPUT_DIR' not in __settings:
+    raise Exception('need OUTPUT_DIR settings')
+
+if 'PC_REFS' not in __settings:
+    raise Exception('need PC_REFS settings')
+
+if 'PC_NAMES' not in __settings:
+    raise Exception('need PC_NAMES settings')
+
+if 'ACCOUNTING_YEARS' in __settings:
+    __settings['ACCOUNTING_YEARS'] = [(
+        year,
+        datetime.datetime.strptime(dbegin, "%Y/%m/%d").date(),
+        datetime.datetime.strptime(dend, "%Y/%m/%d").date(),
+        ) for (year, dbegin, dend) in __settings['ACCOUNTING_YEARS']]

+ 2 - 0
requirements.pip

@@ -0,0 +1,2 @@
+
+

+ 6 - 0
utils/pyenv-exec

@@ -0,0 +1,6 @@
+#! /bin/sh
+
+# this script aim to initialise the environement.
+. $(dirname $0)/scripts.cfg
+
+"$@"

+ 6 - 0
utils/pyenv-himport

@@ -0,0 +1,6 @@
+#! /bin/sh
+
+# this script aim to initialise the environement.
+. $(dirname $0)/scripts.cfg
+
+./bin/himport $@

+ 23 - 0
utils/pyenv-init

@@ -0,0 +1,23 @@
+#! /bin/bash
+
+set -e
+
+# this script aim to initialise the environement.
+. $(dirname $0)/scripts.cfg
+
+# create python virtualenv
+if ! which virtualenv >/dev/null; then
+    echo "Please install python-virtualenv"
+    exit 1
+fi
+
+if [ ! -d "$PYTHONENV_DIR" ]; then 
+    if ! virtualenv $PYTHONENV_DIR --system-site-packages; then
+        virtualenv $PYTHONENV_DIR
+    fi
+fi
+
+. $PYTHONENV_DIR/bin/activate
+
+pip install -r $BASE_DIR/requirements.pip
+

+ 14 - 0
utils/scripts.cfg

@@ -0,0 +1,14 @@
+#! /bin/sh
+
+DIR=$(dirname $0)
+BASE_DIR=$(readlink -f $DIR/..)
+UTILS_DIR=$BASE_DIR/utils
+PYTHONENV_DIR=${BASE_DIR}/python-env
+
+# use the virtual python environnement in $VAR_DIR/python-env
+[ ! -e "$PYTHONENV_DIR/bin/activate" ] || . $PYTHONENV_DIR/bin/activate
+
+# Include the project python path
+export PYTHONPATH="$BASE_DIR:$PYTHONPATH"
+
+