feat: 积分商城-供应商-对账-发票4
This commit is contained in:
@@ -324,6 +324,32 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, _options = {})
|
||||
const invoiceDate = dateValue.replace(/^(\d{4})-(\d{2})-(\d{2}).*$/, '$1年$2月$3日')
|
||||
const title = Number(invoice.invoiceType) === 9 ? '电子发票(增值税专用发票)' : '电子发票(普通发票)'
|
||||
|
||||
const products = Array.isArray(invoice.orderProducts) && invoice.orderProducts.length
|
||||
? invoice.orderProducts
|
||||
: [{ name: invoice.invoiceContent, number: invoice.orderNum, price: invoice.total / invoice.orderNum, unit: '项' }]
|
||||
const weights = products.map(product => Math.max(Number(product.price || 0) * Number(product.number || 1), 0))
|
||||
const totalWeight = weights.reduce((sum, value) => sum + value, 0)
|
||||
const nakedCents = Math.round(invoice.naked * 100)
|
||||
const taxCents = Math.round(invoice.tax * 100)
|
||||
let accumulatedWeight = 0
|
||||
let allocatedNaked = 0
|
||||
let allocatedTax = 0
|
||||
// 按商品金额分摊票面金额,累计取整以消除逐行计算产生的分位尾差。
|
||||
const invoiceItems = products.map((product, index) => {
|
||||
accumulatedWeight += weights[index]
|
||||
const ratio = totalWeight > 0 ? accumulatedWeight / totalWeight : (index + 1) / products.length
|
||||
const nextNaked = index === products.length - 1 ? nakedCents : Math.round(nakedCents * ratio)
|
||||
const nextTax = index === products.length - 1 ? taxCents : Math.round(taxCents * ratio)
|
||||
const item = { ...product, number: Number(product.number || 1), naked: (nextNaked - allocatedNaked) / 100, tax: (nextTax - allocatedTax) / 100 }
|
||||
allocatedNaked = nextNaked
|
||||
allocatedTax = nextTax
|
||||
return item
|
||||
})
|
||||
const pages = []
|
||||
const itemsPerPage = 5
|
||||
for (let itemOffset = 0; itemOffset < invoiceItems.length; itemOffset += itemsPerPage) {
|
||||
commands = []
|
||||
const lastItemPage = itemOffset + itemsPerPage >= invoiceItems.length
|
||||
commands.push('1 1 1 rg 0 0 842 595 re f')
|
||||
text(title, (PAGE_WIDTH - title.length * 21) / 2, 546, 21, labelColor)
|
||||
line(245, 531, 590, 531, 0.8, labelColor)
|
||||
@@ -358,22 +384,30 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, _options = {})
|
||||
const x = (columns[index] + columns[index + 1] - label.length * 9) / 2
|
||||
text(label, x, 382, 9, labelColor)
|
||||
})
|
||||
wrappedText(`*商品*${String(invoice.invoiceContent).length > 50 ? '订单商品汇总(详见后附明细)' : invoice.invoiceContent}`, 50, 360, 20, 9, 14)
|
||||
text('项', 344, 360, 9)
|
||||
fitText(invoice.orderNum, 398, 360, 32, 9)
|
||||
fitText(money(invoice.naked / invoice.orderNum), 445, 360, 60, 9)
|
||||
fitText(money(invoice.naked), 526, 360, 73, 9)
|
||||
fitText(`${Number((invoice.taxRate * 100).toFixed(4))}%`, 627, 360, 60, 9)
|
||||
fitText(money(invoice.tax), 722, 360, 70, 9)
|
||||
invoiceItems.slice(itemOffset, itemOffset + itemsPerPage).forEach((item, index) => {
|
||||
const y = 360 - index * 23
|
||||
fitText(`*商品*${item.name || item.sku || '商品'}`, 50, y, 184, 9)
|
||||
fitText(item.spec || item.skuText || '', 247, y, 69, 8)
|
||||
fitText(item.unit || '件', 337, y, 40, 9)
|
||||
fitText(item.number, 391, y, 40, 9)
|
||||
fitText(Number((item.naked / item.number).toFixed(6)).toString(), 445, y, 60, 9)
|
||||
fitText(money(item.naked), 526, y, 73, 9)
|
||||
fitText(`${Number((invoice.taxRate * 100).toFixed(4))}%`, 627, y, 60, 9)
|
||||
fitText(money(item.tax), 722, y, 70, 9)
|
||||
})
|
||||
if (lastItemPage) {
|
||||
text('合 计', 92, 230, 10, labelColor)
|
||||
fitText(`¥${money(invoice.naked)}`, 526, 230, 73, 10)
|
||||
fitText(`¥${money(invoice.tax)}`, 722, 230, 70, 10)
|
||||
} else {
|
||||
text('项目明细续下页,合计见末页', 92, 230, 10, labelColor)
|
||||
}
|
||||
|
||||
line(235, 183, 235, 217, 0.7, labelColor)
|
||||
text('价税合计(大写)', 88, 196, 10, labelColor)
|
||||
fitText(chineseAmount(invoice.total), 245, 196, 340, 11)
|
||||
fitText(lastItemPage ? chineseAmount(invoice.total) : '详见项目明细末页', 245, 196, 340, 11)
|
||||
text('(小写)', 603, 196, 10, labelColor)
|
||||
fitText(`¥${money(invoice.total)}`, 657, 196, 133, 11)
|
||||
if (lastItemPage) fitText(`¥${money(invoice.total)}`, 657, 196, 133, 11)
|
||||
|
||||
line(70, 100, 70, 183, 0.7, labelColor)
|
||||
text('备', 51, 153, 10, labelColor)
|
||||
@@ -384,7 +418,9 @@ export const generateMockInvoicePdf = (invoiceInput, outputPath, _options = {})
|
||||
text('开票人:', 90, 73, 10, labelColor)
|
||||
fitText(invoice.biller || '模拟开票员', 140, 73, 250, 10)
|
||||
|
||||
const pages = [Buffer.from(commands.join('\n'), 'ascii')]
|
||||
text(`项目明细第 ${pages.length + 1} / ${Math.ceil(invoiceItems.length / itemsPerPage)} 页`, 620, 73, 9, labelColor)
|
||||
pages.push(Buffer.from(commands.join('\n'), 'ascii'))
|
||||
}
|
||||
const rows = (Array.isArray(invoice.orderProducts) ? invoice.orderProducts : []).flatMap(product => {
|
||||
const name = Array.from(String(product.name || product.sku || '商品'))
|
||||
const lines = []
|
||||
|
||||
Reference in New Issue
Block a user