feat(contract): 新增合同打印功能,重构产品金额计算逻辑
1. 新增合同打印预览功能,支持A4格式打印,包含合同完整信息和产品明细 2. 新增多个产品金额计算工具函数,统一管理产品税额、无税单价等字段计算 3. 重构产品内容组件,新增税率除数、无税单价、税额列,实现字段联动自动计算 4. 新增合同logo静态资源,优化表格布局和样式
This commit is contained in:
@@ -89,6 +89,122 @@ export function calculateProductTaxTotal(product) {
|
||||
return quantity * taxPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算无税单价 = 含税单价 / 税率除数
|
||||
* @param {Object} product - 产品对象
|
||||
* @returns {number} 无税单价
|
||||
*/
|
||||
export function calculateProductNoTaxPrice(product) {
|
||||
if (!product) return 0;
|
||||
const taxPrice = parseFloat(product.taxPrice) || 0;
|
||||
const taxDivisor = parseFloat(product.taxDivisor) || 1.13;
|
||||
return taxPrice / taxDivisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算无税总额 = 数量 * 无税单价
|
||||
* @param {Object} product - 产品对象
|
||||
* @returns {number} 无税总额
|
||||
*/
|
||||
export function calculateProductNoTaxTotal(product) {
|
||||
if (!product) return 0;
|
||||
const quantity = parseFloat(product.quantity) || 0;
|
||||
const noTaxPrice = parseFloat(product.noTaxPrice) || 0;
|
||||
return quantity * noTaxPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算税额 = 含税总额 - 无税总额
|
||||
* @param {Object} product - 产品对象
|
||||
* @returns {number} 税额
|
||||
*/
|
||||
export function calculateProductTaxAmount(product) {
|
||||
if (!product) return 0;
|
||||
const taxTotal = parseFloat(product.taxTotal) || 0;
|
||||
const noTaxTotal = parseFloat(product.noTaxTotal) || 0;
|
||||
return taxTotal - noTaxTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变更字段重新计算产品所有金额字段
|
||||
* @param {Object} product - 产品对象
|
||||
* @param {string} changedField - 变更的字段名
|
||||
* @returns {Object} 更新后的产品对象
|
||||
*/
|
||||
export function calculateProductFields(product, changedField = 'quantity') {
|
||||
if (!product) return product;
|
||||
|
||||
const quantity = parseFloat(product.quantity) || 0;
|
||||
let taxPrice = parseFloat(product.taxPrice) || 0;
|
||||
let taxDivisor = parseFloat(product.taxDivisor) || 1.13;
|
||||
let noTaxPrice, noTaxTotal, taxTotal, taxAmount;
|
||||
|
||||
switch (changedField) {
|
||||
case 'quantity':
|
||||
case 'taxPrice':
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxPrice = taxPrice / taxDivisor;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
case 'taxDivisor':
|
||||
noTaxPrice = taxPrice / taxDivisor;
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
case 'noTaxPrice':
|
||||
noTaxPrice = parseFloat(product.noTaxPrice) || 0;
|
||||
taxPrice = noTaxPrice * taxDivisor;
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
case 'noTaxTotal':
|
||||
noTaxTotal = parseFloat(product.noTaxTotal) || 0;
|
||||
noTaxPrice = quantity > 0 ? noTaxTotal / quantity : 0;
|
||||
taxPrice = noTaxPrice * taxDivisor;
|
||||
taxTotal = quantity * taxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
case 'taxTotal':
|
||||
taxTotal = parseFloat(product.taxTotal) || 0;
|
||||
taxPrice = quantity > 0 ? taxTotal / quantity : 0;
|
||||
noTaxPrice = taxPrice / taxDivisor;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
case 'taxAmount':
|
||||
taxAmount = parseFloat(product.taxAmount) || 0;
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxTotal = taxTotal - taxAmount;
|
||||
noTaxPrice = quantity > 0 ? noTaxTotal / quantity : 0;
|
||||
taxPrice = quantity > 0 ? noTaxPrice * taxDivisor : 0;
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
break;
|
||||
default:
|
||||
taxTotal = quantity * taxPrice;
|
||||
noTaxPrice = taxPrice / taxDivisor;
|
||||
noTaxTotal = quantity * noTaxPrice;
|
||||
taxAmount = taxTotal - noTaxTotal;
|
||||
}
|
||||
|
||||
const round2 = (v) => Math.round(v * 100) / 100;
|
||||
|
||||
return {
|
||||
...product,
|
||||
quantity,
|
||||
taxPrice: round2(taxPrice),
|
||||
noTaxPrice: round2(noTaxPrice),
|
||||
taxTotal: round2(taxTotal),
|
||||
noTaxTotal: round2(noTaxTotal),
|
||||
taxAmount: round2(taxAmount),
|
||||
taxDivisor
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算所有产品的含税总额和总计
|
||||
* @param {Object} content - 产品内容对象
|
||||
@@ -99,10 +215,9 @@ export function recalculateTotals(content) {
|
||||
return { ...DEFAULT_PRODUCT_CONTENT };
|
||||
}
|
||||
|
||||
const products = (content.products || []).map(product => ({
|
||||
...product,
|
||||
taxTotal: calculateProductTaxTotal(product)
|
||||
}));
|
||||
const products = (content.products || []).map(product =>
|
||||
calculateProductFields(product, 'quantity')
|
||||
);
|
||||
|
||||
const totalQuantity = calculateTotalQuantity(products);
|
||||
const totalTaxTotal = calculateTotalTaxTotal(products);
|
||||
@@ -124,15 +239,16 @@ export function recalculateTotals(content) {
|
||||
export function convertOrderItemToProduct(orderItem) {
|
||||
if (!orderItem) return {};
|
||||
|
||||
return {
|
||||
const product = {
|
||||
spec: orderItem.finishedProductSpec || '',
|
||||
material: orderItem.material || '',
|
||||
quantity: parseFloat(orderItem.weight) || 0,
|
||||
taxPrice: parseFloat(orderItem.contractPrice) || 0,
|
||||
noTaxPrice: parseFloat(orderItem.itemAmount) || 0,
|
||||
taxTotal: (parseFloat(orderItem.contractPrice) || 0) * (parseFloat(orderItem.weight) || 0),
|
||||
taxDivisor: parseFloat(orderItem.taxDivisor) || 1.13,
|
||||
remark: orderItem.remark || ''
|
||||
};
|
||||
|
||||
return calculateProductFields(product, 'quantity');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,6 +420,10 @@ export default {
|
||||
calculateTotalQuantity,
|
||||
calculateTotalTaxTotal,
|
||||
calculateProductTaxTotal,
|
||||
calculateProductNoTaxPrice,
|
||||
calculateProductNoTaxTotal,
|
||||
calculateProductTaxAmount,
|
||||
calculateProductFields,
|
||||
recalculateTotals,
|
||||
convertOrderItemToProduct,
|
||||
convertOrderItemsToContent,
|
||||
|
||||
Reference in New Issue
Block a user