interface LexOfficeContact { id?: string; organizationId: string; version: number; roles: { customer?: { number?: number; }; }; company?: { name: string; taxNumber?: string; vatRegistrationId?: string; allowTaxFreeInvoices?: boolean; contactPersons?: Array<{ salutation?: string; firstName?: string; lastName?: string; primary?: boolean; emailAddress?: string; phoneNumber?: string; }>; }; person?: { salutation?: string; firstName?: string; lastName?: string; }; addresses?: { billing?: Array<{ supplement?: string; street?: string; zip?: string; city?: string; countryCode?: string; }>; }; emailAddresses?: { business?: Array; office?: Array; private?: Array; other?: Array; }; phoneNumbers?: { business?: Array; office?: Array; mobile?: Array; private?: Array; fax?: Array; other?: Array; }; note?: string; } interface LexOfficeQuotation { id?: string; organizationId?: string; createdDate?: string; updatedDate?: string; voucherNumber?: string; voucherDate: string; address: { contactId?: string; name?: string; supplement?: string; street?: string; city?: string; zip?: string; countryCode: string; }; lineItems: Array<{ type: 'custom' | 'text'; name?: string; description?: string; quantity?: number; unitName?: string; unitPrice?: { currency: string; netAmount: number; taxRatePercentage: number; }; }>; totalPrice?: { currency: string; }; taxConditions?: { taxType: 'net' | 'gross' | 'vatfree'; }; title?: string; introduction?: string; remark?: string; } interface LexOfficeInvoice extends Omit { voucherStatus?: string; shippingConditions?: { shippingDate?: string; shippingType?: string; }; paymentConditions?: { paymentTermLabel?: string; paymentTermDuration?: number; }; } export class LexOfficeService { private apiKey: string; private baseUrl = 'https://api.lexoffice.io/v1'; constructor() { this.apiKey = process.env.LEXOFFICE_API_KEY || ''; if (!this.apiKey) { console.warn('LexOffice API Key nicht konfiguriert'); } } private async request(method: string, endpoint: string, data?: any) { try { const response = await fetch(`${this.baseUrl}${endpoint}`, { method, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { const errorText = await response.text(); throw new Error(`LexOffice API Error: ${response.status} - ${errorText}`); } return await response.json(); } catch (error) { console.error('LexOffice API Request failed:', error); throw error; } } async createContact(contact: Partial): Promise<{ id: string; resourceUri: string }> { return this.request('POST', '/contacts', contact); } async getContact(contactId: string): Promise { return this.request('GET', `/contacts/${contactId}`); } async createQuotation(quotation: LexOfficeQuotation): Promise<{ id: string; resourceUri: string; createdDate: string; updatedDate: string; voucherNumber: string }> { return this.request('POST', '/quotations', quotation); } async getQuotation(quotationId: string): Promise { return this.request('GET', `/quotations/${quotationId}`); } async createInvoice(invoice: LexOfficeInvoice): Promise<{ id: string; resourceUri: string; createdDate: string; updatedDate: string; voucherNumber: string }> { return this.request('POST', '/invoices', invoice); } async getInvoice(invoiceId: string): Promise { return this.request('GET', `/invoices/${invoiceId}`); } async finalizeInvoice(invoiceId: string): Promise<{ id: string; resourceUri: string }> { return this.request('PUT', `/invoices/${invoiceId}/pursue`, { precedingSalesVoucherId: null, preserveVoucherNumber: false, }); } async createContactFromBooking(booking: any): Promise { const contact: Partial = { roles: { customer: {}, }, addresses: { billing: [{ street: booking.customerAddress, zip: booking.customerZip, city: booking.customerCity, countryCode: 'DE', }], }, emailAddresses: { business: [booking.customerEmail], }, phoneNumbers: { business: [booking.customerPhone], }, }; if (booking.invoiceType === 'BUSINESS' && booking.companyName) { contact.company = { name: booking.companyName, contactPersons: [{ firstName: booking.customerName.split(' ')[0], lastName: booking.customerName.split(' ').slice(1).join(' '), primary: true, emailAddress: booking.customerEmail, phoneNumber: booking.customerPhone, }], }; } else { const [firstName, ...lastNameParts] = booking.customerName.split(' '); contact.person = { firstName: firstName, lastName: lastNameParts.join(' '), }; } const result = await this.createContact(contact); return result.id; } async createQuotationFromBooking(booking: any, contactId: string): Promise { const quotation: LexOfficeQuotation = { voucherDate: new Date().toISOString().split('T')[0], address: { contactId: contactId, countryCode: 'DE', }, lineItems: [ { type: 'custom', name: 'Fotobox-Vermietung', description: `Event am ${new Date(booking.eventDate).toLocaleDateString('de-DE')} in ${booking.eventCity}`, quantity: 1, unitName: 'Stück', unitPrice: { currency: 'EUR', netAmount: booking.calculatedPrice || 0, taxRatePercentage: 19, }, }, ], totalPrice: { currency: 'EUR', }, taxConditions: { taxType: 'net', }, title: `Angebot Fotobox-Vermietung - ${booking.bookingNumber}`, introduction: 'Vielen Dank für Ihre Anfrage! Gerne erstellen wir Ihnen folgendes Angebot:', remark: 'Wir freuen uns auf Ihre Bestellung!', }; const result = await this.createQuotation(quotation); return result.id; } async createConfirmationFromBooking(booking: any, contactId: string): Promise { const invoice: LexOfficeInvoice = { voucherDate: new Date().toISOString().split('T')[0], address: { contactId: contactId, countryCode: 'DE', }, lineItems: [ { type: 'custom', name: 'Fotobox-Vermietung', description: `Event am ${new Date(booking.eventDate).toLocaleDateString('de-DE')} in ${booking.eventCity}\nAufbau: ${new Date(booking.setupTimeStart).toLocaleString('de-DE')}\nOrt: ${booking.eventAddress}, ${booking.eventZip} ${booking.eventCity}`, quantity: 1, unitName: 'Stück', unitPrice: { currency: 'EUR', netAmount: booking.calculatedPrice || 0, taxRatePercentage: 19, }, }, ], totalPrice: { currency: 'EUR', }, taxConditions: { taxType: 'net', }, title: `Auftragsbestätigung - ${booking.bookingNumber}`, introduction: 'Vielen Dank für Ihre Bestellung! Hiermit bestätigen wir Ihren Auftrag:', remark: 'Wir freuen uns auf Ihre Veranstaltung!', shippingConditions: { shippingDate: new Date(booking.eventDate).toISOString().split('T')[0], shippingType: 'service', }, paymentConditions: { paymentTermLabel: 'Zahlung bei Lieferung', paymentTermDuration: 0, }, }; const result = await this.createInvoice(invoice); await this.finalizeInvoice(result.id); return result.id; } } export const lexofficeService = new LexOfficeService();