Compare commits

..

2 Commits
main ... 19.0

3 changed files with 235 additions and 220 deletions

15
.gitignore vendored Normal file
View File

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