Compare commits

..

No commits in common. "19.0" and "main" have entirely different histories.
19.0 ... main

3 changed files with 220 additions and 235 deletions

15
.gitignore vendored
View File

@ -1,15 +0,0 @@
# Python
*.py[cod]
__pycache__/
*.so
# Odoo
*.po~
*.pot~
# Editor / System
.DS_Store
.vscode/
*.swp
*.swo
*~

View File

@ -1,6 +1,6 @@
{ {
'name': 'Vendor Payment with Misc Journals', 'name': 'Vendor Payment with Misc Journals',
'version': '19.0.1.0.0', 'version': '17.0.1.0.0',
'category': 'Accounting', 'category': 'Accounting',
'summary': 'Allow using misc journals for vendor bill payments', 'summary': 'Allow using misc journals for vendor bill payments',
'description': """ 'description': """

View File

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