diff --git a/packages/app-client/src/components/icon/Icon.tsx b/packages/app-client/src/components/icon/Icon.tsx index c80f6af..4e4e984 100644 --- a/packages/app-client/src/components/icon/Icon.tsx +++ b/packages/app-client/src/components/icon/Icon.tsx @@ -3,6 +3,8 @@ import classNames from "classnames"; import React from "react"; export type IconNames = + | "calculator" + | "compass" | "eye" | "eye-slash" | "phone-flip" diff --git a/packages/app-client/src/components/purchase/cost/CostList.tsx b/packages/app-client/src/components/purchase/cost/CostList.tsx index bd11dbd..b56329c 100644 --- a/packages/app-client/src/components/purchase/cost/CostList.tsx +++ b/packages/app-client/src/components/purchase/cost/CostList.tsx @@ -156,15 +156,13 @@ export default function CostList(props: { )} {orderCosts.map((orderCost) => { - if (type === "MATERIAL_TYPE") { - if ( - orderCost.name === "空箱费" || - orderCost.name === "纸箱费" || - orderCost.name === "运费" || - orderCost.name === "草帘费" - ) { - return <>; - } + if ( + orderCost.name === "空箱费" || + orderCost.name === "纸箱费" || + orderCost.name === "运费" || + orderCost.name === "草帘费" + ) { + return <>; } return ( { - return ( - {(record.boxProductWeight * record.boxCount).toFixed(2)} - ); + return new Decimal(record.boxProductWeight) + .mul(new Decimal(record.boxCount)) + .toDecimalPlaces(0, Decimal.ROUND_HALF_UP) + .toNumber(); }, }, ]); @@ -148,10 +151,10 @@ export default function PurchasePreview(props: IPurchasePreviewProps) { return ( - + - - - 空磅 - - {formatCurrency(supplier.emptyWeight)} KG + + + 合计金额 + + {supplier.invoiceAmount} 元{" "} + {supplier.isDepositPaid + ? `(含定金:${calculator.getDepositPaidAmount()}元)` + : ""} + + + + {/* 计算公式区域 */} + + + + + 计算明细 - - 总磅 - - {formatCurrency(supplier.totalWeight || 0)} KG + + + 总重量(kg) + + {supplier.totalWeight || 0} + - - - 毛重 - - {formatCurrency(calculator.getGrossWeight())} 斤 + + 空重量(kg) + + {supplier.emptyWeight || 0} + - - - 净重 - - {formatCurrency(calculator.getNetWeight())} 斤 + + 箱重量(斤) + + {calculator.calculateBoxesTotalWeight("USED") || 0} + - - - 箱重 - - {formatCurrency(calculator.getBoxWeight())} 斤 + + 净重量(斤) + + {supplier.netWeight || 0} + - - - 采购单价 - - {formatCurrency(calculator.getPurchasePrice())} 元/斤 + + 采购单价(元/斤) + + {supplier.purchasePrice || 0} + - - - 合计金额 - - {formatCurrency(calculator.getTotalAmount())} 元{" "} - {supplier.isDepositPaid - ? `(含定金:${formatCurrency(calculator.getDepositPaidAmount())}元)` - : ""} + + 报价方式 + + {supplier.pricingMethod === "BY_GROSS_WEIGHT" + ? "毛重" + : "净重"} + + + + + 计算公式:净重量 × 采购单价 @@ -221,7 +245,7 @@ export default function PurchasePreview(props: IPurchasePreviewProps) { return Object.entries(groupedPackageInfo).map( ([brandName, packageInfos]) => ( - + 纸箱品牌:{brandName} @@ -238,11 +262,11 @@ export default function PurchasePreview(props: IPurchasePreviewProps) { 总箱数 - {formatCurrency(calculator.getBoxCount())} 箱 + {calculator.getBoxCount()} 箱 总箱重 - {formatCurrency(calculator.getBoxWeight())} 斤 + {calculator.getBoxWeight()} 斤 diff --git a/packages/app-client/src/components/purchase/module/TicketUpload.tsx b/packages/app-client/src/components/purchase/module/TicketUpload.tsx index 2f7a924..367c67c 100644 --- a/packages/app-client/src/components/purchase/module/TicketUpload.tsx +++ b/packages/app-client/src/components/purchase/module/TicketUpload.tsx @@ -1,7 +1,7 @@ import { Image, View } from "@tarojs/components"; import { Icon } from "@/components"; -import { Button, Price, Toast } from "@nutui/nutui-react-taro"; -import { uploadFile } from "@/utils"; +import { Button, Toast } from "@nutui/nutui-react-taro"; +import { OrderSupplierCalculator, uploadFile } from "@/utils"; import Taro from "@tarojs/taro"; import { globalStore } from "@/store/global-store"; @@ -29,33 +29,91 @@ export default function TicketUpload(props: ITicketUploadProps) { if (!supplierVO) { return; } + const calculator = new OrderSupplierCalculator( + value as any, + supplierVO as any, + ); return ( - + - {supplierVO.name}的开票信息 + {supplierVO.name}的应开票信息 - - - - 应开票金额 + + {/* 应开票金额突出显示 */} + + + + 应开票金额(元) - - + + {supplierVO.invoiceAmount || 0} - - 上传发票(可跳过) + {/* 计算公式区域 */} + + + + + 计算明细 + + + + + 总重量(kg) + + {supplierVO.totalWeight || 0} + + + + 空重量(kg) + + {supplierVO.emptyWeight || 0} + + + + 箱重量(斤) + + {calculator.calculateBoxesTotalWeight("USED") || 0} + + + + 净重量(斤) + + {supplierVO.netWeight || 0} + + + + 采购单价(元/斤) + + {supplierVO.purchasePrice || 0} + + + + 报价方式 + + {supplierVO.pricingMethod === "BY_GROSS_WEIGHT" + ? "毛重" + : "净重"} + + + + + 计算公式:净重量 × 采购单价 + + + + + + + + + + 上传{supplierVO.name}的发票(可跳过) {supplierVO.invoiceUpload ? ( @@ -213,9 +271,13 @@ export default function TicketUpload(props: ITicketUploadProps) { )} + + - - 上传合同(可跳过) + + + + 上传{supplierVO.name}的合同(可跳过) {supplierVO.contractUpload && supplierVO.contractImg && diff --git a/packages/app-client/src/components/purchase/section/EmptyBoxInfoSection.tsx b/packages/app-client/src/components/purchase/section/EmptyBoxInfoSection.tsx index e6c86f0..c4735a3 100644 --- a/packages/app-client/src/components/purchase/section/EmptyBoxInfoSection.tsx +++ b/packages/app-client/src/components/purchase/section/EmptyBoxInfoSection.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { Button, Input, Popup, SafeArea, Table } from "@nutui/nutui-react-taro"; import { Icon } from "@/components"; import { View } from "@tarojs/components"; +import { Decimal } from "decimal.js"; export default function EmptyBoxInfoSection(props: { purchaseOrderVO: BusinessAPI.PurchaseOrderVO; @@ -336,11 +337,10 @@ export default function EmptyBoxInfoSection(props: { ); } - return formatCurrency( - Number( - (rowData?.boxSalePrice || 0) * rowData.boxProductCount, - ) as number, - ); + return new Decimal(rowData?.boxSalePrice || 0) + .mul(rowData.boxProductCount) + .toDecimalPlaces(0, Decimal.ROUND_HALF_UP) + .toNumber(); }, }; } else if (column.key === "boxProductWeight") { diff --git a/packages/app-client/src/components/purchase/section/MarketPriceSection.tsx b/packages/app-client/src/components/purchase/section/MarketPriceSection.tsx index 09001de..4cb2207 100644 --- a/packages/app-client/src/components/purchase/section/MarketPriceSection.tsx +++ b/packages/app-client/src/components/purchase/section/MarketPriceSection.tsx @@ -133,7 +133,7 @@ export default function MarketPriceSection(props: { // 销售金额 const saleAmount = calculator.getSalesAmount(); - const totalAmount = calculator.getTotalAmount(); + const totalAmount = calculator.getMarketPrice(); // 计算平均单价(所有供应商单价的平均值) const averagePurchasePrice = calculator.getAverageSalesPrice(); @@ -210,26 +210,25 @@ export default function MarketPriceSection(props: { 毛重 - {(supplier.grossWeight || 0).toFixed(2)} 斤 + {supplier.grossWeight || 0} 斤 净重 - {(supplier.netWeight || 0).toFixed(2)} 斤 + {supplier.netWeight || 0} 斤 箱重 - {(supplier.grossWeight - supplier.netWeight).toFixed(2)}{" "} - 斤 + {supplier.grossWeight - supplier.netWeight} 斤 采购单价 - {supplier.purchasePrice.toFixed(2)} 元/斤 + {supplier.purchasePrice} 元/斤 {supplier.isDepositPaid && ( diff --git a/packages/app-client/src/components/purchase/section/PackageInfoSection.tsx b/packages/app-client/src/components/purchase/section/PackageInfoSection.tsx index e64e757..bc41f69 100644 --- a/packages/app-client/src/components/purchase/section/PackageInfoSection.tsx +++ b/packages/app-client/src/components/purchase/section/PackageInfoSection.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { Button, Input, Popup, SafeArea, Table } from "@nutui/nutui-react-taro"; import { Icon } from "@/components"; import { View } from "@tarojs/components"; +import { Decimal } from "decimal.js"; export default function PackageInfoSection(props: { purchaseOrderVO: BusinessAPI.PurchaseOrderVO; @@ -69,15 +70,6 @@ export default function PackageInfoSection(props: { { title: "销售金额(元)", key: "boxSalePayment", - render: ( - value: BusinessAPI.OrderPackage & { - boxProductCount: number; - isTotalRow?: boolean; - }, - ) => - formatCurrency( - Number((value?.boxSalePrice || 0) * value.boxProductCount) as number, - ), }, { title: "箱重(斤)", @@ -189,23 +181,29 @@ export default function PackageInfoSection(props: { } // 计算各项合计 - let totalBoxProductCount = 0; - let totalBoxSalePayment = 0; - let totalBoxProductWeight = 0; + let totalBoxProductCount = new Decimal(0); + let totalBoxSalePayment = new Decimal(0); + let totalBoxProductWeight = new Decimal(0); packageData.forEach((pkg: any) => { - totalBoxProductCount += pkg.boxProductCount || 0; - totalBoxSalePayment += - Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0; - totalBoxProductWeight += - Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0; + totalBoxProductCount = totalBoxProductCount.add(pkg.boxProductCount || 0); + totalBoxSalePayment = totalBoxSalePayment.add( + new Decimal(pkg?.boxSalePrice || 0) + .mul(pkg.boxProductCount || 0) + .toDecimalPlaces(0, Decimal.ROUND_HALF_UP), + ); + totalBoxProductWeight = totalBoxProductWeight.add( + new Decimal(pkg?.boxProductWeight || 0) + .mul(pkg.boxProductCount || 0) + .toDecimalPlaces(0, Decimal.ROUND_HALF_UP), + ); }); return { boxProductName: "合计", - boxProductCount: totalBoxProductCount, - boxSalePayment: totalBoxSalePayment, - boxProductWeight: totalBoxProductWeight, + boxProductCount: totalBoxProductCount.toNumber(), + boxSalePayment: totalBoxSalePayment.toNumber(), + boxProductWeight: totalBoxProductWeight.toNumber(), isTotalRow: true, // 标记这是合计行 }; }; @@ -351,11 +349,10 @@ export default function PackageInfoSection(props: { ); } - return formatCurrency( - Number( - (rowData?.boxSalePrice || 0) * rowData.boxProductCount, - ) as number, - ); + return new Decimal(rowData?.boxSalePrice || 0) + .mul(rowData.boxProductCount) + .toDecimalPlaces(0, Decimal.ROUND_HALF_UP) + .toNumber(); }, }; } else if (column.key === "boxProductWeight") { diff --git a/packages/app-client/src/iconfont.css b/packages/app-client/src/iconfont.css index 6b3fe5f..3666ef4 100644 --- a/packages/app-client/src/iconfont.css +++ b/packages/app-client/src/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 5042354 */ - src: url('//at.alicdn.com/t/c/font_5042354_hkkkrqw0kin.woff2?t=1763456511295') format('woff2'), - url('//at.alicdn.com/t/c/font_5042354_hkkkrqw0kin.woff?t=1763456511295') format('woff'), - url('//at.alicdn.com/t/c/font_5042354_hkkkrqw0kin.ttf?t=1763456511295') format('truetype'); + src: url('//at.alicdn.com/t/c/font_5042354_gjumaiad8dh.woff2?t=1765554257380') format('woff2'), + url('//at.alicdn.com/t/c/font_5042354_gjumaiad8dh.woff?t=1765554257380') format('woff'), + url('//at.alicdn.com/t/c/font_5042354_gjumaiad8dh.ttf?t=1765554257380') format('truetype'); } .iconfont { @@ -13,6 +13,14 @@ -moz-osx-font-smoothing: grayscale; } +.icon-calculator:before { + content: "\e626"; +} + +.icon-compass:before { + content: "\e625"; +} + .icon-phone-flip:before { content: "\e624"; } diff --git a/packages/app-client/src/pages/purchase/approval/audit.tsx b/packages/app-client/src/pages/purchase/approval/audit.tsx index 0474def..0643b67 100644 --- a/packages/app-client/src/pages/purchase/approval/audit.tsx +++ b/packages/app-client/src/pages/purchase/approval/audit.tsx @@ -106,7 +106,7 @@ export default hocAuth(function Page(props: CommonComponent) { item.price * item.count > 0 && ( {item.name} diff --git a/packages/app-client/src/pages/purchase/audit/audit.tsx b/packages/app-client/src/pages/purchase/audit/audit.tsx index 04eb344..2603e44 100644 --- a/packages/app-client/src/pages/purchase/audit/audit.tsx +++ b/packages/app-client/src/pages/purchase/audit/audit.tsx @@ -1,6 +1,6 @@ import hocAuth from "@/hocs/auth"; import { CommonComponent } from "@/types/typings"; -import Taro from "@tarojs/taro"; +import Taro, { usePageScroll } from "@tarojs/taro"; import { business } from "@/services"; import { useEffect, useState } from "react"; import { View } from "@tarojs/components"; @@ -23,6 +23,7 @@ import { DealerInfoSection, DeliveryFormSection, EmptyBoxInfoSection, + Icon, MarketPriceSection, MaterialCostSection, PackageInfoSection, @@ -190,11 +191,11 @@ const fullSections = [ component: CostDifferenceSection, title: "待分红金额复核", }, - // 成本合计 + // 成本合计复核 { name: "costSummary", component: CostSummarySection, - title: "成本合计", + title: "成本合计复核", }, // 个人返点复核 { @@ -309,6 +310,10 @@ export default hocAuth(function Page(props: CommonComponent) { // 控制更多操作的ActionSheet显示状态 const [moreActionVisible, setMoreActionVisible] = useState(false); + // 控制快速导航的显示状态 + const [showQuickNav, setShowQuickNav] = useState(false); + const [quickNavExpanded, setQuickNavExpanded] = useState(false); + const [dealerRebateCustomerVOList, setDealerRebateCustomerVOList] = useState(); @@ -426,6 +431,31 @@ export default hocAuth(function Page(props: CommonComponent) { setProvisionZeroConfirmVisible(false); }; + // 跳转到指定section + const scrollToSection = (sectionKey: string) => { + if (process.env.TARO_ENV === "h5") { + // H5环境使用DOM API + const element = document.getElementById(`section-${sectionKey}`); + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "start" }); + } + } else { + // 小程序环境使用Taro的节点查询 + Taro.createSelectorQuery() + .select(`#section-${sectionKey}`) + .boundingClientRect((rect) => { + if (rect) { + Taro.pageScrollTo({ + //@ts-ignore + scrollTop: rect.top, + duration: 300, + }); + } + }) + .exec(); + } + }; + // 表单校验 const validateForm = () => { // 校验销售方 @@ -568,6 +598,12 @@ export default hocAuth(function Page(props: CommonComponent) { } }, []); + // 使用Taro的页面滚动监听 + usePageScroll((res) => { + const scrollTop = res.scrollTop; + setShowQuickNav(scrollTop > 300); // 滚动超过300px时显示快速导航 + }); + if (!purchaseOrderVO || !dealerRebateCustomerVOList || !costList) { return; } @@ -575,16 +611,84 @@ export default hocAuth(function Page(props: CommonComponent) { const calculator = new PurchaseOrderCalculator(purchaseOrderVO, true); const personalProfit = calculator.getPersonalProfit(); - const sections = + const sections = ( purchaseOrderVO.orderDealer.shortName === "信誉楼" ? xylSections - : defaultSections; + : defaultSections + ) + .map((sectionKey) => { + const section = fullSections.find( + (section) => section.name === sectionKey, + ); + + if (!section) { + return false; + } + + const orderDealer = purchaseOrderVO.orderDealer; + + if (!orderDealer?.enableCompanyRebate && sectionKey === "taxSubsidy") { + return false; + } + + if (!orderDealer?.enableAccrualTax && sectionKey === "taxProvision") { + return false; + } + + if (!orderDealer?.shareAdjusted && sectionKey === "costDifference") { + return false; + } + + if (!orderDealer?.enableLoss && sectionKey === "productionLoss") { + return false; + } + + // 如果没有返点人这个模块,则不渲染 + if ( + (!dealerRebateCustomerVOList || + dealerRebateCustomerVOList.length === 0) && + sectionKey === "rebateCalc" + ) { + return false; + } + + if ( + (!purchaseOrderVO.orderPackageList || + purchaseOrderVO?.orderPackageList.length === 0) && + sectionKey === "emptyBoxInfo" + ) { + return false; + } + + if ( + sectionKey === "purchaseForm" && + purchaseOrderVO.orderDealer.shortName !== "信誉楼" + ) { + return false; + } + + if ( + sectionKey === "deliveryForm" && + purchaseOrderVO.orderDealer.shortName === "信誉楼" + ) { + return false; + } + + return section; + }) + .filter(Boolean); return ( <> { + // 点击页面其他区域时收起导航菜单 + if (quickNavExpanded) { + setQuickNavExpanded(false); + } + }} > {/* 顶部导航 */} @@ -638,75 +742,13 @@ export default hocAuth(function Page(props: CommonComponent) { {/* 循环渲染各部分内容 */} - {sections.map((sectionKey) => { - const section = fullSections.find( - (section) => section.name === sectionKey, - ); - - if (!section) { - return null; - } - - const orderDealer = purchaseOrderVO.orderDealer; - - if ( - !orderDealer?.enableCompanyRebate && - sectionKey === "taxSubsidy" - ) { - return null; - } - - if ( - !orderDealer?.enableAccrualTax && - sectionKey === "taxProvision" - ) { - return null; - } - - if ( - !orderDealer?.shareAdjusted && - sectionKey === "costDifference" - ) { - return null; - } - - if (!orderDealer?.enableLoss && sectionKey === "productionLoss") { - return null; - } - - // 如果没有返点人这个模块,则不渲染 - if ( - (!dealerRebateCustomerVOList || - dealerRebateCustomerVOList.length === 0) && - sectionKey === "rebateCalc" - ) { - return null; - } - - if ( - (!purchaseOrderVO.orderPackageList || - purchaseOrderVO?.orderPackageList.length === 0) && - sectionKey === "emptyBoxInfo" - ) { - return null; - } - - if ( - sectionKey === "purchaseForm" && - purchaseOrderVO.orderDealer.shortName !== "信誉楼" - ) { - return null; - } - - if ( - sectionKey === "deliveryForm" && - purchaseOrderVO.orderDealer.shortName === "信誉楼" - ) { - return null; - } - + {sections.map((section: any) => { return ( - + {section.title} + + {/* 快速导航目录 */} + {showQuickNav && ( + + {/* 导航触发按钮 */} + setQuickNavExpanded(!quickNavExpanded)} + > + + + + {/* 展开的导航菜单 */} + {quickNavExpanded && ( + e.stopPropagation()} // 防止事件冒泡 + > + + + 快速导航 + + + + {sections.map((section: any) => ( + { + scrollToSection(section.name); + setQuickNavExpanded(false); // 跳转后自动收起 + }} + > + {section.title} + + ))} + + {/* 收起按钮 */} + setQuickNavExpanded(false)} + > + 收起导航 + + + )} + + )} ); }); diff --git a/packages/app-client/src/utils/classes/calculators/OrderSupplierCalculator.ts b/packages/app-client/src/utils/classes/calculators/OrderSupplierCalculator.ts index 5228146..4fca00f 100644 --- a/packages/app-client/src/utils/classes/calculators/OrderSupplierCalculator.ts +++ b/packages/app-client/src/utils/classes/calculators/OrderSupplierCalculator.ts @@ -22,10 +22,9 @@ export class OrderSupplierCalculator { * 初始化计算规则 */ private init() { - // this.purchaseOrderVO.orderDealer Decimal.set({ precision: 20, - rounding: 0, // 0 = ROUND_DOWN + rounding: Decimal.ROUND_HALF_UP, // 使用常量更清晰 toExpNeg: -7, toExpPos: 21, }); @@ -65,7 +64,9 @@ export class OrderSupplierCalculator { * 合计金额 */ getTotalAmount(): number { - if (this.orderSupplier.orderPackageList?.some((pkg) => pkg.boxType === "USED")) { + if ( + this.orderSupplier.orderPackageList?.some((pkg) => pkg.boxType === "USED") + ) { return new Decimal(this.getNetWeight()) .mul(this.getPurchasePrice()) .toNumber(); @@ -85,4 +86,25 @@ export class OrderSupplierCalculator { } return new Decimal(this.orderSupplier.depositAmount || 0).toNumber(); } + + /** + * 计算纸箱的总重量(斤) + * @param {string} boxType - 箱子类型 ('USED' 或 'EXTRA',不传则计算所有) + * @returns {number} 总重量(斤) + */ + calculateBoxesTotalWeight(boxType?: any): number { + return ( + this.orderSupplier.orderPackageList + ?.filter((pkg) => pkg.boxType === boxType) + .reduce((sum, pkg) => { + // 纸箱重量单位是斤,直接使用 + const boxWeight = pkg.boxProductWeight || 0; + return new Decimal(sum) + .add( + new Decimal(pkg.boxCount || 0).mul(boxWeight).toDecimalPlaces(0), + ) + .toNumber(); + }, 0) || 0 + ); + } } diff --git a/packages/app-client/src/utils/classes/calculators/PurchaseOrderCalculator.ts b/packages/app-client/src/utils/classes/calculators/PurchaseOrderCalculator.ts index 99f1c33..2afe96a 100644 --- a/packages/app-client/src/utils/classes/calculators/PurchaseOrderCalculator.ts +++ b/packages/app-client/src/utils/classes/calculators/PurchaseOrderCalculator.ts @@ -18,30 +18,29 @@ export class PurchaseOrderCalculator { console.table([ { - 成本项总和: this.getTotalCostItemAmount(), - 辅料费: this.getCostAmount("MATERIAL_TYPE"), + 成本项总和: this.getTotalCostAmount(), 人工费: this.getCostAmount("ARTIFICIAL_TYPE"), + 辅料费: this.getCostAmount("MATERIAL_TYPE"), 产地垫付: this.getCostAmount("PRODUCTION_TYPE"), - 纸箱费: - this.getCostAmount("MATERIAL_TYPE", "纸箱费") || this.getBoxSale(), + 空箱费: this.getCostAmount("MATERIAL_TYPE", "空箱费"), + 纸箱费: this.getCostAmount("MATERIAL_TYPE", "纸箱费"), 计提费: this.getCostAmount("OTHER_TYPE", "计提费"), 收代办费: this.getCostAmount("OTHER_TYPE", "收代办费"), 王超费用: this.getCostAmount("OTHER_TYPE", "王超费用"), - 其他费用: this.getCostAmount("OTHER_TYPE"), - 草帘费: this.getStrawCurtainCost(), + 草帘费: this.getCostAmount("OTHER_TYPE", "草帘费"), }, ]); console.table([ { - 西瓜采购成本: this.getSupplierPurchaseCost(), - 成本项总和: this.getTotalCostItemAmount(), + 西瓜采购成本: this.getMelonPurchaseCost(), + 西瓜成本1: this.getMelonCost1(), + 西瓜成本2: this.getMelonCost2(), + 成本项总和: this.getTotalCostAmount(), 税费补贴: this.getTaxSubsidy(), 计提税金: this.getTaxProvision(), 成本差异: this.getCostDifference(), - "采购成本(不包含运费)": this.getTotalPurchaseCost(), 运费: this.getDeliveryFee(), - "采购成本(含运费)": this.getMelonCost1(), - 市场报价: this.getSalesAmount(), + 销售金额: this.getSalesAmount(), 平均单价: this.getAverageSalesPrice(), }, ]); @@ -62,7 +61,7 @@ export class PurchaseOrderCalculator { private init() { Decimal.set({ precision: 20, - rounding: 0, // 0 = ROUND_DOWN + rounding: Decimal.ROUND_HALF_UP, // 使用常量更清晰 toExpNeg: -7, toExpPos: 21, }); @@ -79,39 +78,25 @@ export class PurchaseOrderCalculator { ?.filter((item) => item.type === type && (!name || item.name === name)) .reduce((sum, cost) => { return new Decimal(sum) - .plus(new Decimal(cost.price || 0).mul(cost.count || 0)) + .plus(new Decimal(cost.price || 0).mul(cost.count || 0).toDecimalPlaces(0)) .toNumber(); }, 0); } /** - * 计算成本项 = 辅料费 + 人工费 + 纸箱费 + 计提费 + 收代办费 + 王超费用 + 手动添加的其他费用 + 草帘费(作为我方成本) + 发货之后其他业务流程产生的成本费用 + * 计算成本项 = 辅料费 + 人工费 + 纸箱费 + 运费(作为我方成本) + 计提费 + 收代办费 + 王超费用 + 手动添加的其他费用 + 草帘费(作为我方成本) + 发货之后其他业务流程产生的成本费用 */ - getTotalCostItemAmount(): number { - const costItemsCost = this.purchaseOrderVO.orderCostList?.reduce( - (sum, cost) => { - // 先过滤一下 - if (cost.name === "纸箱费") { - return new Decimal(sum).toNumber(); - } - return new Decimal(sum) - .plus(new Decimal(cost.price || 0).mul(cost.count || 0)) - .toNumber(); - }, - 0, - ); - - const boxCost = this.getBoxSale(); - - // 计算草帘费 - const strawCurtainCost = this.purchaseOrderVO.orderDealer?.strawMatCostFlag - ? this.getStrawCurtainCost() - : 0; - - return new Decimal(costItemsCost) - .plus(boxCost) - .plus(strawCurtainCost) - .toNumber(); + getTotalCostAmount(): number { + return this.purchaseOrderVO.orderCostList?.reduce((sum, cost) => { + if (cost.name === "运费" && !this.purchaseOrderVO.orderDealer?.freightCostFlag) { + return new Decimal(sum).toNumber(); + } + return new Decimal(sum) + .plus( + new Decimal(cost.price || 0).mul(cost.count || 0).toDecimalPlaces(0), + ) + .toNumber(); + }, 0); } /** @@ -119,17 +104,6 @@ export class PurchaseOrderCalculator { */ getSupplierPurchaseCost(): number { return this.purchaseOrderVO.orderSupplierList.reduce((sum, supplier) => { - const ownBoxWeight = - supplier.orderPackageList - ?.filter((pkg) => pkg.boxType === "USED") - ?.reduce((sum, pkg) => { - return new Decimal(sum) - .plus( - new Decimal(pkg.boxCount || 0).mul(pkg.boxProductWeight || 0), - ) - .toNumber(); - }, 0) || 0; - return new Decimal(sum) .plus( new Decimal( @@ -137,8 +111,8 @@ export class PurchaseOrderCalculator { ? supplier.grossWeight : supplier.netWeight, ) - .plus(ownBoxWeight) - .mul(supplier.purchasePrice || 0), + .mul(supplier.purchasePrice || 0) + .toDecimalPlaces(0), ) .toNumber(); }, 0); @@ -150,7 +124,7 @@ export class PurchaseOrderCalculator { */ getTotalPurchaseCost(): number { return new Decimal(this.getSupplierPurchaseCost()) - .plus(this.getTotalCostItemAmount()) + .plus(this.getTotalCostAmount()) .plus(this.getTaxSubsidy()) .plus(this.getTaxProvision()) .plus(this.getCostDifference()) @@ -158,17 +132,29 @@ export class PurchaseOrderCalculator { } /** - * 西瓜成本1 = 采购成本(不包含运费) + 运费 + * 西瓜采购成本 + */ + getMelonPurchaseCost(): number { + return this.getSupplierPurchaseCost(); + } + + /** + * 西瓜成本1 = 西瓜采购成本 + 成本项总和 */ getMelonCost1(): number { - const totalPurchaseCost = this.getTotalPurchaseCost(); + const melonPurchaseCost = this.getMelonPurchaseCost(); + const totalCostAmount = this.getTotalCostAmount(); - // 计算运费 - const deliveryFee = this.purchaseOrderVO.orderDealer?.freightCostFlag - ? this.getDeliveryFee() - : 0; + return new Decimal(melonPurchaseCost).plus(totalCostAmount).toNumber(); + } - return new Decimal(totalPurchaseCost).plus(deliveryFee).toNumber(); + /** + * 获取西瓜成本2 = 西瓜成本1 + 成本差异(调诚信志远分成) + */ + getMelonCost2(): number { + return new Decimal(this.getMelonCost1()) + .plus(this.getCostDifference()) + .toNumber(); } /** @@ -230,7 +216,11 @@ export class PurchaseOrderCalculator { ?.filter((pkg) => pkg.boxType === "USED") .reduce((sum, pkg) => { return new Decimal(sum) - .plus(new Decimal(pkg.boxCount || 0).mul(pkg.boxSalePrice || 0)) + .plus( + new Decimal(pkg.boxCount || 0) + .mul(pkg.boxSalePrice || 0) + .toDecimalPlaces(0), + ) .toNumber(); }, 0) || 0, ) @@ -320,22 +310,12 @@ export class PurchaseOrderCalculator { * 西瓜毛利 = 市场报价 - 西瓜成本1 */ getMelonGrossProfit(): number { - // 计算各种金额 const salesAmount = this.getMarketPrice(); // 市场报价 const melonCost1 = this.getMelonCost1(); // 西瓜成本1 return new Decimal(salesAmount).minus(melonCost1).toNumber(); // 西瓜毛利 } - /** - * 获取西瓜成本2 = 西瓜成本1 + 成本差异(调诚信志远分成) - */ - getMelonCost2(): number { - return new Decimal(this.getMelonCost1()) - .plus(this.getCostDifference()) - .toNumber(); - } - /** * 成本差异(调诚信志远分成) */ @@ -376,13 +356,6 @@ export class PurchaseOrderCalculator { return 0; } - /** - * 获取市场报价 - */ - getMarketPrice(): number { - return new Decimal(this.getSalesAmount()).toNumber(); - } - /** * 平均采购价单价(老板看的收购单价) */ @@ -412,14 +385,14 @@ export class PurchaseOrderCalculator { /** * 计算市场报价(销售金额 + 成本项) */ - getTotalAmount(): number { + getMarketPrice(): number { const decimal = new Decimal(this.getSalesAmount()); const includePackingFlag = this.purchaseOrderVO.orderDealer?.includePackingFlag; if (includePackingFlag) { - return decimal.plus(this.getTotalCostItemAmount()).toNumber(); + return decimal.plus(this.getTotalCostAmount()).toNumber(); } return decimal.toNumber(); @@ -439,7 +412,10 @@ export class PurchaseOrderCalculator { ? supplier.grossWeight : supplier.netWeight; - return new Decimal(weight || 0).mul(salePrice || 0).toNumber(); + return new Decimal(weight || 0) + .mul(salePrice || 0) + .toDecimalPlaces(0) + .toNumber(); } /** @@ -447,7 +423,7 @@ export class PurchaseOrderCalculator { */ getDefaultTaxProvision(): number { if (this.purchaseOrderVO.orderDealer?.enableAccrualTax) { - const totalAmount = this.getTotalAmount(); + const totalAmount = this.getMarketPrice(); const taxSubsidyValue = this.getTaxSubsidy(); return new Decimal(totalAmount) @@ -465,7 +441,7 @@ export class PurchaseOrderCalculator { */ getDefaultTaxSubsidy(): number { if (this.purchaseOrderVO.orderDealer?.enableCompanyRebate) { - const totalPackagingCost = this.getTotalCostItemAmount(); + const totalPackagingCost = this.getTotalCostAmount(); const salesAmount1 = this.getSalesAmount(); return new Decimal(salesAmount1) @@ -487,7 +463,10 @@ export class PurchaseOrderCalculator { supplier.orderPackageList ?.filter((pkg) => pkg.boxType === "USED") ?.reduce((sum, pkg) => { - return new Decimal(sum).plus(pkg.boxCount || 0).toNumber(); + return new Decimal(sum) + .plus(pkg.boxCount || 0) + .toDecimalPlaces(0) + .toNumber(); }, 0) || 0, ) .toNumber(); @@ -506,7 +485,9 @@ export class PurchaseOrderCalculator { ?.reduce((sum, pkg) => { return new Decimal(sum) .plus( - new Decimal(pkg.boxProductWeight || 0).mul(pkg.boxCount || 0), + new Decimal(pkg.boxProductWeight || 0) + .mul(pkg.boxCount || 0) + .toDecimalPlaces(0), ) .toNumber(); }, 0) || 0, diff --git a/packages/app-client/src/utils/classes/calculators/SupplierWeightCalculator.ts b/packages/app-client/src/utils/classes/calculators/SupplierWeightCalculator.ts index ae3f566..650f533 100644 --- a/packages/app-client/src/utils/classes/calculators/SupplierWeightCalculator.ts +++ b/packages/app-client/src/utils/classes/calculators/SupplierWeightCalculator.ts @@ -18,7 +18,7 @@ export class SupplierWeightCalculator { private init() { Decimal.set({ precision: 20, - rounding: 0, // 向下舍入(更保守的计算方式) + rounding: Decimal.ROUND_HALF_UP, // 使用常量更清晰 toExpNeg: -7, toExpPos: 21, }); @@ -29,7 +29,7 @@ export class SupplierWeightCalculator { * @returns {Array} 包含净重、毛重和空磅的农户数据 */ calculate(): BusinessAPI.OrderSupplier[] { - console.log("开始计算采购订单的农户重量信息..."); + console.log("开始计算采购订单的农户重量信息...", this.suppliers); if (!this.suppliers || this.suppliers.length === 0) { return this.suppliers; } @@ -51,16 +51,7 @@ export class SupplierWeightCalculator { } // 计算本次使用纸箱的总重量(斤) - const usedBoxesWeight = new Decimal( - this.calculateBoxesTotalWeight(supplier.orderPackageList || [], "USED"), - ) - .toNumber(); - - // 计算额外配送的已使用纸箱总重量(斤) - const extraUsedBoxesWeight = this.calculateBoxesTotalWeight( - supplier.orderPackageList || [], - "EXTRA_USED", - ); + const usedBoxesWeight = this.calculateBoxesTotalWeight(supplier.orderPackageList || [], "USED") if (!supplier.isPaper) { // 如果不是纸箱包装,直接使用原始重量(kg转斤) @@ -71,12 +62,12 @@ export class SupplierWeightCalculator { supplier.netWeight = new Decimal(supplier.grossWeight || 0) .sub(usedBoxesWeight) - .sub(extraUsedBoxesWeight) .toNumber(); previousTotalWeight = supplier.totalWeight; supplier.invoiceAmount = new Decimal(supplier.netWeight || 0) .mul(supplier.purchasePrice || 0) + .toDecimalPlaces(0) .toNumber(); continue; } @@ -93,15 +84,15 @@ export class SupplierWeightCalculator { "REMAIN", ); + // 计算额外配送的已使用纸箱总重量(斤) + const extraUsedBoxesWeight = this.calculateBoxesTotalWeight( + supplier.orderPackageList || [], + "EXTRA_USED", + ); + if (isFirstSupplier && isLastSupplier) { // 既是第一个也是最后一个瓜农(单个瓜农情况)- 优先使用最后一个瓜农算法 // 净重 = (总磅 - 空磅) * 2 + 剩余空箱子重量 - 已使用额外纸箱重量 - - console.log("总磅", supplier.totalWeight); - console.log("空磅", initialEmptyWeight); - console.log("剩余空箱子重量", remainingBoxesWeight); - console.log("已使用额外纸箱重量", extraUsedBoxesWeight); - supplier.netWeight = new Decimal(supplier.totalWeight || 0) .sub(initialEmptyWeight) .mul(2) @@ -183,7 +174,7 @@ export class SupplierWeightCalculator { // 纸箱重量单位是斤,直接使用 const boxWeight = pkg.boxProductWeight || 0; return new Decimal(sum) - .add(new Decimal(pkg.boxCount || 0).mul(boxWeight)) + .add(new Decimal(pkg.boxCount || 0).mul(boxWeight).toDecimalPlaces(0)) .toNumber(); }, 0); }