feat: 积分商城-供应商-对账-发票
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
export type MockInvoicePdfInput = Record<string, unknown>
|
||||
|
||||
export type MockInvoicePdfOptions = {
|
||||
fontPath?: string
|
||||
}
|
||||
|
||||
export function generateMockInvoicePdf(
|
||||
invoiceInput: MockInvoicePdfInput,
|
||||
outputPath: string,
|
||||
options?: MockInvoicePdfOptions
|
||||
): string
|
||||
@@ -184,6 +184,8 @@ const normalizeInvoice = input => {
|
||||
...input,
|
||||
invoiceDate: input.invoiceDate || new Date().toISOString().slice(0, 10),
|
||||
invoiceNumber: input.invoiceNumber || digest.slice(0, 20),
|
||||
documentInvoiceCode: input.documentInvoiceCode || digest.slice(0, 12),
|
||||
taxIdNumber: input.taxIdNumber || input.buyerTaxId || input.invoiceCode || '',
|
||||
checkCode: input.checkCode || digest.slice(20, 40),
|
||||
sellerName: input.sellerName || '模拟积分商城供应商',
|
||||
sellerTaxId: input.sellerTaxId || input.supplierId || 'SIMULATED-SUPPLIER',
|
||||
@@ -198,19 +200,11 @@ const normalizeInvoice = input => {
|
||||
}
|
||||
}
|
||||
|
||||
export const generateMockInvoicePdf = (invoiceInput, outputPath, options = {}) => {
|
||||
export const generateMockInvoicePdf = (invoiceInput, outputPath, _options = {}) => {
|
||||
const invoice = normalizeInvoice(invoiceInput)
|
||||
const fontPath = findFont(options.fontPath)
|
||||
const font = readFileSync(fontPath)
|
||||
const glyphFor = createGlyphMapper(font)
|
||||
const glyphUnicode = new Map()
|
||||
const commands = []
|
||||
|
||||
const encode = value => Array.from(String(value ?? '')).map(character => {
|
||||
const glyph = glyphFor(character.codePointAt(0))
|
||||
if (glyph && !glyphUnicode.has(glyph)) glyphUnicode.set(glyph, character)
|
||||
return glyph.toString(16).padStart(4, '0')
|
||||
}).join('').toUpperCase()
|
||||
const encode = value => utf16Hex(String(value ?? ''))
|
||||
const text = (value, x, y, size = 10, color = '0 0 0') => {
|
||||
commands.push(`BT /F1 ${size} Tf ${color} rg 1 0 0 1 ${x} ${y} Tm <${encode(value)}> Tj ET`)
|
||||
}
|
||||
@@ -232,7 +226,8 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, options = {}) =
|
||||
line(274, 540, 568, 540, 1.2, '0.65 0.05 0.05')
|
||||
text('仅供系统联调测试,禁止报销', 302, 522, 11, '0.75 0.1 0.1')
|
||||
|
||||
text(`发票号码:${invoice.invoiceNumber}`, 610, 555, 9)
|
||||
text(`发票号码:${invoice.invoiceNumber}`, 610, 572, 9)
|
||||
text(`发票代码:${invoice.documentInvoiceCode}`, 610, 555, 9)
|
||||
text(`开票日期:${invoice.invoiceDate}`, 610, 538, 9)
|
||||
text(`校验码:${invoice.checkCode}`, 610, 521, 9)
|
||||
text(`结算单号:${invoice.settlementId || ''}`, 50, 521, 9)
|
||||
@@ -252,7 +247,7 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, options = {}) =
|
||||
text('名称:', 132, 488, 9)
|
||||
wrappedText(invoice.invoiceTitle, 180, 488, 26, 9)
|
||||
text('纳税人识别号:', 132, 464, 9)
|
||||
text(invoice.invoiceCode, 235, 464, 9)
|
||||
text(invoice.taxIdNumber, 235, 464, 9)
|
||||
text('地址、电话:', 487, 488, 9)
|
||||
wrappedText(`${invoice.invoiceAddress || ''} ${invoice.invoiceContact || ''}`, 574, 488, 20, 8)
|
||||
text('开户行及账号:', 487, 448, 9)
|
||||
@@ -298,32 +293,13 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, options = {}) =
|
||||
commands.push('0.75 0.08 0.08 RG 2 w 798 88 m 798 111.196 779.196 130 756 130 c 732.804 130 714 111.196 714 88 c 714 64.804 732.804 46 756 46 c 779.196 46 798 64.804 798 88 c S')
|
||||
|
||||
const content = Buffer.from(commands.join('\n'), 'ascii')
|
||||
const toUnicodeEntries = [...glyphUnicode.entries()].map(([glyph, character]) =>
|
||||
`<${glyph.toString(16).padStart(4, '0').toUpperCase()}> <${utf16Hex(character)}>`)
|
||||
const toUnicodeBlocks = []
|
||||
for (let index = 0; index < toUnicodeEntries.length; index += 100) {
|
||||
const entries = toUnicodeEntries.slice(index, index + 100)
|
||||
toUnicodeBlocks.push(`${entries.length} beginbfchar`, ...entries, 'endbfchar')
|
||||
}
|
||||
const toUnicode = Buffer.from([
|
||||
'/CIDInit /ProcSet findresource begin', '12 dict begin', 'begincmap',
|
||||
'/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def',
|
||||
'/CMapName /MockInvoiceUnicode def', '/CMapType 2 def',
|
||||
'1 begincodespacerange', '<0000> <FFFF>', 'endcodespacerange',
|
||||
...toUnicodeBlocks,
|
||||
'endcmap', 'CMapName currentdict /CMap defineresource pop', 'end', 'end'
|
||||
].join('\n'), 'ascii')
|
||||
|
||||
const writer = createPdfWriter()
|
||||
writer.add('<< /Type /Catalog /Pages 2 0 R >>')
|
||||
writer.add('<< /Type /Pages /Kids [3 0 R] /Count 1 >>')
|
||||
writer.add(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>`)
|
||||
writer.add(streamObject('', content))
|
||||
writer.add('<< /Type /Font /Subtype /Type0 /BaseFont /MockInvoiceFont /Encoding /Identity-H /DescendantFonts [6 0 R] /ToUnicode 9 0 R >>')
|
||||
writer.add('<< /Type /Font /Subtype /CIDFontType2 /BaseFont /MockInvoiceFont /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 7 0 R /DW 1000 /CIDToGIDMap /Identity >>')
|
||||
writer.add('<< /Type /FontDescriptor /FontName /MockInvoiceFont /Flags 4 /FontBBox [-1000 -1000 3000 3000] /ItalicAngle 0 /Ascent 1000 /Descent -300 /CapHeight 800 /StemV 80 /FontFile2 8 0 R >>')
|
||||
writer.add(streamObject(`/Length1 ${font.length}`, font))
|
||||
writer.add(streamObject('', toUnicode))
|
||||
writer.add('<< /Type /Font /Subtype /Type0 /BaseFont /STSong-Light /Encoding /UniGB-UCS2-H /DescendantFonts [6 0 R] >>')
|
||||
writer.add('<< /Type /Font /Subtype /CIDFontType0 /BaseFont /STSong-Light /CIDSystemInfo << /Registry (Adobe) /Ordering (GB1) /Supplement 4 >> /DW 1000 >>')
|
||||
writer.save(outputPath)
|
||||
return outputPath
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user