/** * POST /api/accounting/amazon-ads/import { invoiceNos: string[], chargeId, feeChargeId?, taxId? } * Books the SELECTED Amazon Ads invoices as AP invoices (importInvoice.ts), * sequentially, each with its original PDF: from the local cache (uploaded or * downloaded earlier) or freshly downloaded from the Ads API. API-sourced rows * without campaign lines get the /invoices/{id} detail first so every campaign * becomes its own charge line. One result per invoice; never aborts the batch. * Seller FEE invoices (kind 'fee') in the same selection use `feeChargeId` and a * vendor resolved from the issuer's VAT id instead of the fixed Ads vendor. */ import { withRefresh, orgIdOf, userNameOf } from '../../../utils/amazonAds/routeShared' import getTokenHelper from '../../../utils/getTokenHelper' import { getAdsConfig, isAdsApiConnected, getAdsInvoiceDetail, mapAdsApiInvoice, downloadAdsInvoicePdf } from '../../../utils/amazonAds/adsApi' import { getInvoice, readInvoicePdf, upsertInvoice, markImported, markImportError } from '../../../utils/amazonAds/invoiceStore' import { importAdsInvoice, resolveFeeVendor } from '../../../utils/amazonAds/importInvoice' export default withRefresh(async (event, authToken = null) => { const token = authToken ?? await getTokenHelper(event) const body = await readBody(event) const invoiceNos: string[] = [...new Set(((body?.invoiceNos || []) as any[]).map(s => String(s || '').trim()).filter(Boolean))].slice(0, 100) const organizationId = orgIdOf(event) if (!organizationId) return { status: 422, message: 'Keine Organisation im Login-Kontext — bitte erneut anmelden.' } if (!invoiceNos.length) return { status: 400, message: 'Keine Rechnungen ausgewählt' } const cfg = getAdsConfig(event) const chargeId = Number(body?.chargeId || cfg.defaultChargeId || 0) const taxId = body?.taxId ? Number(body.taxId) : null const feeChargeId = Number(body?.feeChargeId || cfg.defaultFeeChargeId || 0) const user = userNameOf(event) const results: any[] = [] for (const no of invoiceNos) { const row = await getInvoice(event, token, no) if (!row?.invoice) { results.push({ invoiceNo: no, status: 404, message: `Rechnung ${no} ist nicht (mehr) in der Liste — bitte Liste neu laden.` }); continue } let inv = row.invoice let pdf: Buffer | null = await readInvoicePdf(event, token, row) let pdfName: string | null = row.pdfName || inv.fileName || null try { if (inv.source === 'api' && isAdsApiConnected(event)) { if (!inv.lines?.length) { try { const detail = await getAdsInvoiceDetail(event, no) inv = { ...mapAdsApiInvoice({ invoiceSummary: detail?.invoice?.invoiceSummary || detail?.invoiceSummary || inv }, detail, inv.countryCode), source: 'api' } if (!inv.invoiceDate) inv.invoiceDate = row.invoice.invoiceDate await upsertInvoice(event, token, organizationId, inv) } catch (e: any) { results.push({ invoiceNo: no, status: 502, message: `Details konnten nicht geladen werden: ${e?.message || e}` }); continue } } if (!pdf) { try { const doc = await downloadAdsInvoicePdf(event, no, 'INVOICE') if (doc) { pdf = doc.buffer; pdfName = doc.fileName; await upsertInvoice(event, token, organizationId, inv, { pdf, pdfName }) } } catch (e: any) { results.push({ invoiceNo: no, status: 502, message: `PDF konnte nicht von Amazon geladen werden: ${e?.message || e}` }); continue } } } if (!pdf && body?.requirePdf !== false) { results.push({ invoiceNo: no, status: 422, message: `Rechnung ${no}: kein Amazon-PDF vorhanden — bitte die PDF hochladen (oder API verbinden).` }); continue } const isFee = inv.kind === 'fee' const useCharge = isFee ? feeChargeId : chargeId if (!useCharge) { results.push({ invoiceNo: no, status: 422, message: isFee ? 'Bitte eine Charge für Gebührenrechnungen (z. B. „Amazon Gebühren“) wählen.' : 'Bitte eine Charge (Kostenart, z. B. „Werbekosten“) wählen.' }); continue } const vendorWarnings: string[] = [] const vendorId = isFee ? await resolveFeeVendor(event, token, organizationId, inv, vendorWarnings) : cfg.vendorBpartnerId const r = await importAdsInvoice(event, token, inv, { organizationId, vendorId, chargeId: useCharge, taxId: isFee ? null : taxId, pdf, pdfName: pdfName || (isFee ? `${no}.pdf` : `Amazon-Ads-INVOICE-${no}.pdf`), user }) if (vendorWarnings.length) r.warnings = [...vendorWarnings, ...(r.warnings || [])] if (r.status === 200 && r.cInvoiceId) await markImported(event, token, row, no, r.cInvoiceId, r.documentNo || no, user) else if (r.status !== 409) await markImportError(event, token, row, r.message || '') results.push({ invoiceNo: no, ...r, ...(r.status === 200 ? { chargeId: useCharge } : {}) }) } catch (e: any) { results.push({ invoiceNo: no, status: 500, message: e?.data?.detail || e?.message || String(e) }) } } const ok = results.filter(r => r.status === 200).length return { status: 200, ok, failed: results.length - ok, results } })