[MIG] account_move_base_import: Migration to 11.0
parent
1e59a3bd51
commit
68a91a1d71
|
@ -1,5 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import parser
|
from . import parser
|
||||||
from . import wizard
|
from . import wizard
|
||||||
from . import models
|
from . import models
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
{
|
{
|
||||||
'name': "Journal Entry base import",
|
'name': "Journal Entry base import",
|
||||||
'version': '10.0.1.1.0',
|
'version': '11.0.1.0.0',
|
||||||
'author': "Akretion,Camptocamp,Odoo Community Association (OCA)",
|
'author': "Akretion,Camptocamp,Odoo Community Association (OCA)",
|
||||||
'category': 'Finance',
|
'category': 'Finance',
|
||||||
'depends': ['account'],
|
'depends': ['account'],
|
||||||
|
@ -19,13 +18,6 @@
|
||||||
"views/journal_view.xml",
|
"views/journal_view.xml",
|
||||||
"views/partner_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,
|
'installable': True,
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
|
|
|
@ -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,5 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import account_journal
|
from . import account_journal
|
||||||
from . import account_move
|
from . import account_move
|
||||||
from . import partner
|
from . import partner
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2019 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -330,7 +329,9 @@ class AccountJournal(models.Model):
|
||||||
if not result_row_list:
|
if not result_row_list:
|
||||||
raise UserError(_("Nothing to import: "
|
raise UserError(_("Nothing to import: "
|
||||||
"The file is empty"))
|
"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:
|
for col in parsed_cols:
|
||||||
if col not in move_line_obj._fields:
|
if col not in move_line_obj._fields:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
|
@ -345,9 +346,10 @@ class AccountJournal(models.Model):
|
||||||
parser_vals = parser.get_move_line_vals(line)
|
parser_vals = parser.get_move_line_vals(line)
|
||||||
values = self.prepare_move_line_vals(parser_vals, move)
|
values = self.prepare_move_line_vals(parser_vals, move)
|
||||||
move_store.append(values)
|
move_store.append(values)
|
||||||
|
# TODO Check if this is still needed
|
||||||
# Hack to bypass ORM poor perfomance. Sob...
|
# Hack to bypass ORM poor perfomance. Sob...
|
||||||
move_line_obj._insert_lines(move_store)
|
move_line_obj._insert_lines(move_store)
|
||||||
self.env.invalidate_all()
|
self.invalidate_cache()
|
||||||
self._write_extra_move_lines(parser, move)
|
self._write_extra_move_lines(parser, move)
|
||||||
if self.create_counterpart:
|
if self.create_counterpart:
|
||||||
self._create_counterpart(parser, move)
|
self._create_counterpart(parser, move)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
|
@ -10,8 +9,9 @@ import logging
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models, registry
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tools import float_repr
|
||||||
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
@ -62,7 +62,8 @@ class AccountMoveCompletionRule(models.Model):
|
||||||
'From line name (based on partner field)'),
|
'From line name (based on partner field)'),
|
||||||
('get_from_name_and_partner_name',
|
('get_from_name_and_partner_name',
|
||||||
'From line name (based on partner name)')
|
'From line name (based on partner name)')
|
||||||
], string='Method')
|
], string='Method'
|
||||||
|
)
|
||||||
|
|
||||||
def _find_invoice(self, line, inv_type):
|
def _find_invoice(self, line, inv_type):
|
||||||
"""Find invoice related to statement line"""
|
"""Find invoice related to statement line"""
|
||||||
|
@ -262,9 +263,9 @@ class AccountMoveLine(models.Model):
|
||||||
"""Return writeable by SQL columns"""
|
"""Return writeable by SQL columns"""
|
||||||
model_cols = self._fields
|
model_cols = self._fields
|
||||||
avail = [
|
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()
|
keys.sort()
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
|
@ -273,9 +274,34 @@ class AccountMoveLine(models.Model):
|
||||||
Return a copy of statement
|
Return a copy of statement
|
||||||
"""
|
"""
|
||||||
move_copy = move
|
move_copy = move
|
||||||
for k, col in move_copy.iteritems():
|
for k, col in move_copy.items():
|
||||||
if k in cols:
|
if k in cols:
|
||||||
move_copy[k] = self._fields[k].convert_to_column(col, None)
|
# 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
|
return move_copy
|
||||||
|
|
||||||
def _prepare_manyinsert(self, move_store, cols):
|
def _prepare_manyinsert(self, move_store, cols):
|
||||||
|
@ -333,19 +359,16 @@ class AccountMove(models.Model):
|
||||||
related='journal_id.used_for_completion',
|
related='journal_id.used_for_completion',
|
||||||
readonly=True)
|
readonly=True)
|
||||||
completion_logs = fields.Text(string='Completion Log', 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',
|
import_partner_id = fields.Many2one('res.partner',
|
||||||
string="Partner from import")
|
string="Partner from import")
|
||||||
|
|
||||||
@api.one
|
|
||||||
@api.depends('line_ids.partner_id', 'import_partner_id')
|
@api.depends('line_ids.partner_id', 'import_partner_id')
|
||||||
def _compute_partner_id(self):
|
def _compute_partner_id(self):
|
||||||
if self.import_partner_id:
|
for move in self:
|
||||||
self.partner_id = self.import_partner_id
|
if move.import_partner_id:
|
||||||
elif self.line_ids:
|
move.partner_id = move.import_partner_id
|
||||||
self.partner_id = self.line_ids[0].partner_id
|
else:
|
||||||
|
super(AccountMove, move)._compute_partner_id()
|
||||||
|
|
||||||
def write_completion_log(self, error_msg, number_imported):
|
def write_completion_log(self, error_msg, number_imported):
|
||||||
"""Write the log in the completion_logs field of the bank statement to
|
"""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)
|
res = line._get_line_values_from_rules(rules)
|
||||||
if res:
|
if res:
|
||||||
compl_lines += 1
|
compl_lines += 1
|
||||||
except ErrorTooManyPartner, exc:
|
except ErrorTooManyPartner as exc:
|
||||||
msg_lines.append(repr(exc))
|
msg_lines.append(repr(exc))
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
msg_lines.append(repr(exc))
|
msg_lines.append(repr(exc))
|
||||||
error_type, error_value, trbk = sys.exc_info()
|
error_type, error_value, trbk = sys.exc_info()
|
||||||
st = "Error: %s\nDescription: %s\nTraceback:" % (
|
st = "Error: %s\nDescription: %s\nTraceback:" % (
|
||||||
|
@ -411,10 +434,6 @@ class AccountMove(models.Model):
|
||||||
error_type.__name__, error_value)
|
error_type.__name__, error_value)
|
||||||
st += ''.join(traceback.format_tb(trbk, 30))
|
st += ''.join(traceback.format_tb(trbk, 30))
|
||||||
_logger.error(st)
|
_logger.error(st)
|
||||||
# we can commit as it is not needed to be atomic
|
msg = '\n'.join(msg_lines)
|
||||||
# commiting here adds a nice perfo boost
|
|
||||||
if not compl_lines % 500:
|
|
||||||
self.env.cr.commit()
|
|
||||||
msg = u'\n'.join(msg_lines)
|
|
||||||
self.write_completion_log(msg, compl_lines)
|
self.write_completion_log(msg, compl_lines)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
from odoo import fields, models
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011 Akretion
|
||||||
# © 2011 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
from .parser import new_move_parser
|
from .parser import new_move_parser
|
||||||
from .parser import AccountMoveImportParser
|
from .parser import AccountMoveImportParser
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011 Akretion
|
||||||
# © 2011 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# 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 datetime
|
||||||
|
import tempfile
|
||||||
|
import xlrd
|
||||||
|
|
||||||
|
from odoo import _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
from .parser import AccountMoveImportParser, UnicodeDictReader
|
from .parser import AccountMoveImportParser, UnicodeDictReader
|
||||||
try:
|
|
||||||
import xlrd
|
|
||||||
except:
|
|
||||||
raise Exception(_('Please install python lib xlrd'))
|
|
||||||
|
|
||||||
|
|
||||||
def float_or_zero(val):
|
def float_or_zero(val):
|
||||||
|
@ -37,14 +36,14 @@ class FileParser(AccountMoveImportParser):
|
||||||
:param list: header : specify header fields if the csv file has no
|
:param list: header : specify header fields if the csv file has no
|
||||||
header
|
header
|
||||||
"""
|
"""
|
||||||
super(FileParser, self).__init__(journal, **kwargs)
|
super().__init__(journal, **kwargs)
|
||||||
if ftype in ('csv', 'xls', 'xlsx'):
|
if ftype in ('csv', 'xls', 'xlsx'):
|
||||||
self.ftype = ftype[0:3]
|
self.ftype = ftype[0:3]
|
||||||
else:
|
else:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
|
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
|
||||||
self.conversion_dict = extra_fields
|
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.fieldnames = header
|
||||||
self._datemode = 0 # used only for xls documents,
|
self._datemode = 0 # used only for xls documents,
|
||||||
# 0 means Windows mode (1900 based dates).
|
# 0 means Windows mode (1900 based dates).
|
||||||
|
@ -79,7 +78,7 @@ class FileParser(AccountMoveImportParser):
|
||||||
separately (in the field: fieldnames).
|
separately (in the field: fieldnames).
|
||||||
"""
|
"""
|
||||||
if self.fieldnames is None:
|
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:
|
for col in self.keys_to_validate:
|
||||||
if col not in parsed_cols:
|
if col not in parsed_cols:
|
||||||
raise UserError(_('Column %s not present in file') % col)
|
raise UserError(_('Column %s not present in file') % col)
|
||||||
|
@ -113,7 +112,7 @@ class FileParser(AccountMoveImportParser):
|
||||||
header = sheet.row_values(0)
|
header = sheet.row_values(0)
|
||||||
res = []
|
res = []
|
||||||
for rownum in range(1, sheet.nrows):
|
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
|
return res
|
||||||
|
|
||||||
def _from_csv(self, result_set, conversion_rules):
|
def _from_csv(self, result_set, conversion_rules):
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011 Akretion
|
||||||
# © 2011 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
import datetime
|
import datetime
|
||||||
from .file_parser import FileParser
|
from .file_parser import FileParser, float_or_zero
|
||||||
from openerp.addons.account_move_base_import.parser.file_parser import (
|
from odoo.tools import ustr
|
||||||
float_or_zero
|
|
||||||
)
|
|
||||||
from openerp.tools import ustr
|
|
||||||
|
|
||||||
|
|
||||||
class GenericFileParser(FileParser):
|
class GenericFileParser(FileParser):
|
||||||
|
@ -27,7 +23,7 @@ class GenericFileParser(FileParser):
|
||||||
}
|
}
|
||||||
# set self.env for later ORM searches
|
# set self.env for later ORM searches
|
||||||
self.env = journal.env
|
self.env = journal.env
|
||||||
super(GenericFileParser, self).__init__(
|
super().__init__(
|
||||||
journal, ftype=ftype,
|
journal, ftype=ftype,
|
||||||
extra_fields=conversion_dict,
|
extra_fields=conversion_dict,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011 Akretion
|
||||||
# © 2011 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
import base64
|
import base64
|
||||||
import csv
|
import csv
|
||||||
|
@ -21,9 +20,9 @@ def UnicodeDictReader(utf8_data, **kwargs):
|
||||||
dialect = kwargs.pop('dialect')
|
dialect = kwargs.pop('dialect')
|
||||||
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
|
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
|
||||||
for row in csv_reader:
|
for row in csv_reader:
|
||||||
yield dict([(unicode(key or '', 'utf-8'),
|
yield dict([(str(key or ''),
|
||||||
unicode(value or '', 'utf-8'))
|
str(value or ''))
|
||||||
for key, value in row.iteritems()])
|
for key, value in row.items()])
|
||||||
|
|
||||||
|
|
||||||
class AccountMoveImportParser(object):
|
class AccountMoveImportParser(object):
|
||||||
|
|
|
@ -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>
|
|
@ -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.
|
|
@ -0,0 +1 @@
|
||||||
|
* As for now, the module does not handle multicurrency imports.
|
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB |
|
@ -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")
|
|
|
@ -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')
|
|
|
@ -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
|
|
|
@ -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')
|
|
|
@ -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'
|
|
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011 Akretion
|
||||||
# © 2011 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
from . import test_base_completion
|
from . import test_base_completion
|
||||||
from . import test_base_import
|
from . import test_base_import
|
||||||
|
from . import test_invoice
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
from odoo import fields, tools
|
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 odoo.tests import common
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
@ -36,10 +35,10 @@ NAMES_COMPLETION_CASES = [
|
||||||
class BaseCompletion(common.TransactionCase):
|
class BaseCompletion(common.TransactionCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BaseCompletion, self).setUp()
|
super().setUp()
|
||||||
tools.convert_file(self.cr, 'account',
|
tools.convert_file(self.cr, 'account',
|
||||||
get_module_resource('account', 'test',
|
get_resource_path('account', 'test',
|
||||||
'account_minimal_test.xml'),
|
'account_minimal_test.xml'),
|
||||||
{}, 'init', False, 'test')
|
{}, 'init', False, 'test')
|
||||||
self.account_move_obj = self.env["account.move"]
|
self.account_move_obj = self.env["account.move"]
|
||||||
self.account_move_line_obj = \
|
self.account_move_line_obj = \
|
||||||
|
@ -82,13 +81,13 @@ class BaseCompletion(common.TransactionCase):
|
||||||
"Partner_id must be blank before completion")
|
"Partner_id must be blank before completion")
|
||||||
self.move.button_auto_completion()
|
self.move.button_auto_completion()
|
||||||
if case.should_match:
|
if case.should_match:
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
self.partner, self.move_line.partner_id,
|
self.partner, self.move_line.partner_id,
|
||||||
"Missing expected partner id after completion "
|
"Missing expected partner id after completion "
|
||||||
"(partner_name: %s, line_name: %s)" %
|
"(partner_name: %s, line_name: %s)" %
|
||||||
(case.partner_name, case.line_label))
|
(case.partner_name, case.line_label))
|
||||||
else:
|
else:
|
||||||
self.assertNotEquals(
|
self.assertNotEqual(
|
||||||
self.partner, self.move_line.partner_id,
|
self.partner, self.move_line.partner_id,
|
||||||
"Partner id should be empty after completion "
|
"Partner id should be empty after completion "
|
||||||
"(partner_name: %s, line_name: %s)"
|
"(partner_name: %s, line_name: %s)"
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
import base64
|
import base64
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
from odoo import tools
|
from odoo import tools
|
||||||
from odoo.modules import get_module_resource
|
from odoo.modules import get_resource_path
|
||||||
|
|
||||||
|
|
||||||
class TestCodaImport(common.TransactionCase):
|
class TestCodaImport(common.TransactionCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCodaImport, self).setUp()
|
super().setUp()
|
||||||
self.company_a = self.browse_ref('base.main_company')
|
self.company_a = self.browse_ref('base.main_company')
|
||||||
tools.convert_file(self.cr, 'account',
|
tools.convert_file(self.cr, 'account',
|
||||||
get_module_resource('account', 'test',
|
get_resource_path('account', 'test',
|
||||||
'account_minimal_test.xml'),
|
'account_minimal_test.xml'),
|
||||||
{}, 'init', False, 'test')
|
{}, 'init', False, 'test')
|
||||||
self.account_move_obj = self.env["account.move"]
|
self.account_move_obj = self.env["account.move"]
|
||||||
self.account_move_line_obj = self.env["account.move.line"]
|
self.account_move_line_obj = self.env["account.move.line"]
|
||||||
|
@ -37,15 +35,11 @@ class TestCodaImport(common.TransactionCase):
|
||||||
'create_counterpart': True,
|
'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):
|
def _import_file(self, file_name):
|
||||||
""" import a file using the wizard
|
""" import a file using the wizard
|
||||||
return the create account.bank.statement object
|
return the create account.bank.statement object
|
||||||
"""
|
"""
|
||||||
with open(file_name) as f:
|
with open(file_name, 'rb') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
self.wizard = self.import_wizard_obj.create({
|
self.wizard = self.import_wizard_obj.create({
|
||||||
"journal_id": self.journal.id,
|
"journal_id": self.journal.id,
|
||||||
|
@ -58,16 +52,18 @@ class TestCodaImport(common.TransactionCase):
|
||||||
def test_simple_xls(self):
|
def test_simple_xls(self):
|
||||||
"""Test import from xls
|
"""Test import from xls
|
||||||
"""
|
"""
|
||||||
file_name = self._filename_to_abs_filename(
|
file_name = get_resource_path(
|
||||||
os.path.join("..", "data", "statement.xls"))
|
'account_move_base_import', 'demo', 'statement.xls'
|
||||||
|
)
|
||||||
move = self._import_file(file_name)
|
move = self._import_file(file_name)
|
||||||
self._validate_imported_move(move)
|
self._validate_imported_move(move)
|
||||||
|
|
||||||
def test_simple_csv(self):
|
def test_simple_csv(self):
|
||||||
"""Test import from csv
|
"""Test import from csv
|
||||||
"""
|
"""
|
||||||
file_name = self._filename_to_abs_filename(
|
file_name = get_resource_path(
|
||||||
os.path.join("..", "data", "statement.csv"))
|
'account_move_base_import', 'demo', 'statement.csv'
|
||||||
|
)
|
||||||
move = self._import_file(file_name)
|
move = self._import_file(file_name)
|
||||||
self._validate_imported_move(move)
|
self._validate_imported_move(move)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
)
|
|
@ -8,7 +8,7 @@
|
||||||
<field name="journal_id" position="after">
|
<field name="journal_id" position="after">
|
||||||
<field name="used_for_completion" invisible="1"/>
|
<field name="used_for_completion" invisible="1"/>
|
||||||
</field>
|
</field>
|
||||||
<button name="button_cancel" position="after">
|
<button name="post" position="after">
|
||||||
<button name="button_auto_completion"
|
<button name="button_auto_completion"
|
||||||
string="Auto Completion"
|
string="Auto Completion"
|
||||||
type="object"
|
type="object"
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import import_statement
|
from . import import_statement
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2011-2016 Akretion
|
||||||
# © 2011-2016 Akretion
|
# Copyright 2011-2019 Camptocamp SA
|
||||||
# © 2011-2016 Camptocamp SA
|
# Copyright 2013 Savoir-faire Linux
|
||||||
# © 2013 Savoir-faire Linux
|
# Copyright 2014 ACSONE SA/NV
|
||||||
# © 2014 ACSONE SA/NV
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
"""
|
"""
|
||||||
Wizard to import financial institute date in bank statement
|
Wizard to import financial institute date in bank statement
|
||||||
|
@ -19,8 +18,8 @@ class CreditPartnerStatementImporter(models.TransientModel):
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def default_get(self, fields):
|
def default_get(self, fields):
|
||||||
ctx = self._context
|
res = super().default_get(fields)
|
||||||
res = {}
|
ctx = self.env.context
|
||||||
if (
|
if (
|
||||||
ctx.get('active_model') == 'account.journal' and
|
ctx.get('active_model') == 'account.journal' and
|
||||||
ctx.get('active_ids')):
|
ctx.get('active_ids')):
|
||||||
|
|
Loading…
Reference in New Issue