diff --git a/wizard/pos_export_bc_wizard.py b/wizard/pos_export_bc_wizard.py index f425d69..c226587 100644 --- a/wizard/pos_export_bc_wizard.py +++ b/wizard/pos_export_bc_wizard.py @@ -12,6 +12,11 @@ try: except ImportError: xlsxwriter = None +try: + import xlwt +except ImportError: + xlwt = None + class PosExportBcWizard(models.TransientModel): _name = 'pos.export.bc.wizard' _description = 'POS Export BC Format Wizard' @@ -23,7 +28,7 @@ class PosExportBcWizard(models.TransientModel): return self._generate_export(old_format=False) def action_export_old_popcorn(self): - return self._generate_export(old_format=True) + return self._generate_export_xls() def _generate_export(self, old_format=False): self.ensure_one() @@ -37,9 +42,10 @@ class PosExportBcWizard(models.TransientModel): workbook = xlsxwriter.Workbook(output, {'in_memory': True}) # Define formats - header_format = workbook.add_format({ - 'bold': True, 'align': 'center', 'valign': 'vcenter', 'border': 1 - }) + header_kwargs = {'bold': True, 'align': 'center', 'valign': 'vcenter'} + if not old_format: + header_kwargs['border'] = 1 + header_format = workbook.add_format(header_kwargs) title_format = workbook.add_format({ 'bold': True, 'align': 'center', 'valign': 'vcenter', 'font_color': 'red', 'size': 14 }) @@ -129,14 +135,26 @@ class PosExportBcWizard(models.TransientModel): is_first_line = True for line in order.lines: - sheet.write(row_num, 0, order_no) - if local_date: - date_str = local_date.strftime('%d-%m-%Y %H:%M:%S') - sheet.write_string(row_num, 1, date_str) - - sheet.write(row_num, 2, outlet) - sheet.write(row_num, 3, table_customer) - sheet.write(row_num, 4, invoice) + if old_format and line.price_unit < 0: + continue + + if old_format and not is_first_line: + sheet.write_string(row_num, 0, "") + sheet.write_string(row_num, 1, "") + sheet.write_string(row_num, 2, "") + sheet.write_string(row_num, 3, "") + sheet.write_string(row_num, 4, "") + else: + sheet.write(row_num, 0, order_no) + if local_date: + date_str = local_date.strftime('%d-%m-%Y %H:%M:%S') + sheet.write_string(row_num, 1, date_str) + else: + sheet.write_string(row_num, 1, "") + + sheet.write(row_num, 2, outlet) + sheet.write(row_num, 3, table_customer) + sheet.write(row_num, 4, invoice) category = line.product_id.pos_categ_ids[0].name if line.product_id.pos_categ_ids else '' sku = line.product_id.x_studio_bc_item_id if 'x_studio_bc_item_id' in line.product_id._fields and line.product_id.x_studio_bc_item_id else '' @@ -235,3 +253,182 @@ class PosExportBcWizard(models.TransientModel): 'url': f'/web/content/{attachment.id}?download=true', 'target': 'self', } + + def _generate_export_xls(self): + self.ensure_one() + if not xlwt: + raise UserError(_("The Python library 'xlwt' is required. Please install it with 'pip install xlwt'.")) + + if self.start_date > self.end_date: + raise UserError(_("Start Date must be earlier or equal to End Date.")) + + output = io.BytesIO() + workbook = xlwt.Workbook(encoding='utf-8') + + # Define formats for xlwt + header_format = xlwt.easyxf('font: bold on; align: horiz center, vert center;') + title_format = xlwt.easyxf('font: bold on, color red, height 280; align: horiz center, vert center;') # height is in twips (14 * 20) + subtitle_format = xlwt.easyxf('font: bold on, color black, height 240; align: horiz center, vert center;') # 12 * 20 + date_time_format = xlwt.easyxf(num_format_str='DD-MM-YYYY HH:MM:SS') + number_format = xlwt.easyxf(num_format_str='#,##0.00') + + headers = [ + "No", "Date", "Outlet", "Table/Customer", "Invoice", "Category", "SKU", "Product", "Quantity", + "Price Type", "Price", "Price Cut", "Subtotal", "Discount", "Tax", "Service", "Rounding", + "Charge", "Paid", "Pax", "Return", "Refund", "Payment", "Note", "Dinein", "User" + ] + + # Datetime timezone conversion + user_tz_str = self.env.user.tz or 'UTC' + user_tz = pytz.timezone(user_tz_str) + + start_datetime = datetime.combine(self.start_date, datetime.min.time()) + end_datetime = datetime.combine(self.end_date, datetime.max.time()) + + start_utc = user_tz.localize(start_datetime).astimezone(pytz.UTC).replace(tzinfo=None) + end_utc = user_tz.localize(end_datetime).astimezone(pytz.UTC).replace(tzinfo=None) + + def write_sheet(sheet_name, domain): + sheet = workbook.add_sheet(sheet_name) + + # Title + sheet.write_merge(0, 0, 0, 25, 'MIE MAPAN', title_format) + sheet.write_merge(1, 1, 0, 25, 'INVOICES', subtitle_format) + + # Period + start_dt_str = start_datetime.strftime('%d-%m-%Y 00:00') + end_dt_str = end_datetime.strftime('%d-%m-%Y 23:59') + sheet.write(2, 0, 'Period') + sheet.write(2, 1, ':') + sheet.write(2, 2, f"{start_dt_str} - {end_dt_str}") + + # Headers + for col_num, header in enumerate(headers): + sheet.write(4, col_num, header, header_format) + + row_num = 5 + orders = self.env['pos.order'].search(domain, order='date_order asc') + + order_no = 1 + for order in orders: + if 'refund_orders_count' in order._fields and order.refund_orders_count > 0: + continue + + local_date = order.date_order.replace(tzinfo=pytz.UTC).astimezone(user_tz) if order.date_order else False + + outlet = order.config_id.name or '' + table = str(order.table_id.table_number) if 'table_id' in order._fields and order.table_id else '' + customer = order.partner_id.name or '' + floating_order_name = order.floating_order_name if 'floating_order_name' in order._fields and order.floating_order_name else '' + table_customer = table or floating_order_name or customer + invoice = order.pos_reference or order.name + + subtotal = sum(l.price_subtotal for l in order.lines) + discount_order = sum(abs(l.price_subtotal) for l in order.lines if l.price_unit < 0) + tax = order.amount_tax + charge = order.amount_total + paid = sum(p.amount for p in order.payment_ids if p.amount > 0) + pax = order.customer_count if 'customer_count' in order._fields else 1 + return_amt = order.amount_return if 'amount_return' in order._fields else (paid - charge if paid > charge else 0) + + payment_methods = ', '.join(order.payment_ids.mapped('payment_method_id.name')) + note = order.note if 'note' in order._fields else '' + if 'table_id' in order._fields and order.table_id: + dinein = "dinein" + else: + dinein = "takeaway" + user = order.user_id.name or '' + + is_first_line = True + for line in order.lines: + if line.price_unit < 0: + continue + + if not is_first_line: + sheet.write(row_num, 0, "") + sheet.write(row_num, 1, "") + sheet.write(row_num, 2, "") + sheet.write(row_num, 3, "") + sheet.write(row_num, 4, "") + else: + sheet.write(row_num, 0, order_no) + if local_date: + date_str = local_date.strftime('%d-%m-%Y %H:%M:%S') + sheet.write(row_num, 1, date_str) + else: + sheet.write(row_num, 1, "") + + sheet.write(row_num, 2, outlet) + sheet.write(row_num, 3, table_customer) + sheet.write(row_num, 4, invoice) + + category = line.product_id.pos_categ_ids[0].name if line.product_id.pos_categ_ids else '' + sku = line.product_id.x_studio_bc_item_id if 'x_studio_bc_item_id' in line.product_id._fields and line.product_id.x_studio_bc_item_id else '' + product_name = line.product_id.name or '' + qty = line.qty + + price_type = order.pricelist_id.name or 'DEFAULT' + price = line.price_unit + price_cut = (line.price_unit * line.discount / 100) if line.discount else 0.0 + + sheet.write(row_num, 5, category) + sheet.write(row_num, 6, sku) + sheet.write(row_num, 7, product_name) + sheet.write(row_num, 8, qty) + sheet.write(row_num, 9, price_type) + sheet.write(row_num, 10, price, number_format) + sheet.write(row_num, 11, price_cut, number_format) + + if is_first_line: + sheet.write(row_num, 12, subtotal, number_format) + sheet.write(row_num, 13, discount_order, number_format) + sheet.write(row_num, 14, tax, number_format) + sheet.write(row_num, 15, 0, number_format) # Service + sheet.write(row_num, 16, 0, number_format) # Rounding + sheet.write(row_num, 17, charge, number_format) + sheet.write(row_num, 18, paid, number_format) + sheet.write(row_num, 19, pax) + sheet.write(row_num, 20, return_amt, number_format) + sheet.write(row_num, 21, 0, number_format) # Refund + sheet.write(row_num, 22, payment_methods) + sheet.write(row_num, 23, note) + sheet.write(row_num, 24, dinein) + sheet.write(row_num, 25, user) + + is_first_line = False + + row_num += 1 + order_no += 1 + + domain_base = [('date_order', '>=', start_utc), ('date_order', '<=', end_utc)] + write_sheet('Invoice', domain_base + [('amount_total', '>=', 0)]) + write_sheet('Refund', domain_base + [('amount_total', '<', 0)]) + + workbook.save(output) + output.seek(0) + + company = self.env.company + company_ident = (company.company_registry or company.name or '').upper().replace(' ', '') + + # Max length logic + company_len = 10 + company_ident = company_ident[:company_len] + + start_str = self.start_date.strftime('%y%m%d') + end_str = self.end_date.strftime('%y%m%d') + + file_name = f"POS_{company_ident}_{start_str}_to_{end_str}_OLD.xls" + + # Save as an ir.attachment and return action to download + attachment = self.env['ir.attachment'].create({ + 'name': file_name, + 'type': 'binary', + 'datas': base64.b64encode(output.read()), + 'mimetype': 'application/vnd.ms-excel' + }) + + return { + 'type': 'ir.actions.act_url', + 'url': f'/web/content/{attachment.id}?download=true', + 'target': 'self', + }