内文这个东西就像长篇大论的描述,跟在WORD里面一样可以输入无数个字。如果在sap中输入事务码进去可以维护多少个字都可以,但是如果是用.NET调用BAPI传进去内文的话,会将内文超过132个字符的内容全部截掉。要怎么样才能全部导入所有内文呢?

    以下以导入请购单的程序来描述:   

//表头内文 转换多笔实现超长文本插入

......
PRHEADERTEXT.Insert();                             // 这个是BAPI里面导入内文的参数
if (txtQG07.Text.Trim().Length > 120)          // txtQG07.Text的内容就是所有内文,这里用120个字符判断
{
int len = txtQG07.Text.Trim().Length / 120;  // 这里len取整数
len = len + 1;                                             // 可能有结余,故加一
for (int q = 0; q < len; q++)
{
if (q + 1 == len)                                        // 如果到了最后一行,则去最后一行之后的所有内文
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim().Substring(q * 120));
else
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim().Substring(q * 120, 120));
PRHEADERTEXT.Append();                         // 将截取的每一段内容都附加到内文后面。如果不加这一句,则文本会倒置
}
}
else
{
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim());
}

......

如此则可以将内文全部导入。但如果是项目中的内文呢?因为会涉及到传入项目序号,所以需要稍作处理:

//项目内文
PRITEMTEXT.Insert();
if (ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length > 120)
{
int len = ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length / 120;
len = len + 1;
for (int q = 0; q < len; q++)
{
PRITEMTEXT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());    // 将额外需要的参数都添加到这里,每循环一次都要SetValue一次
if (q + 1 == len)
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120));
else
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120, 120));
PRITEMTEXT.Append();
}
}
else
{
PRITEMTEXT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString());
}

 

PS:.NET连接SAP系统专题 就到此结束了。欢迎各位朋友一起交流指正...

posted @ 2011-11-12 01:12 梦心 阅读(217) 评论(0) 编辑

    本周开始,昆山分公司要开始实施SAP了,计划实施周期还是6个月,不再由顾问主导,而是让厦门分公司及台湾总部来规划实施。前期先让两边IT的ERP维护人员把流程整理出来,然后规划出一个方案,按日期整理出来步骤,职责分明。

    今天老大就在跟昆山分公司的IT头头通电话,把主导SAP的权力给拿过来,估计以后她有的忙了。不过忙也有忙的好处,可以学到更多,不至于出去什么都不懂。而小苗和小莫则开始了长期的“打杂”,又要跟年初一样干一些“杂活”,貌似她们挺有怨言。一切都要按照老大的安排来,不得松懈。估计要不了多久老大就要去昆山出差了,而ERP维护人员也是紧随其后出差。现在沟通挺重要的,特别是SAP实施阶段,我想,总是有一些不服管训,总是要有一个头头过来压住所有人。

    我想大概到实施后期我也要出差了,昆山,江苏地界。大概以后过去就是给那边的IT培训如何做二次开发吧。还记得去年六月一号去深圳出差,主导那边分公司ERP上线的情况,挺有意思的,还是第一次做飞机。听说公司出差补贴一天40,好少,貌似也比灿坤好一点点。

    总之,又要开始忙了,忙好,忙了说明有学习的地方~

posted @ 2011-11-08 23:56 梦心 阅读(174) 评论(2) 编辑

    为什么.net调用SAP的BAPI接口需要调用BAPI_TRANSACTION_COMMIT呢?首先得明白BAPI_TRANSACTION_COMMIT这个BAPI的作用。它功劳很大,在SAP里面很多的BAPI直接调用是不会有结果的,因为需要COMMIT一下才能生效,比如生成资产编号的BAPI:BAPI_FIXEDASSET_CREATE1,如果对他直接在SE37中调用运行或者使用SE38调用它,虽然可以得到一个资产编号,但是在AS03里面查询,系统会很白痴得提示你:该资产编号不存在于XX公司。更搞的是当你在AS01中新建资产编号时,新建的资产编号会跳过之前用BAPI生成“失败”的号码。

    那么,这就需要COMMIT一下,在调用这个BAPI之后再紧接调用BAPI_TRANSACTION_COMMIT这个。但是,在SE38中是可以这样做,而在.net中就没那么简单了,直接在调用完BAPI_FIXEDASSET_CREATE1之后再紧接调用BAPI_TRANSACTION_COMMIT是不可以的,虽然还是生成了资产编号,但仍旧是个废号。跟在SE37中调用无异。

    怎么在.net中解决这个问题呢,这就需要用到RfcSessionManager.BeginContext和RfcSessionManager.EndContext这两个方法了。只有在这两个方法之间调用BAPI才能方保万无一失!

    代码如下:

    1、首先引用:using SAP.Middleware.Connector;

    2、调用代码:

