import { string } from 'alga-js' import refreshTokenHelper from "../../utils/refreshTokenHelper" import forceLogoutHelper from "../../utils/forceLogoutHelper" import errorHandlingHelper from "../../utils/errorHandlingHelper" import fetchHelper from "../../utils/fetchHelper" // PayJoe API credentials const PAYJOE_USERNAME = "info@logyou.de" const PAYJOE_API_KEY = "!879sO4x$77Q" const PAYJOE_ZUGANG_ID = 74724 const PAYJOE_API_URL = "https://api.payjoe.de/belegupload" // Currency ISO codes mapping const CURRENCY_ISO_CODES: Record = { 'EUR': 978, 'USD': 840, 'GBP': 826, 'CHF': 756, 'JPY': 392, 'CAD': 124, 'AUD': 36, 'CNY': 156, 'INR': 356, 'PLN': 985, 'SEK': 752, 'NOK': 578, 'DKK': 208, 'CZK': 203, 'HUF': 348, 'RUB': 643, 'TRY': 949, 'BRL': 986, 'MXN': 484, 'ZAR': 710 } const handleFunc = async (event: any, authToken: any = null) => { let data: any = {} const token = authToken ?? await getTokenHelper(event) const body = await readBody(event) const invoiceIds: number[] = body.ids || [] if (!invoiceIds.length) { return { status: 400, message: 'No invoice IDs provided' } } // Fetch invoice details for all provided IDs const belege: any[] = [] const errors: string[] = [] const successIds: number[] = [] for (const invoiceId of invoiceIds) { try { // Fetch invoice with partner and order details const invoice: any = await event.context.fetch( `models/c_invoice/${invoiceId}?$select=C_Invoice_ID,DocumentNo,DateInvoiced,GrandTotal,C_Currency_ID,C_BPartner_ID,C_Order_ID,DocBaseType,isUploadToPayJoe&$expand=C_BPartner_ID($select=Name,Name2,Value,EMail,C_BPartner_ID),C_Currency_ID($select=ISO_Code),C_Order_ID($select=ExternalOrderId)`, 'GET', token, null ) if (!invoice) { errors.push(`Invoice ${invoiceId} not found`) continue } // Check if already uploaded if (invoice.isUploadToPayJoe === true || invoice.isUploadToPayJoe === 'Y') { errors.push(`Invoice ${invoice.DocumentNo} already uploaded to PayJoe`) continue } // Determine Belegtyp: 0 = Rechnung (ARI), 1 = Gutschrift (ARC) let belegtyp = 0 if (invoice.DocBaseType?.id === 'ARC' || invoice.DocBaseType?.identifier === 'ARC') { belegtyp = 1 } // Get currency code const currencyIso = invoice.C_Currency_ID?.ISO_Code || 'EUR' const belegWaehrung = CURRENCY_ISO_CODES[currencyIso] || 978 // Parse partner name into first/last name const partnerName = invoice.C_BPartner_ID?.Name || '' const partnerName2 = invoice.C_BPartner_ID?.Name2 || '' let vorname = '' let nachname = partnerName let firma = '' // If Name2 exists, assume Name is company and Name2 is contact if (partnerName2) { firma = partnerName const nameParts = partnerName2.split(' ') if (nameParts.length > 1) { vorname = nameParts[0] nachname = nameParts.slice(1).join(' ') } else { nachname = partnerName2 } } else { // Try to split Name into first/last const nameParts = partnerName.split(' ') if (nameParts.length > 1) { vorname = nameParts[0] nachname = nameParts.slice(1).join(' ') } } const beleg = { BelegIDExtern: String(invoice.id).substring(0, 30), Belegdatum: invoice.DateInvoiced, Belegtyp: belegtyp, BelegBetrag: invoice.GrandTotal || 0, BelegWaehrung: belegWaehrung, BelegVorname: vorname.substring(0, 100), BelegNachname: nachname.substring(0, 100), BelegFirma: firma.substring(0, 100) || null, BelegKundenNr: (invoice.C_BPartner_ID?.Value || '').substring(0, 30), BelegEmail: (invoice.C_BPartner_ID?.EMail || '').substring(0, 100), BelegNummer: (invoice.DocumentNo || '').substring(0, 30), BelegExterneBestellNr: invoice.C_Order_ID?.ExternalOrderId ? String(invoice.C_Order_ID.ExternalOrderId).substring(0, 40) : null, BelegReferenz1: null, BelegReferenz2: null, BelegReferenz3: null, BelegReferenz4: null, BelegReferenz5: null, BelegReferenz6: null, BelegReferenz7: null, BelegReferenz8: null, BelegReferenz9: null, BelegReferenz10: null } belege.push({ beleg, invoiceId, documentNo: invoice.DocumentNo }) successIds.push(invoiceId) } catch (err: any) { errors.push(`Error fetching invoice ${invoiceId}: ${err.message || 'Unknown error'}`) } } if (!belege.length) { return { status: 400, message: 'No valid invoices to upload', errors } } // Send to PayJoe API const payJoePayload = { UserName: PAYJOE_USERNAME, APIKey: PAYJOE_API_KEY, ZugangID: PAYJOE_ZUGANG_ID, Belege: belege.map(b => b.beleg) } try { const payJoeResponse: any = await $fetch(PAYJOE_API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payJoePayload }) if (payJoeResponse?.Erfolgreich === true) { // Update invoices in iDempiere to mark as uploaded const uploadDate = new Date().toISOString() const updateErrors: string[] = [] for (const { invoiceId, documentNo } of belege) { try { await fetchHelper(event, `models/c_invoice/${invoiceId}`, 'PUT', token, { isUploadToPayJoe: true, UploadDatePayJoe: uploadDate, tableName: 'C_Invoice' }) } catch (updateErr: any) { updateErrors.push(`Failed to update invoice ${documentNo}: ${updateErr.message || 'Unknown error'}`) } } data = { status: 200, message: `Successfully uploaded ${belege.length} invoice(s) to PayJoe`, uploadedCount: belege.length, uploadDate: uploadDate, letztesBuchungsdatum: payJoeResponse.LetztesBuchungsdatum, errors: [...errors, ...updateErrors] } } else { data = { status: 500, message: 'PayJoe API returned unsuccessful response', payJoeResponse, errors } } } catch (payJoeErr: any) { data = { status: 500, message: `PayJoe API error: ${payJoeErr.message || 'Unknown error'}`, errors } } return data } export default defineEventHandler(async (event) => { let data: any = {} try { data = await handleFunc(event) } catch (err: any) { try { let authToken: any = await refreshTokenHelper(event) data = await handleFunc(event, authToken) } catch (error) { data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) forceLogoutHelper(event, data) } } return data })