feat(purchase): 优化采购订单计算逻辑和界面展示
- 引入decimal.js提升金额计算精度 - 重构成本计算方法,明确区分各类费用构成 - 优化采购预览界面,增加计算明细展示 - 改进开票信息展示样式和计算公式说明 - 完善纸箱重量和销售金额的精确计算 - 调整界面布局,提升用户体验和信息可读性 - 修复成本项过滤逻辑,确保数据准确性 - 新增快速导航功能,便于页面内快速定位 - 更新图标资源,支持计算器和指南针图标 - 优化数字格式化处理,统一保留合适的小数位数
This commit is contained in:
parent
a79fc0ef9f
commit
3d217b1122
@ -3,6 +3,8 @@ import classNames from "classnames";
|
||||
import React from "react";
|
||||
|
||||
export type IconNames =
|
||||
| "calculator"
|
||||
| "compass"
|
||||
| "eye"
|
||||
| "eye-slash"
|
||||
| "phone-flip"
|
||||
|
||||
@ -156,7 +156,6 @@ export default function CostList(props: {
|
||||
)}
|
||||
|
||||
{orderCosts.map((orderCost) => {
|
||||
if (type === "MATERIAL_TYPE") {
|
||||
if (
|
||||
orderCost.name === "空箱费" ||
|
||||
orderCost.name === "纸箱费" ||
|
||||
@ -165,7 +164,6 @@ export default function CostList(props: {
|
||||
) {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<CostCard
|
||||
costList={defaultCostList}
|
||||
|
||||
@ -6,6 +6,8 @@ import {
|
||||
OrderSupplierCalculator,
|
||||
PurchaseOrderCalculator,
|
||||
} from "@/utils";
|
||||
import { Icon } from "@/components";
|
||||
import { Decimal } from "decimal.js";
|
||||
|
||||
interface IPurchasePreviewProps {
|
||||
purchaseOrder: BusinessAPI.PurchaseOrderVO;
|
||||
@ -61,9 +63,10 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
|
||||
title: "重量(斤)",
|
||||
key: "boxProductWeight",
|
||||
render: (record: any) => {
|
||||
return (
|
||||
<View>{(record.boxProductWeight * record.boxCount).toFixed(2)}</View>
|
||||
);
|
||||
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 (
|
||||
<View
|
||||
className="rounded-lg bg-white p-2.5 shadow-sm"
|
||||
className="flex flex-col gap-2.5 rounded-lg bg-white p-2.5 shadow-sm"
|
||||
key={supplier.orderSupplierId}
|
||||
>
|
||||
<View className="mb-2 flex items-center justify-between">
|
||||
<View className="flex items-center justify-between">
|
||||
<View
|
||||
className="text-lg font-semibold"
|
||||
style="font-size: 18px;"
|
||||
@ -162,52 +165,73 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
|
||||
{supplier.isLast ? "最后一个" : "不是最后一个"}
|
||||
</View>
|
||||
</View>
|
||||
<View className="mb-3 flex flex-col gap-2 border-b pb-3">
|
||||
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">空磅</View>
|
||||
<View className="text-sm font-medium">
|
||||
{formatCurrency(supplier.emptyWeight)} KG
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">总磅</View>
|
||||
<View className="text-sm font-medium">
|
||||
{formatCurrency(supplier.totalWeight || 0)} KG
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">毛重</View>
|
||||
<View className="text-sm font-medium">
|
||||
{formatCurrency(calculator.getGrossWeight())} 斤
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">净重</View>
|
||||
<View className="text-primary text-sm font-medium">
|
||||
{formatCurrency(calculator.getNetWeight())} 斤
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">箱重</View>
|
||||
<View className="text-sm font-medium">
|
||||
{formatCurrency(calculator.getBoxWeight())} 斤
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">采购单价</View>
|
||||
<View className="text-primary text-sm font-medium">
|
||||
{formatCurrency(calculator.getPurchasePrice())} 元/斤
|
||||
</View>
|
||||
</View>
|
||||
<View className="mt-2 flex items-center justify-between">
|
||||
<View className="text-sm text-gray-600">合计金额</View>
|
||||
<View className="text-primary text-sm font-medium">
|
||||
{formatCurrency(calculator.getTotalAmount())} 元{" "}
|
||||
{supplier.invoiceAmount} 元{" "}
|
||||
{supplier.isDepositPaid
|
||||
? `(含定金:${formatCurrency(calculator.getDepositPaidAmount())}元)`
|
||||
? `(含定金:${calculator.getDepositPaidAmount()}元)`
|
||||
: ""}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 计算公式区域 */}
|
||||
<View className="rounded-lg border border-gray-200 bg-gray-50 p-3">
|
||||
<View className="mb-2 flex items-center">
|
||||
<Icon
|
||||
name="calculator"
|
||||
className="mr-2 leading-4"
|
||||
size={20}
|
||||
/>
|
||||
<View className="text-sm font-medium text-gray-700">
|
||||
计算明细
|
||||
</View>
|
||||
</View>
|
||||
<View className="space-y-1">
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">总重量(kg)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplier.totalWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">空重量(kg)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplier.emptyWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">箱重量(斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{calculator.calculateBoxesTotalWeight("USED") || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">净重量(斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplier.netWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">采购单价(元/斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplier.purchasePrice || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">报价方式</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplier.pricingMethod === "BY_GROSS_WEIGHT"
|
||||
? "毛重"
|
||||
: "净重"}
|
||||
</View>
|
||||
</View>
|
||||
<View className="my-2 h-px bg-gray-300"></View>
|
||||
<View className="text-center text-xs text-gray-500">
|
||||
计算公式:净重量 × 采购单价
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 根据boxBrandName对packageInfoList进行分组显示 */}
|
||||
@ -221,7 +245,7 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
|
||||
|
||||
return Object.entries(groupedPackageInfo).map(
|
||||
([brandName, packageInfos]) => (
|
||||
<View key={brandName} className="mb-2.5">
|
||||
<View key={brandName} className={"flex flex-col gap-2.5"}>
|
||||
<View className="text-primary text-base font-bold">
|
||||
纸箱品牌:{brandName}
|
||||
</View>
|
||||
@ -238,11 +262,11 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
|
||||
<View className="rounded-lg bg-white p-2.5 shadow-md">
|
||||
<View className="flex items-center justify-between text-sm font-bold">
|
||||
<View>总箱数</View>
|
||||
<View>{formatCurrency(calculator.getBoxCount())} 箱</View>
|
||||
<View>{calculator.getBoxCount()} 箱</View>
|
||||
</View>
|
||||
<View className="mt-2 flex items-center justify-between text-sm font-bold">
|
||||
<View>总箱重</View>
|
||||
<View>{formatCurrency(calculator.getBoxWeight())} 斤</View>
|
||||
<View>{calculator.getBoxWeight()} 斤</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@ -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 (
|
||||
<View className="flex flex-1 flex-col bg-[#D1D5DB] px-2.5 pt-2.5">
|
||||
<View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5">
|
||||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||||
<View className="flex flex-col gap-2.5">
|
||||
<View className="text-primary text-base font-bold">
|
||||
{supplierVO.name}的开票信息
|
||||
{supplierVO.name}的应开票信息
|
||||
</View>
|
||||
<View className="bg-primary/10 flex items-center justify-between rounded-lg p-2.5">
|
||||
<View
|
||||
className={"flex flex-1 flex-row items-center justify-between"}
|
||||
>
|
||||
<View className="block text-sm font-normal text-[#000000]">
|
||||
应开票金额
|
||||
|
||||
{/* 应开票金额突出显示 */}
|
||||
<View className="rounded-xl bg-gradient-to-r from-green-500 to-green-600 p-4 text-white shadow-md">
|
||||
<View className="text-center">
|
||||
<View className="mb-1 text-sm font-medium opacity-90">
|
||||
应开票金额(元)
|
||||
</View>
|
||||
<View className="text-primary mt-1 text-base font-semibold">
|
||||
<Price
|
||||
price={supplierVO.invoiceAmount || 0}
|
||||
thousands
|
||||
size={"xlarge"}
|
||||
/>
|
||||
<View className="text-4xl font-bold">
|
||||
{supplierVO.invoiceAmount || 0}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="block text-sm font-normal text-[#000000]">
|
||||
上传发票(可跳过)
|
||||
{/* 计算公式区域 */}
|
||||
<View className="rounded-lg border border-gray-200 bg-gray-50 p-3">
|
||||
<View className="mb-2 flex items-center">
|
||||
<Icon name="calculator" className="mr-2 leading-4" size={20} />
|
||||
<View className="text-sm font-medium text-gray-700">
|
||||
计算明细
|
||||
</View>
|
||||
</View>
|
||||
<View className="space-y-1">
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">总重量(kg)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplierVO.totalWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">空重量(kg)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplierVO.emptyWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">箱重量(斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{calculator.calculateBoxesTotalWeight("USED") || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">净重量(斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplierVO.netWeight || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">采购单价(元/斤)</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplierVO.purchasePrice || 0}
|
||||
</View>
|
||||
</View>
|
||||
<View className="flex justify-between text-sm">
|
||||
<View className="text-gray-600">报价方式</View>
|
||||
<View className="font-medium text-gray-800">
|
||||
{supplierVO.pricingMethod === "BY_GROSS_WEIGHT"
|
||||
? "毛重"
|
||||
: "净重"}
|
||||
</View>
|
||||
</View>
|
||||
<View className="my-2 h-px bg-gray-300"></View>
|
||||
<View className="text-center text-xs text-gray-500">
|
||||
计算公式:净重量 × 采购单价
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||||
<View className="flex flex-col gap-2.5">
|
||||
<View className="text-primary text-base font-bold">
|
||||
上传{supplierVO.name}的发票(可跳过)
|
||||
</View>
|
||||
|
||||
{supplierVO.invoiceUpload ? (
|
||||
@ -213,9 +271,13 @@ export default function TicketUpload(props: ITicketUploadProps) {
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="block text-sm font-normal text-[#000000]">
|
||||
上传合同(可跳过)
|
||||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||||
<View className="flex flex-col gap-2.5">
|
||||
<View className="text-primary text-base font-bold">
|
||||
上传{supplierVO.name}的合同(可跳过)
|
||||
</View>
|
||||
{supplierVO.contractUpload &&
|
||||
supplierVO.contractImg &&
|
||||
|
||||
@ -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: {
|
||||
</span>
|
||||
);
|
||||
}
|
||||
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") {
|
||||
|
||||
@ -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: {
|
||||
<View className="flex items-center justify-between">
|
||||
<Text className="text-sm text-gray-500">毛重</Text>
|
||||
<Text className="text-sm font-medium">
|
||||
{(supplier.grossWeight || 0).toFixed(2)} 斤
|
||||
{supplier.grossWeight || 0} 斤
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<Text className="text-sm text-gray-500">净重</Text>
|
||||
<Text className="text-sm font-medium">
|
||||
{(supplier.netWeight || 0).toFixed(2)} 斤
|
||||
{supplier.netWeight || 0} 斤
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<Text className="text-sm text-gray-500">箱重</Text>
|
||||
<Text className="text-sm font-medium">
|
||||
{(supplier.grossWeight - supplier.netWeight).toFixed(2)}{" "}
|
||||
斤
|
||||
{supplier.grossWeight - supplier.netWeight} 斤
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<Text className="text-sm text-gray-500">采购单价</Text>
|
||||
<Text className="text-sm font-medium">
|
||||
{supplier.purchasePrice.toFixed(2)} 元/斤
|
||||
{supplier.purchasePrice} 元/斤
|
||||
</Text>
|
||||
</View>
|
||||
{supplier.isDepositPaid && (
|
||||
|
||||
@ -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: {
|
||||
</span>
|
||||
);
|
||||
}
|
||||
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") {
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
item.price * item.count > 0 && (
|
||||
<View
|
||||
className="cost-item flex flex-col px-3 py-2"
|
||||
key={item.itemId}
|
||||
key={item.costId}
|
||||
>
|
||||
<View className="text-sm text-gray-500">{item.name}</View>
|
||||
<View className="font-medium">
|
||||
|
||||
@ -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<BusinessAPI.DealerRebateCustomerVO[]>();
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<View
|
||||
className={"overflow-x-hidden overflow-y-auto bg-[#D1D5DB]"}
|
||||
id={"purchase-order-audit"}
|
||||
onClick={() => {
|
||||
// 点击页面其他区域时收起导航菜单
|
||||
if (quickNavExpanded) {
|
||||
setQuickNavExpanded(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View className={"flex flex-col gap-2.5 p-2.5"}>
|
||||
{/* 顶部导航 */}
|
||||
@ -638,75 +742,13 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
</View>
|
||||
|
||||
{/* 循环渲染各部分内容 */}
|
||||
{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 (
|
||||
<View key={sectionKey} className={"flex flex-col gap-2.5"}>
|
||||
<View
|
||||
key={section.name}
|
||||
id={`section-${section.name}`}
|
||||
className={"flex flex-col gap-2.5"}
|
||||
>
|
||||
<View className="text-sm font-bold">{section.title}</View>
|
||||
<View
|
||||
className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`}
|
||||
@ -914,6 +956,66 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
|
||||
<SafeArea position="bottom" />
|
||||
</Popup>
|
||||
|
||||
{/* 快速导航目录 */}
|
||||
{showQuickNav && (
|
||||
<View
|
||||
className={`fixed left-4 z-40 bg-white shadow-lg transition-all duration-300 ${
|
||||
quickNavExpanded ? "top-4 w-48" : "top-4 h-10 w-10"
|
||||
} rounded-lg`}
|
||||
style={{
|
||||
opacity: showQuickNav ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
{/* 导航触发按钮 */}
|
||||
<View
|
||||
className="relative flex h-10 w-10 cursor-pointer items-center justify-center"
|
||||
onClick={() => setQuickNavExpanded(!quickNavExpanded)}
|
||||
>
|
||||
<Icon
|
||||
name="compass"
|
||||
className={`text-blue-600 transition-transform duration-300 ${
|
||||
quickNavExpanded ? "rotate-180" : ""
|
||||
}`}
|
||||
></Icon>
|
||||
</View>
|
||||
|
||||
{/* 展开的导航菜单 */}
|
||||
{quickNavExpanded && (
|
||||
<View
|
||||
className="absolute top-0 left-10 w-48 rounded-lg border border-gray-200 bg-white shadow-lg"
|
||||
onClick={(e) => e.stopPropagation()} // 防止事件冒泡
|
||||
>
|
||||
<View className="border-b border-gray-200 px-3 py-2">
|
||||
<View className="text-xs font-medium text-gray-700">
|
||||
快速导航
|
||||
</View>
|
||||
</View>
|
||||
<View className="max-h-96 overflow-y-auto">
|
||||
{sections.map((section: any) => (
|
||||
<View
|
||||
key={section.name}
|
||||
className="cursor-pointer border-b border-gray-100 px-3 py-2 text-sm text-gray-600 transition-colors last:border-b-0 hover:bg-blue-50 hover:text-blue-600"
|
||||
onClick={() => {
|
||||
scrollToSection(section.name);
|
||||
setQuickNavExpanded(false); // 跳转后自动收起
|
||||
}}
|
||||
>
|
||||
{section.title}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
{/* 收起按钮 */}
|
||||
<View
|
||||
className="cursor-pointer border-t border-gray-200 px-3 py-2 text-xs text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => setQuickNavExpanded(false)}
|
||||
>
|
||||
收起导航
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 === "纸箱费") {
|
||||
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))
|
||||
.toNumber();
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
const boxCost = this.getBoxSale();
|
||||
|
||||
// 计算草帘费
|
||||
const strawCurtainCost = this.purchaseOrderVO.orderDealer?.strawMatCostFlag
|
||||
? this.getStrawCurtainCost()
|
||||
: 0;
|
||||
|
||||
return new Decimal(costItemsCost)
|
||||
.plus(boxCost)
|
||||
.plus(strawCurtainCost)
|
||||
.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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user