Files

361 lines
17 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import json
import logging
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.exceptions import UserError
from odoo.tests import tagged
from odoo import Command, tools
_logger = logging.getLogger(__name__)
@tagged('post_install_l10n', 'post_install', '-at_install')
class AccountTestFecImport(AccountTestInvoicingCommon):
""" Main test class for Account FEC import testing """
# ----------------------------------------
# 1:: Test class body
# ----------------------------------------
@classmethod
@AccountTestInvoicingCommon.setup_country('fr')
def setUpClass(cls):
super().setUpClass()
# Export company
cls.company_data_2 = cls.setup_other_company(vat='FR15437982937')
cls.company_export = cls.company_data_2['company']
# Export records
cls.env['account.move'].with_company(cls.company_export).create([
{
'name': 'INV/001/123456',
'date': datetime.date(2010, 1, 1),
'invoice_date': datetime.date(2010, 1, 1),
'move_type': 'entry',
'partner_id': cls.partner_a.id,
'journal_id': cls.company_data_2['default_journal_sale'].id,
'line_ids': [
Command.create({
'name': 'line-1',
'account_id': cls.company_data_2['default_account_receivable'].id,
'credit': 0.0,
'debit': 100.30,
}),
Command.create({
'name': 'line-2',
'account_id': cls.company_data_2['default_account_tax_sale'].id,
'credit': 100.30,
'debit': 0.0,
}),
],
}, {
'name': 'INV/001/123457',
'move_type': 'entry',
'date': datetime.date(2010, 1, 1),
'invoice_date': datetime.date(2010, 1, 1),
'partner_id': cls.partner_b.id,
'journal_id': cls.company_data_2['default_journal_purchase'].id,
'line_ids': [
Command.create({
'name': 'line-3',
'account_id': cls.company_data_2['default_account_payable'].id,
'credit': 65.15,
'debit': 0.0,
}),
Command.create({
'name': 'line-4',
'account_id': cls.company_data_2['default_account_expense'].id,
'credit': 0.0,
'debit': 65.15,
}),
],
},
])
def _import_fec_file(self, file_name, company=None, **kwargs):
company = company or self.env.company
wizard = self.env['account.fec.import.wizard'].with_company(company).create({**kwargs})
with tools.file_open(f'l10n_fr_fec_import/static/tests/fec_test_files/{file_name}', mode='rb') as raw_file:
decoded_file = raw_file.read().decode()
json_content = json.loads(decoded_file)
header = json_content['header']
chunks = json_content['chunks']
for chunk in chunks:
wizard.process_chunk(header, chunk)
return wizard
# ----------------------------------------
# 2:: Test methods
# ----------------------------------------
def test_fec_import_ignores_empty_partner_refs(self):
""" Ensure that FEC import does not assign a partner when no reference is provided. """
self.env['res.partner'].create({'name': 'Partner A', 'ref': '', 'company_id': self.company.id})
self._import_fec_file('fec_lf_tab_utf8.json')
move = self.env['account.move'].search([('company_id', '=', self.company.id), ('name', '=', 'ACH000001')])
self.assertFalse(move.mapped('line_ids.partner_id'))
self.assertFalse(move.partner_id)
def test_import_fec_partners_no_duplicate(self):
"""
Test that the partners are not imported from the FEC file if already existing
But that if two partners have the same name but a different ref, they are correctly created and linked
"""
partner_1 = self.env['res.partner'].create({
'name': 'PARTNER 01',
'ref': 'PARTNER01'
})
self._import_fec_file('fec_lf_tab_utf8.json')
partners_1 = self.env['res.partner'].search_count([('ref', '=', partner_1.ref)])
self.assertEqual(partners_1, 1)
partners_2 = self.env['res.partner'].search([('ref', '=', 'PARTNER02')])
self.assertEqual(len(partners_2), 1)
domain = [('company_id', '=', self.company.id), ('move_name', '=', 'ACH000007')]
move_lines = self.env['account.move.line'].search(domain, order='move_name, id')
self.assertEqual(move_lines.partner_id, partners_2)
def test_import_fec_big_file(self):
""" Test that FEC import works fine with larger files """
last = self.env['account.move'].search([], order='id desc', limit=1)
self._import_fec_file('fec_crlf_tab_utf8bom_large.json')
new = self.env['account.move'].search([('id', '>', last.id)])
# We delay one move to be sure the FEC matching number is not lost
# when performing the first posting/auto matching
delayed_move = self.env['account.move.line'].search([('matching_number', '=', 'IAA')], limit=1).move_id
(new - delayed_move).action_post()
delayed_move.action_post()
# Verify move_lines presence
move_names = ('ACH000001', 'ACH000002', 'ACH000003')
domain = [('company_id', '=', self.company.id), ('move_name', 'in', move_names)]
move_lines = self.env['account.move.line'].search(domain, order='move_name, id')
self.assertEqual(9, len(move_lines))
# Verify Reconciliation
domain = [('company_id', '=', self.company.id), ('reconciled', '=', True)]
move_lines = self.env['account.move.line'].search(domain)
self.assertEqual(256, len(move_lines))
# Verify Full Reconciliation
domain = [('company_id', '=', self.company.id), ('full_reconcile_id', '!=', False)]
move_lines = self.env['account.move.line'].search(domain)
self.assertEqual(256, len(move_lines))
# Verify Journal types
domain = [('company_id', '=', self.company.id), ('name', '=', 'FEC-BQ 552')]
journal = self.env['account.journal'].search(domain)
self.assertEqual(journal.type, 'bank')
def test_import_fec_export(self):
""" Test that imports the results of a FEC export """
# Export Wizard ----------------------------------------
export_wizard = self.env['l10n_fr.fec.export.wizard'].with_company(self.company_export).create([{
'date_from': datetime.date(1990, 1, 1),
'date_to': datetime.date.today(),
'test_file': True,
'export_type': 'nonofficial'
}])
# Generate the FEC content with the export wizard
def split_fields(csv_content):
lines = csv_content.split('\r\n')
fields = [line.split('|') for line in lines]
# We remove the 'CompAuxNum' @6 because we use partner.id in it
# and we don't care about comparing partner.id
fields[1:] = [[*field[:6], '', *field[7:]] for field in fields[1:]]
return fields
fec_stream = export_wizard.with_context(fec_test_mode=True).generate_fec()['file_content']
content = b''.join(fec_stream).decode()
exported_content = split_fields(content)
with tools.file_open('l10n_fr_fec_import/static/tests/fec_test_files/fec_crlf_pipe_utf8_export.txt', mode='rb') as raw_file:
expected_export = split_fields(raw_file.read().decode())
self.assertEqual(exported_content, expected_export)
# Importing the fec file
self._import_fec_file('fec_crlf_pipe_utf8_export.json')
# Verify moves data
new_moves = self.env['account.move'].search([
('company_id', '=', self.company_export.id),
('closing_return_id', '=', False), # exclude automatic tax closing entries
], order="name")
columns = ['company_id', 'name', 'journal_id', 'partner_id', 'date']
moves_data = [
(self.company_export.id, 'INV/001/123456', self.company_data_2['default_journal_sale'].id, self.partner_a.id, datetime.date(2010, 1, 1)),
(self.company_export.id, 'INV/001/123457', self.company_data_2['default_journal_purchase'].id, self.partner_b.id, datetime.date(2010, 1, 1)),
]
expected_values = [dict(zip(columns, move_data)) for move_data in moves_data]
self.assertRecordValues(new_moves, expected_values)
# Verify moves lines data
columns = ['company_id', 'name', 'credit', 'debit', 'account_id']
lines_data = [
(self.company_export.id, 'line-1', 0.00, 100.30, self.company_data_2['default_account_receivable'].id),
(self.company_export.id, 'line-2', 100.30, 0.00, self.company_data_2['default_account_tax_sale'].id),
(self.company_export.id, 'line-3', 65.15, 0.00, self.company_data_2['default_account_payable'].id),
(self.company_export.id, 'line-4', 0.00, 65.15, self.company_data_2['default_account_expense'].id),
]
expected_values = [dict(zip(columns, line_data)) for line_data in lines_data]
new_lines = new_moves.mapped("line_ids").sorted(key=lambda x: x.name)
self.assertRecordValues(new_lines, expected_values)
def test_balance_moves_by_day(self):
""" Test that the imbalanced moves are correctly balanced with a grouping by day """
self._import_fec_file('fec_cr_tab_utf8_imbalanced_day.json')
domain = [('company_id', '=', self.company.id), ('move_name', 'in', ('ACH/20180808', 'ACH/20180910'))]
move_lines = self.env['account.move.line'].search(domain, order='move_name,name')
self.assertEqual(
move_lines.mapped(lambda line: (line.move_name, line.name)),
[
('ACH/20180808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/20180808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/20180808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/20180808', 'DOMICILIATION'),
('ACH/20180808', 'DOMICILIATION'),
('ACH/20180808', 'DOMICILIATION'),
('ACH/20180910', 'ASSURANCE'),
('ACH/20180910', 'ASSURANCE'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
])
def test_balance_moves_by_month(self):
""" Test that the imbalanced moves are correctly balanced with a grouping by month """
self._import_fec_file('fec_lf_tab_utf8_imbalanced_month.json')
domain = [('company_id', '=', self.company.id), ('move_name', 'in', ('ACH/201808', 'ACH/20180910'))]
move_lines = self.env['account.move.line'].search(domain, order='move_name,name')
self.assertEqual(
move_lines.mapped(lambda line: (line.move_name, line.name)),
[
('ACH/201808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/201808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/201808', 'ADVANCE PAYMENT COMPANY FORMALITIES'),
('ACH/201808', 'DOMICILIATION'),
('ACH/201808', 'DOMICILIATION'),
('ACH/201808', 'DOMICILIATION'),
('ACH/20180910', 'ASSURANCE'),
('ACH/20180910', 'ASSURANCE'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
('ACH/20180910', 'PARTNER 01'),
])
def test_unbalanceable_moves(self):
""" Test that the imbalanced moves raise as they cannot be balanced by day/month """
with self.assertRaises(UserError):
self._import_fec_file('fec_lf_tab_utf8_imbalanced.json')
def test_positive_montant_devise(self):
"""
Test that it doesn't fail even when the MontantDevise is not signed, i.e. MontantDevise is positive even
when the line is credited, or the opposite case: MontantDevise is negative while the line is
debited.
"""
self._import_fec_file('fec_cr_tab_utf8bom.json')
def test_fec_import_multicompany(self):
self._import_fec_file('fec_lf_tab_utf8.json')
fr_company2 = self.setup_other_company(name="Company FR 2")['company']
self._import_fec_file('fec_lf_tab_utf8.json', company=fr_company2)
def test_fec_import_reconciliation(self):
last = self.env['account.move'].search([], order='id desc', limit=1)
self._import_fec_file('fec_crlf_tab_utf8.json')
new = self.env['account.move'].search([('id', '>', last.id)])
self.assertEqual(len(new), 4)
self.assertFalse(new.line_ids.full_reconcile_id, "Reconciliation is only temporary before posting")
new.action_post()
self.assertEqual(len(new.line_ids.full_reconcile_id), 2, "It is fully reconciled after posting")
def test_key_is_empty(self):
with self.assertRaisesRegex(UserError, "journal not found"):
self._import_fec_file('fec_lf_tab_utf8-2.json')
def test_no_update_journal(self):
"""Test that existing journal type is not updated when importing FEC file."""
self._import_fec_file('fec_lf_tab_utf8.json')
purchase_journal = self.env['account.journal'].search([('code', '=', 'ACH')])
self.assertEqual(purchase_journal.type, 'general')
purchase_journal.type = 'purchase'
self._import_fec_file('fec_lf_tab_utf8.json')
self.assertEqual(purchase_journal.type, 'purchase')
def test_created_account_translation(self):
self.env['res.lang']._activate_lang('fr_FR')
self.env['account.account'].search([('name', '=', 'Subscribed capital - uncalled')]).unlink()
self._import_fec_file('fec_lf_pipe_iso.json')
account = self.env['account.account'].search([('name', '=', 'Capital')]).with_context(lang="fr_FR")
self.assertEqual(account.name, 'Capital souscrit - non appelé')
def test_currency_rounding_with_decimal_values(self):
"""
Test that it doesn't fail even when the currency has a rounding of 1, and the lines include values with decimal points.
"""
self.env["res.currency"].create({"name": "xxx", "symbol": "X", "rounding": 1})
self._import_fec_file('fec_lf_tab_utf8-3.json')
def test_import_wizard_fields(self):
last = self.env['account.move.line'].search([], order='id desc', limit=1)
import_summary = self._import_fec_file('fec_lf_tab_utf8.json').import_summary_id
self.assertEqual(import_summary.import_summary_have_data, True)
self.assertNotEqual(last, self.env['account.move.line'].search([], order='id desc', limit=1))
last = self.env['account.move.line'].search([], order='id desc', limit=1)
import_summary = self._import_fec_file('fec_lf_tab_utf8.json', duplicate_documents_handling='ignore').import_summary_id
self.assertEqual(import_summary.import_summary_have_data, False)
self.assertEqual(last, self.env['account.move.line'].search([], order='id desc', limit=1))
last = self.env['account.move.line'].search([], order='id desc', limit=1)
import_summary = self._import_fec_file('fec_lf_tab_utf8.json').import_summary_id
self.assertEqual(import_summary.import_summary_have_data, True)
self.assertNotEqual(last, self.env['account.move.line'].search([], order='id desc', limit=1))
last = self.env['account.move.line'].search([], order='id desc', limit=1)
import_summary = self._import_fec_file('fec_lf_tab_utf8.json', duplicate_documents_handling='ignore', document_prefix='PRE').import_summary_id
self.assertEqual(import_summary.import_summary_have_data, True)
self.assertNotEqual(last, self.env['account.move.line'].search([], order='id desc', limit=1))
last = self.env['account.move.line'].search([], order='id desc', limit=1)
import_summary = self._import_fec_file('fec_lf_tab_utf8.json', duplicate_documents_handling='ignore', document_prefix='PRE').import_summary_id
self.assertEqual(import_summary.import_summary_have_data, False)
self.assertEqual(last, self.env['account.move.line'].search([], order='id desc', limit=1))
def test_account_name(self):
"""
Template should not override the account's name given by the user
We defined
ACH\tACHATS\tACH000001\t20180808\t45500000\tAssociate's current account\t\t\t1\t20180808\tADVANCE PAYMENT COMPANY FORMALITIES\t0,00\t600,00\t\t\t20190725\t\t
We should retrieve the name "Associate's current account"
"""
self._import_fec_file('fec_lf_tab_utf8_imbalanced_month.json')
account = self.env['account.account'].search([('code', '=', '455000')])
self.assertEqual(account.name, "Associate's current account")