pos_export_bc/wizard/pos_export_bc_wizard.py

252 lines
12 KiB
Python

import base64
import io
from datetime import datetime
import pytz
from odoo import api, fields, models, _
from odoo.exceptions import UserError
import odoo.tools
try:
import xlsxwriter
except ImportError:
xlsxwriter = None
class PosExportBcWizard(models.TransientModel):
_name = 'pos.export.bc.wizard'
_description = 'POS Export BC Format Wizard'
start_date = fields.Date(string="Start Date", required=True, default=fields.Date.context_today)
end_date = fields.Date(string="End Date", required=True, default=fields.Date.context_today)
def action_export_bc(self):
return self._generate_export(old_format=False)
def action_export_old_popcorn(self):
return self._generate_export(old_format=True)
def _generate_export(self, old_format=False):
self.ensure_one()
if not xlsxwriter:
raise UserError(_("The Python library 'xlsxwriter' is required. Please install it."))
if self.start_date > self.end_date:
raise UserError(_("Start Date must be earlier or equal to End Date."))
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
# Define formats
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
})
subtitle_format = workbook.add_format({
'bold': True, 'align': 'center', 'valign': 'vcenter', 'font_color': 'black', 'size': 12
})
date_format = workbook.add_format({'num_format': 'dd-mm-yyyy'})
date_time_format = workbook.add_format({'num_format': 'dd-mm-yyyy hh:mm:ss'})
number_format = workbook.add_format({'num_format': '#,##0.00'})
if old_format:
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"
]
else:
headers = [
"No", "Date", "Outlet", "Table/Customer", "Invoice", "Category", "SKU", "Product", "Quantity",
"Price Type", "Price", "Price Cut", "Subtotal", "Discount", "Tax", "Service", "Takeaway Charge",
"Packaging Fee", "Rounding", "Charge", "Paid", "Pax", "Paid At", "Return", "Refund", "Payment",
"Note", "Dinein", "User", "Promo", "Order from", "Nama Penerima", "Alamat Penerima", "Link Maps"
]
# 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_worksheet(sheet_name)
# Title
sheet.merge_range('A1:Z1' if old_format else 'A1:AF1', 'MIE MAPAN', title_format)
sheet.merge_range('A2:Z2' if old_format else 'A2:AF2', '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('A3', 'Period')
sheet.write('B3', ':')
sheet.write('C3', 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
paid = sum(p.amount for p in order.payment_ids if p.amount > 0)
if paid == 0:
continue
rounding = order.amount_paid - order.amount_total
charge = order.amount_paid
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 - order.amount_total if paid > order.amount_total 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 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 ''
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
if old_format:
sheet.write(row_num, 16, rounding, 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)
else:
sheet.write(row_num, 16, 0, number_format) # Takeaway Charge
sheet.write(row_num, 17, 0, number_format) # Packaging Fee
sheet.write(row_num, 18, rounding, number_format) # Rounding
sheet.write(row_num, 19, charge, number_format)
sheet.write(row_num, 20, paid, number_format)
sheet.write(row_num, 21, pax)
if local_date:
date_str = local_date.strftime('%d-%m-%Y %H:%M:%S')
sheet.write_string(row_num, 22, date_str)
sheet.write(row_num, 23, return_amt, number_format)
sheet.write(row_num, 24, 0, number_format) # Refund
sheet.write(row_num, 25, payment_methods)
sheet.write(row_num, 26, note)
sheet.write(row_num, 27, dinein)
sheet.write(row_num, 28, user)
sheet.write(row_num, 29, "") # Promo
sheet.write(row_num, 30, "cashier") # Order from
sheet.write(row_num, 31, "")
sheet.write(row_num, 32, "")
sheet.write(row_num, 33, "")
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.close()
output.seek(0)
company = self.env.company
company_ident = (company.company_registry or company.name or '').upper().replace(' ', '')
# Max length logic
# For old_format: len("POS__YYMMDD_to_YYMMDD_OLD.xlsx") = 30 chars -> leaves 10 chars for company_ident
# For new_format: len("POS__YYMMDD_to_YYMMDD.xlsx") = 26 chars -> leaves 14 chars for company_ident
company_len = 10 if old_format else 14
company_ident = company_ident[:company_len]
start_str = self.start_date.strftime('%y%m%d')
end_str = self.end_date.strftime('%y%m%d')
if old_format:
file_name = f"POS_{company_ident}_{start_str}_to_{end_str}_OLD.xlsx"
else:
file_name = f"POS_{company_ident}_{start_str}_to_{end_str}.xlsx"
# 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.openxmlformats-officedocument.spreadsheetml.sheet'
})
return {
'type': 'ir.actions.act_url',
'url': f'/web/content/{attachment.id}?download=true',
'target': 'self',
}