import base64 import io import logging try: import xlsxwriter except ImportError: xlsxwriter = None from odoo import models, _ from odoo.exceptions import UserError _logger = logging.getLogger(__name__) class PurchaseOrder(models.Model): _inherit = 'purchase.order' def action_export_bc(self): self.ensure_one() if not xlsxwriter: raise UserError(_("The Python library 'xlsxwriter' is required. Please install it.")) output = io.BytesIO() workbook = xlsxwriter.Workbook(output, {'in_memory': True}) header_format = workbook.add_format({ 'bold': True, 'align': 'center', 'valign': 'vcenter', 'border': 1 }) headers = ["Type", "Item No.", "Description", "Qty", "Satuan"] lines_by_category = {} for line in self.order_line: # Handle display_type like line_section or line_note which shouldn't be exported as items if line.display_type in ('line_section', 'line_note'): continue categ_name = line.product_id.categ_id.name or 'Uncategorized' # Sheet names in Excel cannot exceed 31 characters categ_name = str(categ_name)[:31] # Excel sheet names cannot contain certain characters like :, \\, /, ?, *, [, ] invalid_chars = [':', '\\', '/', '?', '*', '[', ']'] for char in invalid_chars: categ_name = categ_name.replace(char, ' ') if categ_name not in lines_by_category: lines_by_category[categ_name] = [] lines_by_category[categ_name].append(line) if not lines_by_category: raise UserError(_("There are no order lines to export.")) for categ_name, lines in lines_by_category.items(): sheet = workbook.add_worksheet(categ_name) for col_num, header in enumerate(headers): sheet.write(0, col_num, header, header_format) row_num = 1 for line in lines: item_type = "Item" # Check custom field x_studio_bc_item_id on product if 'x_studio_bc_item_id' in line.product_id._fields and line.product_id.x_studio_bc_item_id: item_no = line.product_id.x_studio_bc_item_id else: item_no = '' description = line.product_id.name or line.name or '' qty = line.product_qty # Determine packaging or unit if 'product_packaging_id' in line._fields and line.product_packaging_id: satuan = line.product_packaging_id.name else: satuan = line.product_uom_id.name or '' sheet.write(row_num, 0, item_type) sheet.write(row_num, 1, item_no) sheet.write(row_num, 2, description) sheet.write(row_num, 3, qty) sheet.write(row_num, 4, satuan) row_num += 1 workbook.close() output.seek(0) attachment_name = f"PO_Export_BC_{self.name.replace('/', '_')}.xlsx" attachment = self.env['ir.attachment'].create({ 'name': attachment_name, 'type': 'binary', 'datas': base64.b64encode(output.read()), 'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) return { 'type': 'ir.actions.act_url', 'url': f'/web/content/{attachment.id}?download=true', 'target': 'self', }