account_chart_update: allows to set the matching value order

It allows to set the order of matching for Account, Taxes and
Fiscal position.
This commit is contained in:
ernesto
2019-01-29 12:07:29 -05:00
parent 3ae899ed24
commit 31ba15e77c
5 changed files with 453 additions and 39 deletions

View File

@@ -62,6 +62,9 @@ class WizardUpdateChartsAccounts(models.TransientModel):
string="Continue on errors", default=False,
help="If set, the wizard will continue to the next step even if "
"there are minor errors.")
recreate_xml_ids = fields.Boolean(
string="Recreate missing XML-IDs",
)
tax_ids = fields.One2many(
comodel_name='wizard.update.charts.accounts.tax', ondelete='cascade',
inverse_name='update_chart_wizard_id', string='Taxes')
@@ -117,6 +120,24 @@ class WizardUpdateChartsAccounts(models.TransientModel):
domain=lambda self: self._domain_fp_field_ids(),
default=lambda self: self._default_fp_field_ids(),
)
tax_matching_ids = fields.One2many(
comodel_name="wizard.tax.matching",
inverse_name="update_chart_wizard_id",
string="Taxes matching",
default=lambda self: self._default_tax_matching_ids(),
)
account_matching_ids = fields.One2many(
comodel_name="wizard.account.matching",
inverse_name="update_chart_wizard_id",
string="Taxes matching",
default=lambda self: self._default_account_matching_ids(),
)
fp_matching_ids = fields.One2many(
comodel_name="wizard.fp.matching",
inverse_name="update_chart_wizard_id",
string="Taxes matching",
default=lambda self: self._default_fp_matching_ids(),
)
def _domain_per_name(self, name):
return [
@@ -148,6 +169,32 @@ class WizardUpdateChartsAccounts(models.TransientModel):
self._domain_fp_field_ids()
)]
def _get_matching_ids(self, model_name, ordered_opts):
vals = []
for seq, opt in enumerate(ordered_opts, 1):
vals.append((0, False, {'sequence': seq, 'matching_value': opt}))
all_options = self.env[model_name]._get_matching_selection()
all_options = map(lambda x: x[0], all_options)
all_options = list(set(all_options) - set(ordered_opts))
for seq, opt in enumerate(all_options, len(ordered_opts) + 1):
vals.append((0, False, {'sequence': seq, 'matching_value': opt}))
return vals
def _default_fp_matching_ids(self):
ordered_opts = ['xml_id', 'name']
return self._get_matching_ids('wizard.fp.matching', ordered_opts)
def _default_tax_matching_ids(self):
ordered_opts = ['xml_id', 'description', 'name']
return self._get_matching_ids('wizard.tax.matching', ordered_opts)
def _default_account_matching_ids(self):
ordered_opts = ['xml_id', 'code', 'name']
return self._get_matching_ids('wizard.account.matching', ordered_opts)
@api.model
def _get_lang_selection_options(self):
"""Gets the available languages for the selection."""
@@ -298,33 +345,41 @@ class WizardUpdateChartsAccounts(models.TransientModel):
self.state = 'done'
return self._reopen()
def _get_real_xml_name(self, template):
[external_id] = template.get_external_id().values()
(name, module) = external_id.split('.')
return "%s.%d_%s" % (name, self.company_id.id, module)
@api.multi
@tools.ormcache("templates")
def find_tax_by_templates(self, templates):
"""Find a tax that matches the template."""
# search inactive taxes too, to avoid re-creating
# taxes that have been deactivated before
Tax = self.env['account.tax'].with_context(active_test=False)
result = Tax
tax_model = self.env['account.tax'].with_context(active_test=False)
for template in templates:
single = Tax
criteria = (
("name", "=", template.name),
("description", "=", template.name),
("name", "=", template.description),
("description", "=", template.description),
)
for domain in criteria:
if single:
break
if domain[2]:
single = Tax.search(
[domain,
("company_id", "=", self.company_id.id),
("type_tax_use", "=", template.type_tax_use)],
limit=1)
result |= single
return result[:1].id
for matching in self.tax_matching_ids.sorted('sequence'):
if matching.matching_value == 'xml_id':
real = self.env.ref(self._get_real_xml_name(template),
raise_if_not_found=False)
if not real:
continue
criteria = ('id', '=', real.id)
else:
field_name = matching.matching_value
if not template[field_name]:
continue
criteria = (field_name, '=', template[field_name])
result = tax_model.search([
criteria,
("company_id", "=", self.company_id.id),
("type_tax_use", "=", template.type_tax_use),
], limit=1)
if result:
return result.id
return False
@api.model
@tools.ormcache("code")
@@ -336,19 +391,69 @@ class WizardUpdateChartsAccounts(models.TransientModel):
@tools.ormcache("templates")
def find_account_by_templates(self, templates):
"""Find an account that matches the template."""
return self.env['account.account'].search(
[('code', 'in',
list(map(self.padded_code, templates.mapped("code")))),
('company_id', '=', self.company_id.id)],
).id
account_model = self.env['account.account']
for matching in self.account_matching_ids.sorted('sequence'):
if matching.matching_value == 'xml_id':
real = self.env['account.account']
for template in templates:
try:
real |= self.env.ref(self._get_real_xml_name(template))
except:
pass
if not real:
continue
criteria = ('id', 'in', real.ids)
elif matching.matching_value == 'code':
codes = templates.mapped("code")
if not codes:
continue
criteria = ('code', 'in', list(map(self.padded_code, codes)))
else:
field_name = matching.matching_value
field_values = templates.mapped(field_name)
if not field_values:
continue
criteria = (field_name, 'in', field_values)
result = account_model.search(
[criteria, ('company_id', '=', self.company_id.id)])
if result:
return result.id
return False
@api.multi
@tools.ormcache("templates")
def find_fp_by_templates(self, templates):
"""Find a real fiscal position from a template."""
return self.env['account.fiscal.position'].search(
[('name', 'in', templates.mapped("name")),
('company_id', '=', self.company_id.id)], limit=1).id
fp_model = self.env['account.fiscal.position']
for matching in self.fp_matching_ids.sorted('sequence'):
if matching.matching_value == 'xml_id':
real = self.env['account.fiscal.position']
for template in templates:
try:
real |= self.env.ref(self._get_real_xml_name(template))
except:
pass
if not real:
continue
criteria = ('id', 'in', real.ids)
else:
field_name = matching.matching_value
field_values = templates.mapped(field_name)
if not field_values:
continue
criteria = (field_name, 'in', field_values)
result = fp_model.search([
criteria, ('company_id', '=', self.company_id.id)
], limit=1)
if result:
return result.id
return False
@api.multi
@tools.ormcache("templates", "current_fp_accounts")
@@ -420,7 +525,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
},
"account.account.template": {
"chart_template_id",
"code",
},
"account.fiscal.position.template": {
"chart_template_id",
@@ -483,8 +587,13 @@ class WizardUpdateChartsAccounts(models.TransientModel):
if expected is not None:
if expected != [] and expected != real[key]:
result[key] = expected
elif template[key] != real[key]:
result[key] = template[key]
else:
template_value = template[key]
if template._name == "account.account.template" \
and key == 'code':
template_value = self.padded_code(template['code'])
if template_value != real[key]:
result[key] = template_value
# Avoid to cache recordset references
if isinstance(real._fields[key], fields.Many2many):
result[key] = [(6, 0, result[key].ids)]
@@ -521,6 +630,13 @@ class WizardUpdateChartsAccounts(models.TransientModel):
result.append(_("Tax is disabled."))
return "\n".join(result)
def missing_xml_id(self, real_obj):
return not self.env['ir.model.data'].search([
('res_id', '=', real_obj.id),
('model', '=', real_obj._name),
('module', '!=', '__export__'),
])
@api.multi
def _find_taxes(self):
"""Search for, and load, tax templates to create/update/delete."""
@@ -544,6 +660,10 @@ class WizardUpdateChartsAccounts(models.TransientModel):
# Check the tax for changes
tax = self.env['account.tax'].browse(tax_id)
notes = self.diff_notes(template, tax)
if self.recreate_xml_ids and self.missing_xml_id(tax):
notes += (notes and "\n" or "") + _("Missing XML-ID.")
if notes:
# Tax to be updated
self.tax_ids.create({
@@ -586,6 +706,10 @@ class WizardUpdateChartsAccounts(models.TransientModel):
# Check the account for changes
account = self.env['account.account'].browse(account_id)
notes = self.diff_notes(template, account)
if self.recreate_xml_ids and self.missing_xml_id(account):
notes += (notes and "\n" or "") + _("Missing XML-ID.")
if notes:
# Account to be updated
self.account_ids.create({
@@ -620,6 +744,10 @@ class WizardUpdateChartsAccounts(models.TransientModel):
# Check the fiscal position for changes
fp = self.env['account.fiscal.position'].browse(fp_id)
notes = self.diff_notes(template, fp)
if self.recreate_xml_ids and self.missing_xml_id(fp):
notes += (notes and "\n" or "") + _("Missing XML-ID.")
if notes:
# Fiscal position template to be updated
wiz_fp.create({
@@ -630,6 +758,27 @@ class WizardUpdateChartsAccounts(models.TransientModel):
'notes': notes,
})
def recreate_xml_id(self, template, real_obj):
ir_model_data = self.env['ir.model.data']
template_xmlid = ir_model_data.search([
('model', '=', template._name),
('res_id', '=', template.id),
])
new_xml_id = "%d_%s" % (self.company_id.id, template_xmlid.name)
real_xmlid = ir_model_data.search([
('model', '=', real_obj._name),
('res_id', '=', real_obj.id),
], limit=1)
if real_xmlid:
real_xmlid.unlink()
template_xmlid.copy({
'model': real_obj._name,
'res_id': real_obj.id,
'name': new_xml_id,
'noupdate': True,
})
@api.multi
def _update_taxes(self):
"""Process taxes to create/update/deactivate."""
@@ -651,7 +800,11 @@ class WizardUpdateChartsAccounts(models.TransientModel):
if key in {'account_id', 'refund_account_id'}:
continue
tax[key] = value
_logger.info(_("Updated tax %s."), "'%s'" % template.name)
_logger.info(_("Updated tax %s."), "'%s'" % template.name)
if self.recreate_xml_ids and self.missing_xml_id(tax):
self.recreate_xml_id(template, tax)
_logger.info(_("Updated tax %s. (Recreated XML-IDs)"),
"'%s'" % template.name)
@api.multi
def _update_accounts(self):
@@ -701,6 +854,14 @@ class WizardUpdateChartsAccounts(models.TransientModel):
_("Updated account %s."),
"'%s - %s'" % (account.code, account.name),
)
if self.recreate_xml_ids \
and self.missing_xml_id(account):
self.recreate_xml_id(template, account)
_logger.info(
_("Updated account %s. (Recreated XML-ID)"),
"'%s - %s'" % (account.code, account.name),
)
except Exception:
self.rejected_updated_account_number += 1
if config['test_enable']:
@@ -772,13 +933,22 @@ class WizardUpdateChartsAccounts(models.TransientModel):
self.company_id, template, 'account.fiscal.position',
self._prepare_fp_vals(template),
)
_logger.info(
_("Created fiscal position %s."),
"'%s'" % template.name,
)
else:
# Update the given fiscal position
for key, value in self.diff_fields(template, fp).items():
fp[key] = value
_logger.info(
_("Created or updated fiscal position %s."),
"'%s'" % template.name)
_logger.info(_("Updated fiscal position %s."),
"'%s'" % template.name)
if self.recreate_xml_ids and self.missing_xml_id(fp):
self.recreate_xml_id(template, fp)
_logger.info(
_("Updated fiscal position %s. (Recreated XML-ID)"),
"'%s'" % template.name,
)
class WizardUpdateChartsAccountsTax(models.TransientModel):
@@ -850,3 +1020,66 @@ class WizardUpdateChartsAccountsFiscalPosition(models.TransientModel):
comodel_name='account.fiscal.position', required=False,
string='Fiscal position to update', ondelete='set null')
notes = fields.Text('Notes', readonly=True)
class WizardMatching(models.TransientModel):
_name = 'wizard.matching'
_order = 'sequence'
update_chart_wizard_id = fields.Many2one(
comodel_name='wizard.update.charts.accounts',
string='Update chart wizard',
required=True,
ondelete='cascade',
)
sequence = fields.Integer(
required=True,
default=1,
)
matching_value = fields.Selection(
selection="_get_matching_selection",
)
def _get_matching_selection(self):
return [('xml_id', 'XML-ID')]
def _selection_from_files(self, model_name, field_opts):
result = []
for opt in field_opts:
model = self.env[model_name]
desc = model._fields[opt].get_description(self.env)["string"]
result.append((opt, "%s (%s)" % (desc, opt)))
return result
class WizardTaxMatching(models.TransientModel):
_name = "wizard.tax.matching"
_inherit = "wizard.matching"
def _get_matching_selection(self):
vals = super(WizardTaxMatching, self)._get_matching_selection()
vals += self._selection_from_files('account.tax.template',
['description', 'name'])
return vals
class WizardAccountMatching(models.TransientModel):
_name = "wizard.account.matching"
_inherit = "wizard.matching"
def _get_matching_selection(self):
vals = super(WizardAccountMatching, self)._get_matching_selection()
vals += self._selection_from_files('account.account.template',
['code', 'name'])
return vals
class WizardFpMatching(models.TransientModel):
_name = 'wizard.fp.matching'
_inherit = "wizard.matching"
def _get_matching_selection(self):
vals = super(WizardFpMatching, self)._get_matching_selection()
vals += self._selection_from_files('account.fiscal.position.template',
['name'])
return vals