一、OFD
OFD(Open Fixed-layout Document) ,是由工业和信息化部软件司牵头中国电子技术标准化研究院成立的版式编写组制定的版式文档国家标准,属于中国的一种自主格式,要打破政府部门和党委机关电子公文格式不统一,以方便地进行电子文档的存储、读取以及编辑。
二、OFD格式发票解析
ofd文件类似与zip,解析思路是将其解压之后,然后对其中的xml文件进行处理,可以用dom4j,根据需要选择要处理的xml
OFD.XML
<?xml version="1.0" encoding="UTF-8"?>
-<ofd:OFD Version="1.1" DocType="OFD" xmlns:ofd="http://www.ofdspec.org/2016">
-<ofd:DocBody>
-<ofd:DocInfo>
<ofd:DocID>605a0643270f4874b54b541369ea24f7</ofd:DocID>
<ofd:Author>China Tax</ofd:Author>
<ofd:CreationDate>2020-09-10</ofd:CreationDate>
-<ofd:CustomDatas>
<ofd:CustomData Name="template-version">1.0.20.0422</ofd:CustomData>
<ofd:CustomData Name="native-producer">SuwellFormSDK</ofd:CustomData>
<ofd:CustomData Name="producer-version">1.0.20.0603</ofd:CustomData>
<ofd:CustomData Name="发票代码">033022000313</ofd:CustomData>
<ofd:CustomData Name="发票号码">00000001</ofd:CustomData>
<ofd:CustomData Name="合计税额">11283.71</ofd:CustomData>
<ofd:CustomData Name="合计金额">86797.75</ofd:CustomData>
<ofd:CustomData Name="开票日期">2020年09月01日</ofd:CustomData>
<ofd:CustomData Name="校验码">11058 76307 04808 54113</ofd:CustomData>
<ofd:CustomData Name="购买方纳税人识别号">913302127995019136</ofd:CustomData>
<ofd:CustomData Name="销售方纳税人识别号">91330203MA2H7WU45J</ofd:CustomData>
</ofd:CustomDatas>
</ofd:DocInfo>
<ofd:DocRoot>Doc_0/Document.xml</ofd:DocRoot>
<ofd:Signatures>Doc_0/Signs/Signatures.xml</ofd:Signatures>
</ofd:DocBody>
</ofd:OFD>
original_invoice.xml
<?xml version="1.0" encoding="UTF-8"?>
-<eInvoice Version="1.0" xmlns:fp="http://www.edrm.org.cn/schema/e-invoice/2019">
<fp:DocID>03</fp:DocID>
<fp:InvoiceCode>033022000313</fp:InvoiceCode>
<fp:InvoiceNo>00000001</fp:InvoiceNo>
<fp:TypeCode>0</fp:TypeCode>
<fp:IssueDate>2020年09月01日</fp:IssueDate>
<fp:InvoiceCheckCode>11058763070480854113</fp:InvoiceCheckCode>
<fp:MachineNo>667900296527</fp:MachineNo>
<fp:TaxControlCode>00945*1<0-3>2*62377>93158<5< <4024141+0091580+/30920879<8 4+3-*<3><21/723494/+/+01+225 0*6220933>43/501+<>71+>>><7/ </fp:TaxControlCode>
-<fp:Buyer>
<fp:BuyerName>雅戈尔服装控股有限公司</fp:BuyerName>
<fp:BuyerTaxID>913302127995019136</fp:BuyerTaxID>
<fp:BuyerAddrTel>宁波市海曙区鄞县大道西段2号0574-87425577</fp:BuyerAddrTel>
<fp:BuyerFinancialAccount>农行宁波雅戈尔分理处39428001040000723</fp:BuyerFinancialAccount>
</fp:Buyer>
<fp:Tpvn/>
-<fp:Seller>
<fp:SellerName>宁波狮丹努创客科贸有限公司</fp:SellerName>
<fp:SellerTaxID>91330203MA2H7WU45J</fp:SellerTaxID>
<fp:SellerAddrTel>浙江省宁波市海曙区吴家路83号1-987457828</fp:SellerAddrTel>
<fp:SellerFinancialAccount>宁波银行海曙支行20010122000732464</fp:SellerFinancialAccount>
<fp:Issuea1/>
<fp:Issuea2/>
</fp:Seller>
<fp:TaxInclusiveTotalAmount>98081.46</fp:TaxInclusiveTotalAmount>
<fp:Note/>
<fp:InvoiceClerk>管理员</fp:InvoiceClerk>
<fp:Payee>管理员</fp:Payee>
<fp:Checker>管理员</fp:Checker>
<fp:TaxTotalAmount>11283.71</fp:TaxTotalAmount>
<fp:TaxExclusiveTotalAmount>86797.75</fp:TaxExclusiveTotalAmount>
<fp:GraphCode>01,20,033022000313,00000001,86797.75,20200901,11058763070480854113,BCB8,</fp:GraphCode>
<fp:InvoiceSIA1/>
<fp:InvoiceSIA2/>
<fp:Signature>MIIBBQYKKoEcz1UGAQQCAqCB9jCB8wIBATEOMAwGCCqBHM9VAYMRBQAwDAYKKoEcz1UGAQQCATGBzzCBzAIBATBjMFgxCzAJBgNVBAYTAkNOMRswGQYDVQQLDBLlm73lrrbnqI7liqHmgLvlsYAxLDAqBgNVBAMMI+eojuWKoeeUteWtkOivgeS5pueuoeeQhuS4reW/gyhTTTIpAgdEAwAAATl1MAwGCCqBHM9VAYMRBQAwDAYIKoEcz1UBg3UFAARGMEQCIGROWCbe2SVfGU8rXl9DTutArClYOxNJVt2bu6RMBW/PAiBoYhg/z8uTptlM+3Qqs1FUMxn37Puo4ii4TLdD+ah+cw==</fp:Signature>
-<fp:GoodsInfos>
-<fp:GoodsInfo>
<fp:Item>*纺织产品*200g/m2 100%羊毛1*1罗纹</fp:Item>
<fp:Specification>262815-6557</fp:Specification>
<fp:MeasurementDimension>KG</fp:MeasurementDimension>
<fp:Price>267.070006807352</fp:Price>
<fp:Quantity>325</fp:Quantity>
<fp:Amount>86797.75</fp:Amount>
<fp:TaxScheme>13%</fp:TaxScheme>
<fp:TaxAmount>11283.71</fp:TaxAmount>
</fp:GoodsInfo>
</fp:GoodsInfos>
</eInvoice>
:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.data.model.Invoice;
/**
*
* 发票ofd解析util.<br>
*
* @author qianxiangyun <br>
* @version 1.0.0 2021年7月13日<br>
* @see
* @since JDK 1.5.0
*/
public final class InvoiceOfdUtil {
/**
* 构造方法.
*/
private InvoiceOfdUtil() {
}
/**
* 日期格式化.
*/
static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日"); // 注意月份是MM
public static void main(final String[] args) {
// 1.解压ofd文件
try {
final String xmlPath = unzip("C:\\Users\\qxy\\Desktop\\033022000313_00000001.ofd");
final SAXReader reader = new SAXReader();
Document doc;
try {
doc = reader.read(new File(xmlPath));
final Element rootElement = doc.getRootElement(); // 获取document对象根节点,即最外层节点下的内容
// 发票代码、发票号码、
Invoice invoice = new Invoice();
invoice = generateInvoice(rootElement, invoice);
System.out.println(invoice.getInvoiceCode());
} catch (DocumentException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* ofd发票文件解析.
* @param filePath filePath
* @return Invoice
*/
public static Invoice parseOfd(final String filePath) {
// 发票代码、发票号码、
Invoice invoice = new Invoice();
try {
final String xmlPath = unzip(filePath);
final SAXReader reader = new SAXReader();
Document doc;
try {
doc = reader.read(new File(xmlPath));
final Element rootElement = doc.getRootElement(); // 获取document对象根节点,即最外层节点下的内容
invoice = generateInvoice(rootElement, invoice);
System.out.println(invoice.getInvoiceCode());
} catch (DocumentException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return invoice;
}
/**
* 生成发票信息.
* @param rootElement rootElement
* @param invoice invoice
* @return Invoice
*/
private static Invoice generateInvoice(final Element rootElement, final Invoice invoice) {
// 发票代码
final Element invoiceCodeElement = rootElement.element("InvoiceCode");
// 发票号码
final Element invoiceNoElement = rootElement.element("InvoiceNo");
// 开票日期
final Element issueDateElement = rootElement.element("IssueDate");
// 校验码
final Element invoiceCheckCodeElement = rootElement.element("InvoiceCheckCode");
// 机器编号
final Element machineNoElement = rootElement.element("MachineNo");
// 密码区
final Element taxControlCodeElement = rootElement.element("TaxControlCode");
// 购买方
final Element buyerElement = rootElement.element("Buyer");
// 销售方
final Element sellerElement = rootElement.element("Seller");
// 税额
final Element taxTotalAmountElement = rootElement.element("TaxTotalAmount");
// 金额
final Element taxExclusiveTotalAmountElement = rootElement.element("TaxExclusiveTotalAmount");
// 价税合计
final Element taxInclusiveTotalAmountElement = rootElement.element("TaxInclusiveTotalAmount");
// 收款人
final Element payeeElement = rootElement.element("payee");
// 开票人
final Element invoiceClerkElement = rootElement.element("InvoiceClerk");
// 复核
final Element checkerElement = rootElement.element("Checker");
if (invoiceCodeElement != null) {
invoice.setInvoiceCode(invoiceCodeElement.getText());
}
if (invoiceNoElement != null) {
invoice.setInvoiceNumber(invoiceNoElement.getText());
}
if (issueDateElement != null && issueDateElement.getText() != null) {
Date date = null;
try {
date = simpleDateFormat.parse(issueDateElement.getText());
} catch (ParseException e) {
e.printStackTrace();
}
invoice.setBillingDate(date);
}
if (invoiceCheckCodeElement != null) {
invoice.setCheckCode(invoiceCheckCodeElement.getText());
}
if (machineNoElement != null) {
invoice.setMachineCode(machineNoElement.getText());
}
if (taxControlCodeElement != null) {
invoice.setMmq(taxControlCodeElement.getText());
}
if (buyerElement != null) {
final List<Element> buyerName = buyerElement.elements("BuyerName");
final List<Element> buyerTaxID = buyerElement.elements("BuyerTaxID");
final List<Element> buyerAddrTel = buyerElement.elements("BuyerAddrTel");
final List<Element> buyerFinancialAccount = buyerElement.elements("BuyerFinancialAccount");
invoice.setPurchaserName(buyerName.get(0).getText());
invoice.setPurchaserTaxNo(buyerTaxID.get(0).getText());
invoice.setPurchaserAddressPhone(buyerAddrTel.get(0).getText());
invoice.setPurchaserBank(buyerFinancialAccount.get(0).getText());
}
if (sellerElement != null) {
final List<Element> sellerName = sellerElement.elements("SellerName");
final List<Element> sellerTaxID = sellerElement.elements("SellerTaxID");
final List<Element> sellerAddrTel = sellerElement.elements("SellerAddrTel");
final List<Element> sellerFinancialAccount = sellerElement.elements("SellerFinancialAccount");
invoice.setSalesName(sellerName.get(0).getText());
invoice.setSalesTaxNo(sellerTaxID.get(0).getText());
invoice.setSalesAddressPhone(sellerAddrTel.get(0).getText());
invoice.setSalesBank(sellerFinancialAccount.get(0).getText());
}
if (taxTotalAmountElement != null) {
invoice.setTotalTax(new BigDecimal(taxTotalAmountElement.getText()));
}
if (taxExclusiveTotalAmountElement != null) {
invoice.setTotalAmount(new BigDecimal(taxExclusiveTotalAmountElement.getText()));
}
if (taxInclusiveTotalAmountElement != null) {
invoice.setTotalAmount(new BigDecimal(taxInclusiveTotalAmountElement.getText()));
}
if (payeeElement != null) {
invoice.setSkr(payeeElement.getText());
}
if (invoiceClerkElement != null) {
invoice.setKpr(invoiceClerkElement.getText());
}
if (checkerElement != null) {
invoice.setFh(checkerElement.getText());
}
return invoice;
}
/**
* 从字节流写到文件中.
*
* @param is
* 流
* @return file
*/
private static File getFileByBytes(final InputStream is, final String fileName, final String oldFileName) {
final String tempPath = System.getProperty("java.io.tmpdir");
final File dirFile = new File(tempPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
final File file = new File(dirFile, fileName); // zip文件需临时存放服务器
final File oldFile = new File(dirFile, oldFileName);
OutputStream os = null;
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
if (oldFile.exists()) {
file.delete();
}
os = new FileOutputStream(file);
int len;
final byte[] buffer = new byte[4096];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
/**
* 解压ofd文件.
* @param ofdFile ofdFile
* @return ofdFile路径
* @throws IOException IOException
*/
public static String unzip(final String ofdFile) throws IOException {
final ZipFile zipFile = new ZipFile(ofdFile);
String lastFileName = "";
String temp = "";
// 循环查找文件
final Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
final ZipEntry zipEntry = (ZipEntry) entries.nextElement();
if (!zipEntry.isDirectory()) {
String fileFullName = zipEntry.getName();
// 若当前文件包含文件夹名称,则直接去文件名称
if (fileFullName.indexOf("/") != -1 && fileFullName.indexOf("original_invoice") > -1) {
fileFullName = fileFullName.substring(fileFullName.lastIndexOf("/") + 1);
lastFileName = fileFullName;
final InputStream in = zipFile.getInputStream(zipEntry);
final File xmlfile = getFileByBytes(in, fileFullName, lastFileName);
temp = xmlfile.toString();
}
}
}
zipFile.close();
return temp;
}
}