initial odoo 19 changes
This commit is contained in:
parent
3328179534
commit
f7ffb8b16d
50
README.md
50
README.md
@ -1,26 +1,26 @@
|
||||
# Purchase Advance Payment
|
||||
|
||||
This module allows linking payments to purchase orders as advance payments.
|
||||
|
||||
## Features
|
||||
|
||||
- Link payments to purchase orders as advance payments
|
||||
- Automatically subtract advance payments from the total when the PO is fully billed
|
||||
- Create deposit products on the PO so the final invoice includes the deposit product
|
||||
- Track advance payments linked to purchase orders
|
||||
|
||||
## Usage
|
||||
|
||||
1. Create a purchase order
|
||||
2. Create a payment and link it to the purchase order as an advance payment
|
||||
3. When the payment is posted, it will automatically be applied as a deposit to the purchase order
|
||||
4. The deposit will appear as a negative line item on the purchase order
|
||||
5. When creating the vendor bill, the deposit will be included
|
||||
|
||||
## Configuration
|
||||
|
||||
No additional configuration is required.
|
||||
|
||||
## Known Issues
|
||||
|
||||
# Purchase Advance Payment
|
||||
|
||||
This module allows linking payments to purchase orders as advance payments.
|
||||
|
||||
## Features
|
||||
|
||||
- Link payments to purchase orders as advance payments
|
||||
- Automatically subtract advance payments from the total when the PO is fully billed
|
||||
- Create deposit products on the PO so the final invoice includes the deposit product
|
||||
- Track advance payments linked to purchase orders
|
||||
|
||||
## Usage
|
||||
|
||||
1. Create a purchase order
|
||||
2. Create a payment and link it to the purchase order as an advance payment
|
||||
3. When the payment is posted, it will automatically be applied as a deposit to the purchase order
|
||||
4. The deposit will appear as a negative line item on the purchase order
|
||||
5. When creating the vendor bill, the deposit will be included
|
||||
|
||||
## Configuration
|
||||
|
||||
No additional configuration is required.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- None
|
||||
@ -1,2 +1,2 @@
|
||||
from . import models
|
||||
from . import models
|
||||
from . import wizard
|
||||
@ -1,25 +1,25 @@
|
||||
{
|
||||
'name': 'Purchase Advance Payment',
|
||||
'version': '17.0.1.0.0',
|
||||
'category': 'Purchase',
|
||||
'summary': 'Link payments to purchase orders as advance payments',
|
||||
'description': """
|
||||
This module allows linking payments to purchase orders as advance payments.
|
||||
When a PO is fully billed, the total is subtracted by the advance payment made.
|
||||
After payment is linked to the PO, a deposit product is created so the final
|
||||
invoice/vendor bills will include the deposit product.
|
||||
""",
|
||||
'author': 'Suherdy Yacob',
|
||||
'depends': ['purchase', 'account'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/product_data.xml',
|
||||
'wizard/link_advance_payment_wizard_views.xml',
|
||||
'views/purchase_advance_payment_views.xml',
|
||||
'views/purchase_order_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
{
|
||||
'name': 'Purchase Advance Payment',
|
||||
'version': '19.0.1.0.0',
|
||||
'category': 'Purchase',
|
||||
'summary': 'Link payments to purchase orders as advance payments',
|
||||
'description': """
|
||||
This module allows linking payments to purchase orders as advance payments.
|
||||
When a PO is fully billed, the total is subtracted by the advance payment made.
|
||||
After payment is linked to the PO, a deposit product is created so the final
|
||||
invoice/vendor bills will include the deposit product.
|
||||
""",
|
||||
'author': 'Suherdy Yacob',
|
||||
'depends': ['purchase', 'account'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/product_data.xml',
|
||||
'wizard/link_advance_payment_wizard_views.xml',
|
||||
'views/purchase_advance_payment_views.xml',
|
||||
'views/purchase_order_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
||||
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Product Category for Deposit Products -->
|
||||
<record id="product_category_deposit" model="product.category">
|
||||
<field name="name">Deposit</field>
|
||||
<field name="parent_id" ref="product.product_category_all"/>
|
||||
</record>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Product Category for Deposit Products -->
|
||||
<record id="product_category_deposit" model="product.category">
|
||||
<field name="name">Deposit</field>
|
||||
<field name="parent_id" ref="product.product_category_all"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@ -1,3 +1,3 @@
|
||||
from . import purchase_order
|
||||
from . import account_payment
|
||||
from . import purchase_order
|
||||
from . import account_payment
|
||||
from . import res_config_settings
|
||||
BIN
models/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
models/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/account_payment.cpython-310.pyc
Normal file
BIN
models/__pycache__/account_payment.cpython-310.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/account_payment.cpython-312.pyc
Normal file
BIN
models/__pycache__/account_payment.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/purchase_order.cpython-310.pyc
Normal file
BIN
models/__pycache__/purchase_order.cpython-310.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/purchase_order.cpython-312.pyc
Normal file
BIN
models/__pycache__/purchase_order.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/res_config_settings.cpython-310.pyc
Normal file
BIN
models/__pycache__/res_config_settings.cpython-310.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/res_config_settings.cpython-312.pyc
Normal file
BIN
models/__pycache__/res_config_settings.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,31 +1,31 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = 'account.payment'
|
||||
|
||||
purchase_order_id = fields.Many2one(
|
||||
'purchase.order',
|
||||
string='Purchase Order',
|
||||
domain="[('partner_id', '=', partner_id), ('state', 'in', ('purchase', 'done'))]"
|
||||
)
|
||||
|
||||
is_advance_payment = fields.Boolean(
|
||||
string='Is Advance Payment',
|
||||
default=False,
|
||||
help='Identifies if this payment is an advance payment for a purchase order'
|
||||
)
|
||||
|
||||
@api.onchange('purchase_order_id')
|
||||
def _onchange_purchase_order_id(self):
|
||||
if self.purchase_order_id:
|
||||
self.amount = self.purchase_order_id.amount_residual
|
||||
|
||||
def action_post(self):
|
||||
res = super().action_post()
|
||||
# When an advance payment is posted, link it to the purchase order
|
||||
for payment in self:
|
||||
if payment.is_advance_payment and payment.purchase_order_id:
|
||||
# Apply the deposit to the purchase order
|
||||
payment.purchase_order_id.action_apply_deposit()
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = 'account.payment'
|
||||
|
||||
purchase_order_id = fields.Many2one(
|
||||
'purchase.order',
|
||||
string='Purchase Order',
|
||||
domain="[('partner_id', '=', partner_id), ('state', 'in', ('purchase', 'done'))]"
|
||||
)
|
||||
|
||||
is_advance_payment = fields.Boolean(
|
||||
string='Is Advance Payment',
|
||||
default=False,
|
||||
help='Identifies if this payment is an advance payment for a purchase order'
|
||||
)
|
||||
|
||||
@api.onchange('purchase_order_id')
|
||||
def _onchange_purchase_order_id(self):
|
||||
if self.purchase_order_id:
|
||||
self.amount = self.purchase_order_id.amount_residual
|
||||
|
||||
def action_post(self):
|
||||
res = super().action_post()
|
||||
# When an advance payment is posted, link it to the purchase order
|
||||
for payment in self:
|
||||
if payment.is_advance_payment and payment.purchase_order_id:
|
||||
# Apply the deposit to the purchase order
|
||||
payment.purchase_order_id.action_apply_deposit()
|
||||
return res
|
||||
@ -1,187 +1,187 @@
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
advance_payment_ids = fields.One2many(
|
||||
'account.payment',
|
||||
'purchase_order_id',
|
||||
string='Advance Payments',
|
||||
domain=[('state', '=', 'posted')]
|
||||
)
|
||||
|
||||
advance_payment_total = fields.Monetary(
|
||||
string='Advance Payment Total',
|
||||
compute='_compute_advance_payment_total',
|
||||
store=True
|
||||
)
|
||||
|
||||
amount_residual = fields.Monetary(
|
||||
string='Amount Residual',
|
||||
compute='_compute_amount_residual',
|
||||
store=True
|
||||
)
|
||||
|
||||
deposit_product_id = fields.Many2one(
|
||||
'product.product',
|
||||
string='Deposit Product',
|
||||
help='Product used for advance payment deposit'
|
||||
)
|
||||
|
||||
@api.depends('advance_payment_ids', 'advance_payment_ids.state', 'advance_payment_ids.amount')
|
||||
def _compute_advance_payment_total(self):
|
||||
for order in self:
|
||||
order.advance_payment_total = sum(
|
||||
payment.amount for payment in order.advance_payment_ids.filtered(lambda p: p.state == 'posted')
|
||||
)
|
||||
|
||||
@api.depends('amount_total', 'advance_payment_total')
|
||||
def _compute_amount_residual(self):
|
||||
for order in self:
|
||||
order.amount_residual = order.amount_total - order.advance_payment_total
|
||||
|
||||
def action_view_advance_payments(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref('account.action_account_payments').sudo().read()[0]
|
||||
action['domain'] = [('id', 'in', self.advance_payment_ids.ids)]
|
||||
action['context'] = {
|
||||
'default_purchase_order_id': self.id,
|
||||
'default_partner_id': self.partner_id.id,
|
||||
'default_payment_type': 'outbound',
|
||||
'default_partner_type': 'supplier',
|
||||
}
|
||||
return action
|
||||
|
||||
def action_create_deposit_product(self):
|
||||
"""Create a deposit product for this purchase order"""
|
||||
self.ensure_one()
|
||||
# Check if there's a default deposit product in settings
|
||||
default_deposit_product = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'purchase_advance_payment.deposit_product_id')
|
||||
if default_deposit_product:
|
||||
self.deposit_product_id = int(default_deposit_product)
|
||||
return self.deposit_product_id
|
||||
|
||||
# If no default product, create one
|
||||
if not self.deposit_product_id:
|
||||
product_vals = {
|
||||
'name': f'Deposit for PO {self.name}',
|
||||
'type': 'service',
|
||||
'purchase_ok': True,
|
||||
'sale_ok': False,
|
||||
'invoice_policy': 'order', # Ordered quantities for deposit
|
||||
'supplier_taxes_id': [(6, 0, [])], # No supplier taxes
|
||||
}
|
||||
deposit_product = self.env['product.product'].create(product_vals)
|
||||
self.deposit_product_id = deposit_product.id
|
||||
return self.deposit_product_id
|
||||
|
||||
def button_draft(self):
|
||||
res = super().button_draft()
|
||||
# Remove deposit lines when resetting to draft
|
||||
for order in self:
|
||||
deposit_lines = order.order_line.filtered(lambda l: l.is_deposit)
|
||||
deposit_lines.unlink()
|
||||
return res
|
||||
|
||||
def action_apply_deposit(self):
|
||||
"""Apply advance payment as deposit line in the purchase order"""
|
||||
self.ensure_one()
|
||||
if self.advance_payment_total <= 0:
|
||||
raise UserError("No advance payment found for this purchase order.")
|
||||
|
||||
# Create or update deposit product
|
||||
if not self.deposit_product_id:
|
||||
self.action_create_deposit_product()
|
||||
|
||||
# Check if deposit line already exists
|
||||
existing_deposit_line = self.order_line.filtered(lambda l: l.is_deposit)
|
||||
|
||||
if existing_deposit_line:
|
||||
# Update existing deposit line
|
||||
existing_deposit_line.write({
|
||||
'product_qty': 1,
|
||||
'price_unit': -self.advance_payment_total, # Negative value for deposit
|
||||
'taxes_id': [(6, 0, [])], # No taxes
|
||||
})
|
||||
else:
|
||||
# Create new deposit line
|
||||
deposit_vals = {
|
||||
'order_id': self.id,
|
||||
'product_id': self.deposit_product_id.id,
|
||||
'name': f'Deposit payment for PO {self.name}',
|
||||
'product_qty': 1,
|
||||
'product_uom': self.deposit_product_id.uom_id.id,
|
||||
'price_unit': -self.advance_payment_total, # Negative value for deposit
|
||||
'is_deposit': True,
|
||||
'date_planned': fields.Datetime.now(),
|
||||
'taxes_id': [(6, 0, [])], # No taxes
|
||||
}
|
||||
self.env['purchase.order.line'].create(deposit_vals)
|
||||
|
||||
return True
|
||||
|
||||
def action_create_invoice(self):
|
||||
"""Override to ensure deposit line is included in vendor bill"""
|
||||
# Apply deposit before creating invoice
|
||||
for order in self:
|
||||
if order.advance_payment_total > 0:
|
||||
order.action_apply_deposit()
|
||||
|
||||
# Call super to create the invoice
|
||||
invoices = super().action_create_invoice()
|
||||
|
||||
# Ensure deposit lines have quantity 1 in the created invoices
|
||||
if 'res_id' in invoices and invoices['res_id']:
|
||||
# Single invoice
|
||||
invoice = self.env['account.move'].browse(invoices['res_id'])
|
||||
self._fix_deposit_line_quantities(invoice)
|
||||
elif 'domain' in invoices and invoices['domain']:
|
||||
# Multiple invoices
|
||||
invoice_ids = self.env['account.move'].search(invoices['domain'])
|
||||
for invoice in invoice_ids:
|
||||
self._fix_deposit_line_quantities(invoice)
|
||||
|
||||
return invoices
|
||||
|
||||
def _fix_deposit_line_quantities(self, invoice):
|
||||
"""Fix deposit line quantities in the invoice"""
|
||||
for line in invoice.invoice_line_ids:
|
||||
if line.purchase_line_id and line.purchase_line_id.is_deposit:
|
||||
line.write({
|
||||
'quantity': 1.0,
|
||||
'tax_ids': [(6, 0, [])] # No taxes
|
||||
})
|
||||
|
||||
|
||||
class PurchaseOrderLine(models.Model):
|
||||
_inherit = 'purchase.order.line'
|
||||
|
||||
is_deposit = fields.Boolean(
|
||||
string='Is Deposit',
|
||||
default=False,
|
||||
help='Identifies if this line is a deposit payment'
|
||||
)
|
||||
|
||||
def _prepare_account_move_line(self, move=False):
|
||||
"""Override to ensure deposit lines have correct quantity in vendor bill"""
|
||||
self.ensure_one()
|
||||
res = super()._prepare_account_move_line(move)
|
||||
|
||||
# If this is a deposit line, ensure quantity is 1 and no taxes
|
||||
if self.is_deposit:
|
||||
res['quantity'] = 1
|
||||
res['tax_ids'] = [(6, 0, [])] # No taxes
|
||||
|
||||
return res
|
||||
|
||||
def _get_invoice_qty(self):
|
||||
"""Override to ensure deposit lines have correct quantity for invoicing"""
|
||||
self.ensure_one()
|
||||
if self.is_deposit:
|
||||
# For deposit lines, always invoice quantity 1 regardless of received qty
|
||||
return 1.0
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
advance_payment_ids = fields.One2many(
|
||||
'account.payment',
|
||||
'purchase_order_id',
|
||||
string='Advance Payments',
|
||||
domain=[('state', '=', 'posted')]
|
||||
)
|
||||
|
||||
advance_payment_total = fields.Monetary(
|
||||
string='Advance Payment Total',
|
||||
compute='_compute_advance_payment_total',
|
||||
store=True
|
||||
)
|
||||
|
||||
amount_residual = fields.Monetary(
|
||||
string='Amount Residual',
|
||||
compute='_compute_amount_residual',
|
||||
store=True
|
||||
)
|
||||
|
||||
deposit_product_id = fields.Many2one(
|
||||
'product.product',
|
||||
string='Deposit Product',
|
||||
help='Product used for advance payment deposit'
|
||||
)
|
||||
|
||||
@api.depends('advance_payment_ids', 'advance_payment_ids.state', 'advance_payment_ids.amount')
|
||||
def _compute_advance_payment_total(self):
|
||||
for order in self:
|
||||
order.advance_payment_total = sum(
|
||||
payment.amount for payment in order.advance_payment_ids.filtered(lambda p: p.state == 'posted')
|
||||
)
|
||||
|
||||
@api.depends('amount_total', 'advance_payment_total')
|
||||
def _compute_amount_residual(self):
|
||||
for order in self:
|
||||
order.amount_residual = order.amount_total - order.advance_payment_total
|
||||
|
||||
def action_view_advance_payments(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref('account.action_account_payments').sudo().read()[0]
|
||||
action['domain'] = [('id', 'in', self.advance_payment_ids.ids)]
|
||||
action['context'] = {
|
||||
'default_purchase_order_id': self.id,
|
||||
'default_partner_id': self.partner_id.id,
|
||||
'default_payment_type': 'outbound',
|
||||
'default_partner_type': 'supplier',
|
||||
}
|
||||
return action
|
||||
|
||||
def action_create_deposit_product(self):
|
||||
"""Create a deposit product for this purchase order"""
|
||||
self.ensure_one()
|
||||
# Check if there's a default deposit product in settings
|
||||
default_deposit_product = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'purchase_advance_payment.deposit_product_id')
|
||||
if default_deposit_product:
|
||||
self.deposit_product_id = int(default_deposit_product)
|
||||
return self.deposit_product_id
|
||||
|
||||
# If no default product, create one
|
||||
if not self.deposit_product_id:
|
||||
product_vals = {
|
||||
'name': f'Deposit for PO {self.name}',
|
||||
'type': 'service',
|
||||
'purchase_ok': True,
|
||||
'sale_ok': False,
|
||||
'invoice_policy': 'order', # Ordered quantities for deposit
|
||||
'supplier_taxes_id': [(6, 0, [])], # No supplier taxes
|
||||
}
|
||||
deposit_product = self.env['product.product'].create(product_vals)
|
||||
self.deposit_product_id = deposit_product.id
|
||||
return self.deposit_product_id
|
||||
|
||||
def button_draft(self):
|
||||
res = super().button_draft()
|
||||
# Remove deposit lines when resetting to draft
|
||||
for order in self:
|
||||
deposit_lines = order.order_line.filtered(lambda l: l.is_deposit)
|
||||
deposit_lines.unlink()
|
||||
return res
|
||||
|
||||
def action_apply_deposit(self):
|
||||
"""Apply advance payment as deposit line in the purchase order"""
|
||||
self.ensure_one()
|
||||
if self.advance_payment_total <= 0:
|
||||
raise UserError("No advance payment found for this purchase order.")
|
||||
|
||||
# Create or update deposit product
|
||||
if not self.deposit_product_id:
|
||||
self.action_create_deposit_product()
|
||||
|
||||
# Check if deposit line already exists
|
||||
existing_deposit_line = self.order_line.filtered(lambda l: l.is_deposit)
|
||||
|
||||
if existing_deposit_line:
|
||||
# Update existing deposit line
|
||||
existing_deposit_line.write({
|
||||
'product_qty': 1,
|
||||
'price_unit': -self.advance_payment_total, # Negative value for deposit
|
||||
'taxes_id': [(6, 0, [])], # No taxes
|
||||
})
|
||||
else:
|
||||
# Create new deposit line
|
||||
deposit_vals = {
|
||||
'order_id': self.id,
|
||||
'product_id': self.deposit_product_id.id,
|
||||
'name': f'Deposit payment for PO {self.name}',
|
||||
'product_qty': 1,
|
||||
'product_uom': self.deposit_product_id.uom_id.id,
|
||||
'price_unit': -self.advance_payment_total, # Negative value for deposit
|
||||
'is_deposit': True,
|
||||
'date_planned': fields.Datetime.now(),
|
||||
'taxes_id': [(6, 0, [])], # No taxes
|
||||
}
|
||||
self.env['purchase.order.line'].create(deposit_vals)
|
||||
|
||||
return True
|
||||
|
||||
def action_create_invoice(self):
|
||||
"""Override to ensure deposit line is included in vendor bill"""
|
||||
# Apply deposit before creating invoice
|
||||
for order in self:
|
||||
if order.advance_payment_total > 0:
|
||||
order.action_apply_deposit()
|
||||
|
||||
# Call super to create the invoice
|
||||
invoices = super().action_create_invoice()
|
||||
|
||||
# Ensure deposit lines have quantity 1 in the created invoices
|
||||
if 'res_id' in invoices and invoices['res_id']:
|
||||
# Single invoice
|
||||
invoice = self.env['account.move'].browse(invoices['res_id'])
|
||||
self._fix_deposit_line_quantities(invoice)
|
||||
elif 'domain' in invoices and invoices['domain']:
|
||||
# Multiple invoices
|
||||
invoice_ids = self.env['account.move'].search(invoices['domain'])
|
||||
for invoice in invoice_ids:
|
||||
self._fix_deposit_line_quantities(invoice)
|
||||
|
||||
return invoices
|
||||
|
||||
def _fix_deposit_line_quantities(self, invoice):
|
||||
"""Fix deposit line quantities in the invoice"""
|
||||
for line in invoice.invoice_line_ids:
|
||||
if line.purchase_line_id and line.purchase_line_id.is_deposit:
|
||||
line.write({
|
||||
'quantity': 1.0,
|
||||
'tax_ids': [(6, 0, [])] # No taxes
|
||||
})
|
||||
|
||||
|
||||
class PurchaseOrderLine(models.Model):
|
||||
_inherit = 'purchase.order.line'
|
||||
|
||||
is_deposit = fields.Boolean(
|
||||
string='Is Deposit',
|
||||
default=False,
|
||||
help='Identifies if this line is a deposit payment'
|
||||
)
|
||||
|
||||
def _prepare_account_move_line(self, move=False):
|
||||
"""Override to ensure deposit lines have correct quantity in vendor bill"""
|
||||
self.ensure_one()
|
||||
res = super()._prepare_account_move_line(move)
|
||||
|
||||
# If this is a deposit line, ensure quantity is 1 and no taxes
|
||||
if self.is_deposit:
|
||||
res['quantity'] = 1
|
||||
res['tax_ids'] = [(6, 0, [])] # No taxes
|
||||
|
||||
return res
|
||||
|
||||
def _get_invoice_qty(self):
|
||||
"""Override to ensure deposit lines have correct quantity for invoicing"""
|
||||
self.ensure_one()
|
||||
if self.is_deposit:
|
||||
# For deposit lines, always invoice quantity 1 regardless of received qty
|
||||
return 1.0
|
||||
return super()._get_invoice_qty()
|
||||
@ -1,13 +1,13 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
deposit_product_id = fields.Many2one(
|
||||
'product.product',
|
||||
string='Default Deposit Product',
|
||||
domain=[('type', '=', 'service')],
|
||||
config_parameter='purchase_advance_payment.deposit_product_id',
|
||||
help='Default product used for advance payment deposits'
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
deposit_product_id = fields.Many2one(
|
||||
'product.product',
|
||||
string='Default Deposit Product',
|
||||
domain=[('type', '=', 'service')],
|
||||
config_parameter='purchase_advance_payment.deposit_product_id',
|
||||
help='Default product used for advance payment deposits'
|
||||
)
|
||||
@ -1,2 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_link_advance_payment_wizard,access.link.advance.payment.wizard,model_link_advance_payment_wizard,base.group_user,1,1,1,1
|
||||
|
@ -1,40 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Account Payment Form View -->
|
||||
<record id="view_account_payment_form_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">account.payment.form.inherit.advance.payment</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet/group[2]" position="after">
|
||||
<group>
|
||||
<field name="is_advance_payment" invisible="1"/>
|
||||
<field name="purchase_order_id"
|
||||
invisible="not is_advance_payment"
|
||||
required="is_advance_payment"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="%(purchase_advance_payment.action_link_advance_payment_wizard_payment)d"
|
||||
string="Link to PO"
|
||||
type="action"
|
||||
class="btn-primary"
|
||||
invisible="state != 'draft' or is_advance_payment"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Account Payment Tree View -->
|
||||
<record id="view_account_payment_tree_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">account.payment.tree.inherit.advance.payment</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree/field[@name='partner_id']" position="after">
|
||||
<field name="purchase_order_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Account Payment Form View -->
|
||||
<record id="view_account_payment_form_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">account.payment.form.inherit.advance.payment</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet/group[2]" position="after">
|
||||
<group>
|
||||
<field name="is_advance_payment" invisible="1"/>
|
||||
<field name="purchase_order_id"
|
||||
invisible="not is_advance_payment"
|
||||
required="is_advance_payment"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="%(purchase_advance_payment.action_link_advance_payment_wizard_payment)d"
|
||||
string="Link to PO"
|
||||
type="action"
|
||||
class="btn-primary"
|
||||
invisible="state != 'draft' or is_advance_payment"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Account Payment List View -->
|
||||
<record id="view_account_payment_tree_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">account.payment.tree.inherit.advance.payment</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//list/field[@name='partner_id']" position="after">
|
||||
<field name="purchase_order_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@ -1,56 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Purchase Order Form View -->
|
||||
<record id="view_purchase_order_form_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form.inherit.advance.payment</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet/group/group[2]" position="after">
|
||||
<group>
|
||||
<field name="deposit_product_id" invisible="1"/>
|
||||
</group>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//sheet/notebook" position="inside">
|
||||
<page string="Advance Payments" name="advance_payments">
|
||||
<group>
|
||||
<field name="advance_payment_total" widget="monetary"/>
|
||||
<field name="amount_residual" widget="monetary"/>
|
||||
</group>
|
||||
<group>
|
||||
<button name="action_apply_deposit"
|
||||
string="Apply Deposit"
|
||||
type="object"
|
||||
class="btn-primary"
|
||||
invisible="advance_payment_total <= 0"/>
|
||||
<button name="%(purchase_advance_payment.action_link_advance_payment_wizard)d"
|
||||
string="Link Advance Payment"
|
||||
type="action"
|
||||
class="btn-secondary"
|
||||
context="{'default_purchase_order_id': active_id}"/>
|
||||
</group>
|
||||
<field name="advance_payment_ids">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="date"/>
|
||||
<field name="amount" widget="monetary"/>
|
||||
<field name="state"/>
|
||||
<field name="journal_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='price_unit']" position="after">
|
||||
<field name="is_deposit" invisible="1"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='order_line']/form//field[@name='price_unit']" position="after">
|
||||
<field name="is_deposit" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Purchase Order Form View -->
|
||||
<record id="view_purchase_order_form_inherit_advance_payment" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form.inherit.advance.payment</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet/group/group[2]" position="after">
|
||||
<group>
|
||||
<field name="deposit_product_id" invisible="1"/>
|
||||
</group>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//sheet/notebook" position="inside">
|
||||
<page string="Advance Payments" name="advance_payments">
|
||||
<group>
|
||||
<field name="advance_payment_total" widget="monetary"/>
|
||||
<field name="amount_residual" widget="monetary"/>
|
||||
</group>
|
||||
<group>
|
||||
<button name="action_apply_deposit"
|
||||
string="Apply Deposit"
|
||||
type="object"
|
||||
class="btn-primary"
|
||||
invisible="advance_payment_total <= 0"/>
|
||||
<button name="%(purchase_advance_payment.action_link_advance_payment_wizard)d"
|
||||
string="Link Advance Payment"
|
||||
type="action"
|
||||
class="btn-secondary"
|
||||
context="{'default_purchase_order_id': id}"/>
|
||||
</group>
|
||||
<field name="advance_payment_ids">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="date"/>
|
||||
<field name="amount" widget="monetary"/>
|
||||
<field name="state"/>
|
||||
<field name="journal_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='price_unit']" position="after">
|
||||
<field name="is_deposit" invisible="1"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='order_line']/form//field[@name='price_unit']" position="after">
|
||||
<field name="is_deposit" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@ -1,34 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="res_config_settings_view_form_purchase_advance_payment" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.purchase.advance.payment</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<div class="app_settings_block" data-string="Purchase" string="Purchase" data-key="purchase">
|
||||
<h2>Advance Payment</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane" />
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
for="deposit_product_id"
|
||||
string="Advance Payments"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Default product used for advance payment deposits
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<field name="deposit_product_id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="res_config_settings_view_form_purchase_advance_payment" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.purchase.advance.payment</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<div class="app_settings_block" data-string="Purchase" string="Purchase" data-key="purchase">
|
||||
<h2>Advance Payment</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane" />
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
for="deposit_product_id"
|
||||
string="Advance Payments"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Default product used for advance payment deposits
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<field name="deposit_product_id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
wizard/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
wizard/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
wizard/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
wizard/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
wizard/__pycache__/link_advance_payment_wizard.cpython-310.pyc
Normal file
BIN
wizard/__pycache__/link_advance_payment_wizard.cpython-310.pyc
Normal file
Binary file not shown.
BIN
wizard/__pycache__/link_advance_payment_wizard.cpython-312.pyc
Normal file
BIN
wizard/__pycache__/link_advance_payment_wizard.cpython-312.pyc
Normal file
Binary file not shown.
@ -30,15 +30,22 @@ class LinkAdvancePaymentWizard(models.TransientModel):
|
||||
def default_get(self, fields):
|
||||
res = super().default_get(fields)
|
||||
# Handle context from purchase order
|
||||
if self._context.get('active_model') == 'purchase.order' and self._context.get('active_id'):
|
||||
po = self.env['purchase.order'].browse(self._context['active_id'])
|
||||
if self.env.context.get('active_model') == 'purchase.order' and self.env.context.get('active_id'):
|
||||
po = self.env['purchase.order'].browse(self.env.context['active_id'])
|
||||
res['purchase_order_id'] = po.id
|
||||
res['currency_id'] = po.currency_id.id
|
||||
if 'amount' in fields:
|
||||
res['amount'] = po.amount_residual
|
||||
# Handle context from purchase order when called from the button (using default_purchase_order_id)
|
||||
elif self.env.context.get('default_purchase_order_id'):
|
||||
po = self.env['purchase.order'].browse(self.env.context['default_purchase_order_id'])
|
||||
res['purchase_order_id'] = po.id
|
||||
res['currency_id'] = po.currency_id.id
|
||||
if 'amount' in fields and 'amount' not in res:
|
||||
res['amount'] = po.amount_residual
|
||||
# Handle context from payment
|
||||
elif self._context.get('active_model') == 'account.payment' and self._context.get('active_id'):
|
||||
payment = self.env['account.payment'].browse(self._context['active_id'])
|
||||
elif self.env.context.get('active_model') == 'account.payment' and self.env.context.get('active_id'):
|
||||
payment = self.env['account.payment'].browse(self.env.context['active_id'])
|
||||
res['payment_id'] = payment.id
|
||||
res['currency_id'] = payment.currency_id.id
|
||||
if 'amount' in fields:
|
||||
|
||||
@ -1,41 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Link Advance Payment Wizard Form View -->
|
||||
<record id="view_link_advance_payment_wizard_form" model="ir.ui.view">
|
||||
<field name="name">link.advance.payment.wizard.form</field>
|
||||
<field name="model">link.advance.payment.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Link Advance Payment">
|
||||
<group>
|
||||
<field name="purchase_order_id"/>
|
||||
<field name="payment_id" domain="[('state', '=', 'draft'), ('payment_type', '=', 'outbound')]"/>
|
||||
<field name="amount"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Link Payment" name="action_link_payment" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Link Advance Payment Wizard Action -->
|
||||
<record id="action_link_advance_payment_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Link Advance Payment</field>
|
||||
<field name="res_model">link.advance.payment.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!-- Link Advance Payment Wizard Action from Payment -->
|
||||
<record id="action_link_advance_payment_wizard_payment" model="ir.actions.act_window">
|
||||
<field name="name">Link to Purchase Order</field>
|
||||
<field name="res_model">link.advance.payment.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'active_model': 'account.payment', 'active_id': active_id}</field>
|
||||
</record>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Link Advance Payment Wizard Form View -->
|
||||
<record id="view_link_advance_payment_wizard_form" model="ir.ui.view">
|
||||
<field name="name">link.advance.payment.wizard.form</field>
|
||||
<field name="model">link.advance.payment.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Link Advance Payment">
|
||||
<group>
|
||||
<field name="purchase_order_id"/>
|
||||
<field name="payment_id" domain="[('state', '=', 'draft'), ('payment_type', '=', 'outbound')]"/>
|
||||
<field name="amount"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Link Payment" name="action_link_payment" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Link Advance Payment Wizard Action -->
|
||||
<record id="action_link_advance_payment_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Link Advance Payment</field>
|
||||
<field name="res_model">link.advance.payment.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!-- Link Advance Payment Wizard Action from Payment -->
|
||||
<record id="action_link_advance_payment_wizard_payment" model="ir.actions.act_window">
|
||||
<field name="name">Link to Purchase Order</field>
|
||||
<field name="res_model">link.advance.payment.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'active_model': 'account.payment', 'active_id': active_id}</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user