import { DomainConflictError } from '../../../common/errors/domain-conflict.error';
type InvoiceInput = Record<string, unknown>;
function assertObject(input: unknown, code: string): asserts input is InvoiceInput {
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new DomainConflictError(code);
}
function assertText(value: unknown, max: number, code: string): asserts value is string {
if (typeof value !== 'string' || value.trim().length === 0 || value.length > max) throw new DomainConflictError(code);
}
function assertOptionalText(value: unknown, max: number, code: string): void {
if (value !== undefined && value !== null) assertText(value, max, code);
}
function assertScope(input: InvoiceInput, code: string): void {
assertText(input.organizationId, 200, code);
assertText(input.locationId, 200, code);
}
function assertReference(value: unknown, code: string): void {
assertText(value, 200, code);
}
function assertDate(value: unknown, code: string): void {
assertText(value, 10, code);
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) throw new DomainConflictError(code);
const parsed = new Date(`${value}T00:00:00.000Z`);
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {
throw new DomainConflictError(code);
}
}
function assertCustomer(value: unknown, code: string): void {
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new DomainConflictError(code);
const customer = value as Record<string, unknown>;
assertText(customer.name, 300, code);
assertOptionalText(customer.email, 320, code);
if (customer.email !== undefined && customer.email !== null &&
!/^\S+@\S+\.\S+$/.test(String(customer.email))) throw new DomainConflictError(code);
for (const key of ['vatId', 'taxNumber', 'buyerReference', 'electronicAddress']) {
assertOptionalText(customer[key], 500, code);
}
assertOptionalText(customer.locale, 32, code);
if (customer.address !== undefined && customer.address !== null) {
if (typeof customer.address !== 'object' || Array.isArray(customer.address)) {
throw new DomainConflictError(code);
}
if (Object.keys(customer.address as object).length > 40) throw new DomainConflictError(code);
}
}
export function assertInvoiceFromCheckInput(input: unknown): asserts input is InvoiceInput {
const code = 'invoice.input_invalid';
assertObject(input, code);
assertScope(input, code);
assertReference(input.checkId, code);
assertCustomer(input.customer, code);
assertOptionalText(input.userId, 200, code);
if (input.dueDate !== undefined && input.dueDate !== null) assertDate(input.dueDate, code);
assertOptionalText(input.notes, 4000, code);
}
export function assertCreditNoteInput(input: unknown): asserts input is InvoiceInput {
const code = 'invoice.input_invalid';
assertObject(input, code);
assertScope(input, code);
assertReference(input.originalInvoiceId, code);
assertText(input.reason, 2000, code);
assertOptionalText(input.userId, 200, code);
}
export function assertInvoiceReferenceInput(input: unknown): asserts input is InvoiceInput {
const code = 'invoice.input_invalid';
assertObject(input, code);
assertScope(input, code);
assertReference(input.invoiceId, code);
assertOptionalText(input.userId, 200, code);
}
export function assertEInvoiceBuildInput(input: unknown): asserts input is InvoiceInput {
const code = 'einvoice.input_invalid';
assertObject(input, code);
assertText(input.organizationId, 200, code);
assertReference(input.invoiceId, code);
if (input.profile !== 'xrechnung' && input.profile !== 'zugferd') throw new DomainConflictError(code);
}
export function assertEInvoiceValidateInput(input: unknown): asserts input is InvoiceInput {
assertEInvoiceBuildInput(input);
const code = 'einvoice.input_invalid';
assertOptionalText(input.validatorConnectionId, 200, code);
assertOptionalText(input.userId, 200, code);
}
export function assertInvoiceRenderInput(input: unknown): asserts input is InvoiceInput {
const code = 'invoice.render_input_invalid';
assertObject(input, code);
assertText(input.organizationId, 200, code);
assertReference(input.invoiceId, code);
if (!['pdf', 'zugferd_pdf', 'xml'].includes(String(input.renderType))) {
throw new DomainConflictError(code);
}
}
