diff --git a/__init__.py b/__init__.py index 408a6001bdace28f0811aa10474dc3ece2af2e1e..b046ff82fc33987dc39e441795c2abe2b2aae54e 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- -from . import models -from . import wizard +from . import models, wizard diff --git a/__manifest__.py b/__manifest__.py index b68b35a09072bb0a50e9643de56a73b6a2d1a3a8..8ec0bf477a12eaa376fa957abf9f38029917de2c 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,23 +1,15 @@ -# -*- coding: utf-8 -*- { - 'name': 'Import BPCE Bank Statement', - 'category': 'Banking addons', - 'version': '14.0.1.0.0', - 'license': 'AGPL-3', - 'author': 'Odoo SA,' - 'Akretion,' - 'La Louve,' - 'GRAP,' - 'Nicolas JEUDY,' - 'Aurélien DUMAINE,' - 'Odoo Community Association (OCA),' - 'Le Filament', - 'website': 'https://odoo-community.org/', - 'depends': [ - 'account_statement_import', + "name": "Import BPCE Bank Statement", + "category": "Banking addons", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "Odoo SA, Akretion, La Louve, GRAP, Nicolas JEUDY, Aurélien DUMAINE, Odoo Community Association (OCA), Le Filament", + "website": "https://odoo-community.org/", + "depends": [ + "account_statement_import", ], - 'data': [ - 'views/view_account_bank_statement_import.xml', + "data": [ + "views/view_account_bank_statement_import.xml", ], - 'installable': True, + "installable": True, } diff --git a/models/account_journal.py b/models/account_journal.py index 8c53b20d0c72f3765dc0331f064fa7d35fff3fa3..1d92d9476923283d05601caeb42e7d32bfdd1442 100644 --- a/models/account_journal.py +++ b/models/account_journal.py @@ -7,5 +7,5 @@ class AccountJournal(models.Model): def _get_bank_statements_available_import_formats(self): """Adds ofx to supported import formats.""" rslt = super()._get_bank_statements_available_import_formats() - rslt.append('bpce') + rslt.append("bpce") return rslt diff --git a/views/view_account_bank_statement_import.xml b/views/view_account_bank_statement_import.xml index 656c914fd6b354da9c6450e01a7ceb7220a34ce2..53927fac0ba609b4f8239eb472922e0824e4afed 100644 --- a/views/view_account_bank_statement_import.xml +++ b/views/view_account_bank_statement_import.xml @@ -1,8 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <record id="view_account_bank_statement_import_form" model="ir.ui.view"> <field name="model">account.statement.import</field> - <field name="inherit_id" ref="account_statement_import.account_statement_import_form"/> + <field + name="inherit_id" + ref="account_statement_import.account_statement_import_form" + /> <field name="arch" type="xml"> <xpath expr="//ul[@id='statement_format']" position="inside"> <li>BPCE CSV File (.CSV)</li> diff --git a/wizard/__init__.py b/wizard/__init__.py index 5dfe830fbec8d2cc9e0a1b6cc0d2d0dc514341d4..7dafcd167ca8bbf8ed0a70a417d6f74a991824cf 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- from . import account_bank_statement_import diff --git a/wizard/account_bank_statement_import.py b/wizard/account_bank_statement_import.py index 04faeab005310460fe8ba4c720e8598f75f06126..d48eff7848bf5be116546456af695d57fb056e09 100644 --- a/wizard/account_bank_statement_import.py +++ b/wizard/account_bank_statement_import.py @@ -1,55 +1,54 @@ -# -*- coding: utf-8 -*- - +import datetime import logging import re -import datetime -from odoo import api, models, _ +from odoo import _, api, models from odoo.exceptions import Warning _logger = logging.getLogger(__name__) class AccountBankStatementImport(models.TransientModel): - _inherit = 'account.statement.import' + _inherit = "account.statement.import" regexp_version = { - 'version_A' : { - 'line_1' : u"^Code de la banque : (?P<bank_group_code>\d{5});Code de l'agence : (?P<bank_local_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});;$", - 'line_2' : u"^Numéro de compte : (?P<bank_account_number>\d{11});Nom du compte : (?P<bank_account_name>.*);Devise : (?P<currency>.{3});;;$", - 'line_closing_balance' : u"^Solde en fin de période;;;;(?P<balance>\d+(,\d{1,2})?);$", - 'line_opening_balance' : u"^Solde en début de période;;;;(?P<balance>\d+(,\d{1,2})?);$", - 'line_credit' : u"^(?P<date>\d{2}/\d{2}/\d{4});(?P<unique_import_id>.*);(?P<name>.*);;(?P<credit>\d+(,\d{1,2})?);(?P<note>.*)$", - 'line_debit' : u"^(?P<date>\d{2}/\d{2}/\d{4});(?P<unique_import_id>.*);(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*)$", - 'line_date_format' : '%d/%m/%Y', + "version_A": { + "line_1": r"^Code de la banque : (?P<bank_group_code>\d{5});Code de l'agence : (?P<bank_local_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});;$", + "line_2": r"^Numéro de compte : (?P<bank_account_number>\d{11});Nom du compte : (?P<bank_account_name>.*);Devise : (?P<currency>.{3});;;$", + "line_closing_balance": r"^Solde en fin de période;;;;(?P<balance>\d+(,\d{1,2})?);$", + "line_opening_balance": r"^Solde en début de période;;;;(?P<balance>\d+(,\d{1,2})?);$", + "line_credit": r"^(?P<date>\d{2}/\d{2}/\d{4});(?P<unique_import_id>.*);(?P<name>.*);;(?P<credit>\d+(,\d{1,2})?);(?P<note>.*)$", + "line_debit": r"^(?P<date>\d{2}/\d{2}/\d{4});(?P<unique_import_id>.*);(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*)$", + "line_date_format": "%d/%m/%Y", + }, + "version_B": { + "line_1": r"^Code de la banque : (?P<bank_group_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});;$", + "line_2": r"^Numéro de compte : (?P<bank_account_number>\d{11});Devise : (?P<currency>.{3});;;$", + "line_closing_balance": r"^Solde en fin de période;;;(?P<balance>\d+(,\d{1,2})?);$", + "line_opening_balance": r"^Solde en début de période;;;(?P<balance>\d+(,\d{1,2})?);$", + "line_credit": r"^(?P<date>\d{2}/\d{2}/\d{4});(?P<name>.*);;(?P<credit>\d+(,\d{1,2})?);(?P<note>.*)$", + "line_debit": r"^(?P<date>\d{2}/\d{2}/\d{4});(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*)$", + "line_date_format": "%d/%m/%Y", + }, + "version_C": { + "line_1": r"^Code de la banque : (?P<bank_group_code>\d{5});Code de l'agence : (?P<bank_local_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});$", + "line_2": r"^Numéro de compte : (?P<bank_account_number>\d{11});Nom du compte : (?P<nom_compte>.*);Devise : (?P<currency>.{3});$", + "line_closing_balance": r"^Solde en fin de période;;;;(?P<balance>(\+|-)?\d+(,\d{1,2})?)$", + "line_opening_balance": r"^Solde en début de période;;;;(?P<balance>(\+|-)?\d+(,\d{1,2})?)$", + "line_credit": r"^(?P<date>\d{2}/\d{2}/\d{2});(?P<ref>.*);(?P<name>.*);;\+(?P<credit>\d+(,\d{1,2})?);(?P<note>.*);$", + "line_debit": r"^(?P<date>\d{2}/\d{2}/\d{2});(?P<ref>.*);(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*);$", + "line_date_format": "%d/%m/%y", }, - 'version_B' : { - 'line_1' : u"^Code de la banque : (?P<bank_group_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});;$", - 'line_2' : u"^Numéro de compte : (?P<bank_account_number>\d{11});Devise : (?P<currency>.{3});;;$", - 'line_closing_balance' : u"^Solde en fin de période;;;(?P<balance>\d+(,\d{1,2})?);$", - 'line_opening_balance' : u"^Solde en début de période;;;(?P<balance>\d+(,\d{1,2})?);$", - 'line_credit' : u"^(?P<date>\d{2}/\d{2}/\d{4});(?P<name>.*);;(?P<credit>\d+(,\d{1,2})?);(?P<note>.*)$", - 'line_debit' : u"^(?P<date>\d{2}/\d{2}/\d{4});(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*)$", - 'line_date_format' : '%d/%m/%Y', - }, - 'version_C' : { - 'line_1' : u"^Code de la banque : (?P<bank_group_code>\d{5});Code de l'agence : (?P<bank_local_code>\d{5});Date de début de téléchargement : (?P<opening_date>\d{2}/\d{2}/\d{4});Date de fin de téléchargement : (?P<closing_date>\d{2}/\d{2}/\d{4});$", - 'line_2' : u"^Numéro de compte : (?P<bank_account_number>\d{11});Nom du compte : (?P<nom_compte>.*);Devise : (?P<currency>.{3});$", - 'line_closing_balance' : u"^Solde en fin de période;;;;(?P<balance>(\+|-)?\d+(,\d{1,2})?)$", - 'line_opening_balance' : u"^Solde en début de période;;;;(?P<balance>(\+|-)?\d+(,\d{1,2})?)$", - 'line_credit' : u"^(?P<date>\d{2}/\d{2}/\d{2});(?P<ref>.*);(?P<name>.*);;\+(?P<credit>\d+(,\d{1,2})?);(?P<note>.*);$", - 'line_debit' : u"^(?P<date>\d{2}/\d{2}/\d{2});(?P<ref>.*);(?P<name>.*);(?P<debit>-\d+(,\d{1,2})?);;(?P<note>.*);$", - 'line_date_format' : '%d/%m/%y', - } } @api.model def _find_bank_account_id(self, account_number): - """ Get res.partner.bank ID """ + """Get res.partner.bank ID""" bank_account_id = None if account_number and len(account_number) > 4: - bank_account_ids = self.env['res.partner.bank'].search( - [('acc_number', '=', account_number)], limit=1) + bank_account_ids = self.env["res.partner.bank"].search( + [("acc_number", "=", account_number)], limit=1 + ) if bank_account_ids: bank_account_id = bank_account_ids[0].id return bank_account_id @@ -60,83 +59,139 @@ class AccountBankStatementImport(models.TransientModel): file_version = "version_A" # for files generated before june 2017 test_versionA = re.compile( - self.regexp_version[file_version]['line_1']).search(data_file[0]) - if (test_versionA == None): + self.regexp_version[file_version]["line_1"] + ).search(data_file[0]) + if test_versionA is None: # for files generated after june 2017 and before decembre 2017 file_version = "version_B" test_versionB = re.compile( - self.regexp_version[file_version]['line_1']).search(data_file[0]) - if (test_versionB == None): + self.regexp_version[file_version]["line_1"] + ).search(data_file[0]) + if test_versionB is None: # for files generated after december 2017 file_version = "version_C" parse_line_1 = re.compile( - self.regexp_version[file_version]['line_1']).search(data_file[0]) - bank_group_code = parse_line_1.group('bank_group_code') - opening_date = parse_line_1.group('opening_date') - closing_date = parse_line_1.group('closing_date') + self.regexp_version[file_version]["line_1"] + ).search(data_file[0]) + bank_group_code = parse_line_1.group("bank_group_code") + opening_date = parse_line_1.group("opening_date") + closing_date = parse_line_1.group("closing_date") parse_line_2 = re.compile( - self.regexp_version[file_version]['line_2']).search(data_file[1]) - bank_account_number = parse_line_2.group('bank_account_number') - currency = parse_line_2.group('currency') - - closing_balance = float(re.compile(self.regexp_version[file_version]['line_closing_balance']).search(data_file[3]).group('balance').replace(',','.')) - opening_balance = float(re.compile(self.regexp_version[file_version]['line_opening_balance']).search(data_file[len(data_file)-1]).group('balance').replace(',','.')) + self.regexp_version[file_version]["line_2"] + ).search(data_file[1]) + bank_account_number = parse_line_2.group("bank_account_number") + currency = parse_line_2.group("currency") + + closing_balance = float( + re.compile(self.regexp_version[file_version]["line_closing_balance"]) + .search(data_file[3]) + .group("balance") + .replace(",", ".") + ) + opening_balance = float( + re.compile(self.regexp_version[file_version]["line_opening_balance"]) + .search(data_file[len(data_file) - 1]) + .group("balance") + .replace(",", ".") + ) except Exception as e: _logger.debug(e) return False - return (file_version, bank_group_code, opening_date, closing_date, - bank_account_number, opening_balance, closing_balance, - currency) + return ( + file_version, + bank_group_code, + opening_date, + closing_date, + bank_account_number, + opening_balance, + closing_balance, + currency, + ) @api.model def _parse_file(self, data_file): - data_file_split = data_file.decode('iso-8859-1').splitlines() + data_file_split = data_file.decode("iso-8859-1").splitlines() result = self._check_file(data_file_split) if not result: return super()._parse_file(data_file) - file_version, bank_group_code, opening_date, closing_date, bank_account_number, opening_balance, closing_balance, currency = result + ( + file_version, + bank_group_code, + opening_date, + closing_date, + bank_account_number, + opening_balance, + closing_balance, + currency, + ) = result transactions = [] total_amt = 0.00 try: index = 0 - for line in data_file_split[5:len(data_file_split)-1]: - transaction = re.compile(self.regexp_version[file_version]['line_debit']).search(line) - if (transaction != None): - transaction_amount = float(transaction.group('debit').replace(',','.')) - else : - transaction = re.compile(self.regexp_version[file_version]['line_credit']).search(line) - transaction_amount = float(transaction.group('credit').replace(',','.')) - - libelle = transaction.group('name') - if transaction.group('note') != "": - libelle += " */* "+transaction.group('note') + for line in data_file_split[5 : len(data_file_split) - 1]: + transaction = re.compile( + self.regexp_version[file_version]["line_debit"] + ).search(line) + if transaction is not None: + transaction_amount = float( + transaction.group("debit").replace(",", ".") + ) + else: + transaction = re.compile( + self.regexp_version[file_version]["line_credit"] + ).search(line) + transaction_amount = float( + transaction.group("credit").replace(",", ".") + ) + + libelle = transaction.group("name") + if transaction.group("note") != "": + libelle += " */* " + transaction.group("note") vals_line = { - 'date': datetime.datetime.strptime(transaction.group('date'), self.regexp_version[file_version]['line_date_format']).strftime('%Y-%m-%d'), - 'payment_ref': libelle, - #'ref': transaction.group('unique_import_id'), - 'amount': transaction_amount, - #'note': transaction.group('note'), - 'unique_import_id': str(index)+transaction.group('date')+transaction.group('name')+str(transaction_amount)+transaction.group('note'), - 'account_number': bank_account_number, + "date": datetime.datetime.strptime( + transaction.group("date"), + self.regexp_version[file_version]["line_date_format"], + ).strftime("%Y-%m-%d"), + "payment_ref": libelle, + # 'ref': transaction.group('unique_import_id'), + "amount": transaction_amount, + # 'note': transaction.group('note'), + "unique_import_id": str(index) + + transaction.group("date") + + transaction.group("name") + + str(transaction_amount) + + transaction.group("note"), + "account_number": bank_account_number, } total_amt += transaction_amount transactions.append(vals_line) index = index + 1 - if (abs(opening_balance+total_amt-closing_balance) > 0.00001): - raise ValueError(_("Sum of opening balance and transaction lines is not equel to closing balance.")) + if abs(opening_balance + total_amt - closing_balance) > 0.00001: + raise ValueError( + _( + "Sum of opening balance and transaction lines is not equel to closing balance." + ) + ) except Exception as e: - raise Warning(_("The following problem occurred during import. The file might not be valid.\n\n %s" % e.message)) + raise Warning( + _( + "The following problem occurred during import. The file might not be valid.\n\n %s" + % str(e) + ) + ) vals_bank_statement = { - 'name': bank_account_number+"/"+opening_date, - 'date': datetime.datetime.strptime(opening_date, '%d/%m/%Y').strftime('%Y-%m-%d'), - 'transactions': list(reversed(transactions)), - 'balance_start': opening_balance, - 'balance_end_real': closing_balance, + "name": bank_account_number + "/" + opening_date, + "date": datetime.datetime.strptime(opening_date, "%d/%m/%Y").strftime( + "%Y-%m-%d" + ), + "transactions": list(reversed(transactions)), + "balance_start": opening_balance, + "balance_end_real": closing_balance, } return currency, bank_account_number, [vals_bank_statement]