You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							236 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							236 lines
						
					
					
						
							11 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ################################################################################ | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| # | |
| #    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |
| #    Author: Gayathri V (odoo@cybrosys.com) | |
| # | |
| #    You can modify it under the terms of the GNU AFFERO | |
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |
| # | |
| #    This program is distributed in the hope that it will be useful, | |
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |
| # | |
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |
| #    (AGPL v3) along with this program. | |
| ############################################################################## | |
| import time | |
| from datetime import datetime | |
| 
 | |
| from odoo import api, fields, models, _ | |
| from odoo.exceptions import UserError | |
| from odoo.fields import Command | |
| 
 | |
| 
 | |
| class PurchaseOrderAdvancePayment(models.TransientModel): | |
|     """The class is created to creating a new model purchase order advance | |
|     payment.""" | |
|     _name = 'purchase.order.advance.payment' | |
|     _description = 'Purchase Order Advance Payment Bill' | |
| 
 | |
|     @api.model | |
|     def _default_product_id(self): | |
|         """Function to fetch the purchase down payment default product""" | |
|         product_id = self.env['ir.config_parameter'].sudo().get_param( | |
|             'purchase_down_payment.po_deposit_default_product_id') | |
|         return self.env['product.product'].browse(int(product_id)).exists() | |
| 
 | |
|     @api.model | |
|     def _default_currency_id(self): | |
|         """Function to fetch the currency_id from purchase order""" | |
|         if self._context.get( | |
|                 'active_model') == 'purchase.order' and self._context.get( | |
|             'active_id', False): | |
|             purchase_order = self.env['purchase.order'].browse( | |
|                 self._context.get('active_id')) | |
|             return purchase_order.currency_id | |
| 
 | |
|     @api.model | |
|     def _default_has_down_payment(self): | |
|         """Function to check the Purchase order has down payment or not""" | |
|         if self._context.get( | |
|                 'active_model') == 'purchase.order' and self._context.get( | |
|             'active_id', False): | |
|             purchase_order = self.env['purchase.order'].browse( | |
|                 self._context.get('active_id')) | |
|             return purchase_order.order_line.filtered( | |
|                 lambda purchase_order_line: purchase_order_line.is_downpayment | |
|             ) | |
|         return False | |
| 
 | |
