from odoo import models, fields, api, _ from odoo.exceptions import UserError class AccountPayment(models.Model): _inherit = 'account.payment' @api.depends('payment_type', 'partner_type') def _compute_available_journal_ids(self): """ Override to include misc journals for vendor payments """ # Call the parent method to get the original computation with bank/cash journals super()._compute_available_journal_ids() # Then extend with general journals for supplier payments for pay in self: if pay.partner_type == 'supplier': # Include all general journals (not just those with payment methods) # We'll handle payment method availability separately all_general_journals = self.env['account.journal'].search([ '|', ('company_id', 'parent_of', self.env.company.id), ('company_id', 'child_of', self.env.company.id), ('type', '=', 'general'), ]) pay.available_journal_ids |= all_general_journals @api.depends('payment_type', 'journal_id', 'currency_id') def _compute_payment_method_line_fields(self): """ Override to include payment methods for general journals """ for pay in self: available_payment_method_lines = pay.journal_id._get_available_payment_method_lines(pay.payment_type) # For general journals, if no payment methods are available, provide defaults if pay.journal_id.type == 'general' and not available_payment_method_lines: # Get the manual payment methods (these work with general journals) if pay.payment_type == 'outbound': manual_out = self.env.ref('account.account_payment_method_manual_out', raise_if_not_found=False) if manual_out: # Create a payment method line for this journal if it doesn't exist method_line = self.env['account.payment.method.line'].search([ ('journal_id', '=', pay.journal_id.id), ('payment_method_id', '=', manual_out.id) ], limit=1) if not method_line: method_line = self.env['account.payment.method.line'].create({ 'name': 'Manual (Outbound)', 'payment_method_id': manual_out.id, 'journal_id': pay.journal_id.id, }) available_payment_method_lines = method_line elif pay.payment_type == 'inbound': manual_in = self.env.ref('account.account_payment_method_manual_in', raise_if_not_found=False) if manual_in: # Create a payment method line for this journal if it doesn't exist method_line = self.env['account.payment.method.line'].search([ ('journal_id', '=', pay.journal_id.id), ('payment_method_id', '=', manual_in.id) ], limit=1) if not method_line: method_line = self.env['account.payment.method.line'].create({ 'name': 'Manual (Inbound)', 'payment_method_id': manual_in.id, 'journal_id': pay.journal_id.id, }) available_payment_method_lines = method_line pay.available_payment_method_line_ids = available_payment_method_lines to_exclude = pay._get_payment_method_codes_to_exclude() if to_exclude: pay.available_payment_method_line_ids = pay.available_payment_method_line_ids.filtered(lambda x: x.code not in to_exclude) @api.depends('available_payment_method_line_ids') def _compute_payment_method_line_id(self): """ Override to ensure payment method is selected for general journals """ for pay in self: available_payment_method_lines = pay.available_payment_method_line_ids # Select the first available one by default. if pay.payment_method_line_id in available_payment_method_lines: pay.payment_method_line_id = pay.payment_method_line_id elif available_payment_method_lines: pay.payment_method_line_id = available_payment_method_lines[0]._origin else: # For general journals, we might need to handle this differently # But we'll let the constraint handle validation pay.payment_method_line_id = False def _synchronize_from_moves(self, changed_fields): """ Override to allow general journals for payments """ # Remove the check that restricts journals to only bank/cash types if self._context.get('skip_account_move_synchronization'): return for pay in self.with_context(skip_account_move_synchronization=True): # After the migration to 14.0, the journal entry could be shared between the account.payment and the # account.bank.statement.line. In that case, the synchronization will only be made with the statement line. if pay.move_id.statement_line_id: continue move = pay.move_id move_vals_to_write = {} payment_vals_to_write = {} if 'journal_id' in changed_fields: # Remove the original restriction - allow general journals too if pay.journal_id.type not in ('bank', 'cash', 'general'): raise UserError(_("A payment must belongs to a bank, cash, or general journal.")) # Continue with the rest of the original method if 'line_ids' in changed_fields: all_lines = move.line_ids liquidity_lines, counterpart_lines, writeoff_lines = pay._seek_for_lines() # Skip validation if payment has amount_substract (from vendor_payment_diff_amount module) # This module creates additional lines that don't fit the standard pattern has_substract = hasattr(pay, 'amount_substract') and pay.amount_substract and pay.amount_substract > 0 if not has_substract: if len(liquidity_lines) != 1: raise UserError(_( "Journal Entry %s is not valid. In order to proceed, the journal items must " "include one and only one outstanding payments/receipts account.", move.display_name, )) # Allow for additional lines (like substract account from vendor_payment_diff_amount) # Check if we have at least one counterpart line, not exactly one if len(counterpart_lines) < 1: raise UserError(_( "Journal Entry %s is not valid. In order to proceed, the journal items must " "include at least one receivable/payable account (with an exception of " "internal transfers).", move.display_name, )) if any(line.currency_id != all_lines[0].currency_id for line in all_lines): raise UserError(_( "Journal Entry %s is not valid. In order to proceed, the journal items must " "share the same currency.", move.display_name, )) if any(line.partner_id != all_lines[0].partner_id for line in all_lines): raise UserError(_( "Journal Entry %s is not valid. In order to proceed, the journal items must " "share the same partner.", move.display_name, )) if counterpart_lines.account_id.account_type == 'asset_receivable': partner_type = 'customer' else: partner_type = 'supplier' liquidity_amount = liquidity_lines.amount_currency move_vals_to_write.update({ 'currency_id': liquidity_lines.currency_id.id, 'partner_id': liquidity_lines.partner_id.id, }) payment_vals_to_write.update({ 'amount': abs(liquidity_amount), 'partner_type': partner_type, 'currency_id': liquidity_lines.currency_id.id, 'destination_account_id': counterpart_lines.account_id.id, 'partner_id': liquidity_lines.partner_id.id, }) if liquidity_amount > 0.0: payment_vals_to_write.update({'payment_type': 'inbound'}) elif liquidity_amount < 0.0: payment_vals_to_write.update({'payment_type': 'outbound'}) move.write(move._cleanup_write_orm_values(move, move_vals_to_write)) pay.write(move._cleanup_write_orm_values(pay, payment_vals_to_write)) @api.depends('journal_id', 'payment_type', 'payment_method_line_id') def _compute_outstanding_account_id(self): """ Override to use the default account of misc journal when selected """ for pay in self: # If using a general/misc journal, use its default account if pay.journal_id.type == 'general': if pay.journal_id.default_account_id: pay.outstanding_account_id = pay.journal_id.default_account_id else: # Fallback to the original logic if no default account is set if pay.payment_type == 'inbound': pay.outstanding_account_id = (pay.payment_method_line_id.payment_account_id or pay.journal_id.company_id.account_journal_payment_debit_account_id) elif pay.payment_type == 'outbound': pay.outstanding_account_id = (pay.payment_method_line_id.payment_account_id or pay.journal_id.company_id.account_journal_payment_credit_account_id) else: pay.outstanding_account_id = False else: # For bank/cash journals, use the original logic if pay.payment_type == 'inbound': pay.outstanding_account_id = (pay.payment_method_line_id.payment_account_id or pay.journal_id.company_id.account_journal_payment_debit_account_id) elif pay.payment_type == 'outbound': pay.outstanding_account_id = (pay.payment_method_line_id.payment_account_id or pay.journal_id.company_id.account_journal_payment_credit_account_id) else: pay.outstanding_account_id = False