diff --git a/src/pages/fulfillment/OrderFulfillmentCreate.vue b/src/pages/fulfillment/OrderFulfillmentCreate.vue index 95b8428..5203460 100644 --- a/src/pages/fulfillment/OrderFulfillmentCreate.vue +++ b/src/pages/fulfillment/OrderFulfillmentCreate.vue @@ -191,7 +191,7 @@ async function loadFulfillmentSkuList(): Promise { continue; } const originalUnit = getSellableUnitList(item).find(unit => unit.id === item.skuUnitId); - const defaultUnit = originalUnit || getSellableUnitList(item)[0]; + const defaultUnit = getFulfillmentUnitForAvailableQuantity(item, originalUnit || getSellableUnitList(item)[0]); if (defaultUnit?.id) { fulfillmentUnitMap[item.id] = defaultUnit.id; } @@ -217,7 +217,26 @@ function getItemDescription(item: GaoSalesOrderItem): string { } function getBaseUnitName(item: GaoSalesOrderItem): string { - return item.skuId ? fulfillmentSkuMap[item.skuId]?.unitList?.find(unit => unit.isBase)?.name || "基础单位" : "基础单位"; + return getBaseUnit(item)?.name || "基础单位"; +} + +function getBaseUnit(item: GaoSalesOrderItem): ProductSkuUnit | undefined { + return getSellableUnitList(item).find(unit => unit.isBase); +} + +function shouldDowngradeUnit(item: GaoSalesOrderItem, unit?: ProductSkuUnit): boolean { + const sku = item.skuId ? fulfillmentSkuMap[item.skuId] : undefined; + if (!unit || unit.isBase || sku?.allowDecimalQuantity !== false) { + return false; + } + return Number(getAvailableBaseQuantity(item)) < Number(unit.baseQuantityPerUnit); +} + +function getFulfillmentUnitForAvailableQuantity(item: GaoSalesOrderItem, unit?: ProductSkuUnit): ProductSkuUnit | undefined { + if (!shouldDowngradeUnit(item, unit)) { + return unit; + } + return getBaseUnit(item) || unit; } function fillAllQuantity(item: GaoSalesOrderItem): void { @@ -242,6 +261,22 @@ function getFulfillmentUnit(item: GaoSalesOrderItem): ProductSkuUnit | undefined return getSellableUnitList(item).find(unit => unit.id === fulfillmentUnitMap[item.id || ""]); } +function setFulfillmentUnit(item: GaoSalesOrderItem, unit: ProductSkuUnit): void { + if (!item.id) { + return; + } + const currentUnit = getFulfillmentUnit(item); + const currentQuantity = quantityMap[item.id]; + if (currentQuantity && currentUnit?.id !== unit.id) { + quantityMap[item.id] = convertPackageQuantity( + currentQuantity, + currentUnit?.baseQuantityPerUnit ?? "1", + unit.baseQuantityPerUnit + ) || ""; + } + fulfillmentUnitMap[item.id] = unit.id!; +} + function openUnitPicker(item: GaoSalesOrderItem): void { if (!item.id) { return; @@ -257,8 +292,11 @@ function openUnitPicker(item: GaoSalesOrderItem): void { function confirmUnitPicker(value: Array): void { const skuUnitId = String(value[0] || ""); - if (unitPickerSourceItemId.value && skuUnitId) { - fulfillmentUnitMap[unitPickerSourceItemId.value] = skuUnitId; + const item = sourceItems.value.find(sourceItem => sourceItem.id === unitPickerSourceItemId.value); + const selectedUnit = item ? getSellableUnitList(item).find(unit => unit.id === skuUnitId) : undefined; + const fulfillmentUnit = item ? getFulfillmentUnitForAvailableQuantity(item, selectedUnit) : selectedUnit; + if (item?.id && fulfillmentUnit?.id) { + setFulfillmentUnit(item, fulfillmentUnit); } unitPickerVisible.value = false; } diff --git a/src/pages/fulfillment/OrderFulfillmentDetail.vue b/src/pages/fulfillment/OrderFulfillmentDetail.vue index f63e8df..da7d6cb 100644 --- a/src/pages/fulfillment/OrderFulfillmentDetail.vue +++ b/src/pages/fulfillment/OrderFulfillmentDetail.vue @@ -31,7 +31,6 @@ v-for="item in fulfillment.itemList || []" :key="item.id" :title="item.productNameSnapshot || '未命名产品'" - :note="`待履约(基础单位)${item.reservedBaseBefore || 0} → ${item.reservedBaseAfter || 0}`" > @@ -133,11 +143,11 @@ import SalesOrderAPI from "@/api/SalesOrderAPI"; import PhotoScroller from "@/components/PhotoScroller.vue"; import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue"; import { useNavBarStore } from "@/store/navBarStore"; -import type { OrderFulfillment } from "@/types/Fulfillment"; +import type { OrderFulfillment, OrderFulfillmentItem } from "@/types/Fulfillment"; import type { GaoSalesOrderItemType, SalesOrder } from "@/types/SalesOrder"; import type { PhotoItem } from "@/types/Photo"; import type { Attachment } from "timi-web-api"; -import { createPackageRequestNo } from "@/utils/package"; +import { createPackageRequestNo, getPackageQuantityText } from "@/utils/package"; import { getAttachmentReadURL } from "@/utils/attachment"; import { getErrorMessage } from "@/utils/error"; import { getProductPhotoURL } from "@/utils/product"; @@ -186,6 +196,10 @@ const sourceItemType = computed(() => { return order.value?.itemList?.find(item => item.id === sourceOrderItemId)?.itemType; }); +function getFulfillmentQuantityText(item: OrderFulfillmentItem): string { + return `履约数量 ${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || "单位"}`; +} + async function loadData(): Promise { const id = String(route.params.id || ""); if (!id) return; @@ -339,4 +353,8 @@ onUnmounted(() => navBarStore.clearRight()); } } } + +.revoke-form { + margin: 0; +} diff --git a/src/pages/product/ProductStockRecordList.vue b/src/pages/product/ProductStockRecordList.vue index 66679ee..8ad3bea 100644 --- a/src/pages/product/ProductStockRecordList.vue +++ b/src/pages/product/ProductStockRecordList.vue @@ -70,7 +70,6 @@ import { type DropdownValue } from "tdesign-mobile-vue"; import { PassTimeLabel } from "timi-web"; import ProductAPI from "@/api/ProductAPI"; -import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue"; import { useNavBarStore } from "@/store/navBarStore"; import type { StockOperationType, StockRecord, StockRecordDirection } from "@/types/Product"; import { getErrorMessage } from "@/utils/error"; diff --git a/src/pages/salesOrder/SalesOrderDetail.vue b/src/pages/salesOrder/SalesOrderDetail.vue index 80106bd..4f1f0c2 100644 --- a/src/pages/salesOrder/SalesOrderDetail.vue +++ b/src/pages/salesOrder/SalesOrderDetail.vue @@ -310,13 +310,31 @@ function getRemainingAmountClass(amount?: number | null): "green" | "red" | "gra return amount === 0 ? "green" : "red"; } +function getItemUnitName(item: GaoSalesOrderItem): string { + return item.unitSnapshot || "单位"; +} + +function getBaseUnitName(item: GaoSalesOrderItem): string { + return item.baseUnitName || "基础单位"; +} + +function shouldDisplayBaseQuantity(item: GaoSalesOrderItem, baseQuantity?: number | string): boolean { + const isBaseUnit = Number(item.baseQuantityPerUnitSnapshot ?? 1) === 1; + return item.allowDecimalQuantity === false + && !isBaseUnit + && baseQuantity !== undefined + && Number(baseQuantity) < Number(item.baseQuantityPerUnitSnapshot ?? 1); +} + function getItemQuantityText(item: GaoSalesOrderItem, baseQuantity?: number | string): string { const quantity = convertPackageQuantity( String(baseQuantity ?? "0"), "1", item.baseQuantityPerUnitSnapshot ?? "1" ); - return getPackageQuantityText(quantity ?? baseQuantity); + const useBaseUnit = shouldDisplayBaseQuantity(item, baseQuantity); + const displayQuantity = useBaseUnit ? getPackageQuantityText(baseQuantity) : getPackageQuantityText(quantity ?? baseQuantity); + return `${displayQuantity} ${useBaseUnit ? getBaseUnitName(item) : getItemUnitName(item)}`; } function getAvailableQuantityClass(item: GaoSalesOrderItem): "green" | "orange" { diff --git a/src/pages/salesOrder/components/SalesOrderListCell.vue b/src/pages/salesOrder/components/SalesOrderListCell.vue index 1bbba28..4e0d0fb 100644 --- a/src/pages/salesOrder/components/SalesOrderListCell.vue +++ b/src/pages/salesOrder/components/SalesOrderListCell.vue @@ -144,8 +144,9 @@ function sumProductQuantity(field: "quantity" | "fulfilledBaseQuantity"): string } function getProductText(item: NonNullable[number]): string { + const quantity = getPackageQuantityText(item.quantity); const skuText = getProductSkuSnapshotText(item.productNameSnapshot, item.skuNameSnapshot, item.specSummarySnapshot); - return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}${item.unitSnapshot ? ` / ${item.unitSnapshot}` : ""}`; + return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""} × ${quantity}${item.unitSnapshot ? ` ${item.unitSnapshot}` : ""}`; } diff --git a/src/types/SalesOrder.ts b/src/types/SalesOrder.ts index d0f6548..c0a0ba3 100644 --- a/src/types/SalesOrder.ts +++ b/src/types/SalesOrder.ts @@ -26,6 +26,8 @@ export type GaoSalesOrderItem = { skuSpecSnapshot?: ProductSkuSpecSnapshot[]; unitSnapshot?: string; baseQuantityPerUnitSnapshot?: number | string; + allowDecimalQuantity?: boolean; + baseUnitName?: string; quantity?: number | string; baseQuantity?: number | string; reservedBaseQuantity?: number | string;