支付金额取整

This commit is contained in:
Dev
2026-10-28 15:37:49 +08:00
parent 9622b908d6
commit f552fb957f
4 changed files with 134 additions and 24 deletions
@@ -351,17 +351,18 @@ class DemandService
$winContract = ContractService::updateCustomerContractData($demandId, $formData);
//如果新的合同金额与旧的合同金额不符,则通过修改尾款订单的金额的方式来弥补差价
$firstContract = ContractService::getFirstContract($demandId);
if ($firstContract && $firstContract->total_amount != $winContract->total_amount && $winContract->status2 != ContractService::STATUS_FINISHED) {
$diffAmount = $winContract->total_amount - $firstContract->total_amount;
//更新尾款订单的金额
if ($firstContract && bccomp((string)$firstContract->total_amount, (string)$winContract->total_amount, 2) !== 0 && $winContract->status2 != ContractService::STATUS_FINISHED) {
// 尾款 = 新合同总额 - 首付款,不按固定 50% 重算
$firstOrder = OrderService::getDemandOrder($demand->parent_order_no, OrderService::ORDER_TYPE_FIRST_PAYMENT);
$finalOrder = OrderService::getDemandOrder($demand->parent_order_no, OrderService::ORDER_TYPE_FINAL_PAYMENT);
$finalOrder->total_amount += $diffAmount;
$finalOrder->goods_amount += $diffAmount;
$finalOrder->payment_amount += $diffAmount;
$saveRes = $finalOrder->save();
if (!$saveRes) {
exception('弥补订单差价金额失败');
if (!$firstOrder || !$finalOrder) {
exception('获取分期订单失败');
}
$finalAmount = bcsub((string)$winContract->total_amount, (string)$firstOrder->payment_amount, 2);
if (bccomp($finalAmount, '0', 2) < 0) {
exception('合同金额不能小于已确定的首付款');
}
OrderService::applyOrderAmount($finalOrder, $finalAmount);
}
//获取签约订单用于支付
$order = OrderService::getDemandOrder($demand->parent_order_no, $contractType);
@@ -202,16 +202,19 @@ class OrderService
}
$winBid = DemandBiddingService::getOne(['demand_id' => $demandId, 'bidding_status' => DemandBiddingService::STATUS_BIDDING_WIN]);
$orderInfo = self::where(['parent_order_no' => $demand->parent_order_no])->find();
$ratio = 0.5; //分期比率50%
// 首付为总金额 50% 四舍五入到整数元,尾款为剩余未付金额
list($firstAmount, $finalAmount) = self::splitDemandPayment($totalCost);
if (!$orderInfo) {
$promoterId = PromotionService::getPromoterId(intval($demand->user_id));
$installments = 2;//分期数
for ($i = 1; $i <= $installments; $i++) {
$total_amount = $i == 1 ? $totalCost * $ratio : $totalCost * (1 - $ratio);
$installments = [
self::ORDER_TYPE_FIRST_PAYMENT => $firstAmount,
self::ORDER_TYPE_FINAL_PAYMENT => $finalAmount,
];
foreach ($installments as $orderType => $total_amount) {
$goodsDataList = [[
'spu_id' => 0,
'sku_id' => 0,
'goods_name' => '需求签约-' . ($i == 1 ? '首付款' : '尾款') . $demand->parent_order_no,
'goods_name' => '需求签约-' . ($orderType == self::ORDER_TYPE_FIRST_PAYMENT ? '首付款' : '尾款') . $demand->parent_order_no,
'goods_picture_url' => 'https://soft-market.oss-cn-shenzhen.aliyuncs.com/images/contract.png',
'origin_price' => $total_amount,
'actual_price' => $total_amount,
@@ -229,7 +232,7 @@ class OrderService
'parent_order_no' => $demand->parent_order_no,
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
'order_type' => $i,
'order_type' => $orderType,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
'goods_amount' => $total_amount,
@@ -259,7 +262,7 @@ class OrderService
if (!$contractInfo) {
//创建合同数据
$firstContract = ContractService::getFirstContract($demandId);
$depositAmount = $firstContract ? $firstContract->deposit_amount : $formData['totalCost'] * $ratio;
$depositAmount = $firstContract ? $firstContract->deposit_amount : $firstAmount;
ContractService::createData($demandId, $winBid->id ?? 0, $depositAmount, $formData);
}
return $contractInfo;
@@ -268,6 +271,61 @@ class OrderService
}
}
/**
* 需求分期。首付为总金额的 50% 四舍五入到整数元,尾款为总额减去首付
* @param float|string $totalCost
* @return array [首付款, 尾款],均为两位小数字符串
* @throws \Exception
*/
public static function splitDemandPayment($totalCost)
{
$total = bcadd((string)$totalCost, '0', 2);
if (bccomp($total, '0.01', 2) < 0) {
throw new \Exception('合同金额不能小于0.01元');
}
$half = bcmul($total, '0.5', 4);
$first = bcadd(bcadd($half, '0.5', 0), '0', 2);
if (bccomp($first, $total, 2) === 1) {
$first = $total;
}
$final = bcsub($total, $first, 2);
return [$first, $final];
}
/**
* 把订单和订单商品金额改成指定金额
* @param \think\Model $order
* @param float|string $amount
* @return \think\Model
* @throws \Exception
*/
public static function applyOrderAmount($order, $amount)
{
$amount = bcadd((string)$amount, '0', 2);
$order->total_amount = $amount;
$order->goods_amount = $amount;
$order->payment_amount = $amount;
if ($order->save() === false) {
throw new \Exception('更新订单金额失败');
}
$goodsList = $order->orderGoods;
if ($goodsList) {
foreach ($goodsList as $goods) {
$goods->origin_price = $amount;
$goods->actual_price = $amount;
$goods->item_total_amount = $amount;
$goods->item_payment_amount = $amount;
$goods->goods_payment_price = $amount;
if ($goods->save() === false) {
throw new \Exception('更新订单商品金额失败');
}
}
}
return $order;
}
public static function getDemandOrder($parentOrderNo, $contractType)
{
$res = self::getOne(['parent_order_no' => $parentOrderNo, 'order_type' => $contractType], ['orderGoods', 'orderPayment']);
@@ -54,7 +54,10 @@ Page({
attachFiles: [],
// 签约条款
agreementChecked: false
}
},
firstAmount: '',
finalAmount: '',
costTips: '首付款按总费用的50%四舍五入到整数元,尾款为剩余未付金额'
},
/**
@@ -88,20 +91,65 @@ Page({
platformName: payPlatform === 'ios' ? 'iOS' : '安卓',
};
},
// 首付为总金额 50% 四舍五入到整数元,尾款为剩余未付金额。与后端 splitDemandPayment 一致
installmentPreview(amount) {
const text = String(amount == null ? '' : amount).trim();
if (!text || Number(text) <= 0) {
return { firstAmount: '', finalAmount: '' };
}
const parts = text.split('.');
const yuanPart = parseInt(parts[0], 10);
if (Number.isNaN(yuanPart)) {
return { firstAmount: '', finalAmount: '' };
}
const dec = (parts[1] || '0').padEnd(2, '0').slice(0, 2);
const fen = yuanPart * 100 + parseInt(dec, 10);
const firstYuan = Math.floor((fen + 100) / 200);
const firstFen = Math.min(firstYuan * 100, fen);
const finalFen = fen - firstFen;
const format = (value) => (value / 100).toFixed(2);
return {
firstAmount: format(firstFen),
finalAmount: format(finalFen),
};
},
buildCostTips(installment, income, commissionPercent) {
const percent = commissionPercent !== undefined ? commissionPercent : this.data.commissionPercent;
const splitText = installment.firstAmount
? `首付款${installment.firstAmount}元(总费用的50%四舍五入到整数元),尾款${installment.finalAmount}元(剩余未付金额)`
: '首付款按总费用的50%四舍五入到整数元,尾款为剩余未付金额';
if (this.data.source == 'dev') {
return `预计到手金额 ${income.actualAccount}元,已扣平台服务费${percent}%和${income.platformName}支付费率${income.payFeePercent}%。${splitText}`;
}
return splitText;
},
fillPaymentPreview(amount, incomePatch, commissionPercent) {
const income = this.incomePreview(amount, incomePatch);
const installment = this.installmentPreview(amount);
return {
...income,
...installment,
costTips: this.buildCostTips(installment, income, commissionPercent),
};
},
// 拉取平台服务费和支付费率,并重算预计到手金额
async loadCommissionRatio() {
const rates = await fetchIncomeRates();
const commissionPercent = toPercent(rates.commissionRatio);
this.setData({
commissionRatio: rates.commissionRatio,
commissionPercent: toPercent(rates.commissionRatio),
commissionPercent,
androidPayFeeRate: rates.androidPayFeeRate,
iosPayFeeRate: rates.iosPayFeeRate,
androidFeePercent: toPercent(rates.androidPayFeeRate),
iosFeePercent: toPercent(rates.iosPayFeeRate),
...this.incomePreview(this.data.formData?.totalCost, {
...this.fillPaymentPreview(this.data.formData?.totalCost, {
...rates,
payPlatform: this.data.payPlatform,
}),
}, commissionPercent),
});
},
@@ -235,7 +283,7 @@ Page({
onTotalCostChange(e) {
this.setData({
'formData.totalCost': e.detail.value,
...this.incomePreview(e.detail.value),
...this.fillPaymentPreview(e.detail.value),
});
},
@@ -559,7 +607,7 @@ Page({
'formData.taskContent': resData.task_content,
'formData.needDays': resData.need_days,
'formData.totalCost': resData.total_amount,
...this.incomePreview(resData.total_amount, { payPlatform: resData.pay_platform }),
...this.fillPaymentPreview(resData.total_amount, { payPlatform: resData.pay_platform }),
});
// 客户签约时,附件展示开发者签约时上传的附件,仅可预览,不可删除和上传
@@ -690,10 +738,13 @@ Page({
});
return;
}
const payAmount = res.data && res.data.payment_amount ? res.data.payment_amount : '';
wx.showModal({
title: '提示',
showCancel: false,
content: '签约完成,去支付首付款(总金额的50%)',
content: payAmount
? `签约完成,去支付首付款${payAmount}元。尾款为合同剩余未付金额`
: '签约完成,去支付首付款',
success: (res2) => {
if (res2.confirm) {
//支付预付款
@@ -64,7 +64,7 @@
/>
</view>
<t-input label="总费用" placeholder="请输入总费用" suffix="元" align="right" type="number" tips="{{source == 'dev' ? '预计到手金额 ' + actualAccount + '元,已扣平台服务费' + commissionPercent + '%和' + platformName + '支付费率' + payFeePercent + '%' : '总费用 = 首付款50% + 尾款50%'}}" disabled="{{source != 'dev'}}" value="{{formData.totalCost}}" bind:change="onTotalCostChange" />
<t-input label="总费用" placeholder="请输入总费用" suffix="元" align="right" type="number" tips="{{costTips}}" disabled="{{source != 'dev'}}" value="{{formData.totalCost}}" bind:change="onTotalCostChange" />
<t-input label="开发工期" placeholder="请输入开发工期" suffix="天" align="right" type="number" disabled="{{source != 'dev'}}" value="{{formData.needDays}}" bind:change="onDevelopmentPeriodChange" />
</view>