销售发票开票匹配功能

This commit is contained in:
lihao 2025-12-05 16:40:18 +08:00
parent 5f566a3ede
commit 3c17ff219f
3 changed files with 686 additions and 0 deletions

View File

@ -0,0 +1,273 @@
package nccloud.web.so.saleorder.action;
import nccloud.commons.lang.ArrayUtils;
import com.yonyou.cloud.utils.StringUtils;
import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.logging.Logger;
import nccloud.framework.core.exception.BusinessException;
import nccloud.framework.core.exception.ExceptionUtils;
import nccloud.framework.core.io.WebFile;
import nccloud.framework.core.json.IJson;
import nccloud.framework.web.action.itf.ICommonAction;
import nccloud.framework.web.container.IRequest;
import nccloud.framework.web.container.SessionContext;
import nccloud.framework.web.json.JsonFactory;
import nccloud.web.hrwa.psndocwadoc.vo.ImportHead;
import nccloud.web.hrwa.pub.ErrorContent;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SaleInvoiceNumImportAction implements ICommonAction {
public SaleInvoiceNumImportAction() {
}
@Override
public Object doAction(IRequest request) {
WebFile[] webFiles = request.readFiles();
if (ArrayUtils.isEmpty(webFiles)) {
ExceptionUtils.wrapBusinessException(ErrorContent.FILEUPLOADNULL);
}
try {
// 导入Excel文件获取数据列表
List<Map<String, Object>> dataList = importFormFile(
webFiles[0].getInputStream(),
webFiles[0].getFileName());
Map<String, Object> result = new HashMap<>();
if (dataList == null || dataList.isEmpty()) {
result.put("isSuccess", "N");
result.put("error", "导入的数据为空");
} else {
result.put("isSuccess", "Y");
result.put("data", dataList); // 返回数据列表
result.put("totalCount", dataList.size()); // 返回总记录数
}
return result;
} catch (BusinessException e) {
throw e;
} catch (Exception e) {
Logger.error("导入Excel文件失败", e);
throw new BusinessException("导入失败:" + e.getMessage());
}
}
/**
* 导入Excel文件返回数据列表
* @param fileInput 文件输入流
* @param fileName 文件名
* @return 数据列表每个Map包含一行数据
* @throws BusinessException 业务异常
*/
public List<Map<String, Object>> importFormFile(InputStream fileInput, String fileName)
throws BusinessException {
List<Map<String, Object>> dataList = new ArrayList<>();
try {
if (null == fileInput || StringUtils.isBlank(fileName)) {
throw new BusinessException("文件输入流或文件名为空");
}
// 检查文件格式
if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
throw new BusinessException(ErrorContent.FILETYPEERROR);
}
Workbook workbook = null;
if (fileName.endsWith(".xlsx")) {
workbook = new XSSFWorkbook(fileInput);
} else if (fileName.endsWith(".xls")) {
workbook = new HSSFWorkbook(fileInput);
}
if (workbook == null) {
throw new BusinessException("无法创建Workbook对象");
}
Sheet sheet = workbook.getSheetAt(0);
if (sheet == null) {
throw new BusinessException("Excel文件中没有工作表");
}
// 获取表头行
Row headerRow = sheet.getRow(0);
if (headerRow == null) {
throw new BusinessException("Excel文件第一行表头为空");
}
// 验证表头
validateHeader(headerRow);
// 从第二行开始读取数据跳过表头
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue; // 跳过空行
}
try {
Map<String, Object> rowData = parseRowData(row, rowNum + 1);
if (rowData != null && !rowData.isEmpty()) {
// 添加行号方便前端定位
rowData.put("rowNumber", rowNum + 1);
dataList.add(rowData);
}
} catch (Exception e) {
Logger.warn("解析第" + (rowNum + 1) + "行数据失败,跳过此行", e);
// 可以选择继续处理其他行或者抛出异常停止导入
continue;
}
}
workbook.close();
return dataList;
} catch (IOException e) {
Logger.error("读取Excel文件失败", e);
throw new BusinessException("读取Excel文件失败" + e.getMessage());
} finally {
if (fileInput != null) {
try {
fileInput.close();
} catch (IOException e) {
Logger.error("关闭文件流失败", e);
}
}
}
}
/**
* 验证表头格式
*/
private void validateHeader(Row headerRow) throws BusinessException {
String[] expectedHeaders = {"物料编码", "含税单价", "匹配数量"};
if (headerRow.getLastCellNum() < expectedHeaders.length) {
throw new BusinessException("表头列数不正确,应为" + expectedHeaders.length + "");
}
for (int i = 0; i < expectedHeaders.length; i++) {
Cell cell = headerRow.getCell(i);
if (cell == null) {
throw new BusinessException("" + (i + 1) + "列表头为空,应为:" + expectedHeaders[i]);
}
String cellValue = getCellValue(cell).trim();
if (!expectedHeaders[i].equals(cellValue)) {
throw new BusinessException("" + (i + 1) + "列表头不正确,应为:" + expectedHeaders[i] + ",实际为:" + cellValue);
}
}
}
/**
* 解析行数据
*/
private Map<String, Object> parseRowData(Row row, int rowNumber) {
Map<String, Object> rowData = new HashMap<>();
// 检查是否为空行
boolean isEmptyRow = true;
// 物料编码
Cell materialCodeCell = row.getCell(0);
String materialCode = getCellValue(materialCodeCell);
if (StringUtils.isNotBlank(materialCode)) {
rowData.put("materialCode", materialCode.trim());
isEmptyRow = false;
} else {
rowData.put("materialCode", "");
}
// 含税单价
Cell priceCell = row.getCell(1);
BigDecimal taxPrice = null;
try {
String priceStr = getCellValue(priceCell);
if (StringUtils.isNotBlank(priceStr)) {
taxPrice = new BigDecimal(priceStr.trim());
rowData.put("taxPrice", taxPrice);
rowData.put("taxPriceStr", priceStr.trim()); // 保留字符串格式
isEmptyRow = false;
}
} catch (Exception e) {
Logger.warn("" + rowNumber + "行含税单价格式不正确");
rowData.put("taxPriceError", "含税单价格式不正确");
}
// 匹配数量
Cell quantityCell = row.getCell(2);
BigDecimal matchQuantity = null;
try {
String quantityStr = getCellValue(quantityCell);
if (StringUtils.isNotBlank(quantityStr)) {
// 如果是整数直接使用整数
if (quantityStr.matches("\\d+")) {
matchQuantity = new BigDecimal(quantityStr.trim());
} else {
matchQuantity = new BigDecimal(quantityStr.trim());
}
rowData.put("matchQuantity", matchQuantity);
rowData.put("matchQuantityStr", quantityStr.trim()); // 保留字符串格式
isEmptyRow = false;
}
} catch (Exception e) {
Logger.warn("" + rowNumber + "行匹配数量格式不正确");
rowData.put("quantityError", "匹配数量格式不正确");
}
// 如果是空行返回null
return isEmptyRow ? null : rowData;
}
/**
* 获取单元格的值
*/
private String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
try {
cell.setCellType(CellType.STRING);
return cell.getStringCellValue();
} catch (Exception e) {
try {
// 尝试获取数值
double numericValue = cell.getNumericCellValue();
// 如果是整数去掉小数部分
if (numericValue == (long) numericValue) {
return String.valueOf((long) numericValue);
} else {
return String.valueOf(numericValue);
}
} catch (Exception ex) {
// 如果还是失败尝试布尔值
try {
boolean booleanValue = cell.getBooleanCellValue();
return String.valueOf(booleanValue);
} catch (Exception ex2) {
return "";
}
}
}
}
}