public void nco(DataSet ds)
{
IDestinationConfiguration ID = new RfcConfig();
RfcDestinationManager.RegisterDestinationConfiguration(ID);
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
RfcDestinationManager.UnregisterDestinationConfiguration(ID);
nco(prd, ds);
}
public void nco(RfcDestination prd, DataSet ds)
{
bool asset = false;
//选择要调用的BAPI的名称
RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_REQUISITION_CREATE");
//新建调用该BAPI的一个“实例”
IRfcFunction function = null;
function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();
IRfcTable ITEMS = function.GetTable("REQUISITION_ITEMS");
IRfcTable ACCOUNT = function.GetTable("REQUISITION_ACCOUNT_ASSIGNMENT");
IRfcTable RETURN = function.GetTable("RETURN");
int j = 0;
RfcSessionManager.BeginContext(prd);  //因期间需要同一个时间调用BAPI,而且各个BAPI之间有顺序关联,所以最好用这个包围起来
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
ITEMS.Insert();
j = j + 1;
j = j * 10;
ITEMS.CurrentRow.SetValue("PREQ_ITEM", j.ToString());
ITEMS.CurrentRow.SetValue("PREQ_NAME", ds.Tables[0].Rows[i]["QGA27"].ToString());
ITEMS.CurrentRow.SetValue("CREATED_BY", ds.Tables[0].Rows[i]["QGA27"].ToString());
ITEMS.CurrentRow.SetValue("PREQ_DATE", Convert.ToDateTime(ds.Tables[0].Rows[i]["QGA15"].ToString()).Date);
ITEMS.CurrentRow.SetValue("MATERIAL", ds.Tables[0].Rows[i]["QGA04"].ToString());
ITEMS.CurrentRow.SetValue("SHORT_TEXT", ds.Tables[0].Rows[i]["QGA05"].ToString());
ITEMS.CurrentRow.SetValue("PLANT", "1201");
ITEMS.CurrentRow.SetValue("QUANTITY", Convert.ToDecimal(ds.Tables[0].Rows[i]["QGA07"].ToString()));
ITEMS.CurrentRow.SetValue("DELIV_DATE", Convert.ToDateTime(ds.Tables[0].Rows[i]["QGA09"].ToString()).Date);
ITEMS.CurrentRow.SetValue("C_AMT_BAPI", Convert.ToDecimal(ds.Tables[0].Rows[i]["QGA14"].ToString()));
ITEMS.CurrentRow.SetValue("ACCTASSCAT", ds.Tables[0].Rows[i]["QGA12"].ToString());
ITEMS.CurrentRow.SetValue("DOC_TYPE", ds.Tables[0].Rows[i]["QGA28"].ToString());
ITEMS.CurrentRow.SetValue("UNIT", ds.Tables[0].Rows[i]["QGA06"].ToString());

ACCOUNT.Insert();
ACCOUNT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());
ACCOUNT.CurrentRow.SetValue("COST_CTR", ds.Tables[0].Rows[i]["QGA31"].ToString());
ACCOUNT.CurrentRow.SetValue("ORDER_NO", ds.Tables[0].Rows[i]["QGA10"].ToString());

