Generate in .XLS format, remove header border, exclude negative price line (discount), remove duplicate value in

This commit is contained in:
Abdul Aziz Amrullah 2026-06-11 09:50:36 +07:00
parent 8cf0a6c037
commit 5aa9c0f0f5

View File

@ -12,6 +12,11 @@ try:
except ImportError: except ImportError:
xlsxwriter = None xlsxwriter = None
try:
import xlwt
except ImportError:
xlwt = None
class PosExportBcWizard(models.TransientModel): class PosExportBcWizard(models.TransientModel):
_name = 'pos.export.bc.wizard' _name = 'pos.export.bc.wizard'
_description = 'POS Export BC Format Wizard' _description = 'POS Export BC Format Wizard'
@ -23,7 +28,7 @@ class PosExportBcWizard(models.TransientModel):
return self._generate_export(old_format=False) return self._generate_export(old_format=False)
def action_export_old_popcorn(self): 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): def _generate_export(self, old_format=False):
self.ensure_one() self.ensure_one()
@ -37,9 +42,10 @@ class PosExportBcWizard(models.TransientModel):
workbook = xlsxwriter.Workbook(output, {'in_memory': True}) workbook = xlsxwriter.Workbook(output, {'in_memory': True})
# Define formats # Define formats
header_format = workbook.add_format({ header_kwargs = {'bold': True, 'align': 'center', 'valign': 'vcenter'}
'bold': True, 'align': 'center', 'valign': 'vcenter', 'border': 1 if not old_format:
}) header_kwargs['border'] = 1
header_format = workbook.add_format(header_kwargs)
title_format = workbook.add_format({ title_format = workbook.add_format({
'bold': True, 'align': 'center', 'valign': 'vcenter', 'font_color': 'red', 'size': 14 'bold': True, 'align': 'center', 'valign': 'vcenter', 'font_color': 'red', 'size': 14
}) })
@ -129,14 +135,26 @@ class PosExportBcWizard(models.TransientModel):
is_first_line = True is_first_line = True
for line in order.lines: for line in order.lines:
sheet.write(row_num, 0, order_no) if old_format and line.price_unit < 0:
if local_date: continue
date_str = local_date.strftime('%d-%m-%Y %H:%M:%S')
sheet.write_string(row_num, 1, date_str) if old_format and not is_first_line:
sheet.write_string(row_num, 0, "")
sheet.write(row_num, 2, outlet) sheet.write_string(row_num, 1, "")
sheet.write(row_num, 3, table_customer) sheet.write_string(row_num, 2, "")
sheet.write(row_num, 4, invoice) 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 '' 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 '' 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', 'url': f'/web/content/{attachment.id}?download=true',
'target': 'self', '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',
}