物品单位组设置说明------ERP二次开发文档
在本ERP版本的中设计单位组功能是一个为了平衡进销存以及生产、财务管理模块对物品单位计量换算的需求提供的一种解决方案,该方案可以有效的处理各ERP模块中对同一处理对象根据需求状况合理进行单位的计量换算。
根据老版本ERP中的需求来修改为以EXT.net框架的方式来实现。
3.1 当前设计
本文档将主要以“采购申请单”(以下简称“申请单”)为例来阐述单位组功能的设计以及开发。首先,单位组的设置是在物品档案中进行设置的(如:图示1),在物品档案中可以设置物品的基本单位以及进、销、存等各模块所需的单位,并且可以选择所采用单位组;之后我们在“申请单”的添加明细项功能来添加相应的物品信息,其中就包括我们设计的这个物品单位组字段。
图示1
加载到“申请单”中的单位组(对应的字段名称是:采购单位)默认LOAD出的数据是采购模块所应用的单位(如:图示2,这个单位是在建立物品档案时已经设置好的),如果双击采购单位这个字段所对应的某一单元格的话,页面将进入单位组选择状态(如图示3),这个单位的选择功能是使用了下拉列表的样式来实现的;单位组单位选择完成后,当前行采购单位数据就变为我们所选择的单位了,其间此单位所对应的UnitID也被隐式的加载到了当前行数据(注:明细数据的前台保存方式是放在了EXT所提供的STORE组件中)。
图示2
图示3
3.2 当前开发
要用EXT的方式来实现单位组的数据加载,需要对老版本的实现方法在以下几个方面做些调整即可。
3.2.1 页面上的调整
首先我们现在的页面都改为了纯EXT.net的方式来布局整个界面,此时“申请单”中明细这一块也改为了gridpanel控件和store组件来显示和存储数据,所以为了实现单位组功能,我们也需要在这里做个调整:把“采购单位”这个字段设置为可编辑的状态(Editable=”true”,其实ext:Column默认的Editable属性值就是true,所以Editable=”true”这个属性设置此处可以省略,采用默认即可),另外在“采购单位”字段对应Colmun中还要设置列的编辑域属性,编辑域属性是一个控件的内部嵌套复杂属性,在这个复杂属性中,我们可以添加所需的编辑控件ext:ComboBox。
ComboBox中需要设置以下比较关键的属性:ValueField=”CodeName”,DisplayField=”CodeName”;首先说明一下这两个属性的作用,ValueField属性是用来记录ComboBox所选项的value值,DisplayField属性用来记录ComboBox所选项的Text文本值。之所以把这两个属性都设置为单位的名称CodeName属性,是为了解决单元格编辑域中的ComboBox选择某一项结束编辑后会出现单元格显示单位Value值的现象(这种现象可以通过做一个实验来重现),那么这个CodeName字段是在哪里设定的呢?请参看图示5中是定义的一个Store用来绑定和存储单位组ComboBox中所有可选项的数据,这个Store中一共定义了三个字段,其中CodeName是用来记录单位名称的一个字段,UnitID是用来记录单位ID的一个字段,ExRate是用来记录对应单位和基本单位(在物品档案中进行设置)的换算的比率。
示例代码截图如下:
图示4
图示5
另外,相应的在GridPanel中所对应的Store要有这以下三个字段存在,代码截图如下:
图示6
这三个字段,其中“UsedUnitName”绑定到GridPanel中“采购单位”字段所对应的列,代码书写方法:DataIndex=”UsedUnitName”(代码结构见图示4),另一个字段“UsedUnitID”是用来隐式的存放单位组所选单位对应的ID的,而ExRate是用来记录当前所选单位组中单位与物品基本单位换算的一个比率。
最后,在此GridPanel中还要设置其Listener中的监听事件:beforeEdit事件;这个事件是在GridPanel单元格开始编辑前调用的,如下代码截图:
图示7
另外,可以看到在图示4中,ComboBox控件中也有一个有设置了一个监听事件selectchange()事件,这个事件主要是用来选择ComboBox中的某一项数据后所执行的一个事件。
3.2.2 JS代码的调整
JS中的调整应该分为两块来描述,一块是明细中添加数据时,单位组数据的加载;另一块是选择单位组数据,数据处理。
1.明细中添加数据时,单位组数据的加载以及处理
//添加商品时用到的回调函数,用来填充明细信息
function collback(record) {
Ext.Ajax.request({
method: "post",
async: false,
url: "http://www.cnblogs.com/../Handler/Common/UnitGroup.ashx?action=getunitgroup&ProductId=" + record.get("ProductID") + "&operate=show&unitfrom=InUnitID",
success: function(item) {
var rep = item.responseText.split(","); //把返回过来的单位组内容分开后放进一个数组用于操作
var signFrame = DetailTblStore.data.items.length;
signFrame++;
var usedprice = (parseFloat(record.get("StandardBuy")) * parseFloat(rep[2])).toString();
var usedunit = record.get("UsedUnitID").split(",");
var personRecord = new Ext.data.Record({
ID: record.get("ID"),
ProductID: record.get("ProductID"),
ProductNo: record.get("ProductNo"),
ProductName: record.get("ProductName"),
Specification: record.get("Specification"),
ColorName: record.get("ColorName"),
UnitID: usedunit[0],
UnitName: record.get("UnitID"),
UsedPrice: usedprice,
UnitPrice: record.get("StandardBuy"),
UsedUnitCount: FormatAfterDotNumber(0, precisionLength),
UsedUnitName: rep[1],
UsedUnitID: rep[0],
ExRate: rep[2],
ApplyReasonName: "--请选择--",
ApplyReason: "0",
SignFrame: signFrame
});
DetailTblStore.add(personRecord);
fnTotalInfo();//此方法对明细中所有需要计算的数据进行重新计算
}
});
}
这个方法是在向明细中添加物品时执行的,首先是进行了一次请求获取所需单位组数据后对明细进行赋值。
2. 选择单位组数据,数据处理
主要是定义上述的beforeEdit监听事件;代码如下:
//对DetailTbl(GridPanel)编辑之前,e是当前编辑的单元对象
var beforeEdit = function(e) {
switch (e.field) {
case "UsedUnitName":
ddlUnitStore.removeAll();
Ext.Ajax.request({
method: "post",
async: false,
url: "http://www.cnblogs.com/../Handler/Common/UnitGroup.ashx?action=getunitgroup&ProductId=" + e.record.get('ProductID') + "&operate=read&unitfrom=InUnitID",
success: function(item) {
var fun = new Function("return" + item.responseText);
var json = fun();
for (var i = 0; i < json.length; i++) {
var unitrecord = new Ext.data.Record({
UnitID: json[i].UnitID,
CodeName: json[i].CodeName,
ExRate: json[i].ExRate
});
ddlUnitStore.add(unitrecord);
}
}
});
break;
default:
break;
}
}
这个方法是在双击GridPanel中单元格时进入编辑状态时触发的,此方法主要是为了异步加载当前物品所对应单位组中所有单位的数据到ddlUnitStore中来存储。
另外还有一个方法需要注意:selectchange()方法。这个方法是在单位组下拉框ComboBox选择数据时执行,代码如下:
//当下拉列表ddlunit的下拉选项改变时
function selectchange() {
var lastedit = DetailTbl.lastEdit.row;
var unitname = null;
var basecount = null;
var usedunitid = null;
var exrate = null;
for (var i = 0; i < ddlUnitStore.data.items.length; i++) {
if (ddlUnitStore.data.items[i].data.CodeName == ddlUnit.getText()) {
var unitexratechange = ddlUnitStore.data.items[i].data.ExRate;
var productExrate = DetailTblStore.getAt(lastedit).get("ProductID") + "&" + unitexratechange;
var frame = false;
for (var index = 0; index < unitexrate.length; index++) {
if (unitexrate[index].split("&")[0] == DetailTblStore.getAt(lastedit).get("ProductID")) {
unitexrate[index] = productExrate;
frame = true;
}
}
if (frame == false) {
unitexrate.push(productExrate);
}
usedprice = parseFloat(DetailTblStore.data.items[lastedit].data.UnitPrice) * parseFloat(unitexratechange);
unitname = ddlUnitStore.data.items[i].data.CodeName;
usedunitid = ddlUnitStore.data.items[i].data.UnitID;
basecount = DetailTblStore.data.items[lastedit].data.UsedUnitCount;
exrate = ddlUnitStore.data.items[i].data.ExRate
}
}
geteditvalue(DetailTblStore, usedunitid, 'UsedUnitID');
geteditvalue(DetailTblStore, unitname, 'UsedUnitName');
geteditvalue(DetailTblStore, exrate, 'ExRate');
fnTotalInfo();//此方法对明细中所有需要计算的数据进行重新计算
}
function geteditvalue(storename, controlvalue, storedataname) {
var requiredatetext = controlvalue;
var lastedit = DetailTbl.lastEdit.row;
var signframe = DetailTblStore.getAt(lastedit).data["SignFrame"];
var getListProductID = DetailTblStore.getAt(lastedit).data["ProductID"];
var flagrow = 0;
for (var i = 0; i < storename.data.length; i++) {
if (storename.data.items[i].data.ProductID == getListProductID && storename.data.items[i].data.SignFrame == signframe) {
flagrow = i;
}
}
storename.getAt(flagrow).set(storedataname, requiredatetext);
}
这个代码处理快主要是把ComboBox所选项以及相关数据赋值到当前单元格所在行所对应的字段中。
3.2.3 对处理文件中的代码做相应调整
可以看一下这个拼接的URL字符串
url: "http://www.cnblogs.com/../Handler/Common/UnitGroup.ashx?action=getunitgroup&ProductId=" + e.record.get('ProductID') + "&operate=read&unitfrom=InUnitID",
这是在上文所描述的beforeEdit方法中异步请求的url,其中拼接了四个参数;第一个参数是action是标记此次请求是一次从单位组中的获取单位数据的操作(代码见图示8),第二个参数ProductId是为了在处理页面中通过物品的ID来获取物品所对应的单位组数据,第三个参数operate标记这次请求是要返回当前物品所对应的单位组所有的单位数据,第四个参数unitfrom是标记此次请求是哪个模块所需的单位组数据(采购模块中其值为InUnitID,销售模块中其值是SaleUnitID,库存管理模块中其值是StockUnitID等)。
这些参数传入业务处理程序中,相应的会对这些参数接收并加以利用。
图示8
以下代码的图示9是业务处理程序接收参数的一个代码块,前三个标记红色方框的参数以上已经作了说明,但是还有一个参数“Val”是用来接收浏览器端传来的UsedUnitID,如果这个参数设置的话表示这个请求中已经有了物品的采购单位ID了,而此次请求是为了通过这个ID来获取相应的Name。
图示9
合理的接收这些所需参数后就可以进行我们自己的处理了。
分析以下代码,首先是通过参数ProductId获取到了此物品所对应的单位组中所有的数据ds,之后是一个条件判断,通过对本次请求目的做判断之后来筛选ds中的数据。如果ds中只有一个Table的话表示这个物品并没有启用多单位组,所以此时只是把物品的基本单位做了拼接后返回,这个拼接的字符串unitrequiredata的结构组成是这样的:单位ID + 单位Name + 单位和基本单位的换算率;如果ds中有两个Table的话,做了一些其他的条件判断以及数据的处理,其处理方式比较简单,直接粘代码如下:
DataSet ds = XBase.Business.Common.UnitGroup.GetUnitGroupByProductId(Convert.ToInt32(ProductId));
if (operate == "show")
{
if (ds != null && ds.Tables.Count > 0)
{
if (ds.Tables.Count == 1)
{
DataTable dt = ds.Tables[0];
if (dt != null && dt.Rows.Count > 0)
{
int exrate = 1;
unitrequiredata = dt.Rows[0]["UnitID"].ToString() + "," + dt.Rows[0]["CodeName"].ToString() + "," + exrate.ToString();
}
}
else if (ds.Tables.Count == 2)
{
DataTable dt1 = ds.Tables[0];
DataTable dt2 = ds.Tables[1];
if (!string.IsNullOrEmpty(Val))
{
if (Val == dt1.Rows[0]["UnitID"].ToString())
{
int exrate = 1;
unitrequiredata = dt1.Rows[0]["UnitID"].ToString() + "," + dt1.Rows[0]["CodeName"].ToString() + "," + exrate.ToString();
}
}
for (int i = 0; i < dt2.Rows.Count; i++)
{
if (!string.IsNullOrEmpty(Val))
{
if (Val == dt2.Rows[i]["UnitID"].ToString())
{
string exrate = dt2.Rows[i]["ExRate"].ToString();
unitrequiredata = dt2.Rows[i]["UnitID"].ToString() + "," + dt2.Rows[i]["CodeName"].ToString() + "," + exrate;
continue;
}
}
else
{
if (dt1.Rows[0][unitfrom].ToString() == dt2.Rows[i]["UnitID"].ToString())
{
string exrate = dt2.Rows[i]["ExRate"].ToString();
unitrequiredata = dt2.Rows[i]["UnitID"].ToString() + "," + dt2.Rows[i]["CodeName"].ToString() + "," + exrate;
continue;
}
}
}
unitrequiredata2 = dt1.Rows[0]["UnitID"].ToString() + "," + dt1.Rows[0]["CodeName"].ToString() + "," + "1";
}
}
Output(unitrequiredata == "" ? unitrequiredata2 : unitrequiredata);
}
else if (operate == "read")
{
if (ds != null && ds.Tables.Count > 0)
{
if (ds.Tables.Count == 1)
{
DataTable dtmerge = ds.Tables[0];
mytable.Rows.Add();
mytable.Rows[0]["UnitID"] = dtmerge.Rows[0]["UnitID"];
mytable.Rows[0]["ExRate"] = 1.00;
mytable.Rows[0]["CodeName"] = dtmerge.Rows[0]["CodeName"];
}
if (ds.Tables.Count == 2)
{
DataTable dtUnitTable = ds.Tables[0];
DataTable dtUnitTable2 = ds.Tables[1];
int i = 0;
for (i = 0; i < dtUnitTable2.Rows.Count; i++)
{
mytable.Rows.Add();
mytable.Rows[i]["UnitID"] = dtUnitTable2.Rows[i]["UnitID"];
mytable.Rows[i]["ExRate"] = dtUnitTable2.Rows[i]["ExRate"];
mytable.Rows[i]["CodeName"] = dtUnitTable2.Rows[i]["CodeName"];
}
mytable.Rows.Add();
mytable.Rows[i]["UnitID"] = dtUnitTable.Rows[0]["UnitID"];
mytable.Rows[i]["ExRate"] = 1.00;
mytable.Rows[i]["CodeName"] = dtUnitTable.Rows[0]["CodeName"];
}
Output(JsonClass.DataTable2Json(mytable));
}
}
至此,整个设计和开发过程已经描述完毕。