View File

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<actions>
<action>
<name>so.saleinvoice.coop25</name>
<label>销售发票协同生成采购发票卡片</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceCoopTo25Action</clazz>
</action>
<action>
<name>so.saleinvoice.save</name>
<label>销售发票新增保存</label>
<clazz>nccloud.web.so.saleinvoice.action.SaveAction</clazz>
<btncode>Save</btncode>
</action>
<action>
<name>so.saleinvoice.rowopr</name>
<label>销售发票行操作</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceRowOprAction
</clazz>
</action>
<action>
<name>so.saleinvoice.query</name>
<label>销售发票查询</label>
<clazz>nccloud.web.so.saleinvoice.action.QueryAction</clazz>
</action>
<action>
<name>so.saleinvoice.delete</name>
<label>销售发票删除</label>
<clazz>nccloud.web.so.saleinvoice.action.DeleteAction</clazz>
<btncode>Delete</btncode>
</action>
<action>
<name>so.saleinvoice.griddelete</name>
<label>销售发票删除</label>
<clazz>nccloud.web.so.saleinvoice.action.GridDeleteAction</clazz>
<btncode>Delete</btncode>
</action>
<action>
<name>so.saleinvoice.commit</name>
<label>销售发票提交</label>
<clazz>nccloud.web.so.saleinvoice.action.CommitAction</clazz>
<btncode>Commit</btncode>
</action>
<action>
<name>so.saleinvoice.savecommit</name>
<label>销售发票保存提交</label>
<clazz>nccloud.web.so.saleinvoice.action.SaveAndCommitAction</clazz>
<btncode>SaveCommit</btncode>
</action>
<action>
<name>so.saleinvoice.uncommit</name>
<label>销售发票收回</label>
<clazz>nccloud.web.so.saleinvoice.action.UnCommitAction</clazz>
<btncode>UnCommit</btncode>
</action>
<action>
<name>so.saleinvoice.gridcommit</name>
<label>销售发票提交</label>
<clazz>nccloud.web.so.saleinvoice.action.BatchCommitAction</clazz>
<btncode>Commit</btncode>
</action>
<action>
<name>so.saleinvoice.griduncommit</name>
<label>销售发票列表收回</label>
<clazz>nccloud.web.so.saleinvoice.action.BatchUnCommitAction</clazz>
<btncode>UnCommit</btncode>
</action>
<action>
<name>so.saleinvoice.querycard</name>
<label>销售发票列表进卡片</label>
<clazz>nccloud.web.so.saleinvoice.action.CardQueryAction</clazz>
</action>
<action>
<name>so.saleinvoice.headafter</name>
<label>销售发票表头 编辑事件</label>
<clazz>nccloud.web.so.saleinvoice.event.HeadAfterEditAction</clazz>
</action>
<action>
<name>so.saleinvoice.bodyafter</name>
<label>销售发票表体编辑事件</label>
<clazz>nccloud.web.so.saleinvoice.event.BodyAfterEditAction</clazz>
</action>
<action>
<name>so.saleinvoice.bodybefore</name>
<label>销售发票表体编辑前事件</label>
<clazz>nccloud.web.so.saleinvoice.event.BodyBeforeEditAction</clazz>
</action>
<action>
<name>so.saleinvoice.headbefore</name>
<label>销售发票表头编辑前事件</label>
<clazz>nccloud.web.so.saleinvoice.event.HeadBeforeEditAction</clazz>
</action>
<action>
<name>so.saleinvoice.fetchcost</name>
<label>销售发票取成本价</label>
<clazz>nccloud.web.so.saleinvoice.action.FetchCostAction</clazz>
<btncode>FetchCost</btncode>
</action>
<action>
<name>so.saleinvoice.querypagebypks</name>
<label>销售发票翻页查询</label>
<clazz>nccloud.web.so.saleinvoice.action.QueryByPKsAction</clazz>
</action>
<action>
<name>so.saleinvoice.unitarsub</name>
<label>销售发票费用冲抵</label>
<clazz>nccloud.web.so.saleinvoice.action.UnitArsubAction</clazz>
<btncode>Offset</btncode>
</action>
<action>
<name>so.saleinvoice.cancelunitarsub</name>
<label>销售发票取消费用冲抵</label>
<clazz>nccloud.web.so.saleinvoice.action.CancelUnitArsubAction</clazz>
<btncode>UnOffset</btncode>
</action>
<action>
<name>so.saleinvoice.showcombine</name>
<label>销售发票汇总显示</label>
<clazz>nccloud.web.so.saleinvoice.action.ShowCombineAction</clazz>
<btncode>ShowCombine</btncode>
</action>
<action>
<name>so.saleinvoice.showdetail</name>
<label>销售发票明细显示</label>
<clazz>nccloud.web.so.saleinvoice.action.ShowDetailAction</clazz>
<btncode>ShowDetail</btncode>
</action>
<action>
<name>so.saleinvoice.arsubdetail</name>
<label>销售发票费用兑付明细</label>
<clazz>nccloud.web.so.saleinvoice.action.ArsubDetailQueryAction
</clazz>
</action>
<action>
<name>so.saleinvoice.creditquery</name>
<label>销售发票信用查询</label>
<clazz>nccloud.web.so.saleinvoice.action.CreditQueryAction</clazz>
<btncode>CreditQuery</btncode>
</action>
<action>
<name>so.saleinvoice.gridarsubdetail</name>
<label>销售发票列表联查费用兑付明细</label>
<clazz>nccloud.web.so.saleinvoice.action.BatchArsubDetailAction
</clazz>
</action>
<action>
<name>so.saleinvoice.gridcreditquery</name>
<label>销售发票列表联查信用</label>
<clazz>nccloud.web.so.saleinvoice.action.GridCreditQueryAction</clazz>
<btncode>CreditQuery</btncode>
</action>
<action>
<name>so.saleinvoice.query30for32</name>
<label>销售订单提供给销售发票的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleOrderQueryFor32Action
</clazz>
</action>
<action>
<name>so.saleinvoice.query4330for32</name>
<label>销售发出商品期初提供给销售发票的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.InitoutregQueryFor32Action
</clazz>
</action>
<action>
<name>so.saleinvoice.query4cfor32</name>
<label>销售出库提供给销售发票的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleoutQueryFor32Action
</clazz>
</action>
<action>
<name>so.saleinvoice.queryallfor32</name>
<label>销售出库提供给销售发票的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.QueryAllFor32Action</clazz>
</action>
<action>
<name>so.saleinvoice.linkqueryallfor32</name>
<label>销售出库提供给销售发票的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.LinkQueryAllFor32Action</clazz>
</action>
<action>
<name>so.saleinvoice.queryaddtab</name>
<label>销售发票新增页签的查询</label>
<clazz>nccloud.web.so.saleinvoice.action.QueryAddTabAction</clazz>
</action>
<action>
<name>so.saleinvoice.transfer</name>
<label>销售发票开票</label>
<clazz>nccloud.web.so.saleinvoice.action.TransToSaleInvoiceAction
</clazz>
</action>
<action>
<name>so.saleinvoice.outrush</name>
<label>销售发票出库对冲</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleOutRushAction</clazz>
</action>
<action>
<name>so.saleinvoice.refaddline</name>
<label>销售发票参照增行</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceRefAddLineAction
</clazz>
</action>
<action>
<name>so.saleinvoice.print</name>
<label>销售发票打印</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoicePrintAction
</clazz>
<btncode>Print,Output,Print_list</btncode>
</action>
<action>
<name>so.saleinvoice.printpermission</name>
<label>销售发票打印</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoicePrintPermissionAction
</clazz>
<btncode>Print,Output,Print_list</btncode>
</action>
<action>
<name>so.saleinvoice.opposeadd</name>
<label>销售发票生成对冲发票</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceOpposeAddAction
</clazz>
<btncode>OpposeAdd</btncode>
</action>
<action>
<name>so.saleinvoice.exportGoldTax</name>
<label>销售发票传金税</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceExportGoldTaxAction
</clazz>
<btncode>ExportGoldTax</btncode>
</action>
<action>
<name>so.saleinvoice.translateGoldTax</name>
<label>销售发票生成金税vo</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceTranslateGoldTaxAction
</clazz>
</action>
<action>
<name>so.saleinvoice.importtaxcode</name>
<label>销售发票导入税票号</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceImportTaxCodeAction
</clazz>
<btncode>ImportTaxCode</btncode>
</action>
<action>
<name>so.saleinvoice.querytranstype</name>
<label>销售发票交易类型</label>
<clazz>nccloud.web.so.saleinvoice.trantype.M32TransTypeQueryAction
</clazz>
</action>
<action>
<name>so.saleinvoice.edit</name>
<label>销售发票修改</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceEditAction</clazz>
<btncode>Edit</btncode>
</action>
<action>
<name>so.saleinvoice.linkqueryfromvoucher</name>
<label>联查单据</label>
<clazz>nccloud.web.so.saleinvoice.action.LinkQueryFromVoucherAction
</clazz>
</action>
<action>
<name>so.saleinvoice.vatsub</name>
<label>税额小计</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceVatSubAction
</clazz>
</action>
<action>
<name>so.saleinvoice.cardvatsub</name>
<label>卡片税额小计</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceCardVatSubAction
</clazz>
</action>
<action>
<name>so.saleinvoice.saleprofit</name>
<label>毛利预估</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceProfitAction
</clazz>
<btncode>CrossProfitRpt</btncode>
</action>
<action>
<name>so.saleinvoice.saleeditprofit</name>
<label>编辑态毛利预估</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceEditProfitAction
</clazz>
<btncode>CrossProfitRpt</btncode>
</action>
<action>
<name>so.saleinvoice.invoicequeryexec</name>
<label>发票执行情况</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceQueryExecAction
</clazz>
<btncode>InvoiceQueryExec</btncode>
</action>
<action>
<name>so.saleinvoice.issueInvoice</name>
<label>销售发票开票</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceBatchIssueInvAction
</clazz>
<btncode>MakeInv_1,MakeInv_4,MakeInv_3,MakeInv_11,MakeInv_10,MakeInv_8,MakeInv_99,MakeInv_31,MakeInv_32</btncode>
</action>
<action>
<name>so.saleinvoice.checkissueInvoice</name>
<label>销售发票开票校验</label>
<clazz>nccloud.web.so.saleinvoice.action.SaleInvoiceCheckForSSCINV
</clazz>
</action>
<action>
<name>so.saleinvoice.linkQueryVoucher</name>
<label>销售发票联查凭证</label>
<clazz>nccloud.web.so.saleinvoice.action.LinkQueryVoucherAction
</clazz>
<btncode>LinkQueryVoucher</btncode>
</action>
<action>
<name>so.saleinvoice.checkSagaStatus</name>
<label>销售发票检查saga状态</label>
<clazz>nccloud.web.so.saleinvoice.action.CheckSagaStatusAction
</clazz>
</action>
<action>
<name>so.saleinvoice.maintainandLinkInvoice</name>
<label>销售发票维护与联查税务发票</label>
<clazz>nccloud.web.so.saleinvoice.action.MaintainandLinkInvoiceAction
</clazz>
</action>
<action>
<name>so.saleinvoice.SaleInvoiceNumImport</name>
<label>销售发票发票匹配</label>
<clazz>nccloud.web.so.saleorder.action.SaleInvoiceNumImportAction</clazz>
</action>
</actions>

