fix:付款单申请退款时可修改金额、可分多笔退款

dev_1.0.2
jiangpeng 2026-04-27 20:19:58 +08:00
parent 27ac07ad0a
commit 1735901ffe
13 changed files with 413 additions and 47 deletions

View File

@ -104,10 +104,11 @@ export function applyPaymentApi(data) {
} }
// 申请退款 // 申请退款
export function applyRefund(id) { export function applyRefund(data) {
return request({ return request({
url: '/finance/payment/applyRefund/'+id, url: '/finance/payment/applyRefund',
method: 'get', method: 'post',
data: data,
needLoading: true needLoading: true
}) })
} }

View File

@ -122,14 +122,14 @@
<el-dialog title="项目转移" :visible.sync="transferDialogVisible" width="500px" append-to-body> <el-dialog title="项目转移" :visible.sync="transferDialogVisible" width="500px" append-to-body>
<el-form ref="transferForm" :model="transferForm" :rules="transferRules" label-width="100px"> <el-form ref="transferForm" :model="transferForm" :rules="transferRules" label-width="100px">
<el-form-item label="原汇智负责人"> <el-form-item label="原汇智负责人" label-width="130px">
<el-input <el-input
v-model="transferForm.originalHzSupportUserName" v-model="transferForm.originalHzSupportUserName"
placeholder="原汇智负责人" placeholder="原汇智负责人"
readonly readonly
/> />
</el-form-item> </el-form-item>
<el-form-item label="新汇智负责人" prop="hzSupportUserName"> <el-form-item label="新汇智负责人" prop="hzSupportUserName" label-width="130px">
<el-input <el-input
v-model="transferForm.hzSupportUserName" v-model="transferForm.hzSupportUserName"
placeholder="请选择新汇智负责人" placeholder="请选择新汇智负责人"

View File