if (ds.Tables[0].Rows[i]["QGA12"].ToString().Trim() == "A")  //如果类别是A,即资产,则需要资产编号
{
ACCOUNT.CurrentRow.SetValue("ASSET_NO", GetASSET(prd, i, ds));  //设置新建的资产编号
ACCOUNT.CurrentRow.SetValue("CO_AREA", "1000");
ACCOUNT.CurrentRow.SetValue("SUB_NUMBER", "0000");
}
}
function.SetValue("REQUISITION_ITEMS", ITEMS);
function.SetValue("REQUISITION_ACCOUNT_ASSIGNMENT", ACCOUNT);
function.Invoke(prd);//提交调用BAPI
RfcSessionManager.EndContext(prd);
if (RETURN.GetString("TYPE").ToString().Trim() == "I")
{
Suess.Text = RETURN.GetString("MESSAGE").ToString();
BANFN.Text = "返回的请购单号:" + function.GetString("NUMBER").Trim();
}
else if (RETURN.GetString("TYPE").ToString().Trim() == "E")
{
Error.Text = RETURN.GetString("MESSAGE").ToString();
}
prd = null;
}
/// <summary>
/// 取得资产编号
/// </summary>
/// <param name="prd"></param>
/// <returns></returns>
public string GetASSET(RfcDestination prd, int i, DataSet ds)
{
RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_FIXEDASSET_CREATE1");
IRfcFunction function = null;
function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();
IRfcStructure KEY = function.GetStructure("KEY");
KEY.SetValue("COMPANYCODE", "2012");
IRfcStructure GENERALDATA = function.GetStructure("GENERALDATA");
GENERALDATA.SetValue("ASSETCLASS", "00005990");
GENERALDATA.SetValue("DESCRIPT", ds.Tables[0].Rows[i]["QGA05"].ToString());
IRfcStructure GENERALDATAX = function.GetStructure("GENERALDATAX");
GENERALDATAX.SetValue("ASSETCLASS", "X");
GENERALDATAX.SetValue("DESCRIPT", "X");
function.SetValue("KEY", KEY);
function.SetValue("GENERALDATA", GENERALDATA);
function.SetValue("GENERALDATAX", GENERALDATAX);

prd.Repository.ClearFunctionMetadata();  //貌似这句可以省略...
RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD1 = prd.Repository.GetFunctionMetadata("BAPI_TRANSACTION_COMMIT");
IRfcFunction function1 = null;
function1 = BAPI_COMPANYCODE_GETDETAIL_MD1.CreateFunction();
function1.SetValue("WAIT", "X");
RfcSessionManager.BeginContext(prd);
function.Invoke(prd);     //提交调用BAPI_FIXEDASSET_CREATE1  生成资产编号
function1.Invoke(prd);   //提交调用BAPI_TRANSACTION_COMMIT 进行COMMIT一下
RfcSessionManager.EndContext(prd);
twMsgbox.AjaxAlert(function.GetValue("ASSET").ToString().Trim());
return function.GetValue("ASSET").ToString().Trim();
}

新建立之后的请购单一切OK,同时,建立的资产编号在AS03已经可以认出来了!!

posted @ 2011-11-01 23:46 梦心 阅读(233) 评论(1) 编辑

    当所有一切代码准备就绪之后,如果是ASP.NET那就是要发布网站到服务器了。如果服务器上的系统是WIN2003,那很不幸,系统会提示这样的“红脸”过来:

意思是说sapnco_utils.dll和sapnco.dll这两个文件不能载入。

Could not load file or assembly "sapnco_utils,Version=3.0.0.42,... 

网上查找了方法也不尽然,各说纷纭。但是在WIN2008下的IIS7跟WinXP下的IIS5.1都可以完全正常,但是这个WIN2003就不行。

后来在对这两个DLL进行分析的时候发现它们是用VC++2005开发的,想到WIN2003系统可能没有必要的运行库。于是在工作站测试的时候安装了VC++2005 32bit版,然后刷新一切就正常了!

解决方法:安装相应vc++2005运行库即可!(实践证明:VC++2008不行!)

附VC++2005 32位运行库下载地址:

http://files.cnblogs.com/mengxin523/vcredist2005sp1_x86_XiaZaiBa.zip

posted @ 2011-10-24 23:22 梦心 阅读(279) 评论(0) 编辑

    前几天上网无意中看到一条消息说SAP GUI代码编写器跟VS.NET比跟上古时代没啥差别。很多人是从.NET或者JAVA转过来做SAP的,一接触到SAP GUI的代码编写器总觉得非常不满意,但其实实用的多了会发现它在某些方面要比VS.NET优秀的多了。且不说那个双击浏览,单单那个“模式”自动载入函数的功能就让人兴奋不已。省去了很多的麻烦,同时在设置短点方面很人性化,而且Debug的时候还能看到内表的内容出来,真的很方便。

    双击一个方法可以自动调出这个方法的主体,如果是INClude,则可以跳入这个程序里面对应的方法体,看完之后则点击“返回”按钮则可以回到上一步操作的地方。貌似这个功能在VS.NET里面应该是没有的,选择了方法,按F12之后想要回来就不容易了(不知道是我不懂还是真没这个功能)。

    当然,SAP GUI编辑器也仅仅是用作ERP特定的环境来做的,VS.NET作为比较大众和权威的.NET开发语言,其便利性和强大无庸置疑。以前在刚接触SAP的时候我也在感叹ABAP编辑器如果能向VS看齐就好了,如今看来各有各的优点了。

    我很欣赏VS里面只能代码下拉框的提示,这点在ABAP编辑器里面就是没有!因为ABAP语法的单纯,所以本身没有那么多的类型转换,这同时又是语法之间的差别了。如今只能说越来越喜欢ABAP,有点反感C#等一大堆的东西杂一起。可能是心境不一样了吧~

