1.前言
作为软件服务,有时候我们接受的任务是一个全新的,开发团队内没有人有相关经验的这么一个任务,这时候无论对于我们本身还是对于团队来说,都面临着一个新的挑战,如何在没有做过此类任务情况下凭借一些基本经验完成任务,顺利实现给客户的交付,我们还是有一些基本经验可以借鉴的。此贴为此方面的综合帖之一,重点在于记录在探索过程中的经验,并将其分享给大家,大家需要的部分请自行参考,以便加强我们的团队在面对新情况时的应变能力。
此次探索过程以惠众报表开发需求为背景,探索内容也以惠众KIS积累内容为主,在此特别说明。
1.1 此次针对开发条件为金蝶KIS12.0版。
1.1.1基本任务概况
首先说一下我们此次任务的基本概况。
根据前期需求调研的情况,惠众石油在生产过程中使用了KIS12.0专业版作为基本销售数据的管理工具。而我们开发的惠众二期项目,需要从金蝶KIS12.0专业版中调取数据作为数据源,形成能够及时监测销售数据的统计分析系统,给管理层提供统计结果,以便他们进行销售决策,管理公司日常销售行为和工作。这就是本次开发工作的基本目的。
惠众石油能够给我方提供的包括以下资源:
1、KIS专业版的安装包;
2、当前使用的数据库的备份文件。
3、KIS专业版由一个能够联系到的金蝶销售人员。
1.1.2 我方对金蝶KIS12.0的了解情况
一片空白。
1.2 对于基本情况的分析及解决探索过程记录
1.2.1 设想:一般这样的二次开发可选取的方式有两种,第一种是此类通用软件一般来说商家都会开放二次开发的接口公开给大家,以加强产品的适用性,所以应该在网上能够找见此类开发接口的说明包或者代码文件、SDK包,安装使用即可;第二种是找到它的库文件,直接访问它的库文件获取数据。
1.2.2商务人员提供线索:她认识曾经做过金蝶产品二次开发的人,看能不能提供帮助。于是我约了此人,一番讨论后后得知,他们做的是金蝶K3财务软件的二次开发,对于KIS没有接触过,但是提供了线索:他们直接访问K3的库进行开发。
1.2.3在网上搜索相关资料,可以找到的是KIS库的基本说明,里面列出了库中字段名称及基本意义,即一个不知道跟目前版本是否相配的数据字典。
1.2.4未找到金蝶KIS的二次开发工具包。因此第一种方式被否定。
1.2.5测试安装汇众提供的KIS12.0专业版的安装包,发现其安装后系统内多了SQLServer的服务,就是在电脑的桌面右下角多了SQLServer的运行图标。查看原始安装包,发现其内含了MSDE的安装包。
1.2.6 在百度上查找MSDE的相关情况,得知MSDE是SQLServer2000的减缩版本。
1.2.7查找百度,看看怎么能够访问MSDE,得知在本机上安装SQLServer2000的查询分析器即可访问。根据以前的经验,SQLServer2008的企业查询分析器同样可以访问,并且有良好的兼容性。于是确定了看看用 SQLServer2008的企业查询分析器能否访问的想法。
1.2.8根据以前经验,KIS安装后一定有地方记录了数据库访问的用户名、密码,但是未找到。请教金蝶销售人员,得知此配置文件位于MSDE的安装包内。于是在MSDE安装包内找到了配置文件。使用 SQLServer2008的企业查询分析器连接安装好的KIS数据库成功。
1.2.9让运营线人员帮助,建立金蝶FAT测试虚机,在此虚机上安装了KIS,并且将惠众的真实数据恢复到此虚机上,完成了对生产环境的模拟。测试用 SQLServer2008的企业查询分析器连接此虚机上的MSDE库,成功。
1.2.10 确定采用直接访问金蝶数据库进行报表二次开的方案可行。
1.2.11 周知相关干系人。需求人员得知后将我方方案简单汇报给惠众公司。运营线工作人员得知后配合建立了10.0.3.119虚机作为fat金蝶测试环境,建立10.0.3.203虚机作为惠众1.2MYSQL库的运行虚机,建立了10.0.3.202虚机作为惠众1.2产品测试发布虚机。
2.测试环境已搭建好后的探索
2.1 数据库连接信息在KIS安装包的KDPRODUCT\MSDE\setup.ini文件中。SAPWD=”kingdee”说明连接采用“SA”账户,密码为“kingdee”。
2.2 根据资料提示,MSDE库是允许局域网范围的链接访问的。访问方式与sqlserver2000一样,用sqlserver的查询分析器就可以访问,也可以用sqlserver的jar包。找到jar包,在Eclipse中建立一个简单的动态网站项目来进行数据访问。
2.3 用select语句通过简单写好的DAO,查询金蝶库中的任意表格数据,得到结果,说明查询管道建立成功。
2.4根据经验,软件都有用户表,用来存放用户的基本数据,因此用 SQLServer2008的企业查询分析器直接查看数据库,看看相关的数据结构。简单记录结果如下:
用户库用来存放用户基本信息,包括用户名、密码。当下将用得着的字段罗列如下:
[FName]
varchar
NOT NULL 用户名,就是kingdee登陆界面上需要输入的那个用户名。
[FSID]
varchar
NULL 密码,就是kingdee登陆界面上需要输入的那个密码,可以为空。在数据库里村的数据是加密以后的密码。空字符串会被加密为“) F “, ,P T #8 *P!D &D 80!N &@ <0 C ‘< : !M &4 )0 ”;
[FUserID] [smallint] NOT NULL 用户ID,别处会有多处引用。
使用此信息与网上找到的数据字典进行对照,发现有版本差异,但是绝大部分的字段能够在数据字典中得到对应。
2.5根据惠众工作人员提供消息以及需求分析的信息,需要从金蝶数据库提取的数据基本基于它本身的“出库一览表”来进行统计分析。因此决定先用SQLServer2008带的事件查看器抓取 “出库一览表”的SQL语句进行分析。
2.6成功抓取 “出库一览表”的SQL语句,但是是两句,分别如下:
Select top 20000 u1.FDetailID as FListEntryID,(u1.FQty-u1.FAllHookQTY) as FHookQTY,v1.FVchInterID as FVchInterID,v1.FTranType as FTranType,v1.FInterID as FInterID,u1.FEntryID as FEntryID,case when v1.FCheckerID>0 then 'Y' when v1.FCheckerID<0 then 'Y' else '' end as FCheck,v1.Fdate as Fdate,CASE WHEN u1.FHookStatus=1 THEN 'P' WHEN u1.FHookStatus=2 THEN '√' ELSE '' END as FHookStatus,v1.FBillNo as FBillNo,u1.Fauxqty as Fauxqty,u1.Famount as Famount,v1.FStatus as FStatus,case when (u1.FQtyInvoice-u1.FQty)*sign(v1.FRob)>=0 then '√' else ' ' end as FInvoiced,u1.FAuxQtyInvoice as FAuxQtyInvoice,t30.FName as FBaseUnitID,u1.FAuxQtyMust as FAuxQtyMust,case when (v1.FROB <> 1) then 'Y' else '' end as FRedFlag,u1.FConsignAmount as FConsignAmount,u1.FSecQty as FSecQty,case when v1.FCancellation=1 then 'Y' else '' end as FCancellation,u1.FDiscountAmount as FDiscountAmount from ICStockBill v1 INNER JOIN ICStockBillEntry u1 ON v1.FInterID = u1.FInterID AND u1.FInterID <>0
INNER JOIN t_Organization t4 ON v1.FSupplyID = t4.FItemID AND t4.FItemID <>0
LEFT OUTER JOIN t_SubMessage t7 ON v1.FSaleStyle = t7.FInterID AND t7.FInterID <>0
INNER JOIN t_Stock t8 ON u1.FDCStockID = t8.FItemID AND t8.FItemID <>0
LEFT OUTER JOIN t_Emp t9 ON v1.FFManagerID = t9.FItemID AND t9.FItemID <>0
LEFT OUTER JOIN t_Emp t10 ON v1.FSManagerID = t10.FItemID AND t10.FItemID <>0
INNER JOIN t_User t11 ON v1.FBillerID = t11.FUserID AND t11.FUserID <>0
INNER JOIN t_ICItem t14 ON u1.FItemID = t14.FItemID AND t14.FItemID <>0
INNER JOIN t_MeasureUnit t17 ON u1.FUnitID = t17.FItemID AND t17.FItemID <>0
LEFT OUTER JOIN t_User t24 ON v1.Fcheckerid = t24.FUserID AND t24.FUserID <>0
INNER JOIN t_MeasureUnit t30 ON t14.FUnitID = t30.FItemID AND t30.FItemID <>0
LEFT OUTER JOIN t_SubMessage t40 ON v1.FMarketingStyle = t40.FInterID AND t40.FInterID <>0
LEFT OUTER JOIN v_ICTransType t70 ON u1.FSourceTranType = t70.FID AND t70.FID <>0
LEFT OUTER JOIN ICVoucherTpl t13 ON v1.FActualVchTplID = t13.FInterID AND t13.FInterID <>0
LEFT OUTER JOIN t_Department t105 ON v1.FDeptID = t105.FItemID AND t105.FItemID <>0
LEFT OUTER JOIN t_Emp t106 ON v1.FEmpID = t106.FItemID AND t106.FItemID <>0
LEFT OUTER JOIN t_AuxItem t112 ON u1.FAuxPropID = t112.FItemid AND t112.FItemid <>0
LEFT OUTER JOIN t_MeasureUnit t500 ON t14.FStoreUnitID = t500.FItemID AND t500.FItemID <>0
LEFT OUTER JOIN t_Currency t503 ON v1.FCurrencyID = t503.FCurrencyID AND t503.FCurrencyID <>0
LEFT OUTER JOIN ZPStockBill t523 ON v1.FInterID = t523.FRelateBillInterID AND t523.FRelateBillInterID <>0
LEFT OUTER JOIN t_MeasureUnit t552 ON t14.FSecUnitID = t552.FItemID AND t552.FItemID <>0
where 1=1 AND (v1.FTranType=21 AND (v1.FROB=1 and v1.FCancellation = 0)) order by v1.FInterID,u1.FEntryID
第二个:
select top 40 u1.FDetailID AS FListEntryID,’’ AS FSel,t13.FName AS FActualVchTplName,v1.FPlanVchTplID AS FPlanVchTplID,v1.FActualVchTplID AS FActualVchTplID,(u1.FQty-u1.FAllHookQTY) AS FHookQTY,v1.FSupplyID AS FSupplyID,v1.FVchInterID AS FVchInterID,v1.FTranType AS FTranType,v1.FInterID AS FInterID,u1.FEntryID AS FEntryID,v1.Fdate AS Fdate,case when v1.FCheckerID>0 then ‘Y’ when v1.FCheckerID<0 then ‘Y’ else ‘’ end AS FCheck,case when v1.FCancellation=1 then ‘Y’ else ‘’ end AS FCancellation,v1.FBillNo AS FBillNo,t7.FName AS FSaleStyleName,t4.FName AS FSupplyIDName,t8.FName AS FDCStockIDName,t14.FNumber AS FFullNumber,t14.Fname AS FItemName,t14.Fmodel AS FItemModel,t17.FName AS FUnitIDName,u1.FBatchNo AS FBatchNo,u1.Fauxqty AS Fauxqty,u1.Fauxprice AS Fauxprice,u1.Famount AS Famount,t9.FName AS FFManagerIDName,t10.FName AS FSManagerIDName,t11.FName AS FuserName,t24.FName AS FCheckerName,t4.FItemID AS FCustID,case when v1.FVchInterID>0 then ‘Y’ when v1.FVchInterID<0 then ‘Y’ else ‘’ end AS FVoucherStatus,u1.FNote AS FNote,(SELECT (SELECT FName FROM t_VoucherGroup WHERE FGroupID=t_Voucher.FGroupID)+’-’+CONVERT(Varchar(30),FNumber) FROM t_Voucher WHERE FVoucherid=v1.FVchInterID) AS FVoucherNumber,CASE WHEN u1.FHookStatus=1 THEN ‘P’ WHEN u1.FHookStatus=2 THEN ‘√’ ELSE ‘’ END AS FHookStatus,t40.FName AS FMarketingStyleName,v1.FMarketingStyle AS FMarketingStyle,u1.FOrderBillNo AS FOrderBillNo,u1.FSourceBillNo AS FSourceBillNo,t70.FName AS FSourceTranType,t105.FName AS FDeptIDName,t106.FName AS FEmpIDName,v1.FExplanation AS FExplanation,v1.FFetchAdd AS FFetchAdd, (CASE t112.FName WHEN ‘*’ THEN ‘’ ELSE t112.FName END) AS FAuxPropIDName,t14.FQtyDecimal AS FQtyDecimal,t14.FPriceDecimal AS FPriceDecimal,v1.FOrgBillInterID AS FOrgBillInterID,v1.FStatus AS FStatus,case when (v1.FOrgBillInterID <> 0) then ‘Y’ else null end AS FHasSplitBill,case when (u1.FQtyInvoice-u1.FQty)*sign(v1.FRob)>=0 then ‘√’ else ’ ’ end AS FInvoiced,u1.FAuxQtyInvoice AS FAuxQtyInvoice,t30.FName AS FBaseUnitID,u1.FAuxQtyMust AS FAuxQtyMust,Case WHEN t14.FStoreUnitID=0 THEN ‘’ Else t500.FName end AS FCUUnitName,Case When v1.FCurrencyID is Null Or v1.FCurrencyID=’’ then (Select FScale From t_Currency Where FCurrencyID=1) else t503.FScale end AS FAmountDecimal,case when (v1.FROB <> 1) then ‘Y’ else ‘’ end AS FRedFlag,t523.FBillNo AS FZPBillNo,u1.FConsignPrice AS FConsignPrice,u1.FConsignAmount AS FConsignAmount,CASE WHEN v1.FTranStatus=1 THEN ‘Y’ ELSE ‘’ END AS FTranStatus,t552.FName AS FSecUnitName,u1.FSecCoefficient AS FSecCoefficient,u1.FSecQty AS FSecQty,u1.FDiscountRate AS FDiscountRate,u1.FDiscountAmount AS FDiscountAmount from ICStockBill v1 INNER JOIN ICStockBillEntry u1 ON v1.FInterID = u1.FInterID AND u1.FInterID <>0
INNER JOIN t_Organization t4 ON v1.FSupplyID = t4.FItemID AND t4.FItemID <>0
LEFT OUTER JOIN t_SubMessage t7 ON v1.FSaleStyle = t7.FInterID AND t7.FInterID <>0
INNER JOIN t_Stock t8 ON u1.FDCStockID = t8.FItemID AND t8.FItemID <>0
LEFT OUTER JOIN t_Emp t9 ON v1.FFManagerID = t9.FItemID AND t9.FItemID <>0
LEFT OUTER JOIN t_Emp t10 ON v1.FSManagerID = t10.FItemID AND t10.FItemID <>0
INNER JOIN t_User t11 ON v1.FBillerID = t11.FUserID AND t11.FUserID <>0
INNER JOIN t_ICItem t14 ON u1.FItemID = t14.FItemID AND t14.FItemID <>0
INNER JOIN t_MeasureUnit t17 ON u1.FUnitID = t17.FItemID AND t17.FItemID <>0
LEFT OUTER JOIN t_User t24 ON v1.Fcheckerid = t24.FUserID AND t24.FUserID <>0
INNER JOIN t_MeasureUnit t30 ON t14.FUnitID = t30.FItemID AND t30.FItemID <>0
LEFT OUTER JOIN t_SubMessage t40 ON v1.FMarketingStyle = t40.FInterID AND t40.FInterID <>0
LEFT OUTER JOIN v_ICTransType t70 ON u1.FSourceTranType = t70.FID AND t70.FID <>0
LEFT OUTER JOIN ICVoucherTpl t13 ON v1.FActualVchTplID = t13.FInterID AND t13.FInterID <>0
LEFT OUTER JOIN t_Department t105 ON v1.FDeptID = t105.FItemID AND t105.FItemID <>0
LEFT OUTER JOIN t_Emp t106 ON v1.FEmpID = t106.FItemID AND t106.FItemID <>0
LEFT OUTER JOIN t_AuxItem t112 ON u1.FAuxPropID = t112.FItemid AND t112.FItemid <>0
LEFT OUTER JOIN t_MeasureUnit t500 ON t14.FStoreUnitID = t500.FItemID AND t500.FItemID <>0
LEFT OUTER JOIN t_Currency t503 ON v1.FCurrencyID = t503.FCurrencyID AND t503.FCurrencyID <>0
LEFT OUTER JOIN ZPStockBill t523 ON v1.FInterID = t523.FRelateBillInterID AND t523.FRelateBillInterID <>0
LEFT OUTER JOIN t_MeasureUnit t552 ON t14.FSecUnitID = t552.FItemID AND t552.FItemID <>0
where 1=1 AND (v1.FTranType=21 AND (v1.FROB=1 and v1.FCancellation = 0)) and ( (v1.FInterID=1944 and u1.FEntryID=12) or (v1.FInterID=1944 and u1.FEntryID=13) or (v1.FInterID=1946 and u1.FEntryID=1) or (v1.FInterID=1946 and u1.FEntryID=2) or (v1.FInterID=1946 and u1.FEntryID=3) or (v1.FInterID=1948 and u1.FEntryID=1) or (v1.FInterID=1948 and u1.FEntryID=2) or (v1.FInterID=1948 and u1.FEntryID=3) or (v1.FInterID=1948 and u1.FEntryID=4) or (v1.FInterID=1948 and u1.FEntryID=5) or (v1.FInterID=1948 and u1.FEntryID=6) or (v1.FInterID=1948 and u1.FEntryID=7) or (v1.FInterID=1948 and u1.FEntryID=8) or (v1.FInterID=1948 and u1.FEntryID=9) or (v1.FInterID=1948 and u1.FEntryID=10) or (v1.FInterID=1949 and u1.FEntryID=1) or (v1.FInterID=1950 and u1.FEntryID=1) or (v1.FInterID=1950 and u1.FEntryID=2) or (v1.FInterID=1950 and u1.FEntryID=3) or (v1.FInterID=1950 and u1.FEntryID=4) or (v1.FInterID=1950 and u1.FEntryID=5) or (v1.FInterID=1950 and u1.FEntryID=6) or (v1.FInterID=1950 and u1.FEntryID=7) or (v1.FInterID=1951 and u1.FEntryID=1) or (v1.FInterID=1957 and u1.FEntryID=1) or (v1.FInterID=1957 and u1.FEntryID=2) or (v1.FInterID=1957 and u1.FEntryID=3) or (v1.FInterID=1957 and u1.FEntryID=4) or (v1.FInterID=1958 and u1.FEntryID=1) or (v1.FInterID=1958 and u1.FEntryID=2) or (v1.FInterID=1958 and u1.FEntryID=3) or (v1.FInterID=1958 and u1.FEntryID=4) or (v1.FInterID=1958 and u1.FEntryID=5) or (v1.FInterID=1958 and u1.FEntryID=6) or (v1.FInterID=1958 and u1.FEntryID=7) or (v1.FInterID=1958 and u1.FEntryID=8) or (v1.FInterID=1958 and u1.FEntryID=9) or (v1.FInterID=1960 and u1.FEntryID=1)) order by v1.FInterID,u1.FEntryID
2.7 通过跟踪查询可以看出,查询销售出库信息主要使用的就是上面两个SQL语句,而分析上面两个语句后,发现使用的原始表是:ICStockBill、ICStockBillEntry ,因此必须结合数据字典对这两张表及其有效字段进行分析。分析结果如下:
2.7.1 出入库单据表[ICStockBill]
从KISBOS中可以看到此库的所有字段,根据上面的SQL,结合客户导出的EXCEL表格将其中有用到的字段挑取出来:
字段名 数据类型 表单对应 说明
[FVchInterID] [INTEGER] 凭证内码 凭证ID
[FTranType] [INTEGER] 单据类型 单据类型
[FInterID] [INTEGER] 单据内码 单据ID(关键字)
[FCheckerID] [INTEGER] 审核人 审核人
[Fdate ] [DATETIME] 日期(*): 单据日期
[FBillNo] [STRING] 编号(*): 入库单号
[FStatus] [INTEGER] 状态 0-未审核;1-已审核
[FRob] [INTEGER] 红蓝字 1-蓝字;-1-红字
[FCancellation] [INTEGER] 作废 0-未做废;1-已作废
[FSupplyID ] [INTEGER] 供应商(*): 供应商ID
[FSaleStyle] [INTEGER] 销售方式 销售方式
[FFManagerID ] [INTEGER] 验收: 验收人
[FSManagerID ] [INTEGER] 保管: 保管人
[FBillerID ] [INTEGER] 制单: 制单人
[Fcheckerid ] [INTEGER] 审核: 审核人
[FMarketingStyle ] [INTEGER] 销售业务类型 销售业务类型
[FActualVchTplID ] [INTEGER]
[FDeptID ] [INTEGER ] 部门 部门
[FEmpID ] [INTEGER ] 业务员: 业务员
[FCurrencyID ] [INTEGER] 币别 币别
[FPlanVchTplID ] [INTEGER]
[FExplanation ] [STRING] 摘要 摘要
[FFetchAdd ] [STRING] 交货地点 交货地点
[FOrgBillInterID ] [INTEGER] 源单内码: 原单ID
2.7.2 出入库单据分录表[ICStockBillEntry]
[FDetailID ] [INTEGER]
[FQty] [FLOAT] 基本单位实收数量 实际收入、发出数量
[FAllHookQTY] [FLOAT] 已勾稽数量 已勾稽数量
[FEntryID ] [INTEGER ] 分录号 分录号(关键字二)
[FHookStatus]
[Fauxqty ] [FLOAT ] 实收数量 辅助计量单位的数量
[Famount ] [FLOAT ] 金额 金额
[FQtyInvoice] [FLOAT] 基本单位开票数量 基本单位开票数量
[FAuxQtyInvoice ] [FLOAT ] 开票数量 开票数量
[FAuxQtyMust ] [FLOAT ] 应收数量 辅助账存数量
[FConsignAmount ] [FLOAT] 代销金额 代销金额
[FSecQty ] [FLOAT] 辅助数量 辅助数量
[FDiscountAmount ] [FLOAT ] 折扣额 折扣额
[FDCStockID ] [INTEGER] 调入仓库 调入仓库
[FItemID] [INTEGER ] 物料代码 物料ID
[FUnitID ] [INTEGER ] 单位(*): 计量单位ID
2.8根据以上分析结果,对照在金蝶内的操作查询结果,基本可以确定出库查询的字段意义、统计需要的字段等内容,对上面的SQL语句进行整理即可得到视图hzoil_add_baseout作为基础数据源为惠众1.2的开发提供数据。
3、总结
3.1以上是探索提取“销售出库”业务数据源的过程,对于“采购入库”等过程可以照猫画虎地提取业务的响应sql语句,固化为视图后插入金蝶数据库作为二次开发的数据源使用。
3.2考虑到尽可能不影响金蝶软件的使用,将数据源提取工作固化成为视图或者存储过程是最好的选择。
3.3探索过程中与各方的沟通是需要及时进行的、也是必要的,其中一些关键性的线索是在与他们沟通过程中获取到的,这对于整体探索的进程都有明显影响。
3.4任何一项新技术或手段的研究或探索当以应用为目的时,疏通后即可投入使用,以便节省开发成本,而不应全部研究透彻后在投入使用。