# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from flectra import _, api, fields, models from flectra.exceptions import UserError class AccountPaymentLine(models.Model): _name = "account.payment.line" _description = "Payment Lines" _check_company_auto = True name = fields.Char(string="Payment Reference", readonly=True, copy=False) order_id = fields.Many2one( comodel_name="account.payment.order", string="Payment Order", ondelete="cascade", index=True, check_company=True, ) company_id = fields.Many2one( related="order_id.company_id", store=True, readonly=True ) company_currency_id = fields.Many2one( related="order_id.company_currency_id", store=True, readonly=True ) payment_type = fields.Selection( related="order_id.payment_type", store=True, readonly=True ) bank_account_required = fields.Boolean( related="order_id.payment_method_id.bank_account_required", readonly=True ) state = fields.Selection( related="order_id.state", string="State", readonly=True, store=True ) move_line_id = fields.Many2one( comodel_name="account.move.line", string="Journal Item", ondelete="restrict", check_company=True, ) ml_maturity_date = fields.Date(related="move_line_id.date_maturity", readonly=True) currency_id = fields.Many2one( comodel_name="res.currency", string="Currency of the Payment Transaction", required=True, default=lambda self: self.env.user.company_id.currency_id, ) amount_currency = fields.Monetary(string="Amount", currency_field="currency_id") amount_company_currency = fields.Monetary( compute="_compute_amount_company_currency", string="Amount in Company Currency", currency_field="company_currency_id", ) partner_id = fields.Many2one( comodel_name="res.partner", string="Partner", required=True, domain=[("parent_id", "=", False)], check_company=True, ) partner_bank_id = fields.Many2one( comodel_name="res.partner.bank", string="Partner Bank Account", required=False, ondelete="restrict", check_company=True, ) partner_bank_acc_type = fields.Selection( related="partner_bank_id.acc_type", string="Bank Account Type" ) date = fields.Date(string="Payment Date") # communication field is required=False because we don't want to block # the creation of lines from move/invoices when communication is empty # This field is required in the form view and there is an error message # when going from draft to confirm if the field is empty communication = fields.Char( required=False, help="Label of the payment that will be seen by the destinee" ) communication_type = fields.Selection( selection=[("normal", "Free")], required=True, default="normal" ) payment_ids = fields.Many2many( comodel_name="account.payment", string="Payment transaction", readonly=True, ) _sql_constraints = [ ( "name_company_unique", "unique(name, company_id)", "A payment line already exists with this reference in the same company!", ) ] @api.model_create_multi def create(self, vals_list): for vals in vals_list: if vals.get("name", "New") == "New": vals["name"] = ( self.env["ir.sequence"].next_by_code("account.payment.line") or "New" ) return super().create(vals_list) @api.depends("amount_currency", "currency_id", "company_currency_id", "date") def _compute_amount_company_currency(self): for line in self: if line.currency_id and line.company_currency_id: line.amount_company_currency = line.currency_id._convert( line.amount_currency, line.company_currency_id, line.company_id, line.date or fields.Date.today(), ) else: line.amount_company_currency = 0 @api.model def _get_payment_line_grouping_fields(self): """This list of fields is used o compute the grouping hashcode.""" return [ "currency_id", "partner_id", "partner_bank_id", "date", "communication_type", ] def payment_line_hashcode(self): self.ensure_one() values = [] for field in self._get_payment_line_grouping_fields(): values.append(str(self[field])) # Don't group the payment lines that are attached to the same supplier # but to move lines with different accounts (very unlikely), # for easier generation/comprehension of the transfer move values.append(str(self.move_line_id.account_id or False)) # Don't group the payment lines that use a structured communication # otherwise it would break the structured communication system ! if self.communication_type != "normal": values.append(str(self.id)) return "-".join(values) @api.onchange("partner_id") def partner_id_change(self): partner_bank = False if self.partner_id.bank_ids: partner_bank = self.partner_id.bank_ids[0] self.partner_bank_id = partner_bank @api.onchange("move_line_id") def move_line_id_change(self): if self.move_line_id: vals = self.move_line_id._prepare_payment_line_vals(self.order_id) vals.pop("order_id") for field, value in vals.items(): self[field] = value else: self.partner_id = False self.partner_bank_id = False self.amount_currency = 0.0 self.currency_id = self.env.user.company_id.currency_id self.communication = False def invoice_reference_type2communication_type(self): """This method is designed to be inherited by localization modules""" # key = value of 'reference_type' field on account_invoice # value = value of 'communication_type' field on account_payment_line res = {"none": "normal", "structured": "structured"} return res def draft2open_payment_line_check(self): self.ensure_one() if self.bank_account_required and not self.partner_bank_id: raise UserError( _("Missing Partner Bank Account on payment line %s") % self.name ) if not self.communication: raise UserError(_("Communication is empty on payment line %s.") % self.name) def _prepare_account_payment_vals(self): """Prepare the dictionary to create an account payment record from a set of payment lines. """ journal = self.order_id.journal_id vals = { "payment_type": self.order_id.payment_type, "partner_id": self.partner_id.id, "destination_account_id": self.move_line_id.account_id.id, "company_id": self.order_id.company_id.id, "amount": sum(self.mapped("amount_currency")), "date": self[:1].date, "currency_id": self.currency_id.id, "ref": self.order_id.name, # Put the name as the wildcard for forcing a unique name. If not, Flectra gets # the sequence for all the payment at the same time "name": "/", "payment_reference": " - ".join([line.communication for line in self]), "journal_id": journal.id, "partner_bank_id": self.partner_bank_id.id, "payment_order_id": self.order_id.id, "payment_line_ids": [(6, 0, self.ids)], } # Determine payment method line according payment method and journal line = self.env["account.payment.method.line"].search( [ ( "payment_method_id", "=", self.order_id.payment_mode_id.payment_method_id.id, ), ("journal_id", "=", journal.id), ], limit=1, ) if line: vals["payment_method_line_id"] = line.id # Determine partner_type move_type = self[:1].move_line_id.move_id.move_type if move_type in {"out_invoice", "out_refund"}: vals["partner_type"] = "customer" elif move_type in {"in_invoice", "in_refund"}: vals["partner_type"] = "supplier" else: p_type = "customer" if vals["payment_type"] == "inbound" else "supplier" vals["partner_type"] = p_type # Fill destination account if manual payment line with no linked journal item if not vals["destination_account_id"]: if vals["partner_type"] == "customer": vals[ "destination_account_id" ] = self.partner_id.property_account_receivable_id.id else: vals[ "destination_account_id" ] = self.partner_id.property_account_payable_id.id return vals