forked from Mapan/odoo17e
501 lines
27 KiB
Python
501 lines
27 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import base64
|
|
import datetime
|
|
from collections import defaultdict
|
|
from lxml import etree
|
|
|
|
from odoo import Command, fields, models, _
|
|
from odoo.exceptions import RedirectWarning
|
|
|
|
|
|
class SaftImportWizard(models.TransientModel):
|
|
""" SAF-T import wizard is the main class to import SAF-T files. """
|
|
|
|
_name = "account.saft.import.wizard"
|
|
_description = "Account SAF-T import wizard"
|
|
|
|
attachment_name = fields.Char(string="Filename")
|
|
attachment_id = fields.Binary(string="File", required=True, help="Accounting SAF-T data file to be imported")
|
|
company_id = fields.Many2one(comodel_name="res.company", string="Company", help="Company used for the import", default=lambda self: self.env.company, required=True, readonly=True)
|
|
import_opening_balance = fields.Boolean(string="Import account opening balances")
|
|
|
|
# ------------------------------------
|
|
# Utility
|
|
# ------------------------------------
|
|
|
|
def _get_account_types(self):
|
|
""" Returns a mapping between the account types accepted for the SAF-T and the types in Odoo """
|
|
# To be overriden
|
|
return {}
|
|
|
|
def _make_xml_id(self, prefix, key):
|
|
# To be overriden
|
|
if '_' in prefix:
|
|
raise ValueError('`prefix` cannot contain an underscore')
|
|
key = key.replace(' ', '_')
|
|
return f"l10n_{self.company_id.country_code.lower()}_saft_import.{self.company_id.id}_{prefix}_{key}"
|
|
|
|
def _get_cleaned_namespace(self, saft):
|
|
""" Helper that returns the cleaned version of tha namespace. As-is, aft.nsmap cannot be used as there is
|
|
a None key that raises an error (lxml is XPATH 1.0 only)
|
|
"""
|
|
nsmap = dict(saft.nsmap)
|
|
nsmap_key = None
|
|
for key, ns in nsmap.items():
|
|
if ns.startswith('urn:StandardAuditFile-Taxation-Financial'):
|
|
nsmap_key = key
|
|
break
|
|
nsmap['saft'] = nsmap[nsmap_key]
|
|
del nsmap[nsmap_key]
|
|
return nsmap
|
|
|
|
# ------------------------------------
|
|
# Reading
|
|
# ------------------------------------
|
|
|
|
def _prepare_account_data(self, tree):
|
|
""" Extracts the data on accounts to create missing ones and give the mappings used for transactions
|
|
|
|
:param tree: tree of the xml file
|
|
:returns: accounts_to_create: values for the accounts to create
|
|
:returns: account_mapping_ids: mapping between ids coming from the SAF-T with the ones from Odoo and the balance
|
|
"""
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
template_data = self.env['account.chart.template']._get_chart_template_data(self.company_id.chart_template).get('template_data')
|
|
digits = int(template_data.get('code_digits', 6))
|
|
|
|
existing_accounts = self.env['account.account'].search_fetch(
|
|
self.env['account.account']._check_company_domain(self.company_id),
|
|
field_names=['id', 'code'],
|
|
)
|
|
existing_accounts_code = {account.code: account.id for account in existing_accounts}
|
|
account_types = self._get_account_types()
|
|
accounts_to_create = {}
|
|
account_mapping_ids = {}
|
|
for element_account in tree.findall('.//saft:Account', namespaces=nsmap):
|
|
account_id = element_account.find('saft:AccountID', namespaces=nsmap).text
|
|
account_code = element_account.find('saft:StandardAccountID', namespaces=nsmap).text
|
|
account_code = account_code[:digits] + account_code[digits:].rstrip('0')
|
|
account_opening_debit = element_account.find('saft:OpeningDebitBalance', namespaces=nsmap)
|
|
account_opening_credit = element_account.find('saft:OpeningCreditBalance', namespaces=nsmap)
|
|
account_mapping_ids[account_id] = {
|
|
'balance': float(account_opening_debit.text) if account_opening_debit is not None else - float(account_opening_credit.text),
|
|
}
|
|
if account_code in existing_accounts_code:
|
|
account_mapping_ids[account_id].update({'id': existing_accounts_code[account_code]})
|
|
else:
|
|
account_type = element_account.find('saft:AccountType', namespaces=nsmap)
|
|
name = element_account.find('saft:AccountDescription', namespaces=nsmap).text
|
|
xml_id = self._make_xml_id('account', account_code)
|
|
accounts_to_create[xml_id] = {
|
|
'company_id': self.company_id.id,
|
|
'code': account_code,
|
|
'account_type': account_types.get(account_type, 'asset_current'),
|
|
'name': name,
|
|
}
|
|
|
|
existing_accounts_code[account_code] = xml_id
|
|
account_mapping_ids[account_id].update({'id': xml_id})
|
|
return accounts_to_create, account_mapping_ids
|
|
|
|
def _prepare_opening_balance_move(self, tree, map_accounts):
|
|
""" Create a move if there is inconsistency between opening balance for each account and amls in Odoo
|
|
|
|
:param tree: tree of the xml file
|
|
:param map_accounts: dict containing balance and id values for each code
|
|
:returns: dict values for the creation of the opening balance move
|
|
"""
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
selection_start_node = tree.find('.//saft:SelectionStartDate', namespaces=nsmap)
|
|
if selection_start_node is not None:
|
|
start_date = fields.Date.to_date(selection_start_node.text)
|
|
else:
|
|
period_start_node = tree.find('.//saft:PeriodStart', namespaces=nsmap)
|
|
period_start_year_node = tree.find('.//saft:PeriodStartYear', namespaces=nsmap)
|
|
start_date = datetime.date(int(period_start_year_node.text), int(period_start_node.text), 1)
|
|
|
|
default_currency_code = tree.find('.//saft:DefaultCurrencyCode', namespaces=nsmap)
|
|
currency = self.env['res.currency'].with_context(active_test=False).search([('name', '=', default_currency_code.text)])
|
|
|
|
account_diff_balance = {}
|
|
self._cr.execute("""
|
|
SELECT account.id,
|
|
SUM(aml.balance) AS balance
|
|
FROM account_move_line AS aml
|
|
LEFT JOIN account_account AS account
|
|
ON aml.account_id = account.id
|
|
WHERE aml.company_id = %s
|
|
AND aml.date < %s
|
|
and aml.parent_state != 'cancel'
|
|
GROUP BY account.id
|
|
""", [self.company_id.id, start_date])
|
|
existing_balances = defaultdict(dict)
|
|
for balance_row in self._cr.dictfetchall():
|
|
existing_balances[balance_row['id']] = balance_row['balance']
|
|
|
|
for account_dict in map_accounts.values():
|
|
if currency.compare_amounts(account_dict['balance'], existing_account_balance := existing_balances.get(account_dict['id'], 0)) != 0:
|
|
account_diff_balance[account_dict['id']] = account_dict['balance'] - existing_account_balance
|
|
|
|
journal_misc = self.env['account.journal'].search([*self.env['account.journal']._check_company_domain(self.company_id), ('type', '=', 'general')], limit=1)
|
|
|
|
lines_data = []
|
|
for account_id, balance in account_diff_balance.items():
|
|
lines_data.append(
|
|
Command.create({
|
|
'account_id': account_id,
|
|
'currency_id': currency.id,
|
|
'amount_currency': balance,
|
|
'date': fields.Date.today(),
|
|
'journal_id': journal_misc.id,
|
|
})
|
|
)
|
|
if lines_data:
|
|
xml_id = self._make_xml_id('move', f'opening_balance{start_date}')
|
|
return {
|
|
xml_id: {
|
|
'date': fields.Date.today(),
|
|
'ref': _('SAF-T opening balance move'),
|
|
'journal_id': journal_misc.id,
|
|
'partner_id': None,
|
|
'company_id': self.company_id.id,
|
|
'currency_id': currency.id,
|
|
'move_type': 'entry',
|
|
'line_ids': lines_data,
|
|
}
|
|
}
|
|
|
|
def _prepare_partner_data(self, tree):
|
|
""" Extracts the data on partners to create them. Those with the same name and saft_id won't be re-imported
|
|
|
|
:param tree: tree of the xml file
|
|
:returns: partners_to_create: values for the partners to create
|
|
:returns: partner_mapping_ids: mapping between ids coming from the SAF-T with the ones from Odoo
|
|
"""
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
partners_to_create = {}
|
|
existing_partners = self.env['res.partner'].search_fetch(
|
|
self.env['account.account']._check_company_domain(self.company_id),
|
|
field_names=['id', 'name', 'vat'],
|
|
)
|
|
existing_partners_mapping = {(part.name, part.vat): part.id for part in existing_partners}
|
|
partner_mapping_ids = {}
|
|
|
|
element_customers_node = tree.find('.//saft:Customers', namespaces=nsmap)
|
|
customers_node = element_customers_node.findall('.//saft:Customer', namespaces=nsmap) if element_customers_node is not None else []
|
|
element_suppliers_node = tree.find('.//saft:Suppliers', namespaces=nsmap)
|
|
suppliers_node = element_suppliers_node.findall('.//saft:Supplier', namespaces=nsmap) if element_suppliers_node is not None else []
|
|
|
|
for element_partner in customers_node + suppliers_node:
|
|
partner_name = element_partner.find('saft:Name', namespaces=nsmap).text
|
|
partner_vat = element_partner.find('.//saft:TaxRegistrationNumber', namespaces=nsmap)
|
|
partner_vat = partner_vat.text if partner_vat is not None else False
|
|
partner_id = element_partner.find('saft:CustomerID', namespaces=nsmap).text if element_partner.tag == ('{%s}Customer' % nsmap['saft']) else element_partner.find('saft:SupplierID', namespaces=nsmap).text
|
|
|
|
if (partner_name, partner_vat) in existing_partners_mapping:
|
|
partner_mapping_ids[partner_id] = existing_partners_mapping.get((partner_name, partner_vat))
|
|
continue
|
|
|
|
child_partners = []
|
|
for contact_node in element_partner.findall('saft:Contact', namespaces=nsmap):
|
|
first_name = contact_node.find('.//saft:FirstName', namespaces=nsmap)
|
|
last_name = contact_node.find('.//saft:LastName', namespaces=nsmap)
|
|
telephone = contact_node.find('.//saft:Telephone', namespaces=nsmap)
|
|
email = contact_node.find('.//saft:Email', namespaces=nsmap)
|
|
mobile = contact_node.find('.//saft:MobilePhone', namespaces=nsmap)
|
|
|
|
child_partner_name = last_name.text if last_name is not None else ''
|
|
if first_name is not None and first_name.text != 'NotUsed':
|
|
child_partner_name = f'{first_name.text} {child_partner_name}'.strip()
|
|
|
|
child_partners.append(
|
|
Command.create({
|
|
'name': child_partner_name,
|
|
'type': 'contact',
|
|
**({'phone': telephone.text} if telephone is not None else {}),
|
|
**({'email': email.text} if email is not None else {}),
|
|
**({'mobile': mobile.text} if mobile is not None else {}),
|
|
'company_id': self.company_id.id,
|
|
})
|
|
)
|
|
|
|
address_node = element_partner.find('saft:Address', namespaces=nsmap)
|
|
partner_street = address_node.find('saft:StreetName', namespaces=nsmap)
|
|
partner_street2 = address_node.find('saft:AdditionalAddressDetail', namespaces=nsmap)
|
|
partner_city = address_node.find('saft:City', namespaces=nsmap)
|
|
partner_zip = address_node.find('saft:PostalCode', namespaces=nsmap)
|
|
partner_country_code = address_node.find('saft:Country', namespaces=nsmap)
|
|
|
|
xml_id = self._make_xml_id('partner', f'{partner_name}_{partner_vat}')
|
|
partners_to_create[xml_id] = {
|
|
'company_id': self.company_id.id,
|
|
'vat': partner_vat,
|
|
'name': partner_name,
|
|
**({'street': partner_street.text} if partner_street is not None else {}),
|
|
**({'street2': partner_street2.text} if partner_street2 is not None else {}),
|
|
**({'city': partner_city.text} if partner_city is not None else {}),
|
|
**({'zip': partner_zip.text} if partner_zip is not None else {}),
|
|
**({'country_code': partner_country_code.text} if partner_country_code is not None else {}),
|
|
'child_ids': child_partners,
|
|
}
|
|
existing_partners_mapping[(partner_name, partner_vat)] = xml_id
|
|
partner_mapping_ids[partner_id] = xml_id
|
|
|
|
return partners_to_create, partner_mapping_ids
|
|
|
|
def _prepare_tax_data(self, tree):
|
|
""" Extracts the data on taxes to create them.
|
|
Those with the same name, amount_type and amount won't be imported
|
|
|
|
:param tree: tree of the xml file
|
|
:returns: taxes_to_create: values for the taxes to create
|
|
:returns: tax_mapping_ids: mapping between ids coming from the SAF-T with the ones used in Odoo
|
|
"""
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
existing_taxes = self.env['account.tax'].search_fetch(
|
|
self.env['account.tax']._check_company_domain(self.company_id),
|
|
field_names=['name', 'amount_type', 'amount', 'id'],
|
|
)
|
|
# map between name+amount_type+amount and id of a tax that corresponds
|
|
existing_taxes_mapping = {(tax.name, tax.amount_type, tax.amount): tax.id for tax in existing_taxes}
|
|
|
|
default_tax_group = self.env['account.tax.group'].search(
|
|
[*self.env['account.tax.group']._check_company_domain(self.company_id), ('name', '=', 'SAF-T taxes')],
|
|
)
|
|
tax_mapping_ids = {}
|
|
tax_to_create = {}
|
|
for tax_node in tree.findall('.//saft:TaxCodeDetails', namespaces=nsmap):
|
|
tax_code = tax_node.find('.//saft:TaxCode', namespaces=nsmap)
|
|
tax_description = tax_node.find('.//saft:Description', namespaces=nsmap)
|
|
tax_description = tax_description.text if tax_description is not None else False
|
|
tax_percentage = tax_node.find('.//saft:TaxPercentage', namespaces=nsmap)
|
|
tax_percentage = tax_percentage.text if tax_percentage is not None else 0
|
|
tax_flat_rate = tax_node.find('.//saft:TaxFlatRate', namespaces=nsmap)
|
|
tax_flat_rate = tax_flat_rate.text if tax_flat_rate is not None else 0
|
|
tax_mapping_key = (
|
|
tax_description,
|
|
('fixed' if tax_flat_rate else 'percent'),
|
|
float(tax_percentage) or float(tax_flat_rate),
|
|
)
|
|
|
|
if tax_mapping_key in existing_taxes_mapping:
|
|
tax_mapping_ids[tax_code.text] = existing_taxes_mapping[tax_mapping_key]
|
|
else:
|
|
if not default_tax_group:
|
|
# We only want to create it if it does not exist and if we have taxes to create
|
|
default_tax_group = self.env['account.tax.group'].create({
|
|
'name': 'SAF-T taxes',
|
|
'country_id': self.company_id.account_fiscal_country_id.id,
|
|
})
|
|
xml_id = self._make_xml_id('tax', '_'.join(str(elem) for elem in tax_mapping_key))
|
|
tax_to_create[xml_id] = {
|
|
'company_id': self.company_id.id,
|
|
'name': tax_mapping_key[0],
|
|
'amount_type': tax_mapping_key[1],
|
|
'amount': tax_mapping_key[2],
|
|
'country_id': self.company_id.account_fiscal_country_id.id,
|
|
'tax_group_id': default_tax_group.id,
|
|
}
|
|
tax_mapping_ids[tax_code.text] = xml_id
|
|
existing_taxes_mapping[(tax_mapping_key[0], tax_mapping_key[1], tax_mapping_key[2])] = xml_id
|
|
|
|
return tax_to_create, tax_mapping_ids
|
|
|
|
def _prepare_journal_data(self, tree, default_currency, map_accounts, map_taxes, map_currencies, map_partners):
|
|
""" Extracts the data on journals to create those missing (based on the code).
|
|
Then, for each journal, extract the moves associated
|
|
|
|
:param tree: tree of the xml file
|
|
:param default_currency: base currency defined in the SAF-T file
|
|
:param map_accounts: mapping between saft and odoo ids (and balance) for accounts
|
|
:param map_taxes: mapping between saft and odoo ids for taxes
|
|
:param map_currencies: mapping between saft and odoo ids for taxes
|
|
:param map_partners: mapping between saft and odoo ids for partners
|
|
|
|
:returns: journals_to_create: values for the journals to create
|
|
:returns: moves_to_create: values for the moves to create
|
|
"""
|
|
|
|
def _get_next_journal_code(previous_code):
|
|
""" Helper to generate new journal codes """
|
|
next_code_id = int(previous_code.replace('SAF', '0')) + 1
|
|
return f'SAF{next_code_id}'
|
|
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
journals_to_create = {}
|
|
moves_to_create = {}
|
|
existing_journal_xml_ids = self.env['account.journal'].search(self.env['account.journal']._check_company_domain(self.company_id))._get_external_ids()
|
|
existing_journal_xml_ids = {xml_id[0]: journal_id for journal_id, xml_id in existing_journal_xml_ids.items() if xml_id}
|
|
possible_journal_types = self.env['account.journal']._fields['type'].get_values(self.env)
|
|
|
|
journal_saft = self.env['account.journal'].search_fetch(
|
|
[*self.env['account.journal']._check_company_domain(self.company_id), ('code', 'like', 'SAF%')],
|
|
field_names=['code'],
|
|
order='code DESC',
|
|
limit=1,
|
|
)
|
|
journal_code = journal_saft.code or 'SAF0'
|
|
|
|
for element_journal in tree.findall('.//saft:Journal', namespaces=nsmap):
|
|
saft_journal_id = element_journal.find('saft:JournalID', namespaces=nsmap).text
|
|
name = element_journal.find('saft:Description', namespaces=nsmap).text
|
|
journal_type = element_journal.find('saft:Type', namespaces=nsmap)
|
|
journal_type = journal_type.text if journal_type is not None and journal_type.text in possible_journal_types else 'general'
|
|
xml_id = self._make_xml_id('journal', saft_journal_id)
|
|
journal_id = existing_journal_xml_ids.get(xml_id) or xml_id
|
|
if xml_id not in existing_journal_xml_ids:
|
|
journal_code = _get_next_journal_code(previous_code=journal_code)
|
|
journals_to_create[xml_id] = {
|
|
'company_id': self.company_id.id,
|
|
'name': name,
|
|
'code': journal_code,
|
|
'type': journal_type,
|
|
'alias_name': f"{name}-{journal_code}-{self.company_id.name}",
|
|
}
|
|
moves_to_create.update(self._prepare_move_data(element_journal, default_currency, saft_journal_id, journal_id, map_accounts, map_taxes, map_currencies, map_partners))
|
|
|
|
return journals_to_create, moves_to_create
|
|
|
|
def _prepare_move_data(self, journal_tree, default_currency, saft_journal_id, journal_id, map_accounts, map_taxes, map_currencies, map_partners):
|
|
""" Extracts the data on moves to be created. Those with the same name and journal won't be re-imported.
|
|
|
|
:param journal_tree: tree of the xml hierarchy for the journal concerned
|
|
:param default_currency: base currency defined in the SAF-T file
|
|
:param saft_journal_id: saft id of the journal
|
|
:param journal_id: odoo id of the journal
|
|
:param map_accounts: mapping between saft and odoo ids (and balance) for accounts
|
|
:param map_taxes: mapping between saft and odoo ids for taxes
|
|
:param map_currencies: mapping between saft and odoo ids for taxes
|
|
:param map_partners: mapping between saft and odoo ids for partners
|
|
|
|
:returns: moves_to_create: values for the moves to create
|
|
"""
|
|
nsmap = self._get_cleaned_namespace(journal_tree)
|
|
moves_to_create = {}
|
|
already_imported_move_xmlids = self.env['ir.model.data'].sudo().search_fetch(
|
|
[('name', 'like', f'{self.company_id.id}_move_{saft_journal_id}_'), ('model', '=', 'account.move')],
|
|
field_names=['module', 'name'],
|
|
)
|
|
already_imported_move_xmlids = [f'{move_data.module}.{move_data.name}' for move_data in already_imported_move_xmlids]
|
|
|
|
for move_node in journal_tree.findall('saft:Transaction', namespaces=nsmap):
|
|
move_date = move_node.find('./saft:TransactionDate', namespaces=nsmap)
|
|
move_name = move_node.find('./saft:Description', namespaces=nsmap)
|
|
move_customer = move_node.find('./saft:CustomerID', namespaces=nsmap)
|
|
move_supplier = move_node.find('./saft:SupplierID', namespaces=nsmap)
|
|
move_partner = move_customer.text if move_customer is not None else move_supplier.text if move_supplier is not None else None
|
|
xml_id = self._make_xml_id('move', f'{saft_journal_id}_{move_name.text}')
|
|
if xml_id in already_imported_move_xmlids:
|
|
continue
|
|
line_data = []
|
|
for line_node in move_node.findall('.//saft:Line', namespaces=nsmap):
|
|
line_account = line_node.find('saft:AccountID', namespaces=nsmap)
|
|
line_name = line_node.find('saft:Description', namespaces=nsmap)
|
|
line_debit = line_node.find('saft:DebitAmount', namespaces=nsmap)
|
|
line_credit = line_node.find('saft:CreditAmount', namespaces=nsmap)
|
|
line_amount = line_node.find('.//saft:Amount', namespaces=nsmap)
|
|
line_currency_code = line_node.find('.//saft:CurrencyCode', namespaces=nsmap)
|
|
line_currency_amount = line_node.find('.//saft:CurrencyAmount', namespaces=nsmap)
|
|
debit = float(line_amount.text) if line_debit is not None else 0
|
|
credit = float(line_amount.text) if line_credit is not None else 0
|
|
sign = 1 if debit - credit > 0 else -1
|
|
|
|
tax_ids = []
|
|
for tax_node in line_node.findall('saft:TaxInformation', namespaces=nsmap):
|
|
tax_code = tax_node.find('./saft:TaxCode', namespaces=nsmap)
|
|
tax_ids.append(map_taxes[tax_code.text])
|
|
|
|
line_data.append(
|
|
Command.create({
|
|
'account_id': map_accounts[line_account.text]['id'],
|
|
'debit': float(line_amount.text) if line_debit is not None else 0,
|
|
'credit': float(line_amount.text) if line_credit is not None else 0,
|
|
'name': line_name.text,
|
|
'currency_id': map_currencies[line_currency_code.text] if line_currency_code is not None else default_currency.id,
|
|
**({'amount_currency': float(line_currency_amount.text) * sign} if line_currency_amount is not None else {}),
|
|
'tax_ids': [Command.set(tax_ids)],
|
|
})
|
|
)
|
|
|
|
moves_to_create[xml_id] = {
|
|
'company_id': self.company_id.id,
|
|
'journal_id': journal_id,
|
|
'date': move_date.text,
|
|
**({'partner_id': map_partners[move_partner]} if move_partner else {}),
|
|
'name': move_name.text,
|
|
'line_ids': line_data,
|
|
}
|
|
return moves_to_create
|
|
|
|
def _get_data(self):
|
|
""" Returns the data that is stored inside the XML SAF-T attachment, for each model, to be loaded. """
|
|
import_data = base64.b64decode(self.attachment_id)
|
|
tree = etree.fromstring(import_data)
|
|
data = {}
|
|
account_data, map_accounts = self._prepare_account_data(tree)
|
|
data['account.account'] = account_data
|
|
|
|
tax_to_create, map_taxes = self._prepare_tax_data(tree)
|
|
data['account.tax'] = tax_to_create
|
|
|
|
partner_data, map_partners = self._prepare_partner_data(tree)
|
|
data['res.partner'] = partner_data
|
|
|
|
data['account.journal'] = {} # so journals are loaded before moves
|
|
if self.import_opening_balance:
|
|
data['account.move'] = self._prepare_opening_balance_move(tree, map_accounts)
|
|
else:
|
|
data['account.move'] = {}
|
|
|
|
nsmap = self._get_cleaned_namespace(tree)
|
|
default_currency_code = tree.find('.//saft:DefaultCurrencyCode', namespaces=nsmap)
|
|
default_currency = self.env['res.currency'].with_context(active_test=False).search([('name', '=', default_currency_code.text)])
|
|
|
|
all_currency_codes = tree.findall('.//saft:CurrencyCode', namespaces=nsmap)
|
|
currency_codes_to_look_for = {currency_code_node.text for currency_code_node in all_currency_codes}
|
|
currencies = self.env['res.currency'].with_context(active_test=False)._read_group(
|
|
domain=[('name', 'in', list(currency_codes_to_look_for))],
|
|
aggregates=['id:array_agg'],
|
|
groupby=['name'],
|
|
)
|
|
map_currencies = {curr[0]: curr[1][0] for curr in currencies}
|
|
|
|
journal_data, moves_data = self._prepare_journal_data(tree, default_currency, map_accounts, map_taxes, map_currencies, map_partners)
|
|
|
|
data['account.journal'] = journal_data
|
|
data['account.move'].update(moves_data)
|
|
|
|
return data
|
|
|
|
# -----------------------------------
|
|
# Main method
|
|
# -----------------------------------
|
|
|
|
def action_import(self):
|
|
""" Start the import by gathering generators and templates and applying them to attached files. """
|
|
|
|
# Basic checks to start
|
|
if not self.company_id.chart_template:
|
|
action = self.env.ref('account.action_account_config')
|
|
raise RedirectWarning(_('You should install a Fiscal Localization first.'), action.id, _('Accounting Settings'))
|
|
|
|
# In Odoo, move names follow sequences based on the year, so the checks complain
|
|
# if the year present in the move's name doesn't match with the move's date.
|
|
# This is unimportant here since we are importing existing moves from external data.
|
|
# The workaround is to set the sequence.mixin.constraint_start_date parameter
|
|
# to the date of the oldest move (defaulting to today if there is no move at all).
|
|
domain = self.env['account.move']._check_company_domain(self.company_id)
|
|
start_date = self.env['account.move'].search(domain, limit=1, order='date asc').date or fields.Date.today()
|
|
self.env['ir.config_parameter'].sudo().set_param('sequence.mixin.constraint_start_date', start_date.strftime("%Y-%m-%d"))
|
|
|
|
data = self._get_data()
|
|
|
|
# skip_invoice_sync to avoid creating twice the tax lines
|
|
self.env['account.chart.template'].with_context(skip_invoice_sync=True)._load_data(data)
|
|
|
|
return {
|
|
"type": "ir.actions.client",
|
|
"tag": "reload",
|
|
}
|