计算机应用研究
APPLICATION RESEARCH OF COMPUTERS
2000　Vol.17　No.5　P.90-92



大中型粮食加工企业销售系统的开发
欧阳骥　欧阳宝蓉
摘  要  分析了开发大中型粮食加工企业销售系统的关键问题，论述了基于客户机/服务器结构解决这些问题的思想、方法。
关键词  销售系统  客户机/服务器  数据库   数据窗口
0  引言
　　随着我国社会主义市场经济的逐步形成和完善，市场竞争日趋激烈。对于利润率很低的大中型粮食加工企业，如果要在严酷的市场竞争中立于不败之地，除了加强企业内部管理，降低生产成本之外，必须加强销售和销售管理。因此，开发和应用销售管理系统，就显得尤为重要。一方面可以实现销售过程中信息管理的现代化，使管理部门和领导者能够及时、准确地了解企业的生产、库存情况。另一方面可以及时、准确地掌握销售过程中各个环节的具体情况，跟踪市场变化，为管理部门及领导者进行决策提供及时、准确的信息依据。再一方面可以有效地杜绝假单、假票，从而避免由于信息的不准确、不及时可能造成的损失。
1  现行系统分析
　　通过对南方面粉(穗港)股份有限公司和桂林面粉厂的实际销售业务过程的全面、深入、详细调查研究后，得出实际销售业务流程如图1所示。

图1　手工处理的销售业务的业务流程图
　　其中，最关键的处理是发票的处理。发票处理的主要日常工作有(补)开发票、发票注销和冲抵。
　　企业销售过程中开出的发票有普通发票、增值税发票、出口发票三种类型。普通发票分为免税和不免税两种。无论是否免税，发票的最高限额为百万元；增值税发票的最高限额有万元、十万元和百万元三种。无论是哪一种发票，票面所开的总金额不能超过相应的限额。
　　(1)  开发票
　　一般情况是根据客户的要求开具相应的发票。而对于代销点，不定期地为其开出所要求的发票。若代销点的客户要求发票上添写自己的名字，可以满足其要求，但仍按代销点结算。
　　对于信誉好的一般客户可以先开提单或先开出门证，以后再根据开出的提单或出门证及客户的要求补开发票。
　　特别地，根据客户的行业性质、信用情况、购买货物品种、数量多少以及季节的变化，在下面情况下可以享受不同程度的优惠。
　　．同一客户在不同的时间购买相同品种的货物；
　　．同一客户在相同的时间购买不同品种的货物；
　　．不同的客户在相同的时间购买不同品种的货物；
　　．不同的客户在不同的时间购买相同品种的货物。
　　因此，为了便于掌握和了解市场情况，需要将客户信息如：名称、开户银行、帐号、联系方法、所属行业、资信情况等信息记录下来，以方便使用和查询。同时，应把企业有关领导或销售部门负责人或有关业务员签字(或确认)出具的客户优惠方案记录下来，以便在给客户开具发票时能够享受到相应的优惠方案。
　　(2)  发票注销、冲抵
　　当提走的货物出现斤两不足或质量问题时，有降价、换货(换同样的货或换不同的货)、退货(部分退货或全部退货)几种处理方法。其中除了斤两不足中的补货和质量问题中换同样的货账面不反映外，其它均要在账面反映。
　　．斤两不足和质量问题中的降价处理办法是：已入账则冲差额；若未入账则注销原发票后重开。 
　　．对于质量问题中的退全部货物处理办法是：若已入账则全冲；若未入账：则注销。
　　．对于质量问题中的退部分货物和换不同的货物，处理办法是：若已入账：则全冲；若未入账：则注销后重开。
　　当发票注销时在发票上加盖蓝色的带有“注销”字样的戳，对于注销的发票在记帐时忽略。冲抵帐时用红字冲抵。
　　对于普通发票，可以冲差额，也可以全冲。对于增值税发票，不能冲差额，只能全冲。若要冲差额，必须先冲该增值税发票，再开一张所需金额的增值税发票和剩余金额的增值税发票，然后再全冲该增值税发票。
　　对于提单、出门证及分单也有类似的处理要求。系统功能如图2所示。

