diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 967656fd90e..e2301af91d3 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -317,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, @@ -794,7 +794,7 @@ class AccountJournal(models.Model): return { '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): diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 222727f614a..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") @@ -2767,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/chart_template.py b/addons/account/models/chart_template.py index 3ad49898420..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) @@ -265,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 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).")) diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py index ade019768ea..557a482d34f 100644 --- a/addons/account/tests/test_payment.py +++ b/addons/account/tests/test_payment.py @@ -33,6 +33,8 @@ class TestPayment(AccountingTestCase): self.bank_journal_euro = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'BNK67'}) self.account_eur = self.bank_journal_euro.default_debit_account_id + self.cash_journal_euro = self.env['account.journal'].create({'name': 'Cash', 'type': 'cash', 'code': 'CASH'}) + self.bank_journal_usd = self.env['account.journal'].create({'name': 'Bank US', 'type': 'bank', 'code': 'BNK68', 'currency_id': self.currency_usd_id}) self.account_usd = self.bank_journal_usd.default_debit_account_id @@ -425,3 +427,119 @@ class TestPayment(AccountingTestCase): # The invoice should now be paid self.assertEqual(invoice.invoice_payment_state, 'paid', "Invoice should be in 'paid' state after having reconciled the two payments with a bank statement") + + def test_payment_cancel_keep_name(self): + self.bank_journal_euro.update_posted = True + + payment = self.payment_model.create({ + 'payment_type': 'inbound', + 'payment_method_id': self.payment_method_manual_in.id, + 'partner_type': 'customer', + 'partner_id': self.partner_agrolait.id, + 'amount': 90, + 'payment_date': time.strftime('%Y') + '-07-15', + 'payment_difference_handling': 'reconcile', + 'journal_id': self.bank_journal_euro.id, + }) + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 1) + name = payment.move_line_ids.mapped('move_id').name + self.assertTrue(name) + + payment.cancel() + self.assertFalse(payment.move_line_ids.mapped('move_id')) + payment.action_draft() + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 1) + self.assertEqual(name, payment.move_line_ids.mapped('move_id').name) + + def test_payment_transfer_cancel_keep_names(self): + self.bank_journal_euro.update_posted = True + self.cash_journal_euro.update_posted = True + + payment = self.payment_model.create({ + 'payment_type': 'transfer', + 'payment_method_id': self.payment_method_manual_out.id, + 'amount': 90, + 'payment_date': time.strftime('%Y') + '-07-15', + 'journal_id': self.bank_journal_euro.id, + 'destination_journal_id': self.cash_journal_euro.id, + }) + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 2) + + all_moves = payment.move_line_ids.mapped('move_id') + move = all_moves.filtered(lambda m: m.journal_id == self.bank_journal_euro) + transfer_move = all_moves - move + self.assertEqual(transfer_move.journal_id, self.cash_journal_euro) + + name = move.name + transfer_name = transfer_move.name + self.assertTrue(name) + self.assertTrue(transfer_name) + self.assertNotEqual(name, transfer_name) + + reconciled_lines = payment.move_line_ids.filtered(lambda l: l.reconciled) + self.assertEqual(len(reconciled_lines), 2) + self.assertEqual(reconciled_lines.mapped('move_id'), all_moves) + + reconciled_lines.remove_move_reconcile() + payment.cancel() + self.assertFalse(payment.move_line_ids.mapped('move_id')) + payment.action_draft() + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 2) + + all_moves = payment.move_line_ids.mapped('move_id') + move = all_moves.filtered(lambda m: m.journal_id == self.bank_journal_euro) + transfer_move = all_moves - move + self.assertEqual(transfer_move.journal_id, self.cash_journal_euro) + + self.assertEqual(name, move.name) + self.assertEqual(transfer_name, transfer_move.name) + + def test_payment_cancel_to_transfer(self): + self.bank_journal_euro.update_posted = True + + payment = self.payment_model.create({ + 'payment_type': 'inbound', + 'payment_method_id': self.payment_method_manual_in.id, + 'partner_type': 'customer', + 'partner_id': self.partner_agrolait.id, + 'amount': 90, + 'payment_date': time.strftime('%Y') + '-07-15', + 'payment_difference_handling': 'reconcile', + 'journal_id': self.bank_journal_euro.id, + }) + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 1) + name = payment.move_line_ids.mapped('move_id').name + self.assertTrue(name) + + payment.cancel() + self.assertFalse(payment.move_line_ids.mapped('move_id')) + payment.action_draft() + + payment.write({ + 'payment_type': 'transfer', + 'payment_method_id': self.payment_method_manual_out.id, + 'partner_id': False, + 'destination_journal_id': self.cash_journal_euro.id, + }) + + payment.post() + self.assertEqual(len(payment.move_line_ids.mapped('move_id')), 2) + + all_moves = payment.move_line_ids.mapped('move_id') + move = all_moves.filtered(lambda m: m.journal_id == self.bank_journal_euro) + transfer_move = all_moves - move + self.assertEqual(transfer_move.journal_id, self.cash_journal_euro) + + self.assertEqual(name, move.name) + self.assertTrue(transfer_move.name) + self.assertNotEqual(name, transfer_move.name) diff --git a/addons/account/tests/test_reconciliation_matching_rules.py b/addons/account/tests/test_reconciliation_matching_rules.py index bcae241be67..11114cf893b 100644 --- a/addons/account/tests/test_reconciliation_matching_rules.py +++ b/addons/account/tests/test_reconciliation_matching_rules.py @@ -334,9 +334,9 @@ class TestReconciliationMatchingRules(AccountingTestCase): # Check first line has been well reconciled. self.assertRecordValues(self.bank_line_1.journal_entry_ids, [ - {'partner_id': self.partner_1.id, 'debit': 100.0, 'credit': 0.0, 'tax_ids': [self.tax21.id]}, - {'partner_id': self.partner_1.id, 'debit': 21.0, 'credit': 0.0, 'tax_line_id': self.tax21.id}, - {'partner_id': self.partner_1.id, 'debit': 0.0, 'credit': 121.0}, + {'partner_id': self.partner_1.id, 'debit': 100.0, 'credit': 0.0, 'tax_ids': [self.tax21.id], 'tax_line_id': False}, + {'partner_id': self.partner_1.id, 'debit': 21.0, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': self.tax21.id}, + {'partner_id': self.partner_1.id, 'debit': 0.0, 'credit': 121.0, 'tax_ids': [], 'tax_line_id': False}, ]) def test_reverted_move_matching(self): diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index 4269adb3fa9..894976e827d 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -30,7 +30,7 @@ Invoice Date:

-

+
Due Date:

diff --git a/addons/account/views/report_payment_receipt_templates.xml b/addons/account/views/report_payment_receipt_templates.xml index 3da12afe644..19f692b229f 100644 --- a/addons/account/views/report_payment_receipt_templates.xml +++ b/addons/account/views/report_payment_receipt_templates.xml @@ -2,7 +2,7 @@