| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- import type {
- RevenueReport,
- ServiceReport,
- CustomerReport,
- InventoryReport,
- TechnicianReport,
- PartsUsageReport,
- JobAnalyticsReport,
- CustomerRetentionReport,
- TaxReport,
- PastDueInvoicesReport,
- VehicleReportData,
- } from '../Schema/reportTypes'
- import { formatCurrency } from '@/lib/format'
- function downloadCsv(
- filename: string,
- headers: string[],
- rows: Record<string, unknown>[],
- keys: string[]
- ) {
- const escapeCsv = (v: unknown) => {
- const s = String(v ?? '')
- return s.includes(',') || s.includes('"') || s.includes('\n') ? `"${s.replace(/"/g, '""')}"` : s
- }
- const lines = [
- headers.map(escape).join(','),
- ...rows.map((r) => keys.map((k) => escapeCsv(r[k])).join(',')),
- ]
- const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8;' })
- const url = URL.createObjectURL(blob)
- const a = document.createElement('a')
- a.href = url
- a.download = filename
- a.click()
- URL.revokeObjectURL(url)
- }
- export function exportRevenueCsv(
- data: RevenueReport,
- currencyCode: string,
- headers: [string, string, string, string, string, string, string, string] = [
- 'Month',
- 'Revenue',
- 'Collected',
- 'Count',
- 'Parts Cost',
- 'Parts Net Profit',
- 'Labor',
- 'Net Profit',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.monthly.map((m) => ({
- month: m.month,
- revenue: fmt(m.revenue),
- collected: fmt(m.collected),
- count: m.count,
- partsCost: fmt(m.partsCost),
- partsNetProfit: fmt(m.partsNetProfit),
- laborRevenue: fmt(m.laborRevenue),
- netProfit: fmt(m.netProfit),
- }))
- downloadCsv('revenue-report.csv', headers, rows, [
- 'month',
- 'revenue',
- 'collected',
- 'count',
- 'partsCost',
- 'partsNetProfit',
- 'laborRevenue',
- 'netProfit',
- ])
- }
- export function exportServicesCsv(
- data: ServiceReport,
- headers: [string, string, string] = ['Category', 'Label', 'Count']
- ) {
- const rows = [
- ...data.byStatus.map((s) => ({ category: 'Status', label: s.status, count: s.count })),
- ...data.byType.map((t) => ({ category: 'Type', label: t.type, count: t.count })),
- ]
- downloadCsv('services-report.csv', headers, rows, ['category', 'label', 'count'])
- }
- export function exportCustomersCsv(
- data: CustomerReport,
- currencyCode: string,
- headers: [string, string, string, string] = ['Name', 'Company', 'Services', 'Total Spent']
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.topCustomers.map((c) => ({
- name: c.name,
- company: c.company ?? '',
- serviceCount: c.serviceCount,
- totalSpent: fmt(c.totalSpent),
- }))
- downloadCsv('customers-report.csv', headers, rows, [
- 'name',
- 'company',
- 'serviceCount',
- 'totalSpent',
- ])
- }
- export function exportInventoryCsv(
- data: InventoryReport,
- currencyCode: string,
- headers: [string, string, string, string, string] = [
- 'Name',
- 'Part #',
- 'Quantity',
- 'Min Quantity',
- 'Unit Cost',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.lowStock.map((p) => ({
- name: p.name,
- partNumber: p.partNumber ?? '',
- quantity: p.unit ? `${p.quantity} ${p.unit}` : p.quantity,
- minQuantity:
- p.minQuantity != null ? (p.unit ? `${p.minQuantity} ${p.unit}` : p.minQuantity) : '',
- unitCost: p.unitCost != null ? fmt(p.unitCost) : '',
- }))
- downloadCsv('inventory-report.csv', headers, rows, [
- 'name',
- 'partNumber',
- 'quantity',
- 'minQuantity',
- 'unitCost',
- ])
- }
- export function exportTechniciansCsv(
- data: TechnicianReport,
- currencyCode: string,
- headers: [string, string, string, string, string, string] = [
- 'Technician',
- 'Jobs',
- 'Total Revenue',
- 'Avg Revenue',
- 'Total Hours',
- 'Avg Hours',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.technicians.map((t) => ({
- techName: t.techName,
- jobCount: t.jobCount,
- totalRevenue: fmt(t.totalRevenue),
- avgRevenue: fmt(t.avgRevenue),
- totalHours: t.totalLaborHours.toFixed(1),
- avgHours: t.avgHours.toFixed(1),
- }))
- downloadCsv('technicians-report.csv', headers, rows, [
- 'techName',
- 'jobCount',
- 'totalRevenue',
- 'avgRevenue',
- 'totalHours',
- 'avgHours',
- ])
- }
- export function exportPartsCsv(
- data: PartsUsageReport,
- currencyCode: string,
- headers: [string, string, string, string, string, string, string] = [
- 'Part Name',
- 'Part #',
- 'Usage Count',
- 'Total Qty',
- 'Revenue',
- 'Cost',
- 'Net Profit',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.parts.map((p) => ({
- name: p.name,
- partNumber: p.partNumber ?? '',
- usageCount: p.usageCount,
- totalQuantity: p.totalQuantity,
- totalRevenue: fmt(p.totalRevenue),
- totalCost: fmt(p.totalCost),
- netProfit: fmt(p.netProfit),
- }))
- downloadCsv('parts-usage-report.csv', headers, rows, [
- 'name',
- 'partNumber',
- 'usageCount',
- 'totalQuantity',
- 'totalRevenue',
- 'totalCost',
- 'netProfit',
- ])
- }
- export function exportJobAnalyticsCsv(
- data: JobAnalyticsReport,
- currencyCode: string,
- headers: [string, string, string, string] = ['Service Type', 'Count', 'Avg Value', 'Avg Hours']
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.topServiceTypes.map((t) => ({
- type: t.type,
- count: t.count,
- avgValue: fmt(t.avgValue),
- avgHours: t.avgHours.toFixed(1),
- }))
- downloadCsv('job-analytics-report.csv', headers, rows, ['type', 'count', 'avgValue', 'avgHours'])
- }
- export function exportRetentionCsv(
- data: CustomerRetentionReport,
- currencyCode: string,
- headers: [string, string, string, string, string] = [
- 'Customer',
- 'Company',
- 'Visits',
- 'Total Spent',
- 'Avg Days Between Visits',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.topReturning.map((c) => ({
- name: c.name,
- company: c.company ?? '',
- visitCount: c.visitCount,
- totalSpent: fmt(c.totalSpent),
- avgDaysBetweenVisits: c.avgTimeBetweenVisits ?? '',
- }))
- downloadCsv('retention-report.csv', headers, rows, [
- 'name',
- 'company',
- 'visitCount',
- 'totalSpent',
- 'avgDaysBetweenVisits',
- ])
- }
- export function exportPastDueInvoicesCsv(
- data: PastDueInvoicesReport,
- currencyCode: string,
- headers: [string, string, string, string, string, string, string, string] = [
- 'Customer',
- 'Company',
- 'Invoice #',
- 'Total Amount',
- 'Amount Paid',
- 'Amount Due',
- 'Due Date',
- 'Days Past Due',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.invoices.map((inv) => ({
- customer: inv.customerName,
- company: inv.customerCompany ?? '',
- invoiceNumber: inv.invoiceNumber ?? '',
- totalAmount: fmt(inv.totalAmount),
- amountPaid: fmt(inv.amountPaid),
- amountDue: fmt(inv.amountDue),
- dueDate: inv.dueDate,
- daysPastDue: inv.daysPastDue,
- }))
- downloadCsv('past-due-invoices-report.csv', headers, rows, [
- 'customer',
- 'company',
- 'invoiceNumber',
- 'totalAmount',
- 'amountPaid',
- 'amountDue',
- 'dueDate',
- 'daysPastDue',
- ])
- }
- export function exportVehicleReportCsv(
- data: VehicleReportData,
- currencyCode: string,
- headers: [string, string, string, string, string, string, string] = [
- 'Date',
- 'Title',
- 'Type',
- 'Status',
- 'Total',
- 'Parts',
- 'Labor Hours',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.serviceHistory.map((s) => ({
- date: s.date,
- title: s.title,
- type: s.type,
- status: s.status,
- totalAmount: fmt(s.totalAmount),
- partsCount: s.partsCount,
- laborHours: s.laborHours.toFixed(1),
- }))
- const vehicleLabel = `${data.vehicleInfo.year} ${data.vehicleInfo.make} ${data.vehicleInfo.model}`
- downloadCsv(`vehicle-report-${vehicleLabel.replace(/\s+/g, '-')}.csv`, headers, rows, [
- 'date',
- 'title',
- 'type',
- 'status',
- 'totalAmount',
- 'partsCount',
- 'laborHours',
- ])
- }
- export function exportTaxCsv(
- data: TaxReport,
- currencyCode: string,
- headers: [string, string, string, string] = [
- 'Month',
- 'Tax Collected',
- 'Taxable Amount',
- 'Invoice Count',
- ]
- ) {
- const fmt = (n: number) => formatCurrency(n, currencyCode)
- const rows = data.monthly.map((m) => ({
- month: m.month,
- taxCollected: fmt(m.taxCollected),
- taxableAmount: fmt(m.taxableAmount),
- invoiceCount: m.invoiceCount,
- }))
- downloadCsv('tax-report.csv', headers, rows, [
- 'month',
- 'taxCollected',
- 'taxableAmount',
- 'invoiceCount',
- ])
- }
|