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.
		
		
		
		
		
			
		
			
				
					
					
						
							184 lines
						
					
					
						
							9.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							184 lines
						
					
					
						
							9.5 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ################################################################################ | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| # | |
| #    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |
| #    Author: Anfas Faisal K (odoo@cybrosys.info) | |
| # | |
| #    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. | |
| #    If not, see <http://www.gnu.org/licenses/>. | |
| # | |
| ################################################################################ | |
| import io | |
| import json | |
| import xlsxwriter | |
| from odoo import models | |
| from odoo.tools import date_utils | |
| 
 | |
| 
 | |
| class AccountMove(models.Model): | |
|     """Inherits the account move model""" | |
|     _inherit = 'mrp.bom' | |
| 
 | |
|     def action_print_bom_structure(self): | |
|         """Generate and export the BOM Structure Report in Excel format.""" | |
|         bom = self.env['mrp.bom'].browse(self.id) | |
|         candidates = bom.product_id or bom.product_tmpl_id.product_variant_ids | |
|         quantity = bom.product_qty | |
|         for product_variant_id in candidates.ids: | |
|             doc = self.env['report.mrp.report_bom_structure']._get_pdf_line( | |
|                 bom.id, product_id=product_variant_id, | |
|                 qty=quantity, unfolded=True) | |
|         return { | |
|             'type': 'ir.actions.report', | |
|             'data': {'model': 'mrp.bom', | |
|                      'options': json.dumps(doc, | |
|                                            default=date_utils.json_default), | |
|                      'output_format': 'xlsx', | |
|                      'report_name': 'BoM Structure', | |
|                      }, | |
|             'report_type': 'xlsx', | |
|         } | |
| 
 | |
