diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index f4b855b4aad..8b769016a90 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -520,12 +520,10 @@ class AccountInvoice(models.Model): tax_grouped[key]['amount'] += val['amount'] return tax_grouped - @api.multi + @api.one def register_payment(self, payment_line, writeoff_acc_id=False, writeoff_journal_id=False): """ Reconcile payable/receivable lines from the invoice with payment_line """ - line_to_reconcile = self.env['account.move.line'] - for inv in self: - line_to_reconcile += inv.move_id.line_ids.filtered(lambda r: not r.reconciled and r.account_id.internal_type in ('payable', 'receivable')) + line_to_reconcile = self.move_id.line_ids.filtered(lambda r: not r.reconciled and r.account_id.internal_type in ('payable', 'receivable')) return (line_to_reconcile + payment_line).reconcile(writeoff_acc_id, writeoff_journal_id) @api.v7 diff --git a/addons/account/account_move.py b/addons/account/account_move.py index acb682e88bf..61a8bffd3cd 100644 --- a/addons/account/account_move.py +++ b/addons/account/account_move.py @@ -695,17 +695,14 @@ class AccountMoveLine(models.Model): def _get_pair_to_reconcile(self): #field is either 'amount_residual' or 'amount_residual_currency' (if the reconciled account has a secondary currency set) field = self[0].account_id.currency_id and 'amount_residual_currency' or 'amount_residual' - #target the pair of move in self that are the oldest - sorted_moves = sorted(self, key=lambda a: a.date) - debit = credit = False - for aml in sorted_moves: - if credit != False and credit != False: - break - if aml[field] > 0 and debit == False: - debit = aml - elif aml[field] < 0 and credit == False: - credit = aml - return debit, credit + #target the pair of move in self with smallest debit, credit + smallest_debit = smallest_credit = False + for aml in self: + if aml[field] > 0: + smallest_debit = (not smallest_debit or aml[field] < smallest_debit[field]) and aml or smallest_debit + elif aml[field] < 0: + smallest_credit = (not smallest_credit or aml[field] > smallest_credit[field]) and aml or smallest_credit + return smallest_debit, smallest_credit def auto_reconcile_lines(self): """ This function iterates recursively on the recordset given as parameter as long as it diff --git a/addons/account/account_payment.py b/addons/account/account_payment.py index 70d38d6978b..bd0fc240653 100644 --- a/addons/account/account_payment.py +++ b/addons/account/account_payment.py @@ -146,7 +146,7 @@ class account_register_payments(models.TransientModel): 'communication': self.communication, 'invoice_ids': [(4, inv.id, None) for inv in self._get_invoices()], 'payment_type': self.payment_type, - 'amount': self.amount, + 'amount': self._compute_total_invoices_amount(), 'currency_id': self.currency_id.id, 'partner_id': self.partner_id.id, 'partner_type': self.partner_type, @@ -175,7 +175,11 @@ class account_payment(models.Model): def _compute_payment_difference(self): if len(self.invoice_ids) == 0: return - self.payment_difference = self._compute_total_invoices_amount() - self.amount + # A payment on multiple invoices must pay them entirely (and amount be readonly of course) + if len(self.invoice_ids) != 1: + self.payment_difference = 0 + else: + self.payment_difference = self._compute_total_invoices_amount() - self.amount company_id = fields.Many2one(store=True) @@ -310,7 +314,7 @@ class account_payment(models.Model): @api.multi def unlink(self): if any(rec.state != 'draft' for rec in self): - raise UserError(_("You can not delete a payment that is already posted")) + raise UserError(_("In order to delete a payment, it must first be canceled.")) return super(account_payment, self).unlink() @api.multi @@ -363,24 +367,42 @@ class account_payment(models.Model): Return the journal entry. """ aml_obj = self.env['account.move.line'].with_context(check_move_validity=False) - debit, credit, amount_currency = aml_obj.with_context(date=self.payment_date).compute_amount_fields(amount, self.currency_id, self.company_id.currency_id) + invoice_id = len(self.invoice_ids) == 1 and self.invoice_ids[0] or False + total_debit, total_credit, total_amount_currency = aml_obj.with_context(date=self.payment_date).compute_amount_fields(amount, self.currency_id, self.company_id.currency_id) move = self.env['account.move'].create(self._get_move_vals()) - #Write line corresponding to invoice payment - counterpart_aml_dict = self._get_shared_move_line_vals(debit, credit, amount_currency, move.id, False) - counterpart_aml_dict.update(self._get_counterpart_move_line_vals(self.invoice_ids)) - counterpart_aml_dict.update({'currency_id': self.currency_id != self.company_id.currency_id and self.currency_id.id or False}) - counterpart_aml = aml_obj.create(counterpart_aml_dict) - - #Reconcile with the invoices - if self.payment_difference_handling == 'reconcile': - self.invoice_ids.register_payment(counterpart_aml, self.writeoff_account, self.journal_id) + if len(self.invoice_ids) > 1: + total_amount = 0 + for invoice_id in self.invoice_ids: + # Create a reconciliable journal item corresponding to the invoice + inv_amount = -invoice_id.residual * MAP_INVOICE_TYPE_PAYMENT_SIGN[invoice_id.type] + debit = inv_amount > 0 and inv_amount or 0 + credit = inv_amount < 0 and -inv_amount or 0 + amount_currency = self.currency_id != self.company_id.currency_id and self.company_id.currency_id.with_context(date=self.payment_date).compute(inv_amount, self.currency_id) or 0 + # Avoid unbalanced journal entry due to rounding error + total_amount = total_amount + debit - credit + counterpart_aml_dict = self._get_shared_move_line_vals(debit, credit, amount_currency, move.id, invoice_id) + counterpart_aml_dict.update(self._get_counterpart_move_line_vals(invoice_id)) + counterpart_aml_dict.update({'currency_id': self.currency_id != self.company_id.currency_id and self.currency_id.id or False}) + counterpart_aml = aml_obj.create(counterpart_aml_dict) + invoice_id.register_payment(counterpart_aml) + total_debit = total_amount > 0 and total_amount or 0 + total_credit = total_amount < 0 and -total_amount or 0 else: - self.invoice_ids.register_payment(counterpart_aml) + # Create reconciliable journal item + counterpart_aml_dict = self._get_shared_move_line_vals(total_debit, total_credit, total_amount_currency, move.id, invoice_id) + counterpart_aml_dict.update(self._get_counterpart_move_line_vals(invoice_id)) + counterpart_aml = aml_obj.create(counterpart_aml_dict) - #Write counterpart lines - liquidity_aml_dict = self._get_shared_move_line_vals(credit, debit, -amount_currency, move.id, False) + # Reconcile it with the invoice if present + if invoice_id: + if self.payment_difference_handling == 'reconcile': + invoice_id.register_payment(counterpart_aml, self.writeoff_account, self.journal_id) + else: + invoice_id.register_payment(counterpart_aml) + + liquidity_aml_dict = self._get_shared_move_line_vals(total_credit, total_debit, -total_amount_currency, move.id, invoice_id) liquidity_aml_dict.update(self._get_liquidity_move_line_vals(-amount)) aml_obj.create(liquidity_aml_dict) @@ -453,7 +475,7 @@ class account_payment(models.Model): if self.payment_type == 'transfer': name = self.name else: - name = '' + name = invoice and invoice.number + ': ' or '' if self.partner_type == 'customer': if self.payment_type == 'inbound': name += _("Customer Payment") @@ -464,11 +486,6 @@ class account_payment(models.Model): name += _("Supplier Refund") elif self.payment_type == 'outbound': name += _("Supplier Payment") - if invoice: - name += ': ' - for inv in invoice: - name += inv.number+', ' - name = name[:len(name)-2] return { 'name': name, 'account_id': self.destination_account_id.id, diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml index 0ff503e29fe..d15c91629c1 100644 --- a/addons/account/views/account_payment_view.xml +++ b/addons/account/views/account_payment_view.xml @@ -244,7 +244,7 @@ - +