From f7ffb8b16d1c1ca35ff639460ef044aec7e36b1f Mon Sep 17 00:00:00 2001 From: "admin.suherdy" Date: Wed, 12 Nov 2025 16:00:05 +0700 Subject: [PATCH] initial odoo 19 changes --- README.md | 50 +-- __init__.py | 2 +- __manifest__.py | 50 +-- __pycache__/__init__.cpython-310.pyc | Bin 0 -> 252 bytes __pycache__/__init__.cpython-312.pyc | Bin 0 -> 231 bytes data/product_data.xml | 18 +- models/__init__.py | 4 +- models/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 318 bytes models/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 298 bytes .../account_payment.cpython-310.pyc | Bin 0 -> 1347 bytes .../account_payment.cpython-312.pyc | Bin 0 -> 1783 bytes .../purchase_order.cpython-310.pyc | Bin 0 -> 6217 bytes .../purchase_order.cpython-312.pyc | Bin 0 -> 9302 bytes .../res_config_settings.cpython-310.pyc | Bin 0 -> 768 bytes .../res_config_settings.cpython-312.pyc | Bin 0 -> 840 bytes models/account_payment.py | 60 +-- models/purchase_order.py | 372 +++++++++--------- models/res_config_settings.py | 24 +- security/ir.model.access.csv | 2 +- views/purchase_advance_payment_views.xml | 78 ++-- views/purchase_order_views.xml | 110 +++--- views/res_config_settings_views.xml | 66 ++-- wizard/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 251 bytes wizard/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 229 bytes ...ink_advance_payment_wizard.cpython-310.pyc | Bin 0 -> 2963 bytes ...ink_advance_payment_wizard.cpython-312.pyc | Bin 0 -> 5589 bytes wizard/link_advance_payment_wizard.py | 15 +- wizard/link_advance_payment_wizard_views.xml | 80 ++-- 28 files changed, 469 insertions(+), 462 deletions(-) create mode 100644 __pycache__/__init__.cpython-310.pyc create mode 100644 __pycache__/__init__.cpython-312.pyc create mode 100644 models/__pycache__/__init__.cpython-310.pyc create mode 100644 models/__pycache__/__init__.cpython-312.pyc create mode 100644 models/__pycache__/account_payment.cpython-310.pyc create mode 100644 models/__pycache__/account_payment.cpython-312.pyc create mode 100644 models/__pycache__/purchase_order.cpython-310.pyc create mode 100644 models/__pycache__/purchase_order.cpython-312.pyc create mode 100644 models/__pycache__/res_config_settings.cpython-310.pyc create mode 100644 models/__pycache__/res_config_settings.cpython-312.pyc create mode 100644 wizard/__pycache__/__init__.cpython-310.pyc create mode 100644 wizard/__pycache__/__init__.cpython-312.pyc create mode 100644 wizard/__pycache__/link_advance_payment_wizard.cpython-310.pyc create mode 100644 wizard/__pycache__/link_advance_payment_wizard.cpython-312.pyc diff --git a/README.md b/README.md index 6330268..76a6f5c 100644 --- a/README.md +++ b/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 \ No newline at end of file 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..fe3446f 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -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', -} \ No newline at end of file +{ + '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', +} diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a18aaceb60ef25fd716eb523a3ea31065b4fc47 GIT binary patch literal 252 zcmYk0F=_)b5Jjb3Cxl>w50Ey+rdW|KMKC@=Ft~FgCK`bjR+>fHwUKY*GRJYdtt($4 zRYqL~=Fj^ud_JvK&w}UjdUbDnZ^itdip`ccy|AD_*$XcZQWmb5t3$RH5+6l<0r5g!T{`8CRsw<=A}M$BynL*H8u1BBL4+o9U!(NQoQ zsR)VQKa`o$NFER=BsEOYI7n6l9w8X32gu%rQEA<{9 literal 0 HcmV?d00001 diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75a4d1fc1daba186386aace0343db2c85fc2f897 GIT binary patch literal 231 zcmX@j%ge<81PfI!W@rKF#~=<2FhLogWq^$73@HpLj5!Rsj8Tk?AT|?_%@oDN$WY0w z$?}pBs6> - - - - - 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 0000000000000000000000000000000000000000..37691d5c0ef89f6c356b647d2041e3639dbe679a GIT binary patch literal 318 zcmYk1O-ckY5QUTOR%Dn_!9!?oI=FKoGA9thT`vqV$xI@hRFfYe;Dx-7%ig;33a+dO z4i?nQSC0x+1)I%^V7y&^m?wNcYwQ<5f literal 0 HcmV?d00001 diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51654e8700e125264fd306baa42b3f9978c6ddad GIT binary patch literal 298 zcmX|-u}TCn5Qb-V_ux3Vv#`~|O1C+%6A|A)A7BZFki^@C-OQ3?g$3WkXYg6%8A@L#XnzuX7Y!;Etd=6dOm-zAGm*}!=bW0>@FByz<|LPI;;>AXUtiZ{c+Bm zS2@C1l(R47@e-$1KX_fMU?q8DJv&EAYZ_dXy&5~~B3sAa2C2zCHX9jijE&ob`?8q8 z=X{GVlFV3MVaEzTcjj93W30((GFFfwy1Wv42$4Ev47niwW0iSQPTND8=y;&5qkP5GEyA)_QGklNv?Q0);Mxqad)+o_c75r2l(Jy6E0i1O%(0V;K^uCFKA% zHb8-0Ag8{=?r|Tb$6k5usjpC=XovbqH#w972b|#y`OP;Ys>x(TaQyc1@9X1$kUyMq zd!UUx!fEcK00OiiB~5ARrC#Z$J|*7~@W8($;0wA5F8p)=!6``wf1*E0d>fVuL}6^W z%0&Uj`n<|Bw|9FCI>r2m3qeu}B=vx%z6dr0cRmC#_|;EC)Q1ql5F$80JrW03BN$y% zIE3*fxuEIcE3%?6f$3$Gj$e_0q!UDQWT%g_Ol@U-!gr;R_5O&r`l9L#(`u`;H8*0R zGzh()o^*QrL!*EH)4h4cbuEQvIm{o#^Dj{RXGtE!_vWVNwQzg6M2P|28O{Vsig}Ve zOFSEzTIce)4MCMWm;1ZNCVmVrxXeV{0sMCV%`qU*9BDIgzKZL0Zt%JWeT+*kW8Q`I z)|JvRkNZ30CXd9|A#!SmAXa=^)HYa)qOx~-FV=WrMj5?T5V!VleudM#kK&wch#on) zq348oKu)n6PRR{@iZ{3xvPZmh1T!s64qIN>ur(yq21XRC8={ZU@2|7vT9smHwrinb zx7hJa{rsrp6_<-lEpk|%?CQ0W8Me(*fl{A*w#>GsMy3EMG|Sh>yYyL?{<3YfWmnkV zyr_2eLx$(AxqL3z>*zl3cU_8(qR^Yt;Gg~dHydT~KcR0>=Hn6lGz(N}azU=>HF=4I zeNCQCUV7)=#?v#Dek-qnYeLD{@SL6!eTX5+zz)o|651Z}tj?8Wl`^%xn;Z64vMuGa z&Wl%7v11_6JFl@GQ#{N1SO`1p$ho2RfU&IL#xT~@LBE4e+GX$RYsM%>H1fuD}%mi=(L literal 0 HcmV?d00001 diff --git a/models/__pycache__/account_payment.cpython-312.pyc b/models/__pycache__/account_payment.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56439b3cc0414519bd223c35365a8c689493f410 GIT binary patch literal 1783 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..515dd827b9a303f887a8458fc5bae8464ea8020d GIT binary patch literal 840 zcmZ`&zi-qq6tG~R;HP)??qs$0tFGF zJ{M^0QJ-+3IF@?V8gxSp~q~glIgx`WiA^7hXk&STXd{6lD0OG)Exs4qiR}+8>_u-fX;k6knGKg>HjXBW^qnrl7QYry*^5?aBE6;H2hj1`YTTzbx&kb80*d&TRk zuDe#$l-SjxM!!=jb&svfQ2q~qUzg^m3L*Rg26HetLrV~ypwDYt^R=yCKn5lJ55U3l A*#H0l literal 0 HcmV?d00001 diff --git a/models/account_payment.py b/models/account_payment.py index be311f5..a41e86a 100644 --- a/models/account_payment.py +++ b/models/account_payment.py @@ -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 \ No newline at end of file diff --git a/models/purchase_order.py b/models/purchase_order.py index cbec487..3d666b3 100644 --- a/models/purchase_order.py +++ b/models/purchase_order.py @@ -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() \ 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..8fe0d42 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 +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 diff --git a/views/purchase_advance_payment_views.xml b/views/purchase_advance_payment_views.xml index 073c861..5ca002b 100644 --- a/views/purchase_advance_payment_views.xml +++ b/views/purchase_advance_payment_views.xml @@ -1,40 +1,40 @@ - - - - - - account.payment.form.inherit.advance.payment - account.payment - - - - - - - - - -