from odoo import _, fields, models from odoo.exceptions import UserError class AccountMoveLine(models.Model): _inherit = "account.move.line" def _create_writeoff(self, writeoff_vals): """Create a writeoff move per journal for the account.move.lines in self. If debit/credit is not specified in vals, the writeoff amount will be computed as the sum of amount_residual of the given recordset. :param writeoff_vals: list of dicts containing values suitable for account_move_line.create(). The data in vals will be processed to create both writeoff account.move.line and their enclosing account.move. """ def compute_writeoff_counterpart_vals(values): line_values = values.copy() line_values["debit"], line_values["credit"] = ( line_values["credit"], line_values["debit"], ) if "amount_currency" in values: line_values["amount_currency"] = -line_values["amount_currency"] return line_values # Group writeoff_vals by journals writeoff_dict = {} for val in writeoff_vals: journal_id = val.get("journal_id", False) if not writeoff_dict.get(journal_id, False): writeoff_dict[journal_id] = [val] else: writeoff_dict[journal_id].append(val) partner_id = ( self.env["res.partner"]._find_accounting_partner(self[0].partner_id).id ) company_currency = self[0].account_id.company_id.currency_id writeoff_currency = self[0].account_id.currency_id or company_currency line_to_reconcile = self.env["account.move.line"] # Iterate and create one writeoff by journal writeoff_moves = self.env["account.move"] for journal_id, lines in writeoff_dict.items(): total = 0 total_currency = 0 writeoff_lines = [] date = fields.Date.today() for vals in lines: # Check and complete vals if "account_id" not in vals or "journal_id" not in vals: raise UserError( _( "It is mandatory to specify an account and a " "journal to create a write-off." ) ) if ("debit" in vals) ^ ("credit" in vals): raise UserError(_("Either pass both debit and credit or none.")) if "date" not in vals: vals["date"] = self._context.get("date_p") or fields.Date.today() vals["date"] = fields.Date.to_date(vals["date"]) if vals["date"] and vals["date"] < date: date = vals["date"] if "name" not in vals: vals["name"] = self._context.get("comment") or _("Write-Off") if "analytic_account_id" not in vals: vals["analytic_account_id"] = self.env.context.get( "analytic_id", False ) # compute the writeoff amount if not given if "credit" not in vals and "debit" not in vals: amount = sum([r.amount_residual for r in self]) vals["credit"] = amount > 0 and amount or 0.0 vals["debit"] = amount < 0 and abs(amount) or 0.0 vals["partner_id"] = partner_id total += vals["debit"] - vals["credit"] if ( "amount_currency" not in vals and writeoff_currency != company_currency ): vals["currency_id"] = writeoff_currency.id sign = 1 if vals["debit"] > 0 else -1 vals["amount_currency"] = sign * abs( sum([r.amount_residual_currency for r in self]) ) total_currency += vals["amount_currency"] writeoff_lines.append(compute_writeoff_counterpart_vals(vals)) # Create balance line writeoff_lines.append( { "name": _("Write-Off"), "debit": total > 0 and total or 0.0, "credit": total < 0 and -total or 0.0, "amount_currency": total_currency, "currency_id": total_currency and writeoff_currency.id or False, "journal_id": journal_id, "account_id": self[0].account_id.id, "partner_id": partner_id, } ) # Create the move writeoff_move = self.env["account.move"].create( { "journal_id": journal_id, "date": date, "state": "draft", "line_ids": [(0, 0, line) for line in writeoff_lines], } ) writeoff_moves += writeoff_move line_to_reconcile += writeoff_move.line_ids.filtered( lambda r: r.account_id == self[0].account_id ).sorted(key="id")[-1:] # post all the writeoff moves at once if writeoff_moves: writeoff_moves.action_post() # Return the writeoff move.line which is to be reconciled return line_to_reconcile