• C#/Java 动态生成电子发票


    电子发票是电商时代的产物,PDF发票是最常见的电子发票之一。在这篇文章中,我将给大家分享一个免费的动态生成PDF电子发票的C#方案,并在文章末尾附上Java解决方案。

    典型的发票包含客户和供应商的名称和地址、发票编号、购买物品的描述、付款金额等信息。为了动态地生成发票,我使用MS Word创建了一个模板,在该模板中设计好想要呈现的大部分内容及文档样式,然后通过代码替换文本和插入新内容,最后保存为PDF文档。用代码操作Word文档的部分需要使用免费版的Spire.Doc for .NET7.1

    创建模板
    如图1所示,发票模板由两个表组成。表1用于显示买卖双方的信息和订单信息,表2用于陈列卖方向买方提供的产品或服务列表。我们需要做的是替换表1中以#开头的文本,并将客户的购物清单填充至第二个表格。

    为了能自动计算总金额,需要在某些单元格添加公式域。例如,单元格E2包含公式“=C2*D2”,该公式将计算单元格B2中商品的总价。当买家购买超过一件商品时,我们需要在表格添加更多行并动态更新一些单元格的公式。

    图1 发票模板

    如何替换文本
    Spire.Doc 有一个IBodyRegion.Replace(string given, string replace, bool caseSensitive, bool wholeWord)方法,可以用于替换文档中的指定字符串。例如,将“#orderNum”替换成“2516595027”,我们可以直接使用下面的代码,代码中doc是Document对象。

    doc.Replace("#orderNum", "2516595027", true, true);

    如何更新表格二

    现在,让我们看看如何向现有表格添加行,如何将数据填充至表格,以及如何动态地更新公式。为了使逻辑更清晰,我创建了三个方法,并制作了图2,来展示它们的具体含义以及它们之间的调用关系。

    图2 自定义方法的含义及调用关系

    下面,看一下这三个方法的代码片段:

    1. AddRows()

    此方法实际上复制了现有表格的第二行,将复制行依次添加到第二行的下面。新行继承了第二行的单元格格式、字体样式和公式。因此,我们需要依次更新新行中的公式,以及下面随行数增加而变化的公式。

    private static void AddRows(Table table, int rowNum)
    {
     
        for (int i = 0; i < rowNum; i++)
        {
            //将指定个数的第二行的复制行依次添加到第二行下面
            table.Rows.Insert(2 + i, table.Rows[1].Clone());
     
            //更新“金额”所对应单元格的公式
            foreach (var item in table.Rows[2 + i].Cells[4].Paragraphs[0].ChildObjects)
            {
                if (item is Field)
                {
                    Field field = item as Field;
                    field.Code = string.Format("=C{0}*D{0}\# "0.00"", 3 + i);
                }
                break;
            }
     
        }
     
        //更新“折扣金额”对应的单元格的公式
        foreach (var item in table.Rows[4 + rowNum].Cells[4].Paragraphs[0].ChildObjects)
        {
            if (item is Field)
            {
                Field field = item as Field;
                field.Code = string.Format("=E{0}*0.05\# "0.00"", 3 + rowNum);
            }
            break;
        }
     
        //更新“总计”对应的单元格的公式
        foreach (var item in table.Rows[5 + rowNum].Cells[4].Paragraphs[0].ChildObjects)
        {
            if (item is Field)
            {
                Field field = item as Field;
                field.Code = string.Format("=E{0}-E{1}\# "¥#,##0.00"", 3 + rowNum, 5 + rowNum);
            }
            break;
        }
     
    }

     2. FillTableWithData()

    此方法仅用于将sting[][]类型数据从表格的第二行开始写入表格。

    private static void FillTableWithData(Table table, string[][] data)
     {
         for (int r = 0; r < data.Length; r++)
         {
             for (int c = 0; c < data[r].Length; c++)
             {
                 //将数据从表格的第二行开始写入表格
                 table.Rows[r + 1].Cells[c].Paragraphs[0].Text = data[r][c];
             }
         }
     }

    3. WriteDataToDocument()

    由于发票模板已经有一行(第二行)用于显示一项商品,因此我们需要判断是否需要添加更多行。如果客户只购买一项商品,模板文档就可以容纳商品信息并输出结果;否则,我们需要添加行来容纳更多的项目,并动态更新公式以获得正确的总金额。

    private static void WriteDataToDocument(Document doc, string[][] purhcaseData)
    {
        //获取Word模板中的第二个表格
        Table table = doc.Sections[0].Tables[1] as Table;
     
        //若购买商品多于一项,则添加purhcaseData.Length - 1个行
        if (purhcaseData.Length > 1)
        {
            AddRows(table, purhcaseData.Length - 1);
        }
     
        //将购买数据填充至表格
        FillTableWithData(table, purhcaseData);
    }

    WriteDataToDocument()方法的参数之一是sting[][]对象,该对象存储了客户的购买信息,它的每个元素都是一个字符串数组,可以这样设置:

    string[] product = new string[] { "1023", "华为 P30 Pro (8G+128G)全网通", "1", "4288" };

    string[][]的长度则是商品的项数,如果长度大于1,则需要添加[长度 - 1]个新行。

    生成发票

    以下是Main函数中用于生成PDF发票的代码。

    using Spire.Doc;
    using Spire.Doc.Fields;
     
    namespace CreatePdfInvoice
    {
        class Program
        {
            static void Main(string[] args)
            {
                //加载Word模板文档
                Document doc = new Document();
                doc.LoadFromFile("Invoice-Template.docx");
     
                //替换文档中以#开头的文本
                doc.Replace("#customerName", "小伟", true, true);
                doc.Replace("#contactNum", "13601234567", true, true);
                doc.Replace("#shippingAdd", "北京市海淀区幸福小区1幢2单元3号", true, true);
                doc.Replace("#orderDate", "2019-05-30", true, true);
                doc.Replace("#orderNum", "2516595027", true, true);
     
                //定义客户购买数据
                string[][] purchaseData = {
     
                                      new string[]{"1023","华为 P30 Pro (8G+128G)全网通","1","4288"},
                                      new string[]{"1429","华为Watch GT运动版","2","1288"},
                                      new string[]{"1268","华为无线耳机 FreeBuds 2Pro","2","799"},
                                      new string[]{"1281","华为 MateBook 14 (i5 8G 512G)","1","5999"},
                                  };
     
                //将购买数据写入模板文档的第二个表格
                WriteDataToDocument(doc, purchaseData);
     
                //更新域
                doc.IsUpdateFields = true;
     
                //保存为PDF格式文档
                doc.SaveToFile("Invoice.pdf", FileFormat.PDF);
                System.Diagnostics.Process.Start("Invoice.pdf");
            }
        }
    }

    生成的结果文档如下:

    图 3 多项目PDF发票

    如果你输入购买数据只有一行,那么你将得到如图4所示的结果文档。

    string[][] purchaseData = {new string[]{"1023","华为 P30 Pro (8G+128G)全网通","1","4288"},};

    图4 单项目PDF发票

    工程下载

    C#工程(含DLL和模板文档)  https://pan.baidu.com/s/1jCI1J5J08hGReGIe0ZzOsA  提取码:zp6n
    Java 工程(含Jar和模板文档)  https://pan.baidu.com/s/1bthB3gnd0B0JQYzwZDWmVA   提取码:ciey




    注:

    免费版 Spire.Doc能加载和生成的Word文档不能超过500个段落或25个表格,将Word文档保存为PDF时仅支持前3页。绝大多数发票只有1页或2页,所以该方案适用于大多数情况。


    ————————————————
    版权声明:本文为CSDN博主「ssw_jack」的原创文章。
    原文链接:https://blog.csdn.net/ssw_jack/article/details/91379486

  • 相关阅读:
    css的继承问题
    js弹出层
    实现网页弹出框后背景不能滑动的效果
    vue的爬坑之路(四)之基于vuecli 动态添加iconfont的symbol方法
    vue的爬坑之路(五)之基于vuecli li列表的显示隐藏
    自定义复选框checkbox样式
    vue的爬坑之路(三)之基于vuecli的VueAwesomeSwiper轮播滑块插件的使用及常见问题
    iOS 开发学习总结
    (转载)iOS股票k线组件源码,支持其他图表类型
    iOS Objective C Memory management
  • 原文地址:https://www.cnblogs.com/Yesi/p/11399064.html
Copyright © 2020-2023  润新知