[MIG] account_move_base_import: Migration to 11.0

pull/510/head
Akim Juillerat 2019-07-10 09:48:13 +02:00 committed by Florian da Costa
parent 1e59a3bd51
commit 68a91a1d71
29 changed files with 443 additions and 384 deletions

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
from . import parser
from . import wizard
from . import models

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
'name': "Journal Entry base import",
'version': '10.0.1.1.0',
'version': '11.0.1.0.0',
'author': "Akretion,Camptocamp,Odoo Community Association (OCA)",
'category': 'Finance',
'depends': ['account'],
@ -19,13 +18,6 @@
"views/journal_view.xml",
"views/partner_view.xml",
],
'test': [
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/refund.yml',
'test/completion_test.yml'
],
'installable': True,
'license': 'AGPL-3',
}

View File

@ -1,4 +0,0 @@
"date";"amount";"commission_amount";"label"
2011-03-07 13:45:14;118.4;-11.84;"label a"
2011-03-02 13:45:14;189;-15.12;"label b"
2011-03-02 17:45:14;189;-15.12;"label c"
1 date amount commission_amount label
2 2011-03-07 13:45:14 118.4 -11.84 label a
3 2011-03-02 13:45:14 189 -15.12 label b
4 2011-03-02 17:45:14 189 -15.12 label c

View File

@ -0,0 +1,4 @@
date;amount;commission_amount;label
2011-03-07 13:45:14;118.4;-11.84;label a
2011-03-02 13:45:14;189;-15.12;label b
2011-03-02 17:45:14;189;-15.12;label c
1 date amount commission_amount label
2 2011-03-07 13:45:14 118.4 -11.84 label a
3 2011-03-02 13:45:14 189 -15.12 label b
4 2011-03-02 17:45:14 189 -15.12 label c

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
from . import account_journal
from . import account_move
from . import partner

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2019 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import sys
import traceback
@ -330,7 +329,9 @@ class AccountJournal(models.Model):
if not result_row_list:
raise UserError(_("Nothing to import: "
"The file is empty"))
parsed_cols = parser.get_move_line_vals(result_row_list[0]).keys()
parsed_cols = list(
parser.get_move_line_vals(result_row_list[0]).keys()
)
for col in parsed_cols:
if col not in move_line_obj._fields:
raise UserError(
@ -345,9 +346,10 @@ class AccountJournal(models.Model):
parser_vals = parser.get_move_line_vals(line)
values = self.prepare_move_line_vals(parser_vals, move)
move_store.append(values)
# TODO Check if this is still needed
# Hack to bypass ORM poor perfomance. Sob...
move_line_obj._insert_lines(move_store)
self.env.invalidate_all()
self.invalidate_cache()
self._write_extra_move_lines(parser, move)
if self.create_counterpart:
self._create_counterpart(parser, move)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import traceback
import sys
@ -10,8 +9,9 @@ import logging
import psycopg2
from odoo import _, api, fields, models
from odoo import _, api, fields, models, registry
from odoo.exceptions import ValidationError
from odoo.tools import float_repr
_logger = logging.getLogger(__name__)
@ -62,7 +62,8 @@ class AccountMoveCompletionRule(models.Model):
'From line name (based on partner field)'),
('get_from_name_and_partner_name',
'From line name (based on partner name)')
], string='Method')
], string='Method'
)
def _find_invoice(self, line, inv_type):
"""Find invoice related to statement line"""
@ -262,9 +263,9 @@ class AccountMoveLine(models.Model):
"""Return writeable by SQL columns"""
model_cols = self._fields
avail = [
k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')
k for k, col in model_cols.items() if not hasattr(col, '_fnct')
]
keys = [k for k in move_store[0].keys() if k in avail]
keys = [k for k in list(move_store[0].keys()) if k in avail]
keys.sort()
return keys
@ -273,8 +274,33 @@ class AccountMoveLine(models.Model):
Return a copy of statement
"""
move_copy = move
for k, col in move_copy.iteritems():
for k, col in move_copy.items():
if k in cols:
# In v11, the record is needed to call convert_to_column on a
# Monetary field because it must find the currency to apply the
# proper rounding. So we mimic the call here as best as we can.
if isinstance(self._fields[k], fields.Monetary):
currency_field = move_copy.get(
self._fields[k].currency_field
)
if currency_field:
currency_id = (
move_copy.get(currency_field)
or move.get('company_currency_id')
)
if currency_id:
currency = self.env['res.currency'].browse(
currency_id
)
move_copy[k] = float_repr(
currency.round(col), currency.decimal_places
)
continue
# If no currency was found, set what convert_to_column
# would do and used to do in v10
move_copy[k] = float(col or 0.0)
continue
else:
move_copy[k] = self._fields[k].convert_to_column(col, None)
return move_copy
@ -333,19 +359,16 @@ class AccountMove(models.Model):
related='journal_id.used_for_completion',
readonly=True)
completion_logs = fields.Text(string='Completion Log', readonly=True)
# partner_id is a native field of the account module
# (related='line_ids.partner_id', store=True, readonly=True)
partner_id = fields.Many2one(related=False, compute='_compute_partner_id')
import_partner_id = fields.Many2one('res.partner',
string="Partner from import")
@api.one
@api.depends('line_ids.partner_id', 'import_partner_id')
def _compute_partner_id(self):
if self.import_partner_id:
self.partner_id = self.import_partner_id
elif self.line_ids:
self.partner_id = self.line_ids[0].partner_id
for move in self:
if move.import_partner_id:
move.partner_id = move.import_partner_id
else:
super(AccountMove, move)._compute_partner_id()
def write_completion_log(self, error_msg, number_imported):
"""Write the log in the completion_logs field of the bank statement to
@ -392,9 +415,9 @@ class AccountMove(models.Model):
res = line._get_line_values_from_rules(rules)
if res:
compl_lines += 1
except ErrorTooManyPartner, exc:
except ErrorTooManyPartner as exc:
msg_lines.append(repr(exc))
except Exception, exc:
except Exception as exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
@ -411,10 +434,6 @@ class AccountMove(models.Model):
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
# we can commit as it is not needed to be atomic
# commiting here adds a nice perfo boost
if not compl_lines % 500:
self.env.cr.commit()
msg = u'\n'.join(msg_lines)
msg = '\n'.join(msg_lines)
self.write_completion_log(msg, compl_lines)
return True

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from odoo import fields, models

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from .parser import new_move_parser
from .parser import AccountMoveImportParser

