- Removed the domain mappings module, consolidating related types and schemas into the id-mappings feature. - Updated import paths across the BFF to reflect the new structure, ensuring compliance with import hygiene rules. - Cleaned up unused files and optimized the codebase for better maintainability and clarity.
126 lines
3.7 KiB
TypeScript
126 lines
3.7 KiB
TypeScript
/**
|
|
* WHMCS Billing Provider - Mapper
|
|
*
|
|
* Transforms raw WHMCS invoice data into normalized billing domain types.
|
|
*/
|
|
|
|
import type { Invoice, InvoiceItem } from "../../contract.js";
|
|
import { invoiceSchema } from "../../schema.js";
|
|
import {
|
|
type WhmcsInvoiceRaw,
|
|
whmcsInvoiceRawSchema,
|
|
type WhmcsInvoiceListItem,
|
|
whmcsInvoiceListItemSchema,
|
|
whmcsInvoiceItemsRawSchema,
|
|
} from "./raw.types.js";
|
|
import { parseAmount, formatDate } from "../../../common/providers/whmcs-utils/index.js";
|
|
|
|
export interface TransformInvoiceOptions {
|
|
defaultCurrencyCode?: string;
|
|
defaultCurrencySymbol?: string;
|
|
}
|
|
|
|
// Status mapping from WHMCS to domain
|
|
const STATUS_MAP: Record<string, Invoice["status"]> = {
|
|
draft: "Draft",
|
|
pending: "Pending",
|
|
"payment pending": "Pending",
|
|
paid: "Paid",
|
|
unpaid: "Unpaid",
|
|
cancelled: "Cancelled",
|
|
canceled: "Cancelled",
|
|
overdue: "Overdue",
|
|
refunded: "Refunded",
|
|
collections: "Collections",
|
|
};
|
|
|
|
function mapStatus(status: string): Invoice["status"] {
|
|
const normalized = status?.trim().toLowerCase();
|
|
if (!normalized) {
|
|
throw new Error("Invoice status missing");
|
|
}
|
|
|
|
const mapped = STATUS_MAP[normalized];
|
|
if (!mapped) {
|
|
throw new Error(`Unsupported WHMCS invoice status: ${status}`);
|
|
}
|
|
return mapped;
|
|
}
|
|
|
|
function mapItems(rawItems: unknown): InvoiceItem[] {
|
|
if (!rawItems) return [];
|
|
|
|
const parsed = whmcsInvoiceItemsRawSchema.parse(rawItems);
|
|
const itemArray = Array.isArray(parsed.item) ? parsed.item : [parsed.item];
|
|
|
|
return itemArray.map(item => ({
|
|
id: item.id,
|
|
description: item.description,
|
|
amount: parseAmount(item.amount),
|
|
quantity: 1,
|
|
type: item.type,
|
|
serviceId: typeof item.relid === "number" && item.relid > 0 ? item.relid : undefined,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Transform raw WHMCS invoice data into normalized Invoice type
|
|
*/
|
|
export function transformWhmcsInvoice(
|
|
rawInvoice: unknown,
|
|
options: TransformInvoiceOptions = {}
|
|
): Invoice {
|
|
const invoicePayload =
|
|
rawInvoice && typeof (rawInvoice as { invoiceid?: unknown }).invoiceid !== "undefined"
|
|
? whmcsInvoiceRawSchema.parse(rawInvoice)
|
|
: normalizeListInvoice(rawInvoice);
|
|
|
|
const whmcsInvoice = {
|
|
...invoicePayload,
|
|
invoiceid: invoicePayload.invoiceid ?? invoicePayload.id,
|
|
};
|
|
|
|
const currency = whmcsInvoice.currencycode || options.defaultCurrencyCode || "JPY";
|
|
const currencySymbol =
|
|
whmcsInvoice.currencyprefix || whmcsInvoice.currencysuffix || options.defaultCurrencySymbol;
|
|
|
|
// Transform to domain model
|
|
const invoice: Invoice = {
|
|
id: whmcsInvoice.invoiceid ?? 0,
|
|
number: whmcsInvoice.invoicenum || `INV-${whmcsInvoice.invoiceid}`,
|
|
status: mapStatus(whmcsInvoice.status),
|
|
currency,
|
|
currencySymbol,
|
|
total: parseAmount(whmcsInvoice.total),
|
|
subtotal: parseAmount(whmcsInvoice.subtotal),
|
|
tax: parseAmount(whmcsInvoice.tax) + parseAmount(whmcsInvoice.tax2),
|
|
issuedAt: formatDate(whmcsInvoice.date || whmcsInvoice.datecreated),
|
|
dueDate: formatDate(whmcsInvoice.duedate),
|
|
paidDate: formatDate(whmcsInvoice.datepaid),
|
|
description: whmcsInvoice.notes || undefined,
|
|
items: mapItems(whmcsInvoice.items),
|
|
};
|
|
|
|
// Validate result against domain schema
|
|
return invoiceSchema.parse(invoice);
|
|
}
|
|
|
|
/**
|
|
* Transform multiple WHMCS invoices
|
|
*/
|
|
export function transformWhmcsInvoices(
|
|
rawInvoices: unknown[],
|
|
options: TransformInvoiceOptions = {}
|
|
): Invoice[] {
|
|
return rawInvoices.map(raw => transformWhmcsInvoice(raw, options));
|
|
}
|
|
|
|
function normalizeListInvoice(rawInvoice: unknown): WhmcsInvoiceRaw & { id?: number } {
|
|
const listItem: WhmcsInvoiceListItem = whmcsInvoiceListItemSchema.parse(rawInvoice);
|
|
const invoiceid = listItem.invoiceid ?? listItem.id;
|
|
return {
|
|
...listItem,
|
|
invoiceid,
|
|
};
|
|
}
|