diff --git a/account_move_transactionid_import/README.rst b/account_move_transactionid_import/README.rst new file mode 100644 index 00000000..2da1e2c1 --- /dev/null +++ b/account_move_transactionid_import/README.rst @@ -0,0 +1,54 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +==================================== +Bank statement transaction ID import +==================================== + +This module extends the functionality of +account_statement_base_import, in order to add both importation +and auto-completion for the "transaction_ref" field added in +base_transaction_id. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/98/9.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of +trouble, please check there if your issue has already been reported. If you +spotted it first, help us smashing it by providing a detailed and welcomed +feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Joël Grand-Guillaume +* Matthieu Dietrich + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_move_transactionid_import/__init__.py b/account_move_transactionid_import/__init__.py new file mode 100644 index 00000000..728557cc --- /dev/null +++ b/account_move_transactionid_import/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from . import parser +from . import models diff --git a/account_move_transactionid_import/__openerp__.py b/account_move_transactionid_import/__openerp__.py new file mode 100644 index 00000000..869581e8 --- /dev/null +++ b/account_move_transactionid_import/__openerp__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +{ + 'name': "Bank statement transactionID import", + 'version': '9.0.1.0.0', + 'author': "Camptocamp,Odoo Community Association (OCA)", + 'maintainer': 'Camptocamp', + 'category': 'Finance', + 'complexity': 'normal', + 'depends': [ + 'account_move_base_import', + 'base_transaction_id' + ], + 'data': [ + 'data/completion_rule_data.xml' + ], + 'test': [ + 'test/sale.yml', + 'test/completion_transactionid_test.yml', + 'test/invoice.yml', + 'test/completion_invoice_transactionid_test.yml', + ], + 'website': 'http://www.camptocamp.com', + 'installable': True, + 'auto_install': False, + 'license': 'AGPL-3', +} diff --git a/account_move_transactionid_import/data/completion_rule_data.xml b/account_move_transactionid_import/data/completion_rule_data.xml new file mode 100644 index 00000000..1f993602 --- /dev/null +++ b/account_move_transactionid_import/data/completion_rule_data.xml @@ -0,0 +1,16 @@ + + + + + Match from Sales Order using transaction ref + 30 + get_from_transaction_ref_and_so + + + + Match from Invoice using transaction ref + 40 + get_from_transaction_ref_and_invoice + + + diff --git a/account_move_transactionid_import/data/statement.csv b/account_move_transactionid_import/data/statement.csv new file mode 100644 index 00000000..ecda301b --- /dev/null +++ b/account_move_transactionid_import/data/statement.csv @@ -0,0 +1,4 @@ +"transaction_ref";"date";"amount";"commission_amount";"label" +50969286;2011-03-07 13:45:14;118.4;-11.84;"label a" +51065326;2011-03-05 13:45:14;189;-15.12;"label b" +51179306;2011-03-02 17:45:14;189;-15.12;"label c" diff --git a/account_move_transactionid_import/data/statement.xls b/account_move_transactionid_import/data/statement.xls new file mode 100644 index 00000000..2d1ac865 Binary files /dev/null and b/account_move_transactionid_import/data/statement.xls differ diff --git a/account_move_transactionid_import/i18n/account_statement_transactionid_import.pot b/account_move_transactionid_import/i18n/account_statement_transactionid_import.pot new file mode 100644 index 00000000..6141807a --- /dev/null +++ b/account_move_transactionid_import/i18n/account_statement_transactionid_import.pot @@ -0,0 +1,27 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_statement_transactionid_import +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-01-21 12:02+0000\n" +"PO-Revision-Date: 2014-01-21 12:02+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_statement_transactionid_import +#: code:addons/account_statement_transactionid_import/account_move.py:65 +#, python-format +msgid "Line named \"%s\" was matched by more than one partner." +msgstr "" + +#. module: account_statement_transactionid_import +#: model:ir.model,name:account_statement_transactionid_import.model_account_move_completion_rule +msgid "account.move.completion.rule" +msgstr "" diff --git a/account_move_transactionid_import/i18n/es.po b/account_move_transactionid_import/i18n/es.po new file mode 100644 index 00000000..bdf10323 --- /dev/null +++ b/account_move_transactionid_import/i18n/es.po @@ -0,0 +1,29 @@ +# Spanish translation for banking-addons +# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 +# This file is distributed under the same license as the banking-addons package. +# FIRST AUTHOR , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: banking-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2014-01-21 12:02+0000\n" +"PO-Revision-Date: 2014-06-05 22:41+0000\n" +"Last-Translator: Pedro Manuel Baeza \n" +"Language-Team: Spanish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n" +"X-Generator: Launchpad (build 17031)\n" + +#. module: account_statement_transactionid +#: code:addons/account_statement_transactionid/account_move.py:65 +#, python-format +msgid "Line named \"%s\" (Ref:%s) was matched by more than one partner." +msgstr "La línea llamada \"%s\" (Ref: %s) se casó con más de una empresa." + +#. module: account_statement_transactionid +#: model:ir.model,name:account_statement_transactionid.model_account_move_completion_rule +msgid "account.move.completion.rule" +msgstr "account.move.completion.rule" diff --git a/account_move_transactionid_import/models/__init__.py b/account_move_transactionid_import/models/__init__.py new file mode 100644 index 00000000..079b873d --- /dev/null +++ b/account_move_transactionid_import/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from . import account_journal +from . import account_move diff --git a/account_move_transactionid_import/models/account_journal.py b/account_move_transactionid_import/models/account_journal.py new file mode 100644 index 00000000..f8dda41b --- /dev/null +++ b/account_move_transactionid_import/models/account_journal.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from openerp import models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + def _get_import_type_selection(self): + """Has to be inherited to add parser""" + res = super(AccountJournal, self)._get_import_type_selection() + res.append(('generic_csvxls_transaction', + 'Generic .csv/.xls based on SO transaction ID')) + return res diff --git a/account_move_transactionid_import/models/account_move.py b/account_move_transactionid_import/models/account_move.py new file mode 100644 index 00000000..e2a31bb9 --- /dev/null +++ b/account_move_transactionid_import/models/account_move.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from openerp import _, models +from openerp.addons.account_move_base_import.models.account_move import \ + ErrorTooManyPartner + + +class AccountMoveCompletionRule(models.Model): + """Add a rule based on transaction ID""" + + _inherit = "account.move.completion.rule" + + def _get_functions(self): + res = super(AccountMoveCompletionRule, self)._get_functions() + res += [ + ('get_from_transaction_ref_and_so', + 'Match Sales Order using transaction ref'), + ('get_from_transaction_ref_and_invoice', + 'Match Invoice using transaction ref'), + ] + return res + + def get_from_transaction_ref_and_so(self, line): + """ + Match the partner based on the transaction ID field of the SO. + Then, call the generic st_line method to complete other values. + In that case, we always fullfill the reference of the line with the SO + name. + :param dict st_line: read of the concerned account.bank.statement.line + :return: + A dict of value that can be passed directly to the write method of + the statement line or {} + {'partner_id': value, + 'account_id' : value, + ...} + """ + res = {} + so_obj = self.env['sale.order'] + sales = so_obj.search([('transaction_id', '=', line.transaction_ref)]) + if len(sales) > 1: + raise ErrorTooManyPartner( + _('Line named "%s" was matched by more than ' + 'one partner.') % line.name) + if len(sales) == 1: + sale = sales[0] + res['partner_id'] = sale.partner_id.id + return res + + def get_from_transaction_ref_and_invoice(self, line): + """Match the partner based on the transaction ID field of the invoice. + Then, call the generic st_line method to complete other values. + + In that case, we always fullfill the reference of the line with the + invoice name. + + :param dict st_line: read of the concerned account.bank.statement.line + :return: + A dict of value that can be passed directly to the write method of + the statement line or {} + {'partner_id': value, + 'account_id' : value, + ...} + """ + res = {} + invoice_obj = self.env['account.invoice'] + invoices = invoice_obj.search( + [('transaction_id', '=', line.transaction_ref)]) + if len(invoices) > 1: + raise ErrorTooManyPartner( + _('Line named "%s" was matched by more than ' + 'one partner.') % line.name) + elif len(invoices) == 1: + invoice = invoices[0] + res['partner_id'] = invoice.commercial_partner_id.id + return res diff --git a/account_move_transactionid_import/parser/__init__.py b/account_move_transactionid_import/parser/__init__.py new file mode 100644 index 00000000..9102553c --- /dev/null +++ b/account_move_transactionid_import/parser/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from . import transactionid_file_parser diff --git a/account_move_transactionid_import/parser/transactionid_file_parser.py b/account_move_transactionid_import/parser/transactionid_file_parser.py new file mode 100644 index 00000000..a11501c3 --- /dev/null +++ b/account_move_transactionid_import/parser/transactionid_file_parser.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# © 2011-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import datetime +from openerp.tools import ustr +from openerp.addons.account_move_base_import.parser.file_parser import ( + FileParser, float_or_zero +) + + +class TransactionIDFileParser(FileParser): + """TransactionID parser that use a define format in csv or xls to import + bank statement. + """ + + def __init__(self, profile, ftype='csv', extra_fields=None, header=None, + **kwargs): + """Add transaction_id in header keys + :param char: profile: Reference to the profile + :param char: ftype: extension of the file (could be csv or xls) + :param dict: extra_fields: extra fields to add to the conversion + dict. In the format {fieldname: fieldtype} + :param list: header : specify header fields if the csv file has no + header + """ + conversion_dict = { + 'transaction_ref': ustr, + 'label': ustr, + 'date': datetime.datetime, + 'amount': float_or_zero, + 'commission_amount': float_or_zero, + } + super(TransactionIDFileParser, self).__init__( + profile, extra_fields=conversion_dict, ftype=ftype, header=header, + **kwargs) + + @classmethod + def parser_for(cls, parser_name): + """Used by the new_bank_statement_parser class factory. Return true if + the providen name is generic_csvxls_transaction + """ + return parser_name == 'generic_csvxls_transaction' + + def get_move_line_vals(self, line, *args, **kwargs): + """This method must return a dict of vals that can be passed to create + method of statement line in order to record it. It is the + responsibility of every parser to give this dict of vals, so each one + can implement his own way of recording the lines. + :param: line: a dict of vals that represent a line of + result_row_list + :return: dict of values to give to the create method of statement + line, it MUST contain at least: + { + 'name':value, + 'date':value, + 'amount':value, + 'ref':value, + 'label':value, + 'commission_amount':value, + } + In this generic parser, the commission is given for every line, so we + store it for each one. + """ + amount = line.get('amount', 0.0) + return { + 'name': line.get('label', '/'), + 'date_maturity': line.get('date', datetime.datetime.now().date()), + 'credit': amount > 0.0 and amount or 0.0, + 'debit': amount < 0.0 and amount or 0.0, + 'transaction_ref': line.get('transaction_ref', '/'), + } diff --git a/account_move_transactionid_import/test/completion_invoice_transactionid_test.yml b/account_move_transactionid_import/test/completion_invoice_transactionid_test.yml new file mode 100644 index 00000000..ebfc3206 --- /dev/null +++ b/account_move_transactionid_import/test/completion_invoice_transactionid_test.yml @@ -0,0 +1,44 @@ +- + In order to test the banking framework, I first need to create a journal +- + !record {model: account.journal, id: account.bank_journal}: + used_for_completion: True + rule_ids: + - bank_statement_completion_rule_trans_id_invoice +- + Now I create a move. I create statment lines separately because I need + to find each one by XML id +- + !record {model: account.move, id: move_invoice_transactionid_test1}: + name: Move with transaction ID + journal_id: account.bank_journal + company_id: base.main_company +- + I create a move line for a SO with transaction ID +- + !record {model: account.move.line, id: move_line_invoice_transactionid}: + name: Test autocompletion based on invoice with transaction ID + account_id: account.a_sale + move_id: move_invoice_transactionid_test1 + transaction_ref: XXX77Z + date_maturity: !eval time.strftime('%Y-%m-%d') + credit: 0.0 +- + and add the correct amount +- + !python {model: account.move.line}: | + context['check_move_validity'] = False + model.write(cr, uid, [ref('move_line_invoice_transactionid')], + {'credit': 450.0}, + context) +- + I run the auto complete +- + !python {model: account.move}: | + result = self.button_auto_completion(cr, uid, [ref("move_invoice_transactionid_test1")]) +- + Now I can check that all is nice and shiny, line 1. I expect the invoice has been + recognised from the transaction ID. +- + !assert {model: account.move.line, id: move_line_invoice_transactionid, string: Check completion by Invoice transaction ID}: + - partner_id.name == u'Agrolait' diff --git a/account_move_transactionid_import/test/completion_transactionid_test.yml b/account_move_transactionid_import/test/completion_transactionid_test.yml new file mode 100644 index 00000000..889be36c --- /dev/null +++ b/account_move_transactionid_import/test/completion_transactionid_test.yml @@ -0,0 +1,48 @@ +- + In order to test the banking framework, I first need to create a journal +- + !record {model: account.journal, id: account.bank_journal}: + used_for_completion: True + rule_ids: + - bank_statement_completion_rule_4 + - account_statement_base_import.bank_statement_completion_rule_4 + - account_statement_base_import.bank_statement_completion_rule_5 + - account_statement_base_import.bank_statement_completion_rule_2 + - account_statement_base_import.bank_statement_completion_rule_3 +- + Now I create a move. I create statment lines separately because I need + to find each one by XML id +- + !record {model: account.move, id: move_transactionid_test1}: + name: Move with transaction ID + journal_id: account.bank_journal + company_id: base.main_company +- + I create a move line for a SO with transaction ID +- + !record {model: account.move.line, id: move_line_transactionid}: + name: Test autocompletion based on SO with transaction ID + account_id: account.a_sale + move_id: move_transactionid_test1 + transaction_ref: XXX66Z + date_maturity: !eval "'%s-01-06' %(datetime.now().year)" + credit: 0.0 +- + and add the correct amount +- + !python {model: account.move.line}: | + context['check_move_validity'] = False + model.write(cr, uid, [ref('move_line_transactionid')], + {'credit': 118.4}, + context) +- + I run the auto complete +- + !python {model: account.move}: | + result = self.button_auto_completion(cr, uid, [ref("move_transactionid_test1")]) +- + Now I can check that all is nice and shiny, line 1. I expect the SO has been + recognised from the transaction ID. +- + !assert {model: account.move.line, id: move_line_transactionid, string: Check completion by SO transaction ID}: + - partner_id.name == u'Agrolait' diff --git a/account_move_transactionid_import/test/invoice.yml b/account_move_transactionid_import/test/invoice.yml new file mode 100644 index 00000000..61c10aa2 --- /dev/null +++ b/account_move_transactionid_import/test/invoice.yml @@ -0,0 +1,25 @@ +- + I create a new invoice with transaction ID +- + !record {model: account.invoice, id: invoice_with_transaction_id}: + company_id: base.main_company + currency_id: base.EUR + partner_id: base.res_partner_2 + transaction_id: XXX77Z + invoice_line_ids: + - name: '[PCSC234] PC Assemble SC234' + price_unit: 450.0 + quantity: 1.0 + product_id: product.product_product_3 + uom_id: product.product_uom_unit + journal_id: account.bank_journal + reference_type: none +- + I confirm the Invoice +- + !workflow {model: account.invoice, action: invoice_open, ref: invoice_with_transaction_id} +- + I check that the invoice state is "Open" +- + !assert {model: account.invoice, id: invoice_with_transaction_id}: + - state == 'open' diff --git a/account_move_transactionid_import/test/sale.yml b/account_move_transactionid_import/test/sale.yml new file mode 100644 index 00000000..d712f8ad --- /dev/null +++ b/account_move_transactionid_import/test/sale.yml @@ -0,0 +1,21 @@ +- + I import account minimal data +- + !python {model: account.invoice}: | + openerp.tools.convert_file(cr, + 'account', + openerp.modules.get_module_resource( + 'account', + 'test', + 'account_minimal_test.xml'), + {}, 'init', False, 'test') +- + I create a new Sale Order with transaction ID +- + !record {model: sale.order, id: so_with_transaction_id}: + partner_id: base.res_partner_2 + note: Invoice after delivery + transaction_id: XXX66Z + order_line: + - product_id: product.product_product_7 + product_uom_qty: 8