View File

@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp.tools.translate import _
from openerp.exceptions import UserError
import tempfile
import datetime
import tempfile
import xlrd
from odoo import _
from odoo.exceptions import UserError
from .parser import AccountMoveImportParser, UnicodeDictReader
try:
import xlrd
except:
raise Exception(_('Please install python lib xlrd'))
def float_or_zero(val):
@ -37,14 +36,14 @@ class FileParser(AccountMoveImportParser):
:param list: header : specify header fields if the csv file has no
header
"""
super(FileParser, self).__init__(journal, **kwargs)
super().__init__(journal, **kwargs)
if ftype in ('csv', 'xls', 'xlsx'):
self.ftype = ftype[0:3]
else:
raise UserError(
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
self.conversion_dict = extra_fields
self.keys_to_validate = self.conversion_dict.keys()
self.keys_to_validate = list(self.conversion_dict.keys())
self.fieldnames = header
self._datemode = 0 # used only for xls documents,
# 0 means Windows mode (1900 based dates).
@ -79,7 +78,7 @@ class FileParser(AccountMoveImportParser):
separately (in the field: fieldnames).
"""
if self.fieldnames is None:
parsed_cols = self.result_row_list[0].keys()
parsed_cols = list(self.result_row_list[0].keys())
for col in self.keys_to_validate:
if col not in parsed_cols:
raise UserError(_('Column %s not present in file') % col)
@ -113,7 +112,7 @@ class FileParser(AccountMoveImportParser):
header = sheet.row_values(0)
res = []
for rownum in range(1, sheet.nrows):
res.append(dict(zip(header, sheet.row_values(rownum))))
res.append(dict(list(zip(header, sheet.row_values(rownum)))))
return res
def _from_csv(self, result_set, conversion_rules):

View File

