store/resources/js/lib/thermal-printer/encode-order-receipt.ts

134 lines
3.7 KiB
TypeScript

import ReceiptPrinterEncoder from '@point-of-sale/receipt-printer-encoder';
import { normalizeEncoderOptions } from '@/lib/thermal-printer/normalize-encoder-options';
import type { PaperSize } from '@/lib/thermal-printer/types';
import type { OrderListItem } from '@/types/order';
export type EncodeOrderReceiptOptions = {
storeName: string;
storeAddress: string;
paperSize: PaperSize;
language?: string | null;
codepageMapping?: string | null;
};
export function getPaperColumns(paperSize: PaperSize): number {
return paperSize === '58mm' ? 32 : 48;
}
function dashedLine(columns: number): string {
return '-'.repeat(columns);
}
export function encodeOrderReceipt(
order: OrderListItem,
options: EncodeOrderReceiptOptions,
): Uint8Array {
const columns = getPaperColumns(options.paperSize);
const amountColumnWidth = 14;
const labelColumnWidth = columns - amountColumnWidth;
const encoderOptions = normalizeEncoderOptions({
language: options.language,
codepageMapping: options.codepageMapping,
});
const encoder = new ReceiptPrinterEncoder({
language: encoderOptions.language,
codepageMapping: encoderOptions.codepageMapping,
columns,
});
const createdBy = order.created_by?.profile?.full_name
?? order.created_by?.username
?? '-';
encoder.initialize()
.align('center')
.bold(true)
.text('')
.newline()
.text(options.storeName)
.newline()
.bold(false)
.newline();
for (const addressLine of options.storeAddress.split('\n')) {
const trimmed = addressLine.trim();
if (trimmed) {
encoder.align('center').text(trimmed).newline();
}
}
encoder.align('center').line(dashedLine(columns));
encoder.align('left');
encoder.line(`No : ${order.order_number}`);
encoder.line(`Tgl : ${order.created_at_formatted}`);
if (order.customer) {
encoder.line(`Cust : ${order.customer.name}`);
}
encoder
.line(`Kasir: ${createdBy}`)
.line(dashedLine(columns));
for (const item of order.items) {
const productName = item.product_variant?.product?.name ?? '-';
const variantName = item.product_variant?.name ?? '-';
encoder
.line(productName)
.line(` ${variantName}`)
.table(
[
{ width: labelColumnWidth, align: 'left' },
{ width: amountColumnWidth, align: 'right' },
],
[[
`${item.quantity_formatted} x ${item.unit_price_formatted}`,
(rowEncoder) => rowEncoder.bold().text(item.subtotal_formatted).bold(false),
]],
)
.newline();
}
encoder.line(dashedLine(columns));
const summaryRows: string[][] = [
['Subtotal', order.subtotal_formatted],
['Diskon', order.discount_formatted],
];
if (order.nego_price_formatted) {
summaryRows.push(['Harga Nego', order.nego_price_formatted]);
}
summaryRows.push(['Total', order.total_amount_formatted]);
encoder.table(
[
{ width: labelColumnWidth, align: 'left' },
{ width: amountColumnWidth, align: 'right' },
],
summaryRows,
);
if (order.notes) {
encoder
.newline()
.line('Catatan:')
.line(order.notes);
}
encoder
.newline()
.line(dashedLine(columns))
.align('center')
.line('Terima kasih atas kunjungannya')
.line('Silakan berbelanja kembali')
.newline(3)
.cut();
return encoder.encode();
}