vendor_batch_payment_merge/models/account_batch_payment.py

212 lines
9.0 KiB
Python

# -*- coding: utf-8 -*-
import logging
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class AccountBatchPayment(models.Model):
_inherit = "account.batch.payment"
# Override payment_ids to remove strict company check, allowing cross-company payments to be added
payment_ids = fields.One2many(
'account.payment',
'batch_payment_id',
string="Payments",
required=True,
check_company=False
)
company_id = fields.Many2one(
'res.company',
string='Company',
related='journal_id.company_id',
store=True,
readonly=True
)
# Add a field to store direct payment lines
direct_payment_line_ids = fields.One2many(
'account.batch.payment.line',
'batch_payment_id',
string='Direct Payment Lines'
)
def generate_payments_from_lines(self):
"""Generate actual payments from the direct payment lines"""
self.ensure_one()
payment_ids = []
# First, try to use the journal's available payment methods
available_payment_methods = self.journal_id._get_available_payment_method_lines(self.batch_type)
# Fix: Check payment_method_id.code, not just code
payment_method_line = available_payment_methods.filtered(lambda x: x.payment_method_id.code == 'direct_batch')
if not payment_method_line:
# If no direct batch payment method found, prefer 'manual' method for bank journals
if available_payment_methods:
# Try to find 'manual' payment method (most common for bank transfers)
manual_method = available_payment_methods.filtered(lambda x: x.payment_method_id.code == 'manual')
payment_method_line = manual_method[:1] if manual_method else available_payment_methods[0]
else:
# Fallback: try to find or create a direct batch payment method line
payment_method_line = self.env['account.payment.method.line'].search([
('payment_method_id.code', '=', 'direct_batch'),
('journal_id', '=', self.journal_id.id)
], limit=1)
if not payment_method_line:
# Try to create the payment method line if it doesn't exist
payment_method = self.env['account.payment.method'].search([
('code', '=', 'direct_batch'),
('payment_type', '=', self.batch_type)
], limit=1)
if payment_method:
# Check if a payment method line already exists for this journal and method
existing_pml = self.env['account.payment.method.line'].search([
('payment_method_id', '=', payment_method.id),
('journal_id', '=', self.journal_id.id)
], limit=1)
if not existing_pml:
payment_method_line = self.env['account.payment.method.line'].create({
'name': payment_method.name,
'payment_method_id': payment_method.id,
'journal_id': self.journal_id.id,
})
else:
payment_method_line = existing_pml
else:
raise ValidationError(_("No payment method found for this journal."))
# Check that all lines have a partner
for line in self.direct_payment_line_ids:
if not line.partner_id:
raise ValidationError(_("All payment lines must have a partner selected."))
for line in self.direct_payment_line_ids:
# Determine payment type and partner type based on batch type
payment_type = self.batch_type
partner_type = 'customer' if self.batch_type == 'inbound' else 'supplier'
# Create the payment
payment_vals = {
'payment_type': payment_type,
'partner_type': partner_type,
'partner_id': line.partner_id.id,
'amount': line.amount,
'currency_id': line.currency_id.id,
'date': line.date,
'journal_id': self.journal_id.id,
'company_id': line.company_id.id,
'payment_method_line_id': payment_method_line.id,
'ref': line.memo,
'expense_account_id': line.expense_account_id.id,
}
# Create the payment with the branch's company context, bypassing security limits momentarily
payment = self.env['account.payment'].sudo().with_context(
allowed_company_ids=[line.company_id.id, self.company_id.id, self.env.company.id],
default_company_id=line.company_id.id,
).create(payment_vals)
# Since some compute methods might try to overwrite company_id based on journal, force it again
payment.write({'company_id': line.company_id.id})
payment.action_post()
# Link the payment to the line
line.payment_id = payment.id
payment_ids.append(payment.id)
# Add the generated payments to the batch
if payment_ids:
self.write({
'payment_ids': [(4, payment_id) for payment_id in payment_ids]
})
# Automatically validate the batch payment after generating payments
if self.state == 'draft':
try:
self.validate_batch()
except Exception as e:
# If validation fails, log the error but don't prevent payment creation
_logger.warning(f"Failed to automatically validate batch payment {self.id}: {str(e)}")
@api.constrains('batch_type', 'journal_id', 'payment_ids')
def _check_payments_constrains(self):
"""Override standard constraint to allow cross-company / cross-journal payments."""
for record in self:
all_types = set(record.payment_ids.mapped('payment_type'))
if all_types and record.batch_type not in all_types:
raise ValidationError(_("The batch must have the same type as the payments it contains."))
all_payment_methods = record.payment_ids.payment_method_id
if len(all_payment_methods) > 1:
raise ValidationError(_("All payments in the batch must share the same payment method."))
if all_payment_methods and record.payment_method_id not in all_payment_methods:
raise ValidationError(_("The batch must have the same payment method as the payments it contains."))
payment_null = record.payment_ids.filtered(lambda p: p.amount == 0)
if payment_null:
raise ValidationError(_('You cannot add payments with zero amount in a Batch Payment.'))
non_posted = record.payment_ids.filtered(lambda p: p.state != 'posted')
if non_posted:
raise ValidationError(_('You cannot add payments that are not posted.'))
class AccountBatchPaymentLine(models.Model):
_name = "account.batch.payment.line"
_description = "Batch Payment Line"
batch_payment_id = fields.Many2one(
'account.batch.payment',
string='Batch Payment',
required=True,
ondelete='cascade'
)
company_id = fields.Many2one(
'res.company',
string='Company',
default=lambda self: self.env.company
)
partner_id = fields.Many2one(
'res.partner',
string='Partner',
check_company=True
# Removed required=True to avoid constraint issues
)
amount = fields.Monetary(
string='Amount',
required=True
)
currency_id = fields.Many2one(
'res.currency',
string='Currency',
related='batch_payment_id.currency_id',
store=True
)
expense_account_id = fields.Many2one(
'account.account',
string='Expense Account',
check_company=True,
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable'))]"
)
memo = fields.Char(string='Memo')
date = fields.Date(
string='Payment Date',
default=fields.Date.context_today
)
payment_id = fields.Many2one(
'account.payment',
string='Generated Payment',
readonly=True
)
@api.onchange('partner_id')
def _onchange_partner_id(self):
if self.partner_id:
# Set default expense account from partner if available
# Note: property_account_expense doesn't exist on res.partner in standard Odoo
# We'll leave this empty for now and let the user manually select the expense account
pass