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.
		
		
		
		
		
			
		
			
				
					
					
						
							201 lines
						
					
					
						
							8.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							201 lines
						
					
					
						
							8.5 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ################################################################################ | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| # | |
| #    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |
| #    Author: Ashwin T (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. | |
| ############################################################################## | |
| from odoo import api, models, _ | |
| from odoo.tools import float_is_zero | |
| from itertools import groupby | |
| from odoo.exceptions import UserError | |
| 
 | |
| 
 | |
| class PurchaseOrder(models.Model): | |
|     """ | |
|         This class is created for inherited model Purchase Order. | |
|  | |
|         Methods: _nothing_to_invoice_error(self): Function for showing the | |
|         user error while there is no products are received for the purchase | |
|         order. | |
|  | |
|             _deduct_payment(self): This function is for deducting the down | |
|             payment amount from next down payment invoice. | |
|  | |
|             _prepare_down_payment_section_line(self): Function for creating | |
|             dict of values to create a new purchase down payment section. | |
|  | |
|             _get_invoiceable_lines(self): | |
|                 Function for returning the orders invoiceable lines. | |
|  | |
|         """ | |
|     _inherit = 'purchase.order' | |
| 
 | |
|     @api.model | |
|     def _nothing_to_invoice_error(self): | |
|         """Function to showing user error while there is no products are | |
|         received for the purchase order""" | |
|         return UserError(_( | |
|             "There is nothing to bill!\n\n" | |
|             "Reason(s) of this behavior could be:\n" | |
|             "- You should recieve your products before billing them: " | |
|             "Click on the \"truck\" icon " | |
|             "(top-right of your screen) and follow instructions.\n" | |
|             "- You should modify the control policy of your product: Open the " | |
|             "product, go to the " | |
|             "\"Purchase\" tab and modify control policy from \"On received " | |
|             "quantities\" to \"On ordered" | |
|             "quantities\"." | |
|         )) | |
| 
 | |
|     def _deduct_payment(self, grouped=False, final=False, date=None): | |
|         """ | |
|         Create the Bill associated to the PO. | |
|         :returns: list of created invoices | |
|         """ | |
|         invoice_vals_list = [] | |
|         invoice_item_sequence = 0  # Incremental sequencing to keep the lines | |
|         # order on the invoice. | |
|         for order in self: | |
|             order = order.with_company(order.company_id) | |
|             invoice_vals = order._prepare_invoice() | |
|             invoiceable_lines = order._get_invoiceable_lines(final) | |
|             if not any(not line.display_type for line in invoiceable_lines): | |
|                 continue | |
|             invoice_line_vals = [] | |
|             down_payment_section_added = False | |
|             for line in invoiceable_lines: | |
|                 if not down_payment_section_added and line.is_downpayment: | |
|                     invoice_line_vals.append( | |
|                         (0, 0, order._prepare_down_payment_section_line( | |
|                             sequence=invoice_item_sequence, | |
|                         )), | |
|                     ) | |
|                     down_payment_section_added = True | |
|                     invoice_item_sequence += 1 | |
|                 invoice_line_vals.append( | |
|                     (0, 0, line._prepare_invoice_line( | |
|                         sequence=invoice_item_sequence, | |
|                     )), | |
|                 ) | |
|                 invoice_item_sequence += 1 | |
|             invoice_vals['invoice_line_ids'] += invoice_line_vals | |
|             invoice_vals_list.append(invoice_vals) | |
|         if not invoice_vals_list: | |
|             raise self._nothing_to_invoice_error() | |
|         if not grouped: | |
|             new_invoice_vals_list = [] | |
|             invoice_grouping_keys = self._get_invoice_grouping_keys() | |
|             invoice_vals_list = sorted( | |
|                 invoice_vals_list, | |
|                 key=lambda x: [ | |
|                     x.get(grouping_key) for grouping_key in | |
|                     invoice_grouping_keys | |
|                 ] | |
|             ) | |
|             for grouping_keys, invoices in groupby(invoice_vals_list, | |
|                                                    key=lambda x: [ | |
|                                                        x.get(grouping_key) for | |
|                                                        grouping_key in | |
|                                                        invoice_grouping_keys]): | |
|                 origins = set() | |
|                 payment_refs = set() | |
|                 refs = set() | |
|                 ref_invoice_vals = None | |
|                 for invoice_vals in invoices: | |
|                     if not ref_invoice_vals: | |
|                         ref_invoice_vals = invoice_vals | |
|                     else: | |
|                         ref_invoice_vals['invoice_line_ids'] += invoice_vals[ | |
|                             'invoice_line_ids'] | |
|                     origins.add(invoice_vals['invoice_origin']) | |
|                     payment_refs.add(invoice_vals['payment_reference']) | |
|                     refs.add(invoice_vals['ref']) | |
|                 ref_invoice_vals.update({ | |
|                     'ref': ', '.join(refs)[:2000], | |
|                     'invoice_origin': ', '.join(origins), | |
|                     'payment_reference': len( | |
|                         payment_refs) == 1 and payment_refs.pop() or False, | |
|                 }) | |
|                 new_invoice_vals_list.append(ref_invoice_vals) | |
|             invoice_vals_list = new_invoice_vals_list | |
|         if len(invoice_vals_list) < len(self): | |
|             PurchaseOrderLine = self.env['purchase.order.line'] | |
|             for invoice in invoice_vals_list: | |
|                 sequence = 1 | |
|                 for line in invoice['invoice_line_ids']: | |
|                     line[2][ | |
|                         'sequence'] = PurchaseOrderLine._get_invoice_line_sequence( | |
|                         new=sequence, | |
|                         old=line[2]['sequence']) | |
|                     sequence += 1 | |
|         moves = self.env['account.move'].sudo().with_context( | |
|             default_move_type='out_invoice').create(invoice_vals_list) | |
|         return moves | |
| 
 | |
