如何配置D365FO本地开发机(How-to-configure-onebox-VM-on-local)

前言

现在如果要使用本地的开发机,微软需要我们先在 Azure 门户中创建一个 Web 应用程序,在您将用户设置为管理员之前,您还必须执行一个步骤,那就是生成自签名证书,这里我们使用Azure创建的应用程序 ID,为它配置证书。

配置Azure

打开 Azure 订阅,搜索App registrations

点击New registration

输入名称,选择允许的账户类型

Redirect URI,输入虚拟机中FO的网站地址,类型选择Web,等会我们还要加另外一条,这里点击Register

点击Redirect URIs 加入另外一条然后保存

配置虚拟机

打开桌面上的Generate Self Signed Certificates,这里输入刚刚创建的Application Id

自签名证书创建好之后,打开桌面上的AdminUserProvisioning,输入对应的邮箱即可。

如何通过命令行部署SSRS报表(Deploy-SSRS-reports-through-command-line-in-D365FO)

可以使用 Visual Studio 完成 D365 FO 中的SSRS报表的部署。但我们还有另一种方法可以更快地部署SSRS报表。

我们可以在环境中使用Windows Powershell来更快、更轻松地部署SSRS报表。以下是在 D365 FO 中使用 Windows Powershell 部署SSRS报表的步骤。

1. 以管理员身份打开 Windows Powershell

2. 使用下面的命令进入相关的文件夹

  • CD C:\AosService\PackagesLocalDirectory\Plugins\AxReportVmRoleStartupTask

3. 使用下面的命令部署SSRS报表

  • 所有报表
    • ./DeployAllReportsToSSRS.ps1 -PackageInstallLocation "C:\AosService\PackagesLocalDirectory"
  • 指定的报表
    • ./DeployAllReportsToSSRS.ps1 -Module ApplicationSuite -ReportName AssetDep*,TaxVatRegister.Report -PackageInstallLocation "C:\AosService\PackagesLocalDirectory"

如何将 UAT 的数据在上线时部署到生产环境(How-to-deploy-UAT-data-to-production-server-in-D365FO)

前言

首先这次的操作并不是官方推荐的,但是在绝大多数上线的时候,主数据都是在UAT环境配置和测试,并且绝大多数的应用顾问或者用户都不希望做这种重复劳动,他们太忙了吧~

然而系统并没有提供可以直接部署或者还原的功能,那么我们就创建一个Service Request给微软吧。

Service Request

登录 LCS,打开 Project Details ,创建一个新的Service request

如下图:

新建

如下图:

选择 Sandbox to production

如下图:

接下来需要选择 Sandbox sourcedowntime 等等

提交

勾选所有选项后提交吧,提示说这个过程至少需要5个小时的停机时间,但是绝大多数情况1小时左右就能完成了。

DynamicsAX与第三方系统(Using-webservice-to-connect-with-Dynamics-AX)

Webservice

第三方接口调用AX内部程序

SystemConnector

在”Csharp”代码中,我们可以直接调用”Systemconnector”提供的接口从而实现执行AX内部程序的功能。

如:axServiceProvider.handleAgileData("cig", _XMLStr);

实际上 “axServiceProvider” 是通过内置函数 “CallStaticClassMethod” 来实现调用AX内部程序。

string returnStr = (string)op.CallStaticClassMethod("AX Class", "Class Method", _legal, _XMLStr);

将第三方外部程序组织的XML数据主动传递给AX,AX内部只需要解析该XML数据即可执行相应的业务逻辑操作。

在 axServiceProvider 中我们可以设定AX2009的环境端口,用户,密码,公司等信息,同样可以构建更多的方法来调用Ax不同的功能。

下文是通过”Csharp”代码调用接口的样例:

Consume SystemConnector in VS

1

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using SystemConnector.DynamicsAX;
 
namespace CIG_WCF4AgileAX
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string ret = "";
            //string _XMLStr = GetXmlDocument(@"D:/AgileXml/HDC000076-utf.xml");
            //string _XMLStr = GetXmlDocument(@"E:/AgileXml/test1014/UPD-1014-33-01.xml");
            string _XMLStr = GetXmlDocument(@"E:/AgileXml/test1014/CIG000780.xml");
            //string _XMLStr = "AX-MES-RDIF-Go";
            try
            {
                AXServiceProvider axServiceProvider = new AXServiceProvider();
                //ret = axServiceProvider.handleRFIDData("cig", _XMLStr);
                ret = axServiceProvider.handleAgileData("cig", _XMLStr);              
            }
            catch (Exception ex)
            {
                ret= ex.ToString();
            }
            Response.Write(ret);
        }
 
        private static string GetXmlDocument(string xmlPath)
        {
            try
            {
                XmlDocument doc = null;
                if (File.Exists(xmlPath))
                {
                    doc = new XmlDocument();
                    doc.Load(xmlPath);               
                }
                return doc.InnerXml;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write(ex.Message);
            }
            return null;
        }
    }
}

