diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-09-20 17:33:22 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-09-20 17:34:45 +0200 |
| commit | 64e43808b79badb5869a8d73cc1cc3f3b3aa8e12 (patch) | |
| tree | 9a22d5f6c03073a4d10faedea8df23a5211e8b8f /addons/account/models | |
| parent | 944891ced081cda3b8b3db5faf3b7ac23b6b4ce8 (diff) | |
| parent | 58a83d12228fbbd4e9dde542e133d264204ee0f3 (diff) | |
[MERGE] forward port branch saas-12.5 up to 58a83d12228
Diffstat (limited to 'addons/account/models')
| -rw-r--r-- | addons/account/models/account.py | 37 | ||||
| -rw-r--r-- | addons/account/models/account_move.py | 17 | ||||
| -rw-r--r-- | addons/account/models/account_payment.py | 19 | ||||
| -rw-r--r-- | addons/account/models/account_reconcile_model.py | 16 | ||||
| -rw-r--r-- | addons/account/models/chart_template.py | 18 | ||||
| -rw-r--r-- | addons/account/models/partner.py | 4 | ||||
| -rw-r--r-- | addons/account/models/res_users.py | 13 |
7 files changed, 77 insertions, 47 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 526a9ac2eaf..e2301af91d3 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -211,6 +211,7 @@ class AccountAccount(models.Model): help="Forces all moves for this account to have this account currency.") code = fields.Char(size=64, required=True, index=True) deprecated = fields.Boolean(index=True, default=False) + used = fields.Boolean(store=False, search='_search_used') user_type_id = fields.Many2one('account.account.type', string='Type', required=True, help="Account Type is used for information purpose, to generate country-specific legal reports, and set the rules to close a fiscal year and generate opening entries.") internal_type = fields.Selection(related='user_type_id.type', string="Internal Type", store=True, readonly=True) @@ -252,6 +253,17 @@ class AccountAccount(models.Model): for record in self: record.root_id = record.code and (ord(record.code[0]) * 1000 + ord(record.code[1])) or False + def _search_used(self, operator, value): + if operator not in ['=', '!='] or not isinstance(value, bool): + raise UserError(_('Operation not supported')) + if operator != '=': + value = not value + self._cr.execute(""" + SELECT id FROM account_account account + WHERE EXISTS (SELECT * FROM account_move_line aml WHERE aml.account_id = account.id LIMIT 1) + """) + return [('id', 'in' if value else 'not in', [r[0] for r in self._cr.fetchall()])] + @api.model def _search_new_account_code(self, company, digits, prefix): for num in range(1, 10000): @@ -305,13 +317,13 @@ class AccountAccount(models.Model): if opening_move_line: if amount: # modify the line - setattr(opening_move_line.with_context({'check_move_validity': False}), field, amount) + opening_move_line.with_context(check_move_validity=False)[field] = amount elif counter_part_map[field]: # delete the line (no need to keep a line with value = 0) - opening_move_line.with_context({'check_move_validity': False}).unlink() + opening_move_line.with_context(check_move_validity=False).unlink() elif amount: # create a new line, as none existed before - self.env['account.move.line'].with_context({'check_move_validity': False}).create({ + self.env['account.move.line'].with_context(check_move_validity=False).create({ 'name': _('Opening balance'), field: amount, 'move_id': opening_move.id, @@ -349,7 +361,7 @@ class AccountAccount(models.Model): if operator in expression.NEGATIVE_TERM_OPERATORS: domain = ['&', '!'] + domain[1:] account_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) - return self.browse(account_ids).name_get() + return models.lazy_name_get(self.browse(account_ids).with_user(name_get_uid)) @api.onchange('user_type_id') def _onchange_user_type_id(self): @@ -550,7 +562,7 @@ class AccountGroup(models.Model): criteria_operator = ['|'] if operator not in expression.NEGATIVE_TERM_OPERATORS else ['&', '!'] domain = criteria_operator + [('code_prefix', '=ilike', name + '%'), ('name', operator, name)] group_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) - return self.browse(group_ids).name_get() + return models.lazy_name_get(self.browse(group_ids).with_user(name_get_uid)) class AccountRoot(models.Model): @@ -560,6 +572,7 @@ class AccountRoot(models.Model): name = fields.Char() parent_id = fields.Many2one('account.root') + company_id = fields.Many2one('res.company') def init(self): tools.drop_view_if_exists(self.env.cr, self._table) @@ -567,12 +580,14 @@ class AccountRoot(models.Model): CREATE OR REPLACE VIEW %s AS ( SELECT DISTINCT ASCII(code) * 1000 + ASCII(SUBSTRING(code,2,1)) AS id, LEFT(code,2) AS name, - ASCII(code) AS parent_id + ASCII(code) AS parent_id, + company_id FROM account_account WHERE code IS NOT NULL UNION ALL SELECT DISTINCT ASCII(code) AS id, LEFT(code,1) AS name, - NULL::int AS parent_id + NULL::int AS parent_id, + company_id FROM account_account WHERE code IS NOT NULL )''' % (self._table,) ) @@ -777,9 +792,9 @@ class AccountJournal(models.Model): if self.company_id != self.env.ref('base.main_company'): alias_name += '-' + str(self.company_id.name) return { - 'alias_defaults': {'type': type == 'purchase' and 'in_invoice' or 'out_invoice', 'company_id': self.company_id.id}, + 'alias_defaults': {'type': type == 'purchase' and 'in_invoice' or 'out_invoice', 'company_id': self.company_id.id, 'journal_id': self.id}, 'alias_parent_thread_id': self.id, - 'alias_name': re.sub(r'[^\w]+', '-', alias_name) + 'alias_name': alias_name, } def unlink(self): @@ -1019,7 +1034,7 @@ class AccountJournal(models.Model): connector = '&' if operator in expression.NEGATIVE_TERM_OPERATORS else '|' domain = [connector, ('code', operator, name), ('name', operator, name)] journal_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) - return self.browse(journal_ids).name_get() + return models.lazy_name_get(self.browse(journal_ids).with_user(name_get_uid)) @api.depends('inbound_payment_method_ids', 'outbound_payment_method_ids') def _methods_compute(self): @@ -1233,7 +1248,7 @@ class AccountTax(models.Model): connector = '&' if operator in expression.NEGATIVE_TERM_OPERATORS else '|' domain = [connector, ('description', operator, name), ('name', operator, name)] tax_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) - return self.browse(tax_ids).name_get() + return models.lazy_name_get(self.browse(tax_ids).with_user(name_get_uid)) @api.model def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None): diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 61afd62f4eb..2f23d4c923c 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -133,13 +133,13 @@ class AccountMove(models.Model): amount_residual = fields.Monetary(string='Amount Due', store=True, compute='_compute_amount') amount_untaxed_signed = fields.Monetary(string='Untaxed Amount Signed', store=True, readonly=True, - compute='_compute_amount') + compute='_compute_amount', currency_field='company_currency_id') amount_tax_signed = fields.Monetary(string='Tax Signed', store=True, readonly=True, - compute='_compute_amount') + compute='_compute_amount', currency_field='company_currency_id') amount_total_signed = fields.Monetary(string='Total Signed', store=True, readonly=True, - compute='_compute_amount') + compute='_compute_amount', currency_field='company_currency_id') amount_residual_signed = fields.Monetary(string='Amount Due Signed', store=True, - compute='_compute_amount') + compute='_compute_amount', currency_field='company_currency_id') amount_by_group = fields.Binary(string="Tax amount by group", compute='_compute_invoice_taxes_by_group', help="technical field used in report and in invoice form view with a widget to display the detail of taxes (grouped by tax group) under the subtotal") @@ -1117,7 +1117,7 @@ class AccountMove(models.Model): pay_term_line_ids = move.line_ids.filtered(lambda line: line.account_id.user_type_id.type in ('receivable', 'payable')) domain = [('account_id', 'in', pay_term_line_ids.mapped('account_id').ids), - ('move_id.state', '=', 'posted'), + '|', ('move_id.state', '=', 'posted'), '&', ('move_id.state', '=', 'draft'), ('journal_id.post_at', '=', 'bank_rec'), ('partner_id', '=', move.commercial_partner_id.id), ('reconciled', '=', False), '|', ('amount_residual', '!=', 0.0), ('amount_residual_currency', '!=', 0.0)] @@ -1521,7 +1521,8 @@ class AccountMove(models.Model): result = [] for move in self: if self._context.get('name_groupby') and not (move.type == 'entry' and move.state == 'draft'): - name = '**%s**, %s' % (format_date(self.env, move.date), move.name or '') + move_name = move.name != "/" and move.name or _("Draft %s") % dict(move._fields['type']._description_selection(self.env))[move.type] + name = '**%s**, %s' % (format_date(self.env, move.date), move_name or '') if move.ref: name += ' (%s)' % move.ref if move.partner_id.name: @@ -1529,7 +1530,7 @@ class AccountMove(models.Model): elif move.type == 'entry': # Miscellaneous operation. if move.state == 'draft': - name = '* %s' % str(move.id) + name = _('Draft Entry (* %s)') % str(move.id) else: name = move.name else: @@ -2766,7 +2767,7 @@ class AccountMoveLine(models.Model): reconciled = True line.reconciled = reconciled - line.amount_residual = line.move_id.company_id.currency_id.round(amount * sign) + line.amount_residual = line.move_id.company_id.currency_id.round(amount * sign) if line.move_id.company_id else amount * sign line.amount_residual_currency = line.currency_id and line.currency_id.round(amount_residual_currency * sign) or 0.0 @api.depends('tax_repartition_line_id.invoice_tax_id', 'tax_repartition_line_id.refund_tax_id') diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 25ddf2a3544..6b4dd3a1078 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -319,15 +319,17 @@ class account_payment(models.Model): move_currency = self.env['res.currency'].browse(res['currency_id']) if move_currency == currency and move_currency != company.currency_id: total += res['residual_currency'] - elif move_currency == currency == company.currency_id: - total += res['amount_residual'] else: - total += move_currency._convert(res['amount_residual'], currency, company, date) + total += company.currency_id._convert(res['amount_residual'], currency, company, date) return total def name_get(self): return [(payment.id, payment.name or _('Draft Payment')) for payment in self] + @api.model + def _get_move_name_transfer_separator(self): + return 'ยงยง' + @api.depends('move_line_ids.reconciled') def _get_move_reconciled(self): for payment in self: @@ -448,7 +450,6 @@ class account_payment(models.Model): move.unlink() rec.write({ 'state': 'cancelled', - 'move_name': False, }) def unlink(self): @@ -484,6 +485,7 @@ class account_payment(models.Model): all_move_vals = [] for payment in self: company_currency = payment.company_id.currency_id + move_names = payment.move_name.split(payment._get_move_name_transfer_separator()) if payment.move_name else None # Compute amounts. write_off_amount = payment.payment_difference_handling == 'reconcile' and -payment.payment_difference or 0.0 @@ -591,6 +593,9 @@ class account_payment(models.Model): 'payment_id': payment.id, })) + if move_names: + move_vals['name'] = move_names[0] + all_move_vals.append(move_vals) # ==== 'transfer' ==== @@ -634,6 +639,9 @@ class account_payment(models.Model): ], } + if move_names and len(move_names) == 2: + transfer_move_vals['name'] = move_names[1] + all_move_vals.append(transfer_move_vals) return all_move_vals @@ -677,7 +685,8 @@ class account_payment(models.Model): moves.filtered(lambda move: move.journal_id.post_at != 'bank_rec').post() # Update the state / move before performing any reconciliation. - rec.write({'state': 'posted', 'move_name': moves[0].name}) + move_name = self._get_move_name_transfer_separator().join(moves.mapped('name')) + rec.write({'state': 'posted', 'move_name': move_name}) if rec.payment_type in ('inbound', 'outbound'): # ==== 'inbound' / 'outbound' ==== diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 3a70d0516ae..56a8fc81a4e 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -97,7 +97,7 @@ class AccountReconcileModel(models.Model): ('percentage', 'Percentage of balance'), ('regex', 'From label'), ], required=True, default='percentage') - show_force_tax_included = fields.Boolean(store=False, help='Technical field used to show the force tax included button') + show_force_tax_included = fields.Boolean(compute='_compute_show_force_tax_included', help='Technical field used to show the force tax included button') force_tax_included = fields.Boolean(string='Tax Included in Price', help='Force the tax to be managed as a price included tax.') amount = fields.Float(string='Write-off Amount', digits=0, required=True, default=100.0, help="Fixed amount will count as a debit if it is negative, as a credit if it is positive.") @@ -118,7 +118,7 @@ class AccountReconcileModel(models.Model): ('percentage', 'Percentage of balance'), ('regex', 'From label'), ], string="Second Amount type",required=True, default='percentage') - show_second_force_tax_included = fields.Boolean(store=False, help='Technical field used to show the force tax included button') + show_second_force_tax_included = fields.Boolean(compute='_compute_show_second_force_tax_included', help='Technical field used to show the force tax included button') force_second_tax_included = fields.Boolean(string='Second Tax Included in Price', help='Force the second tax to be managed as a price included tax.') second_amount = fields.Float(string='Second Write-off Amount', digits=0, required=True, default=100.0, help="Fixed amount will count as a debit if it is negative, as a credit if it is positive.") @@ -159,18 +159,26 @@ class AccountReconcileModel(models.Model): def _onchange_tax_ids(self): # Multiple taxes with force_tax_included results in wrong computation, so we # only allow to set the force_tax_included field if we have one tax selected - self.show_force_tax_included = False if len(self.tax_ids) != 1 else True if len(self.tax_ids) != 1: self.force_tax_included = False + @api.depends('tax_ids') + def _compute_show_force_tax_included(self): + for record in self: + record.show_force_tax_included = False if len(record.tax_ids) != 1 else True + @api.onchange('second_tax_ids') def _onchange_second_tax_ids(self): # Multiple taxes with force_tax_included results in wrong computation, so we # only allow to set the force_tax_included field if we have one tax selected - self.show_second_force_tax_included = False if len(self.second_tax_ids) != 1 else True if len(self.second_tax_ids) != 1: self.force_second_tax_included = False + @api.depends('second_tax_ids') + def _compute_show_second_force_tax_included(self): + for record in self: + record.show_second_force_tax_included = False if len(record.second_tax_ids) != 1 else True + @api.onchange('match_total_amount_param') def _onchange_match_total_amount_param(self): if self.match_total_amount_param < 0 or self.match_total_amount_param > 100: diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index d87f87b48bb..dfa524f5cf6 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -238,7 +238,7 @@ class AccountChartTemplate(models.Model): }) # Set the transfer account on the company - company.transfer_account_id = self.env['account.account'].search([('code', '=like', self.transfer_account_code_prefix + '%')])[0] + company.transfer_account_id = self.env['account.account'].search([('code', '=like', self.transfer_account_code_prefix + '%')])[:1] # Create Bank journals self._create_bank_journals(company, acc_template_ref) @@ -249,6 +249,12 @@ class AccountChartTemplate(models.Model): # set the default taxes on the company company.account_sale_tax_id = self.env['account.tax'].search([('type_tax_use', 'in', ('sale', 'all')), ('company_id', '=', company.id)], limit=1).id company.account_purchase_tax_id = self.env['account.tax'].search([('type_tax_use', 'in', ('purchase', 'all')), ('company_id', '=', company.id)], limit=1).id + + # set default taxes to income/expense accounts + if company.account_sale_tax_id: + self.env['account.account'].browse(acc_template_ref.values()).filtered(lambda x: x.user_type_id.internal_group == 'income' and x != company.income_currency_exchange_account_id).tax_ids = company.account_sale_tax_id + if company.account_purchase_tax_id: + self.env['account.account'].browse(acc_template_ref.values()).filtered(lambda x: x.user_type_id.internal_group == 'expense' and x != company.expense_currency_exchange_account_id).tax_ids = company.account_purchase_tax_id return {} @api.model @@ -259,7 +265,7 @@ class AccountChartTemplate(models.Model): """ model_to_check = ['account.move', 'account.payment', 'account.bank.statement'] for model in model_to_check: - if len(self.env[model].search([('company_id', '=', company_id.id)])) > 0: + if self.env[model].sudo().search([('company_id', '=', company_id.id)], limit=1): return True return False @@ -649,15 +655,7 @@ class AccountChartTemplate(models.Model): account_tmpl_obj = self.env['account.account.template'] acc_template = account_tmpl_obj.search([('nocreate', '!=', True), ('chart_template_id', '=', self.id)], order='id') template_vals = [] - all_parents = self._get_chart_parent_ids() - sale_tax = self.env['account.tax.template'].search([('type_tax_use', '=', 'sale'), ('chart_template_id', 'in', all_parents)], limit=1) - purchase_tax = self.env['account.tax.template'].search([('type_tax_use', '=', 'purchase'), ('chart_template_id', 'in', all_parents)], limit=1) for account_template in acc_template: - # Adding a tax by default on each income or expense account, if they are not used for exchange difference. - if account_template.user_type_id.internal_group == 'income' and account_template != self.income_currency_exchange_account_id: - account_template.tax_ids = sale_tax - elif account_template.user_type_id.internal_group == 'expense' and account_template != self.expense_currency_exchange_account_id: - account_template.tax_ids = purchase_tax code_main = account_template.code and len(account_template.code) or 0 code_acc = account_template.code or '' if code_main > 0 and code_main <= code_digits: diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index ef0f030a5ee..e3884d5c975 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -225,7 +225,7 @@ class ResPartner(models.Model): @api.depends_context('force_company') def _credit_debit_get(self): - tables, where_clause, where_params = self.env['account.move.line'].with_context(company_id=self.env.company.id)._query_get() + tables, where_clause, where_params = self.env['account.move.line'].with_context(state='posted', company_id=self.env.company.id)._query_get() where_params = [tuple(self.ids)] + where_params if where_clause: where_clause = 'AND ' + where_clause @@ -266,9 +266,11 @@ class ResPartner(models.Model): SELECT partner.id FROM res_partner partner LEFT JOIN account_move_line aml ON aml.partner_id = partner.id + JOIN account_move move ON move.id = aml.move_id RIGHT JOIN account_account acc ON aml.account_id = acc.id WHERE acc.internal_type = %s AND NOT acc.deprecated AND acc.company_id = %s + AND move.state == 'posted' GROUP BY partner.id HAVING %s * COALESCE(SUM(aml.amount_residual), 0) ''' + operator + ''' %s''', (account_type, self.env.user.company_id.id, sign, operand)) res = self._cr.fetchall() diff --git a/addons/account/models/res_users.py b/addons/account/models/res_users.py index 1bc6ee1c813..6b379668fa3 100644 --- a/addons/account/models/res_users.py +++ b/addons/account/models/res_users.py @@ -12,14 +12,11 @@ class Users(models.Model): def _check_one_user_type(self): super(Users, self)._check_one_user_type() - users_with_both_groups = self.filtered(lambda user: - user.has_group('account.group_show_line_subtotals_tax_included') and - user.has_group('account.group_show_line_subtotals_tax_excluded') - ) - if users_with_both_groups: - names = ", ".join(users_with_both_groups.mapped('name')) + g1 = self.env.ref('account.group_show_line_subtotals_tax_included') + g2 = self.env.ref('account.group_show_line_subtotals_tax_excluded') + + if self._has_multiple_groups([g1.id, g2.id]): raise ValidationError(_("A user cannot have both Tax B2B and Tax B2C.\n" - "Problematic user(s): %s\n" "You should go in General Settings, and choose to display Product Prices\n" "either in 'Tax-Included' or in 'Tax-Excluded' mode\n" - "(or switch twice the mode if you are already in the desired one).") % names) + "(or switch twice the mode if you are already in the desired one).")) |
