// Purchase-order positions report (pdfmake, DIN A4 portrait). // Prints/downloads the full order info: header fields + every position with // product / Value / SKU / MPN / qty / blank "Geprüft" check box / description // (no unit or price columns). // // Shared by the PO edit page (top action bar buttons). The order lines are // fetched fresh on each run via `fetchPurchaseOrderReportLines()` so the report // does not depend on the line table component being mounted / on its tab. import { loadPdfMake } from '~/utils/loadPdfMake' export interface PurchaseOrderReportLine { line: number | string product: string charge: string productSKU: string productMPN: string productValue: string description: string qtyEntered: number | string } // Maps the raw iDempiere `c_orderline` records (GET /api/orders/{id}/order-line) // to the minimal shape the report needs. Same field resolution as the line table. export const mapPurchaseOrderReportLines = (records: any[] = []): PurchaseOrderReportLine[] => { return records.map((item: any) => ({ line: item?.Line || '', product: item?.M_Product_ID?.identifier || item?.M_Product_ID?.Name || '', charge: item?.C_Charge_ID?.identifier || '', productSKU: item?.M_Product_ID?.SKU || '', productMPN: item?.M_Product_ID?.mpn || item?.M_Product_ID?.MPN || '', productValue: item?.M_Product_ID?.Value || '', description: item?.Description || '', qtyEntered: item?.QtyEntered || 1 })).sort((a, b) => Number(a.line) - Number(b.line)) } export const fetchPurchaseOrderReportLines = async (orderId: string | number): Promise => { const res: any = await $fetch('/api/orders/' + orderId + '/order-line', { headers: useRequestHeaders(['cookie']) }) return mapPurchaseOrderReportLines(res?.records || []) } export const buildPurchaseOrderReportDoc = (info: any, entries: PurchaseOrderReportLine[]) => { info = info || {} const headerPairs = [ ['Belegnr.', info.documentNo], ['Status', info.docStatus], ['Lieferant', info.partner], ['Anschrift', info.partnerLocation], ['Organisation', info.organization], ['Lager', info.warehouse], ['Bestelldatum', info.dateOrdered], ['Zugesagt am', info.datePromised], ['Buchungsdatum', info.dateAcct], ['Währung', info.currency], ['Zahlungsbedingung', info.paymentTerm], ['Preisliste', info.priceList], ['Referenz', info.poReference], ['Einkäufer', info.salesRep], ['Beschreibung', info.description] ].filter(p => p[1] !== undefined && p[1] !== null && String(p[1]).trim() !== '') const perCol = Math.ceil(headerPairs.length / 3) || 1 const headerColumns: any[] = [] for (let c = 0; c < 3; c++) { const slice = headerPairs.slice(c * perCol, (c + 1) * perCol) if (!slice.length) continue headerColumns.push({ width: '*', table: { widths: [92, '*'], body: slice.map(([k, v]) => [{ text: k, bold: true }, { text: String(v) }]) }, layout: 'noBorders' }) } const tableBody: any[] = [[ { text: 'Pos.', style: 'th', alignment: 'right' }, { text: 'Produkt', style: 'th' }, { text: 'Value', style: 'th' }, { text: 'SKU', style: 'th' }, { text: 'MPN', style: 'th' }, { text: 'Menge', style: 'th', alignment: 'right' }, // Virtual column — intentionally empty so the printed sheet can be ticked off // by hand when the received quantity is checked. { text: 'Geprüft', style: 'th', alignment: 'center' }, { text: 'Beschreibung', style: 'th' } ]] for (const e of entries) { tableBody.push([ { text: String(e.line ?? ''), alignment: 'right' }, { text: e.product || e.charge || '' }, { text: e.productValue || '' }, { text: e.productSKU || '' }, { text: e.productMPN || '' }, { text: String(e.qtyEntered ?? ''), alignment: 'right' }, { text: '' }, { text: e.description || '' } ]) } tableBody.push([ { text: `${entries.length} Positionen`, colSpan: 8, bold: true }, {}, {}, {}, {}, {}, {}, {} ]) return { pageSize: 'A4', pageOrientation: 'portrait', pageMargins: [28, 30, 28, 34], content: [ { text: `Bestellung ${info.documentNo || ''} — Report`, fontSize: 14, bold: true, margin: [0, 0, 0, 8] }, { columns: headerColumns, columnGap: 14, fontSize: 8, margin: [0, 0, 0, 10] }, { table: { headerRows: 1, dontBreakRows: true, widths: [22, '*', 54, 54, 54, 30, 36, 110], body: tableBody }, layout: { hLineColor: () => '#cccccc', vLineColor: () => '#cccccc', hLineWidth: () => 0.5, vLineWidth: () => 0.5 } }, { text: `Erstellt: ${new Date().toLocaleString('de-DE', { timeZone: 'Europe/Berlin' })}`, fontSize: 8, color: '#777777', margin: [0, 10, 0, 0] } ], styles: { th: { bold: true, fontSize: 8, fillColor: '#f0f0f0', margin: [0, 2, 0, 2] } }, defaultStyle: { fontSize: 8 } } } // Fetches the lines, builds the PDF and either opens the print dialog or downloads it. export const runPurchaseOrderReport = async (info: any, mode: 'print' | 'download' = 'download') => { const orderId = info?.id if (!orderId) throw new Error('Order not loaded') const [entries, pdfMake] = await Promise.all([ fetchPurchaseOrderReportLines(orderId), loadPdfMake() ]) const pdf = pdfMake.createPdf(buildPurchaseOrderReportDoc(info, entries)) if (mode === 'print') { pdf.print() } else { pdf.download(`Bestellung-${info?.documentNo || orderId}-Report.pdf`) } }