AX内部程序调用第三方接口

AX2009 调用外部接口只需要打开AOT-Reference-添加一个服务引用即可,添加引用时可以添加dll文件。

如下图:

2
3

这样就可以了。

如何在 DynamicsAX 中处理CLR对象报错的问题(Working-with-CLR-exceptions-in-Dynamics AX)

CLR对象报错

在 Dynamics AX 中 无论版本是2009还是2012或者D365,总是会遇到下面这两个报错:

  • Object ‘CLRObject’ could not be created
  • ClrObject static method invocation error

而且系统给出的日志实在是太简短了,让人摸不着头脑。不过一般出现这两个问题都是在AX中处理 .net 相关的 dll 或者在调用 web service、WCF。那么剩下的问题就是我们该怎么样得知在调用 .net 框架的时候究竟出了什么问题呢,一旦知道了具体原因就好办了。


好,那我们通过一个 Job 演示一下吧:

static void RaiseCLRException(Args _args)
{
    ;
    //Necessary if executed on the AOS
    new InteropPermission(InteropKind::ClrInterop).assert(); 
 
    try
    {
        //This will cause an exception
        System.Int32::Parse("abc");
    }
    catch(Exception::CLRError)
    {
        //Access the last CLR Exception
        info(CLRInterop::getLastException().ToString());
 
        //See AifUtil::getClrErrorMessage for another alternative
        //how to parse the Exception object 
 
    }
    //Revert CAS back to normal
    CodeAccessPermission::revertAssert();
}

执行上面代码我们可以得到如下信息:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> **System.FormatException: Input string was not in a correct format.**
  at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
  at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
  at System.Int32.Parse(String s)
  — End of inner exception stack trace —
  at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
  at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
  at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
  at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at ClrBridgeImpl.InvokeClrStaticMethod(ClrBridgeImpl* , Char* pszClassName, Char* pszMethodName, Char* assemblyName, Int32 argsLength, ObjectWrapper** arguments, Boolean* argsAreByRef, Boolean* isException)

=> The part System.FormatException: Input string was not in a correct format.

从这一段就可以看出到底是哪里出问题了。

如何安装和配置一台 Dynamics 365 FO的开发机(How-to-setup-a-d365fo-develop-machine)

配置开发机

以下是一些安装条件

从 Microsoft Dynamics Lifecycle Services 下载安装虚拟机文件

共用资产库

img

可下载的 VHD

img

此处略过如何挂载虚拟机…

远程桌面用户名密码: User name: Administrator Password: pass@word1

重命名开发机

在以下两种情况下需要重命名开发机:

  • Accessing a single Microsoft Azure DevOps project across multiple machines: 不同的开发机不能使用相同名称的机器名称来登录 Azure DevOps (但是可以使用相同的帐户)
  • Installing One Version service updates: 确保 D365 FO 的版本升级没有问题

在数据库中更新服务器名称

通过以下命令在 Microsoft SQL Server 2016 中更新:

sp_dropserver [old_name];
GO
sp_addserver [new_name], local;
GO

如果不记得了,可以通过以下命令得到老的服务器名称:select @@servername

img

更新报表服务器

需要在报表服务器配置中重新选择数据库

img

更新 Azure Storage Emulator

开始菜单,打开 Microsoft Azure,打开Microsoft Azure Storage Emulator

img

或者到路径 :C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Azure\Storage Emulator

执行命令:AzureStorageEmulator.exe start
AzureStorageEmulator.exe status
AzureStorageEmulator.exe init -server new_name
AzureStorageEmulator.exe init -forcecreate

更新财务报表

下载更新包后,找到下图目录:

img