posted @ 2011-09-19 23:36 梦心 阅读(229) 评论(3) 编辑
摘要: 接上篇博文。 上篇博文讲到C#调用BAPIBAPI_USER_CREATE来生成SAP账户,但是新建的账户一点权限也没有,现在我们就再次利用BAPI给账户授予权限。 首先,我们在BAPI画面里查找出相关的BAPI出来,利用角色去给用户添加权限: 双击此BAPI,进入到BAPI预览画面: Table属性页: 第一个ACTIVITYGROUPS是指我们要传进去的表格内容,不再是一个栏位和结构内容了。第二个的RETURN则是返回调用结果。 双击BAPIAGR的结构,进入结构详情画面: 以上AGR_NAME是角色名,应该是必须要的。 那么,接下去就是在C#中编写相应...阅读全文
posted @ 2011-08-28 01:09 梦心 阅读(414) 评论(6) 编辑
摘要: 上面博文可知BAPI_USER_CREATE的一些结构和参数。所以在C#中要调用它就很清楚了要输入哪些参数了。 1、首先引用dll,然后在程序开头:using SAP.Middleware.Connector; 2、接下去就是设置登陆参数了,以前相关博文都有说明: public class MyBackendConfig : IDestinationConfiguration { public RfcConfigParameters GetParameters(String destinationName) { if ("PRD_000"....阅读全文
posted @ 2011-08-25 22:41 梦心 阅读(480) 评论(1) 编辑
摘要: 既然BAPI是一些特殊的RFC,封装了业务逻辑,使得将业务都变成一个一个对象,使用者只需要传入传出参数就可以了。 NCO3连接BAPI之前先来看看BAPI的一些参数规则。我们以 BAPI_USER_CREATE 为例子。此BAPI调用了可以生成一个用户,它当然不是仅仅对USR02进行写入那么简单。生成之后该账户什么权限也没有。 输入T-CODE:BAPI,然后在第二个页签下找到该BAPI: 上图中的红色框框里就是我们要调用的BAPI的名称。双击它,进入到该BAPI的一些说明里: 看到了,在属性页里它勾选了Remote... 说明它是可以远程操控的! 上图里,注意到红色框框...阅读全文
posted @ 2011-08-24 22:43 梦心 阅读(403) 评论(0) 编辑
摘要: 周六加班,翻看以前写OA程序的时候无意中发现了当初的一个案子,就是让用户现在OA上申请一个SAP用户,然后提交给相关人员审核,通过之后直接在SAP中生成这个用户,不必在登陆到SAP中请系统管理员来做了。至于这一步,当然是使用BAPI来实现了,不可能对用户表进行新增数据,这样少了很多的关联表和一些逻辑判断,会是一场灾难。 前几篇讲的都是C#去调用RFC的东西,既然BAPI也是属于RFC,如果标准的勾选了那个remote的话,那按道理来说是应该可以实现调用的了。但难点是BAPI里面很多的参数都是引用结构的,而不是单纯的string和int的类型,这点有点困难。在C#中翻看了NCO3是否有相关...阅读全文
posted @ 2011-08-21 23:03 梦心 阅读(407) 评论(0) 编辑
摘要: 有的时候我们需要在RFC中抛出一些自定义的异常,比如输入一个不存在的品号,我们需要抛出一个异常,告知用户品号不存在。有一个笨笨的做法就是通过返回值来判断,但是这样不灵活,同时也会因为品号不存在而继续之行RFC。如果我们能自己抛出异常,程序自动停止执行RFC,那将是两全其美! 现在设定一个功能:输入两个数,第一个数除以第二个数。2个异常:被除数不能为0和除数不能为100。 首先,在SE37中建立一个RFC,就用上篇博文的RFC吧。 在IMPORT页设置如下: Export页面设置如下: Changing和Table页面放空 Exceptions页面设置如下: 然后就是源码咯:...阅读全文
posted @ 2011-08-19 23:04 梦心 阅读(456) 评论(0) 编辑