dump java崩溃自动 不生成_基于Excel和Java自动化工作流程:发票生成器示例

对于销售人员,使用Excel创建发票是很常见的。但是该过程通常涉及许多容易出错的手动操作,例如输入数据,复制/粘贴等。如何实现一个可以将数据从数据库自动填充到发票Excel模板中,而无需再辛苦手动输入,从繁重的手动录入中解脱出来,并且避免认为错误这是每个人迫切的需求。虫虫一直奉行理念:真正的自动化是解决用户痛点问题,把繁重人工劳动释放出来。本文我们就介绍一个老外的利用Java编写自动化程序实现自动化发票生成器的案例,案例中创建了一个Web应用程序Invoice Builder,并利用Excel模版文件,Java和Keikai将这种手动发票录入过程转换为集成的自动化过程。

总体架构

体系图

下图显示了发票生成器应用程序的体系图:

5622417be2540d9f4d88ada3186b34b1.png

首先,导入一个源Excel文件,其中包含2个空表,客户表和产品表。业务员将从这两个表中选择客户和产品。

接着,根据数据库查询,将客户和产品数据填充到相应的表中。

最后,导入2个发票模板;它们是业务员在Excel中创建的。这些模板将在以后使用。

工作流程:选择客户和产品后,应用将通过将客户和产品数据与所选发票模板结合在一起来创建发票。

应用的动图示例如下:

bc4595ae060e7f2553f69ada953cface.gif

MVC模式

Keikai也支持MVC模式,在本应用MVC模式,具体如下:

189b172c3201bbe337d217af1a31cede.png

视图:用ZUL编写的XML文件。ZK将zul文件转换为UI组件并在浏览器中呈现。

控制器:扩展ZK的Java类,SelectorComposer用于监听View触发的事件并控制ZK UI组件。通过Spreadsheet和RangeAPI控制Keikai。

模型:本实例中是CustomerService,但是它也可以是任何其他的Java业务类,例如身份验证,数据查询等。

具体实现

构建界面UI

界面UI构建中在实际选择了zul,当然也可以选择纯Java(例如Swing)来构建UI。可以使用创建组件new Image(),通过将组件添加到Groupbox(容器)appendChild(),并使用来注册事件侦听器addEventListener()。使用这些API,可以在组框内的模板列表数组上动态创建模板预览图:

44312e358c6fbb9afe06b8042f9bd79a.png

@Wireprivate Groupbox templateBox;private String[] templateFileNameList = {"invoice-template1.xlsx", "invoice-template2.xlsx"};...private void buildTemplatePreview() {...Arrays.stream(templateFileNameList).forEach(fileName -> {...Image preview = new Image(fileNameWithoutExt + "-preview.jpg");templateBox.appendChild(preview);preview.setAttribute(TEMPLATE_KEY, fileName);preview.addEventListener(org.zkoss.zk.ui.event.Events.ON_CLICK, event ->selectTemplate((Image) event.getTarget()));...});...}

由于业务人员往往习惯使用Excel模板,主程序界面采用了Keikai Spreadsheet,这样可以继续使用其现有模板。

Keikai基于ZK UI框架,该框架提供了完整的UI组件集以及XML格式的UI语言。

按照ZK的语法,使用XML标签中的以下UI组件构建此Web应用程序的UI:

<hlayout vflex="1" width="100%" apply="io.keikai.devref.usecase.invoice.InvoiceBuilderController"><spreadsheet height="100%" id="spreadsheet" hflex="8"maxVisibleRows="6" maxVisibleColumns="8"src="/WEB-INF/books/invoice-source.xlsx"showSheetbar="true"/><vlayout hflex="2" height="100%"><groupbox id="templateBox" title="Template" style="text-align: center">groupbox><button id="create" label="Create" style="float: right"/>vlayout>hlayout>

:keikai电子表格。

:水平布置其子组件,垂直布置组件。

:带有边框和标题的组件分组。

每个标签都支持一些属性,例如:

src:指定要导入到Keikai的Excel文件路径。

maxVisibleRows:控制keikai在浏览器中渲染工作表时的最大可见行数。

控制器

要为页面指定控制器,只需在apply属性处指定了全限定的类名:

<hlayout vflex="1" width="100%" apply="io.keikai.devref.usecase.invoice.InvoiceBuilderController">...hlayout>