以管理员身份打开 Microsoft Windows PowerShellcd <update folder>\MROneBox\Scripts\Update
.\ConfigureMRDatabase.ps1 -NewAosDatabaseName AxDB -NewAosDatabaseServerName new_name -NewMRDatabaseName ManagementReporter -NewAxAdminUserPassword AOSWebSite@123 -NewMRAdminUserName MRUser -NewMRAdminUserPassword MRWebSite@123 -NewMRRuntimeUserName MRUSer -NewMRRuntimeUserPassword MRWebSite@123 -NewAxMRRuntimeUserName MRUser -NewAxMRRuntimeUserPassword MRWebSite@123

参考文档:

标题 链接
Rename a local development (VHD) environment vso-machine-renaming

连接 VisualStudioTeamService

Azure DevOps (https://dev.azure.com)

Azure DevOps 添加项目

参考文档

标题 链接
添加一个项目 create-project

Azure DevOps 添加用户

这个地址不太好找,记录一下URL: https://dev.azure.com/OrganizationId/ProjectId/_settings/

img

选择 Project SettingsTeam 里面添加用户:

img

点击 Permission ,在 Contributor 中添加刚才创建的用户

img
img

参考文档

标题 链接
在项目中添加用户 add-users-team-project

连接 Azure DevOpsVisual Studio

参考文档

标题 链接
连接到团队资源管理器中的项目 connect-team-project

配置 Excel add ins

初始化 Office app 参数

路径:系统管理 -> 设置 -> Office App 参数 img 把其中三个页签内容全部初始化一遍。 img

Excel add ins

路径:Insert -> Add Ins -> Store -> 搜索 ‘dynamics’

按照下图添加即可。 img

img
img
img

登录 Dynamics 365 FO 实例

提供管理员帐户

img

登录 D365 FO 之前必须提供一个管理员帐户用来登录 D365 FO 环境

以管理员身份运行桌面上的 AdminUserProvisioning 工具

输入你的邮箱地址,建议用微软相关的邮箱,可以使用个人邮箱,然后点击提交

通过 URL 登录 Dynamics 365 FO 实例

https://usnconeboxax1aos.cloud.onebox.dynamics.com.

如何把Dynamics 365 FO UAT的数据库还原到开发环境(how-to-restore-d365fo-sandbox-db-to-dev)

还原数据库

以下是将 Sandbox DB 恢复到 DEV 的过程。

  1. LCS -> UAT -> Maintain -> Move Database -> Export database
  2. bacpac文件会被导出到资产库
  3. 从资产库下载bacpac文件
  4. 下载SqlPackage:sqlpackage-win7-x64-en-16.0.6161.0.zip
  5. 在cmd中运行下列命令
SqlPackage.exe /a:import /sf:D:\Exportedbacpac\my.bacpac /tsn:localhost /tdn:<target database name> /p:CommandTimeout=1200
Example
Demo
1.	SqlPackage.exe /a:import /sf:J:\MSSQL_BACKUP\Demo_PreProdbackup.bacpac /tsn:localhost /tdn:PreProd_20220712 /p:CommandTimeout=1200
Demo
2.	SqlPackage.exe /a:import /sf:J:\MSSQL_BACKUP\Demo_uatbackup.bacpac /tsn:localhost /tdn:uat_20221028 /p:CommandTimeout=1200
  1. 等待
  2. 数据库还原成功后运行以下脚本并且更新数据库的名字
ALTER DATABASE [uatbackup_20190401.bacpac] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 6 DAYS, AUTO_CLEANUP = ON)
 
--
CREATE USER axdeployuser FROM LOGIN axdeployuser
EXEC sp_addrolemember 'db_owner', 'axdeployuser'
 
CREATE USER axdbadmin FROM LOGIN axdbadmin
EXEC sp_addrolemember 'db_owner', 'axdbadmin'
 
CREATE USER axmrruntimeuser FROM LOGIN axmrruntimeuser
EXEC sp_addrolemember 'db_datareader', 'axmrruntimeuser'
EXEC sp_addrolemember 'db_datawriter', 'axmrruntimeuser'
 
CREATE USER axretaildatasyncuser FROM LOGIN axretaildatasyncuser
EXEC sp_addrolemember 'DataSyncUsersRole', 'axretaildatasyncuser'
 
CREATE USER axretailruntimeuser FROM LOGIN axretailruntimeuser
EXEC sp_addrolemember 'UsersRole', 'axretailruntimeuser'
EXEC sp_addrolemember 'ReportUsersRole', 'axretailruntimeuser'
 
CREATE USER axdeployextuser FROM LOGIN axdeployextuser
EXEC sp_addrolemember 'DeployExtensibilityRole', 'axdeployextuser'
 
