|
@@ -0,0 +1,515 @@
|
|
|
+package com.malk.xzkj.controller;
|
|
|
+
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.malk.xzkj.model.McInvoiceDto;
|
|
|
+import com.malk.xzkj.model.McInvoiceKind;
|
|
|
+import com.malk.xzkj.config.TXYConf;
|
|
|
+import com.malk.server.aliwork.YDConf;
|
|
|
+import com.malk.server.aliwork.YDParam;
|
|
|
+import com.malk.server.common.FilePath;
|
|
|
+import com.malk.server.common.McException;
|
|
|
+import com.malk.server.common.McR;
|
|
|
+import com.malk.server.dingtalk.DDR_New;
|
|
|
+import com.malk.service.aliwork.YDClient;
|
|
|
+import com.malk.service.aliwork.YDService;
|
|
|
+import com.malk.utils.*;
|
|
|
+import com.malk.xzkj.service.IvYdService;
|
|
|
+import com.malk.xzkj.service.TXYInvoice;
|
|
|
+import com.spire.pdf.PdfCompressionLevel;
|
|
|
+import com.spire.pdf.PdfDocument;
|
|
|
+import com.spire.pdf.PdfPageBase;
|
|
|
+import com.spire.pdf.exporting.PdfImageInfo;
|
|
|
+import com.spire.pdf.graphics.PdfBitmap;
|
|
|
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import net.coobird.thumbnailator.Thumbnails;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.apache.poi.util.IOUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestBody;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.File;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.net.URL;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 错误抛出与拦截详见CatchException
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+@RequestMapping("/iv/")
|
|
|
+public class IVController {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TXYInvoice txyInvoice;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IvYdService ivYdService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private YDClient ydClient;
|
|
|
+
|
|
|
+ /// 优先获取字段, 新版本接口已支持字段返回
|
|
|
+ private String findValue(List<Map<String, String>> infos, String... names) {
|
|
|
+ for (String name : names) {
|
|
|
+ Optional optional = infos.stream().filter(info -> info.get("Name").equals(name)).findAny();
|
|
|
+ if (optional.isPresent()) {
|
|
|
+ return String.valueOf(((Map) optional.get()).get("Value"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兼容历史配置, 格式 (谷元)
|
|
|
+ private String guyuanNameRepalce(String name) {
|
|
|
+ if (name.contains("谷元")) {
|
|
|
+ return UtilString.replaceBracketIsWhole(name);
|
|
|
+ } else {
|
|
|
+ return UtilString.replaceBracketIsSemiangle(name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// url压缩转base64
|
|
|
+ @SneakyThrows
|
|
|
+ private String imageUrlConvertBase64(String imageUrl) {
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ // scale(比例), outputQuality(质量)
|
|
|
+ Thumbnails.fromURLs(Arrays.asList(new URL(imageUrl))).scale(0.5f).outputQuality(0.25f).toOutputStream(out);
|
|
|
+ InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
|
|
|
+ //转换为base64
|
|
|
+ byte[] bytes = IOUtils.toByteArray(inputStream);
|
|
|
+ return Base64.getEncoder().encodeToString(bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FilePath filePath;
|
|
|
+
|
|
|
+ /// PDF压缩转base64
|
|
|
+ @SneakyThrows
|
|
|
+ private String pdfUrlConvertBase64(String pdfUrl) {
|
|
|
+ String fileName = "tmp_" + new Date().getTime() + ".pdf";
|
|
|
+ // 下载文件
|
|
|
+ File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getTmp());
|
|
|
+ UtilHttp.doDownload(pdfUrl, file);
|
|
|
+
|
|
|
+ // PDF压缩
|
|
|
+ PdfDocument doc = new PdfDocument(); // 创建PdfDocument类的对象
|
|
|
+ doc.loadFromFile(file.getAbsolutePath()); // 加载PDF文档
|
|
|
+ doc.getFileInfo().setIncrementalUpdate(false); // 禁用增量更新
|
|
|
+ doc.setCompressionLevel(PdfCompressionLevel.Normal); // 将压缩级别设置为最佳
|
|
|
+
|
|
|
+ // 遍历文档页面
|
|
|
+ for (int i = 0; i < doc.getPages().getCount(); i++) {
|
|
|
+ PdfPageBase page = doc.getPages().get(i); // 获取指定页面
|
|
|
+ PdfImageInfo[] images = page.getImagesInfo(); // 获取每个页面的图像信息集合
|
|
|
+ // 遍历集合中的所有项目
|
|
|
+ if (images != null && images.length > 0)
|
|
|
+ for (int j = 0; j < images.length; j++) {
|
|
|
+ PdfImageInfo image = images[j]; // 获取指定图片
|
|
|
+ PdfBitmap bp = new PdfBitmap(image.getImage());
|
|
|
+ bp.setQuality(30); // 设置压缩质量
|
|
|
+ page.replaceImage(j, bp); // 将原始图像替换为压缩图像
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 将结果文档保存至另一个PDF文档中: 覆盖
|
|
|
+ doc.saveToFile(file.getAbsolutePath());
|
|
|
+ doc.close();
|
|
|
+ // PDF转base64, 无需透出本地文件地址
|
|
|
+ String base64 = UtilFile.fileToBase64(file.getAbsolutePath());
|
|
|
+ // 删除临时PDF文件
|
|
|
+ UtilFile.deleteFile(file.getAbsolutePath());
|
|
|
+ return base64;
|
|
|
+ }
|
|
|
+
|
|
|
+ // prd 校验发票抬头, 购买方范围
|
|
|
+ private void validateBuyer(String BuyerName, String tips) {
|
|
|
+ List<String> corpNames = Arrays.asList(
|
|
|
+ "珠海金魁新能源科技有限公司",
|
|
|
+ "珠海能魁新能源科技有限公司",
|
|
|
+ "珠海创伟新能源有限公司",
|
|
|
+ "河南能辉绿电科技有限公司",
|
|
|
+ "河南省绿色生态新能源科技有限公司",
|
|
|
+ "贵州能辉智慧能源科技有限公司",
|
|
|
+ "上海能辉科技股份有限公司",
|
|
|
+ "浙江数实通信息技术有限公司");
|
|
|
+ McException.assertAccessException(!corpNames.contains(BuyerName), tips + ", 购买方名称不合法!");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 混票识别 [新版本]
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-iv2")
|
|
|
+ McR invoice_iv2(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
|
|
|
+
|
|
|
+ McException.assertParamException_Null(data, "url");
|
|
|
+ String image = ydClient.convertTemporaryUrl(data.get("url"),6000);
|
|
|
+ log.info("混票识别, 免登地址, {}", image);
|
|
|
+ // 非PDF, 且内存大于3M, 压缩后上传
|
|
|
+ if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
|
|
|
+ image = imageUrlConvertBase64(image);
|
|
|
+ }
|
|
|
+ if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
|
|
|
+ image = pdfUrlConvertBase64(image);
|
|
|
+ }
|
|
|
+ List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
|
|
|
+ List<McInvoiceDto> result = invoices.stream().map(item -> {
|
|
|
+ Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
|
|
|
+
|
|
|
+ String kind = UtilMap.getString(item, "TypeDescription");
|
|
|
+ String invoiceName = UtilMap.getString(item, "SubTypeDescription");
|
|
|
+ if (kind.equals("全电发票")) {
|
|
|
+ kind = invoiceName.contains("专用发票") ? "全电专用发票" : "全电普通发票";
|
|
|
+ }
|
|
|
+ if (kind.equals("增值税发票")) {
|
|
|
+ kind = invoiceName.contains("增值税专用发票") ? "增值税专用发票" : "增值税普通发票";
|
|
|
+ if (invoiceName.contains("增值税电子")) {
|
|
|
+ kind = invoiceName.contains("专用发票") ? "增值税电子专用发票" : "增值税电子普通发票";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // ppExt: 通用字段定义
|
|
|
+ McInvoiceDto invoiceDto = McInvoiceDto.builder()
|
|
|
+ .name(UtilMap.getString(item, "SubTypeDescription"))
|
|
|
+ .kindName(kind)
|
|
|
+ .kind(UtilMap.getInt(item, "Type"))
|
|
|
+ .code(UtilMap.getString(prop, "Code"))
|
|
|
+ .serial(UtilMap.getString(prop, "Number"))
|
|
|
+ .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
|
|
|
+ .checkCode(UtilMap.getString(prop, "CheckCode"))
|
|
|
+ // ppExt: 多明细行时, 优先取值合计 [全电票返回了subTotal字段, 但值为空]
|
|
|
+ .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "SubTotal", "Total")))
|
|
|
+ .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "SubTax", "Tax")))
|
|
|
+ .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
|
|
|
+ .buyerName(StringUtils.isBlank(guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))?"上海能辉科技股份有限公司":guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
|
|
|
+ // ppExt: 中央非税未返回税号官方说明: 非税发票理论是没有税号的,图片中属于信用代码
|
|
|
+ .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))?"91310000685457643J": UtilMap.getString(prop, "BuyerTaxID"))
|
|
|
+ .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer"))) // 行程单: 填开单位
|
|
|
+ .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode")) // 行程单: 销售单位代号
|
|
|
+ .passengerName(UtilMap.getString_first(prop, "Name", "UserName")) // 火车票, 行程单
|
|
|
+ // 交通出行
|
|
|
+ .seatType(UtilMap.getString(prop, "Seat"))
|
|
|
+ .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"))
|
|
|
+ .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place")) // 火车票: 出发车站, 过路过桥费: 入口, 出租车: 发票所在地
|
|
|
+ .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit")) // 行程单, 火车票: 到达车站, 过路过桥费: 出口
|
|
|
+ .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate")) // 火车票: 车次, 出租车: 车牌号
|
|
|
+ .insuranceCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "Insurance")))) // 行程单: 保险费
|
|
|
+ .fuelCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "FuelSurcharge")))) // 行程单: 燃油附加费
|
|
|
+ .constructionCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "AirDevelopmentFund")))) // 行程单: 民航发展基金
|
|
|
+ .build();
|
|
|
+ // ppExt: 机票行程单, 行程与座位信息在明细内
|
|
|
+ if ("机票行程单".equals(item.get("TypeDescription"))) {
|
|
|
+ Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
|
|
|
+ invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"));
|
|
|
+ invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
|
|
|
+ invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
|
|
|
+ invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
|
|
|
+ }
|
|
|
+ if ("出租车发票".equals(item.get("TypeDescription"))) {
|
|
|
+ // 上下车时间
|
|
|
+ invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn") + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
|
|
|
+ }
|
|
|
+ return invoiceDto;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ return McR.success(McInvoiceDto.formatResponse(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 混票识别 [旧版本, 已废弃]
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-iv")
|
|
|
+ McR invoice_iv(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
|
|
|
+
|
|
|
+ McException.assertParamException_Null(data, "url");
|
|
|
+ String image = ydClient.convertTemporaryUrl(data.get("url"),0);
|
|
|
+ log.info("混票识别, 免登地址, {}", image);
|
|
|
+ // 非PDF, 且内存大于3M, 压缩后上传
|
|
|
+ if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
|
|
|
+ image = imageUrlConvertBase64(image);
|
|
|
+ }
|
|
|
+ if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
|
|
|
+ image = pdfUrlConvertBase64(image);
|
|
|
+ }
|
|
|
+ // ppExt: 通用字段定义
|
|
|
+ List<Map> invoices = (List<Map>) txyInvoice.doMixedInvoiceOCR(image).get("MixedInvoiceItems");
|
|
|
+ List<McInvoiceDto> result = invoices.stream().map(item -> {
|
|
|
+ String kind = TXYConf.TYPE_INVOICE.get(item.get("Type").toString());
|
|
|
+ List<Map<String, String>> infos = (List<Map<String, String>>) item.get("SingleInvoiceInfos");
|
|
|
+
|
|
|
+ McInvoiceDto.assertSuccess(item, kind); // 响应断言
|
|
|
+ String invoiceName = findValue(infos, "发票名称");
|
|
|
+ if (kind.equals("全电发票")) {
|
|
|
+ kind = invoiceName.contains("增值税专用发票") ? "全电专用发票" : "全电普通发票";
|
|
|
+ }
|
|
|
+ if (kind.equals("增值税发票")) {
|
|
|
+ kind = invoiceName.contains("增值税专用发票") ? "增值税专用发票" : "增值税普通发票";
|
|
|
+ if (invoiceName.contains("增值税电子")) {
|
|
|
+ kind = invoiceName.contains("专用发票") ? "增值税电子专用发票" : "增值税电子普通发票";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ McInvoiceDto invoiceDto = McInvoiceDto.builder()
|
|
|
+ .name(invoiceName)
|
|
|
+ .kindName(kind)
|
|
|
+ .kind(McInvoiceKind.getKindCode(kind))
|
|
|
+ .code(findValue(infos, "发票代码", "票据代码")) // 发票, 非税发票
|
|
|
+ // 储存唯一ID [发票, 火车票, 行程单]
|
|
|
+ .serial(findValue(infos, "发票号码", "编号", "电子客票号码", "票据号码").replace("No", "")) // 发票, 非税发票
|
|
|
+ .date(findValue(infos, "开票日期").replace("年", "-").replace("月", "-").replace("日", ""))
|
|
|
+ .checkCode(findValue(infos, "校验码"))
|
|
|
+ .amount(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "小写金额", "价税合计(小写)", "合计金额", "票价", "金额"))) // 发票, 全电票, 行程单, 火车票, 过路过桥费
|
|
|
+ .excludingTax(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "合计金额", "金额", "票价", "小写金额"))) // [ppExt: 多明细行时, 优先取值合计] 行程单, 火车票, 定额发票
|
|
|
+ .tax(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "合计税额"))) // 增值税发票
|
|
|
+ .buyerName(guyuanNameRepalce(findValue(infos, "购买方名称", "交款人"))) // 发票, 非税发票
|
|
|
+ .buyerTaxId(findValue(infos, "购买方识别号", "购买方统一社会信用代码/纳税人识别号", "交款人统一社会信用代码")) // 发票, 全电票, 非税发票
|
|
|
+ .sellerName(guyuanNameRepalce(findValue(infos, "销售方名称", "填开单位"))) // 行程单
|
|
|
+ .sellerTaxId(findValue(infos, "销售方识别号", "销售方统一社会信用代码/纳税人识别号", "销售单位代号")) // 发票, 全电票, 行程单
|
|
|
+ .passengerName(findValue(infos, "旅客姓名", "姓名")) // 行程单, 火车票
|
|
|
+ .seatType(findValue(infos, "座位等级", "席别")) // 行程单, 火车票
|
|
|
+ .departurePort(findValue(infos, "始发地", "出发站", "入口")) // 行程单, 火车票, 过路过桥费
|
|
|
+ .arrivePort(findValue(infos, "目的地", "到达站", "出口")) // 行程单, 火车票, 过路过桥费
|
|
|
+ .trainNo(findValue(infos, "航班号", "车次", "车牌号")) // 行程单, 火车票, 出租车
|
|
|
+ .insuranceCosts(UtilNumber.setBigDecimal((findValue(infos, "保险费")))) // 行程单
|
|
|
+ .fuelCosts(UtilNumber.setBigDecimal((findValue(infos, "燃油附加费")))) // 行程单
|
|
|
+ .constructionCosts(UtilNumber.setBigDecimal((findValue(infos, "民航发展基金")))) // 行程单
|
|
|
+ .build();
|
|
|
+ // 价格不一致情况下, 通过合计返回
|
|
|
+ if (!UtilNumber.equalBigDecimal(invoiceDto.getAmount(), invoiceDto.getExcludingTax().add(invoiceDto.getTax()))) {
|
|
|
+ invoiceDto.setAmount(invoiceDto.getExcludingTax().add(invoiceDto.getTax()));
|
|
|
+ }
|
|
|
+ // 机票行程单
|
|
|
+ if (kind.equals(McInvoiceKind.JP.getDesc())) {
|
|
|
+ String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
|
|
|
+ invoiceDto.setDepartureTime(date + " " + findValue(infos, "时间"));
|
|
|
+ }
|
|
|
+ // 火车票
|
|
|
+ if (kind.equals(McInvoiceKind.HC.getDesc())) {
|
|
|
+ invoiceDto.setDepartureTime(findValue(infos, "出发时间").replace("年", "-").replace("月", "-").replace("日", " "));
|
|
|
+ }
|
|
|
+ // 出租车
|
|
|
+ if (kind.equals(McInvoiceKind.CZC.getDesc())) {
|
|
|
+ String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
|
|
|
+ invoiceDto.setDepartureTime(date + " " + findValue(infos, "上车"));
|
|
|
+ }
|
|
|
+ return invoiceDto;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ return McR.success(McInvoiceDto.formatResponse(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发票查重, 验真
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-va")
|
|
|
+ McR invoice_va(@RequestBody Map data) {
|
|
|
+ McException.assertParamException_Null(data, "param");
|
|
|
+ List<McInvoiceDto> invoices = JSON.parseArray(JSON.toJSONString(data.get("param")), McInvoiceDto.class);
|
|
|
+
|
|
|
+ log.info("发票查重, 验真, {}", invoices);
|
|
|
+ invoices.forEach(UtilMc.consumerWithIndex((item, index) -> {
|
|
|
+
|
|
|
+ McInvoiceDto dto = (McInvoiceDto) item;
|
|
|
+ String invoiceNo = dto.getSerial(); // 唯一标识, 发票号码
|
|
|
+
|
|
|
+ String serial = "第【" + (index + 1) + "】张发票";
|
|
|
+ validateBuyer(dto.getBuyerName(), serial + "有疑问");
|
|
|
+ McException.assertAccessException(StringUtils.isBlank(invoiceNo), serial + ", 识别结果为空, 请检查!");
|
|
|
+ YDParam ydParam = YDParam.builder()
|
|
|
+ .formUuid("FORM-FE4634E6D01745C7B26DC58D049EAADC6RP4")
|
|
|
+ .searchFieldJson(JSON.toJSONString(UtilMap.map("radioField_liihyrtb, textField_liihyrt8", "否", invoiceNo)))
|
|
|
+ .build();
|
|
|
+ List<String> idList = (List<String>) ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_search_form_id).getData();
|
|
|
+ if (idList.size() > 0) {
|
|
|
+ McException.exceptionAccess(serial + "已存在, 请勿重复提交!");
|
|
|
+ }
|
|
|
+ // prd 仅仅识别 报销 用途的发票
|
|
|
+ if (dto.getType().equals("报销") && !dto.getKindName().contains("车票") && !dto.getKindName().contains("车发票") && !dto.getKindName().contains("定额发票")) {
|
|
|
+ String serialTips = serial + "有疑问";
|
|
|
+ try {
|
|
|
+ // ppExt: 识别与验真后抬头对比 [全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值]
|
|
|
+ Map rsp = txyInvoice.doVatInvoiceVerifyNew(dto.getKindName(), dto.getCode(), invoiceNo, dto.getDate(), String.valueOf(dto.getAmount()), dto.getCheckCode(), String.valueOf(dto.getExcludingTax()), serialTips);
|
|
|
+ Map invoice = (Map) rsp.get("Invoice");
|
|
|
+ McException.assertAccessException(!dto.getBuyerName().equals(guyuanNameRepalce(invoice.get("BuyerName").toString())), serialTips + ", 购买方名称不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getBuyerTaxId().equals(invoice.get("BuyerTaxCode")), serialTips + ", 购买方税号不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getSellerName().equals(guyuanNameRepalce(invoice.get("SellerName").toString())), serialTips + ", 销售方名称不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getSellerTaxId().equals(invoice.get("SellerTaxCode")), serialTips + ", 销售方税号不匹配!");
|
|
|
+ } catch (TencentCloudSDKException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ // prd: 上传发票为假发票时,提示:该发票有疑问,请联系财务人员
|
|
|
+ String message = e.getMessage();
|
|
|
+ // ppExt: 已经是新版本接口, 过滤提示 [官方答复: 提示不会检测您是否使用的是新版,所有的用户都会提示, 忽略即可]
|
|
|
+ if (message.contains("温馨提示")) {
|
|
|
+ message = message.split("温馨提示")[0];
|
|
|
+ }
|
|
|
+ if (message.contains("发票不存在")) {
|
|
|
+ message = "有疑问,请联系财务人员";
|
|
|
+ }
|
|
|
+ McException.exceptionAccess(serial + message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发票状态更新: 服务注册
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-up")
|
|
|
+ McR invoice_va(HttpServletRequest request) {
|
|
|
+ Map data = UtilServlet.getParamMap(request);
|
|
|
+ log.info("发票状态更新: 服务注册, {}", data);
|
|
|
+
|
|
|
+ String compId = UtilMap.getString(data, "compId");
|
|
|
+ String status = UtilMap.getString(data, "status");
|
|
|
+
|
|
|
+ // 读取关联表单
|
|
|
+ String formUUid="";
|
|
|
+ List<String> formInstanceIds = new ArrayList<>();
|
|
|
+ if(compId.equals("selectField_lzs0bpk2")){
|
|
|
+ // 采购表单
|
|
|
+ formUUid="FORM-B5A7B20013AE4CD09AD87FAB9A3E145FS3P6";
|
|
|
+ List<Map> associationData = (List<Map>) JSON.parse(UtilMap.getString(data, "multiAssociation"));
|
|
|
+ formInstanceIds.addAll(associationData.stream().map(form -> UtilMap.getString(form, "instanceId")).collect(Collectors.toList()));
|
|
|
+ }else{
|
|
|
+ formUUid="FORM-FE4634E6D01745C7B26DC58D049EAADC6RP4";
|
|
|
+ List<String> associationForm = (List<String>) JSON.parse(UtilMap.getString(data, "multiAssociation"));
|
|
|
+ for (String record : associationForm) {
|
|
|
+ // 解析关联表单
|
|
|
+ List<Map> associationData = (List<Map>) JSON.parse(record);
|
|
|
+ formInstanceIds.addAll(associationData.stream().map(form -> UtilMap.getString(form, "instanceId")).collect(Collectors.toList()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 宜搭批量更新
|
|
|
+ Map update = UtilMap.map(compId, status);
|
|
|
+ if (compId.equals("selectField_liihyrt6")) {
|
|
|
+ update.put("radioField_liw7rb2q", "否"); // 提交后, 更新是否退回标识为否
|
|
|
+ }
|
|
|
+
|
|
|
+ // prd 9.10 更新报销单, 关联到发票:: ppExt 宜搭服务注册, 提交规则系统默认字段 [详见 YDService]
|
|
|
+ ivYdService.operateData(data, update, YDParam.builder()
|
|
|
+ .formUuid(formUUid)
|
|
|
+ .formInstanceIdList(formInstanceIds)
|
|
|
+ .updateFormDataJson(JSON.toJSONString(update))
|
|
|
+ .build(), YDConf.FORM_OPERATION.multi_update);
|
|
|
+
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发票状态更新: 退回提交
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-zy")
|
|
|
+ McR invoice_zy(@RequestBody Map data) {
|
|
|
+ log.info("发票状态更新: 退回提交, {}", data);
|
|
|
+
|
|
|
+ List<String> pre_ids = (List<String>) data.get("pre_ids"); // 释放修改前
|
|
|
+ List<String> cur_ids = (List<String>) data.get("cur_ids"); // 占用修改后
|
|
|
+
|
|
|
+ // [前端调用添加] 退回为监听宜搭dom事件, 先执行接口调用, 才会校验宜搭必填, 过滤无效调用
|
|
|
+ if (cur_ids.size() == 0) {
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ Map pre_update = (Map) data.get("pre_update");
|
|
|
+ Map cur_update = (Map) data.get("cur_update");
|
|
|
+
|
|
|
+ // 宜搭批量更新
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .formUuid("FORM-FE4634E6D01745C7B26DC58D049EAADC6RP4")
|
|
|
+ .formInstanceIdList(pre_ids)
|
|
|
+ .updateFormDataJson(JSON.toJSONString(pre_update))
|
|
|
+ .build(), YDConf.FORM_OPERATION.multi_update);
|
|
|
+
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .formUuid("FORM-FE4634E6D01745C7B26DC58D049EAADC6RP4")
|
|
|
+ .formInstanceIdList(cur_ids)
|
|
|
+ .updateFormDataJson(JSON.toJSONString(cur_update))
|
|
|
+ .build(), YDConf.FORM_OPERATION.multi_update);
|
|
|
+
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 全局查询(不匹配子表单)
|
|
|
+ */
|
|
|
+ @PostMapping("validate")
|
|
|
+ McR queryAll(HttpServletRequest request) {
|
|
|
+ Map<String, ?> param = UtilServlet.getParamMap(request);
|
|
|
+ log.info("全局查询(不匹配子表单), {}", param);
|
|
|
+ if (ObjectUtil.isNull(param.get("uniques"))) {
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+ McException.assertParamException_Null(param, "uniques", "formUuid", "compId");
|
|
|
+ // 容错 - 尾部分号的空格会被输入框忽略
|
|
|
+ String[] uniques = String.valueOf(param.get("uniques")).replace("; ", ";").split(";");
|
|
|
+ for (String val : uniques) {
|
|
|
+ // 查重校验: 单张发票唯一标识 + 审批已通过 / 审批中
|
|
|
+ List<Map> conditions = new ArrayList<>();
|
|
|
+ Map unique = new HashMap();
|
|
|
+ unique.put("key", param.get("compId"));
|
|
|
+ unique.put("value", val.split(": ")[1]);
|
|
|
+ unique.put("type", "TEXT");
|
|
|
+ unique.put("operator", "like");
|
|
|
+ unique.put("componentName", "TextField");
|
|
|
+ conditions.add(unique);
|
|
|
+
|
|
|
+ Map approve = new HashMap();
|
|
|
+ approve.put("key", "processApprovedResult");
|
|
|
+ approve.put("value", new String[]{"agree"});
|
|
|
+ approve.put("type", "ARRAY");
|
|
|
+ approve.put("operator", "in");
|
|
|
+ approve.put("componentName", "SelectField");
|
|
|
+ conditions.add(approve);
|
|
|
+ YDParam ydParam = YDParam.builder()
|
|
|
+ .formUuid(String.valueOf(param.get("formUuid")))
|
|
|
+ .searchCondition(JSON.toJSONString(conditions))
|
|
|
+ .build();
|
|
|
+ DDR_New ddr_new = ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_list);
|
|
|
+ log.info("审批通过匹配结果, {}, {}", ddr_new.getTotalCount(), ddr_new.getData());
|
|
|
+ if (ddr_new.getTotalCount() > 0) {
|
|
|
+ return McR.errorAccess("发票已被使用, 请勿重复提交!");
|
|
|
+ }
|
|
|
+
|
|
|
+ conditions.remove(approve);
|
|
|
+ Map status = new HashMap();
|
|
|
+ status.put("key", "processInstanceStatus");
|
|
|
+ status.put("value", new String[]{"RUNNING"});
|
|
|
+ status.put("type", "ARRAY");
|
|
|
+ status.put("operator", "in");
|
|
|
+ status.put("componentName", "SelectField");
|
|
|
+ conditions.add(status);
|
|
|
+ ydParam.setSearchCondition(JSON.toJSONString(conditions));
|
|
|
+ ddr_new = ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_list);
|
|
|
+ log.info("审批通过匹配结果, {}, {}", ddr_new.getTotalCount(), ddr_new.getData());
|
|
|
+ if (ddr_new.getTotalCount() > 0) {
|
|
|
+ return McR.errorAccess("发票已在流程中, 请勿重复提交!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("test")
|
|
|
+ McR test() {
|
|
|
+
|
|
|
+
|
|
|
+// List<Map> process = (List<Map>) ydClient.queryData(YDParam.builder()
|
|
|
+// .formUuid("FE4634E6D01745C7B26DC58D049EAADC6RP4")
|
|
|
+// .formInstId("FINST-NGA66WA1FV4EB7QJC3OATA3EV8MK35Z9COEMLFR22")
|
|
|
+// .build(), YDConf.FORM_QUERY.retrieve_id).getData();
|
|
|
+
|
|
|
+ List<Map> process = (List<Map>) ydClient.queryData(YDParam.builder()
|
|
|
+ .formUuid("FORM-0IA66C71F6NBAETREO8DE9SSN43D3YIZ0AYILC")
|
|
|
+ .searchFieldJson(JSON.toJSONString(UtilMap.map("textField_lmewsobs", "Y16668919W4E4FHQ6123ADDHB8XK3S709YEMLXWF")))
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_search_form).getData();
|
|
|
+
|
|
|
+ return McR.success();
|
|
|
+ }
|
|
|
+
|
|
|
+}
|