From a87609261b5f11f896a8cfa728f17ebdc7740b53 Mon Sep 17 00:00:00 2001 From: Timi Date: Mon, 14 Sep 2026 00:47:47 +0800 Subject: [PATCH] add SalesOrderStraightThroughRequest paymentAmount --- src/pages/package/PackagePurchase.vue | 58 ++++++++++++++++----- src/pages/package/usePackagePurchase.ts | 24 ++++++--- src/pages/salesOrder/SalesOrderCreate.vue | 62 ++++++++++++++++++----- src/types/SalesOrder.ts | 2 + 4 files changed, 116 insertions(+), 30 deletions(-) diff --git a/src/pages/package/PackagePurchase.vue b/src/pages/package/PackagePurchase.vue index 73b16c5..65f15bc 100644 --- a/src/pages/package/PackagePurchase.vue +++ b/src/pages/package/PackagePurchase.vue @@ -170,6 +170,24 @@ /> + + +
全部履约会同步启用全额收款;请先准备全部产品库存,任意一步失败,整张订单都会回滚。
- + ([]); const paymentPhotoItems = ref([]); const fulfillmentPhotoItems = ref([]); +const paymentAmountText = ref("0"); const photoUrlList = computed(() => photoItems.value.map(item => item.url)); const paymentPhotoUrlList = computed(() => paymentPhotoItems.value.map(item => item.url)); const fulfillmentPhotoUrlList = computed(() => fulfillmentPhotoItems.value.map(item => item.url)); +const receiveFullPayment = ref(false); +const fulfillAll = ref(false); +const parsedPaymentAmount = computed(() => parsePackageAmount(paymentAmountText.value)); +const paymentVerified = computed(() => !receiveFullPayment.value + && 0 < paymentAmountText.value.trim().length + && parsedPaymentAmount.value !== 0); function buildAttachments(source: PhotoItem[] = photoItems.value): PackageAttachment[] { return source.flatMap(item => item.tempFileId ? [{ @@ -497,9 +522,6 @@ function buildAttachments(source: PhotoItem[] = photoItems.value): PackageAttach }] : []); } -const receiveFullPayment = ref(false); -const fulfillAll = ref(false); - const { isSaleable, canPriceOverride, @@ -514,6 +536,8 @@ const { config, selectedCustomerList, note, + paymentVerified, + paymentAmount: parsedPaymentAmount, receiveFullPayment, fulfillAll, hasPermission: permissionCode => userStore.hasPermissions(permissionCode), @@ -524,6 +548,7 @@ const { }); const parsedSaleAmount = computed(() => parsePackageAmount(saleAmountText.value)); +const paymentAmountPlaceholder = computed(() => `订单总金额:${formatPackageAmount(parsedSaleAmount.value || 0)}`); const canReceiveImmediately = computed(() => userStore.hasPermissions(permissionCodes.fundReceive.create) && userStore.hasPermissions(permissionCodes.fundReceive.verify)); const canFulfillImmediately = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create) @@ -537,7 +562,7 @@ watch(fulfillAll, value => { }); watch(receiveFullPayment, value => { - if (!value && parsedSaleAmount.value !== 0) { + if (!value && parsedSaleAmount.value !== 0 && (!paymentVerified.value || parsedPaymentAmount.value !== parsedSaleAmount.value)) { fulfillAll.value = false; } }); @@ -744,12 +769,14 @@ async function submitRecord(): Promise { if (!validatePurchaseForm() || !validateItemDecimalQuantity()) { return; } - if (receiveFullPayment.value || fulfillAll.value) { + if (paymentVerified.value || receiveFullPayment.value || fulfillAll.value) { const dialog = DialogPlugin.confirm({ title: "确认直通完成交易", content: fulfillAll.value - ? "将一次性购买套餐、核销全额收款,并完成全部履约和库存扣减。提交后如需更正,只能走撤销或冲正流程。" - : "将一次性购买套餐并核销全额收款。提交后如需更正,只能走资金冲正流程。", + ? "将一次性购买套餐、完成收款核销,并完成全部履约和库存扣减。提交后如需更正,只能走撤销或冲正流程。" + : paymentVerified.value + ? "将一次性购买套餐并登记已核销收款。提交后如需更正,只能走资金冲正流程。" + : "将一次性购买套餐并核销全额收款。提交后如需更正,只能走资金冲正流程。", cancelBtn: "取消", confirmBtn: "确认并提交", onCancel() { @@ -775,7 +802,7 @@ async function purchasePackage(): Promise { return; } await settingStore.playRegisterSuccessSound(); - Message.success(fulfillAll.value ? "套餐、收款及履约已完成" : receiveFullPayment.value ? "套餐及全额收款已完成" : "套餐购买已提交"); + Message.success(fulfillAll.value ? "套餐、收款及履约已完成" : paymentVerified.value ? "套餐及已核销收款已完成" : receiveFullPayment.value ? "套餐及全额收款已完成" : "套餐购买已提交"); await router.replace({ name: "SalesOrderDetail", params: { id: result.salesOrderId }, @@ -947,15 +974,14 @@ watch(config, value => { } .item { - padding: 10px; + padding: 6px; box-shadow: .25rem .25rem 1rem rgba(0, 0, 0, .08); border-radius: 1rem; .avatar { width: 2rem; height: 2rem; - flex: none; - border-radius: .5rem; + border-radius: .5rem !important; } &::after { @@ -998,6 +1024,14 @@ watch(config, value => { } } } + + .payment-item.disabled { + color: var(--td-text-color-disabled); + + :deep(.t-form__label) { + color: var(--td-text-color-disabled); + } + } } .time-actions { diff --git a/src/pages/package/usePackagePurchase.ts b/src/pages/package/usePackagePurchase.ts index 8c6599b..89d4d7b 100644 --- a/src/pages/package/usePackagePurchase.ts +++ b/src/pages/package/usePackagePurchase.ts @@ -10,6 +10,8 @@ interface UsePackagePurchaseOptions { config: Ref; selectedCustomerList: Ref; note: Ref; + paymentVerified: Ref; + paymentAmount: Ref; receiveFullPayment: Ref; fulfillAll: Ref; hasPermission: (permissionCode: string) => boolean; @@ -95,7 +97,9 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) { saleAmount, purchasedAt: options.buildRegisteredAt(), attachmentList: options.buildAttachments(), - straightThrough: options.receiveFullPayment.value || options.fulfillAll.value ? { + straightThrough: options.paymentVerified.value || options.receiveFullPayment.value || options.fulfillAll.value ? { + paymentVerified: options.paymentVerified.value, + paymentAmount: options.paymentAmount.value, receiveFullPayment: options.receiveFullPayment.value, paymentAttachmentList: options.buildPaymentAttachments(), fulfillAll: options.fulfillAll.value, @@ -155,20 +159,28 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) { const canFulfillImmediately = options.hasPermission(permissionCodes.orderFulfillment.create) && options.hasPermission(permissionCodes.orderFulfillment.verify) && (saleAmount === 0 || canReceiveImmediately); - if (options.receiveFullPayment.value && !canReceiveImmediately) { + if ((options.paymentVerified.value || options.receiveFullPayment.value) && !canReceiveImmediately) { Message.warning("当前账号没有收款登记和核销权限"); return false; } + if (options.paymentVerified.value && (options.paymentAmount.value === undefined || options.paymentAmount.value <= 0 || options.paymentAmount.value > saleAmount)) { + Message.warning("已核销收款金额必须大于 0 且不能超过成交价"); + return false; + } if (options.fulfillAll.value && !canFulfillImmediately) { Message.warning("当前账号没有交易直通所需权限"); return false; } - if (options.fulfillAll.value && saleAmount !== 0 && !options.receiveFullPayment.value) { - Message.warning("全部履约直通必须同时直接全额收款"); + if (options.fulfillAll.value && saleAmount !== 0 && !options.paymentVerified.value && !options.receiveFullPayment.value) { + Message.warning("全部履约直通必须同时完成收款核销"); return false; } - if (options.receiveFullPayment.value && saleAmount !== 0 && !options.buildPaymentAttachments().length) { - Message.warning("直接全额收款必须上传收款凭证照片"); + if (options.fulfillAll.value && options.paymentVerified.value && options.paymentAmount.value !== saleAmount) { + Message.warning("全部履约直通必须先完成全额收款核销"); + return false; + } + if ((options.paymentVerified.value || options.receiveFullPayment.value) && saleAmount !== 0 && !options.buildPaymentAttachments().length) { + Message.warning("收款核销必须上传收款凭证照片"); return false; } if (options.fulfillAll.value && !options.buildFulfillmentAttachments().length) { diff --git a/src/pages/salesOrder/SalesOrderCreate.vue b/src/pages/salesOrder/SalesOrderCreate.vue index c27bc55..613aaf4 100644 --- a/src/pages/salesOrder/SalesOrderCreate.vue +++ b/src/pages/salesOrder/SalesOrderCreate.vue @@ -136,6 +136,24 @@ /> + + +
全部履约会同步启用全额收款;请先准备全部产品库存,任意一步失败,整张订单都会回滚。
- + ([]); const paymentPhotoItems = ref([]); const fulfillmentPhotoItems = ref([]); +const paymentAmountText = ref("0"); const photoUrlList = computed(() => photoItems.value.map(item => item.url)); const paymentPhotoUrlList = computed(() => paymentPhotoItems.value.map(item => item.url)); const fulfillmentPhotoUrlList = computed(() => fulfillmentPhotoItems.value.map(item => item.url)); +const receiveFullPayment = ref(false); +const fulfillAll = ref(false); +const paymentAmount = computed(() => parsePackageAmount(paymentAmountText.value)); +const paymentVerified = computed(() => !receiveFullPayment.value + && 0 < paymentAmountText.value.trim().length + && paymentAmount.value !== 0); const maxQuantity = Number.MAX_SAFE_INTEGER; const productCache = new Map(); const skuCache = new Map(); @@ -260,8 +285,6 @@ let latestDecodedText = ""; // ---------- 交易直通 ---------- -const receiveFullPayment = ref(false); -const fulfillAll = ref(false); const canReceiveImmediately = computed(() => userStore.hasPermissions(permissionCodes.fundReceive.create) && userStore.hasPermissions(permissionCodes.fundReceive.verify)); const canFulfillImmediately = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create) @@ -275,7 +298,7 @@ watch(fulfillAll, value => { }); watch(receiveFullPayment, value => { - if (!value && totalAmount.value !== 0) { + if (!value && totalAmount.value !== 0 && (!paymentVerified.value || paymentAmount.value !== totalAmount.value)) { fulfillAll.value = false; } }); @@ -315,6 +338,7 @@ const totalAmount = computed(() => { return amount; }); const totalAmountText = computed(() => totalAmount.value === undefined ? "0.00" : formatProductAmount(totalAmount.value)); +const paymentAmountPlaceholder = computed(() => `订单总金额:${formatProductAmount(totalAmount.value || 0)}`); function createItem(): FormItem { return { @@ -454,21 +478,25 @@ function validate(): boolean { keys.add(key); if (canPriceOverride.value && item.unitPriceText && parsePackageAmount(item.unitPriceText) === undefined) { Message.warning("成交单价必须是合法金额"); return false; } } - if (receiveFullPayment.value && !canReceiveImmediately.value) { Message.warning("当前账号没有收款登记和核销权限"); return false; } + if ((paymentVerified.value || receiveFullPayment.value) && !canReceiveImmediately.value) { Message.warning("当前账号没有收款登记和核销权限"); return false; } + if (paymentVerified.value && (paymentAmount.value === undefined || paymentAmount.value <= 0 || paymentAmount.value > (totalAmount.value || 0))) { Message.warning("已核销收款金额必须大于 0 且不能超过订单总价"); return false; } if (fulfillAll.value && !canFulfillImmediately.value) { Message.warning("当前账号没有交易直通所需权限"); return false; } - if (fulfillAll.value && totalAmount.value !== 0 && !receiveFullPayment.value) { Message.warning("全部履约直通必须同时直接全额收款"); return false; } - if (receiveFullPayment.value && totalAmount.value && !buildAttachments(paymentPhotoItems.value).length) { Message.warning("直接全额收款必须上传收款凭证照片"); return false; } + if (fulfillAll.value && totalAmount.value !== 0 && !paymentVerified.value && !receiveFullPayment.value) { Message.warning("全部履约直通必须同时完成收款核销"); return false; } + if (fulfillAll.value && paymentVerified.value && paymentAmount.value !== totalAmount.value) { Message.warning("全部履约直通必须先完成全额收款核销"); return false; } + if ((paymentVerified.value || receiveFullPayment.value) && totalAmount.value && !buildAttachments(paymentPhotoItems.value).length) { Message.warning("收款核销必须上传收款凭证照片"); return false; } if (fulfillAll.value && !buildAttachments(fulfillmentPhotoItems.value).length) { Message.warning("全部履约直通必须上传履约凭证照片"); return false; } return true; } async function save(): Promise { if (isSaving.value || !validate()) return; - if (receiveFullPayment.value || fulfillAll.value) { + if (paymentVerified.value || receiveFullPayment.value || fulfillAll.value) { const dialog = DialogPlugin.confirm({ title: "确认直通完成交易", content: fulfillAll.value - ? "将一次性创建订单、核销全额收款,并完成全部履约和库存扣减。提交后如需更正,只能走撤销或冲正流程。" - : "将一次性创建订单并核销全额收款。提交后如需更正,只能走资金冲正流程。", + ? "将一次性创建订单、完成收款核销,并完成全部履约和库存扣减。提交后如需更正,只能走撤销或冲正流程。" + : paymentVerified.value + ? "将一次性创建订单并登记已核销收款。提交后如需更正,只能走资金冲正流程。" + : "将一次性创建订单并核销全额收款。提交后如需更正,只能走资金冲正流程。", cancelBtn: "取消", confirmBtn: "确认并提交", onCancel() { @@ -501,7 +529,9 @@ async function submitOrder(): Promise { purchasedAt: resolveTimestamp(purchasedAtText.value), itemList, attachmentList: buildAttachments(), - straightThrough: receiveFullPayment.value || fulfillAll.value ? { + straightThrough: paymentVerified.value || receiveFullPayment.value || fulfillAll.value ? { + paymentVerified: paymentVerified.value, + paymentAmount: paymentAmount.value, receiveFullPayment: receiveFullPayment.value, paymentAttachmentList: buildAttachments(paymentPhotoItems.value), fulfillAll: fulfillAll.value, @@ -509,7 +539,7 @@ async function submitOrder(): Promise { } : undefined, remark: remark.value.trim() || null }); - Message.success(fulfillAll.value ? "订单、收款及履约已完成" : receiveFullPayment.value ? "订单及全额收款已完成" : "销售订单已创建"); + Message.success(fulfillAll.value ? "订单、收款及履约已完成" : paymentVerified.value ? "订单及已核销收款已完成" : receiveFullPayment.value ? "订单及全额收款已完成" : "销售订单已创建"); await router.replace(`/management/sales-orders/${result.id}`); } catch (error) { const message = getErrorMessage(error, "销售订单创建失败,重试会复用同一请求号"); @@ -562,6 +592,14 @@ onUnmounted(() => navBarStore.clearRight()); justify-content: flex-end; } + .payment-item.disabled { + color: var(--td-text-color-disabled); + + :deep(.t-form__label) { + color: var(--td-text-color-disabled); + } + } + .product-part { > .header { diff --git a/src/types/SalesOrder.ts b/src/types/SalesOrder.ts index 5f7da3e..0b76948 100644 --- a/src/types/SalesOrder.ts +++ b/src/types/SalesOrder.ts @@ -99,6 +99,8 @@ export type DirectProductOrderItemRequest = { }; export type SalesOrderStraightThroughRequest = { + paymentVerified?: boolean; + paymentAmount?: number; receiveFullPayment?: boolean; paymentOccurredAt?: number; paymentAttachmentList?: Partial[];