View File

@ -0,0 +1,74 @@
<authorizes>
<authorize>
<appcode>400601200,400601202,400601206</appcode>
<actions>
<action>so.saleinvoice.coop25</action>
<action>so.saleinvoice.batchcoop25</action>
<action>so.saleinvoice.query</action>
<action>so.saleinvoice.delete</action>
<action>so.saleinvoice.griddelete</action>
<action>so.saleinvoice.save</action>
<action>so.saleinvoice.commit</action>
<action>so.saleinvoice.uncommit</action>
<action>so.saleinvoice.gridcommit</action>
<action>so.saleinvoice.griduncommit</action>
<action>so.saleinvoice.rowopr</action>
<action>so.saleinvoice.querycard</action>
<action>so.saleinvoice.headafter</action>
<action>so.saleinvoice.bodyafter</action>
<action>so.saleinvoice.bodybefore</action>
<action>so.saleinvoice.headbefore</action>
<action>so.saleinvoice.fetchcost</action>
<action>so.saleinvoice.querypagebypks</action>
<action>so.saleinvoice.unitarsub</action>
<action>so.saleinvoice.cancelunitarsub</action>
<action>so.saleinvoice.showcombine</action>
<action>so.saleinvoice.showdetail</action>
<action>so.saleinvoice.arsubdetail</action>
<action>so.saleinvoice.creditquery</action>
<action>so.saleinvoice.gridarsubdetail</action>
<action>so.saleinvoice.gridcreditquery</action>
<action>so.saleinvoice.query30for32</action>
<action>so.saleinvoice.query4330for32</action>
<action>so.saleinvoice.query4cfor32</action>
<action>so.saleinvoice.queryallfor32</action>
<action>so.saleinvoice.linkqueryallfor32</action>
<action>so.saleinvoice.queryaddtab</action>
<action>so.saleinvoice.transfer</action>
<action>so.saleinvoice.outrush</action>
<action>so.saleinvoice.refaddline</action>
<action>so.saleinvoice.print</action>
<action>so.saleinvoice.printpermission</action>
<action>so.saleinvoice.opposeadd</action>
<action>so.saleinvoice.exportGoldTax</action>
<action>so.saleinvoice.translateGoldTax</action>
<action>so.saleinvoice.importtaxcode</action>
<action>so.saleinvoice.edit</action>
<action>so.saleinvoice.linkqueryfromvoucher</action>
<action>so.saleinvoice.linkQueryVoucher</action>
<action>so.saleinvoice.vatsub</action>
<action>so.saleinvoice.cardvatsub</action>
<action>so.saleinvoice.saleprofit</action>
<action>so.saleinvoice.saleeditprofit</action>
<action>so.saleinvoice.invoicequeryexec</action>
<action>so.saleinvoice.issueInvoice</action>
<action>so.saleinvoice.checkissueInvoice</action>
<action>so.saleinvoice.savecommit</action>
<action>so.saleinvoice.checkSagaStatus</action>
<action>so.saleinvoice.maintainandLinkInvoice</action>
<action>so.saleinvoice.SaleInvoiceNumImport</action>
</actions>
</authorize>
<authorize>
<appcode>*</appcode>
<actions>
<action>so.saleinvoice.querytranstype</action>
</actions>
</authorize>
</authorizes>