国际电子面单添加自定义物流公司(Python编译C#代码试例)原创
金蝶云社区-angen
angen
7人赞赏了该文章 1,503次浏览 未经作者许可,禁止转载编辑于2022年07月13日 16:27:40
summary-icon摘要由AI智能服务提供

金蝶云星空供应链销售管理模块在2021年11月发布的补丁中新增了国际电子面单功能,但早期版本未及时更新支持的物流公司。为解决此问题,提供了一种不升级系统而采用Python编译C#代码的二开方法,通过注册Python插件和编写C#代码动态扩展物流公司选项,如添加顺丰和中通,并展示了实施步骤和效果截图。

    金蝶云星空供应链领域销售管理模块在2021年11月04日发布的补丁 PT-146894 [7.7.0.202111] 中添加了国际电子面单功能,通过菜单‘获取电子面单(国际)’可进入操作界面,其功能界面如下图1-1所示: 

image.png

图1-1


    在 PT-146911 [8.0.0.202205] 之前的版本中,国际电子面单中的物流公司能选择的只有dhl, fedex, jet,后续快递100添加了新的物流公司,而金蝶云星空没有及时同步更新,导致一些国际物流公司不能输入(后续PT-146915 [8.0.0.202206]版本将支持新的物流公司)。如果客户不想升级,可参照本论坛,本文介绍一种用Python编译C#代码的二开方法处理相关逻辑。首先打开BOSIDE,在获取电子面单界面中注册Python插件,如下图1-2所示:

image.png

图1-2


其中的Python代码如下所示:

clr.AddReference('System')
clr.AddReference('Kingdee.BOS')
from System import AppDomain
from System.IO import FileInfo
from System.Reflection import Assembly
from System.Reflection import BindingFlags
from System.CodeDom.Compiler import CompilerParameters
from Microsoft.CSharp import CSharpCodeProvider
from Kingdee.BOS.Core.Util import MD5Compute
from Kingdee.BOS.Cache import KCacheManagerFactory
refDlls = '''
Kingdee.BOS
Kingdee.BOS.App
Kingdee.BOS.App.Core
Kingdee.BOS.Business.DynamicForm
Kingdee.BOS.Business.PlugIn
Kingdee.BOS.BusinessEntity
Kingdee.BOS.Contracts
Kingdee.BOS.Core
Kingdee.BOS.DataEntity
Kingdee.BOS.Model
Kingdee.BOS.OrmEngine
Kingdee.BOS.ServiceHelper
Kingdee.BOS.Web
Kingdee.K3.Core
Kingdee.K3.SCM.Sal.Business.PlugIn
log4net
Newtonsoft.Json
System
System.configuration
System.Core
System.Data
System.Data.DataSetExtensions
System.Web
System.Xml
'''
code = '''
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.K3.SCM.Sal.Business.PlugIn;
namespace Kingdee.K3.SCM.Custom.BusinessPlugin
{
    public class CustomGuoJiGetKuaidiBillEdit : AbstractDynamicFormPlugIn
    {
        public override void AfterDoOperation(AfterDoOperationEventArgs e)
        {
            base.AfterDoOperation(e);
            this.View.ShowErrMessage("测试代码 CustomGuoJiGetKuaidiBillEdit:" + e.Operation.Operation.ToUpperInvariant());
            //以上只是一个测试代码
        }
        static CustomGuoJiGetKuaidiBillEdit()
        {
            if (GetKuaidiBillEdit.INTERNATIONAL_COMPANY_CODES != null)
            {
                GetKuaidiBillEdit.INTERNATIONAL_COMPANY_CODES.AddRange(new List<string>() { "shunfeng", "zhongtong" });
            }
        }
    }
}
'''
def CompileCode(code, refDlls):   
    refAsmNames = filter(lambda y: y != '', map(lambda x: x.strip(), refDlls.split()))
    try:
        kcmgr = KCacheManagerFactory.Instance.GetCacheManager('PyCodeGeneratorCache', 'PyCodeGeneratorCache')
    except:
        return None
    cacheKey = MD5Compute().MDString(code + '-'.join(refAsmNames))
    if(kcmgr.Get(cacheKey) is not None):
        return kcmgr.Get(cacheKey)
    cSharpCodePrivoder = CSharpCodeProvider({'CompilerVersion':'v4.0'})
    codeCompiler = cSharpCodePrivoder.CreateCompiler()
    compilerParameters = CompilerParameters()
    compilerParameters.GenerateExecutable = False; 
    compilerParameters.GenerateInMemory = True; 
    for refAsmName in refAsmNames:
        asms = filter(lambda asm: asm.GetName().Name == refAsmName, AppDomain.CurrentDomain.GetAssemblies())
        if(len(asms) > 0):
            compilerParameters.ReferencedAssemblies.Add(FileInfo(asms[0].CodeBase.Substring(8)).DirectoryName.Replace('\\', '/')+'/' + refAsmName + '.dll')
        else:
            try:
                compilerParameters.ReferencedAssemblies.Add(FileInfo(Assembly.Load(refAsmName).CodeBase.Substring(8)).DirectoryName.Replace('\\', '/') + '/' + refAsmName + '.dll');
            except:
                pass
        compilerResults = codeCompiler.CompileAssemblyFromSource(compilerParameters, code);
    if (compilerResults.Errors.HasErrors):
        raise Exception('\r\n'.join(map(lambda err: err.ErrorText, compilerResults.Errors)))
    compiledAsm = compilerResults.CompiledAssembly;
    kcmgr.Put(cacheKey, compiledAsm)
    return compiledAsm
assembly = CompileCode(code, refDlls)
csPlugin = assembly.CreateInstance('Kingdee.K3.SCM.Custom.BusinessPlugin.CustomGuoJiGetKuaidiBillEdit') if assembly is not None else None
def getPlugIn():
    csPlugin.SetContext(this.Context, this.View)
    return csPlugin
def AfterDoOperation(e):
    getPlugIn().AfterDoOperation(e)


    注:以上代码不要直接通过论坛复制,要使用附件中的代码,上面的Python脚本会先申明一个refDlls和code变量,refDlls为C#项目引用,这个code变量里面是一个完整的C#代码块(包含了using指令,命名空间,类,方法等),后面的Python逻辑定义了动态编译功能,最终通过定义Python方法AfterDoOperation并在里面调用了code 变量中的同名C#方法,以下图2-1和2-2为Python重要脚本相关截图。

image.png

图2-1


image.png

图2-2


      这种方法优点是只需要写C#代码,不需要懂太多Python语法,客户可以用类似方法自定义二开插件逻辑。以下图2-3是二开效果图,可以看到顺丰和中通已经能够在国际电子面单界面中选择了:image.png

图2-3

图标赞 7
7人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!