@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import datetime
from .file_parser import FileParser
from openerp.addons.account_move_base_import.parser.file_parser import (
float_or_zero
)
from openerp.tools import ustr
from .file_parser import FileParser, float_or_zero
from odoo.tools import ustr
class GenericFileParser(FileParser):
@ -27,7 +23,7 @@ class GenericFileParser(FileParser):
}
# set self.env for later ORM searches
self.env = journal.env
super(GenericFileParser, self).__init__(
super().__init__(
journal, ftype=ftype,
extra_fields=conversion_dict,
**kwargs)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import base64
import csv
@ -21,9 +20,9 @@ def UnicodeDictReader(utf8_data, **kwargs):
dialect = kwargs.pop('dialect')
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader:
yield dict([(unicode(key or '', 'utf-8'),
unicode(value or '', 'utf-8'))
for key, value in row.iteritems()])
yield dict([(str(key or ''),
str(value or ''))
for key, value in row.items()])
class AccountMoveImportParser(object):

View File

@ -0,0 +1,7 @@
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Sébastien Beau <sebastien.beau@akretion.com>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
* Akim Juillerat <akim.juillerat@camptocamp.com>

View File

@ -0,0 +1,42 @@
This module is a grouping of 7.0/8.0 modules, used to import accounting files
and completing them automatically:
* account_statement_base_completion
* account_statement_base_import
* account_statement_commission
* account_statement_ext
The main change is that, in order to import financial data, this information
is now imported directly as a Journal Entry.
Most of the information present in the "statement profile" is now located in
the account journal (with 2 boolean parameters which allows to use
this journal for importation and/or auto-completion).
Financial data can be imported using a standard .csv or .xls file (you'll find
it in the 'data' folder). It respects the journal to pass the entries.
This module can handle a commission taken by the payment office and has the
following format:
* __date__: date of the payment
* __amount__: amount paid in the currency of the journal used in the
importation
* __label__: the comunication given by the payment office, used as
communication in the generated entries.
Another column which can be used is __commission_amount__, representing
the amount for the commission taken by line.
Afterwards, the goal is to populate the journal items with information that
the bank or office gave you. For this, completion rules can be specified by
journal.
Some basic rules are provided in this module:
1) Match from statement line label (based on partner field 'Bank Statement
Label')
2) Match from statement line label (based on partner name)
3) Match from statement line label (based on Invoice reference)
Feel free to extend either the importation method, the completion method, or
both.

View File

@ -0,0 +1 @@
* As for now, the module does not handle multicurrency imports.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -1,108 +0,0 @@
-
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
- bank_statement_completion_rule_2
- bank_statement_completion_rule_3
- bank_statement_completion_rule_5
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.move, id: move_test1}:
name: Move 2
journal_id: account.bank_journal
company_id: base.main_company
-
I create a move line for a CI
-
!record {model: account.move.line, id: move_line_ci}:
name: \
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-20'
credit: 0.0
-
I create a move line for a SI
-
!record {model: account.move.line, id: move_line_si}:
name: \
account_id: account.a_expense
move_id: move_test1
date_maturity: '2013-12-19'
debit: 0.0
-
I create a move line for a CR
-
!record {model: account.move.line, id: move_line_cr}:
name: \
account_id: account.a_expense
move_id: move_test1
date_maturity: '2013-12-19'
debit: 0.0
-
I create a move line for the Partner Name
-
!record {model: account.move.line, id: move_line_partner_name}:
name: Test autocompletion based on Partner Name Camptocamp
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-17'
credit: 0.0
-
I create a move line for the Partner Label
-
!record {model: account.move.line, id: move_line_partner_label}:
name: XXX66Z
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-24'
debit: 0.0
-
and add the correct name
-
!python {model: account.move.line}: |
import datetime as dt
model.browse(ref('move_line_ci')).with_context(check_move_validity=False).write({'name': dt.date.today().strftime('TBNK/%Y/0001'),'credit': 210.0})
model.browse(ref('move_line_si')).with_context(check_move_validity=False).write({'name': 'T2S12345','debit': 65.0})
model.browse(ref('move_line_cr')).with_context(check_move_validity=False).write({'name': dt.date.today().strftime('RTEXJ/%Y/0001'),'debit': 210.0})
model.browse(ref('move_line_partner_name')).with_context(check_move_validity=False).write({'credit': 600.0})
model.browse(ref('move_line_partner_label')).with_context(check_move_validity=False).write({'debit': 932.4})
-
I run the auto complete
-
!python {model: account.move, ref: move_test1}: |
result = model.browse(ref('move_test1')).button_auto_completion()
-
Now I can check that all is nice and shiny, line 1. I expect the Customer
Invoice Number to be recognised.
I Use _ref, because ref conflicts with the field ref of the statement line
-
!assert {model: account.move.line, id: move_line_ci, string: Check completion by CI number}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 2. I expect the Supplier invoice number to be recognised. The supplier
invoice was created by the account module demo data, and we confirmed it
here.
-
!assert {model: account.move.line, id: move_line_si, string: Check completion by SI number}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 3. I expect the Customer refund number to be recognised. It should be
the commercial partner, and not the regular partner.
-
!assert {model: account.move.line, id: move_line_cr, string: Check completion by CR number and commercial partner}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 4. I check that the partner name has been recognised.
-
!assert {model: account.move.line, id: move_line_partner_name, string: Check completion by partner name}:
- partner_id.name == 'Camptocamp'
-
Line 5. I check that the partner special label has been recognised.
-
!assert {model: account.move.line, id: move_line_partner_label, string: Check completion by partner label}:
- partner_id.id == _ref("base.res_partner_4")

