fix(erp): 单据明细单价口径由「不含税单价」改为「含税单价」(前端)
明细公式改为:含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;
不含税金额 = 含税金额 − 税额。销售订单/采购订单/销售出库/采购入库/销售退货/采购退货
六张单据统一;采购申请补齐不含税金额、税额、含税合计三列。
已有单据的明细行(带 id)沿用后端返回值、不再重算,避免历史单据前端展示
按新口径算出与库中/列表页不一致的金额。
列顺序统一为 数量 | 含税单价(元) | 税率(%) | 不含税金额(元) | 税额(元) | 含税合计(元);
同步打印模板、详情页、选单弹窗、列表页与表头表单文案(金额合计→不含税金额合计、
优惠后金额→优惠后含税金额、税额合计→含税合计)。
Co-Authored-By: Claude Code <noreply@anthropic.com>
| | |
| | | productUnitId?: number; // 产品单位编号 |
| | | productUnitName?: string; // 产品单位名称 |
| | | productBarCode?: string; // 产品条码 |
| | | productPrice?: number; // 产品单价(参考价) |
| | | productPrice?: number; // 含税单价(参考价) |
| | | count: number; // 数量 |
| | | taxPercent?: number; // 税率 |
| | | totalPrice?: number; // 不含税金额 |
| | | taxPrice?: number; // 税额 |
| | | demandTime?: string; // 需求日期 |
| | | remark?: string; // 备注 |
| | | qcCheckFlag?: boolean; // 是否质检 |
| | | inCount?: number; // 入库数量 |
| | | } |
| | | } |
| | | |
| | |
| | | }, |
| | | { |
| | | fieldName: 'discountedPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | fixed: 'right', |
| | | minWidth: 120, |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '产品金额', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | fixed: 'right', |
| | | title: '合计金额', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | // TODO 芋艿 |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpPurchaseInApi.PurchaseInItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpPurchaseInApi.PurchaseInItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpPurchaseInApi.PurchaseInItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | |
| | | { field: 'supplierName', title: '供应商', minWidth: 120 }, |
| | | { field: 'orderTime', title: '订单时间', width: 120, formatter: 'formatDate' }, |
| | | { field: 'totalCount', title: '合计数量', width: 100 }, |
| | | { field: 'totalPrice', title: '合计金额', width: 100 }, |
| | | { field: 'totalPrice', title: '含税合计', width: 100 }, |
| | | ]; |
| | | } |
| | | |
| | |
| | | <div>订单号:{{ selectedItem.no || '-' }}</div> |
| | | <div>供应商:{{ selectedItem.supplierName || '-' }}</div> |
| | | <div>订单时间:{{ selectedItem.orderTime || '-' }}</div> |
| | | <div>合计金额:{{ selectedItem.totalPrice || '-' }} 元</div> |
| | | <div>含税合计:{{ selectedItem.totalPrice || '-' }} 元</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | |
| | | return z |
| | | .number() |
| | | .min(0, '订金不能小于0') |
| | | .max(values.totalPrice, '订金不能大于优惠后金额'); |
| | | .max(values.totalPrice, '订金不能大于优惠后含税金额'); |
| | | } |
| | | return z.number().min(0, '订金不能小于0'); |
| | | }, |
| | |
| | | }, |
| | | { |
| | | fieldName: 'totalPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPercent', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '税额合计', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | render: (val) => erpCountInputFormatter(val) || '-', |
| | | }, |
| | | { |
| | | label: '合计金额', |
| | | label: '含税合计金额', |
| | | field: 'totalPrice', |
| | | render: (val) => erpPriceInputFormatter(val) || '-', |
| | | }, |
| | |
| | | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpPurchaseOrderApi.PurchaseOrderItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpPurchaseOrderApi.PurchaseOrderItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpPurchaseOrderApi.PurchaseOrderItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '单价(参考价)', |
| | | title: '含税单价(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '预估金额', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPercent', |
| | |
| | | minWidth: 100, |
| | | fixed: 'right', |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPrice', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'totalTaxInclusivePrice' }, |
| | | }, |
| | | { |
| | | field: 'demandTime', |
| | |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '预估金额', |
| | | title: '含税金额', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '单价(参考价)', |
| | | minWidth: 120, |
| | | formatter: ({ cellValue }) => erpPriceInputFormatter(cellValue) || '-', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '预估金额', |
| | | title: '含税单价(元)', |
| | | minWidth: 120, |
| | | formatter: ({ cellValue }) => erpPriceInputFormatter(cellValue) || '-', |
| | | }, |
| | |
| | | title: '税率(%)', |
| | | minWidth: 90, |
| | | formatter: ({ cellValue }) => (cellValue ? `${cellValue}%` : '-'), |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | formatter: ({ cellValue }) => erpPriceInputFormatter(cellValue) || '-', |
| | | }, |
| | | { |
| | | field: 'taxPrice', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | formatter: ({ cellValue }) => erpPriceInputFormatter(cellValue) || '-', |
| | | }, |
| | | { |
| | | field: 'totalTaxInclusivePrice', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | formatter: ({ row }) => |
| | | erpPriceInputFormatter((row.totalPrice || 0) + (row.taxPrice || 0)) || '-', |
| | | }, |
| | | { field: 'demandTime', title: '需求日期', minWidth: 110 }, |
| | | { |
| | |
| | | render: (val) => erpCountInputFormatter(val) || '-', |
| | | }, |
| | | { |
| | | label: '合计金额', |
| | | label: '含税合计金额', |
| | | field: 'totalPrice', |
| | | render: (val) => erpPriceInputFormatter(val) || '-', |
| | | }, |
| | |
| | | |
| | | const tableData = ref<ErpPurchaseRequestApi.PurchaseRequestItem[]>([]); |
| | | |
| | | /** 获取表格合计数据 */ |
| | | /** 获取表格合计数据(行上 totalPrice 为不含税金额,含税合计需加上税额) */ |
| | | const summaries = computed(() => { |
| | | return { |
| | | count: tableData.value.reduce((sum, item) => sum + (item.count || 0), 0), |
| | | totalPrice: tableData.value.reduce( |
| | | (sum, item) => { |
| | | const price = erpPriceMultiply(item.productPrice || 0, item.count || 0) ?? 0; |
| | | return sum + price; |
| | | }, |
| | | (sum, item) => sum + (item.totalPrice || 0), |
| | | 0, |
| | | ), |
| | | taxPrice: tableData.value.reduce((sum, item) => sum + (item.taxPrice || 0), 0), |
| | | }; |
| | | }); |
| | | |
| | | /** 含税合计 = 不含税金额 + 税额 */ |
| | | const totalTaxInclusivePrice = computed( |
| | | () => summaries.value.totalPrice + summaries.value.taxPrice, |
| | | ); |
| | | |
| | | /** 表格配置 */ |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | |
| | | handleRowChange(row); |
| | | } |
| | | |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpPurchaseRequestApi.PurchaseRequestItem) { |
| | | if (row.productPrice && row.count) { |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = taxInclusivePrice - row.taxPrice; |
| | | } |
| | | } |
| | | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | | } else { |
| | | tableData.value[index] = row; |
| | | } |
| | | // 计算预估金额 |
| | | if (row.productPrice && row.count) { |
| | | row.totalPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | } |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpPurchaseRequestApi.PurchaseRequestItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | /> |
| | | <span v-else>{{ row.taxPercent ? `${row.taxPercent}%` : '-' }}</span> |
| | | </template> |
| | | <template #totalTaxInclusivePrice="{ row }"> |
| | | <span>{{ erpPriceInputFormatter((row.totalPrice || 0) + (row.taxPrice || 0)) }}</span> |
| | | </template> |
| | | <template #demandTime="{ row }"> |
| | | <DatePicker |
| | | v-if="!disabled" |
| | |
| | | <span class="font-medium text-foreground">合计:</span> |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span>预估金额:{{ erpPriceInputFormatter(summaries.totalPrice) }}</span> |
| | | <span>不含税金额:{{ erpPriceInputFormatter(summaries.totalPrice) }}</span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span>含税合计:{{ erpPriceInputFormatter(totalTaxInclusivePrice) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | }, |
| | | { |
| | | fieldName: 'discountedPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | fixed: 'right', |
| | | minWidth: 120, |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '产品金额', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | fixed: 'right', |
| | | title: '合计金额', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | // TODO 芋艿 |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpPurchaseReturnApi.PurchaseReturnItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpPurchaseReturnApi.PurchaseReturnItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpPurchaseReturnApi.PurchaseReturnItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | |
| | | { field: 'customerName', title: '客户', minWidth: 120 }, |
| | | { field: 'orderTime', title: '订单时间', width: 120, formatter: 'formatDate' }, |
| | | { field: 'totalCount', title: '合计数量', width: 100 }, |
| | | { field: 'totalPrice', title: '合计金额', width: 100 }, |
| | | { field: 'totalPrice', title: '含税合计', width: 100 }, |
| | | ]; |
| | | } |
| | | |
| | |
| | | <div>订单号:{{ selectedItem.no || '-' }}</div> |
| | | <div>客户:{{ selectedItem.customerName || '-' }}</div> |
| | | <div>订单时间:{{ selectedItem.orderTime || '-' }}</div> |
| | | <div>合计金额:{{ selectedItem.totalPrice || '-' }} 元</div> |
| | | <div>含税合计:{{ selectedItem.totalPrice || '-' }} 元</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | |
| | | }, |
| | | { |
| | | fieldName: 'totalPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | return z |
| | | .number() |
| | | .min(0, '订金不能小于0') |
| | | .max(values.totalPrice, '收取订金不能大于优惠后金额'); |
| | | .max(values.totalPrice, '收取订金不能大于优惠后含税金额'); |
| | | } |
| | | return z.number().min(0, '订金不能小于0').optional(); |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPercent', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: '税额合计', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | render: (val) => erpCountInputFormatter(val) || '-', |
| | | }, |
| | | { |
| | | label: '合计金额', |
| | | label: '含税合计金额', |
| | | field: 'totalPrice', |
| | | render: (val) => erpPriceInputFormatter(val) || '-', |
| | | }, |
| | |
| | | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpSaleOrderApi.SaleOrderItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpSaleOrderApi.SaleOrderItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpSaleOrderApi.SaleOrderItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | // 默认需要生产 |
| | | if (row.needProduction === undefined) { |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | |
| | | } |
| | | |
| | | /** |
| | | * 明细行(后端不返回明细行的 totalProductPrice/totalPrice,需按单价×数量计算: |
| | | * 金额 = 单价 × 数量;税额 = 金额 × 税率;含税金额 = 金额 + 税额) |
| | | * 明细行(优先用后端返回值;缺省时按「单价为含税单价」计算: |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额) |
| | | */ |
| | | const printRows = computed(() => |
| | | (printData.value.items || []).map((item) => { |
| | | const totalProductPrice = |
| | | item.totalProductPrice ?? multiplyPrice(item.productPrice, item.count) ?? 0; |
| | | const taxInclusivePrice = multiplyPrice(item.productPrice, item.count); |
| | | const taxPrice = |
| | | item.taxPrice ?? multiplyPrice(totalProductPrice, (item.taxPercent ?? 0) / 100) ?? 0; |
| | | const totalPrice = item.totalPrice ?? totalProductPrice + taxPrice; |
| | | item.taxPrice ?? multiplyPrice(taxInclusivePrice, (item.taxPercent ?? 0) / 100) ?? 0; |
| | | const totalPrice = item.totalPrice ?? taxInclusivePrice ?? 0; |
| | | const totalProductPrice = item.totalProductPrice ?? totalPrice - taxPrice; |
| | | return { ...item, totalProductPrice, taxPrice, totalPrice }; |
| | | }), |
| | | ); |
| | | |
| | | /** 明细合计:数量/金额优先取订单头(后端权威值),税额与含税金额按明细累加(订单头 totalPrice 为优惠后金额,另在金额信息区展示) */ |
| | | /** 明细合计:数量/金额优先取订单头(后端权威值),税额与含税金额按明细累加(订单头 totalPrice 为优惠后含税金额,另在金额信息区展示) */ |
| | | const summaries = computed(() => { |
| | | const rows = printRows.value; |
| | | const count = rows.reduce((sum, item) => sum + (item.count || 0), 0); |
| | |
| | | <th |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right font-bold" |
| | | > |
| | | 产品单价(元) |
| | | </th> |
| | | <th |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right font-bold" |
| | | > |
| | | 金额(元) |
| | | 含税单价(元) |
| | | </th> |
| | | <th |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right font-bold" |
| | | > |
| | | 税率(%) |
| | | </th> |
| | | <th |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right font-bold" |
| | | > |
| | | 不含税金额(元) |
| | | </th> |
| | | <th |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right font-bold" |
| | |
| | | {{ formatPrice(item.productPrice) }} |
| | | </td> |
| | | <td class="border border-solid border-[#dcdfe6] p-2 text-right"> |
| | | {{ formatPrice(item.totalProductPrice) }} |
| | | {{ formatPrice(item.taxPercent) }} |
| | | </td> |
| | | <td class="border border-solid border-[#dcdfe6] p-2 text-right"> |
| | | {{ formatPrice(item.taxPercent) }} |
| | | {{ formatPrice(item.totalProductPrice) }} |
| | | </td> |
| | | <td class="border border-solid border-[#dcdfe6] p-2 text-right"> |
| | | {{ formatPrice(item.taxPrice) }} |
| | |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2" |
| | | ></td> |
| | | <td |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2" |
| | | ></td> |
| | | <td |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right" |
| | | > |
| | | {{ formatPrice(summaries.totalProductPrice) }} |
| | | </td> |
| | | <td |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2" |
| | | ></td> |
| | | <td |
| | | class="border border-solid border-[#dcdfe6] bg-[#f5f7fa] p-2 text-right" |
| | | > |
| | |
| | | > |
| | | <div>优惠率(%):{{ formatPrice(printData.discountPercent) }}</div> |
| | | <div>付款优惠(元):{{ formatPrice(printData.discountPrice) }}</div> |
| | | <div>优惠后金额(元):{{ formatPrice(printData.totalPrice) }}</div> |
| | | <div>优惠后含税金额(元):{{ formatPrice(printData.totalPrice) }}</div> |
| | | <div>收取订金(元):{{ formatPrice(printData.depositPrice) }}</div> |
| | | </div> |
| | | |
| | |
| | | }, |
| | | { |
| | | fieldName: 'discountedPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | fixed: 'right', |
| | | minWidth: 120, |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '产品金额', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | fixed: 'right', |
| | | title: '合计金额', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpSaleOutApi.SaleOutItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpSaleOutApi.SaleOutItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpSaleOutApi.SaleOutItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | |
| | | }, |
| | | { |
| | | fieldName: 'discountedPrice', |
| | | label: '优惠后金额', |
| | | label: '优惠后含税金额', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '优惠后金额', |
| | | placeholder: '优惠后含税金额', |
| | | precision: 2, |
| | | formatter: erpPriceInputFormatter, |
| | | disabled: true, |
| | |
| | | }, |
| | | { |
| | | field: 'productPrice', |
| | | title: '产品单价', |
| | | title: '含税单价(元)', |
| | | fixed: 'right', |
| | | minWidth: 120, |
| | | slots: { default: 'productPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '产品金额', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | |
| | | slots: { default: 'taxPercent' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | fixed: 'right', |
| | | title: '不含税金额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | fixed: 'right', |
| | | field: 'taxPrice', |
| | | title: '税额', |
| | | title: '税额(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | fixed: 'right', |
| | | title: '合计金额', |
| | | title: '含税合计(元)', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | title: '不含税金额合计', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | |
| | | |
| | | /** 处理行数据变更 */ |
| | | function handleRowChange(row: any) { |
| | | recalcRow(row); |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** 初始化行数据 */ |
| | | function initRow(row: ErpSaleReturnApi.SaleReturnItem) { |
| | | /** |
| | | * 重算行金额(单价为**含税**单价) |
| | | * 含税金额 = 含税单价 × 数量;税额 = 含税金额 × 税率;不含税金额 = 含税金额 - 税额 |
| | | */ |
| | | function recalcRow(row: ErpSaleReturnApi.SaleReturnItem) { |
| | | if (row.productPrice && row.count) { |
| | | row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | const taxInclusivePrice = erpPriceMultiply(row.productPrice, row.count) ?? 0; |
| | | row.taxPrice = |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | erpPriceMultiply(taxInclusivePrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalProductPrice = taxInclusivePrice - row.taxPrice; |
| | | row.totalPrice = taxInclusivePrice; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化行数据 |
| | | * 已有单据的明细行(带 id)沿用后端返回的金额,避免历史单据被新口径覆盖; |
| | | * 只有未保存的新增行才按新口径重算。 |
| | | */ |
| | | function initRow(row: ErpSaleReturnApi.SaleReturnItem) { |
| | | if (!row.id) { |
| | | recalcRow(row); |
| | | } |
| | | } |
| | | |
| | |
| | | <div class="flex space-x-4"> |
| | | <span>数量:{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span> |
| | | 金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | 不含税金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }} |
| | | </span> |
| | | <span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span> |
| | | <span> |
| | | 税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | 含税合计:{{ erpPriceInputFormatter(summaries.totalPrice) }} |
| | | </span> |
| | | </div> |
| | | </div> |