diff --git a/README.md b/README.md index 6330268..73eeb1f 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,78 @@ -# 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 \ No newline at end of file +# Purchase Advance Payment + +This module allows creating advance payments directly from purchase orders. +When an advance payment is created, a deposit line is automatically added to the PO. +The journal entry uses the expense account from the default advance payment product +and the outstanding payment account from the selected cash/bank journal. + +## Features + +- Create advance payments directly from Purchase Order form +- Automatic deposit line creation in PO +- Proper accounting entries using: + - Expense account from default advance payment product + - Outstanding payment account from selected journal +- Payment remains in draft state for review before posting +- View all advance payments linked to a PO +- Deposit line automatically updates when payment is posted + +## Configuration + +1. **Set Default Advance Payment Product** + - Go to **Purchase > Configuration > Settings** + - Scroll to **Advance Payment** section + - Set the **Default Deposit Product** - this product's expense account will be used for advance payment journal entries + +2. **Configure Outstanding Payment Accounts** (Choose one option) + + **Option A: Journal-Specific** (for multiple banks) + - Go to **Accounting > Configuration > Journals** + - Open each journal > **Outgoing Payments** tab + - Set **Outstanding Payments Account** in payment methods + + **Option B: Company Default** (for single bank) + - Go to **Accounting > Configuration > Settings** + - Set **Outstanding Payments Account** in Default Accounts section + + Note: Journal-specific accounts take priority over company default. + +## Usage + +### Creating an Advance Payment + +1. Open a Purchase Order (must be in confirmed state) +2. Go to **Advance Payments** tab +3. Click **Create Advance Payment** button +4. Fill in the wizard: + - **Journal**: Select cash or bank journal + - **Amount**: Enter advance payment amount + - **Date**: Payment date + - **Memo**: Optional description +5. Click **Confirm** +6. The payment is created in draft state for review +7. A deposit line is automatically added to the PO with negative amount +8. Review the payment and click **Confirm** to post it + +### Journal Entry Accounts + +When the payment is posted, the journal entry will use: +- **Debit**: Expense account from the default advance payment product +- **Credit**: Outstanding payment account from the selected journal + +### Vendor Bill Creation + +When creating a vendor bill from the PO: +1. The deposit line will be included in the bill +2. The deposit reduces the total amount payable +3. The advance payment is properly accounted for + +## Technical Details + +- Module creates a deposit product line in the PO with negative price +- Payment model is extended to handle advance payment accounting +- Journal entries are customized to use correct accounts +- Deposit line updates automatically when payment is posted + +## Known Issues + +- None diff --git a/__init__.py b/__init__.py index c536983..c6f023c 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -from . import models +from . import models from . import wizard \ No newline at end of file diff --git a/__manifest__.py b/__manifest__.py index 89e5bcb..ba3c141 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,25 +1,31 @@ -{ - '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': '17.0.2.0.0', + 'category': 'Purchase', + 'summary': 'Create advance payments directly from purchase orders', + 'description': """ + This module allows creating advance payments directly from purchase orders. + When an advance payment is created, a deposit line is automatically added to the PO. + The journal entry uses the expense account from the default advance payment product + and the outstanding payment account from the selected cash/bank journal. + + Features: + - Create advance payments from PO form + - Automatic deposit line creation + - Proper accounting with configurable accounts + - Payment remains in draft for review + """, + 'author': 'Suherdy Yacob', + 'depends': ['purchase', 'account'], + 'data': [ + 'security/ir.model.access.csv', + 'data/product_data.xml', + 'wizard/create_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', } \ No newline at end of file diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7ce47c3 Binary files /dev/null and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..202bf71 Binary files /dev/null and b/__pycache__/__init__.cpython-312.pyc differ diff --git a/data/product_data.xml b/data/product_data.xml index 624df5c..70356fe 100644 --- a/data/product_data.xml +++ b/data/product_data.xml @@ -1,10 +1,10 @@ - - - - - - Deposit - - - + + + + + + Deposit + + + \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index 52cd9e9..e4d7a59 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -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 \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..86e34f4 Binary files /dev/null and b/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..236dc33 Binary files /dev/null and b/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/models/__pycache__/account_payment.cpython-310.pyc b/models/__pycache__/account_payment.cpython-310.pyc new file mode 100644 index 0000000..15cca60 Binary files /dev/null and b/models/__pycache__/account_payment.cpython-310.pyc differ diff --git a/models/__pycache__/account_payment.cpython-312.pyc b/models/__pycache__/account_payment.cpython-312.pyc new file mode 100644 index 0000000..57fa516 Binary files /dev/null and b/models/__pycache__/account_payment.cpython-312.pyc differ diff --git a/models/__pycache__/purchase_order.cpython-310.pyc b/models/__pycache__/purchase_order.cpython-310.pyc new file mode 100644 index 0000000..4b81c58 Binary files /dev/null and b/models/__pycache__/purchase_order.cpython-310.pyc differ diff --git a/models/__pycache__/purchase_order.cpython-312.pyc b/models/__pycache__/purchase_order.cpython-312.pyc new file mode 100644 index 0000000..ce86fc9 Binary files /dev/null and b/models/__pycache__/purchase_order.cpython-312.pyc differ diff --git a/models/__pycache__/res_config_settings.cpython-310.pyc b/models/__pycache__/res_config_settings.cpython-310.pyc new file mode 100644 index 0000000..053ca29 Binary files /dev/null and b/models/__pycache__/res_config_settings.cpython-310.pyc differ diff --git a/models/__pycache__/res_config_settings.cpython-312.pyc b/models/__pycache__/res_config_settings.cpython-312.pyc new file mode 100644 index 0000000..c38333e Binary files /dev/null and b/models/__pycache__/res_config_settings.cpython-312.pyc differ diff --git a/models/account_payment.py b/models/account_payment.py index be311f5..4a1dd21 100644 --- a/models/account_payment.py +++ b/models/account_payment.py @@ -1,31 +1,69 @@ -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 _prepare_move_line_default_vals(self, write_off_line_vals=None, force_balance=None): + """Override to use correct accounts for advance payments""" + line_vals_list = super()._prepare_move_line_default_vals(write_off_line_vals=write_off_line_vals, force_balance=force_balance) + + if self.is_advance_payment and self.purchase_order_id: + # Get expense account from default deposit product + 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)) + expense_account = product.property_account_expense_id or product.categ_id.property_account_expense_categ_id + + if 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 outstanding_account: + # Modify the line vals to use correct accounts + for line_vals in line_vals_list: + # The debit line (expense) - partner line + if line_vals.get('debit', 0) > 0 and line_vals.get('partner_id'): + line_vals['account_id'] = expense_account.id + # The credit line (outstanding payment) + elif line_vals.get('credit', 0) > 0: + line_vals['account_id'] = outstanding_account.id + + return line_vals_list + + def action_post(self): + res = super().action_post() + # When an advance payment is posted, update deposit line in purchase order + for payment in self: + if payment.is_advance_payment and payment.purchase_order_id: + # Update the deposit line with the actual posted amount + payment.purchase_order_id._update_deposit_line() return res \ No newline at end of file diff --git a/models/purchase_order.py b/models/purchase_order.py index cbec487..c42ff63 100644 --- a/models/purchase_order.py +++ b/models/purchase_order.py @@ -1,187 +1,238 @@ -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 _update_deposit_line(self): + """Update deposit line based on posted advance payments""" + self.ensure_one() + + # Calculate total posted advance payments + posted_advance_total = sum( + payment.amount for payment in self.advance_payment_ids.filtered(lambda p: p.state == 'posted') + ) + + if posted_advance_total <= 0: + # Remove deposit line if no posted payments + deposit_lines = self.order_line.filtered(lambda l: l.is_deposit) + deposit_lines.unlink() + return + + # 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.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': -posted_advance_total, # Negative value for deposit + 'taxes_id': [(6, 0, [])], # No taxes + }) + else: + # Create new deposit line + deposit_vals = { + 'order_id': self.id, + 'product_id': deposit_product.id, + 'name': f'Advance payment for {self.name}', + 'product_qty': 1, + 'product_uom': deposit_product.uom_id.id, + 'price_unit': -posted_advance_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) + + 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() \ No newline at end of file diff --git a/models/res_config_settings.py b/models/res_config_settings.py index 5d0a8e8..f221f6b 100644 --- a/models/res_config_settings.py +++ b/models/res_config_settings.py @@ -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' ) \ No newline at end of file diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 2f7f514..6164d41 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,2 +1,2 @@ -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 \ No newline at end of file +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_create_advance_payment_wizard,access.create.advance.payment.wizard,model_create_advance_payment_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/views/purchase_advance_payment_views.xml b/views/purchase_advance_payment_views.xml index 073c861..c93f739 100644 --- a/views/purchase_advance_payment_views.xml +++ b/views/purchase_advance_payment_views.xml @@ -1,40 +1,34 @@ - - - - - - account.payment.form.inherit.advance.payment - account.payment - - - - - - - - - -