feat: Enable cross-company batch payments by overriding payment constraints, adjusting payment creation logic, and updating views to support multi-company contexts, while also adding a payment search debug logger.
This commit is contained in:
parent
822ed95c53
commit
a6c1d582ed
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import account_batch_payment
|
from . import account_batch_payment
|
||||||
from . import account_payment
|
from . import account_payment
|
||||||
from . import account_payment_register
|
from . import account_payment_register
|
||||||
|
from . import account_payment_debug
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -9,6 +9,23 @@ _logger = logging.getLogger(__name__)
|
|||||||
class AccountBatchPayment(models.Model):
|
class AccountBatchPayment(models.Model):
|
||||||
_inherit = "account.batch.payment"
|
_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
|
# Add a field to store direct payment lines
|
||||||
direct_payment_line_ids = fields.One2many(
|
direct_payment_line_ids = fields.One2many(
|
||||||
'account.batch.payment.line',
|
'account.batch.payment.line',
|
||||||
@ -83,12 +100,21 @@ class AccountBatchPayment(models.Model):
|
|||||||
'currency_id': line.currency_id.id,
|
'currency_id': line.currency_id.id,
|
||||||
'date': line.date,
|
'date': line.date,
|
||||||
'journal_id': self.journal_id.id,
|
'journal_id': self.journal_id.id,
|
||||||
|
'company_id': line.company_id.id,
|
||||||
'payment_method_line_id': payment_method_line.id,
|
'payment_method_line_id': payment_method_line.id,
|
||||||
'ref': line.memo,
|
'ref': line.memo,
|
||||||
'expense_account_id': line.expense_account_id.id,
|
'expense_account_id': line.expense_account_id.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
payment = self.env['account.payment'].create(payment_vals)
|
# 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()
|
payment.action_post()
|
||||||
|
|
||||||
# Link the payment to the line
|
# Link the payment to the line
|
||||||
@ -109,6 +135,25 @@ class AccountBatchPayment(models.Model):
|
|||||||
# If validation fails, log the error but don't prevent payment creation
|
# 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)}")
|
_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):
|
class AccountBatchPaymentLine(models.Model):
|
||||||
_name = "account.batch.payment.line"
|
_name = "account.batch.payment.line"
|
||||||
@ -120,9 +165,15 @@ class AccountBatchPaymentLine(models.Model):
|
|||||||
required=True,
|
required=True,
|
||||||
ondelete='cascade'
|
ondelete='cascade'
|
||||||
)
|
)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
'res.company',
|
||||||
|
string='Company',
|
||||||
|
default=lambda self: self.env.company
|
||||||
|
)
|
||||||
partner_id = fields.Many2one(
|
partner_id = fields.Many2one(
|
||||||
'res.partner',
|
'res.partner',
|
||||||
string='Partner'
|
string='Partner',
|
||||||
|
check_company=True
|
||||||
# Removed required=True to avoid constraint issues
|
# Removed required=True to avoid constraint issues
|
||||||
)
|
)
|
||||||
amount = fields.Monetary(
|
amount = fields.Monetary(
|
||||||
@ -138,6 +189,7 @@ class AccountBatchPaymentLine(models.Model):
|
|||||||
expense_account_id = fields.Many2one(
|
expense_account_id = fields.Many2one(
|
||||||
'account.account',
|
'account.account',
|
||||||
string='Expense Account',
|
string='Expense Account',
|
||||||
|
check_company=True,
|
||||||
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable'))]"
|
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable'))]"
|
||||||
)
|
)
|
||||||
memo = fields.Char(string='Memo')
|
memo = fields.Char(string='Memo')
|
||||||
|
|||||||
22
models/account_payment_debug.py
Normal file
22
models/account_payment_debug.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import logging
|
||||||
|
from odoo import models, api
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AccountPayment(models.Model):
|
||||||
|
_inherit = "account.payment"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def search(self, args, offset=0, limit=None, order=None, count=False):
|
||||||
|
try:
|
||||||
|
with open('/tmp/odoo_payment_search.log', 'a') as f:
|
||||||
|
f.write(f"\\n{'='*40}\\n")
|
||||||
|
f.write(f"ACCOUNT PAYMENT SEARCH CALLED\\n")
|
||||||
|
f.write(f"Args: {args}\\n")
|
||||||
|
f.write(f"Context: {self.env.context}\\n")
|
||||||
|
f.write(f"{'='*40}\\n")
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error(f"Failed to log to /tmp: {e}")
|
||||||
|
|
||||||
|
return super(AccountPayment, self).search(args, offset, limit, order, count)
|
||||||
|
|
||||||
@ -9,6 +9,7 @@
|
|||||||
<page string="Direct Payment Lines" name="direct_payment_lines">
|
<page string="Direct Payment Lines" name="direct_payment_lines">
|
||||||
<field name="direct_payment_line_ids">
|
<field name="direct_payment_line_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
|
<field name="company_id" options="{'no_create': True}"/>
|
||||||
<field name="partner_id" domain="parent.batch_type == 'outbound' and [('supplier_rank', '>', 0)] or [('customer_rank', '>', 0)]" options="{'no_create': True}"/>
|
<field name="partner_id" domain="parent.batch_type == 'outbound' and [('supplier_rank', '>', 0)] or [('customer_rank', '>', 0)]" options="{'no_create': True}"/>
|
||||||
<field name="amount" sum="Total"/>
|
<field name="amount" sum="Total"/>
|
||||||
<field name="expense_account_id"/>
|
<field name="expense_account_id"/>
|
||||||
@ -23,6 +24,15 @@
|
|||||||
<button name="generate_payments_from_lines" string="Generate Payments" type="object"
|
<button name="generate_payments_from_lines" string="Generate Payments" type="object"
|
||||||
class="oe_highlight" invisible="state != 'draft'"/>
|
class="oe_highlight" invisible="state != 'draft'"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<!-- Override payment_ids to remove journal_id constraint to allow cross-company branch payments -->
|
||||||
|
<xpath expr="//field[@name='payment_ids']" position="before">
|
||||||
|
<field name="company_id" invisible="1"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='payment_ids']" position="attributes">
|
||||||
|
<attribute name="domain">[('batch_payment_id', '=', False), ('state', '=', 'posted'), ('is_move_sent', '=', False), ('payment_method_id', '=', payment_method_id), ('payment_type','=',batch_type), ('amount', '!=', 0)]</attribute>
|
||||||
|
<!-- Context to allow viewing payments across all companies the user has access to -->
|
||||||
|
<attribute name="context">{'default_payment_type': batch_type, 'default_journal_id': journal_id}</attribute>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
Loading…
Reference in New Issue
Block a user