Skip to content
Extraits de code Groupes Projets
riga_odoo_import.py 10,3 ko
Newer Older
  • Learn to ignore specific revisions
  • Benjamin's avatar
    Benjamin a validé
    # © 2019 Le Filament (<http://www.le-filament.com>)
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
    
    import csv
    import io
    import datetime
    import logging
    from ftplib import FTP
    
    
    from odoo import models, fields, exceptions
    
    Benjamin's avatar
    Benjamin a validé
    
    _logger = logging.getLogger(__name__)
    
    
    class CgscopRigaOdooImport(models.Model):
    
        """
            Ce modèle permet de logguer les imports de RIGA
            vers Odoo
    
    Benjamin's avatar
    Benjamin a validé
        """
        _name = 'riga.odoo.import'
        _description = 'Synchronisation RIGA vers Odoo'
        _rec_name = 'model_id'
        _order = 'create_date desc'
    
        model_id = fields.Many2one(
            comodel_name='ir.model',
            string='Modèle')
        is_sync = fields.Boolean('Synchronisé', default=False)
        is_warning = fields.Boolean('Warning', default=False)
        log = fields.Text('Log')
        is_error = fields.Boolean('Erreur Synchro', default=False)
    
        is_resync = fields.Boolean('Re-Synchronisé', default=False)
    
        filename = fields.Char('Nom du fichier')
    
    Benjamin's avatar
    Benjamin a validé
    
        # ------------------------------------------------------
        # Fonctions génériques
        # ------------------------------------------------------
    
    Benjamin's avatar
    Benjamin a validé
        def _get_ftp_file(self, filename, ftp_channel):
    
            """
                Fonction de récupértion d'ufichier sur un FTP.
    
    Benjamin's avatar
    Benjamin a validé
    
    
                Cette fonction prend en paramètre un nom de fichier et recherche
                dans le fichier de conf Odoo les login et password, puis dans les
                paramètres système l'url et le path pour se connecter et retourner
                le fichier binaire souhaité.
    
    Benjamin's avatar
    Benjamin a validé
    
    
                : param filename: (string) nom du fichier
    
    Benjamin's avatar
    Benjamin a validé
    
    
                @return csv_file: (binary) fichier binaire à traiter
    
    Benjamin's avatar
    Benjamin a validé
            """
    
    Benjamin's avatar
    Benjamin a validé
            param = ftp_channel
    
            ftp_login = param.login
            ftp_pass = param.password
    
    Benjamin's avatar
    Benjamin a validé
            # Connexion FTP
            try:
                # Connexion ftp
    
                ftp = FTP(param.url, ftp_login, ftp_pass)
    
    Benjamin's avatar
    Benjamin a validé
                # Changement de path
    
                ftp.cwd(param.path)
    
    Benjamin's avatar
    Benjamin a validé
                # Lecture du fichier
                csv_file = io.BytesIO()
                ftp.retrbinary(
                    "RETR " + filename,
                    csv_file.write)
    
                _logger.info(
                    "Connecté à l'URL : %s  - Path : %s",
    
                    param.url,
                    param.path
    
    Benjamin's avatar
    Benjamin a validé
                return csv_file
            except Exception as e:
                raise exceptions.Warning(e)
    
    
        def _read_file(self, model, file, table, primary_key, header_key,
    
            filename, resync=False, log_id=None):
    
            """
                Cette fonction prend en paramètre un fichier CSV et lit
                chaque ligne pour mettre à jour la ligne de la table correspondante
                si elle existe ou la créer si l'organisme existe
    
                :param model (obj): modèle pour importer les données
                :param file (binary): fichier CSV à traiter
                :param table (list): liste de tuples de correspondance
                                    ('champ_odoo', 'champ_csv')
                :param primary_key (string): header du fichier CSV correspondant à la clé
                :param parent_id (string):  header du fichier CSV pour lequel on va
                                            rechercher l'ID RIGA de l'organisme
    
                :param filename (string): nom du fichier
    
                :param resync (bool): le fichier a été synchronisé manuellement
                :param log_id (obj): ojjet à mettre à jour si pas de création
    
    Benjamin's avatar
    Benjamin a validé
            """
    
            try:
                file.seek(0)
                # Création du lecteur CSV.
                reader = csv.DictReader(
                    io.TextIOWrapper(file, encoding="utf-8-sig", newline=None),
                    delimiter=';')
                model_obj = self.env[model.model]
                crea_nb = 0
                maj_nb = 0
                log = ""
                # Lecture de chaque ligne
                for row in reader:
                    # Création de l'objet
                    vals = {}
                    for field in table:
    
    Benjamin's avatar
    Benjamin a validé
                        vals.update({
    
                            field[0]: self._cast_type(
                                model=model,
                                field=field[0],
                                value=row[field[1]],
                                relation=field[2])
    
    Benjamin's avatar
    Benjamin a validé
                        })
    
                    # Vérification de la ligne dans la base
                    line = model_obj.search([
                        ['id_riga', '=', row[primary_key]]])
                    # Si il y a un enregistrement, on met à jour
                    if line:
                        line.write(vals)
                        maj_nb += 1
    
    Benjamin's avatar
    Benjamin a validé
                    else:
    
                        # Check d'un organisme dans la base
                        partner = self.env['res.partner'].search([
                            ['id_riga', '=', row[header_key]],
                            ['is_cooperative', '=', True]])
                        # Si l'organisme existe, on crée un enregistrement
                        if partner:
                            vals.update({
                                'partner_id': partner.id
                            })
                            model_obj.create(vals)
                            crea_nb += 1
                        else:
                            log += ("Pas d'enredistrement trouvé pour l'id " +
                                    row[primary_key] + " avec l'id riga " +
                                    row[header_key] + "\n")
                
                # Création du log
    
                    'model_id': model.id,
                    'is_sync': True,
    
                    'filename': filename,
                    'log': ("Import du fichier " + filename +
    
                            date_value.strftime(data.suffix) + '.' +
                            data.riga_extension + " : \n" + 
                            " - Création de " + str(crea_nb) + " lignes\n" +
                            " - Mise à jour de " + str(maj_nb) + " lignes\n\n" + log),
                    'is_warning': True if log else False,
    
                    'is_resync': resync,
    
                }
                if not log_id:
                    self.create(vals)
                else:
                    log_id.update(vals)
    
    Benjamin's avatar
    Benjamin a validé
    
    
            except Exception as e:
                _logger.error(e.__str__())
                # Création du log
    
                    'model_id': model.id,
                    'is_sync': False,
                    'is_error': True,
                    'log': str(e)
    
                }
                if not log_id:
                    self.create(vals)
                else:
                    log_id.update(vals)
    
    Benjamin's avatar
    Benjamin a validé
    
        def _cast_type(self, model, field, value, relation=None):
    
    Benjamin's avatar
    Benjamin a validé
            """ Détermine en fonction du champ la valeur typée à renvoyer
            pour l'ORM
    
            :param model : objet modèle de donnée Odoo
            :param field : nom du champ
            :param value : valeur à caster
    
            @return valeur castée
            """
            odoo_field = self.env['ir.model.fields'].search([
                ['model', '=', model.model],
                ['name', '=', field]])
            field_type = odoo_field.ttype
            if value:
                if field_type == 'integer':
                    return int(float(value.replace(',', '.')))
                elif field_type == 'float':
                    return float(value.replace(',', '.'))
    
    Rémi - Le Filament's avatar
    Rémi - Le Filament a validé
                elif field_type == 'date' or field_type == 'datetime':
    
    Benjamin's avatar
    Benjamin a validé
                    date_value = datetime.datetime.strptime(
                        value, '%d/%m/%Y %H:%M:%S')
                    return date_value
    
    Benjamin's avatar
    Benjamin a validé
                elif field_type == 'selection':
    
    Rémi - Le Filament's avatar
    Rémi - Le Filament a validé
                    selection = self.env[model.model]._fields.get(
                        odoo_field.name).selection
    
    Benjamin's avatar
    Benjamin a validé
                    for item in selection:
                        if item[1] == value:
                            return item[0]
                elif field_type == 'many2one':
    
    Benjamin's avatar
    Benjamin a validé
                    m2o_value = self.env[odoo_field.relation].search([
    
    Benjamin's avatar
    Benjamin a validé
                        [relation, '=', value]]).id
    
    Benjamin's avatar
    Benjamin a validé
                    return m2o_value
                else:
                    return value
            else:
                return False
    
        # ------------------------------------------------------
        # Import données
        # ------------------------------------------------------
        def odoo_sync(self):
    
            model_sync = self.env['riga.files.matching'].sudo().search([
                ['is_active', '=', True]])
            self.sync_file(model_sync)
    
        def sync_file(self, model_sync):
    
            """
                Fonction de synchronisation des fichiers CSV RIGA
                avec les tables associées Odoo.
    
    Benjamin's avatar
    Benjamin a validé
    
    
                La fonction recherche l'ensembles des synchronisations actives
                dans la table riga.files.matching, elle transforme la table
                de correspondance de string à list puis appelle la fonction
                _read_file qui permet d'intégrer ou de metre à jour les valeurs
    
    Benjamin's avatar
    Benjamin a validé
            """
            for model in model_sync:
                table = []
    
                # Calcul nom du fichier
                if model.params_active:
                    date_value = fields.Datetime.now() + datetime.timedelta(days=model.day)
                    filename = (model.riga_filename +
                                date_value.strftime(model.suffix) + '.' +
                                model.riga_extension)
                else:
                    filename = (model.riga_filename + '.' +
                                model.riga_extension)
    
                _logger.info(
                    "Lecture Fichier %s pour le modèle %s",
    
                    filename,
    
                    model.model_id.name
                )
    
    Benjamin's avatar
    Benjamin a validé
                for header in model.matching_table_ids:
    
    Benjamin's avatar
    Benjamin a validé
                    table.append([header.name, header.riga_name, header.relation])
    
    Benjamin's avatar
    Benjamin a validé
                self._read_file(
                    model=model.model_id,
    
    Rémi - Le Filament's avatar
    Rémi - Le Filament a validé
                    file=self._get_ftp_file(filename, model.ftp_channel_id),
    
    Benjamin's avatar
    Benjamin a validé
                    table=table,
                    primary_key=model.primary_key,
    
                    header_key=model.header_key,
                    filename=filename)
    
    
        def resync_file(self):
    
            """
                Fonction appelée pour la resynchronisation d'un log
                en erreur
            """
    
            model = self.env['riga.files.matching'].sudo().search([
                    ['model_id', '=', self.model_id.id]])
            table = []
            for header in model.matching_table_ids:
                table.append([header.name, header.riga_name, header.relation])
            self._read_file(
                model=self.model_id,
                file=self._get_ftp_file(self.filename, model.ftp_channel_id),
                table=table,
                primary_key=model.primary_key,
                header_key=model.header_key,
    
                resync=True,
    
                log_id=self,
                filename=self.filename)