252 lines
8.2 KiB
TypeScript
252 lines
8.2 KiB
TypeScript
import type { PaymentReport } from '@/types/services/analytic'
|
|
|
|
export class ExcelExportPaymentService {
|
|
/**
|
|
* Export Payment Method Report to Excel
|
|
*/
|
|
static async exportPaymentMethodToExcel(paymentData: PaymentReport, filename?: string) {
|
|
try {
|
|
// Dynamic import untuk xlsx library
|
|
const XLSX = await import('xlsx')
|
|
|
|
// Prepare data untuk Excel
|
|
const worksheetData: any[][] = []
|
|
|
|
// Header dengan report info (baris 1-2)
|
|
worksheetData.push(['LAPORAN METODE PEMBAYARAN']) // Row 0 - Main title
|
|
worksheetData.push([`Periode: ${paymentData.date_from.split('T')[0]} - ${paymentData.date_to.split('T')[0]}`]) // Row 1 - Period
|
|
worksheetData.push([]) // Empty row
|
|
|
|
// Add Summary Section
|
|
worksheetData.push(['RINGKASAN PERIODE']) // Section header
|
|
worksheetData.push([]) // Empty row
|
|
|
|
const summaryData = [
|
|
['Total Amount:', `Rp ${paymentData.summary.total_amount.toLocaleString('id-ID')}`],
|
|
['Total Orders:', paymentData.summary.total_orders.toString()],
|
|
['Total Payments:', paymentData.summary.total_payments.toString()],
|
|
['Average Order Value:', `Rp ${paymentData.summary.average_order_value.toLocaleString('id-ID')}`]
|
|
]
|
|
|
|
summaryData.forEach(row => {
|
|
worksheetData.push([row[0], row[1]]) // Only 2 columns needed
|
|
})
|
|
|
|
worksheetData.push([]) // Empty row
|
|
worksheetData.push([]) // Empty row
|
|
|
|
// Payment Method Details Section Header
|
|
worksheetData.push(['RINCIAN METODE PEMBAYARAN']) // Section header
|
|
worksheetData.push([]) // Empty row
|
|
|
|
// Header row untuk tabel payment method data
|
|
const headerRow = ['No', 'Metode Pembayaran', 'Tipe', 'Jumlah Order', 'Total Amount', 'Persentase']
|
|
worksheetData.push(headerRow)
|
|
|
|
// Add payment method data rows
|
|
paymentData.data.forEach((payment, index) => {
|
|
const rowData = [
|
|
index + 1, // No
|
|
payment.payment_method_name,
|
|
payment.payment_method_type.toUpperCase(),
|
|
payment.order_count,
|
|
payment.total_amount, // Store as number for Excel formatting
|
|
`${(payment.percentage ?? 0).toFixed(1)}%`
|
|
]
|
|
worksheetData.push(rowData)
|
|
})
|
|
|
|
// Add total row
|
|
const totalRow = ['TOTAL', '', '', paymentData.summary.total_orders, paymentData.summary.total_amount, '100.0%']
|
|
worksheetData.push(totalRow)
|
|
|
|
// Create workbook dan worksheet
|
|
const workbook = XLSX.utils.book_new()
|
|
const worksheet = XLSX.utils.aoa_to_sheet(worksheetData)
|
|
|
|
// Apply basic formatting
|
|
this.applyBasicFormatting(worksheet, worksheetData.length, XLSX)
|
|
|
|
// Add worksheet ke workbook
|
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Metode Pembayaran')
|
|
|
|
// Generate filename
|
|
const exportFilename = filename || this.generateFilename('Metode_Pembayaran')
|
|
|
|
// Download file
|
|
XLSX.writeFile(workbook, exportFilename)
|
|
|
|
return { success: true, filename: exportFilename }
|
|
} catch (error) {
|
|
console.error('Error exporting to Excel:', error)
|
|
return { success: false, error: 'Failed to export Excel file' }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply basic formatting (SheetJS compatible)
|
|
*/
|
|
private static applyBasicFormatting(worksheet: any, totalRows: number, XLSX: any) {
|
|
// Set column widths
|
|
const colWidths = [
|
|
{ wch: 8 }, // No
|
|
{ wch: 25 }, // Metode Pembayaran
|
|
{ wch: 12 }, // Tipe
|
|
{ wch: 15 }, // Jumlah Order
|
|
{ wch: 20 }, // Total Amount
|
|
{ wch: 12 } // Persentase
|
|
]
|
|
worksheet['!cols'] = colWidths
|
|
|
|
// Set row heights for better spacing
|
|
worksheet['!rows'] = [
|
|
{ hpt: 30 }, // Title row
|
|
{ hpt: 25 }, // Period row
|
|
{ hpt: 15 }, // Empty row
|
|
{ hpt: 25 }, // Summary header
|
|
{ hpt: 15 } // Empty row
|
|
]
|
|
|
|
// Merge cells untuk headers
|
|
const merges = [
|
|
{ s: { r: 0, c: 0 }, e: { r: 0, c: 5 } }, // Title (span across all columns)
|
|
{ s: { r: 1, c: 0 }, e: { r: 1, c: 5 } }, // Period (span across all columns)
|
|
{ s: { r: 3, c: 0 }, e: { r: 3, c: 5 } } // Summary header (span across all columns)
|
|
]
|
|
|
|
// Find and add merge for payment method details header
|
|
for (let i = 0; i < totalRows; i++) {
|
|
const cell = worksheet[XLSX.utils.encode_cell({ r: i, c: 0 })]
|
|
if (cell && cell.v === 'RINCIAN METODE PEMBAYARAN') {
|
|
merges.push({ s: { r: i, c: 0 }, e: { r: i, c: 5 } }) // Span across all columns
|
|
break
|
|
}
|
|
}
|
|
|
|
worksheet['!merges'] = merges
|
|
|
|
// Apply number formatting untuk currency cells
|
|
this.applyNumberFormatting(worksheet, totalRows, XLSX)
|
|
}
|
|
|
|
/**
|
|
* Apply number formatting for currency and styling
|
|
*/
|
|
private static applyNumberFormatting(worksheet: any, totalRows: number, XLSX: any) {
|
|
// Find table data start (after header row)
|
|
let dataStartRow = -1
|
|
for (let i = 0; i < totalRows; i++) {
|
|
const cell = worksheet[XLSX.utils.encode_cell({ r: i, c: 0 })]
|
|
if (cell && cell.v === 'No') {
|
|
dataStartRow = i + 1
|
|
break
|
|
}
|
|
}
|
|
|
|
if (dataStartRow === -1) return
|
|
|
|
// Count actual data rows (excluding total row)
|
|
const dataRowsCount = totalRows - dataStartRow - 1 // -1 for total row
|
|
|
|
// Apply currency formatting to Total Amount column (column 4 - index 4)
|
|
for (let row = dataStartRow; row <= dataStartRow + dataRowsCount; row++) {
|
|
// Include total row
|
|
const cellAddress = XLSX.utils.encode_cell({ r: row, c: 4 }) // Total Amount column
|
|
const cell = worksheet[cellAddress]
|
|
|
|
if (cell && typeof cell.v === 'number') {
|
|
// Apply Indonesian currency format
|
|
cell.z = '#,##0'
|
|
cell.t = 'n'
|
|
}
|
|
}
|
|
|
|
// Apply styling to header row
|
|
const headerRowIndex = dataStartRow - 1
|
|
for (let col = 0; col < 6; col++) {
|
|
const cellAddress = XLSX.utils.encode_cell({ r: headerRowIndex, c: col })
|
|
const cell = worksheet[cellAddress]
|
|
|
|
if (cell) {
|
|
// Apply bold formatting (basic approach for SheetJS)
|
|
cell.s = {
|
|
font: { bold: true },
|
|
fill: { fgColor: { rgb: 'F3F4F6' } }, // Light gray background
|
|
border: {
|
|
bottom: { style: 'medium', color: { rgb: '000000' } }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply styling to total row
|
|
const totalRowIndex = dataStartRow + dataRowsCount
|
|
for (let col = 0; col < 6; col++) {
|
|
const cellAddress = XLSX.utils.encode_cell({ r: totalRowIndex, c: col })
|
|
const cell = worksheet[cellAddress]
|
|
|
|
if (cell) {
|
|
// Apply bold formatting for total row
|
|
cell.s = {
|
|
font: { bold: true },
|
|
border: {
|
|
top: { style: 'medium', color: { rgb: '000000' } }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate filename with timestamp
|
|
*/
|
|
private static generateFilename(prefix: string): string {
|
|
const now = new Date()
|
|
const year = now.getFullYear()
|
|
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
|
const day = now.getDate().toString().padStart(2, '0')
|
|
const hour = now.getHours().toString().padStart(2, '0')
|
|
const minute = now.getMinutes().toString().padStart(2, '0')
|
|
|
|
return `${prefix}_${year}_${month}_${day}_${hour}${minute}.xlsx`
|
|
}
|
|
|
|
/**
|
|
* Export Payment Method data with custom configuration
|
|
*/
|
|
static async exportCustomPaymentData(
|
|
data: any[][],
|
|
sheetName: string = 'Payment Method',
|
|
filename?: string,
|
|
options?: {
|
|
colWidths?: { wch: number }[]
|
|
merges?: { s: { r: number; c: number }; e: { r: number; c: number } }[]
|
|
}
|
|
) {
|
|
try {
|
|
const XLSX = await import('xlsx')
|
|
|
|
const workbook = XLSX.utils.book_new()
|
|
const worksheet = XLSX.utils.aoa_to_sheet(data)
|
|
|
|
// Apply options
|
|
if (options?.colWidths) {
|
|
worksheet['!cols'] = options.colWidths
|
|
}
|
|
if (options?.merges) {
|
|
worksheet['!merges'] = options.merges
|
|
}
|
|
|
|
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
|
|
|
|
const exportFilename = filename || this.generateFilename('Payment_Method_Export')
|
|
XLSX.writeFile(workbook, exportFilename)
|
|
|
|
return { success: true, filename: exportFilename }
|
|
} catch (error) {
|
|
console.error('Error exporting to Excel:', error)
|
|
return { success: false, error: 'Failed to export Excel file' }
|
|
}
|
|
}
|
|
}
|