From d43fcbe39a59d12d0d4ea6cc84e4fdcffdc63c3d Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Fri, 29 Jun 2018 13:59:01 +0200 Subject: [PATCH 1/5] [ADD] Camt054 Import module from Marco (not ready for l10n_ch) --- l10n_ch_import_camt054/README.rst | 26 +++ l10n_ch_import_camt054/__init__.py | 2 + l10n_ch_import_camt054/__manifest__.py | 23 +++ l10n_ch_import_camt054/models/__init__.py | 6 + .../models/account_bank_statement.py | 15 ++ .../models/account_bank_statement_line.py | 68 +++++++ .../models/account_move_line.py | 7 + .../models/custom_import_stmt.py | 53 +++++ .../models/custom_parser.py | 189 ++++++++++++++++++ .../models/fds_postfinance_file_camt.py | 51 +++++ .../res/camt.053.demo-1000.xml | 90 +++++++++ .../res/camt.054.demo-1000.xml | 141 +++++++++++++ l10n_ch_import_camt054/res/test_data.yml | 81 ++++++++ l10n_ch_import_camt054/tests/__init__.py | 1 + .../tests/test_import_camt.py | 135 +++++++++++++ .../account_bank_statement_line_test.xml | 14 ++ 16 files changed, 902 insertions(+) create mode 100644 l10n_ch_import_camt054/README.rst create mode 100644 l10n_ch_import_camt054/__init__.py create mode 100644 l10n_ch_import_camt054/__manifest__.py create mode 100644 l10n_ch_import_camt054/models/__init__.py create mode 100644 l10n_ch_import_camt054/models/account_bank_statement.py create mode 100644 l10n_ch_import_camt054/models/account_bank_statement_line.py create mode 100644 l10n_ch_import_camt054/models/account_move_line.py create mode 100644 l10n_ch_import_camt054/models/custom_import_stmt.py create mode 100644 l10n_ch_import_camt054/models/custom_parser.py create mode 100644 l10n_ch_import_camt054/models/fds_postfinance_file_camt.py create mode 100644 l10n_ch_import_camt054/res/camt.053.demo-1000.xml create mode 100644 l10n_ch_import_camt054/res/camt.054.demo-1000.xml create mode 100644 l10n_ch_import_camt054/res/test_data.yml create mode 100644 l10n_ch_import_camt054/tests/__init__.py create mode 100644 l10n_ch_import_camt054/tests/test_import_camt.py create mode 100644 l10n_ch_import_camt054/views/account_bank_statement_line_test.xml diff --git a/l10n_ch_import_camt054/README.rst b/l10n_ch_import_camt054/README.rst new file mode 100644 index 000000000..271c5de36 --- /dev/null +++ b/l10n_ch_import_camt054/README.rst @@ -0,0 +1,26 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Switzerland camt 054 and transfert account reconcile +==================================================== + +This module allow you to import camt 054 and reconcile all lines in the transfert account. + +** Features list :** + * import camt 054 + * refuse the import of a camt 054 file when the NtryRef field is different from the original camt 054 + * Add children in school to employee + * New function to reconcile automatically all the lines from the transfert account + +** Remarks :** +To use the reconcilion function you need to make a cron. You can do it in the menu : ```Settings->Automation->Scheduled Actions``` +and create a new action. The object needed to reach the new function is : ```account.bank.statement.line``` and the function name is : ```camt054_reconcile```. +The unique parameter is the transfert account number, for example : ```("1099",)``` + +Known issues / Roadmap +====================== + +Contributors +------------ + +* Marco Monzione diff --git a/l10n_ch_import_camt054/__init__.py b/l10n_ch_import_camt054/__init__.py new file mode 100644 index 000000000..a9e337226 --- /dev/null +++ b/l10n_ch_import_camt054/__init__.py @@ -0,0 +1,2 @@ + +from . import models diff --git a/l10n_ch_import_camt054/__manifest__.py b/l10n_ch_import_camt054/__manifest__.py new file mode 100644 index 000000000..e88c2ddd1 --- /dev/null +++ b/l10n_ch_import_camt054/__manifest__.py @@ -0,0 +1,23 @@ +{ + 'name': 'CAMT 054 import and reconcile', + 'version': '10.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Monzione Marco', + 'website': '...', + 'category': 'Banking addons', + 'depends': [ + 'account_bank_statement_import_camt_details', + 'l10n_ch_fds_postfinance', + 'account_payment_line_cancel', + ], + 'data': [ + 'views/account_bank_statement_line_test.xml', + ], + 'demo': [ + + ], + 'test': [ + 'res/test_data.yml', + ], + 'installable': True, +} diff --git a/l10n_ch_import_camt054/models/__init__.py b/l10n_ch_import_camt054/models/__init__.py new file mode 100644 index 000000000..8c4aeebe3 --- /dev/null +++ b/l10n_ch_import_camt054/models/__init__.py @@ -0,0 +1,6 @@ +from . import custom_parser +from . import account_bank_statement_line +from . import account_move_line +from . import fds_postfinance_file_camt +from . import custom_import_stmt +from . import account_bank_statement diff --git a/l10n_ch_import_camt054/models/account_bank_statement.py b/l10n_ch_import_camt054/models/account_bank_statement.py new file mode 100644 index 000000000..509c579e1 --- /dev/null +++ b/l10n_ch_import_camt054/models/account_bank_statement.py @@ -0,0 +1,15 @@ + +from odoo import models + + +class AccountBankStatement(models.Model): + _inherit = 'account.bank.statement' + + # Todo shoud autoreconcile at the close of a statement + # def button_confirm_bank(self): + # + # account_bank_stmt_line_obj = self.env['account.bank.statement.line'] + # + # super(AccountBankStatement, self).button_confirm_bank() + # + # account_bank_stmt_line_obj.camt054_reconcile(self.journal_id.default_debit_account_id.code) diff --git a/l10n_ch_import_camt054/models/account_bank_statement_line.py b/l10n_ch_import_camt054/models/account_bank_statement_line.py new file mode 100644 index 000000000..b073dc70a --- /dev/null +++ b/l10n_ch_import_camt054/models/account_bank_statement_line.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +"""Add process_camt method to account.bank.statement.import.""" +# © 2017 Compassion CH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models, fields + + +class AccountBankStatementLine(models.Model): + """Add process_camt method to account.bank.statement.import.""" + _inherit = 'account.bank.statement.line' + + acct_svcr_ref = fields.Char() + + def process_reconciliation(self, counterpart_aml_dicts=None, + payment_aml_rec=None, new_aml_dicts=None): + counterpart_moves = super(AccountBankStatementLine, self).process_reconciliation( + counterpart_aml_dicts, payment_aml_rec, new_aml_dicts) + + if hasattr(self, 'acct_svcr_ref') and self.acct_svcr_ref: + for move_line in counterpart_moves.line_ids: + move_line.acct_svcr_ref = self.acct_svcr_ref + + return counterpart_moves + + def _prepare_reconciliation_move_line(self, move, amount): + data = super(AccountBankStatementLine, self).\ + _prepare_reconciliation_move_line(move, amount) + # Add the acct svcr ref to both move line. + data['acct_svcr_ref'] = self.acct_svcr_ref + return data + + def camt054_reconcile(self, account_code): + move_line_obj = self.env['account.move.line'] + + move_line_list = move_line_obj.search([ + ('reconciled', '!=', 'False'), + ('account_id.code', '=', account_code), + ('acct_svcr_ref', '!=', None) + ]) + + list_line = dict() + + # Group each line by acct_svcr_ref + for line in move_line_list: + acct_svcr_ref = line.acct_svcr_ref + + # Add the acct_svcr_ref to the list if it's not already present + if acct_svcr_ref not in list_line: + list_line[acct_svcr_ref] = [] + # If it is already present we add the line the the list of + # this acct_svcr_ref + list_line[acct_svcr_ref].append(line) + + for list_acct_svcr_ref in list_line: + + credit = 0 + debit = 0 + move_line_list = move_line_obj.search([ + ('acct_svcr_ref', '=', list_acct_svcr_ref), + ('reconciled', '!=', 'False'), + ('account_id.code', '=', account_code)]) + + # Check if credit = debit + for line in move_line_list: + credit += line.credit + debit += line.debit + if credit == debit: + move_line_list.reconcile() diff --git a/l10n_ch_import_camt054/models/account_move_line.py b/l10n_ch_import_camt054/models/account_move_line.py new file mode 100644 index 000000000..57245e323 --- /dev/null +++ b/l10n_ch_import_camt054/models/account_move_line.py @@ -0,0 +1,7 @@ +from odoo import models, fields, api + + +class accountMoveLine(models.Model): + _inherit = 'account.move.line' + # The new field would be use for an automatic reconciliation. + acct_svcr_ref = fields.Char() diff --git a/l10n_ch_import_camt054/models/custom_import_stmt.py b/l10n_ch_import_camt054/models/custom_import_stmt.py new file mode 100644 index 000000000..ed81aa2de --- /dev/null +++ b/l10n_ch_import_camt054/models/custom_import_stmt.py @@ -0,0 +1,53 @@ +from odoo import api, models + +import base64 + + +class AccountStatementImportCustomCamt053(models.TransientModel): + _inherit = 'account.bank.statement.import' + + @api.model + def _complete_stmts_vals(self, stmts_vals, journal, account_number): + # When a return transaction is found, it search for the + # opposite transaction (same ref). + + stmts_vals = super(AccountStatementImportCustomCamt053, self).\ + _complete_stmts_vals(stmts_vals, journal, account_number) + + list_transactions = stmts_vals[0]['transactions'] + + for transaction in list_transactions: + if transaction.get('sub_fmly_cd') == 'RRTN'\ + and 'account_id' in transaction \ + and 'ref' in transaction: + + for transactionBis in list_transactions: + if 'ref' in transactionBis \ + and transactionBis['ref'] == transaction['ref']\ + and transactionBis != transaction: + + transactionBis['account_id'] =\ + transaction['account_id'] + + return stmts_vals + + def _create_bank_statements(self, stmts_vals): + statement_ids, notifications =\ + super(AccountStatementImportCustomCamt053, self).\ + _create_bank_statements(stmts_vals) + + if 'data_file' in stmts_vals[0]: + # Add the file imported file to the statement. + if 'file_name' in stmts_vals[0]: + file_name = stmts_vals[0]['file_name'] + else: + file_name = self.filename + + self.env['ir.attachment'].create({ + 'datas_fname': file_name, + 'res_model': 'account.bank.statement', + 'datas': base64.b64encode(stmts_vals[0]['data_file']), + 'name': file_name, + 'res_id': statement_ids[0]}) + + return statement_ids, notifications diff --git a/l10n_ch_import_camt054/models/custom_parser.py b/l10n_ch_import_camt054/models/custom_parser.py new file mode 100644 index 000000000..0b18c28d4 --- /dev/null +++ b/l10n_ch_import_camt054/models/custom_parser.py @@ -0,0 +1,189 @@ +import re + +from odoo import models + + +class CustomParser(models.AbstractModel): + _inherit = 'account.bank.statement.import.camt.parser' + + def parse_entry(self, ns, node): + """Parse an Ntry node and yield transactions""" + bank_payment_line_obj = self.env['bank.payment.line'] + payment_line_obj = self.env['account.payment.line'] + + # Get some basic infos about the transaction in the XML. + transaction = {'name': '/', 'amount': 0} # fallback defaults + self.add_value_from_node( + ns, node, './ns:BkTxCd/ns:Prtry/ns:Cd', transaction, + 'transfer_type' + ) + self.add_value_from_node( + ns, node, './ns:BookgDt/ns:Dt', transaction, 'date') + self.add_value_from_node( + ns, node, './ns:BookgDt/ns:Dt', transaction, 'execution_date') + self.add_value_from_node( + ns, node, './ns:ValDt/ns:Dt', transaction, 'value_date') + amount = self.parse_amount(ns, node) + if amount != 0.0: + transaction['amount'] = amount + self.add_value_from_node( + ns, node, './ns:AddtlNtryInf', transaction, 'name') + self.add_value_from_node( + ns, node, [ + './ns:NtryDtls/ns:RmtInf/ns:Strd/ns:CdtrRefInf/ns:Ref', + './ns:NtryDtls/ns:Btch/ns:PmtInfId', + './ns:NtryDtls/ns:TxDtls/ns:Refs/ns:AcctSvcrRef' + ], + transaction, 'acct_svcr_ref' + ) + + # If there is a 'TxDtls' node in the XML we get the value of + # 'AcctSvcrRef' in it. + details_nodes = node.xpath( + './ns:NtryDtls/ns:TxDtls', namespaces={'ns': ns}) + if len(details_nodes) == 0: + yield transaction + self.add_value_from_node( + ns, node, './ns:AcctSvcrRef', transaction, 'acct_svcr_ref') + return + + self.add_value_from_node( + ns, + node, + './ns:BkTxCd/ns:Domn/ns:Fmly/ns:SubFmlyCd', + transaction, + 'sub_fmly_cd') + + self.add_value_from_node( + ns, + node, + './ns:NtryDtls/ns:TxDtls/ns:Refs/ns:EndToEndId', + transaction, + 'EndToEndId') + + data_supp = dict() + + # In the case of a transaction return we try to find the original + # transaction and link them together, we cancel the transaction + # in the payment order too. + if transaction['sub_fmly_cd'] == 'RRTN': + bank_payment_line = bank_payment_line_obj.search( + [('name', '=', transaction['EndToEndId'])]) + + payment_line = payment_line_obj.search( + [('bank_line_id', '=', bank_payment_line.id)]) + + payment_order = bank_payment_line.order_id + data_supp['add_tl_inf'] = transaction['name'] + + account_payment_mode = payment_order.payment_mode_id + + if account_payment_mode.offsetting_account == 'transfer_account': + transfer_account = account_payment_mode.transfer_account_id + transaction['account_id'] = transfer_account.id + + payment_line.write({ + 'cancel_reason': transaction['name'].encode('utf-8') + }) + payment_line.cancel_line() + + transaction_base = transaction + for node in details_nodes: + transaction = transaction_base.copy() + self.parse_transaction_details(ns, node, transaction) + yield transaction + + def parse_transaction_details(self, ns, node, transaction): + super(CustomParser, self).parse_transaction_details( + ns, node, transaction) + # Check if a global AcctSvcrRef exist + found_node = node.xpath('../../ns:AcctSvcrRef', namespaces={'ns': ns}) + if len(found_node) != 0: + self.add_value_from_node( + ns, node, '../../ns:AcctSvcrRef', transaction, + 'acct_svcr_ref') + else: + self.add_value_from_node(ns, node, './ns:Refs/ns:AcctSvcrRef', + transaction, 'acct_svcr_ref') + + def parse_statement(self, ns, node): + + result = {} + entry_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns}) + + if len(entry_nodes) > 0: + result = super(CustomParser, self).parse_statement(ns, node) + + entry_ref = node.xpath('./ns:Ntry/ns:NtryRef', namespaces={ + 'ns': ns}) + if len(entry_ref) > 1 and '054' in ns: + first_entry = entry_ref[0].text + # Parse all entry ref node to check if they're all the same. + for entry in entry_ref: + if first_entry != entry.text: + raise ValueError('Different entry ref in same file ' + 'not supported !') + self.add_value_from_node( + ns, node, './ns:Ntry/ns:NtryRef', result, 'ntryRef') + result['camt_headers'] = ns + # In case of an empty camt file + else: + result['transactions'] = '' + result['is_empty'] = True + return result + + def parse(self, data): + + result = super(CustomParser, self).parse(data) + currency = result[0] + account_number = result[1] + statements = result[2] + if len(statements) > 0: + if 'camt_headers' in statements[0]: + if 'camt.053' not in statements[0]['camt_headers']: + if 'ntryRef' in statements[0]: + account_number = statements[0]['ntryRef'] + + if hasattr(self, 'data_file'): + statements[0]['data_file'] = self.data_file + else: + statements[0]['data_file'] = data + + if hasattr(self, 'file_name'): + statements[0]['file_name'] = self.file_name + return currency, account_number, statements + + def get_balance_amounts(self, ns, node): + result = super(CustomParser, self).get_balance_amounts(ns, node) + start_balance_node = result[0] + end_balance_node = result[0] + + details_nodes = node.xpath( + './ns:Bal/ns:Amt', namespaces={'ns': ns}) + + if start_balance_node == 0.0 and not len(details_nodes): + start_balance_node = node.xpath('./ns:Ntry', namespaces={'ns': + ns}) + amount_tot = 0 + for node in start_balance_node: + amount_tot -= self.parse_amount(ns, node) + return ( + amount_tot, + end_balance_node + ) + return result + + def check_version(self, ns, root): + try: + super(CustomParser, self).check_version(ns, root) + except ValueError: + re_camt_version = re.compile( + r'(^urn:iso:std:iso:20022:tech:xsd:camt.054.' + r'|^ISO:camt.054.)' + ) + if not re_camt_version.search(ns): + raise ValueError('no camt 052 or 053 or 054: ' + ns) + # Check GrpHdr element: + root_0_0 = root[0][0].tag[len(ns) + 2:] # strip namespace + if root_0_0 != 'GrpHdr': + raise ValueError('expected GrpHdr, got: ' + root_0_0) diff --git a/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py b/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py new file mode 100644 index 000000000..be7c0dc35 --- /dev/null +++ b/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py @@ -0,0 +1,51 @@ +from odoo import models, api + +import base64 +import logging + +_logger = logging.getLogger(__name__) + + +class FdsPostfinanceFileCamt(models.Model): + _inherit = 'fds.postfinance.file' + + @api.multi + def import2bankStatements(self): + + camt_files = self.env[self._name] + account_camt_parser_obj = self.env[ + 'account.bank.statement.import.camt.parser'] + + for pf_file in self: + try: + decoded_file = base64.b64decode(pf_file.data) + + result = account_camt_parser_obj.parse(decoded_file) + + if len(result) > 2 and result[0] is None and result[1] is \ + None: + pf_file.write({ + 'state': 'done', + 'data': pf_file.data, + }) + + _logger.info("[OK] import file '%s' as an empty camt", + pf_file.filename) + camt_files += pf_file + except Exception as e: + self.env.cr.rollback() + self.env.invalidate_all() + if pf_file.state != 'error': + pf_file.write({ + 'state': 'error', + 'error_message': e.message or e.args and e.args[0] + }) + # Here we must commit the error message otherwise it + # can be unset by a next file producing an error + # pylint: disable=invalid-commit + self.env.cr.commit() + _logger.warning("[FAIL] import file '%s' as an empy camt", + (pf_file.filename)) + + return super(FdsPostfinanceFileCamt, self - + camt_files).import2bankStatements() diff --git a/l10n_ch_import_camt054/res/camt.053.demo-1000.xml b/l10n_ch_import_camt054/res/camt.053.demo-1000.xml new file mode 100644 index 000000000..40a50c9f2 --- /dev/null +++ b/l10n_ch_import_camt054/res/camt.053.demo-1000.xml @@ -0,0 +1,90 @@ + + + + + 20171016375204002967365 + 2017-10-16T15:06:00 + + 1 + true + + Productive + + + 20171016375204002967366 + 202 + 2017-10-16T15:06:00 + + 2017-10-16T00:00:00 + 2017-10-16T23:59:59 + + + + CH0110100001101001000 + + + Address test + + + + + + OPBD + + + 0.00 + CRDT +
+
2017-10-13
+ +
+ + + + CLBD + + + 1000.00 + CRDT +
+
2017-10-16
+ +
+ + 1000.00 + CRDT + false + BOOK + +
2017-10-16
+
+ +
2017-10-16
+
+ 99999999999999999999999999999999 + + + PMNT + + ICDT + ESCT + + + + + + + 2017/1029 + 99999999999999999999999999999999 + 38482 + 38482 + + 1000.00 + CRDT + + + Demo Camt053 +
+
+
+
diff --git a/l10n_ch_import_camt054/res/camt.054.demo-1000.xml b/l10n_ch_import_camt054/res/camt.054.demo-1000.xml new file mode 100644 index 000000000..8b24a8e46 --- /dev/null +++ b/l10n_ch_import_camt054/res/camt.054.demo-1000.xml @@ -0,0 +1,141 @@ + + + + + 20171030375204003295156 + 2017-10-31T01:39:34 + + 1 + true + + Productive + + + 20171030375204003295155 + 2017-10-31T01:39:34 + + 2017-10-28T00:00:00 + 2017-10-30T23:59:59 + + + + CH1010101010101010101 + + + Address test + + + + 010101011 + 1000 + CRDT + false + BOOK + +
2017-10-30
+
+ +
2017-10-30
+
+ 99999999999999999999999999999999 + + + PMNT + + IDDT + PMDD + + + + + + 1 + + + + 99999999999999999999999999999999 + + 04 + 01110000011010010000000 + + + 400 + CRDT + + + PMNT + + IDDT + PMDD + + + + + + Jean jacque + + CH + Street 10 + 1010 test + + + + + CH0110111101111000000 + + + + + Demo 1 + + + 2017-10-28T20:00:00 + + + + + 99999999999999999999999999999999 + + 04 + 01110000011010010000000 + + + 600 + CRDT + + + PMNT + + IDDT + PMDD + + + + + + bob bob + + CH + Street 10 + 1010 test + + + + + CH0110111101111000000 + + + + + Demo 2 + + + 2017-10-28T20:00:00 + + + + Demo 2 +
+
+
+
diff --git a/l10n_ch_import_camt054/res/test_data.yml b/l10n_ch_import_camt054/res/test_data.yml new file mode 100644 index 000000000..995378432 --- /dev/null +++ b/l10n_ch_import_camt054/res/test_data.yml @@ -0,0 +1,81 @@ +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# Create bank +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +- + !record {model: res.bank, id: bank_post, view: False}: + name: 'Postfinance AG' + bic: 'POFICHBEXXX' + street: 'Postfinance' + zip: '3030' + city: 'Bern' +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# Create accounts types +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +- + !record {model: account.account.type, id: account_type_post, view: False}: + name: 'Bilan : Liquidites et titres' + type: 'liquidity' +- + !record {model: account.account.type, id: account_type_asset, view: False}: + name: 'Asset' + type: 'other' +- + !record {model: account.account.type, id: account_type_debiteur, view: False}: + name: 'Bilan : Debiteurs' + type: 'receivable' +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# Create account +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +- + !record {model: account.account, id: post_account, view: False}: + code: '1010' + name: 'Postal Account CHF ' + reconcile: True + user_type_id: account_type_post +- + !record {model: account.account, id: post_account_lsv, view: False}: + code: '1098' + name: 'transfert lsv dd' + reconcile: True + user_type_id: account_type_asset +- + !record {model: account.account, id: post_account_receivable, view: False}: + code: '1050' + name: 'Receivables' + reconcile: True + user_type_id: account_type_debiteur +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# Create partner banks +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +- + !record {model: res.partner.bank, id: company_bank_post, view: False}: + acc_type: 'postal' + acc_number: 'CH0110100001101001000' + partner_id: base.main_partner #YourCompany + bank_id: bank_post +- + !record {model: res.partner.bank, id: company_bank_lsv_transfert, view: False}: + acc_type: 'postal' + acc_number: '01-010101-1' + partner_id: base.main_partner #YourCompany + bank_id: bank_post +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# Create journals +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +- + !record {model: account.journal, id: post_account_journal, view: False}: + name: 'Postal account' + type: bank + bank_account_id: company_bank_post + update_posted: True + default_debit_account_id: post_account + default_credit_account_id: post_account +- + !record {model: account.journal, id: lsv_account_journal, view: False}: + name: 'LSV test' + type: bank + bank_account_id: company_bank_lsv_transfert + update_posted: True + default_debit_account_id: post_account_lsv + default_credit_account_id: post_account_lsv + diff --git a/l10n_ch_import_camt054/tests/__init__.py b/l10n_ch_import_camt054/tests/__init__.py new file mode 100644 index 000000000..83f550606 --- /dev/null +++ b/l10n_ch_import_camt054/tests/__init__.py @@ -0,0 +1 @@ +from . import test_import_camt diff --git a/l10n_ch_import_camt054/tests/test_import_camt.py b/l10n_ch_import_camt054/tests/test_import_camt.py new file mode 100644 index 000000000..80c2806e2 --- /dev/null +++ b/l10n_ch_import_camt054/tests/test_import_camt.py @@ -0,0 +1,135 @@ +import base64 + +from odoo.tests import TransactionCase +from odoo.modules import get_module_resource + + +class TestImportCamt(TransactionCase): + + def setUp(self): + super(TestImportCamt, self).setUp() + + account_bank_statement_import_obj = \ + self.env['account.bank.statement.import'] + + account_bank_statement_line_obj =\ + self.env['account.bank.statement.line'] + + account_account_obj = self.env['account.account'] + account_move_line_obj = self.env['account.move.line'] + + test_file_path_camt053 = get_module_resource( + 'l10n_ch_import_camt054', 'res', 'camt.053.demo-1000.xml') + test_file_path_camt054 = get_module_resource( + 'l10n_ch_import_camt054', 'res', 'camt.054.demo-1000.xml') + + # Open the file + file_to_import_053 = open(test_file_path_camt053, 'r') + file_to_import_054 = open(test_file_path_camt054, 'r') + # Get the content of the file and remove the line return + data053 = file_to_import_053.read() + data054 = file_to_import_054.read() + data053 = data053.replace("\n", "") + data054 = data054.replace("\n", "") + # Convert the content in base 64 + data053_64 = base64.b64encode(data053) + data054_64 = base64.b64encode(data054) + + # import the file in the journal + bank_import_053 = account_bank_statement_import_obj.create({ + 'data_file': data053_64, + 'filename': 'camt.053.demo-1000.xml' + }) + bank_import_054 = account_bank_statement_import_obj.create({ + 'data_file': data054_64, + 'filename': 'camt.054.demo-1000.xml' + }) + + bank_import_053.import_file() + bank_import_054.import_file() + + account_1098 = account_account_obj.search([('code', '=', '1098')]) + account_1050 = account_account_obj.search([('code', '=', '1050')]) + + statement_line_camt053 = account_bank_statement_line_obj.search([ + ('name', '=', 'Demo Camt053')]) + + # Reconcile line from the camt 053 + new_aml_dicts = [] + new_aml_dicts.append({"account_id": account_1098.id, + "credit": 1000, + "debit": 0, + "name": statement_line_camt053.name}) + + statement_line_camt053.process_reconciliation([], + account_move_line_obj, + new_aml_dicts) + + # Reconcile line from the camt 054 + statement_line_camt054 = account_bank_statement_line_obj.search( + [('name', '=', 'Demo Camt054')]) + + for statement_line in statement_line_camt054: + new_aml_dicts = [] + new_aml_dicts.append({"account_id": account_1050.id, + "credit": statement_line.amount, + "debit": 0, + "name": statement_line.name}) + statement_line.process_reconciliation([], + account_move_line_obj, + new_aml_dicts) + + # Tests for camt053 + def test_camt053_imported(self): + statement = self.env['account.bank.statement'].search([ + ('reference', '=', 'camt.053.demo-1000.xml')]) + + self.assertTrue(statement) + + def test_statement053_is_open(self): + statement = self.env['account.bank.statement'].search( + [('state', '=', 'open')]) + + self.assertTrue(statement) + + def test_move_line053_exist(self): + move_lines = self.env['account.move.line'].search( + [('name', '=', 'Demo Camt053')]) + + self.assertTrue(move_lines) + + # Tests for camt054 + def test_camt054_imported(self): + statement = self.env['account.bank.statement'].search([ + ('reference', '=', 'camt.054.demo-1000.xml')]) + + self.assertTrue(statement) + + def test_statement054_is_open(self): + statement = self.env['account.bank.statement'].search( + [('state', '=', 'open')]) + + self.assertTrue(statement) + + def test_move_line054_exist(self): + move_lines = self.env['account.move.line'].search( + [('name', '=', 'Demo Camt054')]) + + self.assertTrue(move_lines) + # Test final reconciliation + + def test_move_line_are_reconcilied(self): + account_bank_statement_line_obj =\ + self.env['account.bank.statement.line'] + + account_1098 = self.env['account.account'].search( + [('code', '=', '1098')]) + + account_bank_statement_line_obj.camt054_reconcile('1098') + + move_lines = self.env['account.move.line'].search( + [('acct_svcr_ref', '=', '99999999999999999999999999999999'), + ('account_id', '=', account_1098.id)]) + + for move_line in move_lines: + self.assertTrue(move_line.reconciled) diff --git a/l10n_ch_import_camt054/views/account_bank_statement_line_test.xml b/l10n_ch_import_camt054/views/account_bank_statement_line_test.xml new file mode 100644 index 000000000..6c66d81e3 --- /dev/null +++ b/l10n_ch_import_camt054/views/account_bank_statement_line_test.xml @@ -0,0 +1,14 @@ + + + + bank.statement.line.details.acct_svcr_ref + account.bank.statement.line + form + + + + + + + + From 28c6d0042d63ffbbd46c2db2201a3e45a9629c05 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Thu, 12 Jul 2018 11:12:59 +0200 Subject: [PATCH 2/5] Move field in analytic attribution view --- .../views/analytic_attribution_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_analytic_attribution/views/analytic_attribution_view.xml b/account_analytic_attribution/views/analytic_attribution_view.xml index 1bac9815c..8d6b1f3df 100644 --- a/account_analytic_attribution/views/analytic_attribution_view.xml +++ b/account_analytic_attribution/views/analytic_attribution_view.xml @@ -24,7 +24,6 @@ - @@ -36,6 +35,7 @@ + From 2501d43ca383cdf65fdc4c04b3aa561cb3910511 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Mon, 3 Sep 2018 11:49:02 +0200 Subject: [PATCH 3/5] Remove default start_date of contract (is set at validation) --- recurring_contract/models/recurring_contract.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/recurring_contract/models/recurring_contract.py b/recurring_contract/models/recurring_contract.py index 55865bee1..272c0abd8 100644 --- a/recurring_contract/models/recurring_contract.py +++ b/recurring_contract/models/recurring_contract.py @@ -74,8 +74,7 @@ class RecurringContract(models.Model): default="/", required=True, readonly=True, states={'draft': [('readonly', False)]}, copy=False) start_date = fields.Date( - default=datetime.today().strftime(DF), required=True, readonly=True, - states={'draft': [('readonly', False)]}, + readonly=True, states={'draft': [('readonly', False)]}, copy=False, track_visibility="onchange") end_date = fields.Datetime( readonly=False, states={'terminated': [('readonly', True)]}, From 64dcce8b98890cf582a70d13584b052bd964bdc1 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Tue, 4 Sep 2018 09:21:17 +0200 Subject: [PATCH 4/5] FIX pylint issues --- .travis.yml | 1 + l10n_ch_import_camt054/__manifest__.py | 3 ++- l10n_ch_import_camt054/models/account_bank_statement.py | 2 +- .../models/account_bank_statement_line.py | 5 +++-- l10n_ch_import_camt054/models/account_move_line.py | 7 +++++-- l10n_ch_import_camt054/models/custom_import_stmt.py | 1 + l10n_ch_import_camt054/models/custom_parser.py | 1 + l10n_ch_import_camt054/models/fds_postfinance_file_camt.py | 1 + l10n_ch_import_camt054/tests/test_import_camt.py | 1 + oca_dependencies.txt | 2 ++ recurring_contract/models/contract_group.py | 2 +- requirements.txt | 1 + 12 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 requirements.txt diff --git a/.travis.yml b/.travis.yml index 735e65537..587cf0f5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ virtualenv: install: + - pip install -r requirements.txt - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly diff --git a/l10n_ch_import_camt054/__manifest__.py b/l10n_ch_import_camt054/__manifest__.py index e88c2ddd1..ca34c10e3 100644 --- a/l10n_ch_import_camt054/__manifest__.py +++ b/l10n_ch_import_camt054/__manifest__.py @@ -1,8 +1,9 @@ +# -*- coding: utf-8 -*- { 'name': 'CAMT 054 import and reconcile', 'version': '10.0.1.0.0', 'license': 'AGPL-3', - 'author': 'Monzione Marco', + 'author': 'Monzione Marco, Odoo Community Association (OCA)', 'website': '...', 'category': 'Banking addons', 'depends': [ diff --git a/l10n_ch_import_camt054/models/account_bank_statement.py b/l10n_ch_import_camt054/models/account_bank_statement.py index 509c579e1..9637cce99 100644 --- a/l10n_ch_import_camt054/models/account_bank_statement.py +++ b/l10n_ch_import_camt054/models/account_bank_statement.py @@ -1,4 +1,4 @@ - +# -*- coding: utf-8 -*- from odoo import models diff --git a/l10n_ch_import_camt054/models/account_bank_statement_line.py b/l10n_ch_import_camt054/models/account_bank_statement_line.py index b073dc70a..a523fdee5 100644 --- a/l10n_ch_import_camt054/models/account_bank_statement_line.py +++ b/l10n_ch_import_camt054/models/account_bank_statement_line.py @@ -13,8 +13,9 @@ class AccountBankStatementLine(models.Model): def process_reconciliation(self, counterpart_aml_dicts=None, payment_aml_rec=None, new_aml_dicts=None): - counterpart_moves = super(AccountBankStatementLine, self).process_reconciliation( - counterpart_aml_dicts, payment_aml_rec, new_aml_dicts) + counterpart_moves = super( + AccountBankStatementLine, self).process_reconciliation( + counterpart_aml_dicts, payment_aml_rec, new_aml_dicts) if hasattr(self, 'acct_svcr_ref') and self.acct_svcr_ref: for move_line in counterpart_moves.line_ids: diff --git a/l10n_ch_import_camt054/models/account_move_line.py b/l10n_ch_import_camt054/models/account_move_line.py index 57245e323..e24e8bd65 100644 --- a/l10n_ch_import_camt054/models/account_move_line.py +++ b/l10n_ch_import_camt054/models/account_move_line.py @@ -1,7 +1,10 @@ -from odoo import models, fields, api +# -*- coding: utf-8 -*- +# © 2017 Compassion CH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models, fields -class accountMoveLine(models.Model): +class AccountMoveLine(models.Model): _inherit = 'account.move.line' # The new field would be use for an automatic reconciliation. acct_svcr_ref = fields.Char() diff --git a/l10n_ch_import_camt054/models/custom_import_stmt.py b/l10n_ch_import_camt054/models/custom_import_stmt.py index ed81aa2de..ba036e36a 100644 --- a/l10n_ch_import_camt054/models/custom_import_stmt.py +++ b/l10n_ch_import_camt054/models/custom_import_stmt.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from odoo import api, models import base64 diff --git a/l10n_ch_import_camt054/models/custom_parser.py b/l10n_ch_import_camt054/models/custom_parser.py index 0b18c28d4..d7a06454e 100644 --- a/l10n_ch_import_camt054/models/custom_parser.py +++ b/l10n_ch_import_camt054/models/custom_parser.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re from odoo import models diff --git a/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py b/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py index be7c0dc35..0e17e1a97 100644 --- a/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py +++ b/l10n_ch_import_camt054/models/fds_postfinance_file_camt.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from odoo import models, api import base64 diff --git a/l10n_ch_import_camt054/tests/test_import_camt.py b/l10n_ch_import_camt054/tests/test_import_camt.py index 80c2806e2..a8e1d31bc 100644 --- a/l10n_ch_import_camt054/tests/test_import_camt.py +++ b/l10n_ch_import_camt054/tests/test_import_camt.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import base64 from odoo.tests import TransactionCase diff --git a/oca_dependencies.txt b/oca_dependencies.txt index a7a07caae..c8017a640 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -14,6 +14,8 @@ # To provide both the URL and a branch, use: # sale-workflow https://github.com/OCA/sale-workflow branchname bank-payment +bank-statement-import +l10n-switzerland https://github.com/CompassionCH/l10n-switzerland 10.0-mig-fds queue server-tools account-financial-tools diff --git a/recurring_contract/models/contract_group.py b/recurring_contract/models/contract_group.py index ba5f7d3e1..08a746ced 100644 --- a/recurring_contract/models/contract_group.py +++ b/recurring_contract/models/contract_group.py @@ -199,7 +199,7 @@ def _generate_invoices(self, invoicer=None): for contract_group in self.filtered('next_invoice_date'): # After a ContractGroup is done, we commit all writes in order to # avoid doing it again in case of an error or a timeout - self.env.cr.commit() + self.env.cr.commit() # pylint: disable=invalid-commit logger.info("Generating invoices for group {0}/{1}".format( count, nb_groups)) month_delta = contract_group.advance_billing_months or 1 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..d66dfa17b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pycrypto \ No newline at end of file From 11a53f7791fcd4b295f69fe9492624430260ffaa Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Tue, 4 Sep 2018 16:22:46 +0200 Subject: [PATCH 5/5] FIX camt054 tests --- l10n_ch_import_camt054/__manifest__.py | 5 +---- .../{res => demo}/test_data.yml | 0 .../res/camt.054.demo-1000.xml | 2 +- .../tests/test_import_camt.py | 19 ++++++++++--------- 4 files changed, 12 insertions(+), 14 deletions(-) rename l10n_ch_import_camt054/{res => demo}/test_data.yml (100%) diff --git a/l10n_ch_import_camt054/__manifest__.py b/l10n_ch_import_camt054/__manifest__.py index ca34c10e3..81d7a16d7 100644 --- a/l10n_ch_import_camt054/__manifest__.py +++ b/l10n_ch_import_camt054/__manifest__.py @@ -15,10 +15,7 @@ 'views/account_bank_statement_line_test.xml', ], 'demo': [ - - ], - 'test': [ - 'res/test_data.yml', + 'demo/test_data.yml', ], 'installable': True, } diff --git a/l10n_ch_import_camt054/res/test_data.yml b/l10n_ch_import_camt054/demo/test_data.yml similarity index 100% rename from l10n_ch_import_camt054/res/test_data.yml rename to l10n_ch_import_camt054/demo/test_data.yml diff --git a/l10n_ch_import_camt054/res/camt.054.demo-1000.xml b/l10n_ch_import_camt054/res/camt.054.demo-1000.xml index 8b24a8e46..3f063add5 100644 --- a/l10n_ch_import_camt054/res/camt.054.demo-1000.xml +++ b/l10n_ch_import_camt054/res/camt.054.demo-1000.xml @@ -134,7 +134,7 @@ - Demo 2 + Demo Camt054 diff --git a/l10n_ch_import_camt054/tests/test_import_camt.py b/l10n_ch_import_camt054/tests/test_import_camt.py index a8e1d31bc..d42db5fae 100644 --- a/l10n_ch_import_camt054/tests/test_import_camt.py +++ b/l10n_ch_import_camt054/tests/test_import_camt.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- import base64 -from odoo.tests import TransactionCase +from odoo.tests import SingleTransactionCase from odoo.modules import get_module_resource -class TestImportCamt(TransactionCase): +class TestImportCamt(SingleTransactionCase): - def setUp(self): - super(TestImportCamt, self).setUp() + @classmethod + def setUpClass(cls): + super(TestImportCamt, cls).setUpClass() account_bank_statement_import_obj = \ - self.env['account.bank.statement.import'] + cls.env['account.bank.statement.import'] - account_bank_statement_line_obj =\ - self.env['account.bank.statement.line'] + account_bank_statement_line_obj = \ + cls.env['account.bank.statement.line'] - account_account_obj = self.env['account.account'] - account_move_line_obj = self.env['account.move.line'] + account_account_obj = cls.env['account.account'] + account_move_line_obj = cls.env['account.move.line'] test_file_path_camt053 = get_module_resource( 'l10n_ch_import_camt054', 'res', 'camt.053.demo-1000.xml')