190 lines
6.9 KiB
Python
190 lines
6.9 KiB
Python
from odoo import models, fields, api
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class CreateAdvancePaymentWizard(models.TransientModel):
|
|
_name = 'create.advance.payment.wizard'
|
|
_description = 'Create Advance Payment Wizard'
|
|
|
|
purchase_order_id = fields.Many2one(
|
|
'purchase.order',
|
|
string='Purchase Order',
|
|
required=True,
|
|
readonly=True
|
|
)
|
|
|
|
partner_id = fields.Many2one(
|
|
'res.partner',
|
|
string='Vendor',
|
|
related='purchase_order_id.partner_id',
|
|
readonly=True
|
|
)
|
|
|
|
journal_id = fields.Many2one(
|
|
'account.journal',
|
|
string='Journal',
|
|
required=True,
|
|
domain=[('type', 'in', ['bank', 'cash'])]
|
|
)
|
|
|
|
amount = fields.Monetary(
|
|
string='Amount',
|
|
required=True,
|
|
currency_field='currency_id'
|
|
)
|
|
|
|
currency_id = fields.Many2one(
|
|
'res.currency',
|
|
string='Currency',
|
|
related='purchase_order_id.currency_id',
|
|
readonly=True
|
|
)
|
|
|
|
date = fields.Date(
|
|
string='Date',
|
|
required=True,
|
|
default=fields.Date.context_today
|
|
)
|
|
|
|
memo = fields.Char(
|
|
string='Memo'
|
|
)
|
|
|
|
expense_account_id = fields.Many2one(
|
|
'account.account',
|
|
string='Expense Account',
|
|
compute='_compute_expense_account',
|
|
store=True,
|
|
readonly=True
|
|
)
|
|
|
|
@api.model
|
|
def default_get(self, fields_list):
|
|
res = super().default_get(fields_list)
|
|
if self._context.get('default_purchase_order_id'):
|
|
po = self.env['purchase.order'].browse(self._context['default_purchase_order_id'])
|
|
res['amount'] = po.amount_residual
|
|
res['memo'] = f'Advance payment for {po.name}'
|
|
return res
|
|
|
|
@api.depends('purchase_order_id')
|
|
def _compute_expense_account(self):
|
|
"""Get expense account from default advance payment product"""
|
|
for wizard in self:
|
|
deposit_product_id = self.env['ir.config_parameter'].sudo().get_param(
|
|
'purchase_advance_payment.deposit_product_id')
|
|
if deposit_product_id:
|
|
product = self.env['product.product'].browse(int(deposit_product_id))
|
|
# Get expense account from product
|
|
account = product.property_account_expense_id or product.categ_id.property_account_expense_categ_id
|
|
wizard.expense_account_id = account.id
|
|
else:
|
|
wizard.expense_account_id = False
|
|
|
|
def action_create_payment(self):
|
|
"""Create advance payment with proper journal entries"""
|
|
self.ensure_one()
|
|
|
|
if self.amount <= 0:
|
|
raise UserError("Amount must be greater than zero.")
|
|
|
|
# Get expense account from default deposit product
|
|
if not self.expense_account_id:
|
|
raise UserError(
|
|
"Please configure a default advance payment product in Purchase settings "
|
|
"with a valid expense account."
|
|
)
|
|
|
|
# Get outstanding payment account from journal
|
|
# For outbound payments, first check journal-specific account, then company default
|
|
outstanding_account = None
|
|
|
|
# Try to get from journal's outbound payment method
|
|
if self.journal_id.outbound_payment_method_line_ids:
|
|
outstanding_account = self.journal_id.outbound_payment_method_line_ids[0].payment_account_id
|
|
|
|
# Fall back to company default if not set on journal
|
|
if not outstanding_account:
|
|
outstanding_account = self.journal_id.company_id.account_journal_payment_credit_account_id
|
|
|
|
if not outstanding_account:
|
|
raise UserError(
|
|
f"Please configure an outstanding payment account for journal '{self.journal_id.name}'.\n"
|
|
f"You can set it in:\n"
|
|
f"1. Journal level: Accounting > Configuration > Journals > {self.journal_id.name} > "
|
|
f"Outgoing Payments tab > Payment Method > Outstanding Payments Account\n"
|
|
f"OR\n"
|
|
f"2. Company level: Accounting > Configuration > Settings > Default Accounts > "
|
|
f"Outstanding Payments Account"
|
|
)
|
|
|
|
# Create payment
|
|
payment_vals = {
|
|
'payment_type': 'outbound',
|
|
'partner_type': 'supplier',
|
|
'partner_id': self.partner_id.id,
|
|
'amount': self.amount,
|
|
'currency_id': self.currency_id.id,
|
|
'date': self.date,
|
|
'journal_id': self.journal_id.id,
|
|
'ref': self.memo or f'Advance payment for {self.purchase_order_id.name}',
|
|
'purchase_order_id': self.purchase_order_id.id,
|
|
'is_advance_payment': True,
|
|
}
|
|
|
|
payment = self.env['account.payment'].create(payment_vals)
|
|
|
|
# Create deposit line in purchase order immediately
|
|
self._create_deposit_line()
|
|
|
|
# Return action to open the created payment
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Advance Payment',
|
|
'res_model': 'account.payment',
|
|
'res_id': payment.id,
|
|
'view_mode': 'form',
|
|
'target': 'current',
|
|
}
|
|
|
|
def _create_deposit_line(self):
|
|
"""Create deposit line in purchase order"""
|
|
# Get or create deposit product
|
|
deposit_product_id = self.env['ir.config_parameter'].sudo().get_param(
|
|
'purchase_advance_payment.deposit_product_id')
|
|
|
|
if not deposit_product_id:
|
|
raise UserError(
|
|
"Please configure a default advance payment product in Purchase settings."
|
|
)
|
|
|
|
deposit_product = self.env['product.product'].browse(int(deposit_product_id))
|
|
|
|
# Check if deposit line already exists
|
|
existing_deposit_line = self.purchase_order_id.order_line.filtered(lambda l: l.is_deposit)
|
|
|
|
# Calculate new total advance payment
|
|
new_advance_total = self.purchase_order_id.advance_payment_total + self.amount
|
|
|
|
if existing_deposit_line:
|
|
# Update existing deposit line
|
|
existing_deposit_line.write({
|
|
'product_qty': 1,
|
|
'price_unit': -new_advance_total,
|
|
'taxes_id': [(6, 0, [])],
|
|
})
|
|
else:
|
|
# Create new deposit line
|
|
deposit_vals = {
|
|
'order_id': self.purchase_order_id.id,
|
|
'product_id': deposit_product.id,
|
|
'name': f'Advance payment for {self.purchase_order_id.name}',
|
|
'product_qty': 1,
|
|
'product_uom': deposit_product.uom_id.id,
|
|
'price_unit': -self.amount,
|
|
'is_deposit': True,
|
|
'date_planned': fields.Datetime.now(),
|
|
'taxes_id': [(6, 0, [])],
|
|
}
|
|
self.env['purchase.order.line'].create(deposit_vals)
|