1# -*- coding: utf-8 -*- 2# Part of Odoo. See LICENSE file for full copyright and licensing details. 3 4from collections import defaultdict 5import logging 6 7from odoo import api, models 8 9_logger = logging.getLogger(__name__) 10 11 12class AccountChartTemplate(models.Model): 13 _inherit = 'account.chart.template' 14 15 def _load(self, sale_tax_rate, purchase_tax_rate, company): 16 res = super(AccountChartTemplate, self)._load(sale_tax_rate, purchase_tax_rate, company) 17 # Copy chart of account translations when loading chart of account 18 for chart_template in self.filtered('spoken_languages'): 19 external_id = self.env['ir.model.data'].search([ 20 ('model', '=', 'account.chart.template'), 21 ('res_id', '=', chart_template.id), 22 ], order='id', limit=1) 23 module = external_id and self.env.ref('base.module_' + external_id.module) 24 if module and module.state == 'installed': 25 langs = chart_template._get_langs() 26 if langs: 27 chart_template._process_single_company_coa_translations(company.id, langs) 28 return res 29 30 def process_translations(self, langs, in_field, in_ids, out_ids): 31 """ 32 This method copies translations values of templates into new Accounts/Taxes/Journals for languages selected 33 34 :param langs: List of languages to load for new records 35 :param in_field: Name of the translatable field of source templates 36 :param in_ids: Recordset of ids of source object 37 :param out_ids: Recordset of ids of destination object 38 39 :return: True 40 """ 41 xlat_obj = self.env['ir.translation'] 42 #find the source from Account Template 43 for lang in langs: 44 #find the value from Translation 45 value = xlat_obj._get_ids(in_ids._name + ',' + in_field, 'model', lang, in_ids.ids) 46 counter = 0 47 for element in in_ids.with_context(lang=None): 48 if value[element.id]: 49 #copy Translation from Source to Destination object 50 xlat_obj._set_ids( 51 out_ids._name + ',' + in_field, 52 'model', 53 lang, 54 out_ids[counter].ids, 55 value[element.id], 56 element[in_field] 57 ) 58 else: 59 _logger.info('Language: %s. Translation from template: there is no translation available for %s!' % (lang, element[in_field])) 60 counter += 1 61 return True 62 63 def process_coa_translations(self): 64 company_obj = self.env['res.company'] 65 for chart_template_id in self: 66 langs = chart_template_id._get_langs() 67 if langs: 68 company_ids = company_obj.search([('chart_template_id', '=', chart_template_id.id)]) 69 for company in company_ids: 70 chart_template_id._process_single_company_coa_translations(company.id, langs) 71 return True 72 73 def _process_single_company_coa_translations(self, company_id, langs): 74 # write account.account translations in the real COA 75 self._process_accounts_translations(company_id, langs, 'name') 76 # write account.group translations 77 self._process_account_group_translations(company_id, langs, 'name') 78 # copy account.tax name translations 79 self._process_taxes_translations(company_id, langs, 'name') 80 # copy account.tax description translations 81 self._process_taxes_translations(company_id, langs, 'description') 82 # copy account.fiscal.position translations 83 self._process_fiscal_pos_translations(company_id, langs, 'name') 84 85 def _get_langs(self): 86 if not self.spoken_languages: 87 return [] 88 89 installed_langs = dict(self.env['res.lang'].get_installed()) 90 langs = [] 91 for lang in self.spoken_languages.split(';'): 92 if lang not in installed_langs: 93 # the language is not installed, so we don't need to load its translations 94 continue 95 else: 96 langs.append(lang) 97 return langs 98 99 def _process_accounts_translations(self, company_id, langs, field): 100 in_ids, out_ids = self._get_template_from_model(company_id, 'account.account') 101 return self.process_translations(langs, field, in_ids, out_ids) 102 103 def _process_account_group_translations(self, company_id, langs, field): 104 in_ids, out_ids = self._get_template_from_model(company_id, 'account.group') 105 return self.process_translations(langs, field, in_ids, out_ids) 106 107 def _process_taxes_translations(self, company_id, langs, field): 108 in_ids, out_ids = self._get_template_from_model(company_id, 'account.tax') 109 return self.process_translations(langs, field, in_ids, out_ids) 110 111 def _process_fiscal_pos_translations(self, company_id, langs, field): 112 in_ids, out_ids = self._get_template_from_model(company_id, 'account.fiscal.position') 113 return self.process_translations(langs, field, in_ids, out_ids) 114 115 def _get_template_from_model(self, company_id, model): 116 """ Find the records and their matching template """ 117 # generated records have an external id with the format <company id>_<template xml id> 118 grouped_out_data = defaultdict(lambda: self.env['ir.model.data']) 119 for imd in self.env['ir.model.data'].search([ 120 ('model', '=', model), 121 ('name', '=like', str(company_id) + '_%') 122 ]): 123 grouped_out_data[imd.module] += imd 124 125 in_records = self.env[model + '.template'] 126 out_records = self.env[model] 127 for module, out_data in grouped_out_data.items(): 128 # templates and records may have been created in a different order 129 # reorder them based on external id names 130 expected_in_xml_id_names = {xml_id.name.partition(str(company_id) + '_')[-1]: xml_id for xml_id in out_data} 131 132 in_xml_ids = self.env['ir.model.data'].search([ 133 ('model', '=', model + '.template'), 134 ('module', '=', module), 135 ('name', 'in', list(expected_in_xml_id_names)) 136 ]) 137 in_xml_ids = {xml_id.name: xml_id for xml_id in in_xml_ids} 138 139 for name, xml_id in expected_in_xml_id_names.items(): 140 # ignore nonconforming customized data 141 if name not in in_xml_ids: 142 continue 143 in_records += self.env[model + '.template'].browse(in_xml_ids[name].res_id) 144 out_records += self.env[model].browse(xml_id.res_id) 145 146 return (in_records, out_records) 147 148class BaseLanguageInstall(models.TransientModel): 149 """ Install Language""" 150 _inherit = "base.language.install" 151 152 def lang_install(self): 153 self.ensure_one() 154 already_installed = self.lang in [code for code, _ in self.env['res.lang'].get_installed()] 155 res = super(BaseLanguageInstall, self).lang_install() 156 if already_installed: 157 # update of translations instead of new installation 158 # skip to avoid duplicating the translations 159 return res 160 161 # CoA in multilang mode 162 for coa in self.env['account.chart.template'].search([('spoken_languages', '!=', False)]): 163 if self.lang in coa.spoken_languages.split(';'): 164 # companies on which it is installed 165 for company in self.env['res.company'].search([('chart_template_id', '=', coa.id)]): 166 # write account.account translations in the real COA 167 coa._process_accounts_translations(company.id, [self.lang], 'name') 168 # write account.group translations 169 coa._process_account_group_translations(company.id, [self.lang], 'name') 170 # copy account.tax name translations 171 coa._process_taxes_translations(company.id, [self.lang], 'name') 172 # copy account.tax description translations 173 coa._process_taxes_translations(company.id, [self.lang], 'description') 174 # copy account.fiscal.position translations 175 coa._process_fiscal_pos_translations(company.id, [self.lang], 'name') 176 return res 177