View File

@ -1,43 +0,0 @@
-
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 customer Invoice to be found by the completion.
-
!record {model: account.invoice, id: invoice_for_completion_1}:
company_id: base.main_company
currency_id: base.EUR
invoice_line_ids:
- name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uom_id: product.product_uom_unit
journal_id: account.bank_journal
partner_id: base.res_partner_12
reference_type: none
-
I confirm the Invoice
-
!python {model: account.invoice, id: invoice_for_completion_1}:
self.action_invoice_open()
-
I check that the invoice state is "Open"
-
!assert {model: account.invoice, id: invoice_for_completion_1}:
- state == 'open'
-
I check that it is given the number "TBNK/%Y/0001"
-
!python {model: account.invoice}: |
import datetime as dt
invoice = model.browse(ref('invoice_for_completion_1'))
assert invoice.number == dt.date.today().strftime('TBNK/%Y/0001')

View File

@ -1,5 +0,0 @@
-
I fill in the field Bank Statement Label in a Partner
-
!record {model: res.partner, id: base.res_partner_4}:
bank_statement_label: XXX66Z

View File

@ -1,43 +0,0 @@
-
I create a "child" partner, to use in the invoice
(and have a different commercial_partner_id than itself)
-
!record {model: res.partner, id: res_partner_12_child}:
name: Child Partner
supplier: False
customer: True
is_company: False
parent_id: base.res_partner_12
-
I create a customer refund to be found by the completion.
-
!record {model: account.invoice, id: refund_for_completion_1}:
company_id: base.main_company
currency_id: base.EUR
invoice_line_ids:
- name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uom_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: res_partner_12_child
type: 'out_refund'
reference_type: none
-
I confirm the refund
-
!python {model: account.invoice, ref: invoice_for_completion_1}: |
model.browse(ref('refund_for_completion_1')).action_invoice_open()
-
I check that the refund state is "Open"
-
!assert {model: account.invoice, id: refund_for_completion_1}:
- state == 'open'
-
I check that it is given the number "RTEXJ/%Y/0001"
-
!python {model: account.invoice}: |
import datetime as dt
invoice = model.browse(ref('refund_for_completion_1'))
assert invoice.number == dt.date.today().strftime('RTEXJ/%Y/0001')

View File

@ -1,41 +0,0 @@
-
I import account minimal data
-
!python {model: account.invoice}: |
openerp.tools.convert_file(cr,
'account',
openerp.modules.get_module_resource(
'account',
'demo',
'account_invoice_demo.yml'),
{}, 'init', False, 'test')
-
I check that my invoice is a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type}:
- type == 'in_invoice'
-
I add a reference to an existing supplier invoce
-
!python {model: account.invoice, id: account.demo_invoice_0}: |
self.write({'reference': 'T2S12345'})
-
I check a second time that my invoice is still a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 2}:
- type == 'in_invoice'
-
Now I confirm it
-
!python {model: account.invoice, ref: account.demo_invoice_0}: |
self.action_invoice_open()
-
I check that the supplier number is there
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check supplier number}:
- reference == 'T2S12345'
-
I check a third time that my invoice is still a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 3}:
- type == 'in_invoice'

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import test_base_completion
from . import test_base_import
from . import test_invoice