图2　系统功能主层次结构图
2  系统设计与实现
　　对于前面所分析的关键的处理问题在系统设计和实现过程中，采用基于Windows NT的客户/服务器结构，从服务器和客户端两个方面着手来加以解决。
2.1  系统开发与应用环境
　　通过对实际业务工作的分析，再综合费用、操作的方便性、数据信息的安全性和系统的扩展性等因素，决定系统的开发与应用环境采用基于Windows NT的客户/服务器结构。所有的库存数据集中存储在服务器中，从而便于实现对数据的定期维护和备份以及在数据的完整性、管理和安全性方面提供严格的集中控制。工作站运行应用程序，向服务器发出数据查询或更新操作请求，服务器接受操作请求并把处理结果回送给工作站。
　　服务器端选用Microsoft SQL Server 6.5。首先，它是基于Windows NT，使用的是Win32操作系统线程而不是模拟数据库内核中的线程，拥有Win32线程的所有特征――对多线程处理的强大支持、直接硬件访问和对不同线程的内存保护。从而使得即使有单个的线程有毛病也不会造成整个可执行体崩溃，并且SQL Server 6.5能够捕获这种线程且继续执行。其次，SQL Server 6.5支持Windows NT支持的所有网络协议，从而支持几乎任何网络的客户。最后，SQL Server 6.5的费用比其它的大型关系数据库产品要低得多，更容易被接受。
　　客户端开发工具选用PowerBuilder 6.0。它是一个面向对象的客户/服务器开发工具，可以在Windows系列、Macintosh、Sun Solaris等开发平台上运行。此外，具有方便灵活的集成开发环境，特别是它所提供的功能强大的数据窗口(DataWindow)描绘器，通过使用数据窗口及其交互功能，开发人员可以方便地设计数据处理界面，实现对数据库的各种操作，如数据检索、插入、删除、更新以及按需要的格式显示数据。从而可以大大减少程序编写和调试工作量，提高开发效率，缩短开发周期。
2.2  服务器端设计的关键问题
　　在服务器端的设计和实现中，最主要和最关键的就是数据库的设计。而数据库设计的基础和核心是数据库中表的定义。表定义的好坏不仅影响到其它数据库对象的定义，而且影响到所开发的整个系统的性能和质量。因此，本部分仅就如何定义好表加以阐述。
　　数据库表的定义是以分析所得到的数据流程图和数据字典为基础，以概念结构设计所得到的E-R图为根据进行的，并且应该是规范化的。但是，对于实际应用系统的表，如果定义成规范化的表，由于具体系统的功能和性能要求的限制，反而有缺陷和不足。这些缺陷和不足主要表现在：
　　．对于规范化的表，需要更多的连接来把信息从多个表中组合到一起。而连接需要更多的I/O操作。
　　．处理规范化的表时，需要CPU资源来执行连接逻辑和维护数据和引用完整性，因此会引起额外的CPU操作，
　　．规范化的表没有摘要数据，因为摘要数据从两方面违反了规范化：它是多余的信息以及没有独立的事务意义。而为了处理摘要数据，需要进行聚合处理，从而引起大量的CPU处理和I/O访问。
　　．在规范化的表中，保存的数据难以反映出系统对表中数据的特定处理要求。
　　为了解决上面规范化表所存在的缺陷和不足，本系统在表的定义时一般有意违反规范化要求而将表定义成非规范化的。表的非规范化定义的基本方法是数据冗余，具体的方法有：
　　．适当增加表。对于经常要使用、相对变化很小的某些实体的属性抽出来另外定义成一些表，将这些数据保存在增加的表中。如在本系统中将货物的基本情况：编号、名称、规格、计量单位、单价另外定义成货物目录表。一方面在各种单据处理时凡涉及到货物时就以弹出方式提供给操作者，可以提高数据录入的速度以及减少数据的输入性错误。另一方面可以有效地保证数据的完整性和便于系统管理员对数据的维护。
　　．表的分解。为了方便处理和减少处理过程中I/O操作，常将实际使用中的表按内容的使用频度和具体的处理要求分解成几个表。如本系统的发票，一张发票可以开一种或几种货物，且可几次交款。因此，对于实际的一张发票在定义时分解成三个表：invoice_table(发票表)，invoice_volue(发票内容表)和invoice_charge(发票付款表)，后两个表用外码invoice_no(发票编码)和invoice_table建立起关联。
　　．人为增加列。人为增加列包括人为增加主关键字列、处理标识列和派生列三种方式。
如客户档案表，本系统人为增加序号(serial_no)作为表的主关键字并以此建立簇索引，并且在表的数据录入时由代码处理生成而不是由操作者输入的办法来解决其唯一性问题。
　　处理标识列是为了反映出系统对表中数据的特定处理情况而增加的列。如本系统中为了标识注销和冲抵处理，在相应单据的主表中增加注销(discard _or)和冲抵(against_or)列来加以标识；又如在invoice_volue表中增加(write_or)和(write_out)列来标识发票中的某种货物是否开提单和是否开完。一般地，处理标识列都是binary类型。
　　派生列是为了避免处理时的重复计算减少I/O和CPU操作而增加的列。如发票中有所开货物的总量和总金额栏，在各种查询或统计时都要频繁访问，但其内容可以通过invoice_no码关联invoice_volue表计算得到。根据规范化要求，这样的列是应该去掉的。但实际开发时在invoice_table派生出这些列，在数据输入保存到数据库时计算一次并保存在表中，以后每次检索查询或统计时就不需要再通过连接计算求得而可以直接得到。
2.3  客户端设计的关键问题
　　用PowerBuilder 6.0开发客户端应用程序时，应该充分利用PowerBuilder 6.0的数据窗口及其交互功能。在开发过程中的关键问题是如何在一个数据窗口中向数据库提交数据时保证多个表的数据同步，既对表的更新要么都更新，要么一个也不更新。下面以开发票为例说明解决这两个问题的思想和办法：
　　定义开发票的数据窗口d_invoice和对应的数据窗口控件dw_invoice，d_invoice的数据源为三个表：invoice_table，invoice_volue，goods_count_table(货物帐存表)，选择的列全部是invoice_table的列而不选择其它两个表的列。在创建数据窗口对象时invoice_table定义为可修改的，而其它两个表定义为不可修改的。
　　在更新各个表的数据时先更新invoice_table 表，然后用Modify函数依次改变已更新表中列的update属性为NO、设定要更新的表和修改表中列的update属性为YES。如果所有的表更新成功，则用COMMIT命令将数据提交，完成对应表的更新。其&quot;提交&quot;按钮的Clicked事件中的代码如下：
integer  update_num
//修改invoice_table表,其修改特性在数据窗口定义时定义为可修改
update_num=dw_invoice.update(TRUE,FALSE)
if  update_num=1  then 
　dw_invoice.Modify(＂invoice_table.invoice_no.update=NO＂)
　dw_invoice.Modify(＂invoice_table.invoice_date.update=NO＂)
　…//类似的命令形式关闭invoice_table表中所有列的update属性
　dw_invoice.Modify(＂DataWindow.table.updateTable=～＂
　　invoice_volue)
　//使invoice_volue表可更新且打开表中所有列的update属性
　dw_invoice.Modify(＂invoice_volue.invoice_no.update=YES＂)
　dw_invoice.Modify(＂invoice_volue.serial_no.update=YES＂)
　…  
//更新invoice_volue表
update_num=dw_invoice.update( )
　if  update_num=1  then
　dw_invoice.Modify(＂invoice_volue.invoice_no.update=NO＂)
　dw_invoice.Modify(＂invoice_volue.serial_no.update=NO＂)
　…//类似的命令形式关闭invoice_volue表中所有列的update属性
　dw_invoice.Modify(＂DataWindow.table.updateTable=～＂
　　goods_count_table)
　//使goods_count_table表可更新且打开表中所有列的update属性
　dw_invoice.Modify(＂goods_count_table.goods_no.update=YES＂)
　dw_invoice.Modify(＂goods_count_table.goods_name.update=YES＂)
　…  
　//更新goods_count_table表
　update_num=dw_invoice.update( )
　if  update_num=1  then
　COMMIT USING SQLCA;  //所有表修改成功，提交
　dw_invoice.Modify(＂goods_count_table.goods_no.update=NO＂)
　dw_invoice.Modify(＂goods_count_table.goods_name.update=NO＂)
　…//类似的命令形式关闭goods_count_table表中所有列的
　　//update属性
　dw_invoice.Modify(＂DataWindow.table.updateTable=～＂
　　　invoice_table)
　　//使invoice_table表可更新且恢复表中所有列的update属性 
　dw_invoice.Modify(＂invoice_table.invoice_no.update=YES＂)
　dw_invoice.Modify(＂invoice_table.invoice_date.update=YES＂)
　…  
　cb_continue.enabled=true　//继续按钮有效 
　Else
　　ROLLBACK USING SQLCA;　//修改不成功，滚回所有的修改
　End if
Else
　ROLLBACK USING SQLCA;
End if
Else
ROLLBACK USING SQLCA;
End if
欧阳骥(郑州粮食学院计算机科学系  郑州 450052)
欧阳宝蓉(桂林面粉厂  桂林 541003)
参考文献
1，萨师煊, 王  珊著. 数据库系统概论(第二版). 北京: 高等教育出版社, 1997
2，D.Solomon, R.Rankins著, 熊桂喜, 高  峰, 冯学民译. SQL SERVER 6.5开发指南(第二版). 北京: 清华大学出版社, 1998
3，吴洁明编著. PowerBuilder 6.0应用与开发. 北京: 清华大学出版社, 1998
4，张长富, 李  匀, 严苏娅编著. PowerBuilder 6.0用户参考手册. 北京希望电脑公司, 1998
5，Simon Gallagher, Simon Herbert著. 康博创作室译. PowerBuilder 6.0程序设计大全. 北京:机械工业出版社, 1998
收稿日期：1999-11-18