@ -233,7 +233,7 @@
size="mini" size="mini"
type="text" type="text"
icon="el-icon-refresh-right" icon="el-icon-refresh-right"
v-if="scope.row.paymentStatus === '2' && scope.row.refundStatus !== 'REFUND_APPLIED' && scope.row.paymentBillType !== 'REFUND'" v-if="canApplyRefund(scope.row)"
@click="handleApplyRefund(scope.row)" @click="handleApplyRefund(scope.row)"
>申请退款</el-button> >申请退款</el-button>
<el-button <el-button
@ -304,6 +304,56 @@
<el-button type="primary" @click="submitApplyPayment"> </el-button> <el-button type="primary" @click="submitApplyPayment"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 申请退款弹窗 -->
<el-dialog title="申请退款" :visible.sync="applyRefundOpen" width="600px" append-to-body>
<el-form ref="applyRefundForm" :model="applyRefundForm" :rules="applyRefundRules" label-width="120px">
<el-form-item label="退款方式" prop="paymentMethod">
<el-select v-model="applyRefundForm.paymentMethod" placeholder="请选择退款方式" style="width: 100%">
<el-option
v-for="dict in dict.type.payment_method"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="账户名称" prop="payName">
<el-input v-model="applyRefundForm.payName" placeholder="请输入账户名称" />
</el-form-item>
<el-form-item label="银行账号" prop="payBankNumber">
<el-input v-model="applyRefundForm.payBankNumber" placeholder="请输入银行账号" />
</el-form-item>
<el-form-item label="银行开户行" prop="payBankOpenAddress">
<el-input v-model="applyRefundForm.payBankOpenAddress" placeholder="请输入银行开户行" />
</el-form-item>
<el-form-item label="银行行号" prop="bankNumber">
<el-input v-model="applyRefundForm.bankNumber" placeholder="请输入银行行号" />
</el-form-item>
<el-form-item label="含税金额" prop="totalPriceWithTax">
<el-input v-model="applyRefundForm.totalPriceWithTax" @input="syncApplyRefundAmounts" />
</el-form-item>
<el-form-item label="剩余可退款金额">
<el-input :value="applyRefundMaxAmount" disabled />
</el-form-item>
<el-form-item label="未税金额">
<el-input v-model="applyRefundForm.totalPriceWithoutTax" disabled />
</el-form-item>
<el-form-item label="税额">
<el-input v-model="applyRefundForm.taxAmount" disabled />
</el-form-item>
<el-form-item label="税率">
<el-input :value="formatTaxRate(applyRefundTaxRate)" disabled />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :rows="3" v-model="applyRefundForm.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="applyRefundOpen = false"> </el-button>
<el-button type="primary" @click="submitApplyRefund"> </el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
@ -377,6 +427,25 @@ export default {
// //
applyPaymentOpen: false, applyPaymentOpen: false,
applyPaymentForm: {}, applyPaymentForm: {},
// 退
applyRefundOpen: false,
applyRefundTaxRate: null,
applyRefundOriginalTotalPriceWithTax: null,
applyRefundMaxAmount: null,
applyRefundCalcWithoutTax: false,
applyRefundCalcTaxAmount: false,
applyRefundForm: {},
applyRefundRules: {
paymentMethod: [{ required: true, message: "请选择退款方式", trigger: "change" }],
payName: [{ required: true, message: "请输入账户名称", trigger: "blur" }],
payBankNumber: [{ required: true, message: "请输入银行账号", trigger: "blur" }],
payBankOpenAddress: [{ required: true, message: "请输入银行开户行", trigger: "blur" }],
bankNumber: [{ required: true, message: "请输入银行行号", trigger: "blur" }],
totalPriceWithTax: [
{ required: true, message: "请输入含税金额", trigger: "blur" },
{ validator: (rule, value, callback) => this.validateApplyRefundTotalPrice(rule, value, callback), trigger: ["blur", "change"] }
]
}
}; };
}, },
created() { created() {
@ -393,7 +462,9 @@ export default {
payBankNumber: row.payBankNumber, payBankNumber: row.payBankNumber,
payBankOpenAddress: row.payBankOpenAddress, payBankOpenAddress: row.payBankOpenAddress,
bankNumber: row.bankNumber, bankNumber: row.bankNumber,
totalPriceWithTax: row.totalPriceWithTax totalPriceWithTax: row.totalPriceWithTax,
totalPriceWithoutTax: row.totalPriceWithoutTax,
taxAmount: row.taxAmount
}; };
this.applyPaymentOpen = true; this.applyPaymentOpen = true;
}, },
@ -501,12 +572,105 @@ export default {
}, },
/** 申请退款按钮操作 */ /** 申请退款按钮操作 */
handleApplyRefund(row) { handleApplyRefund(row) {
this.$modal.confirm('收款单退款申请,确认提交至财务审批吗?').then(() => { this.applyRefundTaxRate = row.taxRate || null;
return applyRefund(row.id); this.applyRefundOriginalTotalPriceWithTax = row.totalPriceWithTax || null;
}).then(() => { this.applyRefundMaxAmount = row.remainingRefundAmount || row.totalPriceWithTax || null;
this.getList(); this.applyRefundCalcWithoutTax = this.isValidRefundAmount(row.totalPriceWithoutTax);
this.$modal.msgSuccess("申请退款成功"); this.applyRefundCalcTaxAmount = this.isValidRefundAmount(row.taxAmount);
}).catch(() => {}); this.applyRefundForm = {
id: row.id,
paymentMethod: row.paymentMethod || null,
payName: row.payName || null,
payBankNumber: row.payBankNumber || null,
payBankOpenAddress: row.payBankOpenAddress || null,
bankNumber: row.bankNumber || null,
totalPriceWithTax: row.remainingRefundAmount || row.totalPriceWithTax || null,
totalPriceWithoutTax: null,
taxAmount: null,
remark: null
};
this.syncApplyRefundAmounts();
this.applyRefundOpen = true;
this.$nextTick(() => {
this.$refs["applyRefundForm"] && this.$refs["applyRefundForm"].clearValidate();
});
},
canApplyRefund(row) {
const remainingRefundAmount = Number(row.remainingRefundAmount);
return row.paymentBillType !== 'REFUND'
&& row.paymentStatus === '2'
&& ['WAIT_REFUNDED', 'PART_REFUNDED', null, undefined, ''].includes(row.refundStatus)
&& !Number.isNaN(remainingRefundAmount)
&& remainingRefundAmount > 0;
},
validateApplyRefundTotalPrice(rule, value, callback) {
if (value === null || value === undefined || value === '') {
callback();
return;
}
const currentValue = Number(value);
const maxValue = Number(this.applyRefundMaxAmount);
if (Number.isNaN(currentValue)) {
callback(new Error("含税金额格式不正确"));
return;
}
if (currentValue <= 0) {
callback(new Error("含税金额必须大于0"));
return;
}
if (!Number.isNaN(maxValue) && currentValue > maxValue) {
callback(new Error("含税金额不能大于剩余可退款金额"));
return;
}
callback();
},
syncApplyRefundAmounts() {
const totalPriceWithTax = Number(this.applyRefundForm.totalPriceWithTax);
const taxRate = Number(this.applyRefundTaxRate);
if (Number.isNaN(totalPriceWithTax) || this.applyRefundForm.totalPriceWithTax === '' || this.applyRefundForm.totalPriceWithTax === null) {
this.applyRefundForm.totalPriceWithoutTax = null;
this.applyRefundForm.taxAmount = null;
return;
}
if (Number.isNaN(taxRate)) {
this.applyRefundForm.totalPriceWithoutTax = this.applyRefundCalcWithoutTax ? totalPriceWithTax.toFixed(2) : null;
this.applyRefundForm.taxAmount = this.applyRefundCalcTaxAmount ? '0.00' : null;
return;
}
const taxAmount = (totalPriceWithTax * taxRate).toFixed(2);
const totalPriceWithoutTax = (totalPriceWithTax - Number(taxAmount)).toFixed(2);
this.applyRefundForm.taxAmount = this.applyRefundCalcTaxAmount ? taxAmount : null;
this.applyRefundForm.totalPriceWithoutTax = this.applyRefundCalcWithoutTax ? totalPriceWithoutTax : null;
},
isValidRefundAmount(value) {
const amount = Number(value);
return value !== null && value !== undefined && value !== '' && !Number.isNaN(amount) && amount !== 0;
},
submitApplyRefund() {
this.$refs["applyRefundForm"].validate(valid => {
if (!valid) {
return;
}
const submitData = {
id: this.applyRefundForm.id,
paymentMethod: this.applyRefundForm.paymentMethod,
payName: this.applyRefundForm.payName,
payBankNumber: this.applyRefundForm.payBankNumber,
payBankOpenAddress: this.applyRefundForm.payBankOpenAddress,
bankNumber: this.applyRefundForm.bankNumber,
totalPriceWithTax: this.applyRefundForm.totalPriceWithTax,
totalPriceWithoutTax: this.applyRefundForm.totalPriceWithoutTax,
taxAmount: this.applyRefundForm.taxAmount,
remark: this.applyRefundForm.remark
};
this.$modal.confirm('付款单退款申请,确认提交至财务审批吗?').then(() => {
return applyRefund(submitData);
}).then(() => {
this.applyRefundOpen = false;
this.getList();
this.$modal.msgSuccess("申请退款成功");
}).catch(() => {});
});
}, },
handleDelete(id){ handleDelete(id){
this.$modal.confirm('确认删除该笔预付单数据吗?').then(() => { this.$modal.confirm('确认删除该笔预付单数据吗?').then(() => {
@ -559,6 +723,10 @@ export default {
if (value === null || value === undefined || value === '') return ''; if (value === null || value === undefined || value === '') return '';
return value.substring(0, 10); return value.substring(0, 10);
}, },
formatTaxRate(value) {
if (value === null || value === undefined || value === '') return '';
return `${Number(value) * 100}%`;
},
handleUpdatePaymentBillSubmit(form) { handleUpdatePaymentBillSubmit(form) {
console.log(form); console.log(form);
updatePaymentBill(form).then(response => { updatePaymentBill(form).then(response => {

View File

@ -259,11 +259,11 @@ public class OmsPaymentBillController extends BaseController
/** /**
* 退 * 退
*/ */
@GetMapping("/applyRefund/{id}") @PostMapping("/applyRefund")
@ResponseBody @ResponseBody
public AjaxResult applyRefund(@PathVariable("id") Long id) { public AjaxResult applyRefund(@RequestBody OmsPaymentBill paymentBill) {
try { try {
return omsPaymentBillService.applyRefund(id); return omsPaymentBillService.applyRefund(paymentBill);
} catch (Exception e) { } catch (Exception e) {
logger.error("申请退款失败", e); logger.error("申请退款失败", e);
return AjaxResult.error("操作失败:" + e.getMessage()); return AjaxResult.error("操作失败:" + e.getMessage());

View File

@ -70,6 +70,9 @@ public class OmsPaymentBill extends BaseEntity
@Excel(name = "税额") @Excel(name = "税额")
private BigDecimal taxAmount; private BigDecimal taxAmount;
/** 税率 */
private BigDecimal taxRate;
/** 删除标志0代表存在 2代表删除 */ /** 删除标志0代表存在 2代表删除 */
private String delFlag; private String delFlag;
@ -130,6 +133,10 @@ public class OmsPaymentBill extends BaseEntity
/** 退款状态 */ /** 退款状态 */
private String refundStatus; private String refundStatus;
/** 已申请退款金额 */
private BigDecimal refundedAmount;
/** 剩余可退款金额 */
private BigDecimal remainingRefundAmount;
private String hzUserName; private String hzUserName;
private Long approveUser; private Long approveUser;
private Date applyTime; private Date applyTime;
@ -194,8 +201,8 @@ public class OmsPaymentBill extends BaseEntity
@Getter @Getter
public enum RefundStatusEnum { public enum RefundStatusEnum {
/** 应付单生成 */ REFUNDED("REFUND_APPLIED", "全部退款"),
REFUNDED("REFUND_APPLIED", "已申请退款"), PART_REFUNDED("PART_REFUNDED", "部分退款"),
WAIT_REFUNDED("WAIT_REFUNDED", "未退款") WAIT_REFUNDED("WAIT_REFUNDED", "未退款")
; ;

View File

@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Param;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
public interface OmsPayablePaymentDetailMapper { public interface OmsPayablePaymentDetailMapper {
public int insertOmsPayablePaymentDetail(OmsPayablePaymentDetail omsPayablePaymentDetail); public int insertOmsPayablePaymentDetail(OmsPayablePaymentDetail omsPayablePaymentDetail);
@ -19,6 +20,8 @@ public interface OmsPayablePaymentDetailMapper {
List<OmsPayablePaymentDetail> selectByPaymentPlanIds(@Param("paymentPlanIds") List<Long> paymentPlanIds); List<OmsPayablePaymentDetail> selectByPaymentPlanIds(@Param("paymentPlanIds") List<Long> paymentPlanIds);
List<Map<String, Object>> listPaymentBillTaxRate(@Param("paymentBillCodeList") List<String> paymentBillCodeList);
int clearWriteOffByWriteOffId(Long[] ids); int clearWriteOffByWriteOffId(Long[] ids);
List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> writeOffIds); List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> writeOffIds);

View File

@ -1,6 +1,7 @@
package com.ruoyi.sip.mapper; package com.ruoyi.sip.mapper;
import java.util.List; import java.util.List;
import java.util.Map;
import com.ruoyi.sip.domain.OmsPaymentBill; import com.ruoyi.sip.domain.OmsPaymentBill;
import com.ruoyi.sip.domain.dto.PaymentBillDetailDTO; import com.ruoyi.sip.domain.dto.PaymentBillDetailDTO;
@ -100,4 +101,7 @@ public interface OmsPaymentBillMapper
List<OmsPaymentBill> listPreResidueAmountByVendorCodeList(List<String> collect); List<OmsPaymentBill> listPreResidueAmountByVendorCodeList(List<String> collect);
List<Map<String, Object>> listRefundSummaryByOriginalBillIds(@Param("originalBillIds") List<Long> originalBillIds,
@Param("excludeApproveStatus") String excludeApproveStatus);
} }

View File

@ -5,6 +5,7 @@ import com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO;
import com.ruoyi.sip.domain.dto.ProjectPaymentBillDto; import com.ruoyi.sip.domain.dto.ProjectPaymentBillDto;
import java.util.Date; import java.util.Date;
import java.util.Map;
import java.util.List; import java.util.List;
public interface IOmsPayablePaymentDetailService { public interface IOmsPayablePaymentDetailService {
@ -21,6 +22,8 @@ public interface IOmsPayablePaymentDetailService {
List<OmsPayablePaymentDetail> listBypaymentCode(String paymentBillCode); List<OmsPayablePaymentDetail> listBypaymentCode(String paymentBillCode);
Map<String, java.math.BigDecimal> getPaymentBillTaxRateMap(List<String> paymentBillCodeList);
List<OmsPayablePaymentDetail> listByWriteOffIds(Long[] ids); List<OmsPayablePaymentDetail> listByWriteOffIds(Long[] ids);
void insertBatch(List<OmsPayablePaymentDetail> detailList); void insertBatch(List<OmsPayablePaymentDetail> detailList);

View File

@ -86,10 +86,10 @@ public interface IOmsPaymentBillService
/** /**
* 退 * 退
* *
* @param originalPaymentId ID * @param omsPaymentBill 退
* @return * @return
*/ */
public AjaxResult applyRefund(Long originalPaymentId); public AjaxResult applyRefund(OmsPaymentBill omsPaymentBill);
/** /**
* 退 * 退

View File

@ -125,6 +125,26 @@ public class OmsPayablePaymentDetailServiceImpl implements IOmsPayablePaymentDet
return omsPayablePaymentDetailMapper.list(omsPayablePaymentDetail); return omsPayablePaymentDetailMapper.list(omsPayablePaymentDetail);
} }
@Override
public Map<String, BigDecimal> getPaymentBillTaxRateMap(List<String> paymentBillCodeList) {
if (CollUtil.isEmpty(paymentBillCodeList)) {
return Collections.emptyMap();
}
List<Map<String, Object>> taxRateList = omsPayablePaymentDetailMapper.listPaymentBillTaxRate(paymentBillCodeList);
if (CollUtil.isEmpty(taxRateList)) {
return Collections.emptyMap();
}
Map<String, BigDecimal> taxRateMap = new HashMap<>();
for (Map<String, Object> item : taxRateList) {
Object paymentBillCode = item.get("paymentBillCode");
Object taxRate = item.get("taxRate");
if (paymentBillCode instanceof String) {
taxRateMap.put((String) paymentBillCode, taxRate instanceof BigDecimal ? (BigDecimal) taxRate : null);
}
}
return taxRateMap;
}
@Override @Override
public void insertBatch(List<OmsPayablePaymentDetail> detailList) { public void insertBatch(List<OmsPayablePaymentDetail> detailList) {
omsPayablePaymentDetailMapper.insertBatch(detailList); omsPayablePaymentDetailMapper.insertBatch(detailList);

View File

@ -86,6 +86,7 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
public OmsPaymentBill selectOmsPaymentBillById(Long id) public OmsPaymentBill selectOmsPaymentBillById(Long id)
{ {
OmsPaymentBill omsPaymentBill = omsPaymentBillMapper.selectOmsPaymentBillById(id); OmsPaymentBill omsPaymentBill = omsPaymentBillMapper.selectOmsPaymentBillById(id);
fillRefundSummary(Collections.singletonList(omsPaymentBill));
if (omsPaymentBill != null && StrUtil.isNotEmpty(omsPaymentBill.getFileId())) { if (omsPaymentBill != null && StrUtil.isNotEmpty(omsPaymentBill.getFileId())) {
List<Integer> idList = StrUtil.split(omsPaymentBill.getFileId(), ',') List<Integer> idList = StrUtil.split(omsPaymentBill.getFileId(), ',')
.stream() .stream()
@ -110,7 +111,24 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
@Override @Override
public List<OmsPaymentBill> selectOmsPaymentBillList(OmsPaymentBill omsPaymentBill) public List<OmsPaymentBill> selectOmsPaymentBillList(OmsPaymentBill omsPaymentBill)
{ {
return omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill); List<OmsPaymentBill> paymentBillList = omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill);
if (CollUtil.isEmpty(paymentBillList)) {
return paymentBillList;
}
fillRefundSummary(paymentBillList);
List<String> paymentBillCodeList = paymentBillList.stream()
.map(OmsPaymentBill::getPaymentBillCode)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.toList());
Map<String, BigDecimal> taxRateMap = detailService.getPaymentBillTaxRateMap(paymentBillCodeList);
if (CollUtil.isEmpty(taxRateMap)) {
return paymentBillList;
}
for (OmsPaymentBill paymentBill : paymentBillList) {
paymentBill.setTaxRate(taxRateMap.get(paymentBill.getPaymentBillCode()));
}
return paymentBillList;
} }
@Override @Override
@ -256,6 +274,95 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
return paymentBillType; return paymentBillType;
} }
private void fillRefundSummary(List<OmsPaymentBill> paymentBillList) {
if (CollUtil.isEmpty(paymentBillList)) {
return;
}
List<Long> originalBillIds = paymentBillList.stream()
.filter(Objects::nonNull)
.filter(item -> !OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode().equals(item.getPaymentBillType()))
.map(OmsPaymentBill::getId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (CollUtil.isEmpty(originalBillIds)) {
return;
}
Map<Long, BigDecimal> refundedAmountMap = getRefundedAmountMap(originalBillIds);
for (OmsPaymentBill paymentBill : paymentBillList) {
if (paymentBill == null || OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode().equals(paymentBill.getPaymentBillType()) || paymentBill.getId() == null) {
continue;
}
BigDecimal totalAmount = defaultAmount(paymentBill.getTotalPriceWithTax());
BigDecimal refundedAmount = refundedAmountMap.getOrDefault(paymentBill.getId(), BigDecimal.ZERO);
BigDecimal remainingRefundAmount = totalAmount.subtract(refundedAmount);
if (remainingRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
remainingRefundAmount = BigDecimal.ZERO;
}
paymentBill.setRefundedAmount(refundedAmount);
paymentBill.setRemainingRefundAmount(remainingRefundAmount);
paymentBill.setRefundStatus(resolveRefundStatus(totalAmount, refundedAmount));
}
}
private Map<Long, BigDecimal> getRefundedAmountMap(List<Long> originalBillIds) {
if (CollUtil.isEmpty(originalBillIds)) {
return Collections.emptyMap();
}
List<Map<String, Object>> summaryList = omsPaymentBillMapper.listRefundSummaryByOriginalBillIds(
originalBillIds, ApproveStatusEnum.APPROVE_REJECT.getCode());
if (CollUtil.isEmpty(summaryList)) {
return Collections.emptyMap();
}
Map<Long, BigDecimal> refundedAmountMap = new HashMap<>();
for (Map<String, Object> summary : summaryList) {
Object originalBillId = summary.get("originalBillId");
Object refundedAmount = summary.get("refundedAmount");
if (originalBillId == null) {
continue;
}
Long billId = Long.valueOf(String.valueOf(originalBillId));
BigDecimal amount = refundedAmount instanceof BigDecimal
? (BigDecimal) refundedAmount
: new BigDecimal(String.valueOf(refundedAmount));
refundedAmountMap.put(billId, amount);
}
return refundedAmountMap;
}
private void refreshOriginalBillRefundStatus(Long originalBillId) {
if (originalBillId == null) {
return;
}
OmsPaymentBill originalBill = omsPaymentBillMapper.selectOmsPaymentBillById(originalBillId);
if (originalBill == null || OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode().equals(originalBill.getPaymentBillType())) {
return;
}
BigDecimal totalAmount = defaultAmount(originalBill.getTotalPriceWithTax());
BigDecimal refundedAmount = getRefundedAmountMap(Collections.singletonList(originalBillId))
.getOrDefault(originalBillId, BigDecimal.ZERO);
OmsPaymentBill updateBill = new OmsPaymentBill();
updateBill.setId(originalBillId);
updateBill.setRefundStatus(resolveRefundStatus(totalAmount, refundedAmount));
omsPaymentBillMapper.updateOmsPaymentBill(updateBill);
}
private BigDecimal defaultAmount(BigDecimal amount) {
return Optional.ofNullable(amount).orElse(BigDecimal.ZERO).abs();
}
private String resolveRefundStatus(BigDecimal totalAmount, BigDecimal refundedAmount) {
BigDecimal total = defaultAmount(totalAmount);
BigDecimal refunded = defaultAmount(refundedAmount);
if (refunded.compareTo(BigDecimal.ZERO) <= 0) {
return OmsPaymentBill.RefundStatusEnum.WAIT_REFUNDED.getCode();
}
if (refunded.compareTo(total) < 0) {
return OmsPaymentBill.RefundStatusEnum.PART_REFUNDED.getCode();
}
return OmsPaymentBill.RefundStatusEnum.REFUNDED.getCode();
}
/** /**
* *
* *
@ -489,10 +596,14 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
} }
private void handleRejectOrder(String businessKey) { private void handleRejectOrder(String businessKey) {
OmsPaymentBill existBill = omsPaymentBillMapper.selectOmsPaymentBillByCode(businessKey);
OmsPaymentBill omsPaymentBill = new OmsPaymentBill(); OmsPaymentBill omsPaymentBill = new OmsPaymentBill();
omsPaymentBill.setPaymentBillCode(businessKey); omsPaymentBill.setPaymentBillCode(businessKey);
omsPaymentBill.setApproveStatus(ApproveStatusEnum.APPROVE_REJECT.getCode()); omsPaymentBill.setApproveStatus(ApproveStatusEnum.APPROVE_REJECT.getCode());
omsPaymentBillMapper.updateOmsPaymentBillByCode(omsPaymentBill); omsPaymentBillMapper.updateOmsPaymentBillByCode(omsPaymentBill);
if (existBill != null && OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode().equals(existBill.getPaymentBillType())) {
refreshOriginalBillRefundStatus(existBill.getOriginalBillId());
}
} }
@Override @Override
@ -554,7 +665,14 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
} }
@Override @Override
public AjaxResult applyRefund(Long originalPaymentId) { public AjaxResult applyRefund(OmsPaymentBill omsPaymentBill) {
if (omsPaymentBill == null || omsPaymentBill.getId() == null) {
return AjaxResult.error("原始付款单ID不能为空");
}
if (omsPaymentBill.getTotalPriceWithTax() == null || omsPaymentBill.getTotalPriceWithTax().compareTo(BigDecimal.ZERO) <= 0) {
return AjaxResult.error("退款金额必须大于0");
}
Long originalPaymentId = omsPaymentBill.getId();
// 1. 验证原始付款单 // 1. 验证原始付款单
OmsPaymentBill originalBill = selectOmsPaymentBillById(originalPaymentId); OmsPaymentBill originalBill = selectOmsPaymentBillById(originalPaymentId);
if (originalBill == null) { if (originalBill == null) {
@ -563,24 +681,32 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
if (!OmsPaymentBill.PaymentStatusEnum.PAYMENT.getCode().equals(originalBill.getPaymentStatus())) { if (!OmsPaymentBill.PaymentStatusEnum.PAYMENT.getCode().equals(originalBill.getPaymentStatus())) {
return AjaxResult.error("只有已付款的订单才能申请退款"); return AjaxResult.error("只有已付款的订单才能申请退款");
} }
if (OmsPaymentBill.RefundStatusEnum.REFUNDED.getCode().equals(originalBill.getRefundStatus())) { BigDecimal originalAmount = defaultAmount(originalBill.getTotalPriceWithTax());
return AjaxResult.error("该付款单已申请过退款,请勿重复操作"); BigDecimal appliedRefundAmount = getRefundedAmountMap(Collections.singletonList(originalPaymentId))
.getOrDefault(originalPaymentId, BigDecimal.ZERO);
BigDecimal remainingRefundAmount = originalAmount.subtract(appliedRefundAmount);
if (remainingRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
return AjaxResult.error("该付款单已无可退款金额");
}
BigDecimal currentRefundAmount = defaultAmount(omsPaymentBill.getTotalPriceWithTax());
if (currentRefundAmount.compareTo(remainingRefundAmount) > 0) {
return AjaxResult.error("当前退款金额超出可退款金额,剩余可退款金额:" + remainingRefundAmount);
} }
// 2. 创建新的退款单 // 2. 创建新的退款单
OmsPaymentBill refundBill = new OmsPaymentBill(); OmsPaymentBill refundBill = new OmsPaymentBill();
// 复制属性并取反金额 // 复制属性并取反金额
refundBill.setTotalPriceWithTax(originalBill.getTotalPriceWithTax().negate()); refundBill.setTotalPriceWithTax(currentRefundAmount.negate());
refundBill.setTotalPriceWithoutTax(originalBill.getTotalPriceWithoutTax().negate()); refundBill.setTotalPriceWithoutTax(defaultAmount(omsPaymentBill.getTotalPriceWithoutTax()).negate());
refundBill.setTaxAmount(originalBill.getTaxAmount().negate()); refundBill.setTaxAmount(defaultAmount(omsPaymentBill.getTaxAmount()).negate());
refundBill.setVendorCode(originalBill.getVendorCode()); refundBill.setVendorCode(originalBill.getVendorCode());
refundBill.setOrderCode(originalBill.getOrderCode()); refundBill.setOrderCode(originalBill.getOrderCode());
refundBill.setPayName(originalBill.getPayName()); refundBill.setPayName(StrUtil.isNotBlank(omsPaymentBill.getPayName()) ? omsPaymentBill.getPayName() : originalBill.getPayName());
refundBill.setPayBankNumber(originalBill.getPayBankNumber()); refundBill.setPayBankNumber(StrUtil.isNotBlank(omsPaymentBill.getPayBankNumber()) ? omsPaymentBill.getPayBankNumber() : originalBill.getPayBankNumber());
refundBill.setPayBankOpenAddress(originalBill.getPayBankOpenAddress()); refundBill.setPayBankOpenAddress(StrUtil.isNotBlank(omsPaymentBill.getPayBankOpenAddress()) ? omsPaymentBill.getPayBankOpenAddress() : originalBill.getPayBankOpenAddress());
refundBill.setBankNumber(originalBill.getBankNumber()); refundBill.setBankNumber(StrUtil.isNotBlank(omsPaymentBill.getBankNumber()) ? omsPaymentBill.getBankNumber() : originalBill.getBankNumber());
refundBill.setPaymentBillCode(generatePaymentBillCode()); refundBill.setPaymentBillCode(generatePaymentBillCode());
// 设置新属性 // 设置新属性
refundBill.setPaymentBillType(OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode()); refundBill.setPaymentBillType(OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode());
@ -588,14 +714,15 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
refundBill.setApproveStatus(ApproveStatusEnum.WAIT_APPROVE.getCode()); refundBill.setApproveStatus(ApproveStatusEnum.WAIT_APPROVE.getCode());
refundBill.setOriginalBillId(originalPaymentId); refundBill.setOriginalBillId(originalPaymentId);
refundBill.setPaymentTime(null); refundBill.setPaymentTime(null);
refundBill.setPaymentMethod(originalBill.getPaymentMethod()); refundBill.setPaymentMethod(StrUtil.isNotBlank(omsPaymentBill.getPaymentMethod()) ? omsPaymentBill.getPaymentMethod() : originalBill.getPaymentMethod());
refundBill.setRemark("退款-关联原付款单:" + originalBill.getPaymentBillCode()); refundBill.setRemark(StrUtil.isNotBlank(omsPaymentBill.getRemark())
? "退款-关联原付款单:" + originalBill.getPaymentBillCode() + "" + omsPaymentBill.getRemark()
: "退款-关联原付款单:" + originalBill.getPaymentBillCode());
insertOmsPaymentBill(refundBill); insertOmsPaymentBill(refundBill);
// 3. 更新原始付款单状态 // 3. 更新原始付款单状态
originalBill.setRefundStatus(OmsPaymentBill.RefundStatusEnum.REFUNDED.getCode()); refreshOriginalBillRefundStatus(originalPaymentId);
updateOmsPaymentBill(originalBill);
//4 创建付款明细 //4 创建付款明细
detailService.applyRefund(originalBill.getPaymentBillCode(),refundBill.getPaymentBillCode()); detailService.applyRefund(originalBill.getPaymentBillCode(),refundBill.getPaymentBillCode());
//5. 开始退款审批流程 //5. 开始退款审批流程
@ -628,8 +755,8 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
// 更新付款单 // 更新付款单
// 更新为“已付款” // 退款单上传退款图后,标记为已退款
paymentBill.setPaymentStatus(OmsPaymentBill.PaymentStatusEnum.PAYMENT.getCode()); paymentBill.setPaymentStatus(OmsPaymentBill.PaymentStatusEnum.REFUNDED.getCode());
paymentBill.setActualPaymentTime(DateUtils.getNowDate()); paymentBill.setActualPaymentTime(DateUtils.getNowDate());
updateOmsPaymentBill(paymentBill); updateOmsPaymentBill(paymentBill);
if (!paymentBill.getPaymentBillType().equals(OmsPaymentBill.PaymentBillTypeEnum.PRE_PAYMENT.getCode())) { if (!paymentBill.getPaymentBillType().equals(OmsPaymentBill.PaymentBillTypeEnum.PRE_PAYMENT.getCode())) {
@ -673,10 +800,7 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
// 撤销退款单 // 撤销退款单
deleteOmsPaymentBillById(existBill.getId()); deleteOmsPaymentBillById(existBill.getId());
detailService.deleteByPaymentCode(existBill.getPaymentBillCode()); detailService.deleteByPaymentCode(existBill.getPaymentBillCode());
OmsPaymentBill omsPaymentBill = new OmsPaymentBill(); refreshOriginalBillRefundStatus(existBill.getOriginalBillId());
omsPaymentBill.setId(existBill.getOriginalBillId());
omsPaymentBill.setRefundStatus(OmsPaymentBill.RefundStatusEnum.WAIT_REFUNDED.getCode());
updateOmsPaymentBill(omsPaymentBill);
}else{ }else{
if (OmsPaymentBill.PaymentStatusEnum.PAYMENT.getCode().equals(existBill.getPaymentStatus())){ if (OmsPaymentBill.PaymentStatusEnum.PAYMENT.getCode().equals(existBill.getPaymentStatus())){
return AjaxResult.error("该退款单已付款成功,无法撤销"); return AjaxResult.error("该退款单已付款成功,无法撤销");
@ -851,8 +975,9 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
@Override @Override
public OmsPaymentBill selectOmsPaymentBillByCode(String paymentBillCode) { public OmsPaymentBill selectOmsPaymentBillByCode(String paymentBillCode) {
OmsPaymentBill paymentBill = omsPaymentBillMapper.selectOmsPaymentBillByCode(paymentBillCode);
return omsPaymentBillMapper.selectOmsPaymentBillByCode(paymentBillCode); fillRefundSummary(Collections.singletonList(paymentBill));
return paymentBill;
} }
@Override @Override

View File

@ -121,6 +121,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach> </foreach>
ORDER BY t1.create_time DESC ORDER BY t1.create_time DESC
</select> </select>
<select id="listPaymentBillTaxRate" resultType="java.util.HashMap">
select
t1.payment_bill_code as paymentBillCode,
case
when count(distinct t2.tax_rate) = 1 then max(t2.tax_rate)
else null
end as taxRate
from oms_payable_payment_detail t1
left join oms_payable_bill t2 on t1.payable_bill_id = t2.id
where t1.payment_bill_code in
<foreach item="item" collection="paymentBillCodeList" separator="," open="(" close=")" index="">
#{item}
</foreach>
group by t1.payment_bill_code
</select>
<select id="listPayableByPaymentCode" resultType="com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO"> <select id="listPayableByPaymentCode" resultType="com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO">
select t1.payment_amount, t1.payable_bill_id, t2.payable_bill_code, t3.project_id, t4.project_name, t4.project_code, select t1.payment_amount, t1.payable_bill_id, t2.payable_bill_code, t3.project_id, t4.project_name, t4.project_code,
t2.total_price_with_tax,t2.product_type, t5.product_code, t5.model, t5.product_name t2.total_price_with_tax,t2.product_type, t5.product_code, t5.model, t5.product_name

View File

@ -35,7 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="payBankOpenAddress" column="pay_bank_open_address" /> <result property="payBankOpenAddress" column="pay_bank_open_address" />
<result property="bankNumber" column="bank_number" /> <result property="bankNumber" column="bank_number" />
<result property="fileId" column="file_id" /> <result property="fileId" column="file_id" />
<result property="originalBillId" column="original_bill_id" />
<result property="refundStatus" column="refund_status" /> <result property="refundStatus" column="refund_status" />
</resultMap> </resultMap>
@ -103,6 +103,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
pb.pay_bank_open_address, pb.pay_bank_open_address,
pb.bank_number, pb.bank_number,
pb.file_id, pb.file_id,
pb.original_bill_id,
pb.refund_status, pb.refund_status,
ovi.vendor_name, ovi.vendor_name,
ovi.pay_type, ovi.pay_type,
@ -272,6 +273,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="payBankOpenAddress != null">pay_bank_open_address,</if> <if test="payBankOpenAddress != null">pay_bank_open_address,</if>
<if test="bankNumber != null">bank_number,</if> <if test="bankNumber != null">bank_number,</if>
<if test="fileId != null">file_id,</if> <if test="fileId != null">file_id,</if>
<if test="originalBillId != null">original_bill_id,</if>
<if test="refundStatus != null">refund_status,</if> <if test="refundStatus != null">refund_status,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
@ -303,6 +305,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="payBankOpenAddress != null">#{payBankOpenAddress},</if> <if test="payBankOpenAddress != null">#{payBankOpenAddress},</if>
<if test="bankNumber != null">#{bankNumber},</if> <if test="bankNumber != null">#{bankNumber},</if>
<if test="fileId != null">#{fileId},</if> <if test="fileId != null">#{fileId},</if>
<if test="originalBillId != null">#{originalBillId},</if>
<if test="refundStatus != null">#{refundStatus},</if> <if test="refundStatus != null">#{refundStatus},</if>
</trim> </trim>
</insert> </insert>
@ -338,7 +341,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="payBankOpenAddress != null">pay_bank_open_address = #{payBankOpenAddress},</if> <if test="payBankOpenAddress != null">pay_bank_open_address = #{payBankOpenAddress},</if>
<if test="bankNumber != null">bank_number = #{bankNumber},</if> <if test="bankNumber != null">bank_number = #{bankNumber},</if>
<if test="fileId != null">file_id = #{fileId},</if> <if test="fileId != null">file_id = #{fileId},</if>
<if test="originalBillId != null">original_bill_id = #{originalBillId},</if>
<if test="refundStatus != null">refund_status = #{refundStatus},</if> <if test="refundStatus != null">refund_status = #{refundStatus},</if>
</trim> </trim>
where id = #{id} where id = #{id}
@ -372,7 +375,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="payBankNumber != null">pay_bank_number = #{payBankNumber},</if> <if test="payBankNumber != null">pay_bank_number = #{payBankNumber},</if>
<if test="payBankOpenAddress != null">pay_bank_open_address = #{payBankOpenAddress},</if> <if test="payBankOpenAddress != null">pay_bank_open_address = #{payBankOpenAddress},</if>
<if test="bankNumber != null">bank_number = #{bankNumber},</if> <if test="bankNumber != null">bank_number = #{bankNumber},</if>
<if test="originalBillId != null">original_bill_id = #{originalBillId},</if>
<if test="refundStatus != null">refund_status = #{refundStatus},</if> <if test="refundStatus != null">refund_status = #{refundStatus},</if>
</trim> </trim>
where payment_bill_code = #{paymentBillCode} where payment_bill_code = #{paymentBillCode}
@ -515,5 +518,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
vendor_code vendor_code
</select> </select>
<select id="listRefundSummaryByOriginalBillIds" resultType="java.util.HashMap">
select
original_bill_id as originalBillId,
ifnull(sum(abs(total_price_with_tax)), 0) as refundedAmount
from oms_payment_bill
where del_flag = '0'
and payment_bill_type = 'REFUND'
and original_bill_id in
<foreach item="item" collection="originalBillIds" separator="," open="(" close=")">
#{item}
</foreach>
<if test="excludeApproveStatus != null and excludeApproveStatus != ''">
and approve_status != #{excludeApproveStatus}
</if>
group by original_bill_id
</select>
</mapper> </mapper>