|     def get_xlsx_report(self, data, response): | |
|         """ Generate an Excel report with BOM structure and cost.""" | |
|         output = io.BytesIO() | |
|         workbook = xlsxwriter.Workbook(output, {'in_memory': True}) | |
|         sheet = workbook.add_worksheet() | |
|         # Define cell formats | |
|         head = workbook.add_format( | |
|             {'align': 'left', 'bold': True, 'font_size': '20px'}) | |
|         format3 = workbook.add_format({'font_size': '15px', 'bold': True}) | |
|         format4 = workbook.add_format({'font_size': 10}) | |
|         format6 = workbook.add_format({'font_size': 10, 'bold': True}) | |
|         format7 = workbook.add_format({'font_size': 10, 'font_color': 'green'}) | |
|         format8 = workbook.add_format({'font_size': 10, 'font_color': 'red'}) | |
|         format9 = workbook.add_format({'font_size': 10, 'font_color': 'yellow'}) | |
|         # Check if there are components or lines in the data | |
|         if data and data.get('components') or data.get('lines'): | |
|             # Merge and format header cells | |
|             sheet.merge_range('A1:F2', 'BoM Structure & Cost', head) | |
|             sheet.merge_range('A4:Z5', data['name'], format3) | |
|             # Merge and format cells for product information | |
|             sheet.merge_range('A7:C7', 'Products', format6) | |
|             sheet.merge_range('D7:E7', 'Quantity', format6) | |
|             sheet.merge_range('F7:G7', 'Unit of Measure', format6) | |
|             sheet.merge_range('H7:I7', 'Ready to Produce', format6) | |
|             sheet.merge_range('J7:K7', 'Free to Use / On Hand', format6) | |
|             sheet.merge_range('L7:M7', 'Availability', format6) | |
|             sheet.merge_range('N7:O7', 'Lead Time', format6) | |
|             sheet.merge_range('P7:Q7', 'Route', format6) | |
|             sheet.merge_range('R7:S7', 'Product Cost', format6) | |
|             sheet.merge_range('T7:U7', 'BOM Cost', format6) | |
|             row_start = 9  # Starting row for data | |
|             currency_symbol = self.env.user.company_id.currency_id.symbol | |
|             # Iterate through lines in the data | |
|             for index, value in enumerate(data.get('lines')): | |
|                 # Calculate leading spaces based on the level | |
|                 if value['level'] != 0: | |
|                     space_td = '    ' * value['level'] | |
|                 else: | |
|                     space_td = '    ' | |
|                 # Merge and format cells for product name | |
|                 sheet.merge_range('A8:C8', data['name'], format6) | |
|                 sheet.merge_range('D8:E8', data['quantity'], format4) | |
|                 sheet.merge_range(f'A{index + row_start}:C{index + row_start}', | |
|                                   space_td + value['name'], format4) | |
|                 # Merge and format cells for quantity | |
|                 sheet.merge_range(f'D{index + row_start}:E{index + row_start}', | |
|                                   value['quantity'], format4) | |
|                 # Merge and format cells for unit of measure | |
|                 if 'uom' in value: | |
|                     sheet.merge_range( | |
|                         f'F{index + row_start}:G{index + row_start}', | |
|                         value['uom'], format4) | |
|                 # Merge and format cells for 'Ready to Produce' | |
|                 if 'producible_qty' in value: | |
|                     sheet.merge_range('H8:I8', data['producible_qty'], format4) | |
|                     sheet.merge_range( | |
|                         f'H{index + row_start}:I{index + row_start}', | |
|                         value['producible_qty'], format4) | |
|                 # Merge and format cells for 'Quantity Available / On Hand' | |
|                 if 'quantity_available' in value: | |
|                     quantity_available_on_hand = \ | |
|                         f"{value['quantity_available']} / {value['quantity_on_hand']}" | |
|                     sheet.merge_range( | |
|                         'J8:K8', f"{data['quantity_available']} / " | |
|                                  f"{data['quantity_on_hand']}", format4) | |
|                     sheet.merge_range( | |
|                         f'J{index + row_start}:K{index + row_start}', | |
|                         quantity_available_on_hand, format4) | |
|                 # Merge and format cells for 'Availability' | |
|                 if 'availability_display' in value: | |
|                     availability_main_text = data['availability_display'] | |
|                     availability_text = value['availability_display'] | |
|                     color_format_main = format7 if ( | |
|                             availability_main_text == 'Available') \ | |
|                         else ( | |
|                         format8 if availability_main_text == 'Not Available' | |
|                         else format9) | |
|                     color_format = format7 if availability_text == 'Available' \ | |
|                         else (format8 if availability_text == 'Not Available' | |
|                               else format9) | |
|                     sheet.merge_range( | |
|                         'L8:M8', availability_main_text, color_format_main) | |
|                     sheet.merge_range( | |
|                         f'L{index + row_start}:M{index + row_start}', | |
|                         availability_text, color_format) | |
|                 # Merge and format cells for 'Product Cost' | |
|                 if 'prod_cost' in value: | |
|                     prod_cost_with_symbol = f"{currency_symbol} {data['prod_cost']} " | |
|                     sheet.merge_range( | |
|                         'R8:S8', prod_cost_with_symbol, format4) | |
|                     sheet.merge_range( | |
|                         f'R{index + row_start}:S{index + row_start}', | |
|                         f"{currency_symbol} {value['prod_cost']}", format4) | |
|                 # Merge and format cells for 'BOM Cost' | |
|                 if 'bom_cost' in value: | |
|                     bom_cost_with_symbol = f" {currency_symbol} {data['bom_cost']}" | |
|                     sheet.merge_range( | |
|                         'T8:U8', bom_cost_with_symbol, format4) | |
|                     sheet.merge_range( | |
|                         f'T{index + row_start}:U{index + row_start}', | |
|                         f" {currency_symbol} {value['bom_cost']}", format4) | |
|                 # Merge and format cells for 'Route Info' | |
|                 if 'route_name' in value: | |
|                     route_info = f"{value['route_name']} {value['route_detail']}" | |
|                     sheet.merge_range( | |
|                         f'P{index + row_start}:Q{index + row_start}', | |
|                         route_info, format4) | |
|                 # Merge and format cells for 'Lead Time' | |
|                 if 'lead_time' in value: | |
|                     lead_time = value['lead_time'] | |
|                     lead_time_days = f"{int(lead_time)} days" if lead_time != 0.0 else "0 days" | |
|                     sheet.merge_range( | |
|                         f'N{index + row_start}:O{index + row_start}', | |
|                         lead_time_days, format4) | |
|                 # Check if 'prod_cost' is present in the data dictionary | |
|                 if 'prod_cost' in data: | |
|                     prod_cost_with_symbol = f"{currency_symbol} {data['prod_cost']}" | |
|                     bom_cost_with_symbol = f" {currency_symbol} {data['bom_cost']}" | |
|                     sheet.merge_range( | |
|                         f'D{index + row_start + 1}:E{index + row_start + 1}', | |
|                         'UnitCost', format6) | |
|                     sheet.merge_range( | |
|                         f'T{index + row_start + 1}:U{index + row_start + 1}', | |
|                         bom_cost_with_symbol, format4) | |
|                     sheet.merge_range( | |
|                         f'R{index + row_start + 1}:S{index + row_start + 1}', | |
|                         prod_cost_with_symbol, format4) | |
|         # Close the workbook, seek to the beginning, and stream the output | |
|         workbook.close() | |
|         output.seek(0) | |
|         response.stream.write(output.read()) | |
|         output.close()
 | |
| 
 |