然后,该控制器可以控制其子组件。我通常在页面的根组件上指定一个控制器。

自动填充客户

现在可以显示电子表格和源文件,接着需要将数据自动填充到表。

源Excel文件仅包含一个空客户表,其表样式如列名和标题颜色。这里的一件好事是,这个Excel文件是由我的销售人员使用Excel创建的-他更清楚自己想在此表中看到的内容。

51dd626bf4a2861d8a6da9862610df72.png

从服务类加载客户列表,并将列表填充到表中:

private void populateCustomers() {List<String[]> customers = CustomerService.getCustomerList();Range startingCell = customerTable.toCellRange(0, 1); //the 1st column is for checkboxfor (String[] c : customers) {RangeHelper.setValuesInRow(startingCell, c);startingCell = startingCell.toShiftedRange(1, 0);}}

CustomerService 也可以是您所提供的数据实体的任何Java类。

setValuesInRow() 用字符串数组(例如B2,C2,D2 ...)一行一行地填充多个单元格

toShiftedRange(1, 0)转移startingCell到下一行。

用命名范围填充数据

将数据填充到电子表格UI时,需要指定要将数据填充到的目标单元格。选择命名范围是因为它是一种灵活的方法。

首先,创建几个指定范围中的每个模板文件例如Name,Phone和Email客户详细信息。最终用户选择客户和产品后,控制器将每一行提取为地图。索引是标题,该值是对应的单元格值,例如

{Name: Debra, Phone: 338-8777, Email: debra@...}。

然后,从所选模板中克隆发票表,并将客户详细信息填充到相应的命名范围中。

8efa5f865f80edd1baad73766add23b9.png

@Listen(org.zkoss.zk.ui.event.Events.ON_CLICK + "=#create")public void createInvoice() {...Book invoiceBook = Books.createBook("invoice.xlsx");for (Map customer : selectedCustomers) {Sheet invoiceSheet = Ranges.range(invoiceBook).cloneSheetFrom(customer.get("CompanyName").toString(), templates.get(getSelectedTemplateFileName()).getSheetAt(0));populateNamedRange(generateAgentData(), invoiceSheet);populateNamedRange(customer, invoiceSheet);...}...}private void populateNamedRange(Map fieldMap, Sheet sheet) {List namedRanges = Ranges.getNames(sheet);fieldMap.forEach((name, value) -> {if (namedRanges.contains(name)) {Range range = Ranges.rangeByName(sheet, name);range.setCellValue(value);}});}

用户权限控制

在此应用程序中,客户数据是从数据库中填充的,不希望用户可以更改,只可以选择这些记录。因此,通过以下方式限制了它们在用户界面上可以执行的操作:

隐藏工具栏和上下文菜单

通过指定使工作表标签可见showSheetbar="true"。默认情况下,其他所有内容(如工具栏,公式栏和上下文菜单)都是不可见的。这样,用户就不会无意间更改了UI上显示的内容。

<spreadsheet ... showSheetbar="true"/>

启用工作表保护并禁用添加工作表

通过以下方式启用工作表保护:protectSheet()将所有工作表设为只读,并禁止用户通过添加工作表disableUserAction()。

private void limitAccess() {for (int i = 0; i < spreadsheet.getBook().getNumberOfSheets(); i++) {Ranges.range(spreadsheet.getBook().getSheetAt(i)).protectSheet(SELECTION_FILTER);}spreadsheet.disableUserAction(AuxAction.ADD_SHEET, true);}

工作表保护下的可编辑区域

在Excel中,可以取消选中锁定状态以在工作表保护下使单元格可编辑。其他单元将保持只读状态。使用此设置,可以在受保护的图纸中允许一定范围的可编辑区域。导入到Keikai后,此设置将保留,因此可以在准备源文件时从Excel端完成。

d3eed3bee0d5a6ad8a4c0d3ae9ab9781.png

重用

在应用程序中,有2个Excel模板,想一次导入它们,然后在需要时使用它们。

Keikai Importer将Excel xlsx文件转换为Book。可以将Book分配给Spreadsheet并将其呈现给浏览器。或者,可以直接操作Bookwith Range,而无需将其分配给Spreadsheet。最常见的用法是从模板书克隆表或复制单元格。每个需要Excel模板的人都可以从其中获取内容,而无需再次导入模板文件。在应用程序中,将Book2个模板Excel文件的对象存储在Map(templateWarehouse)中,以备将来使用:

private static HashMap<String, Book> templateWarehouse = new HashMap<>();private static Importer importer = Importers.getImporter();...private void importInvoiceTemplate() {...for (String fileName : templateFileNameList) {if (!templateWarehouse.containsKey(fileName)) { //avoid importing againtemplateWarehouse.put(fileName, importer.imports(new File(WebApps.getCurrent().getRealPath(BookUtil.DEFAULT_BOOK_FOLDER), fileName), fileName));}}...}

importer.imports(new File(...)) 返回一个Book。

总结

本文中我们演示了如何将现有的手动Excel文件的流程转换为具有Excel文件,Java和Keikai的Web应用程序。该应用程序与后端服务集成在一起,包括数据库和用户权限控制。可以将相同的技术应用于涉及基于Excel的流程的任何其他方案,将手动工作流转变为自动化和集成的Web Apps。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/267550.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go将服务器图片响应给客户端,Go中来自客户端和服务器的RPC

我目前正在使用thrift(thrift4go)来实现服务器->客户端和客户端->服务器RPC功能。默认情况下&#xff0c;thrift仅像net / rpc一样执行客户端->服务器调用。由于还需要服务器与客户机之间的通信&#xff0c;因此我进行了一些研究&#xff0c;发现bidi-thrift。Bidi-th…

hdu 3887 Counting Offspring

题目连接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid3887 题意&#xff1a;给出一棵树&#xff0c;对于每一个节点&#xff0c;问他的子孙节点中有多少个节点小于该节点。 思路&#xff1a;首先找出这棵树的DFS序列&#xff0c;每一个节点出现在两个位置&#xff…

搭建vue开发环境的步骤

搭建vue开发环境的步骤 相信很多人在刚开始学习vue这个框架的时候&#xff0c;在最开始搭建开发环境的时候&#xff0c;都会遇到一些大大小小的坑&#xff0c;我之前在学习angular的时候搭建过一次&#xff0c;过了一个月后在搭建第二次的时候&#xff0c;竟然有一些混乱&#…

ccxprocess可以禁用么_提效 | 5G时代网站还需要加速么?

看到标题, 你一定会说都已经 5G 时代了, 还要在网络提速上下功夫么? 答案是: 是的. 如果你是做国际或者全球业务的, 会更有体会, 很多国家和地区的网络是非常差的, 说他们停留在 2G 时代也不过分的.即使是国内良好的网络环境下, 做好网络提速, 也是可以提高用户体验以及缓解服…

springboot ---- sts如何启动两个实例

idea 实现启动两个spring boot工程实例的方法 可以参考这篇博客。 因为使用spring tool suite 学习spring cloud 需要实例化两个spring boot 工程来构成一个小小的集群系统&#xff0c; 可以通过sts ,右击选择run as, 使用run configuration..., 配置jvm参数为 -Dserver.portxx…

java trie实现

http://www.riaos.com/ria/5814

如何利用python在一个wen'dang'li_如何利用Python网络爬虫给自己跟朋友来一份穷游攻略!走到哪里穷到哪里的哦!...

【一、项目背景】穷游网提供原创实用的出境游旅行指南、攻略&#xff0c;旅行社区和问答交流平台&#xff0c;以及智能的旅行规划解决方案&#xff0c;同时提供签证、保险、机票、酒店预订、租车等在线增值服务。穷游“鼓励和帮助中国旅行者以自己的视角和方式体验世界”。今天…

ubuntu下命令安装与卸载软件方法

2019独角兽企业重金招聘Python工程师标准>>> 刚 刚接触ubuntu就开始一直在安装语言包、升级、常用软件和其它相关依赖等等东西&#xff0c;总的来说&#xff0c;是一个劲儿的装&#xff0c;缺全然不知怎么卸载&#xff0c;现在把安装和卸载写 在一块&#xff0c;一旦…

为什么用线程池

1、创建/销毁线程伴随着系统开销&#xff0c;过于频繁的创建/销毁线程&#xff0c;会很大程度上影响处理效率 2、线程并发数量过多&#xff0c;抢占系统资源从而导致阻塞 3、对线程进行一些简单的管理 延时执行、定时循环执行的策略等 https://www.cnblogs.com/dongguacai/p/60…

delphi7存取配置文件与sqlserver数据库连接_Delphi7存取配置文件与SQLServer数据库连接...

时间:2012-06-28 18:57来源:未知 整理:寂涯网络 点击:2975次引言Delphi7 具有强大的数据库应用程序开发功能 在使用Delphi7 开发 房产管理系统 时&#xff0c;后台数据库采用 SQLServer 数据库管理系统 使用 TADOConnection 组件与数据库建立连接&#xff0c;需要指定 TADOConn…

C 语言和C++语言的 struct对比

C 语言和C语言的 struct对比 author: stonehat 1. C语言将struct当成类来处理的&#xff0c;所以C的struct可以包含C类的所有东西&#xff0c;例如构造函数&#xff0c;析构函数&#xff0c;友元等&#xff0c;C的struct和C类唯一不同就是 struct成员默认的是public&#xff0c…

UOJ.117.欧拉回路

题目链接 这数据。。简直了 有自环和重边&#xff0c;有些点可能没有连边(并查集不好使 就DFS吧) 因为重边自环可能非常多&#xff0c;同一个点可能经过n次&#xff0c;所以必须要重置表头H[x](类似当前弧优化) 另外是找欧拉回路不是欧拉路径&#xff0c;判断不要错 无向图: 所…

c++ linux 线程等待与唤醒_C++并发编程 等待与唤醒

C并发编程 等待与唤醒条件变量条件变量, 包括(std::condition_variable 和 std::condition_variable_any)定义在 condition_variable 头文件中, 它们都需要与互斥量(作为同步工具)一起才能工作.std::condition_variable 允许阻塞一个线程, 直到条件达成.成员函数void wait(std:…

iOS 动画基础总结篇

iOS 动画基础总结篇 动画的大体分类(个人总结可能有误) 分类.png UIView 动画 属性动画 12345678910111213141516171819[UIView beginAnimations:nil context:nil];[UIView setAnimationDelay:1];[UIView setAnimationDuration:2];[UIView setAnimationRepeatCount:100];[UIVie…

最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用41

入局在北京初冬寒风的吹拂下&#xff0c;吴言渐渐清醒了起来&#xff0c;他现在需要做的是赶紧想办法应付下周赵海波要召开的会议&#xff0c;而不是无谓的生气。吴言走在冬日的街头&#xff0c;一边走一边想。下周的事情还真不少&#xff0c;第一件事是赵海波要召开的公司改组…

bootstraptable获得所有行_郎酒领衔,2020“川酒全国行”首站香满花城

12月8日&#xff0c;2020“川酒全国行”首站在广州开启。以“川酒金花醉美珠江”为主题&#xff0c;本次活动由四川省经济和信息化厅指导&#xff0c;四川中国白酒金三角酒业协会主办&#xff0c;四川郎酒股份有限公司承办&#xff0c;五粮液、泸州老窖、剑南春、舍得、水井坊协…

判断DataRow中某列是否为空的方法

DataRow drdt.Rows[0]; if(!dr.IsNull("列名")) { //不为空的操作 }转载于:https://www.cnblogs.com/12go/archive/2011/10/09/2203606.html

centos7根据端口查进程_记录一次CentOs7下Nginx+WSGI部署Django项目(超详细)

记录一次Django部署的文章&#xff0c;不是很熟悉Linux系统&#xff0c;踩了不少坑&#xff0c;本篇文章相当于是一个总结&#xff0c;我会在本文中详细介绍&#xff0c;部署单个Django项目和多个Django的方法&#xff0c;如读者有更好的方法&#xff0c;欢迎留言一起探讨~长话…

淘宝跨域获取Cookie分析

最近在发现使用Taobao的时候的一个小细节&#xff0c;于是便萌发起了写这篇文章。 当我们在 www.taobao.com 中进行登录之后&#xff0c;然后直接切换到 www.tmall.com 域名下&#xff0c;发现www.tmall.com首页的最顶部马上显示成了&#xff1a; 您好, andyfaces。 首先&#…

mqtt 传文件断开连接的原因_MQTT PythonClient断开连接不当

MikeScotty的回答不对。缺少的是启动MQTT网络循环。需要启动循环来处理发送keep-alive包、为QoS 1/2消息进行多段握手以及处理传入的订阅。在如果你只想保持联系或者10秒钟&#xff0c;那么这样的方法就可以了import paho.mqtt.client as mqttimport timeBroker "192.168…