feat(finance): 添加采购应付收票核销功能及相关优化

- 在 EditForm.vue 中为回执单和发票图添加预览功能,支持 PDF 和图片格式
- 增加 PDF 预览对话框和图片预览对话框组件
- 优化附件显示逻辑,当无附件时展示 '-' 占位符
- 禁止订单审批弹窗点击遮罩关闭,防止误操作
- 调整应付单列表项目名称列宽及操作列宽度,提升界面布局合理性
- 修改生成收票单按钮显示条件,确保仅在未收票金额大于 0 时可见
- 移除付款单中的删除按钮,替换为生成收票单按钮
- 放宽发起付款按钮的显示限制,不再限定付款单类型
- 新增 IOmsPayableTicketWriteOffService 接口及其实现类,支持收票核销业务
- 添加 OmsPayableTicketWriteOff 实体类及相关 Mapper、Controller 层代码
- 实现用户手动收票核销与系统自动收票核销逻辑
- 为 MergePaymentDialog 添加禁止点击遮罩关闭属性,增强用户体验
- 在 IOmsPayablePaymentDetailService 和 IOmsPayableTicketDetailService 中新增批量更新方法
- 补充 MyBatis XML 映射文件中的 updateBatch SQL 语句
- 完善收票核销单据编号生成规则,确保唯一性和可读性
- 增强核销删除功能,实现关联数据的反核销处理和金额恢复逻辑
dev_1.0.1
chenhao 2025-12-18 17:21:27 +08:00
parent 9be3b62aaf
commit e7ed169f94
21 changed files with 871 additions and 20 deletions

View File

@ -106,6 +106,7 @@
<el-dialog <el-dialog
title="订单审批" title="订单审批"
:visible.sync="approveDialogVisible" :visible.sync="approveDialogVisible"
:close-on-click-modal="false"
custom-class="approve-dialog" custom-class="approve-dialog"
width="80%" width="80%"
top="5vh" top="5vh"

View File

@ -115,8 +115,11 @@
<el-table-column prop="paymentBillCode" label="采购付款单编号"></el-table-column> <el-table-column prop="paymentBillCode" label="采购付款单编号"></el-table-column>
<el-table-column label="回执单/退款图"> <el-table-column label="回执单/退款图">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.finAttachment && scope.row.finAttachment.fileName || '-'}} <span v-if="scope.row.finAttachment">
<el-button v-show="scope.row.finAttachment" type="primary" size="mini" @click="downloadFile(scope.row.finAttachment)"></el-button> <el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.finAttachment)"></el-button>
<el-button type="text" size="mini" icon="el-icon-download" @click="downloadFile(scope.row.finAttachment)"></el-button>
</span>
<span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -143,8 +146,11 @@
<el-table-column prop="ticketBillCode" label="采购收票单编号"></el-table-column> <el-table-column prop="ticketBillCode" label="采购收票单编号"></el-table-column>
<el-table-column label="发票图"> <el-table-column label="发票图">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.finAttachment && scope.row.finAttachment.fileName || '-'}} <span v-if="scope.row.finAttachment">
<el-button v-show="scope.row.finAttachment" type="primary" size="mini" @click="downloadFile(scope.row.finAttachment)"></el-button> <el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.finAttachment)"></el-button>
<el-button type="text" size="mini" icon="el-icon-download" @click="downloadFile(scope.row.finAttachment)"></el-button>
</span>
<span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -157,6 +163,14 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;max-height: 60vh" />
</el-dialog>
</div> </div>
<!-- <div slot="footer" class="dialog-footer">--> <!-- <div slot="footer" class="dialog-footer">-->
<!-- <el-button @click="handleClose"> </el-button>--> <!-- <el-button @click="handleClose"> </el-button>-->
@ -171,6 +185,7 @@ import PaymentPlan from './PaymentPlan.vue';
import ReceivingTicketPlan from './ReceivingTicketPlan.vue'; import ReceivingTicketPlan from './ReceivingTicketPlan.vue';
import { getPayable } from "@/api/finance/payable"; import { getPayable } from "@/api/finance/payable";
import ReceiptPlan from "@/views/finance/receivable/components/ReceiptPlan.vue"; import ReceiptPlan from "@/views/finance/receivable/components/ReceiptPlan.vue";
import request from '@/utils/request';
export default { export default {
name: "EditForm", name: "EditForm",
@ -194,7 +209,11 @@ export default {
return { return {
internalVisible: this.visible, // Local copy of the visible prop internalVisible: this.visible, // Local copy of the visible prop
activeTab: 'details', activeTab: 'details',
formData: {} formData: {},
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
}; };
}, },
watch: { watch: {
@ -220,6 +239,30 @@ export default {
this.formData = res.data; this.formData = res.data;
}); });
}, },
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(attachment) {
if (!attachment) return;
if (this.isPdf(attachment.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: attachment.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(attachment.filePath);
this.imagePreviewVisible = true;
}
},
downloadFile(attachment){ downloadFile(attachment){
if (attachment){ if (attachment){
const link = document.createElement('a'); const link = document.createElement('a');

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog title="合并发起付款单" :visible.sync="dialogVisible" width="80%" @close="handleClose" append-to-body> <el-dialog title="合并发起付款单" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false" @close="handleClose" append-to-body>
<div class="dialog-body"> <div class="dialog-body">
<el-form ref="form" :model="form" :inline="true" label-width="120px"> <el-form ref="form" :model="form" :inline="true" label-width="120px">
<el-row> <el-row>

View File

@ -125,7 +125,7 @@
<el-table v-loading="loading" :data="payableList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="payableList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" /> <el-table-column type="selection" width="50" />
<el-table-column label="项目编号" align="center" prop="projectCode" width="120" /> <el-table-column label="项目编号" align="center" prop="projectCode" width="120" />
<el-table-column label="项目名称" align="center" prop="projectName" width="150" /> <el-table-column label="项目名称" align="center" prop="projectName" width="260" />
<el-table-column label="应付单编号" align="center" prop="payableBillCode" width="150" /> <el-table-column label="应付单编号" align="center" prop="payableBillCode" width="150" />
<!-- <el-table-column label="生成时间" align="center" prop="createTime" width="180"/>--> <!-- <el-table-column label="生成时间" align="center" prop="createTime" width="180"/>-->
<el-table-column label="预计付款时间" align="center" prop="planPaymentDate" width="180"/> <el-table-column label="预计付款时间" align="center" prop="planPaymentDate" width="180"/>
@ -174,7 +174,7 @@
<el-table-column label="未付款金额" align="center" prop="unpaidPaymentAmount" width="120" /> <el-table-column label="未付款金额" align="center" prop="unpaidPaymentAmount" width="120" />
<el-table-column label="未收票金额" align="center" prop="unreceivedTicketAmount" width="120" /> <el-table-column label="未收票金额" align="center" prop="unreceivedTicketAmount" width="120" />
<!-- <el-table-column label="付款中金额" align="center" prop="payingAmount" width="120" />--> <!-- <el-table-column label="付款中金额" align="center" prop="payingAmount" width="120" />-->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160" fixed="right"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300" fixed="right">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
size="mini" size="mini"
@ -187,17 +187,18 @@
size="mini" size="mini"
type="text" type="text"
icon="el-icon-edit" icon="el-icon-edit"
v-show="scope.row.unpaidAmount!==0" v-show="scope.row.unpaidPaymentAmount>0"
@click="handleGeneratedPayment(scope.row)" @click="handleGeneratedPayment(scope.row)"
v-hasPermi="['finance:payable:edit']" v-hasPermi="['finance:payable:edit']"
>生成付款单</el-button> >生成付款单</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-delete" icon="el-icon-edit"
@click="handleDelete(scope.row)" v-show="scope.row.unreceivedTicketAmount>0"
v-hasPermi="['finance:payable:remove']" @click="handleGeneratedTicket(scope.row)"
>删除</el-button> v-hasPermi="['finance:payable:edit']"
>生成收票单</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -341,6 +342,10 @@ export default {
this.selectedPayableRows=[row] this.selectedPayableRows=[row]
this.handleMergeAndInitiatePayment() this.handleMergeAndInitiatePayment()
}, },
handleGeneratedTicket(row) {
this.selectedPayableRows=[row]
this.handleMergeAndInitiateReceipt()
},
/** 合并并发起付款单按钮操作 */ /** 合并并发起付款单按钮操作 */
handleMergeAndInitiatePayment() { handleMergeAndInitiatePayment() {
if (this.selectedPayableRows.length === 0) { if (this.selectedPayableRows.length === 0) {

View File

@ -186,7 +186,7 @@
size="mini" size="mini"
type="text" type="text"
icon="el-icon-position" icon="el-icon-position"
v-show="(scope.row.approveStatus==='0' || scope.row.approveStatus==='3') && scope.row.paymentBillType==='FROM_PAYABLE' " v-show="(scope.row.approveStatus==='0' || scope.row.approveStatus==='3')"
@click="applyPayment(scope.row)" @click="applyPayment(scope.row)"
>发起付款</el-button> >发起付款</el-button>
<el-button <el-button

View File

@ -0,0 +1,124 @@
package com.ruoyi.sip.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.OmsPayableTicketWriteOff;
import com.ruoyi.sip.dto.WriteOffRequestDto;
import com.ruoyi.sip.dto.WriteOffTicketRequestDto;
import com.ruoyi.sip.service.IOmsPayableTicketWriteOffService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* Controller
*
* @author zghz
* @date 2025-12-17
*/
@RestController
@RequestMapping("/finance/ticketWriteoff")
public class OmsPayableTicketWriteOffController extends BaseController
{
@Autowired
private IOmsPayableTicketWriteOffService omsPayableTicketWriteOffService;
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:list")
@GetMapping("/list")
public TableDataInfo list(OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
startPage();
List<OmsPayableTicketWriteOff> list = omsPayableTicketWriteOffService.selectOmsPayableTicketWriteOffList(omsPayableTicketWriteOff);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:export")
@Log(title = "采购应付收票核销单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
List<OmsPayableTicketWriteOff> list = omsPayableTicketWriteOffService.selectOmsPayableTicketWriteOffList(omsPayableTicketWriteOff);
ExcelUtil<OmsPayableTicketWriteOff> util = new ExcelUtil<OmsPayableTicketWriteOff>(OmsPayableTicketWriteOff.class);
util.exportExcel(response, list, "采购应付收票核销单数据");
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:query")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return AjaxResult.success(omsPayableTicketWriteOffService.selectOmsPayableTicketWriteOffById(id));
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:add")
@Log(title = "采购应付收票核销单", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
return toAjax(omsPayableTicketWriteOffService.insertOmsPayableTicketWriteOff(omsPayableTicketWriteOff));
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:edit")
@Log(title = "采购应付收票核销单", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
return toAjax(omsPayableTicketWriteOffService.updateOmsPayableTicketWriteOff(omsPayableTicketWriteOff));
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:remove")
@Log(title = "采购应付收票核销单", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(omsPayableTicketWriteOffService.deleteOmsPayableTicketWriteOffByIds(ids));
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:add")
@Log(title = "采购应付收票核销", businessType = BusinessType.INSERT)
@PostMapping("/userTicketWriteOff")
public AjaxResult userTicketWriteOff(@RequestBody WriteOffRequestDto writeOffRequestDto)
{
Long writeOffId = omsPayableTicketWriteOffService.userTicketWriteOff(writeOffRequestDto);
return AjaxResult.success(writeOffId);
}
/**
*
*/
@RequiresPermissions("sip:ticketWriteoff:add")
@Log(title = "采购应付收票自动核销", businessType = BusinessType.INSERT)
@PostMapping("/autoTicketWriteOff")
public AjaxResult autoTicketWriteOff(@RequestBody WriteOffTicketRequestDto writeOffRequestDto)
{
omsPayableTicketWriteOffService.autoTicketWriteOff(writeOffRequestDto);
return AjaxResult.success();
}
}

View File

@ -0,0 +1,101 @@
package com.ruoyi.sip.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* oms_payable_ticket_write_off
*
* @author zghz
* @date 2025-12-17
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class OmsPayableTicketWriteOff extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 核销单编号 */
@Excel(name = "核销单编号")
private String writeOffCode;
/** 核销类型 AUTO:系统自动核销 USER:用户手动核销 */
@Excel(name = "核销类型")
private String writeOffType;
/** 收票单编号 */
@Excel(name = "收票单编号")
private String ticketBillCode;
private List<String> ticketBillCodeList;
/** 供应商编码 */
@Excel(name = "供应商编码")
private String vendorCode;
private String vendorName;
/** 本次核销总金额 */
@Excel(name = "本次核销总金额")
private BigDecimal writeOffAmount ;
/** 本次核销未税总金额 */
@Excel(name = "本次核销未税总金额")
private BigDecimal writeOffAmountWithoutTax ;
/** 本次核销税额 */
@Excel(name = "本次核销税额")
private BigDecimal writeOffTaxAmount ;
/** 核销时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "核销时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date writeOffTime;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 创建人 */
private String createBy;
private String createByName;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新人 */
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
private List<PaymentBillPayableDetailDTO> detailList;
private OmsTicketBill ticketBill;
@Getter
public enum WriteOffTypeEnum {
AUTO("AUTO", "自动核销"),
USER("USER", "手动核销"),
;
private final String code;
private final String desc;
WriteOffTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
}
}

View File

@ -0,0 +1,42 @@
package com.ruoyi.sip.dto;
import com.ruoyi.sip.domain.OmsPayablePaymentDetail;
import com.ruoyi.sip.domain.OmsPayableTicketDetail;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* DTO
*
* @author zghz
* @date 2025-12-16
*/
@Data
public class WriteOffTicketRequestDto
{
/**
* ID
*/
@NotNull(message = "收票单ID不能为空")
private Long ticketBillId;
/**
*
*/
private List<OmsPayableTicketDetail> detailList;
/**
*
*/
@NotBlank(message = "供应商代码不能为空")
private String vendorCode;
/**
*
*/
private String remark;
}

View File

@ -20,4 +20,6 @@ public interface OmsPayablePaymentDetailMapper {
int deleteByWriteOffIds(@Param("ids") Long[] ids); int deleteByWriteOffIds(@Param("ids") Long[] ids);
List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> writeOffIds); List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> writeOffIds);
void updateBatch(List<OmsPayablePaymentDetail> updateList);
} }

View File

@ -74,4 +74,7 @@ public interface OmsPayableTicketDetailMapper
List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> longs); List<PaymentBillPayableDetailDTO> listPayableByWriteOffId(List<Long> longs);
void deleteByWriteOffIds(Long[] ids); void deleteByWriteOffIds(Long[] ids);
void updateBatch(List<OmsPayableTicketDetail> updateList);
} }

View File

@ -0,0 +1,77 @@
package com.ruoyi.sip.mapper;
import java.util.List;
import com.ruoyi.sip.domain.OmsPayableTicketWriteOff;
/**
* Mapper
*
* @author zghz
* @date 2025-12-17
*/
public interface OmsPayableTicketWriteOffMapper
{
/**
*
*
* @param id
* @return
*/
public OmsPayableTicketWriteOff selectOmsPayableTicketWriteOffById(Long id);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public List<OmsPayableTicketWriteOff> selectOmsPayableTicketWriteOffList(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public int insertOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public int updateOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param id
* @return
*/
public int deleteOmsPayableTicketWriteOffById(Long id);
/**
*
*
* @param ids
* @return
*/
public int deleteOmsPayableTicketWriteOffByIds(Long[] ids);
/**
* ID
*
* @param ids ID
* @return
*/
List<OmsPayableTicketWriteOff> listByIds(Long[] ids);
/**
*
*
* @param codePrefix
* @return
*/
int selectMaxCodeByPrefix(String codePrefix);
}

View File

@ -26,5 +26,8 @@ public interface IOmsPayablePaymentDetailService {
int deleteByWriteOffIds(Long[] ids); int deleteByWriteOffIds(Long[] ids);
void updateBatch(List<OmsPayablePaymentDetail> updateList);
// List<OmsPayableWriteOffDetail> listWriteOffByPaymentCode(List<String> ); // List<OmsPayableWriteOffDetail> listWriteOffByPaymentCode(List<String> );
} }

View File

@ -80,4 +80,6 @@ public interface IOmsPayableTicketDetailService
void deleteByWriteOffIds(Long[] ids); void deleteByWriteOffIds(Long[] ids);
void insertBatch(List<OmsPayableTicketDetail> detailList); void insertBatch(List<OmsPayableTicketDetail> detailList);
void updateBatch(List<OmsPayableTicketDetail> updateList);
} }

View File

@ -0,0 +1,83 @@
package com.ruoyi.sip.service;
import java.util.List;
import com.ruoyi.sip.domain.OmsPayableTicketWriteOff;
import com.ruoyi.sip.dto.WriteOffRequestDto;
import com.ruoyi.sip.dto.WriteOffTicketRequestDto;
/**
* Service
*
* @author zghz
* @date 2025-12-17
*/
public interface IOmsPayableTicketWriteOffService
{
/**
*
*
* @param id
* @return
*/
public OmsPayableTicketWriteOff selectOmsPayableTicketWriteOffById(Long id);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public List<OmsPayableTicketWriteOff> selectOmsPayableTicketWriteOffList(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public int insertOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
public int updateOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff);
/**
*
*
* @param ids
* @return
*/
public int deleteOmsPayableTicketWriteOffByIds(Long[] ids);
/**
*
*
* @param id
* @return
*/
public int deleteOmsPayableTicketWriteOffById(Long id);
/**
*
* @param writeOffRequestDto
* @return ID
*/
public Long userTicketWriteOff(WriteOffRequestDto writeOffRequestDto);
/**
*
* @param writeOffRequestDto
*/
public void autoTicketWriteOff(WriteOffTicketRequestDto writeOffRequestDto);
/**
*
* @param collect
* @return
*/
List<OmsPayableTicketWriteOff> listByTicketBillCodeList(List<String> collect);
}

View File

@ -149,4 +149,9 @@ public class OmsPayablePaymentDetailServiceImpl implements IOmsPayablePaymentDet
} }
return omsPayablePaymentDetailMapper.deleteByWriteOffIds(ids); return omsPayablePaymentDetailMapper.deleteByWriteOffIds(ids);
} }
@Override
public void updateBatch(List<OmsPayablePaymentDetail> updateList) {
omsPayablePaymentDetailMapper.updateBatch(updateList);
}
} }

View File

@ -210,4 +210,9 @@ public class OmsPayableTicketDetailServiceImpl implements IOmsPayableTicketDetai
public void insertBatch(List<OmsPayableTicketDetail> detailList) { public void insertBatch(List<OmsPayableTicketDetail> detailList) {
omsPayableTicketDetailMapper.insertBatch(detailList); omsPayableTicketDetailMapper.insertBatch(detailList);
} }
@Override
public void updateBatch(List<OmsPayableTicketDetail> updateList) {
omsPayableTicketDetailMapper.updateBatch(updateList);
}
} }

View File

@ -0,0 +1,332 @@
package com.ruoyi.sip.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.sip.domain.OmsPayablePaymentDetail;
import com.ruoyi.sip.domain.OmsPayableTicketDetail;
import com.ruoyi.sip.domain.OmsPayableTicketWriteOff;
import com.ruoyi.sip.domain.OmsTicketBill;
import com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO;
import com.ruoyi.sip.dto.WriteOffRequestDto;
import com.ruoyi.sip.dto.WriteOffTicketRequestDto;
import com.ruoyi.sip.mapper.OmsPayableTicketWriteOffMapper;
import com.ruoyi.sip.service.IOmsPayableBillService;
import com.ruoyi.sip.service.IOmsPayableTicketDetailService;
import com.ruoyi.sip.service.IOmsPayableTicketWriteOffService;
import com.ruoyi.sip.service.IOmsTicketBillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.*;
/**
* Service
*
* @author zghz
* @date 2025-12-17
*/
@Service
public class OmsPayableTicketWriteOffServiceImpl implements IOmsPayableTicketWriteOffService
{
@Autowired
private OmsPayableTicketWriteOffMapper omsPayableTicketWriteOffMapper;
@Autowired
private IOmsTicketBillService omsTicketBillService;
@Autowired
private IOmsPayableTicketDetailService omsPayableTicketDetailService;
@Autowired
private IOmsPayableBillService payableBillService;
/**
*
*
* @param id
* @return
*/
@Override
public OmsPayableTicketWriteOff selectOmsPayableTicketWriteOffById(Long id)
{
OmsPayableTicketWriteOff omsPayableTicketWriteOff = omsPayableTicketWriteOffMapper.selectOmsPayableTicketWriteOffById(id);
OmsTicketBill omsTicketBill = omsTicketBillService.selectOmsTicketBillByCode(omsPayableTicketWriteOff.getTicketBillCode());
omsPayableTicketWriteOff.setTicketBill(omsTicketBill);
List<PaymentBillPayableDetailDTO> detailDTOList = omsPayableTicketDetailService.listPayableByWriteOffId(Collections.singletonList(id));
omsPayableTicketWriteOff.setDetailList(detailDTOList);
return omsPayableTicketWriteOff;
}
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
@Override
public List<OmsPayableTicketWriteOff> selectOmsPayableTicketWriteOffList(OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
return omsPayableTicketWriteOffMapper.selectOmsPayableTicketWriteOffList(omsPayableTicketWriteOff);
}
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
@Override
@Transactional
public int insertOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
// 生成核销单编号
omsPayableTicketWriteOff.setWriteOffCode(generateTicketWriteOffCode());
omsPayableTicketWriteOff.setWriteOffType(OmsPayableTicketWriteOff.WriteOffTypeEnum.USER.getCode());
omsPayableTicketWriteOff.setWriteOffTime(new Date());
omsPayableTicketWriteOff.setCreateTime(DateUtils.getNowDate());
omsPayableTicketWriteOff.setCreateBy(ShiroUtils.getUserId().toString());
return omsPayableTicketWriteOffMapper.insertOmsPayableTicketWriteOff(omsPayableTicketWriteOff);
}
/**
*
*
* @param omsPayableTicketWriteOff
* @return
*/
@Override
public int updateOmsPayableTicketWriteOff(OmsPayableTicketWriteOff omsPayableTicketWriteOff)
{
omsPayableTicketWriteOff.setUpdateTime(DateUtils.getNowDate());
return omsPayableTicketWriteOffMapper.updateOmsPayableTicketWriteOff(omsPayableTicketWriteOff);
}
/**
*
*
* @param ids
* @return
*/
@Override
@Transactional
public int deleteOmsPayableTicketWriteOffByIds(Long[] ids)
{
if (ids == null || ids.length == 0) {
return 0;
}
// 获取要删除的核销单列表
List<OmsPayableTicketWriteOff> omsPayableTicketWriteOffs = omsPayableTicketWriteOffMapper.listByIds(ids);
if (CollUtil.isEmpty(omsPayableTicketWriteOffs)) {
return 0;
}
// 获取核销单对应的收票详情记录
List<OmsPayableTicketDetail> omsPayableTicketDetails =
omsPayableTicketDetailService.listByWriteOffIds(ids);
// 反核销处理:恢复收票单状态
omsTicketBillService.returnTicketWriteOff(omsPayableTicketWriteOffs.stream()
.map(OmsPayableTicketWriteOff::getTicketBillCode).collect(java.util.stream.Collectors.toList()),
omsPayableTicketDetails);
// 删除对应的收票详情记录
omsPayableTicketDetailService.deleteByWriteOffIds(ids);
// 获取涉及的应付单ID用于更新应付单的收票金额
List<Long> payableBillIds = omsPayableTicketDetails.stream()
.map(OmsPayableTicketDetail::getPayableBillId)
.distinct()
.collect(java.util.stream.Collectors.toList());
// 更新应付单的收票金额
if (CollUtil.isNotEmpty(payableBillIds)) {
payableBillService.updateTicketAmount(payableBillIds);
}
// 删除核销单主记录
return omsPayableTicketWriteOffMapper.deleteOmsPayableTicketWriteOffByIds(ids);
}
/**
*
*
* @param id
* @return
*/
@Override
@Transactional
public int deleteOmsPayableTicketWriteOffById(Long id)
{
return deleteOmsPayableTicketWriteOffByIds(new Long[]{id});
}
/**
*
*/
@Transactional
@Override
public Long userTicketWriteOff(WriteOffRequestDto writeOffRequestDto) {
// if (CollUtil.isEmpty(writeOffRequestDto.getDetailList())) {
// throw new ServiceException("请选择核销明细");
// }
// // 获取收票单信息
// OmsTicketBill ticketBill = omsTicketBillService.selectOmsTicketBillById(writeOffRequestDto.getTicketBillId());
// if (ticketBill == null) {
// throw new ServiceException("收票单不存在");
// }
//
// // 创建核销主记录
// OmsPayableTicketWriteOff writeOff = new OmsPayableTicketWriteOff();
// writeOff.setWriteOffCode(generateTicketWriteOffCode());
// // 默认为用户手动核销
// writeOff.setWriteOffType(OmsPayableTicketWriteOff.WriteOffTypeEnum.USER.getCode());
// writeOff.setTicketBillCode(ticketBill.getTicketBillCode());
// writeOff.setVendorCode(writeOffRequestDto.getVendorCode());
// writeOff.setWriteOffTime(new Date());
// writeOff.setRemark(writeOffRequestDto.getRemark());
//
// // 计算总金额和相关税额
// BigDecimal totalAmount = BigDecimal.ZERO;
// BigDecimal totalAmountWithoutTax = BigDecimal.ZERO;
// BigDecimal totalTaxAmount = BigDecimal.ZERO;
//
// for (OmsPayableTicketDetail item : writeOffRequestDto.getDetailList()) {
// totalAmount = totalAmount.add(item.getPaymentAmount());
// if (item.getPaymentAmountWithoutTax() != null) {
// totalAmountWithoutTax = totalAmountWithoutTax.add(item.getPaymentAmountWithoutTax());
// }
// if (item.getPaymentAmountTax() != null) {
// totalTaxAmount = totalTaxAmount.add(item.getPaymentAmountTax());
// }
// }
//
// writeOff.setWriteOffAmount(totalAmount);
// writeOff.setWriteOffAmountWithoutTax(totalAmountWithoutTax);
// writeOff.setWriteOffTaxAmount(totalTaxAmount);
//
// // 设置创建时间
// writeOff.setCreateTime(new Date());
// writeOff.setCreateBy(ShiroUtils.getUserId().toString());
// writeOff.setUpdateTime(new Date());
// // 保存核销主记录
// omsPayableTicketWriteOffMapper.insertOmsPayableTicketWriteOff(writeOff);
//
// // 保存核销明细
// for (OmsPayableTicketDetail omsPayableTicketDetail : writeOffRequestDto.getDetailList()) {
// omsPayableTicketDetail.setPayableDetailType(OmsPayableTicketDetail.PayableDetailTypeEnum.TICKET_WRITE_OFF.getCode());
// omsPayableTicketDetail.setPaymentTime(DateUtils.getNowDate());
// omsPayableTicketDetail.setCreateBy(ShiroUtils.getUserId().toString());
// omsPayableTicketDetail.setWriteOffId(writeOff.getId());
// }
// omsPayableTicketDetailService.insertBatch(writeOffRequestDto.getDetailList());
// // 更新对应应付单的收票金额
// payableBillService.updateTicketAmount(writeOffRequestDto.getDetailList().stream()
// .map(OmsPayableTicketDetail::getPayableBillId).distinct()
// .collect(java.util.stream.Collectors.toList()));
// return writeOff.getId();
return null;
}
private String generateTicketWriteOffCode() {
String prefix = "SPHX";
// 查询当天已有的最大序列号
String codePrefix = prefix + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_PATTERN);
int maxSequence = omsPayableTicketWriteOffMapper.selectMaxCodeByPrefix(codePrefix);
// 生成新的序列号
int newSequence = maxSequence + 1;
// 序列号补零到4位
String sequenceStr = String.format("%04d", newSequence);
return codePrefix + sequenceStr;
}
/**
*
*/
@Override
@Transactional
public void autoTicketWriteOff(WriteOffTicketRequestDto writeOffRequestDto) {
if (CollUtil.isEmpty(writeOffRequestDto.getDetailList())) {
throw new ServiceException("请选择核销明细");
}
// 获取收票单信息
OmsTicketBill ticketBill = omsTicketBillService.selectOmsTicketBillById(writeOffRequestDto.getTicketBillId());
if (ticketBill == null) {
throw new ServiceException("收票单不存在");
}
//
// // 创建核销主记录
OmsPayableTicketWriteOff writeOff = new OmsPayableTicketWriteOff();
writeOff.setWriteOffCode(generateTicketWriteOffCode());
writeOff.setWriteOffType(OmsPayableTicketWriteOff.WriteOffTypeEnum.AUTO.getCode());
writeOff.setTicketBillCode(ticketBill.getTicketBillCode());
writeOff.setVendorCode(writeOffRequestDto.getVendorCode());
writeOff.setWriteOffTime(new Date());
writeOff.setRemark(writeOffRequestDto.getRemark());
//
// // 计算总金额和相关税额
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal totalAmountWithoutTax = BigDecimal.ZERO;
BigDecimal totalTaxAmount = BigDecimal.ZERO;
for (OmsPayableTicketDetail item : writeOffRequestDto.getDetailList()) {
totalAmount = totalAmount.add(item.getPaymentAmount());
if (item.getPaymentAmountWithoutTax() != null) {
totalAmountWithoutTax = totalAmountWithoutTax.add(item.getPaymentAmountWithoutTax());
}
if (item.getPaymentAmountTax() != null) {
totalTaxAmount = totalTaxAmount.add(item.getPaymentAmountTax());
}
}
writeOff.setWriteOffAmount(totalAmount);
writeOff.setWriteOffAmountWithoutTax(totalAmountWithoutTax);
writeOff.setWriteOffTaxAmount(totalTaxAmount);
// 设置创建时间
writeOff.setCreateTime(new Date());
writeOff.setCreateBy(ShiroUtils.getUserId().toString());
writeOff.setUpdateTime(new Date());
// 保存核销主记录
omsPayableTicketWriteOffMapper.insertOmsPayableTicketWriteOff(writeOff);
List<OmsPayableTicketDetail> updateList = new ArrayList<>();
// 保存核销明细
// 保存核销明细
for (OmsPayableTicketDetail omsPayableTicketDetail : writeOffRequestDto.getDetailList()) {
OmsPayableTicketDetail temp = new OmsPayableTicketDetail();
temp.setId(omsPayableTicketDetail.getId());
temp.setWriteOffId(writeOff.getId());
updateList.add(temp);
}
if (CollUtil.isNotEmpty(updateList)) {
omsPayableTicketDetailService.updateBatch(updateList);
}
// 更新对应应付单的收票金额
payableBillService.updateTicketAmount(writeOffRequestDto.getDetailList().stream()
.map(OmsPayableTicketDetail::getPayableBillId).distinct()
.collect(java.util.stream.Collectors.toList()));
}
@Override
public List<OmsPayableTicketWriteOff> listByTicketBillCodeList(List<String> collect) {
OmsPayableTicketWriteOff omsPayableTicketWriteOff = new OmsPayableTicketWriteOff();
omsPayableTicketWriteOff.setTicketBillCodeList(collect);
return omsPayableTicketWriteOffMapper.selectOmsPayableTicketWriteOffList(omsPayableTicketWriteOff);
}
// 添加Mapper方法用于查询核销单列表
public List<OmsPayableTicketWriteOff> selectOmsPayableTicketWriteOffListByIds(Long[] ids) {
return omsPayableTicketWriteOffMapper.listByIds(ids);
}
}

View File

@ -295,14 +295,17 @@ public class OmsPayableWriteOffServiceImpl implements IOmsPayableWriteOffService
// 保存核销主记录 // 保存核销主记录
omsPayableWriteOffMapper.insertOmsPayableWriteOff(writeOff); omsPayableWriteOffMapper.insertOmsPayableWriteOff(writeOff);
List<OmsPayablePaymentDetail> updateList = new ArrayList<>();
// 保存核销明细 // 保存核销明细
for (OmsPayablePaymentDetail omsPayablePaymentDetail : writeOffRequestDto.getDetailList()) { for (OmsPayablePaymentDetail omsPayablePaymentDetail : writeOffRequestDto.getDetailList()) {
omsPayablePaymentDetail.setPayableDetailType(OmsPayablePaymentDetail.PayableDetailTypeEnum.WRITE_OFF.getCode()); OmsPayablePaymentDetail temp = new OmsPayablePaymentDetail();
omsPayablePaymentDetail.setPaymentTime(DateUtils.getNowDate()); temp.setId(omsPayablePaymentDetail.getId());
omsPayablePaymentDetail.setCreateBy(ShiroUtils.getUserId().toString()); temp.setWriteOffId(writeOff.getId());
omsPayablePaymentDetail.setWriteOffId(writeOff.getId()); updateList.add(temp);
}
if (CollUtil.isNotEmpty(updateList)) {
omsPayablePaymentDetailService.updateBatch(updateList);
} }
omsPayablePaymentDetailService.insertBatch( writeOffRequestDto.getDetailList());
payableBillService.updatePaymentAmount(writeOffRequestDto.getDetailList().stream().map(OmsPayablePaymentDetail::getPayableBillId).distinct().collect(Collectors.toList())); payableBillService.updatePaymentAmount(writeOffRequestDto.getDetailList().stream().map(OmsPayablePaymentDetail::getPayableBillId).distinct().collect(Collectors.toList()));
} }

View File

@ -66,6 +66,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
sysdate()) sysdate())
</foreach> </foreach>
</insert> </insert>
<update id="updateBatch">
<foreach item="item" collection="list" separator=";" index="">
update oms_payable_payment_detail
<set>
<if test="item.writeOffId != null">write_off_id = #{item.writeOffId},</if>
</set>
where id = #{item.id}
</foreach>
</update>
<select id="list" resultType="com.ruoyi.sip.domain.OmsPayablePaymentDetail"> <select id="list" resultType="com.ruoyi.sip.domain.OmsPayablePaymentDetail">
SELECT SELECT

View File

@ -419,7 +419,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
sum( pre_residue_amount ) pre_residue_amount,vendor_code sum( pre_residue_amount ) pre_residue_amount,vendor_code
FROM FROM
oms_payment_bill oms_payment_bill
where vendor_code in where payment_status='2' and vendor_code in
<foreach item="item" collection="list" separator="," open="(" close=")" index="index"> <foreach item="item" collection="list" separator="," open="(" close=")" index="index">
#{item} #{item}
</foreach> </foreach>

View File

@ -305,6 +305,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim> </trim>
where id = #{id} where id = #{id}
</update> </update>
<update id="updateBatch">
<foreach item="item" collection="list">
update oms_payable_ticket_detail
<trim prefix="SET" suffixOverrides=",">
<if test="item.writeOffId != null">
write_off_id = #{item.writeOffId},
</if>
</trim>
where id=#{item.id}
</foreach>
</update>
<delete id="deleteOmsPayableTicketDetailById" parameterType="Long"> <delete id="deleteOmsPayableTicketDetailById" parameterType="Long">
delete from oms_payable_ticket_detail where id = #{id} delete from oms_payable_ticket_detail where id = #{id}