This commit is contained in:
Your Name
2026-06-04 09:25:00 +08:00
parent 6b528c34c8
commit 5ab3be1fdd
7 changed files with 361 additions and 64 deletions
+2 -2
View File
@@ -487,8 +487,8 @@ export function prescriptionOrderComplete(params: { id: number; fulfillment_stat
return request.post({ url: '/tcm.prescriptionOrder/complete', params }) return request.post({ url: '/tcm.prescriptionOrder/complete', params })
} }
/** 业务订单退款(须填写原因;关联收款单标记已退款、已付总额清零 */ /** 业务订单退款(须填写原因;refund_amount 可选,不传则退满可退上限 */
export function prescriptionOrderRefund(params: { id: number; reason: string }) { export function prescriptionOrderRefund(params: { id: number; reason: string; refund_amount?: number }) {
return request.post({ url: '/tcm.prescriptionOrder/refund', params }) return request.post({ url: '/tcm.prescriptionOrder/refund', params })
} }
@@ -2163,6 +2163,20 @@
label-width="88px" label-width="88px"
class="mt-3" class="mt-3"
> >
<el-form-item label="退款金额" prop="refund_amount">
<el-input-number
v-model="completeRefundForm.refund_amount"
:min="0.01"
:max="completeOrderRefundMax > 0 ? completeOrderRefundMax : undefined"
:step="0.01"
:precision="2"
class="w-full"
controls-position="right"
/>
<div class="text-xs text-gray-500 mt-1">
可退上限 ¥{{ formatMoney(completeOrderRefundMax) }}
</div>
</el-form-item>
<el-form-item label="退款原因" prop="reason"> <el-form-item label="退款原因" prop="reason">
<el-input <el-input
v-model="completeRefundForm.reason" v-model="completeRefundForm.reason"
@@ -2201,9 +2215,23 @@
:closable="false" :closable="false"
show-icon show-icon
class="mb-4 !items-start" class="mb-4 !items-start"
title="确认退款后,关联收款记录将全部标记为「已退款」,已付总额清零,且不可撤销。" :title="refundOrderAlertTitle"
/> />
<el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px"> <el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px">
<el-form-item label="退款金额" prop="refund_amount">
<el-input-number
v-model="refundOrderForm.refund_amount"
:min="0.01"
:max="refundOrderMaxAmount > 0 ? refundOrderMaxAmount : undefined"
:step="0.01"
:precision="2"
class="w-full"
controls-position="right"
/>
<div class="text-xs text-gray-500 mt-1">
可退上限 ¥{{ formatMoney(refundOrderMaxAmount) }}默认全额部分退款时按金额从关联收款单依次标记已退款并扣减本单已付
</div>
</el-form-item>
<el-form-item label="退款原因" prop="reason"> <el-form-item label="退款原因" prop="reason">
<el-input <el-input
v-model="refundOrderForm.reason" v-model="refundOrderForm.reason"
@@ -5332,20 +5360,31 @@ const completeOrderSubmitting = ref(false)
const completeOrderPayLoading = ref(false) const completeOrderPayLoading = ref(false)
const completeOrderLinkedPays = ref<any[]>([]) const completeOrderLinkedPays = ref<any[]>([])
const completeRefundFormRef = ref<FormInstance>() const completeRefundFormRef = ref<FormInstance>()
const completeRefundForm = reactive({ reason: '' }) const completeRefundForm = reactive({ reason: '', refund_amount: undefined as number | undefined })
const completeRefundRules: FormRules = { const completeOrderRefundMax = ref(0)
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }] const completeOrderRefundTotal = computed(() => calcRefundableFromLinkedPays(completeOrderLinkedPays.value))
} const completeRefundRules = computed<FormRules>(() => ({
const completeOrderRefundTotal = computed(() => { refund_amount: [
let sum = 0 { required: true, message: '请输入退款金额', trigger: 'change' },
for (const o of completeOrderLinkedPays.value) { {
const st = Number(o?.status) validator: (_rule, v, cb) => {
if (st === 2 || st === 5) { const n = Number(v)
sum += Number(o?.amount) || 0 const cap = completeOrderRefundMax.value
if (!Number.isFinite(n) || n <= 0) {
cb(new Error('退款金额须大于 0'))
return
}
if (cap > 0 && n > cap + 0.009) {
cb(new Error(`不能超过可退上限 ¥${formatMoney(cap)}`))
return
}
cb()
},
trigger: 'change'
} }
} ],
return Math.round(sum * 100) / 100 reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
}) }))
let completeOrderLogisticsRequestSeq = 0 let completeOrderLogisticsRequestSeq = 0
function onCompleteOrderDialogClosed() { function onCompleteOrderDialogClosed() {
@@ -5353,6 +5392,8 @@ function onCompleteOrderDialogClosed() {
completeOrderLinkedPays.value = [] completeOrderLinkedPays.value = []
completeOrderPayLoading.value = false completeOrderPayLoading.value = false
completeRefundForm.reason = '' completeRefundForm.reason = ''
completeRefundForm.refund_amount = undefined
completeOrderRefundMax.value = 0
completeRefundFormRef.value?.clearValidate() completeRefundFormRef.value?.clearValidate()
completeOrderLogisticsRequestSeq += 1 completeOrderLogisticsRequestSeq += 1
completeOrderLogisticsPayload.value = null completeOrderLogisticsPayload.value = null
@@ -5370,8 +5411,13 @@ async function loadCompleteOrderPayContext(orderId: number) {
const d = r?.data ?? r ?? null const d = r?.data ?? r ?? null
const arr = d?.linked_pay_orders const arr = d?.linked_pay_orders
completeOrderLinkedPays.value = Array.isArray(arr) ? arr : [] completeOrderLinkedPays.value = Array.isArray(arr) ? arr : []
const linkedMax = calcRefundableFromLinkedPays(completeOrderLinkedPays.value)
const paid = Number(d?.paid) || 0
completeOrderRefundMax.value = linkedMax > 0 ? linkedMax : paid
completeRefundForm.refund_amount = completeOrderRefundMax.value
} catch { } catch {
completeOrderLinkedPays.value = [] completeOrderLinkedPays.value = []
completeOrderRefundMax.value = 0
} finally { } finally {
completeOrderPayLoading.value = false completeOrderPayLoading.value = false
} }
@@ -5421,7 +5467,11 @@ async function submitCompleteOrder() {
if (!valid) return if (!valid) return
completeOrderSubmitting.value = true completeOrderSubmitting.value = true
try { try {
await prescriptionOrderRefund({ id, reason: completeRefundForm.reason.trim() }) await prescriptionOrderRefund({
id,
reason: completeRefundForm.reason.trim(),
refund_amount: Number(completeRefundForm.refund_amount)
})
feedback.msgSuccess('退款成功') feedback.msgSuccess('退款成功')
completeOrderDialogVisible.value = false completeOrderDialogVisible.value = false
getLists() getLists()
@@ -5462,25 +5512,79 @@ async function submitCompleteOrder() {
} }
// 退/ + // 退/ +
function calcRefundableFromLinkedPays(list: any[]) {
let sum = 0
for (const o of list || []) {
const st = Number(o?.status)
if (st === 2 || st === 5) {
sum += Number(o?.amount) || 0
}
}
return Math.round(sum * 100) / 100
}
const refundOrderDialogVisible = ref(false) const refundOrderDialogVisible = ref(false)
const refundOrderId = ref(0) const refundOrderId = ref(0)
const refundOrderSubmitting = ref(false) const refundOrderSubmitting = ref(false)
const refundOrderFormRef = ref<FormInstance>() const refundOrderFormRef = ref<FormInstance>()
const refundOrderForm = reactive({ reason: '' }) const refundOrderMaxAmount = ref(0)
const refundOrderRules: FormRules = { const refundOrderForm = reactive({ reason: '', refund_amount: undefined as number | undefined })
const refundOrderAlertTitle = computed(() => {
const cap = formatMoney(refundOrderMaxAmount.value)
const amt = formatMoney(refundOrderForm.refund_amount)
if (Number(refundOrderForm.refund_amount) >= refundOrderMaxAmount.value - 0.009) {
return `确认全额退款 ¥${cap}:关联收款将标记已退款,本单已付清零,且不可撤销。`
}
return `确认部分退款 ¥${amt}(可退上限 ¥${cap}):将扣减本单已付,并按金额依次标记关联收款已退款。`
})
const refundOrderRules = computed<FormRules>(() => ({
refund_amount: [
{ required: true, message: '请输入退款金额', trigger: 'change' },
{
validator: (_rule, v, cb) => {
const n = Number(v)
const cap = refundOrderMaxAmount.value
if (!Number.isFinite(n) || n <= 0) {
cb(new Error('退款金额须大于 0'))
return
}
if (cap > 0 && n > cap + 0.009) {
cb(new Error(`不能超过可退上限 ¥${formatMoney(cap)}`))
return
}
cb()
},
trigger: 'change'
}
],
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }] reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
} }))
function onRefundOrderDialogClosed() { function onRefundOrderDialogClosed() {
refundOrderId.value = 0 refundOrderId.value = 0
refundOrderForm.reason = '' refundOrderForm.reason = ''
refundOrderForm.refund_amount = undefined
refundOrderMaxAmount.value = 0
refundOrderFormRef.value?.clearValidate() refundOrderFormRef.value?.clearValidate()
} }
function openRefundOrder(row: { id: number }) { async function openRefundOrder(row: { id: number }) {
refundOrderId.value = row.id refundOrderId.value = row.id
refundOrderForm.reason = '' refundOrderForm.reason = ''
refundOrderForm.refund_amount = undefined
refundOrderMaxAmount.value = 0
refundOrderDialogVisible.value = true refundOrderDialogVisible.value = true
try {
const r: any = await prescriptionOrderDetail({ id: row.id })
const d = r?.data ?? r ?? null
const linked = Array.isArray(d?.linked_pay_orders) ? d.linked_pay_orders : []
const linkedMax = calcRefundableFromLinkedPays(linked)
const paid = Number(d?.paid) || 0
refundOrderMaxAmount.value = linkedMax > 0 ? linkedMax : paid
refundOrderForm.refund_amount = refundOrderMaxAmount.value
} catch {
refundOrderMaxAmount.value = 0
}
} }
async function submitRefundOrder() { async function submitRefundOrder() {
@@ -5490,7 +5594,11 @@ async function submitRefundOrder() {
if (!valid) return if (!valid) return
refundOrderSubmitting.value = true refundOrderSubmitting.value = true
try { try {
await prescriptionOrderRefund({ id, reason: refundOrderForm.reason.trim() }) await prescriptionOrderRefund({
id,
reason: refundOrderForm.reason.trim(),
refund_amount: Number(refundOrderForm.refund_amount)
})
feedback.msgSuccess('退款成功') feedback.msgSuccess('退款成功')
refundOrderDialogVisible.value = false refundOrderDialogVisible.value = false
getLists() getLists()
@@ -5676,6 +5784,7 @@ function confirmComplete(row: {
completeOrderId.value = row.id completeOrderId.value = row.id
completeFulfillmentStatus.value = 3 completeFulfillmentStatus.value = 3
completeRefundForm.reason = '' completeRefundForm.reason = ''
completeRefundForm.refund_amount = undefined
completeOrderLinkedPays.value = [] completeOrderLinkedPays.value = []
completeOrderLogisticsPayload.value = null completeOrderLogisticsPayload.value = null
completeOrderDialogVisible.value = true completeOrderDialogVisible.value = true
@@ -1880,6 +1880,17 @@
label-width="80px" label-width="80px"
class="mt-2" class="mt-2"
> >
<el-form-item label="退款金额" prop="refund_amount">
<el-input-number
v-model="completeRefundForm.refund_amount"
:min="0.01"
:max="completeOrderRefundMax > 0 ? completeOrderRefundMax : undefined"
:step="0.01"
:precision="2"
class="w-full"
/>
<div class="text-xs text-gray-500 mt-1">可退上限 ¥{{ formatMoney(completeOrderRefundMax) }}</div>
</el-form-item>
<el-form-item label="退款原因" prop="reason"> <el-form-item label="退款原因" prop="reason">
<el-input <el-input
v-model="completeRefundForm.reason" v-model="completeRefundForm.reason"
@@ -1917,9 +1928,20 @@
:closable="false" :closable="false"
show-icon show-icon
class="mb-4 !items-start" class="mb-4 !items-start"
title="确认退款后,关联收款记录将全部标记为「已退款」,已付总额清零,且不可撤销。" :title="refundOrderAlertTitle"
/> />
<el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px"> <el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px">
<el-form-item label="退款金额" prop="refund_amount">
<el-input-number
v-model="refundOrderForm.refund_amount"
:min="0.01"
:max="refundOrderMaxAmount > 0 ? refundOrderMaxAmount : undefined"
:step="0.01"
:precision="2"
class="w-full"
/>
<div class="text-xs text-gray-500 mt-1">可退上限 ¥{{ formatMoney(refundOrderMaxAmount) }}</div>
</el-form-item>
<el-form-item label="退款原因" prop="reason"> <el-form-item label="退款原因" prop="reason">
<el-input <el-input
v-model="refundOrderForm.reason" v-model="refundOrderForm.reason"
@@ -4656,23 +4678,41 @@ const completeFulfillmentStatus = ref<number>(3)
const completeOrderSubmitting = ref(false) const completeOrderSubmitting = ref(false)
const completeOrderLinkedPays = ref<any[]>([]) const completeOrderLinkedPays = ref<any[]>([])
const completeRefundFormRef = ref<FormInstance>() const completeRefundFormRef = ref<FormInstance>()
const completeRefundForm = reactive({ reason: '' }) function calcRefundableFromLinkedPays(list: any[]) {
const completeRefundRules: FormRules = {
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
}
const completeOrderRefundTotal = computed(() => {
let sum = 0 let sum = 0
for (const o of completeOrderLinkedPays.value) { for (const o of list || []) {
const st = Number(o?.status) const st = Number(o?.status)
if (st === 2 || st === 5) sum += Number(o?.amount) || 0 if (st === 2 || st === 5) sum += Number(o?.amount) || 0
} }
return Math.round(sum * 100) / 100 return Math.round(sum * 100) / 100
}) }
const completeRefundForm = reactive({ reason: '', refund_amount: undefined as number | undefined })
const completeOrderRefundMax = ref(0)
const completeOrderRefundTotal = computed(() => calcRefundableFromLinkedPays(completeOrderLinkedPays.value))
const completeRefundRules = computed<FormRules>(() => ({
refund_amount: [
{ required: true, message: '请输入退款金额', trigger: 'change' },
{
validator: (_r, v, cb) => {
const n = Number(v)
const cap = completeOrderRefundMax.value
if (!Number.isFinite(n) || n <= 0) return cb(new Error('退款金额须大于 0'))
if (cap > 0 && n > cap + 0.009) return cb(new Error(`不能超过 ¥${formatMoney(cap)}`))
cb()
},
trigger: 'change'
}
],
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
}))
function onCompleteOrderDialogClosed() { function onCompleteOrderDialogClosed() {
completeOrderId.value = 0 completeOrderId.value = 0
completeOrderLinkedPays.value = [] completeOrderLinkedPays.value = []
completeRefundForm.reason = '' completeRefundForm.reason = ''
completeRefundForm.refund_amount = undefined
completeOrderRefundMax.value = 0
completeRefundFormRef.value?.clearValidate() completeRefundFormRef.value?.clearValidate()
} }
@@ -4685,8 +4725,13 @@ async function loadCompleteOrderPayContext(orderId: number) {
const r: any = await prescriptionOrderDetail({ id: orderId }) const r: any = await prescriptionOrderDetail({ id: orderId })
const d = r?.data ?? r ?? null const d = r?.data ?? r ?? null
completeOrderLinkedPays.value = Array.isArray(d?.linked_pay_orders) ? d.linked_pay_orders : [] completeOrderLinkedPays.value = Array.isArray(d?.linked_pay_orders) ? d.linked_pay_orders : []
const linkedMax = calcRefundableFromLinkedPays(completeOrderLinkedPays.value)
const paid = Number(d?.paid) || 0
completeOrderRefundMax.value = linkedMax > 0 ? linkedMax : paid
completeRefundForm.refund_amount = completeOrderRefundMax.value
} catch { } catch {
completeOrderLinkedPays.value = [] completeOrderLinkedPays.value = []
completeOrderRefundMax.value = 0
} }
} }
@@ -4699,7 +4744,11 @@ async function submitCompleteOrder() {
if (!valid) return if (!valid) return
completeOrderSubmitting.value = true completeOrderSubmitting.value = true
try { try {
await prescriptionOrderRefund({ id, reason: completeRefundForm.reason.trim() }) await prescriptionOrderRefund({
id,
reason: completeRefundForm.reason.trim(),
refund_amount: Number(completeRefundForm.refund_amount)
})
feedback.msgSuccess('退款成功') feedback.msgSuccess('退款成功')
completeOrderDialogVisible.value = false completeOrderDialogVisible.value = false
getLists() getLists()
@@ -4743,21 +4792,57 @@ const refundOrderDialogVisible = ref(false)
const refundOrderId = ref(0) const refundOrderId = ref(0)
const refundOrderSubmitting = ref(false) const refundOrderSubmitting = ref(false)
const refundOrderFormRef = ref<FormInstance>() const refundOrderFormRef = ref<FormInstance>()
const refundOrderForm = reactive({ reason: '' }) const refundOrderMaxAmount = ref(0)
const refundOrderRules: FormRules = { const refundOrderForm = reactive({ reason: '', refund_amount: undefined as number | undefined })
const refundOrderAlertTitle = computed(() => {
const cap = formatMoney(refundOrderMaxAmount.value)
const amt = formatMoney(refundOrderForm.refund_amount)
if (Number(refundOrderForm.refund_amount) >= refundOrderMaxAmount.value - 0.009) {
return `全额退款 ¥${cap}:关联收款标记已退款,已付清零。`
}
return `部分退款 ¥${amt}(上限 ¥${cap}`
})
const refundOrderRules = computed<FormRules>(() => ({
refund_amount: [
{ required: true, message: '请输入退款金额', trigger: 'change' },
{
validator: (_r, v, cb) => {
const n = Number(v)
const cap = refundOrderMaxAmount.value
if (!Number.isFinite(n) || n <= 0) return cb(new Error('退款金额须大于 0'))
if (cap > 0 && n > cap + 0.009) return cb(new Error(`不能超过 ¥${formatMoney(cap)}`))
cb()
},
trigger: 'change'
}
],
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }] reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
} }))
function onRefundOrderDialogClosed() { function onRefundOrderDialogClosed() {
refundOrderId.value = 0 refundOrderId.value = 0
refundOrderForm.reason = '' refundOrderForm.reason = ''
refundOrderForm.refund_amount = undefined
refundOrderMaxAmount.value = 0
refundOrderFormRef.value?.clearValidate() refundOrderFormRef.value?.clearValidate()
} }
function openRefundOrder(row: { id: number }) { async function openRefundOrder(row: { id: number }) {
refundOrderId.value = row.id refundOrderId.value = row.id
refundOrderForm.reason = '' refundOrderForm.reason = ''
refundOrderMaxAmount.value = 0
refundOrderDialogVisible.value = true refundOrderDialogVisible.value = true
try {
const r: any = await prescriptionOrderDetail({ id: row.id })
const d = r?.data ?? r ?? null
const linked = Array.isArray(d?.linked_pay_orders) ? d.linked_pay_orders : []
const linkedMax = calcRefundableFromLinkedPays(linked)
const paid = Number(d?.paid) || 0
refundOrderMaxAmount.value = linkedMax > 0 ? linkedMax : paid
refundOrderForm.refund_amount = refundOrderMaxAmount.value
} catch {
refundOrderMaxAmount.value = 0
}
} }
async function submitRefundOrder() { async function submitRefundOrder() {
@@ -4767,7 +4852,11 @@ async function submitRefundOrder() {
if (!valid) return if (!valid) return
refundOrderSubmitting.value = true refundOrderSubmitting.value = true
try { try {
await prescriptionOrderRefund({ id, reason: refundOrderForm.reason.trim() }) await prescriptionOrderRefund({
id,
reason: refundOrderForm.reason.trim(),
refund_amount: Number(refundOrderForm.refund_amount)
})
feedback.msgSuccess('退款成功') feedback.msgSuccess('退款成功')
refundOrderDialogVisible.value = false refundOrderDialogVisible.value = false
getLists() getLists()
@@ -351,11 +351,17 @@ class PrescriptionOrderController extends BaseAdminController
public function refund() public function refund()
{ {
$params = (new PrescriptionOrderValidate())->post()->goCheck('refund'); $params = (new PrescriptionOrderValidate())->post()->goCheck('refund');
$rawRefundAmount = $params['refund_amount'] ?? null;
$refundAmount = ($rawRefundAmount === null || $rawRefundAmount === '')
? null
: round((float) $rawRefundAmount, 2);
$result = PrescriptionOrderLogic::refund( $result = PrescriptionOrderLogic::refund(
(int) $params['id'], (int) $params['id'],
(string) ($params['reason'] ?? ''), (string) ($params['reason'] ?? ''),
$this->adminId, $this->adminId,
$this->adminInfo $this->adminInfo,
$refundAmount
); );
if ($result === false) { if ($result === false) {
return $this->fail(PrescriptionOrderLogic::getError()); return $this->fail(PrescriptionOrderLogic::getError());
@@ -782,6 +782,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
'export_medication_days' => '天数', 'export_medication_days' => '天数',
'export_amount' => '总金额', 'export_amount' => '总金额',
'export_paid_amount' => '已付金额', 'export_paid_amount' => '已付金额',
'export_refund_amount' => '退款金额',
'export_agency_collect' => '代收金额', 'export_agency_collect' => '代收金额',
'export_tracking_number' => '快递单号', 'export_tracking_number' => '快递单号',
'export_sign_time' => '签收日期', 'export_sign_time' => '签收日期',
@@ -2626,11 +2626,43 @@ class PrescriptionOrderLogic
} }
/** /**
* 业务订单退款:须填写原因;关联收款单标记为已退款,实付清零。 * 关联收款单中可退款的记录(已支付/待审核)
* *
* @return array<int, array<string, mixed>>
*/
private static function linkedPayRefundableRows(int $prescriptionOrderId): array
{
$ids = self::linkedPayOrderIdList($prescriptionOrderId);
if ($ids === []) {
return [];
}
return Order::whereIn('id', $ids)
->whereNull('delete_time')
->whereIn('status', [2, 5])
->field(['id', 'amount'])
->order('id', 'asc')
->select()
->toArray();
}
private static function linkedPayRefundableTotal(int $prescriptionOrderId): float
{
$sum = 0.0;
foreach (self::linkedPayRefundableRows($prescriptionOrderId) as $row) {
$sum += (float) ($row['amount'] ?? 0);
}
return round($sum, 2);
}
/**
* 业务订单退款:须填写原因;可指定退款金额(部分退)或退满可退上限(全额)。
*
* @param float|null $refundAmount 为空或 0 表示退满可退上限
* @return array<string,mixed>|false * @return array<string,mixed>|false
*/ */
public static function refund(int $id, string $reason, int $adminId, array $adminInfo) public static function refund(int $id, string $reason, int $adminId, array $adminInfo, ?float $refundAmount = null)
{ {
self::$error = ''; self::$error = '';
$reason = trim($reason); $reason = trim($reason);
@@ -2663,30 +2695,72 @@ class PrescriptionOrderLogic
return false; return false;
} }
$linkedPayOrderIds = self::linkedPayOrderIdList($id); $maxRefundable = self::linkedPayRefundableTotal($id);
$refundedPayCount = 0; $currentPaid = round((float) $order->paid, 2);
$refundedAmount = 0.0; $cap = $maxRefundable > 0 ? $maxRefundable : $currentPaid;
if ($cap <= 0) {
self::$error = '当前无可退金额';
return false;
}
if ($refundAmount === null || $refundAmount <= 0) {
$refundAmount = $cap;
} else {
$refundAmount = round($refundAmount, 2);
if ($refundAmount > $cap + 0.009) {
self::$error = '退款金额不能超过可退上限 ¥' . $cap;
return false;
}
}
$isFullRefund = $refundAmount >= $cap - 0.009;
$toRefundRows = self::linkedPayRefundableRows($id);
$refundedPayCount = 0;
$markedPayTotal = 0.0;
try { try {
if ($linkedPayOrderIds !== []) { if ($isFullRefund && $toRefundRows !== []) {
$toRefundRows = Order::whereIn('id', $linkedPayOrderIds) $ids = array_map('intval', array_column($toRefundRows, 'id'));
$refundedPayCount = Order::whereIn('id', $ids)
->whereNull('delete_time') ->whereNull('delete_time')
->whereIn('status', [2, 5]) ->update(['status' => 4]);
->field(['id', 'amount'])
->select()
->toArray();
foreach ($toRefundRows as $pr) { foreach ($toRefundRows as $pr) {
$refundedAmount += (float) ($pr['amount'] ?? 0); $markedPayTotal += (float) ($pr['amount'] ?? 0);
} }
$refundedAmount = round($refundedAmount, 2); $markedPayTotal = round($markedPayTotal, 2);
if ($toRefundRows !== []) { } elseif (!$isFullRefund && $toRefundRows !== []) {
$refundedPayCount = Order::whereIn('id', array_column($toRefundRows, 'id')) $remaining = $refundAmount;
->whereNull('delete_time') foreach ($toRefundRows as $pr) {
->update(['status' => 4]); if ($remaining < 0.01) {
break;
}
$amt = round((float) ($pr['amount'] ?? 0), 2);
if ($amt <= $remaining + 0.001) {
Order::where('id', (int) $pr['id'])
->whereNull('delete_time')
->update(['status' => 4]);
$remaining -= $amt;
$markedPayTotal += $amt;
$refundedPayCount++;
}
}
$markedPayTotal = round($markedPayTotal, 2);
}
if ($isFullRefund) {
$order->fulfillment_status = 10;
$order->paid = 0;
} else {
$order->paid = round(max(0, $currentPaid - $refundAmount), 2);
if ($order->paid <= 0.009) {
$order->paid = 0;
$order->fulfillment_status = 10;
} }
} }
$order->fulfillment_status = 10; $prevStoredRefund = round((float) ($order->refund_amount ?? 0), 2);
$order->paid = 0; $order->refund_amount = round($prevStoredRefund + $refundAmount, 2);
$order->save(); $order->save();
} catch (\Throwable $e) { } catch (\Throwable $e) {
self::$error = $e->getMessage(); self::$error = $e->getMessage();
@@ -2694,21 +2768,34 @@ class PrescriptionOrderLogic
return false; return false;
} }
$unassignSummary = self::clearDiagnosisAssistantOnComplete((int) $order->diagnosis_id); $unassignSummary = '';
$logLine = '订单退款,状态变更为「退款」;原因:' . $reason; if ($isFullRefund || (float) $order->paid <= 0.009) {
$unassignSummary = self::clearDiagnosisAssistantOnComplete((int) $order->diagnosis_id);
}
if ($isFullRefund) {
$logLine = '订单全额退款,状态变更为「退款」;原因:' . $reason . ';退款金额 ¥' . $refundAmount;
} else {
$logLine = '订单部分退款;原因:' . $reason . ';本次退款 ¥' . $refundAmount . ';已付余额 ¥' . $order->paid;
}
if ($refundedPayCount > 0) { if ($refundedPayCount > 0) {
$logLine .= ';关联收款单 ' . $refundedPayCount . ' 笔已标记为已退款'; $logLine .= ';关联收款单 ' . $refundedPayCount . ' 笔已标记为已退款';
if ($refundedAmount > 0) { if ($markedPayTotal > 0) {
$logLine .= '合计 ¥' . $refundedAmount; $logLine .= '(收款侧合计 ¥' . $markedPayTotal . '';
} }
} elseif ($linkedPayOrderIds !== []) { } elseif ($toRefundRows !== []) {
$logLine .= ';关联收款单无待退款的已支付/待审核记录'; $logLine .= ';关联收款单无匹配的已支付/待审核记录可标记';
}
if ($isFullRefund) {
$logLine .= ';已付总额清零' . $unassignSummary;
} }
$logLine .= ';已付总额清零' . $unassignSummary;
self::writeLog($id, $adminId, $adminInfo, 'refund', $logLine); self::writeLog($id, $adminId, $adminInfo, 'refund', $logLine);
$out = $order->toArray(); $out = $order->toArray();
$out['this_refund_amount'] = $refundAmount;
$out['refund_full'] = $isFullRefund;
$out['refundable_cap'] = $cap;
self::maskInternalCostIfNeeded($out, $adminInfo); self::maskInternalCostIfNeeded($out, $adminInfo);
self::maskRemarkExtraIfNeeded($out, $adminInfo); self::maskRemarkExtraIfNeeded($out, $adminInfo);
self::attachLinkedPayOrders($out); self::attachLinkedPayOrders($out);
@@ -3251,6 +3338,10 @@ class PrescriptionOrderLogic
$paid = round((float) ($item['linked_pay_paid_total'] ?? 0), 2); $paid = round((float) ($item['linked_pay_paid_total'] ?? 0), 2);
$item['export_amount'] = number_format($amt, 2, '.', ''); $item['export_amount'] = number_format($amt, 2, '.', '');
$item['export_paid_amount'] = number_format($paid, 2, '.', ''); $item['export_paid_amount'] = number_format($paid, 2, '.', '');
$refundStored = round((float) ($item['refund_amount'] ?? 0), 2);
$item['export_refund_amount'] = $refundStored > 0
? number_format($refundStored, 2, '.', '')
: '';
$item['export_agency_collect'] = number_format(round($amt - $paid, 2), 2, '.', ''); $item['export_agency_collect'] = number_format(round($amt - $paid, 2), 2, '.', '');
$item['export_tracking_number'] = (string) ($item['tracking_number'] ?? ''); $item['export_tracking_number'] = (string) ($item['tracking_number'] ?? '');
@@ -34,6 +34,7 @@ class PrescriptionOrderValidate extends BaseValidate
'remark' => 'max:500', 'remark' => 'max:500',
'fulfillment_status' => 'require|integer|in:3,7,8,9,11,12', 'fulfillment_status' => 'require|integer|in:3,7,8,9,11,12',
'reason' => 'require|max:500', 'reason' => 'require|max:500',
'refund_amount' => 'float|egt:0',
'patient_name' => 'require|max:50', 'patient_name' => 'require|max:50',
'phone' => 'require|max:20', 'phone' => 'require|max:20',
'phone_tail' => 'max:20|regex:/^\\d*$/', 'phone_tail' => 'max:20|regex:/^\\d*$/',
@@ -77,7 +78,7 @@ class PrescriptionOrderValidate extends BaseValidate
'linkPayOrder' => ['id', 'pay_order_id'], 'linkPayOrder' => ['id', 'pay_order_id'],
'requestCompletion' => ['id'], 'requestCompletion' => ['id'],
'complete' => ['id', 'fulfillment_status'], 'complete' => ['id', 'fulfillment_status'],
'refund' => ['id', 'reason'], 'refund' => ['id', 'reason', 'refund_amount'],
'submitGancaoRecipel' => ['id'], 'submitGancaoRecipel' => ['id'],
'previewGancaoRecipel' => ['id'], 'previewGancaoRecipel' => ['id'],
'patchPrescriptionPatient' => ['id', 'patient_name', 'phone'], 'patchPrescriptionPatient' => ['id', 'patient_name', 'phone'],