|     advance_payment_method = fields.Selection([ | |
|         ('delivered', 'Regular bill'), | |
|         ('percentage', 'Down payment (percentage)'), | |
|         ('fixed', 'Down payment (fixed amount)') | |
|     ], string='Create Bill', default='delivered', required=True, | |
|         help="A standard bill is issued with all the order lines ready for billing, \ | |
|         according to their billing policy (based on ordered or delivered quantity).") | |
|     has_down_payments = fields.Boolean(string='Has down payments', | |
|                                        default=_default_has_down_payment, | |
|                                        readonly=True, | |
|                                        help='To check the invoice in down ' | |
|                                             'payment or not') | |
|     product_id = fields.Many2one('product.product', | |
|                                  string='Down Payment Product', | |
|                                  domain=[('type', '=', 'service')], | |
|                                  default=_default_product_id, | |
|                                  help='To add the down payment product') | |
|     deduct_down_payments = fields.Boolean(string='Deduct down payments', | |
|                                           default=False, | |
|                                           help='To mention the down payment ' | |
|                                                'amount deduct to next invoice') | |
|     amount = fields.Float(string='Down Payment Amount', digits='Account', | |
|                           help="The percentage of amount to be bill in " | |
|                                "advance, taxes excluded.") | |
|     currency_id = fields.Many2one('res.currency', string='Currency', | |
|                                   default=_default_currency_id, | |
|                                   help='Default company currency') | |
|     fixed_amount = fields.Monetary(string='Down Payment Amount (Fixed)', | |
|                                    help="The fixed amount to be bill in " | |
|                                         "advance, taxes excluded.") | |
|     deposit_account_id = fields.Many2one('account.account', | |
|                                          string="Income Account", | |
|                                          domain=[('deprecated', '=', False)], | |
|                                          help="Account used for deposits") | |
|     deposit_taxes_ids = fields.Many2many('account.tax', | |
|                                          string="Customer Taxes", | |
|                                          domain=[ | |
|                                              ('type_tax_use', '=', 'purchase')], | |
|                                          help="Taxes used for deposits") | |
| 
 | |
|     def _get_advance_details(self, order): | |
|         """Function to find get the down payment amount and purchase order""" | |
|         if self.advance_payment_method == 'percentage': | |
|             if all(self.product_id.taxes_id.mapped('price_include')): | |
|                 amount = order.amount_total * self.amount / 100 | |
|             else: | |
|                 amount = order.amount_untaxed * self.amount / 100 | |
|             name = _("Down payment of %s%%") % (self.amount) | |
|         else: | |
|             amount = self.fixed_amount | |
|             name = _('Down Payment') | |
|         context = {'lang': order.partner_id.lang} | |
|         del context | |
|         return amount, name | |
| 
 | |
|     def _prepare_po_line(self, order, tax_ids, amount): | |
|         """Function to getting the purchase order line data""" | |
|         context = {'lang': order.partner_id.lang} | |
|         po_values = { | |
|             'name': _('Down Payment: %s / %s') % ( | |
|                 time.strftime('%m-%Y-%d'), order.name), | |
|             'price_unit': amount, | |
|             'product_uom_qty': 0.0, | |
|             'product_qty': 0.0, | |
|             'order_id': order.id, | |
|             'product_uom': self.product_id.uom_id.id, | |
|             'product_id': self.product_id.id, | |
|             'taxes_id': [(6, 0, tax_ids)], | |
|             'is_downpayment': True, | |
|             'sequence': order.order_line and order.order_line[ | |
|                 -1].sequence + 1 or 10, | |
|             'date_planned': datetime.today(), | |
|         } | |
|         del context | |
|         return po_values | |
| 
 | |
|     def _prepare_invoice_values(self, order, name, amount, so_line): | |
|         """Function for take invoice values""" | |
|         invoice_vals = { | |
|             'ref': order.partner_ref or '', | |
|             'move_type': 'in_invoice', | |
|             'invoice_origin': order.name, | |
|             'invoice_user_id': order.user_id.id, | |
|             'narration': order.notes, | |
|             'partner_id': order.partner_id.id, | |
|             'fiscal_position_id': ( | |
|                     order.fiscal_position_id or order.fiscal_position_id._get_fiscal_position( | |
|                 order.partner_id)).id, | |
|             'currency_id': order.currency_id.id, | |
|             'payment_reference': order.partner_ref or '', | |
|             'invoice_payment_term_id': order.payment_term_id.id, | |
|             'partner_bank_id': order.company_id.partner_id.bank_ids[:1].id, | |
|             'invoice_line_ids': [(0, 0, { | |
|                 'name': name, | |
|                 'price_unit': amount, | |
|                 'quantity': 1.0, | |
|                 'product_id': self.product_id.id, | |
|                 'purchase_line_id': so_line.id, | |
|                 'product_uom_id': so_line.product_uom.id, | |
|             })], | |
|         } | |
|         return invoice_vals | |
| 
 | |
|     def _create_bill(self, order, so_line, amount): | |
|         """Function for creating the purchase bill""" | |
|         if ( | |
|                 self.advance_payment_method == 'percentage' and self.amount <= 0.00) or ( | |
|                 self.advance_payment_method == 'fixed' and self.fixed_amount <= 0.00): | |
|             raise UserError( | |
|                 _('The value of the down payment amount must be positive.')) | |
|         amount, name = self._get_advance_details(order) | |
|         invoice_vals = self._prepare_invoice_values(order, name, amount, | |
|                                                     so_line) | |
|         if order.fiscal_position_id: | |
|             invoice_vals['fiscal_position_id'] = order.fiscal_position_id.id | |
|         invoice = self.env['account.move'].with_company(order.company_id) \ | |
|             .sudo().create(invoice_vals).with_user(self.env.uid) | |
|         return invoice | |
| 
 | |
|     def _prepare_down_payment_product_values(self): | |
|         return { | |
|             'name': _('Down payment'), | |
|             'type': 'service', | |
|             'invoice_policy': 'order', | |
|             'company_id': False, | |
|             'property_account_income_id': self.deposit_account_id.id, | |
|             'taxes_id': [Command.set(self.deposit_taxes_ids.ids)], | |
|         } | |
| 
 | |
|     def action_create_advance_bill(self): | |
|         """Function for creating purchase down payment bill""" | |
|         purchase_order = self.env['purchase.order'].browse( | |
|             self._context.get('active_ids', [])) | |
|         if self.advance_payment_method == 'delivered': | |
|             if self.deduct_down_payments: | |
|                 purchase_order._deduct_payment(final=self.deduct_down_payments) | |
|             else: | |
|                 purchase_order.action_create_invoice() | |
|         else: | |
|             if not self.product_id: | |
|                 vals = self._prepare_down_payment_product_values() | |
|                 self.product_id = self.env['product.product'].create(vals) | |
|                 self.env['ir.config_parameter'].sudo().set_param( | |
|                     'purchase_down_payment.po_deposit_default_product_id', | |
|                     self.product_id.id) | |
|             purchase_line_obj = self.env['purchase.order.line'] | |
|             for order in purchase_order: | |
|                 amount, name = self._get_advance_details(order) | |
|                 if self.product_id.invoice_policy != 'order': | |
|                     raise UserError( | |
|                         _('The product used to invoice a down payment should ' | |
|                           'have an invoice policy set to "Ordered ' | |
|                           'quantities". Please update your deposit product to ' | |
|                           'be able to create a deposit invoice.')) | |
|                 if self.product_id.type != 'service': | |
|                     raise UserError( | |
|                         _("The product used to invoice a down payment should " | |
|                           "be of type 'Service'. Please use another product " | |
|                           "or update this product.")) | |
|                 taxes = self.product_id.taxes_id.filtered( | |
|                     lambda | |
|                         r: not order.company_id or r.company_id == order.company_id) | |
|                 tax_ids = order.fiscal_position_id.map_tax(taxes).ids | |
|                 po_line_values = self._prepare_po_line(order, | |
|                                                        tax_ids, amount) | |
|                 po_line = purchase_line_obj.create(po_line_values) | |
|                 self._create_bill(order, po_line, amount) | |
|                 if self._context.get('open_invoices', False): | |
|                     return purchase_order.action_view_invoice() | |
|             return {'type': 'ir.actions.act_window_close'} | |
|         if self._context.get('open_invoices', False): | |
|             return purchase_order.action_view_invoice() | |
|         return {'type': 'ir.actions.act_window_close'}
 | |
| 
 |