diff --git a/App/app/api/po/[id]/export/route.ts b/App/app/api/po/[id]/export/route.ts index 61f6ccb..beff233 100644 --- a/App/app/api/po/[id]/export/route.ts +++ b/App/app/api/po/[id]/export/route.ts @@ -459,14 +459,49 @@ export async function GET(request: NextRequest, { params }: Props) { ws.getRow(SIG_ROW + 1).height = 14; ws.getRow(SIG_ROW + 2).height = 14; - // Left sig block (approver — the manager who authorized the PO) - if (signatureBase64) { + // Left signatory block (cols A-D). Position images by absolute pixels via native + // EMU offsets — ExcelJS's fractional-column anchors don't map cleanly to pixels. + const EMU = 9525; // EMU per pixel + const COL_PX = [22, 4, 28, 15, 8, 15, 15, 8, 16].map((w) => Math.round(w * 7 + 5)); + const SIG_BLOCK_PX = COL_PX[0] + COL_PX[1] + COL_PX[2] + COL_PX[3]; // A-D + const anchorAt = (leftPx: number, row: number) => { + let x = 0; + for (let c = 0; c < COL_PX.length - 1; c++) { + if (leftPx < x + COL_PX[c]) { + return { nativeCol: c, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor; + } + x += COL_PX[c]; + } + return { nativeCol: COL_PX.length - 1, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor; + }; + + const sigExt = signatureBase64 ? scaleToBox(signatureSize ?? { width: 360, height: 96 }, 165, 44) : null; + const sigLeft = sigExt ? Math.round((SIG_BLOCK_PX - sigExt.width) / 2) : 0; // centred over the name + + // Stamp / seal — drawn FIRST so it sits BEHIND the signature, tucked to its right. + if (stampImg) { + const stampExt = scaleToBox(stampImg, 80, 66); + const stampLeft = sigExt + ? Math.min(SIG_BLOCK_PX - stampExt.width, sigLeft + sigExt.width - Math.round(stampExt.width * 0.35)) + : SIG_BLOCK_PX - stampExt.width - 6; + const stampId = wb.addImage({ + base64: stampImg.base64, + extension: stampImg.mime === "image/jpeg" ? "jpeg" : "png", + }); + ws.addImage(stampId, { + tl: anchorAt(Math.max(0, stampLeft), SIG_ROW - 1), + ext: stampExt, + editAs: "oneCell", + }); + } + + // Approver signature — drawn AFTER the stamp (on top), centred over the name. + if (signatureBase64 && sigExt) { const imgType = signatureMime === "image/jpeg" ? "jpeg" : "png"; const imgId = wb.addImage({ base64: signatureBase64, extension: imgType }); - // Sized by pixels (aspect preserved) and placed inside the signatory box ws.addImage(imgId, { - tl: { col: 0.4, row: SIG_ROW - 1 } as unknown as ExcelJS.Anchor, - ext: scaleToBox(signatureSize ?? { width: 360, height: 96 }, 165, 44), + tl: anchorAt(Math.max(0, sigLeft), SIG_ROW - 1), + ext: sigExt, editAs: "oneCell", }); sc(SIG_ROW, 1, "", { border: { top: thin(), left: thin(), right: thin() } }); @@ -485,19 +520,6 @@ export async function GET(request: NextRequest, { params }: Props) { ws.getRow(SIG_ROW + 2).height = 14; ws.getRow(SIG_ROW + 3).height = 14; - // Company stamp / seal — overlays the right of the approver's signatory block (aspect preserved) - if (stampImg) { - const stampId = wb.addImage({ - base64: stampImg.base64, - extension: stampImg.mime === "image/jpeg" ? "jpeg" : "png", - }); - ws.addImage(stampId, { - tl: { col: 2.35, row: SIG_ROW - 1 } as unknown as ExcelJS.Anchor, - ext: scaleToBox(stampImg, 80, 66), - editAs: "oneCell", - }); - } - // Right sig block (vendor) const vName = po.vendor?.name ?? ""; sc(SIG_ROW, 6, vName, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC });