应付录入计功能开发

This commit is contained in:
lSun 2025-05-07 13:41:01 +08:00
parent 548103e265
commit e100624e91
6 changed files with 430 additions and 5 deletions

View File

@ -14,12 +14,18 @@ import com.bonus.boot.manager.manager.table.PageTableRequest;
import com.bonus.boot.manager.manager.table.PageTableResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -172,4 +178,287 @@ public class PayableController {
}
@PostMapping("/importExcel")
@ApiOperation(value = "导入Excel数据")
public R importExcel(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return R.fail("请选择要导入的Excel文件");
}
try (InputStream is = file.getInputStream()) {
Workbook workbook = new XSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 验证Excel模板是否正确
String validateResult = validateExcelTemplate(sheet);
if (validateResult != null) {
return R.fail(validateResult);
}
List<PayableBean> payableList = new ArrayList<>();
List<String> errorMsgs = new ArrayList<>();
// 从第二行开始读取数据跳过表头
for (int i = 2; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) {
continue;
}
PayableBean payable = new PayableBean();
boolean hasError = false;
// 校验并设置日期
String rq = getCellValueAsString(row.getCell(0));
if (rq == null || rq.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:日期不能为空");
hasError = true;
} else if (!rq.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
errorMsgs.add("" + (i + 1) + "日期格式不正确应为yyyy-MM-dd格式");
hasError = true;
} else {
try {
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
sdf.parse(rq);
payable.setRq(rq);
} catch (Exception e) {
errorMsgs.add("" + (i + 1) + "行:无效的日期");
hasError = true;
}
}
// 校验并设置单号
String pzh = getCellValueAsString(row.getCell(1));
if (pzh == null || pzh.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:单号不能为空");
hasError = true;
} else {
payable.setPzh(pzh);
}
// 校验并设置合同名称
String htmc = getCellValueAsString(row.getCell(2));
if (htmc == null || htmc.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:合同名称不能为空");
hasError = true;
} else if (!dao.checkHtmcExists(htmc)) {
errorMsgs.add("" + (i + 1) + "行:合同名称不存在");
hasError = true;
} else {
payable.setHtmc(htmc);
}
// 校验并设置供应商名称
String gysmc = getCellValueAsString(row.getCell(3));
if (gysmc == null || gysmc.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:供应商名称不能为空");
hasError = true;
} else if (!dao.checkGysmcExists(gysmc, htmc)) {
errorMsgs.add("" + (i + 1) + "行:供应商名称不存在或与合同不匹配");
hasError = true;
} else {
payable.setGysmc(gysmc);
}
// 校验并设置商品名称
String spmc = getCellValueAsString(row.getCell(4));
if (spmc == null || spmc.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:商品名称不能为空");
hasError = true;
} else {
String spid = dao.getSpidByName(spmc);
if (spid == null) {
errorMsgs.add("" + (i + 1) + "行:商品名称不存在");
hasError = true;
} else {
payable.setSpbh(spid);
}
}
// 校验并设置付款账号
String accountName = getCellValueAsString(row.getCell(5));
if (accountName == null || accountName.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:付款账号不能为空");
hasError = true;
} else {
Integer accountId = dao.getAccountIdByName(accountName);
if (accountId == null) {
errorMsgs.add("" + (i + 1) + "行:付款账号不存在");
hasError = true;
} else {
payable.setAccountId(accountId);
}
}
// 校验并设置金额
String je = getCellValueAsString(row.getCell(6));
if (je == null || je.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:金额不能为空");
hasError = true;
} else if (!je.matches("^[0-9]+(\\.[0-9]{1,2})?$")) {
errorMsgs.add("" + (i + 1) + "行:金额格式不正确");
hasError = true;
} else {
payable.setJe(new BigDecimal(je));
}
// 校验并设置人次
String personNum = getCellValueAsString(row.getCell(7));
if (personNum == null || personNum.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:人次不能为空");
hasError = true;
} else {
try {
// 处理可能带有小数点的数字
double num = Double.parseDouble(personNum);
int intValue = (int) num;
if (intValue != num || intValue < 1 || intValue > 999999) {
errorMsgs.add("" + (i + 1) + "人次必须为1-999999之间的正整数");
hasError = true;
} else {
payable.setPersonNum(intValue);
}
} catch (NumberFormatException e) {
errorMsgs.add("" + (i + 1) + "人次必须为1-999999之间的正整数");
hasError = true;
}
}
// 校验并设置发票号码
String fphm = getCellValueAsString(row.getCell(8));
if (fphm == null || fphm.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:发票号码不能为空");
hasError = true;
} else {
payable.setFphm(fphm);
}
// 校验并设置开票金额
String kpje = getCellValueAsString(row.getCell(9));
if (kpje == null || kpje.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:开票金额不能为空");
hasError = true;
} else if (!kpje.matches("^[0-9]+(\\.[0-9]{1,2})?$")) {
errorMsgs.add("" + (i + 1) + "行:开票金额格式不正确");
hasError = true;
} else {
payable.setKpje(new BigDecimal(kpje));
}
// 校验并设置收票摘要
String spzy = getCellValueAsString(row.getCell(10));
if (spzy == null || spzy.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:收票摘要不能为空");
hasError = true;
} else {
payable.setSpzy(spzy);
}
// 校验并设置制单人
String zdr = getCellValueAsString(row.getCell(11));
if (zdr == null || zdr.trim().isEmpty()) {
errorMsgs.add("" + (i + 1) + "行:制单人不能为空");
hasError = true;
} else {
String zdrid = dao.getZdridByName(zdr);
if (zdrid == null) {
errorMsgs.add("" + (i + 1) + "行:制单人不存在");
hasError = true;
} else {
payable.setZdr(zdrid);
}
}
// 只有当该行没有错误时才添加到列表
if (!hasError) {
payableList.add(payable);
}
}
// 如果有错误信息返回所有错误信息
if (!errorMsgs.isEmpty()) {
return R.fail(String.join("\n", errorMsgs));
}
// 没有错误时才进行保存操作
// service.batchSave(payableList);
for (PayableBean payable : payableList) {
service.save(payable);
}
return R.ok("导入成功");
} catch (IOException e) {
return R.fail("导入失败:" + e.getMessage());
}
}
/**
* 验证Excel模板是否正确
* @param sheet Excel工作表
* @return 如果模板正确返回null否则返回错误信息
*/
private String validateExcelTemplate(Sheet sheet) {
// 预期的表头
String[] expectedHeaders = {
"日期", "单号(凭证号)", "合同名称(项目名称)", "供应商名称",
"商品名称", "付款账号", "金额", "人次",
"发票号码", "开票金额", "收票摘要", "制单人"
};
// 获取第一行表头
Row headerRow = sheet.getRow(1);
if (headerRow == null) {
return "Excel文件格式错误未找到表头行";
}
// 检查列数是否匹配
if (headerRow.getPhysicalNumberOfCells() != expectedHeaders.length) {
return "Excel文件格式错误列数不匹配请使用正确的导入模板";
}
// 验证每一列的表头
for (int i = 0; i < expectedHeaders.length; i++) {
Cell cell = headerRow.getCell(i);
String headerValue = getCellValueAsString(cell);
if (headerValue == null || !headerValue.equals(expectedHeaders[i])) {
return String.format("Excel文件格式错误第%d列应为'%s',实际为'%s',请使用正确的导入模板",
(i + 1), expectedHeaders[i], headerValue == null ? "" : headerValue);
}
}
// 检查是否有数据行
if (sheet.getLastRowNum() < 1) {
return "Excel文件中没有数据";
}
return null;
}
private String getCellValueAsString(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return new java.text.SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());
}
// 处理数值类型去掉可能存在的.0后缀
double numericValue = cell.getNumericCellValue();
if (numericValue == (long) numericValue) {
return String.valueOf((long) numericValue);
}
return String.valueOf(numericValue);
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
default:
return "";
}
}
}

View File

@ -85,4 +85,14 @@ public interface PayableDao {
List<Integer> getHTIDByMC(String htmc);
List<PayAccountInfoBean> getBankAccount();
boolean checkHtmcExists(String htmc);
boolean checkGysmcExists(String gysmc, String htmc);
String getSpidByName(String spmc);
Integer getAccountIdByName(String accountName);
String getZdridByName(String zdr);
}

View File

@ -41,6 +41,7 @@ public class PayableServiceImpl implements PayableService {
@Override
public void save(PayableBean payableBean) {
System.err.println(payableBean);
LoginUser loginUser = UserUtil.getLoginUser();
String username = loginUser.getUsername();
String userId = dao.getIdByName(username);

View File

@ -134,6 +134,9 @@
<if test="params.zdr != null and params.zdr !='' ">
and a.zdr like concat ('%',#{params.zdr},'%')
</if>
<if test="params.je != null and params.je !='' ">
and a.je like concat ('%',#{params.je},'%')
</if>
<if test="params.startTime != null and params.startTime !=''and params.endTime != null and params.endTime !='' ">
and a.rq between #{params.startTime} and #{params.endTime}
</if>
@ -331,6 +334,11 @@
<if test="params.zdr != null and params.zdr !='' ">
and a.zdr like concat ('%',#{params.zdr},'%')
</if>
<if test="params.je != null and params.je !='' ">
and a.je like concat ('%',#{params.je},'%')
</if>
<if test="params.startTime != null and params.startTime !=''and params.endTime != null and params.endTime !='' ">
and a.rq between #{params.startTime} and #{params.endTime}
</if>
@ -759,6 +767,39 @@
select id,name
from ca_bm_pay_account_info where IS_ACTIVE = '1'
</select>
<select id="checkHtmcExists" resultType="java.lang.Boolean">
select DISTINCT count(1)
from ca_bm_supplier_info
where IS_ACTIVE = '1'
and CONTRACT_OR_PRO_NAME = #{htmc}
</select>
<select id="checkGysmcExists" resultType="java.lang.Boolean">
select DISTINCT count(1)
from ca_bm_supplier_info
where IS_ACTIVE = '1'
and CONTRACT_OR_PRO_NAME = #{htmc}
and name = #{gysmc}
</select>
<select id="getSpidByName" resultType="java.lang.String">
select id
from ca_bm_goods_info
where IS_ACTIVE = '1'
and name = #{spmc}
</select>
<select id="getAccountIdByName" resultType="java.lang.Integer">
select id
from ca_bm_pay_account_info
where IS_ACTIVE = '1'
and name = #{accountName}
</select>
<select id="getZdridByName" resultType="java.lang.String">
select id
from sys_user
where IS_ACTIVE = '1'
and username = #{zdr}
</select>
</mapper>

View File

@ -272,10 +272,11 @@
})
}
layui.use(['layer', 'form'], function(){
layui.use(['layer', 'form', 'upload'], function(){
layer = layui.layer;
form = layui.form;
var laydate = layui.laydate;
var upload = layui.upload;
// 渲染
laydate.render({
elem: '#rq'

View File

@ -59,11 +59,17 @@
<input id="pzh" type="text" class="layui-input" style="width: 180px " placeholder="请输入单号(凭证号)">
</div>
</div>
<div class="layui-inline">
<!--<div class="layui-inline">
<div class="layui-input-inline">
<input id="zdr" type="text" class="layui-input" style="width: 150px" placeholder="请输入制单人">
</div>
</div>-->
<div class="layui-inline">
<div class="layui-input-inline">
<input id="je" type="text" class="layui-input" style="width: 150px" placeholder="请输入金额">
</div>
</div>
<div class="layui-inline" >
<div class="layui-input-inline" style="width: 200px">
<select name="accountId" id="accountId" lay-search="">
@ -89,6 +95,19 @@
<button id="exportBt" class="layui-btn" >
下载
</button>
<button class="layui-btn" onclick="downloadPayable()">
模版下载
</button>
<div class="layui-inline">
<input id="payableList" name="excelFile" type="file" class="form-control" style="width: 300px; display: inline;" />
<input id="uploadExcel" type="button" style="width: 60px;height: 35px;" value="导入" />
<input type="file" id="import-excel" name="image" style="display: none;"/>
<!-- <button type="button" class="layui-btn layui-btn-normal" id="payableList">选择文件</button>-->
<!-- <button type="button" id="uploadExcel" class="layui-btn layui-btn-normal layui-btn-danger" >开始上传</button>-->
</div>
</div>
</form>
</td>
@ -164,7 +183,8 @@
gysmc :$('#gysmc').val(),
spbh :$('#spbh').val(),
pzh :$('#pzh').val(),
zdr :$('#zdr').val(),
// zdr :$('#zdr').val(),
je :$('#je').val(),
startTime :$('#startTime').val(),
endTime :$('#endTime').val()
} //post请求必须加where post请求需要的参数
@ -212,6 +232,11 @@
},
toolbar: "#toolbar"
});
$("#uploadExcel").click(function () {
importExcel();
});
});
function buttonquerygysmc(htmcid) {
@ -344,7 +369,8 @@
gysmc :$('#gysmc').val(),
spbh :$('#spbh').val(),
pzh :$('#pzh').val(),
zdr :$('#zdr').val(),
// zdr :$('#zdr').val(),
je :$('#je').val(),
accountId :$('#accountId').val(),
startTime :$('#startTime').val(),
endTime :$('#endTime').val()
@ -356,7 +382,7 @@
$("#exportBt").click(function () {
var token = localStorage.getItem("token");
var loadingMsg = layer.msg('下载中,请稍候...', {icon: 16, scrollbar: false, time: 0});
var url = ctxPath + "/payable/exp?htmc=" + $("#htmc").val().trim()+"&gysmc=" + $("#gysmc").val().trim()+ "&spbh=" + $("#spbh").val().trim()+"&pzh=" + $("#pzh").val().trim()+"&zdr=" + $("#zdr").val().trim()+"&accountId=" + $("#accountId").val().trim()+"&startTime=" + $("#startTime").val().trim()+"&endTime=" + $("#endTime").val().trim()+"&token=" + token;
var url = ctxPath + "/payable/exp?htmc=" + $("#htmc").val().trim()+"&gysmc=" + $("#gysmc").val().trim()+ "&spbh=" + $("#spbh").val().trim()+"&pzh=" + $("#pzh").val().trim()+"&je=" + $("#je").val().trim()+"&accountId=" + $("#accountId").val().trim()+"&startTime=" + $("#startTime").val().trim()+"&endTime=" + $("#endTime").val().trim()+"&token=" + token;
var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.responseType = "blob"; // 转换流
@ -376,4 +402,61 @@
};
xhr.send();
});
function downloadPayable() {
window.open(ctxPath + "/download/download?filename=应付录入导入模版.xlsx")
}
function importExcel() {
var formData = new FormData($('form')[0]);
var name = $("#payableList").val();
if (name == null || name == "") {
layer.msg("请上传正确的Excel表格!");
return;
}
if (!(name.endsWith(".xls") || name.endsWith(".xlsx") || name.endsWith(".xlsm"))) {
layer.msg("请上传正确的Excel表格!");
$("#payableList").val("");
return;
}
formData.append("file", $("#payableList")[0].files[0]);
var idx = layer.msg('正在提交数据,请稍等...', {
icon: 16
, shade: 0.01
, time: '-1'
});
setTimeout(function () {
$.ajax({
url: ctxPath + "/payable/importExcel",
type: 'POST',
async: false,
data: formData,
timeout: 20000,
// 告诉jQuery不要去处理发送的数据
processData: false,
// 告诉jQuery不要去设置Content-Type请求头
contentType: false,
success: function (data) {
layer.close(idx);
if (data.code == 200) {
layer.alert("导入成功", {icon: 1});
table.reload('menuTable');// 刷新页面
} else if(data.code == 400){
layer.alert("导入失败,请检查模版", {icon: 2});
}else if(data.code == 500){
layer.alert(data.msg, {icon: 2});
}else {
layer.alert("导入失败,请检查模版", {icon: 2});
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(JSON.stringify(errorThrown));
layer.close(idx);
}
});
$("#payableList").val("");
}, 1000);
}
</script>