View File

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from odoo import fields, tools
from odoo.modules import get_module_resource
from odoo.modules import get_resource_path
from odoo.tests import common
from collections import namedtuple
@ -36,9 +35,9 @@ NAMES_COMPLETION_CASES = [
class BaseCompletion(common.TransactionCase):
def setUp(self):
super(BaseCompletion, self).setUp()
super().setUp()
tools.convert_file(self.cr, 'account',
get_module_resource('account', 'test',
get_resource_path('account', 'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
self.account_move_obj = self.env["account.move"]
@ -82,13 +81,13 @@ class BaseCompletion(common.TransactionCase):
"Partner_id must be blank before completion")
self.move.button_auto_completion()
if case.should_match:
self.assertEquals(
self.assertEqual(
self.partner, self.move_line.partner_id,
"Missing expected partner id after completion "
"(partner_name: %s, line_name: %s)" %
(case.partner_name, case.line_label))
else:
self.assertNotEquals(
self.assertNotEqual(
self.partner, self.move_line.partner_id,
"Partner id should be empty after completion "
"(partner_name: %s, line_name: %s)"

View File

@ -1,25 +1,23 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import base64
import inspect
import os
from operator import attrgetter
from odoo.tests import common
from odoo import tools
from odoo.modules import get_module_resource
from odoo.modules import get_resource_path
class TestCodaImport(common.TransactionCase):
def setUp(self):
super(TestCodaImport, self).setUp()
super().setUp()
self.company_a = self.browse_ref('base.main_company')
tools.convert_file(self.cr, 'account',
get_module_resource('account', 'test',
get_resource_path('account', 'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
self.account_move_obj = self.env["account.move"]
@ -37,15 +35,11 @@ class TestCodaImport(common.TransactionCase):
'create_counterpart': True,
})
def _filename_to_abs_filename(self, file_name):
dir_name = os.path.dirname(inspect.getfile(self.__class__))
return os.path.join(dir_name, file_name)
def _import_file(self, file_name):
""" import a file using the wizard
return the create account.bank.statement object
"""
with open(file_name) as f:
with open(file_name, 'rb') as f:
content = f.read()
self.wizard = self.import_wizard_obj.create({
"journal_id": self.journal.id,
@ -58,16 +52,18 @@ class TestCodaImport(common.TransactionCase):
def test_simple_xls(self):
"""Test import from xls
"""
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.xls"))
file_name = get_resource_path(
'account_move_base_import', 'demo', 'statement.xls'
)
move = self._import_file(file_name)
self._validate_imported_move(move)
def test_simple_csv(self):
"""Test import from csv
"""
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.csv"))
file_name = get_resource_path(
'account_move_base_import', 'demo', 'statement.csv'
)
move = self._import_file(file_name)
self._validate_imported_move(move)

View File

@ -0,0 +1,256 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
import datetime as dt
from odoo.modules import get_resource_path
from odoo.tests import SingleTransactionCase
from odoo.tools import convert_file
class TestInvoice(SingleTransactionCase):
def setUp(self):
super().setUp()
self.account_move_obj = self.env["account.move"]
self.account_move_line_obj = \
self.env["account.move.line"]
self.company_a = self.env.ref('base.main_company')
self.partner = self.env.ref("base.res_partner_12")
def test_01_partner(self):
# I fill in the field Bank Statement Label in a Partner
self.partner_4 = self.env.ref('base.res_partner_4')
self.partner_4.bank_statement_label = 'XXX66Z'
self.assertEqual(self.partner_4.bank_statement_label, 'XXX66Z')
def test_02_invoice(self):
convert_file(
self.cr, 'account', get_resource_path(
'account', 'test', 'account_minimal_test.xml'
), {}, 'init', False, 'test'
)
self.journal = self.env.ref("account.bank_journal")
self.account_id = self.env.ref("account.a_recv")
# I create a customer Invoice to be found by the completion.
product_3 = self.env.ref('product.product_product_3')
self.invoice_for_completion_1 = self.env['account.invoice'].create({
'currency_id': self.env.ref('base.EUR').id,
'invoice_line_ids': [(0, 0, {
'name': '[PCSC234] PC Assemble SC234',
'product_id': product_3.id,
'price_unit': 210.0,
'quantity': 1.0,
'uom_id': self.env.ref('product.product_uom_unit').id,
'account_id': self.env.ref('account.a_sale').id,
})],
'journal_id': self.journal.id,
'partner_id': self.partner.id,
'reference_type': 'none',
'account_id': self.env.ref('account.a_recv').id,
})
# I confirm the Invoice
self.invoice_for_completion_1.action_invoice_open()
# I check that the invoice state is "Open"
self.assertEqual(self.invoice_for_completion_1.state, 'open')
# I check that it is given the number "TBNK/%Y/0001"
self.assertEqual(
self.invoice_for_completion_1.number,
dt.date.today().strftime('TBNK/%Y/0001')
)
def test_03_supplier_invoice(self):
# I import account minimal data
convert_file(
self.cr, 'account', get_resource_path(
'account', 'demo', 'account_invoice_demo.yml'
), {}, 'init', False, 'test'
)
# I check that my invoice is a supplier invoice
demo_invoice_0 = self.env.ref('account.demo_invoice_0')
self.assertEqual(
demo_invoice_0.type, 'in_invoice', msg="Check invoice type"
)
# I add a reference to an existing supplier invoice
demo_invoice_0.write({'reference': 'T2S12345'})
# I check a second time that my invoice is still a supplier invoice
self.assertEqual(
demo_invoice_0.type, 'in_invoice', msg="Check invoice type 2"
)
# Now I confirm it
demo_invoice_0.action_invoice_open()
# I check that the supplier number is there
self.assertEqual(
demo_invoice_0.reference, 'T2S12345', msg="Check supplier number"
)
# I check a third time that my invoice is still a supplier invoice
self.assertEqual(
demo_invoice_0.type, 'in_invoice', msg="Check invoice type 3"
)
def test_04_refund(self):
# I create a "child" partner, to use in the invoice
# (and have a different commercial_partner_id than itself)
res_partner_12_child = self.env['res.partner'].create({
'name': 'Child Partner',
'supplier': False,
'customer': True,
'is_company': False,
'parent_id': self.partner.id,
})
# I create a customer refund to be found by the completion.
product_3 = self.env.ref('product.product_product_3')
self.refund_for_completion_1 = self.env['account.invoice'].create({
'currency_id': self.env.ref('base.EUR').id,
'invoice_line_ids': [(0, 0, {
'name': '[PCSC234] PC Assemble SC234',
'product_id': product_3.id,
'price_unit': 210.0,
'quantity': 1.0,
'uom_id': self.env.ref('product.product_uom_unit').id,
'account_id': self.env.ref('account.a_sale').id,
})],
'journal_id': self.env.ref('account.expenses_journal').id,
'partner_id': res_partner_12_child.id,
'type': 'out_refund',
'reference_type': 'none',
'account_id': self.env.ref('account.a_recv').id,
})
# I confirm the refund
self.refund_for_completion_1.action_invoice_open()
# I check that the refund state is "Open"
self.assertEqual(self.refund_for_completion_1.state, 'open')
# I check that it is given the number "RTEXJ/%Y/0001"
self.assertEqual(
self.refund_for_completion_1.number,
dt.date.today().strftime('RTEXJ/%Y/0001')
)
def test_05_completion(self):
# In order to test the banking framework, I first need to create a
# journal
self.journal = self.env.ref("account.bank_journal")
completion_rule_4 = self.env.ref(
'account_move_base_import.bank_statement_completion_rule_4'
)
completion_rule_2 = self.env.ref(
'account_move_base_import.bank_statement_completion_rule_2'
)
completion_rule_3 = self.env.ref(
'account_move_base_import.bank_statement_completion_rule_3'
)
completion_rule_5 = self.env.ref(
'account_move_base_import.bank_statement_completion_rule_5'
)
completion_rules = (
completion_rule_2 | completion_rule_3 | completion_rule_4
| completion_rule_5
)
self.journal.write({
'used_for_completion': True,
'rule_ids': [
(4, comp_rule.id, False) for comp_rule in completion_rules
]
})
# Now I create a statement. I create statment lines separately because
# I need to find each one by XML id
move_test1 = self.env['account.move'].create({
'name': 'Move 2',
'journal_id': self.journal.id,
})
# I create a move line for a CI
move_line_ci = self.env['account.move.line'].create({
'name': '\\',
'account_id': self.env.ref('account.a_sale').id,
'move_id': move_test1.id,
'date_maturity': '2013-12-20',
'credit': 0.0,
})
# I create a move line for a SI
move_line_si = self.env['account.move.line'].create({
'name': '\\',
'account_id': self.env.ref('account.a_expense').id,
'move_id': move_test1.id,
'date_maturity': '2013-12-19',
'debit': 0.0,
})
# I create a move line for a CR
move_line_cr = self.env['account.move.line'].create({
'name': '\\',
'account_id': self.env.ref('account.a_expense').id,
'move_id': move_test1.id,
'date_maturity': '2013-12-19',
'debit': 0.0,
})
# I create a move line for the Partner Name
move_line_partner_name = self.env['account.move.line'].create({
'name': 'Test autocompletion based on Partner Name Camptocamp',
'account_id': self.env.ref('account.a_sale').id,
'move_id': move_test1.id,
'date_maturity': '2013-12-17',
'credit': 0.0,
})
# I create a move line for the Partner Label
move_line_partner_label = self.env['account.move.line'].create({
'name': 'XXX66Z',
'account_id': self.env.ref('account.a_sale').id,
'move_id': move_test1.id,
'date_maturity': '2013-12-24',
'debit': 0.0,
})
# and add the correct name
move_line_ci.with_context(check_move_validity=False).write({
'name': dt.date.today().strftime('TBNK/%Y/0001'),
'credit': 210.0,
})
move_line_si.with_context(check_move_validity=False).write({
'name': 'T2S12345',
'debit': 65.0,
})
move_line_cr.with_context(check_move_validity=False).write({
'name': dt.date.today().strftime('RTEXJ/%Y/0001'),
'debit': 210.0,
})
move_line_partner_name.with_context(check_move_validity=False).write({
'credit': 600.0,
})
move_line_partner_label.with_context(check_move_validity=False).write({
'debit': 932.4,
})
# I run the auto complete
move_test1.button_auto_completion()
# Now I can check that all is nice and shiny, line 1. I expect the
# Customer Invoice Number to be recognised.
# I Use _ref, because ref conflicts with the field ref of the
# statement line
self.assertEqual(
move_line_ci.partner_id, self.partner,
msg="Check completion by CI number"
)
# Line 2. I expect the Supplier invoice number to be recognised. The
# supplier invoice was created by the account module demo data, and we
# confirmed it here.
self.assertEqual(
move_line_si.partner_id, self.partner,
msg="Check completion by SI number"
)
# Line 3. I expect the Customer refund number to be recognised. It
# should be the commercial partner, and not the regular partner.
self.assertEqual(
move_line_cr.partner_id, self.partner,
msg="Check completion by CR number and commercial partner"
)
# Line 4. I check that the partner name has been recognised.
self.assertEqual(
move_line_partner_name.partner_id.name, 'Camptocamp',
msg="Check completion by partner name"
)
# Line 5. I check that the partner special label has been recognised.
self.partner_4 = self.env.ref('base.res_partner_4')
self.assertEqual(
move_line_partner_label.partner_id,
self.partner_4,
msg="Check completion by partner label"
)

View File

@ -8,7 +8,7 @@
<field name="journal_id" position="after">
<field name="used_for_completion" invisible="1"/>
</field>
<button name="button_cancel" position="after">
<button name="post" position="after">
<button name="button_auto_completion"
string="Auto Completion"
type="object"

View File

@ -1,3 +1 @@
# -*- coding: utf-8 -*-
from . import import_statement

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# Copyright 2011-2016 Akretion
# Copyright 2011-2019 Camptocamp SA
# Copyright 2013 Savoir-faire Linux
# Copyright 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
"""
Wizard to import financial institute date in bank statement
@ -19,8 +18,8 @@ class CreditPartnerStatementImporter(models.TransientModel):
@api.model
def default_get(self, fields):
ctx = self._context
res = {}
res = super().default_get(fields)
ctx = self.env.context
if (
ctx.get('active_model') == 'account.journal' and
ctx.get('active_ids')):