CREATE USER [NT AUTHORITY\NETWORK SERVICE] FROM LOGIN [NT AUTHORITY\NETWORK SERVICE]
EXEC sp_addrolemember 'db_owner', 'NT AUTHORITY\NETWORK SERVICE'
 
 
UPDATE T1
SET T1.storageproviderid = 0
, T1.accessinformation =''
, T1.modifiedby = 'Admin'
, T1.modifieddatetime = getdate()
FROM docuvalue T1
WHERE T1.storageproviderid = 1 --Azure storage
 
ALTER DATABASE [uatbackup_20190401.bacpac] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 6 DAYS, AUTO_CLEANUP = ON)
GO
DROP PROCEDURE IF EXISTS SP_ConfigureTablesForChangeTracking
DROP PROCEDURE IF EXISTS SP_ConfigureTablesForChangeTracking_V2
GO
 
-- Begin Refresh Retail FullText Catalogs
DECLARE @RFTXNAME NVARCHAR(MAX);
DECLARE @RFTXSQL NVARCHAR(MAX);
DECLARE retail_ftx CURSOR FOR
SELECT OBJECT_SCHEMA_NAME(object_id) + '.' + OBJECT_NAME(object_id) fullname FROM SYS.FULLTEXT_INDEXES
WHERE FULLTEXT_CATALOG_ID = (SELECT TOP 1 FULLTEXT_CATALOG_ID FROM SYS.FULLTEXT_CATALOGS WHERE NAME = 'COMMERCEFULLTEXTCATALOG');
OPEN retail_ftx;
FETCH NEXT FROM retail_ftx INTO @RFTXNAME;
 
BEGIN TRY
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Refreshing Full Text Index ' + @RFTXNAME;
EXEC SP_FULLTEXT_TABLE @RFTXNAME, 'activate';
SET @RFTXSQL = 'ALTER FULLTEXT INDEX ON ' + @RFTXNAME + ' START FULL POPULATION';
EXEC SP_EXECUTESQL @RFTXSQL;
FETCH NEXT FROM retail_ftx INTO @RFTXNAME;
END
END TRY
BEGIN CATCH
PRINT error_message()
END CATCH
 
CLOSE retail_ftx;
DEALLOCATE retail_ftx;
-- End Refresh Retail FullText Catalogs
==
  1. 停止所有D365FO的服务
  2. 重命名AXDB -> AXDB_OldYYYYMMDD
  3. 重命名还原好的数据库 -> AXDB
  4. 打开VS做数据库同步
  5. 启动所有D365FO的服务

如何在D365FO创建SSRS报表(三)Contract Class(how-to-create-ssrs-report-in-d365fo-part3-contract-class)

Contract

存放报表打印时使用的参数

实现接口 SysOperationValidatable

[
    DataContractAttribute,
    SysOperationGroupAttribute('Parameters', "@SYS28007", '1'),
    SysOperationGroupAttribute('ViewGroup', "@SYS5252", '2')
]
public class Demo_InventJournalTransTransferContract implements SysOperationValidatable
{
    TransDate							fromDate,toDate;
    InventDimViewContract   InventDimViewContract;
 
 
    [
        DataMemberAttribute("From date"),
        SysOperationLabelAttribute(literalstr("From date")),
        SysOperationDisplayOrderAttribute('1')
    ]
    public TransDate parmFromDate(TransDate _fromDate = fromDate)
    {
        fromDate = _fromDate;
        return fromDate;
    }
 
    [
        DataMemberAttribute("To date"),
        SysOperationLabelAttribute(literalstr("To date")),
        SysOperationDisplayOrderAttribute('2')
    ]
    public TransDate parmToDate(TransDate _toDate = toDate)
    {
        toDate = _toDate;
        return toDate;
    }
 
    [
        DataMemberAttribute('InventDimViewContract')
    ]
    public InventDimViewContract parmInventDimViewContract(
        InventDimViewContract _inventDimViewContract = inventDimViewContract
        )
    {
        inventDimViewContract = _inventDimViewContract;
 
        return inventDimViewContract;
    }
 
    public boolean validate()
    {
        boolean isValid = true;
 
        return isValid;
    }
 
}

如何修改系统标准的SSRS报表(how-to-extend-standard-ssrs-report)

CustAccountStatementExt

演示如何向 Customer account statement report 添加新字段

1.添加新字段