|     @api.model | |
|     def _prepare_down_payment_section_line(self, **optional_values): | |
|         """ | |
|         Prepare the dict of values to create a new down payment section for a | |
|         purchase order line. :param optional_values: any parameter that | |
|         should be added to the returned down payment section | |
|         """ | |
|         context = {'lang': self.partner_id.lang} | |
|         down_payments_section_line = { | |
|             'display_type': 'line_section', | |
|             'name': _('Down Payments'), | |
|             'product_id': False, | |
|             'product_uom_id': False, | |
|             'quantity': 0, | |
|             'discount': 0, | |
|             'price_unit': 0, | |
|             'account_id': False | |
|         } | |
|         del context | |
|         if optional_values: | |
|             down_payments_section_line.update(optional_values) | |
|         return down_payments_section_line | |
| 
 | |
|     def _get_invoice_grouping_keys(self): | |
|         """Return invoice grouping keys""" | |
|         return ['company_id', 'partner_id', 'currency_id'] | |
| 
 | |
|     def _get_invoiceable_lines(self, final=False): | |
|         """Return the invoiceable lines for order `self`.""" | |
|         down_payment_line_ids = [] | |
|         invoiceable_line_ids = [] | |
|         pending_section = None | |
|         precision = self.env['decimal.precision'].precision_get( | |
|             'Product Unit of Measure') | |
|         for line in self.order_line: | |
|             line.display_type = '' | |
|             if line.display_type == 'line_section': | |
|                 pending_section = line | |
|                 continue | |
|             if line.display_type != 'line_note' and float_is_zero( | |
|                     line.qty_to_invoice, precision_digits=precision): | |
|                 continue | |
|             if (line.qty_to_invoice > 0 or ( | |
|                     line.qty_to_invoice < 0 and final) or line.display_type == | |
|                     'line_note'): | |
|                 if line.is_downpayment: | |
|                     down_payment_line_ids.append(line.id) | |
|                     continue | |
|                 if pending_section: | |
|                     invoiceable_line_ids.append(pending_section.id) | |
|                     pending_section = None | |
|                 invoiceable_line_ids.append(line.id) | |
|         return self.env['purchase.order.line'].browse( | |
|             invoiceable_line_ids + down_payment_line_ids)
 | |
| 
 |