主临时表是 CustAccountStatementExtTmp,右键单击并创建扩展;我将添加一个新的字符串字段 MaxTxT。

2. 复制这个报表

重命名为:CustAccountStatementExt

3.修改报告设计

右键单击报表数据集并选择恢复以刷新新字段,打开报表设计器并将该字段添加到表中

4. 创建一个新的扩展类来扩展标准报表Controller class

class CustAccountStatementExtControllerExtextends CustAccountStatementExtController
{
    //Add construct
    public static CustAccountStatementExtControllerExtconstruct()
    {
        return new CustAccountStatementExtControllerExt();
    }
 
    public static void main(Args _args)
    {
        SrsPrintMgmtFormLetterController controller = new CustAccountStatementExtControllerExt();
        controller.parmReportName(PrintMgmtDocType::construct(PrintMgmtDocumentType::CustAccountStatement).getDefaultReportFormat());
        controller.parmArgs(_args);
        CustAccountStatementExtControllerExt::startControllerOperation(controller, _args);
    }
     
    protected static void startControllerOperation(SrsPrintMgmtFormLetterController _controller, Args _args)
    {
        _controller.startOperation();
    }
}

可选方法,确定报表的默认设计,有些报表不使用

protected void outputReport()
{
    SRSCatalogItemName  reportDesign;
    reportDesign = ssrsReportStr(CustAccountStatementExt,Report);
    this.parmReportName(reportDesign);
    this.parmReportContract().parmReportName(reportDesign);
    formletterReport.parmReportRun().settingDetail().parmReportFormatName(reportDesign);
    super();
}

5. 创建新的report handler class

我们有两种不同的方法来填充报表处理程序类中的数据:

  • 添加临时表插入事件,逐行计算。转到 AOT 中的 CustAccountStatementExtTmp,展开事件节点,然后复制事件处理程序方法。
  • 添加数据处理post-handler,插入对标准解决方案的结果集使用单次传递的操作。
添加临时表插入事件
class MaxCustAccountStatementExtHandler
{
    [DataEventHandlerAttribute(tableStr(CustAccountStatementExtTmp), DataEventType::Inserting)]
    public static void CustAccountStatementExtTmpInsertEvent(Common c, DataEventArgs e)
    {
        CustAccountStatementExtTmp  tempTable = c;
        CustGroup custGroup;
        select * from tempTable
            where tempTable.CustGroup == custGroup.CustGroup;
 
        tempTable.MaxTxT = custGroup.Description;
    }
}
添加数据处理post-handler
class MaxCustAccountStatementExtHandler
{
    [PostHandlerFor(classStr(CustAccountStatementExtDP), methodstr(CustAccountStatementExtDP, processReport))]
    public static void TmpTablePostHandler(XppPrePostArgs arguments)
    {
        CustAccountStatementExtDP dpInstance = arguments.getThis() as CustAccountStatementExtDP;
        CustAccountStatementExtTmp tmpTable = dpInstance.getCustAccountStatementExtTmp();
        CustGroup custGroup;
        ttsbegin;
        while select forUpdate tmpTable
        {
            select * from tempTable
                where tempTable.CustGroup == custGroup.CustGroup;
            tempTable.MaxTxT = custGroup.Description;
            tmpTable.update();
        }
        ttscommit;
    }
}

6. 添加delegate handler

在此示例中,使用以下代码扩展 PrintMgtDocTypeHandlerExt 类中的 getDefaultReportFormatDelegate 方法。

class PrintMgtDocTypeHandlersExt
{
    [SubscribesTo(classstr(PrintMgmtDocType), delegatestr(PrintMgmtDocType, getDefaultReportFormatDelegate))]
    public static void getDefaultReportFormatDelegate(PrintMgmtDocumentType _docType, EventHandlerResult _result)
    {
        switch (_docType)
        {
            case PrintMgmtDocumentType::CustAccountStatement:
                _result.result(ssrsReportStr(CustAccountStatementExt, Report));
                break;
        }
    }
}

7. 为现有菜单项创建扩展

导航到 CustAccountStatementExt 输出菜单项并创建扩展。另请确保将 Object 属性的值设置为 CustAccountStatementExtControllerExt,以将用户导航重定向。

8. 更新Print management settings

转至应付账款 > 查询和报告 > 设置 > 表单 > 表单设置 单击打印管理,找到文档配置设置,然后选择自定义设计

关于如何根据不同的公司打印不同的报表Design,请参考:

9. 运行报告并测试结果