微软-Dynamic-NAV-2013-应用设计-全-

微软 Dynamic NAV 2013 应用设计(全)

原文:zh.annas-archive.org/md5/9d06224588c3fc7b97e3177915130829

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

1997 年,我所在的公司正在寻找替换其基于 MS-DOS 的软件包。我们非常幸运地找到了 Navision Financials 1.1 作为一款支持即将到来的 Windows 平台且足够灵活以实施、满足我们需求的软件包。

尽管标准功能远非我们今天所拥有的那样,但应用设计的结构简单、稳固,自那时起并未改变。

在那之后的几年里,更多公司采用了 Navision 作为应对市场变化需求的解决方案,许多至今仍存在的垂直解决方案也开始了它们的生命周期。随着微软收购 Navision,新合作伙伴的兴趣增长到了我们今天所知道的渠道。

微软 Dynamics NAV 提供了一种独特的开发体验,只有当你理解了标准应用部分的设计方式后,才能充分利用。

当适当授权时,每个人都可以改变应用的工作方式。这种巨大的可能性伴随着巨大的责任,因为这意味着我们也可以轻易地破坏重要的业务逻辑。

这导致了对应用设计师的独特需求,他们需要在微软 Dynamics NAV 内部运行的应用程序中了解更多关于应用的信息,而无需深入了解功能细节。

本书将在学习如何设计微软 Dynamics NAV 的标准应用功能和如何在我们自己的解决方案中使用这些知识之间取得平衡。理解应用功能和技术设计之间的区域非常狭窄。

在本书中,我们将对标准应用进行修改,并创建新的解决方案。我们还将讨论微软 Dynamics NAV 如何与其他应用协同工作。

本书涵盖的内容

第一章,微软 Dynamics NAV 简介,将向您介绍微软 Dynamics NAV。我们将简要介绍应用的历史,并讨论相关概念。我们将涵盖一些基本设计模式,如数字序列和导航。然后,我们将讨论微软 Dynamics NAV 使用的主数据、日记账和由文档覆盖的账簿分录所采用的数据模型原则。

第二章,示例应用,将在示例应用中实现你在第一章中学到的理论。本章的目标是更好地理解整个系统中日记账和账簿分录的工作方式,以及如何创建自己的日记账应用。你将学习如何逆向工程标准应用以从中学习,并将其应用于你自己的定制中。我们将把应用与微软 Dynamics NAV 中的关系管理和销售集成,并扩展我们的解决方案中的导航和维度。

第三章,财务管理,将探讨如何使用应用程序的财务管理部分以及其设计。这是 Microsoft Dynamics NAV 的核心。您将学习重要概念,如增值税和税,过账组,结账日期,条目应用和财务数据分析。我们将对核心应用程序进行一些更改,向总账添加新信息,并学习如何将财务管理集成到我们的附加解决方案中。

第四章,关系管理,将帮助您分析我们系统中的销售数据,并更有效地服务客户。我们将探讨此应用程序部分的独特设计,并将其与我们创建的第二章,一个示例应用程序中的示例应用程序集成。

第五章,生产,将向我们展示如何为生产型企业设置 Microsoft Dynamics NAV。这些企业处于供应链的起点。我们将讨论组装管理和制造。当使用此应用程序部分时,项目成本核算和项目跟踪是关键要素。我们将查看规划工作表,以及如何使用按订单制造和按库存制造策略创建生产订单。我们将逆向工程库存概要偏移代码单元,并了解这是如何导致计划和采购订单的。在本章结束时,我们将探讨针对垂直行业定制生产的十种方法。

第六章,贸易,将讨论销售、库存管理和采购之间的关系,以及如何在不同复杂程度下涉及仓储。没有销售,大多数公司都无法生存。我们将从技术角度学习如何使用系统中的预留条目。

第七章,储存和物流,将设计和构建一个用于规划运输路线的解决方案,这是 Microsoft Dynamics NAV 中不可用的功能。我们将设计一个解决方案,不仅可用于贸易公司的自身运输,也可用于仓储公司。该解决方案与 Dynamics NAV 产品无缝集成。我们将扩展我们在第二章,一个示例应用程序和第三章,财务管理中学习的日记知识,并使用我们在第五章,生产和第六章,贸易中学习的新文档结构。

第八章,咨询,将讨论如何使用四个示例工作来实施工作功能,并使用资源组和计算扩展工作功能,包括问题注册和工时表应用。Microsoft Dynamics NAV 中的工作功能可以与附加解决方案相比较。它是设计在财务管理之外和贸易之外,但仍然集成到产品中。

第九章,接口,将讨论如何设计一个稳固的 B2B 接口。在过去十年中,接口已成为设计和实施 ERP 系统的一个关键部分。我们将向您展示可用于接口的技术,以及这些技术在标准产品中的实现方式。我们将讨论所有内置的与其他 Microsoft 应用程序(如 Office、SharePoint、BizTalk 和 Exchange)的接口。

第十章,应用设计,将专注于应用设计概念及其在 Microsoft Dynamics NAV 中的应用。我们将重点关注设计以使用、维护、支持、升级、执行和分析。这包括用户界面、版本管理和开发方法的概念。

附录,安装指南,将涵盖与 Dynamics NAV 对象相关的安装程序。

您需要这本书什么

要成功跟随本书中的示例,您需要以下内容:

  • 安装应用程序所需的 Microsoft Dynamics NAV 2013 产品 CD。

  • 一个完整的开发者许可证,可以通过注册或注册为 Microsoft Dynamics NAV 合作伙伴来获得。或者,大多数示例代码可以使用演示许可证进行探索,该许可证可以从 MSDN 下载。

  • 用于第九章,接口中接口示例的 Microsoft Office 和 SQL Server Management Studio。

附录,安装指南,描述了如何安装这些先决条件。

本书面向谁

基本上,本书是为:

  • NAV 顾问和开发者

  • 商业应用程序的设计者

  • 最终用户的系统管理员

  • 商业所有者和影响者

本书假设您对业务管理系统、应用开发有基本了解,并且对 Microsoft Dynamics NAV 或另一个 ERP 系统有实际操作知识。

术语约定

在本书中,您将找到许多不同信息的文本样式,以区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名如下所示:“我们需要复制CreateVendor函数。”

以下是一个代码块:

Currency Code - OnValidate()
IF "Currency Code" <> xRec."Currency Code" THEN
IF NOT JobLedgEntryExist THEN
CurrencyUpdatePlanningLines
ELSE
ERROR(Text000,FIELDCAPTION("Currency Code"),TABLECAPTION);

当我们希望引起你对代码块中特定部分的注意时,相关的行或项目将以粗体显示:

UpdateSquashPlayer()
WITH SquashPlayer DO BEGIN
GET(ContBusRel."No.");
xRecRef.GETTABLE(SquashPlayer);
NoSerie := "No. Series";
TRANSFERFIELDS(Cont);
"No." := ContBusRel."No.";
"No. Series" := NoSerie;
MODIFY;
RecRef.GETTABLE(SquashPlayer);
ChangeLogMgt.LogModification(RecRef,xRecRef);
END;

新术语重要词汇以粗体显示。你在屏幕上看到的,例如在菜单或对话框中的词汇,在文本中会这样显示:“我们可以在全局菜单中添加功能。”

注意

警告或重要注意事项以这样的框显示。

小贴士

小技巧和窍门会这样显示。

本书中的屏幕在哪里

本书中的大多数屏幕都是使用带有 Microsoft Dynamics NAV 2013 Release 2 的 Windows 客户端创建的。在可能和必要时,会提到所使用的角色中心。一些章节有新的或修改过的角色中心。

要查找屏幕,请在角色中心右上角搜索窗口中输入名称,如下面的截图所示。这将引导你到那里,并告诉你如何在菜单中找到它。

本书中屏幕的位置

搜索窗口

截图

本书中的所有截图都是从 Windows 客户端中拍摄的,该客户端是在 Microsoft Dynamics NAV 2013 Release 2 中引入的。

对于大多数图像,为了节省空间,已经关闭了操作窗格事实窗格。这可以通过每个页面的自定义选项来完成。

截图

自定义选项

如何阅读应用程序架构

本书的大部分章节都有架构来阐明数据在系统中的流动。它们是专门为这本书设计的。

如何阅读应用程序架构

要阅读架构,请按照箭头指示。尽可能将功能区域分组使用框。一些架构可能有更多的起始点和结束点,因为这是应用程序的设计方式。使用规范业务逻辑处理多个主数据表。

数字和日期标点符号

这本书是由荷兰作者撰写的,这意味着所有的数字和日期格式都是按照荷兰格式进行的,例如 1.000,00 而不是 1,000.00,以及 18-10-10 代表 2010 年 10 月 18 日。

读者反馈

我们欢迎读者的反馈。告诉我们你对这本书的看法——你喜欢什么或可能不喜欢什么。读者反馈对我们开发你真正能从中获得最大价值的标题非常重要。

要发送给我们一般性的反馈,只需发送电子邮件到<feedback@packtpub.com>,并在邮件的主题中提及书名。

如果你在一个领域有专业知识,并且你对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在你已经是 Packt 书籍的骄傲拥有者,我们有一些事情可以帮助你从你的购买中获得最大价值。

错误清单

尽管我们已经尽最大努力确保内容的准确性,错误仍然可能发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者感到沮丧,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/support,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告它们。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站,或添加到该标题的勘误部分现有的勘误列表中。

盗版

在互联网上,版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

请通过发送链接到疑似盗版材料的方式,与我们联系 <copyright@packtpub.com>

我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。

问题

如果您在本书的任何方面遇到问题,可以通过 <questions@packtpub.com> 联系我们,我们将尽力解决。

第一章. Microsoft Dynamics NAV 简介

从前有座山,这个故事往往从这里开始,尽管 Microsoft Dynamics NAV 的故事远非童话,但它确实有一些神奇之处。

超过 10 万次的安装量,使其成为中端市场最受欢迎的 ERP 软件包之一。在这本书中,我们将探讨 Dynamics NAV 应用程序的神奇之处。我们将看到 Dynamics NAV 如何提供更好的信息来了解我们的业务状况,并提供更好的洞察力,以优化或需要改变的过程。

在本章中,我们将讨论 Microsoft Dynamics NAV 应用程序的基本原则,它的结构以及原因。阅读本章后,您将更好地了解在实施和设计 Microsoft Dynamics NAV 时可以期待什么。

版本和历史

在本书出版时,Microsoft Dynamics NAV 2013R2 是产品的最新版本。当 Windows 版本首次在 1995 年推出时,该产品被称为 Navision Financials 1.0。最初开发该产品的丹麦软件公司 Navision Software A/S 尚未被微软收购,这是一场革命。它是一个完整的 Windows 产品,拥有小型公司所需的所有基本功能。重要的是要理解,原始版本的目标是针对小型公司。

从那时起,我们已经推出了许多(20 多个)版本。所有新版本都包含了新的功能,随着产品的成熟,它也更适合大型公司。这得益于 Microsoft SQL Server 平台的支持,使得更多并发用户可以在同一应用程序区域中工作。

直到 5.0 版本,产品的技术并未发生变化。微软的初衷是同时发布一个全新的技术平台和新的功能变更。但结果证明这是一个非常困难的任务,因此他们决定将技术分为两个版本发布。5.0 版本包含了新的功能和改进,而 2009 版,或称 6.0 版,作为技术发布号,则是一个技术发布。

技术挑战是从旧的 C++平台迁移到.NET,并从两层架构迁移到三层架构。这也是第一个用户界面发生重大变化的发布。Microsoft Dynamics NAV 2009 包含了一个全新的用户界面,即角色定制客户端,它是从头开始全新构建的——现有的(经典)用户界面保持不变,没有任何改动。在这次迁移过程中,尽管在 2009 SP1 中进行了小的改进和错误修复,但所有应用程序功能都被冻结。

使用 Microsoft Dynamics NAV 2013,我们已经进入了一个全新的时代,其中转型已经完成。产品已转换为 .NET,甚至支持从 C/AL 编程语言直接使用 DotNet 互操作性。经典用户界面和本地数据库都已停止使用。

本书支持 2013 版本的功能,尽管大多数概念都与旧版本相关。

本书的内容是什么

本书标题为《Microsoft Dynamics NAV 2013 应用设计》。应用设计是什么意思?在 Microsoft Dynamics NAV 2013 中它又意味着什么?

Microsoft Dynamics NAV 2013 是一个完整的 ERP 软件包,但与其他 ERP 软件包不同,它具有提供开放结构和开发平台的设计能力。其理念是提供 80% 的解决方案,并允许其他 20% 由合格的商业应用开发者设计。

合作渠道是 Microsoft Dynamics NAV 的一个独特部分。自从 Navision 上市以来,公司管理层就决定,只有采用间接销售模式,并让分销商(称为合作伙伴)有权更改产品并添加新功能,才具有实际意义。

本书既涉及 80%,也涉及 20%。我们将看到,应用这些百分比时,不同行业会有所不同。有些行业几乎达到 100% 的匹配,而其他行业则需要 80% 的开发。

因此,本书在标准应用程序的使用与设计更改、扩展产品之间有一条非常细的界限。尽管这不是一本开发书,但我们几乎在每一章都会深入探讨代码和对象。

阅读本章将足以理解代码,但如果您想了解更多,我们强烈推荐阅读《Programming Microsoft Dynamics NAV 2013》,作者 David A. Studebaker、Christopher D. Studebaker,出版社 Packt Publishing。

本书不是 Microsoft Dynamics NAV 2013 的手册。它将向您清晰地展示应用程序结构的布局及其可能性。我们不想取代或重写 Microsoft 文档,而是想提供您可能未曾考虑过的想法。

设置与定制

在 Microsoft Dynamics NAV 中,实施和开发之间的界限非常模糊。在其他 ERP 软件包中,您可能会进行大量的设置,但在 Dynamics NAV 中,使用开发工具进行更改通常更有意义。

标准包在功能上非常完整,但并不支持所有行业。它更多的是为合作伙伴工作的框架。在本书中,我们将解释这个框架及其构建的哲学。理解这个哲学对于了解如何扩展功能至关重要。

然而,扩展功能意味着定制应用程序。2013 年的最终用户是否仍然想要定制应用程序?大多数情况下,他们会说他们不希望他们的软件被定制,但紧接着,他们会说软件应该改变以适应他们的业务方式,并且他们应该不得不改变他们的业务以适应软件。

正因如此,微软推动他们的合作伙伴在标准产品之上创建水平和垂直解决方案,并将这些解决方案作为具有自己版本的独立产品发布,仿佛它们是标准应用程序的一部分。这种使用合作伙伴渠道的方式是一个独特概念,已被证明非常成功,使 Microsoft Dynamics NAV 几乎适用于任何行业。

然而,大多数公司都有其独特的工作方式,因此他们总是需要或多或少定制化的解决方案。拥有总成本取决于定制的程度以及这些定制是如何设计的。

关键在于知道何时进行设置和何时进行定制。只有对应用程序有坚实的理解,才能帮助你确定哪一个是正确的。

阅读这本书后,你将知道如何设计你的应用程序以在拥有权和功能之间取得良好的平衡。

简单之美

如前所述,应用程序被设计成可以由外部合作伙伴扩展和更改。当创建这个合作伙伴计划时,做出了一项决定,即如果应用程序完全开放供他们添加和更改,合作伙伴才能做好工作。这种理念在开始实施或更改 Microsoft Dynamics NAV 时非常重要。

合作伙伴可以更改应用程序中的所有业务逻辑。他们可以向表中添加新字段并创建自己的表。他们唯一不能做的是从基础应用程序的表中删除字段。

如您所见,Microsoft Dynamics NAV 是一个极其灵活和开放的产品,拥有很多自由。但自由伴随着责任。在 Dynamics NAV 中,你负责你系统中的维护工作。

水平解决方案与垂直解决方案

由于这个开放系统,合作伙伴已经对系统进行了数千次大小不同的更改。其中一些更改被捆绑成新的功能组件,并被称为附加组件。这些附加组件通常是改变 Dynamics NAV 成为特定行业产品而不是通用 ERP 系统的解决方案。其他附加组件是可以在所有行业中使用的特定功能,例如 EDI 或工作流。微软将这些特定行业的附加组件称为垂直解决方案,将通用附加组件称为水平解决方案。

开源

尽管 Dynamics NAV 为合作伙伴提供了开源,但它并没有配备像大多数开发者所习惯的开发环境。它有一个定制工具,允许你像定制另一个 ERP 系统一样定制应用设置。这个定制工具是一个基本工具,使用起来很方便,但缺少一些开发功能,如版本控制或 IntelliSense。这使得跟踪你的更改变得更加困难。我们将在第十章“应用设计”中讨论如何使用 Team Foundation Server 进行对象版本控制,第十章。

设计模式

当定制 Microsoft Dynamics NAV 时,你可以使用应用中的经过验证的概念。这些经过验证的概念被称为设计模式。有三种类型的设计模式。

架构模式

架构模式是主要的数据处理和表结构。例如,主数据、单例表、文档以及过账和存档流程。

设计模式

虽然这个名字是人们用来指代整个概念的名称,但设计模式是可重用的元素,用于解决特定问题,如数字序列和阻塞实体。

实现模式

不同的开发技术被称为实现模式。例如,代理、外观、临时数据集和钩子。

APIs

应用程序编程接口API)是一般不会改变的代码块,它们对于使用 Dynamics NAV 来说与.NET 库对于在 C#中工作一样重要。在 Microsoft Dynamics NAV 中,我们有一些可重用但不会改变的构建块。例如,地址格式化和导航页面。

本书结构

本书将涵盖 Dynamics NAV 在多个垂直行业中的大多数功能元素。我们将通过供应链矩阵来完成这项工作。我们将研究的具体行业包括时尚、汽车、药品、食品和家具。对于生产和贸易,我们将研究一般流程,并了解咨询公司和分销公司如何帮助这一流程。

下图显示了本书的结构:

本书结构

对于所有这些行业,我们将研究标准产品中哪些部分可以被利用,以及我们需要在哪些地方提供垂直解决方案。我们将讨论这些垂直解决方案如何与标准包接口,或者甚至可能改变标准产品的行为。

然而,产品的两个部分在用途和易用性上对所有行业都非常通用,我们将单独讨论它们。这些是财务管理关系管理

为了强调垂直概念的优势,我们将为一家分销公司设计和创建一个垂直解决方案。

现在,我们将探讨应用的一些基本概念。

角色定制概念

在 NAV 2009 版本发布时,微软营销决定引入角色定制 ERP 的概念。到目前为止,大多数 ERP 系统都是模块驱动的,这意味着应用程序有一个财务、CRM、销售、采购等区域。对各个模块的访问是分开的。采购人员需要切换到销售才能查看销售订单。

公司中的大多数人都有专门的任务,ERP 系统应该支持这些任务。在经典的 ERP 界面中,用户必须自己决定所需部件的位置。随着角色定制概念的引入,这种情况发生了变化。

角色定制概念

这张截图显示了采购者的角色中心。正如您所看到的,这个人在组织中的所有信息都集中在一个地方,并且可以以类似工作流程的方式使用。此外,销售订单可以从主菜单窗口访问。这与 5.0 版本或之前的菜单完全不同。

角色定制概念

然而,角色定制概念并不是新的。Dynamics NAV 合作伙伴已经实施它多年。在经典菜单中,如前一张截图所示,创建新菜单非常容易,大多数公司根据角色实施自己的菜单。当在 4.0 版本中引入 Microsoft Outlook 风格菜单时,最终用户可以创建快捷菜单套件,这些套件也很快成为角色中心。您可以看出,角色定制概念对 Dynamics NAV 来说就像回家一样。

在 Microsoft Dynamics NAV 2013 中,Windows 客户端不再被称为角色定制客户端,就像在 2009 版本中那样。所有可用的客户端都是角色定制的。

构成要素

为了理解本书中的开发示例,我们将讨论 Microsoft Dynamics NAV 2013 的一些基本构建块。

构成要素

与所有数据库应用程序一样,它从表开始。它们以结构化的方式包含显示的所有信息。重要的是要理解,Microsoft Dynamics NAV 的表并不是完全规范化的。表的结构与用户界面的工作方式相匹配。这使得非技术人员更容易理解数据模型。我们将在第二章中讨论应用的独特结构,“一个示例应用”。

然而,表不仅包含数据,还包含业务逻辑。由于它们的结构类似于数据库中的功能,表包含简单的功能,如地址验证,以及用于增值税和折扣计算的更复杂的功能。

当功能变得更加复杂或可以在应用程序中共享时,最好将它们移动到代码单元对象。这些是针对特定目的的业务逻辑容器。表也可以用作类,而不存储数据。这允许更结构化的编程。

对于用户界面,有两种对象类型:报告和页面。报告最初是打算打印在纸上的,但随着当前技术状况的发展,它们越来越多地被用作信息仪表板,将管理信息与钻取功能相结合。

由于表是按照应用程序的工作方式结构化的,页面必然绑定到一个表上。对于新接触这个概念的人来说,有时需要一段时间才能习惯。

菜单套件定义了当人们离开角色中心并前往部门页面时导航的结构方式。菜单套件用于搜索窗口。

最后一个对象类型是外部接口对象。XML 端口使得可以在系统和外部之间导入和导出数据。

查询对象是在 Microsoft Dynamics NAV 2013 中引入的,允许开发者在元数据级别上定义可用于 C/AL 代码的 SQL Server SELECT 语句。可以将多个表合并到一个查询中。查询对象还可以公开为 OData Web 服务。

对于这本书,表和页面对象是最重要的理解对象。然而,这本书的大部分内容也可以应用于旧版本,但此时应将表单应用于本书中提到的页面。

作为用户界面和业务逻辑的表

Microsoft Dynamics NAV 中的表对象非常重要。因为它不是规范化的,所以它包含了大量关于数据库如何工作的信息。

例如,工作卡(88)基于一个表,即工作(167)。这个表包含了这个屏幕所需的所有字段。

作为用户界面和业务逻辑的表

在传统的开发环境中,这个屏幕会有一个事务GetJobDataUpdateJobData。这些事务会从数据库中读取信息,映射到屏幕上,如果用户完成操作,则将信息保存到数据库中。然而,在 Microsoft Dynamics NAV 中,所有在界面上显示的字段都存储在一个表中。这使得屏幕可以内置触发器来获取数据和更新数据库。

表对象随后包含实现此文档所需的所有业务逻辑。让我们看看这个表中的一些字段:

作为用户界面和业务逻辑的表

在前面的截图中,我们可以看到许多字段,例如WIP 方法货币代码等,这些都是完成工作所必需的。

作为用户界面和业务逻辑的表

当我们点击C/AL 代码图标并聚焦于货币代码时,我们得到以下结果:

Currency Code - OnValidate()
IF "Currency Code" <> xRec."Currency Code" THEN
  IF NOT JobLedgEntryExist THEN
    CurrencyUpdatePlanningLines
  ELSE
    ERROR(Text000,FIELDCAPTION("Currency Code"),TABLECAPTION);

这包含在每次此字段发生变化时执行的业务逻辑。在这种情况下,货币因子将被重新计算并在销售行中更新。

因此,Microsoft Dynamics NAV 中的表不仅仅是数据容器,它们是业务逻辑和应用工作流程的基础。

Dynamics NAV 在供应链中的应用

Dynamics NAV 产品几乎在商业供应链的每个地方都被使用。这主要是因为它是一个高度可定制的 ERP 系统。Dynamics NAV 用于传统的供应链公司,如制造工厂、批发公司和零售业,无论是否有很大变化。但通过附加组件,该产品也用于运输公司或回收行业。

为了更好地理解这一点,了解公司的工作方式很重要。公司是一个人或一组人使用材料和资源向其他公司或最终消费者提供产品或服务的人或团体。一组共同工作的公司被称为供应链。Dynamics NAV 可以用于所有这些公司,尽管它传统上用于有 5 到 250 个并发用户的公司。

为了服务这一过程,Dynamics NAV 有一系列以下基本模块:

  • 财务管理:传统上,这被用于公司以符合联邦的簿记法规。对于刚开始创业的创业者来说,这通常是他们最不喜欢的部分。然而,良好的簿记可以清楚地了解公司的状况,并利用良好的财务信息支持战略决策。

  • 库存:每个成长中的公司都会达到一个点,那时没有系统就无法处理库存。持有过多的库存非常昂贵。一个好的库存系统可以帮助你尽可能高效地保持库存。

  • 关系管理(RM):当涉及到人时,公司不仅与客户和供应商打交道。RM 将帮助您跟踪您的公司所涉及的每一个公司和每个人。

  • 销售:销售流程通常是企业赚钱的地方。系统将帮助您跟踪客户下订单的情况。

  • 采购:采购部门通常分为两部分。一部分是公司自身所需商品的采购。这种设施管理在大公司中可以发展成为自己的业务。另一部分采购是购买销售过程中所需的材料和资源。对于一些贸易公司,这甚至可以是一个直接发货的过程,你永远不会在仓库里有购买的货物。

  • 仓储管理:仓库越来越大,对支持拣选和上架过程的系统的需求也越来越大。这通常与销售和采购过程紧密相连。

  • 制造:当你自己制造产品时,你需要一个系统来帮助你从一种或多种购买的物料和资源中创建新的项目。

  • 工作:在某些公司,提供服务的流程非常复杂,需要自己的管理流程。时间和计费通常是这些公司非常重要的流程。

  • 服务管理:这支持处理您的物品的保修和必要的定期维护的服务流程。

一些基本设计模式

Microsoft Dynamics NAV 有一些基本的设计模式,这些模式在整个应用程序中被重复使用,并且理解本书的概念是必要的。

编号系列

数据库需要唯一的记录。应用程序有两种方法来实现这一点。

一些表有自动递增编号,无法被影响。这些通常是具有可审计目的的会计表。这些表的例子包括总账分录、总账登记簿和增值税分录。

另一种方式是使用灵活的字母数字代码。在一些设置表中,用户可以自由创建自己的编号,就像在位置表中一样,但大多数时候使用编号系列功能。这些功能可以根据最终用户的访问权限进行影响。让我们更详细地看看它们:

编号系列

用户可以定义自己的编号,通常以一个字母数字字符开始。编号可以是自动的、手动的,或者两者的组合。编号可以有一个起始日期和递增编号。这样,你可以对你的销售发票进行编号,例如 SI11-0001。SI 代表销售发票,11 代表 2011 年,0001 是递增编号。

例如,编号系列可以相互链接,使得可以为国内外客户使用不同的编号系列。

扩展文本

在 Microsoft Dynamics NAV 中,大多数主数据表都有两个描述字段,但可以添加额外的文本。

文本可以定义在系统中的所有语言,并使其在特定时期内有效。

我们可以启用或禁用系统中的大多数文档的文本使用,这样我们就可以为销售报价提供一些长文本,为销售发票提供一些较短的文本,如下面的截图所示:

扩展文本

导航

Microsoft Dynamics NAV 顾问们喜欢您使用像 SI11-0001 这样的编号的原因是因为导航功能。这个功能使得在数据库中找到与该文档相关联的所有信息成为可能。如果您将您的销售发票命名为 110001,您的采购发票也是同样的名称,系统将无法在详细级别找到信息。

导航

当在 CRONUS 演示数据库中导航到过账销售发票 103006时,我们得到与这个编号相关联的所有信息。

导航显示文档和条目。使用显示选项,我们可以深入到记录中,甚至更深入到信息中。导航存在于大多数显示过账交易和历史数据的页面上。

设置表格

一个 ERP 应用程序可以用多种不同的方式使用,为了使其按我们的意愿工作,我们需要正确设置它。我们已经讨论过,Dynamics NAV 比其他 ERP 软件包设置要少得多,并且更有可能被更改,但无论如何,还是有设置工作要做。

应用程序的每个部分都有自己的设置表。还有一些是应用程序级或跨应用程序的设置表。在实施过程中,我们需要确保触及所有这些表。实施后更改这些设置应非常谨慎。

设置表使用单例表设计模式。以下表格显示了按类型分组的所有 Microsoft Dynamics NAV 设置表:

特定设置表 应用程序级设置表
总账设置销售与应收账款设置采购与应付账款设置库存设置资源设置工作设置营销设置人力资源设置生产计划设置 FA 设置非库存项目设置仓库设置服务管理设置制造设置 源代码设置更改日志设置 SMTP 邮件设置审批设置作业队列设置在线地图设置交互模板设置员工门户设置通知设置订单承诺设置 BizTalk 管理设置

当我们从应用程序打开设置时,我们会看到一些选项,包括我们之前讨论的编号:

设置表

发布组

Microsoft Dynamics NAV 在将其发布到 总账方面非常灵活。这是在发布组中设置的。这些形成了一个矩阵,由应用程序筛选出来。

大多数应用程序区域都有一个或多个发布组表:

  • 客户发布组

  • 供应商发布组

  • 库存发布组

  • 招聘发布组

  • 通用业务发布组

  • 通用产品发布组

  • 银行账户发布组

  • 增值税业务发布组

  • 增值税产品发布组

  • FA 发布组

    小贴士

    我们将在 第三章 财务管理 中更详细地讨论发布组。

定价

当涉及到定价和折扣时,Microsoft Dynamics NAV 有一种非常简单但有效的方法来计算。

所有销售和采购价格都存储在四个简单的表中:

  • 7002 – 销售价格

  • 7004 – 销售行折扣

  • 7012 – 采购价格

  • 7014 – 采购行折扣

系统通过在这些表中筛选来找到适当的价格。筛选越窄,价格被应用的可能性就越大。

定价

例如,项目卡片上项目1972-W的正常价格为 97,480,但从 2011-1-1 开始为 843,345

筛选是在代码单元销售价格计算管理(7000)和采购价格计算管理(7010)中完成的。我们将在 第二章 一个示例应用程序 中讨论这个结构,我们也将为我们的应用程序创建这样的结构。

维度

在整个应用程序中,可以使用无限数量的维度来分析数据。这些维度是从主数据表中继承的。

维度

应用程序有两个全局维度,这些维度直接过账到每个交易中。另外六个维度可以定义为快捷维度,以便直接在日记账和文档中使用。可以添加无限数量的附加维度,但需要付出额外的努力才能访问。

维度

前面的截图显示了如何在销售文档中使用全局和快捷维度。

Microsoft Dynamics NAV 内置了 OLAP 功能。它允许我们在应用程序内或 SQL Server 分析服务中创建用于分析的立方体。

维度

虽然立方体可以在过账时实时更新,但强烈建议定期批量更新它们。此外,维度的数量会影响系统的性能。

在 Microsoft Dynamics NAV 2013 中重新设计了维度。这次重新设计对应用程序性能产生了巨大影响,可以将数据库大小减少高达 30%。

架构设计模式

在创建自己的结构之前,了解 Microsoft Dynamics NAV 的一些特定架构设计模式原则非常重要。构建块是分层和重复使用的,并且相互依赖以确保数据完整性。

主数据

数据模型从主数据开始。有三种类型或级别的主数据。它们都用于交易。我们区分补充、正常和辅助主数据。

补充数据的例子包括货币、地点和付款条款。它们通常不使用编号系列,但允许我们创建自己的唯一代码。

主数据示例包括总账科目、客户、供应商、项目、资源和固定资产。它们使用编号系列进行编号,并拥有自己的日记账结构。

补充表格的一个例子是项目供应商表。

日记账

每个交易都以日记账开始。每个日记账可以包含多个子交易,这些子交易由系统视为一个整体。这样,系统就能够检查,例如,交易完成后系统的一致性是否得到保持。

下图显示了日记账的结构。PK代表主键,它是表的唯一标识符:

日记账

每个日记账可以包含一个或多个模板,每个模板可以包含一个或多个批次,允许多个用户拥有多个模板和批次。日记账行有一个源编号字段,例如,它引用的是总账科目编号或我们正在更改的项目编号。当我们过账日记账时,更改将存储在分录表中,以及所有行。对于日记账,维护一个登记簿,以便审计师可以检查交易是否一致。

总账

要查看应用程序中它是如何工作的,我们可以转到 会计科目表总分类账,如下面的截图所示:

总账

如果我们选择 总账科目 1140 并向下钻取,我们将看到此记录的详细信息。

这些是通过日记创建的,所以让我们打开一个日记:

总账

这本日记包含了同一过账日期的两份文件,余额为零。当我们过账这本日记时,系统将创建总账条目和登记簿。

总账

这是 Dynamics NAV 的基本构建块。Dynamics NAV 中的所有内容都是建立在日记、登记簿和条目之上的。

总账

平衡

在任何 ERP 系统中,总计和平衡至关重要,无论你是总计总账、客户付款还是库存,了解每个账户、客户或项目的余额都很重要。

传统上,这需要计算这些余额并决定一个存储总计和子总计的地方。在 Dynamics NAV 中,系统内置了处理平衡和总计的技术。

这种内置技术被称为 求和索引字段技术SIFT)。对于 Dynamics NAV 来说,这是其成功的关键特性。

它的工作方式是,作为开发者,你在索引级别上定义你的总计。通过将总计字段与一个键关联,系统知道它需要为你维护总计。

在原始的专有数据库中,这项技术是内置的,对用户不可见,但在 SQL Server 数据库中,我们可以看到它是如何工作的。

如果我们进入 CRONUS 数据库并打开带有其键的 总账分录 表,我们会看到以下信息,如下面的截图所示:

平衡

让我们以第二个键为例。该键包含总账科目编号和过账日期字段。如果我们仔细查看 SumIndexFields 列,我们会看到以下字段列出:

平衡

注意,这些都是十进制类型的字段。这是 SumIndexfields 的强制性要求。

小贴士

从 SQL Server Management studio 中,你可以看到由 SumIndexField 定义生成的数据。每个带有 SumIndexField 的键在数据库中生成一个视图。在旧版本(5 SP 1 之前),SumIndexFields 被保存在表中。

因此,现在我们知道我们不必担心维护总计,我们可以把时间花在真正重要的事情上。

流字段和流过滤器

如前所述,Microsoft Dynamics NAV 中的屏幕是直接基于一个表构建的。这些表定义包含所有字段,包括总计。然而,这些总计并不是真正的数据库字段。

这可以通过比较 Microsoft Dynamics NAV 中的表定义与 SQL Server 中的表定义来展示:

流程字段和流程筛选器

字段日期筛选器(28)预算金额(33)不是数据库中的实际字段。它们是辅助字段,用于在屏幕上显示数据。

流程筛选器可以有七种类型;总和、平均值、存在、计数、最小值、最大值和查找,并包含对数据库的查询。例如,日期余额(31)显示以下内容:

Sum("G/L Entry".Amount 
  WHERE (G/L Account No.=FIELD(No.),
         G/L Account No.=FIELD(FILTER(Totaling)),
         Business Unit Code=FIELD(Business Unit Filter),
         Global Dimension 1 Code=FIELD(Global Dimension 1 Filter),
         Global Dimension 2 Code=FIELD(Global Dimension 2 Filter),
         Posting Date=FIELD(UPPERLIMIT(Date Filter))))

这在 G/L 条目表(17)的字段金额上创建总和,筛选条件为G/L 账户G/L 账户编号业务单元代码全局维度 1 和 2 代码以及过账日期

其中一些筛选器是 G/L 账户表中的实际字段,但其他的是流程筛选器。不存在的字段可以用作运行时筛选器,以限制查询的结果。

我们将在本书的后面部分使用和讨论更多这些流程筛选器和流程字段。

更多期刊和条目

因此,现在我们知道了期刊是如何工作的,构建 Dynamics NAV 的过账图可能很有趣。Dynamics NAV 有多个期刊、登记簿和条目,它们相互叠加。

下表显示了最重要的期刊、登记簿和条目:

期刊 登记簿 条目
总账分录行(81)项目分录行(83)资源分录行(207)工作分录行(210) G/L 登记簿(45)项目登记簿(46)资源登记簿(240)工作登记簿(241) G/L 条目(17)客户账簿条目(21)供应商账簿条目(25)项目账簿条目(32)工作账簿条目(169)资源账簿条目(203)增值税条目(254)银行账户账簿条目(271)

请注意,当你在数据库中查看时,你会找到更多这些表格,但这些都是主要构建块。

每个期刊负责创建自己的条目,但可能需要运行另一个期刊。例如,项目期刊可能需要使用总账期刊来生成 G/L 条目,而工作期刊可能需要使用项目期刊来创建项目账簿条目。

我们已经讨论了 G/L 条目表,该表用于存储基本财务信息。这是基本管理表。

其他条目表是子账簿表。它们存储冗余信息,但为特定用途提供额外信息。子账簿的总计应始终与 G/L 平衡。我们将在第三章中看到它是如何工作的,财务管理。以下是更多表格:

  • 客户和供应商账簿条目表用于存储关于应收账款的具体信息。它们与客户和供应商主数据表相关联。

  • 增值税条目表存储特定信息以简化注册。大多数公司每月或每季度与一个或多个政府机构进行增值税注册。许多国家的增值税与本书中描述的本地化国家系统可能不同。

  • 银行账户条目应显示我们在银行账户上进行的所有交易。

ERP 软件包的物流部分由项目日志处理。每个购买、生产或销售的项目都通过这个日志处理。服务通过资源日志处理。资源可以是人员或像电梯这样的设备。

工作日志是一个覆盖整个应用的伞状层。它允许您分组交易,使分析大型项目的成本和利润变得更容易。

过账架构

当我们将所有这些信息组合到一个架构中时,我们可以创建以下基本的 Microsoft Dynamics NAV 过账架构:

过账架构

在这里,您可以清楚地看到哪个杂志负责创建哪个条目。条目表始终由一个流程维护。

总账是应用程序的核心,其中基本财务信息在账簿条目中创建。所有基本信息都在 G/L 条目表中,这些条目被分组在 G/L 注册表中,然后总是保持平衡。客户供应商增值税银行账户账簿条目是子表,始终引用 G/L 注册表。我们永远不能创建这些条目而不触及应用程序的这一部分。

子条目和详细条目

当创建条目时,其基本结构不应更改以保持审计能力。这就是为什么 Microsoft Dynamics NAV 中的大多数条目都有子条目或详细条目的原因。

客户供应商账簿条目包含应用、未实现损益、各种折扣和更正的详细信息。这样,我们能够跟踪条目的变化,而不改变原始信息。

子条目和详细条目

项目账簿条目根据您对项目的操作有多种子条目。

Microsoft Dynamics NAV 中最重要的表之一是价值条目表。每个项目账簿条目都有一个或多个这样的条目。这个表是库存和应用程序财务部分之间的软连接。

仓库条目允许在我们组织内部移动项目,而不触及基本库存或财务应用程序。

将杂志结合到流程中

杂志和条目表使我们能够在公司中进行基本的平衡,但公司的人并不习惯于使用杂志。

传统上,公司与文档一起工作。在引入 ERP 应用程序之前也是如此。销售代表会带着纸质的订单块穿越全国,然后回到后台办公室。后台办公室随后使用运输文件和发票发货。

Microsoft Dynamics NAV 支持与文档一起工作。传统上,我们将文档分为销售和采购文档,但 Microsoft Dynamics NAV 的后续版本也有仓库文档。其他支持的文档包括提醒和服务文档。

文档结构

Microsoft Dynamics NAV 中的文档总是包含标题和行。标题包含关于交易的基本信息,例如发货日期、地址和付款条款。

行包含有关销售或购买的信息。这可能包括各种总账账户、项目和资源。

文档可以根据交易类型具有不同的阶段。报价是销售或采购过程中的典型起点。当报价被批准时,它可以升级为订单,然后发货并开具发票。该过程也可以通过退货订单反向进行,从而产生贷项通知。

文档结构

文档交易

数据库中的交易可以通过文档启动。当文档被处理时,必要的日记会自动填充。例如,当订单发货时,货物离开仓库,然后创建并过账一个项目日记来处理这种情况。当发票过账时,会生成一个总账日记来创建总账分录和客户或供应商账簿分录。

其他模式

之前讨论的日记和文档模式无疑是交易结构中最重要的。但 Microsoft Dynamics NAV 也有其他结构。

三个最重要的其他模式是 CRM、工作和制造。这些领域都是其他流程的伞状结构。

关系管理

Microsoft Dynamics NAV RM 帮助您维护主数据和分析交易数据。它既在数据处理过程的开始,也在结束。

我们已经看到了客户、供应商和银行主数据记录。但如果供应商也是客户,或者反之,怎么办。我们不希望维护相同的数据两次。我们可能还想保留额外的信息,例如联系人信息和客户及供应商的兴趣。我们将在第四章关系管理中看到更多。

我们还需要分析我们使用文档和日记结构创建的数据。

关系管理

工作

有时,一个项目可能不仅仅是一个购买和/或销售文档。一个项目可能需要从几周到一年以上,并需要多个文档。

Dynamics NAV 中的工作结构允许您处理这种情况。每个文档和日记交易都可以附加到工作上,这使得分析盈亏甚至安排工作变得容易。

工作模块还允许您在开始项目之前进行计算,并在整个过程中平衡这个计算。

工作

制造

当您生产自己的产品时,在 ERP 流程中您有不同的需求,与您只购买您销售的产品相比。

微软 Dynamics NAV 的制造模块允许您处理此过程。基本上,它的作用是从一个或多个其他项目或资源中创建一个项目。

制造

摘要

在本章中,我们介绍了微软 Dynamics NAV 的基本结构。我们讨论了设计理念、应用程序对象以及独特的表结构。我们探讨了角色定制概念及其对产品旧版本的反映。我们讨论了产品的一些基本功能,如编号系列和应用设置。我们还讨论了重要的基本过账结构和 SIFT 的工作方式。我们讨论了文档结构如何覆盖日记账结构,以及总结构如何位于其上。

在下一章中,我们将探讨一个示例行业应用及其对标准功能的影响。

第二章. 示例应用程序

让我们在微软 Dynamics NAV 中创建一个自己的结构。为此,我们必须想到一些在标准包中尚未提供但可以在此基础上构建的东西。

对于我们的示例应用程序,我们将运行一个网球场。运行网球场很容易理解,但我们不能不改变和扩展产品来做这件事。为了定义我们的更改,我们首先需要进行适配-缺口分析。

在本章之后,你将更好地理解如何重用微软 Dynamics NAV 应用程序的框架。我们将展示如何通过进入应用程序代码来逆向工程应用程序并研究其功能。

对于这个例子,需要一些新的和更改的对象。《附录》,安装指南,描述了如何找到这些对象以及如何安装和激活它们。

在第一部分,我们将研究如何逆向工程标准应用程序,以查看和学习它是如何工作的,以及如何在我们自己的解决方案中重用这些结构。

在本章的第二部分,我们将学习如何在自定义应用程序中使用日记和条目。

最后,我们将研究如何将我们的解决方案与标准应用程序集成;在我们的案例中,是销售开票。

适配-缺口分析

当我们进行适配-缺口分析时,我们查看公司的流程,并定义我们可以和不能使用标准包做什么。当一个业务流程可以用标准软件处理时,我们称之为适配。当这不能做到时,我们称之为缺口。所有缺口都必须开发,或者我们需要购买一个附加组件。

然而,即使可以使用标准软件功能来做某事,这并不意味着这样做是明智的。标准应用程序应该用于其设计的目的。将标准功能用于其他目的可能在当前版本中可行,但如果在新版本中发生变化,可能就不再适用。因此,最好设计一些新的东西,而不是错误地使用标准功能。

设计网球场应用程序

网球场公司的基本流程是出租网球场给网球场球员;会员和非会员。有一个处理会员和非会员不同价格的预订和开票流程。

尽管可以使用网球场作为项目,顾客作为玩家来实现这一点,但这将是一个典型的错误使用标准功能的例子。而不是这样做,我们将研究项目和顾客是如何设计和使用的,并利用这一点来创建一个新的网球场应用程序。

使用标准 NAV 功能设计一个特定应用程序是总拥有成本TCO)的问题。如果只有一个客户使用这个解决方案,最好以创新的方式使用标准应用程序。然而,如果我们从本章开始部署设计到多租户架构,并让成千上万的公司运行它,那么从经济上讲,制作最佳应用程序是可行的。每次你做出设计决策时,都要记住这一点。

看,学,爱

为了确定这个应用程序的设计,我们首先将查看标准应用程序的各个部分,我们可以使用这些部分来学习它们是如何工作的。我们将利用这些知识来设计我们自己的应用程序。

在 Microsoft Dynamics NAV 中,客户和供应商主数据是通过关系管理RM)来维护的。对于我们的解决方案,我们将创建一个新的主数据,用于表示壁球运动员,作为应用程序的业务部分。这也会与 RM 集成。

为了设计壁球场地,我们将查看标准包中项目的布局。壁球场地将是我们的应用程序的产品部分,有一个日记来创建预订条目,我们可以对其开票。

对于这个开票流程,我们将使用并集成 Microsoft Dynamics NAV 的销售部分。

绘制表格和过账方案

在我们决定应用程序的设计后,我们可以像上一章所做的那样绘制表格和过账程序。这将向其他人阐明设计,并指导我们通过开发过程。

绘制表格和过账方案

在前面的图中,关系管理销售中的对象是标准对象,我们可能需要对其进行修改。壁球应用程序的对象是新的对象,但基于标准应用程序中的类似对象。

项目方法

为了跟踪我们的项目,我们将把更改分成更小的任务。第一个任务是进行关系管理中的更改,以便能够从联系人创建壁球运动员。第二部分是创建壁球场地。预订和开票流程是第三和第四部分。

与标准应用程序接口

在我们的架构中,我们可以看到我们有两个流程需要在标准 Microsoft Dynamics NAV 流程上工作,这些流程是关系管理销售

设计模式

为了创建壁球场地应用程序,我们可以使用经过验证的设计模式。这将限制我们开发成功的风险,并使与其他熟悉这些模式的人沟通变得容易。

我们将使用的模式示例包括主数据、编号序列和日记。

并非你需要的一切都会在模式中记录。有时创新是必要的。如果你这样做,仍然重要的是要想象你的设计作为一个模式,并为未来的使用进行记录。

开始

在设计过程的第一个部分,我们将查看如何逆向工程标准应用程序,以便在我们的解决方案中学习和重用知识。

创建壁球运动员

对于我们壁球运动员的管理,我们使用联系表中的数据。在标准产品中,可以使用联系数据创建客户或供应商。我们需要创建壁球运动员的相同功能,让我们看看微软是如何实现的。

打开联系人卡并尝试找到此功能,如下截图所示:

创建壁球运动员

我们希望为我们的壁球运动员有一个这样的功能。所以让我们进去看看它做了什么。为此,我们需要设计页面并查看操作。在这种情况下,页面编号是5050,我们可以通过点击页面右上角的关于此页面来找到它,如下截图所示:

创建壁球运动员

此选项可以非常有助于查找页面上的字段信息、过滤器或源表。

创建壁球运动员

要打开页面,我们需要在开发环境中打开对象设计器Shift + F12),如下截图所示:

创建壁球运动员

在这里,我们可以在页面中找到5050 联系人卡

创建壁球运动员

我们正在寻找此页面上的操作。如果你不熟悉页面设计器,它们可能很难找到。要打开操作,光标应位于最后一行填充内容下方的空白行。然后点击鼠标右键选择操作,或者从视图下拉菜单中选择操作

创建壁球运动员

小贴士

或者,你也可以使用视图下拉菜单中的预览选项来查找操作。

现在,我们处于操作设计器中,我们可以搜索创建为选项。要查看它做了什么,我们需要通过按 F9 或从视图下拉菜单中选择C/AL 代码进入 C/AL 代码:

创建壁球运动员

CreateVendor 与 CreateCustomer

在 Microsoft Dynamics NAV 中,从联系人创建客户和供应商之间有一个小的区别。当创建客户时,系统会要求我们选择一个客户模板。供应商选项没有这个要求。为了保持简单,我们将在本章中查看并学习“供应商”功能。

客户和供应商表在结构上几乎相同,字段在两个表中都按类似方式编号。这被称为销售和采购之间的交易镜像,我们将在第六章贸易中进一步讨论。我们将以类似的方式镜像我们的新表到其他 Microsoft Dynamics NAV 表中。

Action 中的 C/AL 代码告诉我们,当点击 菜单 选项时,联系人表中的 CreateVendor 函数将被启动。为了复制此功能,我们需要创建一个新的函数,CreateSquashPlayer。让我们在深入研究此代码时记住这一点。

CreateVendor 与 CreateCustomer 对比

打开联系人表(5050)并搜索函数 CreateVendor。您可以通过在表设计器中的任何位置进入 C/AL 代码(F9)以及使用 查找 [Ctrl+F] 功能在表中查找函数,如下面的截图所示:

CreateVendor 与 CreateCustomer 对比

逆向工程

我们需要逆向工程此代码,以便查看我们需要为我们的 CreateSquashPlayer 函数创建什么。我们将查看 C/AL 代码的每个部分,以决定我们是否需要它。

逆向工程

以下代码片段做了什么?

TESTFIELD("Company No.");

这测试当前记录的有效 公司编号。如果失败,则无法继续,最终用户将收到运行时错误。

RMSetup.GET;
RMSetup.TESTFIELD("Bus. Rel. Code for Vendors");

此代码从系统中读取 Marketing Setup 表并测试 供应商业务关系代码 是否有效。在这里,我们需要为壁球运动员添加一个新的代码,该代码将作为新字段添加到设置表中:

CLEAR(Vend);
Vend.SetInsertFromContact(TRUE);
Vend.INSERT(TRUE);
Vend.SetInsertFromContact(FALSE);

在这里,清除了 Vendor 表,并在该表中调用了一个函数,然后激活必要的业务逻辑的同时在数据库中插入了一条新记录。然后再次使用另一个参数调用相同的函数。由于 Vendor 表是我们需要复制的,我们将写下我们可能需要一个类似于 SetInsertFromContact 的类似函数:

IF Type = Type::Company THEN
  ContComp := Rec 
ELSE
  ContComp.GET("Company No.");

此代码检查当前联系人是否为公司。如果是,则使用此记录填充 ContComp 变量。如果不是,则使用当前联系人相关的公司填充 ContComp

ContBusRel."Contact No." := ContComp."No.";
ContBusRel."Business Relation Code" := RMSetup."Bus. Rel. Code for Vendors";
ContBusRel."Link to Table" := ContBusRel."Link to Table"::Vendor;
ContBusRel."No." := Vend."No.";
ContBusRel.INSERT(TRUE);

ContBusRel 函数引用联系人业务关系表(5054),在 Microsoft Dynamics NAV 数据模型中是一个链接表。技术上,一个联系人可以连接到多个客户和供应商,尽管这没有意义。此表在此处填充。让我们写下我们需要检查此表并查看是否需要更改:

UpdateCustVendBank.UpdateVendor(ContComp,ContBusRel);

UpdateCustVendBank 是一个外部代码单元,与 UpdateVendor 函数一起使用。我们可能需要一个 Squash players 的函数副本。

MESSAGE(Text009,Vend.TABLECAPTION,Vend."No.");

上述代码为最终用户显示一个消息框,告知记录已使用新编号创建。现在,我们的待办事项列表上有几件事情:

  1. 创建一个类似于 Vendor 表的主数据表。

  2. 我们需要复制 CreateVendor 函数。

  3. 查看联系人业务关系表和 CustVendBank-Update (5055) 代码单元。

让我们在开始第一个函数之前先看看后者,以了解一些重要信息:

UpdateVendor()
WITH Vend DO BEGIN
  GET(ContBusRel."No.");
  xRecRef.GETTABLE(Vend);
  NoSerie := "No. Series";
  PurchaserCode :=  Vend."Purchaser Code";
  TRANSFERFIELDS(Cont);
  "No." := ContBusRel."No.";
  "No. Series" := NoSerie;
  Vend."Purchaser Code" := PurchaserCode;
  MODIFY;
  RecRef.GETTABLE(Vend);
  ChangeLogMgt.LogModification(RecRef,xRecRef);
END;

此代码通过使用TRANSFERFIELDS函数同步联系表和供应商表。该函数将具有相同数字的所有字段从一个表转移到另一个表。这意味着我们无法在字段编号上发挥创意。例如,在联系表中,Name字段编号为2。如果我们为Name字段使用不同的编号,TRANSFERFIELDS将不会复制信息。

使用这些信息,我们的表应该看起来像这样:

逆向工程

注意,我们使用字段19为我们的Squash Player特定字段。这是因为字段19在供应商表中用于Budgeted Amount。因此,我们可以安全地假设微软将来不会在联系表中使用字段19

如果我们想要更加安全,另一种方法是将特定于我们解决方案的字段作为我们的附加数字序列的字段。在我们的例子中,它将是 123.456.700。

小贴士

你可以从一个表复制粘贴字段到另一个表。请注意,表关系和OnValidateOnLookup触发器中的 C/AL 代码也会被复制。如果我们想创建的表类似于现有表,我们也可以使用文件下拉菜单中的另存为选项。

下一步是为表添加一些业务逻辑。我们希望这个表使用数字序列功能,就像供应商表一样。这需要一些标准步骤:

  1. 首先,我们创建设置表。数字序列在设置表中定义。由于Squash Court模块将相当复杂,我们将创建自己的。逆向工程

    小贴士

    在 MSDN 上,你可以观看有关单例模式的视频,msdn.microsoft.com/en-us/dynamics/nav/dn722393.aspx

    设置表总是有一个单一的主键字段,如前图所示,以及必要的设置字段。这个表设计为只有一个记录。

  2. 然后,我们创建一个链接到数字序列。现在,我们的Squash Player表需要有一个链接到数字序列。我们可以从供应商表中复制这个字段,并可以创建一个与No. Series表的表关系,如下截图所示:逆向工程

  3. 现在,我们将 C/AL 业务逻辑添加到我们的表中,但首先我们需要定义所需的变量。这些是我们新的Squash Setup表和Number Series Management代码单元。逆向工程

    我们可以在特别创建的C/AL Globals菜单中定义变量。

    逆向工程

    小贴士

    强烈推荐使用微软的命名标准,这允许你复制粘贴大量代码,并使其他人更容易阅读你的代码。

数字系列需要三处代码。此代码确保数字系列功能的业务逻辑始终得到遵循:

  1. 以下代码放入OnInsert触发器中。它使用数字系列的下一个值填充No.字段:

    OnInsert()
    IF "No." = '' THEN BEGIN
      SquashSetup.GET;
      SquashSetup.TESTFIELD("Squash Player Nos.");
      NoSeriesMgt.InitSeries(SquashSetup."Squash Player Nos.",
        xRec."No. Series",0D,"No.","No. Series");
    END;
    
  2. No.字段的OnValidate触发器测试当用户手动输入值时是否允许:

    No. - OnValidate()
    IF "No." <> xRec."No." THEN BEGIN
      SquashSetup.GET;
      NoSeriesMgt.TestManual(SquashSetup."Squash Player Nos.");
      "No. Series" := '';
    END;
    
  3. 最后,我们创建一个新的AssistEdit函数。这个函数是为了可读性,以及其他人阅读你之后的代码。代码用于页面或表单,并允许用户在链接的数字系列之间切换:

    AssistEdit() : Boolean
    SquashSetup.GET;
    SquashSetup.TESTFIELD("Squash Player Nos.");
    IF NoSeriesMgt.SelectSeries(SquashSetup."Squash Player Nos.",
      xRec."No. Series","No. Series") 
    THEN BEGIN
      NoSeriesMgt.SetSeries("No.");
      EXIT(TRUE);
    END;
    

当数字系列就绪后,我们可以在接触业务关系表中做出必要的更改。

在这个表中,我们需要添加将壁球运动员链接到联系人的功能。这可以通过表设计器属性窗口完成,该窗口可以通过按(Shift + F4)或使用视图下拉菜单中的属性选项访问,如下面的截图所示:

逆向工程

首先,我们将壁球运动员选项添加到链接到表字段中,如下面的截图所示:

逆向工程

小贴士

选项被转换为 SQL Integer 数据类型。确保添加一些空白选项,这样当微软发布其他功能时,我们不会受到影响。更改现有选项字段的整数值需要大量工作。

然后,我们创建一个与我们的新表的关系,如下面的截图所示:

逆向工程

下一步是扩展CustVendBank-Update代码单元,添加一个新的UpdateSquashPlayer函数。这是之前讨论过的UpdateVendor函数的副本。我们可以在全局菜单中添加函数。

复制函数有两种方式。我们可以手动创建一个新函数并复制 C/AL 代码和变量,或者从列表中选择一个函数并使用复制和粘贴,然后重命名该函数。

逆向工程

小贴士

当你在函数中添加---行时,其他人可以看到它不是一个微软函数。你还可以包括项目名称,如---Squash。这也使得代码更容易升级或与其他代码合并。

此代码还需要一个新的全局变量,SquashPlayer

UpdateSquashPlayer()
WITH SquashPlayer DO BEGIN
  GET(ContBusRel."No.");
  xRecRef.GETTABLE(SquashPlayer);
  NoSerie := "No. Series";
  TRANSFERFIELDS(Cont);
  "No." := ContBusRel."No.";
  "No. Series" := NoSerie;
  MODIFY;
  RecRef.GETTABLE(SquashPlayer);
  ChangeLogMgt.LogModification(RecRef,xRecRef);
END;

最后的准备工作是向营销设置表添加壁球运动员业务关系代码字段,如下面的截图所示:

逆向工程

小贴士

我们在我们的字段中使用与我们的对象相同的编号。这样,如果将来添加更多功能,更容易看到什么属于什么。

在完成所有准备工作后,我们终于可以开始创建我们的函数了,这个函数位于接触表(5050)中,我们可以从用户界面调用它:

CreateSquashPlayer()
TESTFIELD(Type, Type::Person);

RMSetup.GET;
RMSetup.TESTFIELD("Bus. Rel. Code for Squash Pl.");

CLEAR(SquashPlayer);
SquashPlayer.INSERT(TRUE);

ContBusRel."Contact No." := Cont."No.";
ContBusRel."Business Relation Code" := 
  RMSetup."Bus. Rel. Code for Squash Pl.";
ContBusRel."Link to Table" := 
  ContBusRel."Link to Table"::"Squash Player";
ContBusRel."No." := SquashPlayer."No.";
ContBusRel.INSERT(TRUE);

UpdateCustVendBank.UpdateSquashPlayer(Cont,ContBusRel);

MESSAGE(Text009,SquashPlayer.TABLECAPTION,SquashPlayer."No.");

请注意,我们不需要SetInsertFromContact函数。此函数允许用户首先创建一个新的供应商,然后使用供应商信息创建一个联系。我们不想在我们的应用程序中支持此方法。

现在,我们可以将此功能添加到页面并测试我们的功能:

逆向工程

设计日记

现在,是时候开始壁球应用程序的产品部分了。在这一部分,我们将不再详细进行逆向工程。我们将学习如何在标准功能中搜索并重用我们自己的软件中的部分。

对于这部分,我们将查看 Microsoft Dynamics NAV 中的资源。资源类似于使用作为产品的项目,但远没有这么复杂,这使得查找和学习更容易。

壁球法庭主数据

我们公司有 12 个法庭,我们想在 Microsoft Dynamics NAV 中注册这些法庭。这些主数据类似于资源,因此我们将继续并复制这个功能。资源不像供应商/壁球运动员表那样附加到联系表上。我们需要再次使用编号系列,所以我们将为我们的壁球设置表添加一个新的编号系列。

创建后,壁球法庭表应该看起来像这样:

壁球法庭主数据

章节对象

本章需要一些对象。有关如何导入这些对象的描述可以在附录,安装指南中找到。

章节对象

在导入过程完成后,请确保您的当前数据库是角色定制客户端的默认数据库,并运行页面 123456701,壁球设置

章节对象

从此页面,选择操作初始化壁球应用程序。这将执行此页面的InitSquashApp函数中的 C/AL 代码,这将为我们准备演示数据。对象是在 Microsoft Dynamics NAV 2013 R2 W1 数据库中准备和测试的。

预订

在运行壁球法庭时,我们希望能够跟踪预订。查看标准 Dynamics NAV 功能,创建一个壁球运动员日记可能是个好主意。日记可以创建可以开票的预订条目。

日记需要对象结构。日记是用本章提供的对象准备的。从头开始创建一个新的日记是一项大量工作,并且很容易导致出错。从类似我们设计所需的日记的标准应用程序中复制现有的日记结构更容易且更安全。

在我们的示例中,我们已经复制了资源日记:

预订

小贴士

您可以将这些对象以文本格式导出,然后重命名和重新编号要重用的对象。壁球日记对象是从资源日记中重新编号和重命名的。

如第一章中所述,Microsoft Dynamics NAV 简介,所有期刊都具有相同的结构。模板、批次和登记簿表几乎总是相同的,而期刊行和总账条目表包含特定功能的字段。让我们逐一查看它们。

期刊模板包含多个字段,如下面的截图所示:

预订

让我们更详细地讨论这些字段:

  • 名称:这是一个唯一的名称。可以定义所需数量的模板,但通常每个表单 ID 一个模板,一个用于周期性即可。如果您需要具有不同源代码的期刊,则需要更多模板。

  • 描述:对其目的的一个可读且易于理解的描述。

  • 测试报告 ID:所有模板都有一个测试报告,允许用户检查过账错误。

  • 表单 ID:对于某些期刊,需要更多的 UI 对象。例如,一般期刊有银行和现金的特殊表单。

  • 过账报告 ID:当用户选择过账打印时,打印此报告。

  • 强制过账报告:当必须生成过账报告时使用此选项。

  • 源代码:在这里,您可以输入通过此期刊完成的全部条目的跟踪代码。

  • 原因代码:此功能与源代码类似。

  • 周期性:每次从周期性期刊过账行时,都会自动创建新的行,并使用周期性日期公式中定义的过账日期。

  • 编号系列:当您使用此功能时,期刊行中的文档编号将自动填充从这个编号系列中的新编号。

  • 过账编号系列:用于周期性期刊。

期刊批次包含各种字段,如下面的截图所示:

预订

让我们更详细地讨论这些字段:

  • 期刊模板名称:此批次所引用的期刊模板的名称

  • 名称:每个批次都应该有一个唯一的代码

  • 描述:对此次批次的一个可读且解释性的描述

  • 原因代码:当填写此原因代码时,将覆盖期刊模板中的原因代码

  • 编号系列:当填写此编号系列时,将覆盖期刊模板中的编号系列

  • 过账编号系列:当填写此过账编号系列时,将覆盖期刊模板中的过账编号系列

登记簿表包含各种字段,如下面的截图所示:

预订

您需要了解的来自期刊登记簿标签的术语包括:

  • 编号:此字段为每个交易自动递增填充,并且数字之间没有空缺。

  • 起始条目编号:此交易引用的是创建的第一个总账条目。

  • 至过账编号:使用此交易创建的最后账簿过账的引用

  • 创建日期:始终填写交易过账的实际日期

  • 用户 ID:已发布交易的最后用户 ID

日记账

日记账行有许多强制性字段,这些字段适用于所有日记账,以及一些适用于其设计功能的字段。

在我们的案例中,日记账应该创建一个预订,然后可以开具发票。这需要在行中填写一些信息。

预订

预订流程是一个物流流程,需要我们知道网球场号码、预订日期和时间。我们还需要知道玩家想要玩多长时间。为了检查预订,存储网球场球员的号码也可能很有用。

发票

对于发票部分,我们需要知道需要开具发票的价格。存储成本以查看我们的利润也可能很有用。为了系统确定营业额的正确总账科目,我们还需要定义一个通用产品过账组。我们将在第三章 财务管理中看到更多关于它是如何工作的内容。

发票

让我们更详细地讨论这些字段:

  • 日记账模板名称:这是对当前日记账模板的引用。

  • 行号:每个日记账都有几乎无限的行数;这个数字会自动增加 10000,允许在行之间创建。

  • 过账类型:这是预订或发票。

  • 文档编号:这个号码可以用作给网球场球员的预订号码。当过账类型发票时,它是发票号码。

  • 过账日期:这通常是预订日期,但当过账类型发票时,它可能是发票日期,这可能与总账的过账日期不同。

  • 网球场球员编号:这是对已预订的网球场球员的引用。

  • 网球场编号:这是对网球的引用。

  • 描述:这会自动更新为网球场编号、预订日期和时间,但可以被用户更改。

  • 预订日期:这是实际预订日期。

  • 从时间:这是预订的开始时间。我们只允许整点和半小时。

  • 至时间:这是预订的结束时间。我们只允许整点和半小时。当人们输入数量时,这会自动填写。

  • 数量:这是玩球时间的小时数。我们只允许输入 0.5 的倍数。当时间填写时,这会自动计算。

  • 单位成本:这是运行一个网球场一小时的成本。

  • 总成本:这是此次预订的成本。

  • 单价:这是每小时预订的发票价格。这取决于壁球运动员是否是会员。

  • 总价:这是此预订的总发票价格。

  • 快捷维度代码 1 和 2:这是对用于此交易的维度的引用。

  • 适用于条目编号:当预订被开票时,这是对预订的壁球条目编号的引用。

  • 源代码:这是从日记账批次或模板继承的,并在过账交易时使用。

  • 可收费:当使用此选项时,预订将不会有发票。

  • 日记账批次名称:这是对用于此交易的日记账批次的引用。

  • 原因代码:这是从日记账批次或模板继承的,并在过账交易时使用。

  • 重复方法:当日记账是重复日记账时,您可以使用此字段确定在过账行之后是否将金额字段清空。

  • 重复频率:此字段确定在过账重复行之后的新过账日期。

  • 通用业务过账组:通用业务和产品过账组的组合决定了我们在开票预订时的营业额的 G/L 账户。通用业务过账组是从开票客户那里继承的。

  • 通用生产过账组:这将从壁球运动员那里继承。

  • 外部文档编号:当壁球运动员希望我们记录一个参考编号时,我们可以在这里存储它。

  • 过账编号系列:当日记账模板有过账编号系列时,它将在这里被填充,以便在过账时使用。

  • 开票客户编号:这决定了谁为预订付费。我们将从壁球运动员那里继承这个信息。

因此,我们现在有一个地方可以输入预订,但在我们可以开始这样做之前,我们还有一些事情要做。一些字段被确定为继承和计算:

  • 时间字段需要进行计算,以避免人们输入错误值

  • 单价应该被计算

  • 单位成本过账组开票客户编号需要被继承

  • 作为最后的点缀,我们将探讨实施维度

时间计算

当涉及到时间时,我们只想允许特定的开始和结束时间。我们的壁球场地可以以半小时为一个时间段使用。数量字段应根据输入的时间计算,反之亦然。

为了实现最灵活的解决方案,我们将创建一个新的表格,其中包含允许的开始和结束时间。这个表格将有两个字段:预订时间持续时间

持续时间字段将是一个小数字段,我们将将其提升为索引求和字段。这将使我们能够使用 SIFT 来计算数量。

时间计算

当表格被填充时,它将看起来像这样:

时间计算

网球场日记账表中的时间字段现在将与这个表建立表关系。这防止用户输入表中没有的值,从而只输入有效的开始和结束时间。这一切都无需任何 C/AL 代码,并且当时间改变时具有灵活性。

时间计算

现在,我们需要一些代码来根据输入计算数量:

From Time - OnValidate()
CalcQty;

To Time - OnValidate()
CalcQty;

CalcQty()
IF ("From Time" <> 0T) AND ("To Time" <> 0T) THEN BEGIN
  IF "To Time" <= "From Time" THEN
    FIELDERROR("To Time");
  ResTime.SETRANGE("Reservation Time", "From Time", 
    "To Time");
  ResTime.FIND('+');
  ResTime.NEXT(-1);
  ResTime.SETRANGE("Reservation Time", "From Time", 
    ResTime."Reservation Time");
  ResTime.CALCSUMS(Duration);
  VALIDATE(Quantity, ResTime.Duration);
END;

当用户在从时间到时间字段中输入值时,会执行CalcQty函数。这会检查这两个字段是否有值,然后检查到时间是否大于从时间

然后我们在预订时间表上放置一个过滤器。现在,当用户从8:00预订到9:00时,过滤器中有三个记录,使得Calcsums(所有记录的总和)的持续时间变为1,5。因此,我们找到之前的预订时间并使用它。

这个例子展示了如何轻松地使用内置的 Microsoft Dynamics NAV 功能,如表关系和Calcsums,而不是复杂的时计算,我们也可以使用。

价格计算

如第一章中所述,Microsoft Dynamics NAV 简介,有一个特殊的技巧来确定价格。价格存储在一个表中,其中所有可能的参数作为字段,通过过滤这些字段,确定最佳价格。如果需要,还需要额外的逻辑来找到最低(或最高)价格,如果找到多个价格。

为了查看、学习和热爱标准应用的部分,我们使用了销售价格表(7002)和销售价格计算管理代码单元(7000),尽管我们只需要这部分功能的一小部分。这种价格计算机制在整个应用中使用,并提供了计算销售价格的标准化方法。类似的构造用于采购价格,使用采购价格表(7012)和采购价格计算管理代码单元(7010)。

压缩价格

在我们的例子中,我们已经确定我们有一个针对会员的特殊价格,但让我们假设我们还有一个针对冬季和夏季白天和晚上的特殊价格。

这可能使我们的表看起来如下:

压缩价格

我们可以为冬季和夏季的日期为会员制定特殊价格,并使价格仅在特定时间有效。我们还可以为球场制定特殊价格。

这个表可以通过各种代码进行创造性地扩展,直到我们最终得到标准产品中的销售价格表(7002),这是我们例子的模板。

价格计算管理代码单元

为了计算价格,我们需要一个类似于标准产品的代码单元。这个代码单元使用网球场日记账记录调用,并将所有有效的价格存储在缓冲表中,然后如果存在任何重叠,找到最低价格:

FindSquashPrice()
WITH FromSquashPrice DO BEGIN
  SETFILTER("Ending Date",'%1|>=%2',0D,StartingDate);
  SETRANGE("Starting Date",0D,StartingDate);

  ToSquashPrice.RESET;
  ToSquashPrice.DELETEALL;

  SETRANGE(Member, IsMember);

  SETRANGE("Ending Time", 0T);
  SETRANGE("Squash Court No.", '');
  CopySquashPriceToSquashPrice(FromSquashPrice,ToSquashPrice);

  SETRANGE("Ending Time", 0T);
  SETRANGE("Squash Court No.", CourtNo);
  CopySquashPriceToSquashPrice(FromSquashPrice,ToSquashPrice);

  SETRANGE("Squash Court No.", '');
  IF StartingTime <> 0T THEN BEGIN
    SETFILTER("Ending Time",'%1|>=%2',000001T,StartingTime);
    CopySquashPriceToSquashPrice(FromSquashPrice,
      ToSquashPrice);
  END;

  SETRANGE("Squash Court No.", CourtNo);
  IF StartingTime <> 0T THEN BEGIN
    SETFILTER("Ending Time",'%1|>=%2',000001T,StartingTime);
    CopySquashPriceToSquashPrice(FromSquashPrice,
      ToSquashPrice);
  END;
END;

如果过滤器中没有价格,它将使用网球场单位价格,如下所示:

CalcBestUnitPrice()
WITH SquashPrice DO BEGIN
  FoundSquashPrice := FINDSET;
  IF FoundSquashPrice THEN BEGIN
    BestSquashPrice := SquashPrice;
    REPEAT
      IF SquashPrice."Unit Price" < 
        BestSquashPrice."Unit Price" 
      THEN
        BestSquashPrice := SquashPrice;
    UNTIL NEXT = 0;
  END;
END;

// No price found in agreement
IF BestSquashPrice."Unit Price" = 0 THEN
  BestSquashPrice."Unit Price" := SquashCourt."Unit Price";

SquashPrice := BestSquashPrice;

继承数据

要使用日记账的应用程序产品部分,我们希望从主数据表中继承一些字段。为了实现这一点,我们需要将这些字段从其他表复制并粘贴到我们的主数据表中,并填充它。

在我们的示例中,我们可以从资源表(156)复制并粘贴字段。我们还需要在日记账表中的 OnValidate 触发器中添加代码。

继承数据

例如,壁球场地表通过以下字段扩展:单位代码单价一般生产过账组增值税过账组,如前一张截图所示。

我们现在可以向日记账表中的 Squash Court No. 字段的 OnValidate 中添加代码。

Squash Court No. - OnValidate()
IF SquashCourt.GET("Squash Court No.") THEN BEGIN
  Description := SquashCourt.Description;
  "Unit Cost" := SquashCourt."Unit Cost";
  "Gen. Prod. Posting Group" := SquashCourt."Gen. Prod. Posting Group";
  FindSquashPlayerPrice;
END;

请注意,在从 FindSquashPlayerPrice 函数执行时,Squash 价格计算管理代码单元使用的是单价。

维度

在 Microsoft Dynamics NAV 中,维度在主数据中定义,并过账到总账条目以用于分析视图条目。在第三章 财务管理中,我们将讨论如何分析维度生成数据。在这个过程中,它们会在不同的表中移动很多,如下所示:

  • 表 348 | 维度:这是定义主要维度代码的地方。

  • 表 349 | 维度值:这是每个维度可以拥有无限多个值的表。

  • 表 350 | 维度组合:在这个表中,我们可以阻止某些维度代码的组合。

  • 表 351 | 维度值组合:在这个表中,我们可以阻止某些维度值的组合。如果这个表被填充,这些维度的维度组合表中将填充值 Limited

  • 表 352 | 默认维度:这个表为所有定义了维度的主数据填充。

  • 表 354 | 默认维度优先级:当一笔交易中有一个以上的主数据记录具有相同的维度时,可以在这里设置优先级。

  • 表 480 | 维度集条目:这个表包含所有使用维度组合的矩阵。

  • 代码单元 408 | 维度管理:这是应用程序中所有维度移动的唯一点。

在我们的应用程序中,维度通过壁球日志行从壁球运动员、壁球场地和客户表移动到壁球总账条目。当我们创建发票时,我们将维度从总账条目移动到销售行表。

主数据

要将维度连接到主数据,我们首先需要允许更改代码单元 408 维度管理。

SetupObjectNoList()
TableIDArray[1] := DATABASE::"Salesperson/Purchaser";
TableIDArray[2] := DATABASE::"G/L Account";
TableIDArray[3] := DATABASE::Customer;
...
TableIDArray[22] := DATABASE::"Service Item Group";
TableIDArray[23] := DATABASE::"Service Item";

//* Squash Application
TableIDArray[49] := DATABASE::"Squash Player";
TableIDArray[50] := DATABASE::"Squash Court";
//* Squash Application

Object.SETRANGE(Type,Object.Type::Table);

FOR Index := 1 TO ARRAYLEN(TableIDArray) DO BEGIN
  ...

TableIDArray 变量有默认的 23 个维度数。我们将这个数改为 50

小贴士

通过留下空位,我们允许微软在未来无需我们更改代码的情况下添加主数据表。

如果不进行此更改,当尝试使用维度时,系统将返回以下错误消息:

主数据

下一个更改是将全局维度字段添加到主数据表中。它们可以从其他主数据表中复制和粘贴。

主数据

当这些字段被验证时,会执行ValidateShortcutDimCode函数,如下所示:

ValidateShortcutDimCode()
DimMgt.ValidateDimValueCode(FieldNumber,ShortcutDimCode);
DimMgt.SaveDefaultDim(DATABASE::"Squash Player","No.",
  FieldNumber,ShortcutDimCode);
MODIFY;

日记账

当我们在日记账表中使用主数据记录时,维度会从默认维度表复制到维度集条目表。这是通过从每个主数据引用字段的OnValidate中调用的以下代码片段来完成的:

CreateDim()
TableID[1] := Type1;
No[1] := No1;
TableID[2] := Type2;
No[2] := No2;
TableID[3] := Type3;
No[3] := No3;

"Shortcut Dimension 1 Code" := '';
"Shortcut Dimension 2 Code" := '';

"Dimension Set ID" :=
  DimMgt.GetDefaultDimID(TableID,No,"Source Code",
    "Shortcut Dimension 1 Code",
      "Shortcut Dimension 2 Code",0,0);

为了决定要继承哪些维度,我们首先应该分析在我们的日记账中使用默认维度的哪些主数据。

Squash Court No. - OnValidate()
CreateDim(
  DATABASE::"Squash Court","Squash Court No.",
  DATABASE::"Squash Player","Squash Player No.",
  DATABASE::Customer,"Bill-to Customer No.");

在我们的案例中,Table[1]Squash PlayerTable[2]Squash CourtTable[3]Customer。维度管理代码单元确保一切都被复制。我们可以使用标准的 Microsoft Dynamics NAV 函数。

过账

当我们使用Codeunit Squash Jnl.-Post Line (123456703)过账日记账时,维度会使用以下维度集 ID 进行复制:

Code()

...
SquashLedgEntry."Dimension Set ID" := "Dimension Set ID";
...

SquashLedgEntry.INSERT;

NextEntryNo := NextEntryNo + 1;

此字段还用于我们将在本章的发票部分创建的合并发票报告中,我们将创建。

CreateLn()
...
SalesLn.INIT;

SalesLn."Dimension Set ID" := "Dimension Set ID";

SalesLn.INSERT(TRUE);

过账过程

我们现在已准备好过账日记账。我们已经实现了所有业务逻辑,除了过账代码。

过账过程

在 Microsoft Dynamics NAV 中,日记账的过账过程有几个代码单元用于结构:

  • Jnl.-Check Line: 这个代码单元检查日记账行是否适用于过账。

  • Jnl.-Post Line: 这个代码单元执行实际的日记账条目和登记表创建,并在必要时调用其他Jnl.-Post Line代码单元,以在第一章 Microsoft Dynamics NAV 简介中提供交易结构。

  • Jnl.-Post Batch: 这个代码单元会遍历日记账批次中的所有日记账行,并过账所有行。

  • Jnl.-Post: 这是从页面调用的代码单元。它调用Jnl.-Post Batch代码单元,并处理一些用户消息。

  • Jnl.-Post+Print: 当您点击过账 + 打印时,会调用此代码单元。它执行与Jnl.-Post代码单元相同的功能,但会额外打印在日记账模板中定义的报告。

  • Jnl.-B.Post: 这会过账所有没有错误的日记账行,并标记有错误的行。

  • Jnl.-B.Post+Print: 这与Jnl.-B.Post执行相同的功能,但会额外打印在日记账模板中定义的报告。

检查行

让我们看看检查行代码单元。在测试时,Microsoft Dynamics NAV 有一个简单的规则:

测试近,测试远,做,清理

首先,我们需要测试日记账行表中的字段,然后读取外部数据表以检查一切是否正常,然后过账行并从日记账表中删除数据。

如果我们自己的表中的文档编号为空,或者因为过账日期不在有效范围内而开始过账过程并出错,那么从数据库中读取总账设置表是没有意义的。这会导致从数据库到客户端的大量不必要的 I/O。

RunCheck()
WITH SquashJnlLine DO BEGIN
  IF EmptyLine THEN
    EXIT;

  TESTFIELD("Squash Player No.");
  TESTFIELD("Squash Court No.");
  TESTFIELD("Posting Date");
  TESTFIELD("Gen. Prod. Posting Group");
  TESTFIELD("From Time");
  TESTFIELD("To Time");
  TESTFIELD("Reservation Date");
  TESTFIELD("Bill-to Customer No.");

  IF "Entry Type" = "Entry Type"::Invoice THEN
    TESTFIELD("Applies-to Entry No.");

  IF "Applies-to Entry No." <> 0 THEN
    TESTFIELD("Entry Type", "Entry Type"::Invoice);
  IF "Posting Date" <> NORMALDATE("Posting Date") THEN
    FIELDERROR("Posting Date",Text000);

  IF (AllowPostingFrom = 0D) AND (AllowPostingTo = 0D) THEN 
    ...  
  END;

  ...

  IF NOT DimMgt.CheckDimIDComb("Dimension Set ID") THEN
    ...
  TableID[1] := DATABASE::"Squash Player";
  No[1] := "Squash Player No.";
  ...
  IF NOT DimMgt.CheckJnlLineDimValuePosting(JnlLineDim,
    TableID,No) 
  THEN
    IF "Line No." <> 0 THEN
      ..................

在前面的代码中,我们可以清楚地看到首先检查我们表中的字段,然后是日期验证,最后是维度检查。

过账行

实际的过账代码非常简单。检查值后,创建或更新一个登记簿。

Code()
WITH SquashJnlLine DO BEGIN
  IF EmptyLine THEN
    EXIT;

  SquashJnlCheckLine.RunCheck(SquashJnlLine,TempJnlLineDim);

  IF NextEntryNo = 0 THEN BEGIN
    SquashLedgEntry.LOCKTABLE;
    IF SquashLedgEntry.FIND('+') THEN
      NextEntryNo := SquashLedgEntry."Entry No.";
    NextEntryNo := NextEntryNo + 1;
  END;

  IF SquashReg."No." = 0 THEN BEGIN
    SquashReg.LOCKTABLE;
    IF (NOT SquashReg.FIND('+') OR ... THEN BEGIN
      SquashReg.INIT;
      SquashReg."No." := SquashReg."No." + 1;
      ...
      SquashReg.INSERT;
    END;
  END;
  SquashReg."To Entry No." := NextEntryNo;
  SquashReg.MODIFY;

  SquashPlayer.GET("Squash Player No.");
  SquashPlayer.TESTFIELD(Blocked,FALSE);

  IF (GenPostingSetup."Gen. Bus. Posting Group" <> 
    "Gen. Bus. Posting Group") OR
    (GenPostingSetup."Gen. Prod. Posting Group" <> 
    "Gen. Prod. Posting Group")
  THEN
    GenPostingSetup.GET("Gen. Bus. Posting Group",
      "Gen. Prod. Posting Group");

  SquashLedgEntry.INIT;
  SquashLedgEntry."Entry Type" := "Entry Type";
  SquashLedgEntry."Document No." := "Document No.";
  ...
  SquashLedgEntry."No. Series" := "Posting No. Series";

  SquashLedgEntry.INSERT;

所有字段都简单地移动到账簿分录表中。这就是使 Microsoft Dynamics NAV 简单而强大的原因。

在这里,我们可以清楚地看到向过账过程添加字段是多么容易。只需将字段添加到日记账行、账簿分录,并在过账过程中添加一行代码即可。

发票

我们待办事项列表上的最后一个问题是发票过程。为此,我们使用标准应用程序的一部分。

如第一章中所述,Microsoft Dynamics NAV 简介,发票是通过具有头和行表的文档结构完成的。这有一个过账程序,将启动日记账交易。

对于我们的应用程序,我们需要创建发票文档,并确保过账时更新我们的子管理。

发票文档

Microsoft Dynamics NAV 中的销售发票文档存储在销售头(36)和销售行(37)表中。我们将创建一个报告,将未结预订条目合并成发票,允许用户在压缩账簿分录表中按特定条目或任何其他字段值进行筛选。

Microsoft Dynamics NAV 中的报告不仅用于打印文档;我们还可以使用其数据集功能来启动批处理作业。

为了启用此功能,我们的批处理作业需要具有一个特殊属性,ProcessingOnly,所以让我们开始一个空白报告并执行此操作。

发票文档

报告将遍历按条目类型预订筛选的压缩账簿分录,并打开。排序顺序为打开条目类型客户发票编号预订日期。为了使用排序,必须在表定义中将字段一起定义为键。

发票文档

由于客户发票编号是排序中第一个非筛选值,我们可以假设如果这个值发生变化,我们需要一个新的销售头。

对于每个压缩账簿分录,我们将生成以下销售行:

Squash Ledger Entry - OnAfterGetRecord()
IF "Bill-to Customer No." <> SalesHdr."Bill-to Customer No." 
THEN
  CreateSalesHdr;

CreateLn;

销售头

创建销售头的代码如下:

CreateSalesHdr()
CLEAR(SalesHdr);
SalesHdr.SetHideValidationDialog(TRUE);
SalesHdr."Document Type" := SalesHdr."Document Type"::Invoice;
SalesHdr."Document Date" := WORKDATE;
SalesHdr."Posting Date" := WORKDATE;
SalesHdr.VALIDATE("Sell-to Customer No.", 
  "Squash Ledger Entry"."Bill-to Customer No.");
SalesHdr.INSERT(TRUE);

NextLineNo := 10000;
CounterOK := CounterOK + 1;

SetHideValidationDialog 函数确保在验证值时不会出现弹出消息。这是 Microsoft Dynamics NAV 中的一个标准函数,专为这个目的设计。

INSERT 语句的 TRUE 参数确保触发了编号序列。

销售行

要创建销售行,我们需要以下代码的最小版本。请注意,我们在销售行表中添加了字段“适用于压榨条目编号”。

CreateLn()
WITH "Squash Ledger Entry" DO BEGIN
  GenPstSetup.GET("Gen. Bus. Posting Group", 
    "Gen. Prod. Posting Group");
  GenPstSetup.TESTFIELD("Sales Account");

  SalesLn.INIT;
  SalesLn."Document Type" := SalesHdr."Document Type";
  SalesLn."Document No." := SalesHdr."No.";
  SalesLn."Line No." := NextLineNo;
  SalesLn."Dimension Set ID" := "Dimension Set ID";

  SalesLn."System-Created Entry" := TRUE;

  SalesLn.Type := SalesLn.Type::"G/L Account";
  SalesLn.VALIDATE("No.", GenPstSetup."Sales Account");
  SalesLn.Description := Description;

  SalesLn.VALIDATE(Quantity, Quantity);
  SalesLn.VALIDATE("Unit Price", "Unit Price");
  SalesLn.VALIDATE("Unit Cost (LCY)", "Unit Cost");

  SalesLn."Applies-to Squash Entry No." := "Entry No.";
  SalesLn.INSERT(TRUE);

END;
NextLineNo := NextLineNo + 10000;

小贴士

当您向销售和采购文档表添加字段时,请确保也将这些字段添加到这些表的已过账等效表中,并使用相同的编号。这样,您可以确保信息被复制到历史数据中。这是使用TRANSFERFIELDS命令完成的。我们将在第六章 贸易中讨论这些表。

对话框

如果合并开票需要一些时间,显示一个进度条可能是个好主意。为此,Microsoft Dynamics NAV 有一个标准结构。

窗口显示当前正在处理的发票客户编号以及从 1%到 100%的进度条。这是通过保持计数器来计算的。

在过程结束时,我们显示一条消息,告诉用户从压榨账目条目中创建了多少发票。

Squash Ledger Entry - OnPreDataItem()
CounterTotal := COUNT;
Window.OPEN(Text000);

Squash Ledger Entry - OnAfterGetRecord()
Counter := Counter + 1;
Window.UPDATE(1,"Bill-to Customer No.");
Window.UPDATE(2,ROUND(Counter / CounterTotal * 10000,1));

...

Squash Ledger Entry - OnPostDataItem()
Window.CLOSE;
MESSAGE(Text001,CounterOK,CounterTotal);

要完成此操作,我们需要一些变量。窗口变量是对话框类型,而计数器计数器总数计数器 OK是整数,如以下截图所示:

对话框

常量Text000具有特殊值#1##########@2@@@@@@@@@@@@@。前者允许我们显示和更新一些文本;后者用于创建进度条。

对话框

结果将类似于以下截图所示:

对话框

备注

关于使用进度条及其对性能影响的最佳实践文档可在www.mibuso.com/howtoinfo.asp?FileID=17找到。

过账过程

现在,我们的销售发票已经准备好,我们可以开始对过账过程进行必要的更改。过账销售文档是通过单个过账代码单元和一些辅助对象完成的。

  • 报告 297:此报告可用于使用过滤器同时过账多个文档。

  • 代码单元 80:这是我们即将更改的实际过账例程。

  • 代码单元 81:此代码单元从用户界面调用,如果用户想要发货、开票或两者兼而有之(如果文档是订单),如果文档是发票或贷项通知,则显示是/否对话框。

  • 代码单元 82:当用户选择过账并打印时,此代码单元将被执行,它执行与代码单元 81相同的功能,并打印报告。

因此,我们将对代码单元 80进行修改。在进入并修改之前,我们需要了解这个代码单元的特定结构。

分析对象

代码单元还具有测试近处测试远处执行清理策略,因此第一步是在实际过账开始之前确保一切就绪。让我们看看这个代码单元是如何结构的。

备注

销售后置代码单元太长,无法详细讨论。我们将关注最重要的部分,并学习如何阅读这类代码流程。

这一部分执行了接近步骤的测试和远离步骤的一部分测试。ShipInvoiceReceive字段在代码单元 81 和 82 中设置,但会进行检查和完成以确保无误。

Code()
...
WITH SalesHeader DO BEGIN
  TESTFIELD("Document Type");
  TESTFIELD("Sell-to Customer No.");
  TESTFIELD("Bill-to Customer No.");
  TESTFIELD("Posting Date");
  TESTFIELD("Document Date");
  IF GenJnlCheckLine.DateNotAllowed("Posting Date") THEN
    FIELDERROR("Posting Date",Text045);
  CASE "Document Type" OF
    "Document Type"::Order:
      Receive := FALSE;
    "Document Type"::Invoice:
      BEGIN
        Ship := TRUE;
        Invoice := TRUE;
        Receive := FALSE;
      END;
    "Document Type"::"Return Order":
      Ship := FALSE;
    "Document Type"::"Credit Memo":
      BEGIN
        Ship := FALSE;
        Invoice := TRUE;
        Receive := TRUE;
      END;
  END;

  IF NOT (Ship OR Invoice OR Receive) THEN
    ERROR(...);

  WhseReference := "Posting from Whse. Ref.";
  "Posting from Whse. Ref." := 0;

  IF Invoice THEN
    CreatePrepaymentLines(...);
  CheckDim;

下一步是将销售表头信息移动到发货、发票、贷项通知或退货收据表头的历史表中。这些部分注释如下:

  // Insert invoice header or credit memo header
  IF Invoice THEN
    IF "Document Type" IN ["Document Type"::Order,
      "Document Type"::Invoice] 
    THEN BEGIN
      SalesInvHeader.INIT;
      SalesInvHeader.TRANSFERFIELDS(SalesHeader);

注意

我们将在第六章贸易中讨论销售表头与销售发货、销售发票、销售贷项通知和退货收据之间的关系。

完成这些操作后,处理销售行。它们也被移动到各种已过账行表中。这都属于过账流程中的“执行”部分。

  // Lines
  InvPostingBuffer[1].DELETEALL;
  DropShipPostBuffer.DELETEALL;
  EverythingInvoiced := TRUE;

  SalesLine.RESET;
  SalesLine.SETRANGE("Document Type","Document Type");
  SalesLine.SETRANGE("Document No.","No.");
  LineCount := 0;
  RoundingLineInserted := FALSE;
  MergeSaleslines(...);

如果采购订单中有寄售,这里将处理这种情况。我们将在第六章贸易中讨论寄售。

  // Post drop shipment of purchase order
  PurchSetup.GET;
  IF DropShipPostBuffer.FIND('-') THEN
    REPEAT
      PurchOrderHeader.GET(
        PurchOrderHeader."Document Type"::Order,
        DropShipPostBuffer."Order No.");

然后有一个部分在总账中创建财务信息。我们将在第三章财务管理中深入探讨这一部分。

  IF Invoice THEN BEGIN
    // Post sales and VAT to G/L entries from posting buffer
    LineCount := 0;
    IF InvPostingBuffer[1].FIND('+') THEN
      REPEAT
        LineCount := LineCount + 1;
        Window.UPDATE(3,LineCount);

        GenJnlLine.INIT;
        GenJnlLine."Posting Date" := "Posting Date";
        GenJnlLine."Document Date" := "Document Date";

然后,“清理”部分开始于计算剩余数量、增值税,并在可能的情况下删除销售表头和销售行。

IF ("Document Type" IN ["Document Type"::Order,
  "Document Type"::"Return Order"]) AND
   (NOT EverythingInvoiced)
THEN BEGIN
  MODIFY;
  // Insert T336 records
  InsertTrackingSpecification;

  IF SalesLine.FINDSET THEN
    REPEAT
      IF SalesLine.Quantity <> 0 THEN BEGIN
        IF Ship THEN BEGIN
          SalesLine."Quantity Shipped" :=
            SalesLine."Quantity Shipped" +
            SalesLine."Qty. to Ship";
          SalesLine."Qty. Shipped (Base)" :=
            SalesLine."Qty. Shipped (Base)" +
            SalesLine."Qty. to Ship (Base)";
        END;

“清理”部分以删除销售文档和相关信息以及清除使用的变量结束。

IF HASLINKS THEN DELETELINKS;
DELETE;
...

SalesLine.DELETEALL;
DeleteItemChargeAssgnt;
...

CLEAR(WhsePostRcpt);
CLEAR(WhsePostShpt);
...
CLEAR(WhseJnlPostLine);
CLEAR(InvtAdjmt);
Window.CLOSE;

进行更改

我们将要进行的更改是在处理行的部分:

// Squash Journal Line
IF SalesLine."Applies-to Squash Entry No." <> 0 THEN
  PostSquashJnlLn;

IF (SalesLine.Type >= SalesLine.Type::"G/L Account") AND 
  (SalesLine."Qty. to Invoice" <> 0) 
THEN BEGIN
  // Copy sales to buffer

我们将创建一个新的函数PostSquashJnlLn。这样我们最小化了标准代码的影响,当我们升级到新版本时,我们可以轻松地复制和粘贴我们的函数,并且如果需要,只需更改调用位置即可。

小贴士

任何时候都要尽量设计易于升级。记住,微软可能会在新版本中更改此代码,因此我们越灵活,越能尽量减少对标准代码的影响,就越好。

PostSquashJnlLn()
WITH SalesHeader DO BEGIN
  OldSquashLedEnt.GET(
    SalesLine."Applies-to Squash Entry No.");
  OldSquashLedEnt.TESTFIELD(Open);
  OldSquashLedEnt.TESTFIELD("Bill-to Customer No.", 
    "Bill-to Customer No.");

  SquashJnlLn.INIT;
  SquashJnlLn."Posting Date" := "Posting Date";
  SquashJnlLn."Reason Code" := "Reason Code";
  ...
  SquashJnlLn."Document No." := GenJnlLineDocNo;
  SquashJnlLn."External Document No." := GenJnlLineExtDocNo;
  SquashJnlLn.Quantity := -SalesLine."Qty. to Invoice";
  SquashJnlLn."Source Code" := SrcCode;
  SquashJnlLn."Dimension Set ID" := 
    SalesLine."Dimension Set ID";
  SquashJnlLn.Chargeable := TRUE;
  SquashJnlLn."Posting No. Series" := "Posting No. Series";
  SquashJnlPostLine.RunWithCheck(SquashJnlLn);
END;

我们的新函数首先获取它应用的压平账目条目,并测试它是否仍然开放以及账单客户编号是否没有更改。然后,我们通过销售行和旧的压平账目条目来填充压平日记账行。然后处理维度,并过账压平日记账行。

小贴士

日记账行实际上从未真正插入到数据库中。这是出于性能和并发性的原因。这里的所有日记账交易都在服务层缓存中处理。日记账也永远不会使用Validate来填充。这使得您能够非常清楚地看到发生了什么。

现在我们过账发票时,我们可以看到创建了发票条目:

进行更改

导航

现在我们已经涵盖了我们的压块球场应用程序运行所需的所有内容,但当我们添加新文档和账目条目时,Microsoft Dynamics NAV 有一个特殊功能需要更改:Navigate 功能。

该功能已在 第一章 中讨论过,Microsoft Dynamics NAV 简介。该对象是应用程序中的一个单独页面(344),需要更改两个地方。

FindRecords

我们首先更改的功能是 FindRecords。这个功能会遍历数据库,寻找所有可能的文档编号和过账日期的组合。

FindRecords()
...
// Squash Ledger Entries
IF SquashLedgEntry.READPERMISSION THEN BEGIN
  SquashLedgEntry.RESET;
  SquashLedgEntry.SETCURRENTKEY("Document No.",
    "Posting Date");
  SquashLedgEntry.SETFILTER("Document No.",DocNoFilter);
  SquashLedgEntry.SETFILTER("Posting Date",PostingDateFilter);
  InsertIntoDocEntry(
    DATABASE::"Squash Ledger Entry",0,
    SquashLedgEntry.TABLECAPTION,SquashLedgEntry.COUNT);
END;
// Squash Ledger Entries

DocExists := FINDFIRST;

该功能首先检查我们是否有权限读取压块账目条目表。如果我们的系统管理员不允许我们查看此表,则它不应显示。

过滤是在文档编号和过账日期上进行的。准备好后,系统将找到的记录数插入到结果表中。

ShowRecords

需要更改的第二个功能是 ShowRecords。这确保我们在点击 显示 动作时能看到压块账目条目。

ShowRecords()
...
    DATABASE::"Warranty Ledger Entry":
      FORM.RUN(0,WarrantyLedgerEntry);
//* Squash Ledger Entries
    DATABASE::"Squash Ledger Entry":
      FORM.RUN(0,SquashLedgEntry);
   END;
END;

测试

现在我们从由我们的合并开票报告生成的过账发票中进行导航时,我们得到以下结果:

测试

摘要

在本章中,我们为 Microsoft Dynamics NAV 创建了自己的垂直附加应用程序。我们使用了类似的数据模型和过账结构,并在适当的地方重用了标准应用程序的部分,但从未错误地使用标准功能。

我们看到了如何逆向工程 Microsoft Dynamics NAV 代码,以便找出要复制、粘贴和修改到我们应用程序中的类似标准功能。

我们还了解到日记和文档过账代码单元的工作原理以及如何使用 Test nearTest farDo itClean up 来构建结构。

在下一章中,我们将探讨 Microsoft Dynamics NAV 的财务功能,并对应用程序的这一部分进行一些更改。

第三章:财务管理

无论你经营的是一家公司、非营利组织还是教育机构,进行适当的簿记是强制性的,也是政府要求的。

这使得财务管理成为 Microsoft Dynamics NAV 最常用的部分,同时也是最不明显的地方进行更改,因为联邦法规不允许在这个应用程序的这一部分有太多的创造性。

本章的第一部分主要讲述了我们在上一章中讨论的“看、学、爱”原则。如果我们不知道工具的基本功能和结构,就无法将我们的应用程序与财务管理集成。

在本章的第二部分,我们将探讨一些如何更改或扩展财务管理工作方式的示例。

最后,我们将探讨如何从新设计的过账程序中在总账中创建过账。

在阅读本章后,你应该能够在新数据库中设置财务管理,并创建基本过账到总账,并理解如何将财务管理与你的应用程序集成。

会计科目表

会计科目表COA)是一个组织使用的账户列表,用于定义每一类支出或收到的资金或等价物。它用于组织实体的财务,并分离支出、收入、资产和负债,以便让相关方更好地了解实体的财务状况。

每个财务系统都以会计科目表(COA)开始,尽管不同国家的编号可能不同,但我们都有损益表和资产负债表。

会计科目表

Microsoft Dynamics NAV 还有一些其他特殊的科目:标题开始总计结束总计科目。

使用这些账户,你可以使会计科目表更易于阅读。总账科目内的账户会自动缩进。

过账科目

在创建新的过账科目时,有几个选项可以选择。大多数选项不是强制性的,但它们使得在生成条目时更容易推动最终用户使用正确的账户。

让我们通过打开总账科目卡来查看选项:

过账科目

需要做的第一个也是最重要的决定是创建的账户类型。它可以是损益表资产负债表。损益表账户在每个新的财政年度重置为零,而资产负债表账户则无限期地继续。资产负债表账户的总和应始终与损益表账户的总和相匹配。

过账科目

小贴士

你可以创建两个总计账户来检查你的总账余额是否准确。对所有损益表和所有资产负债表进行总计。

您也可以强制一个账户只接受借记或贷记记账。空白行数新页面字段在打印报告时使用,对系统没有影响。对账账户几乎不再使用,除非您不使用子会计。

自动外部文本在您使用此账户在销售或采购文档中时,会自动创建第一章中讨论的额外文本,Microsoft Dynamics NAV 简介

直接记账是一个非常重要的选项。强烈建议在账户用于任何记账设置之一时禁用此选项。当直接记账启用时,最终用户可以在此账户中创建条目,破坏总账和子管理之间的平衡。我们将在本章后面更详细地讨论这一点。

当您允许直接记账时,记账选项卡上的字段非常重要。

通用记账类型确定账户是否用于增值税计算和过滤的采购和/或销售。更详细的增值税规范由增值税业务和产品记账组确定。一般业务和产品记账组可以用来自动填写这些字段,当使用此账户时。

合并选项卡中,您可以填写在合并两个或多个公司时使用的合并账户。我们将在本章后面讨论合并。

Microsoft Dynamics NAV 允许使用额外的报告货币。这是从欧元在欧洲之前的日子继承下来的,在欧元引入的年份非常受欢迎。今天,它被国际公司使用,例如,一家位于美国的荷兰母公司。在报告选项卡中,您可以确定在使用此功能时如何处理汇率调整。

增值税与销售税

Microsoft Dynamics NAV 允许计算欧洲增值税和北美销售税。本书中的示例基于欧洲增值税。

对于销售税,使用税区代码税组代码字段而不是增值税业务记账组和增值税产品记账组。

记录表

如第一章中所述,Microsoft Dynamics NAV 简介,当您记账销售或采购文档时,总账的条目是在总账日记账中创建的。因此,让我们更详细地看看这个功能,看看我们能用它做什么。

子会计

理论上,您可以用总账条目表来运行 Microsoft Dynamics NAV,但在会计中,我们已经发明了子管理。

子管理非常古老。在计算机发明之前,人们会为所有客户和供应商制作卡片以跟踪他们的余额。更新这些卡片是一个手动且耗时的工作,出错的可能性很高。

在 Microsoft Dynamics NAV 中,这会自动处理。在总账中,我们有四个子管理:

  • 银行:对于银行账户上的每笔交易,都会创建一个银行账簿条目。所有银行账簿条目的总和应与您的银行账户余额相符。它允许您快速找到付款。

  • 客户:无论何时向客户销售商品或客户支付客户账簿,都会创建一个条目。它允许您分析支付历史并发出提醒。

  • 供应商:当您使用供应商购买商品时,系统会创建一个供应商账簿条目。我们可以使用供应商账簿条目来确定哪些发票需要支付。供应商账簿条目是客户账簿条目的相反。

  • 增值税/税:这些条目帮助我们轻松创建清晰的增值税/税报表。

如前所述,子管理总额与总账匹配非常重要。例如,当您的银行账户余额为2.846,54时,总账账户也应具有该金额。

子会计

对于此,您可以禁用我们之前讨论的直接过账选项。

与总账工作

当我们打开总账时,我们可以开始进行交易。让我们讨论一下可能性。

与总账工作

总账的一般字段中最重要的字段是过账日期单据编号。这些字段的每种组合的金额总和应该始终平衡。换句话说,任何特定组合的过账日期单据编号的所有日记账条目总和应该始终为零。

我们可以发布到不同的账户类型。当我们直接过账到总账账户时,很明显会发生什么;将为该金额创建一个新的总账条目。当我们选择其他账户类型时,子管理将开始工作。例如,当我们选择客户时,将创建一个客户账簿条目以及一个总账条目。使用的总账账户由过账组决定,我们将在本章后面讨论。

在这里,我们还可以看到通用过账类型一般业务产品过账组以及增值税业务产品过账组返回。这些是从我们之前讨论的总账账户继承的,但您可以选择不同的一个。

增值税选项决定了自动进行的增值税计算。会创建一个包含增值税金额的增值税条目,并创建额外的总账条目

平衡总账有两种方法。我们可以创建两个具有相同借方和贷方金额的条目,或者我们可以使用余额字段。

让我们通过一个例子来看看这些。我们在一个不规则的供应商那里购买了一些东西。我们只有一张带有金额和增值税的小收据,我们希望将其纳入我们的公司。

金额是 440 元,包括 10%的增值税,因此我们想要创建以下交易:

成本 增值税 当前账户
400,00 40,00 440,00

交易也可以在以下屏幕截图中看到:

处理总账日记账

我们可以看到,Microsoft Dynamics NAV 计算了增值税金额,并通过填充余额账户,我们只需要一条记录,这条记录总是平衡的。

当我们导航这个交易时,我们看到我们有三个总账分录和一个增值税分录

处理总账日记账

打开总账分录显示正确的金额。

处理总账日记账

在另一个例子中,我们将通过银行日记账创建客户付款。

分录应用

银行日记账是一种具有特定页面 ID 的总账。这使得应用程序可以根据相同的企业逻辑具有不同的用户界面。银行日记账的一个特定功能是能够轻松地将付款应用于发票。

分录应用

银行日记账不直接发布到总账账户,而是使用其他账户类型。在这种情况下,账户类型客户,而余额账户类型银行账户。而不是一个包含总账账户的列表,账户号字段现在指的是客户,而余额账户号字段指的是银行账户。后者自动从日记账批次定义中填充。

我们将使用应用分录功能来确定这笔付款适用于哪张发票。如果我们没有这样做,系统将不知道哪张发票已付款。

另一个选项是自动应用分录,但当客户决定跳过付款时,系统可能会感到困惑,因此强烈建议手动应用分录。

当我们发布这篇日志并导航条目时,我们看到所有必要的子管理都已更新:

分录应用

发布组

在上一节中,我们讨论了在总账中使用客户编号和银行账户编号作为账户编号。然后系统可以确定使用哪些总账账户编号。但这是如何工作的?

这是通过使用各种发布组矩阵来完成的。大多数发布到总账的应用部分都有自己的发布组表。发布组有两种类型:单层和矩阵层。

单层有直接的总账账户列,而矩阵层有一个额外的设置表:

单层 矩阵层
客户过账组 供应商过账组 库存过账组 项目过账组 银行账户过账组 FA 过账组 通用业务过账组 通用产品过账组 增值税业务过账组 增值税产品过账组 库存过账设置

注意

每个国家使用不同的会计科目表和法规。本书中的总账科目用于 CRONUS 示例数据库。这些在每个国家和实施中可能都不同。

让我们看看客户过账组

过账组

我们看到三个不同的代码及其各自的账户。那么这个代码在哪里使用?让我们打开客户卡片

过账组

开票选项卡上,我们看到客户过账组。所以这就是确定客户总账科目的依据。

我们还在客户卡片上看到了其他过账组。有一个通用业务过账组和一个增值税业务过账组

在我们的列表中,它们是矩阵层。因此,它们并不直接指向总账科目。当我们打开通用业务过账组时,我们看到如下:

过账组

只需一个简单的表格将其连接到默认增值税业务过账组。要查看总账科目在哪里定义,我们需要转到一般过账设置

过账组

我们可以看到,当与通用产品过账组结合时,可以确定总账科目。那么通用产品过账组从哪里来?为了找出答案,我们需要转到项目卡片

过账组

这里我们可以看到相同的选项卡,开票,与产品过账组。

我们的旅程在这里结束,因为我们可以看到最后一个矩阵过账组,库存。当我们打开这个设置时,我们看到它是通过库存过账组位置代码的组合来确定的:

过账组

维度

除了总账和子科目之外,Microsoft Dynamics NAV 允许第三级过账。可以将无限数量的维度附加到每个过账中,并用于交叉分析系统。

注意

使用更多维度会导致在处理交易时数据库活动增加,以及系统设置的复杂性增加。在实施过程中应仔细考虑这一点。

维度起源于旧的项目代码和部门代码功能,允许您合并或区分成本和利润。维度是通过过滤机制确定的。每个主数据记录都可以有维度定义。

让我们看看样本维度代码和值:

维度

维度代码区域有几个维度值。在这里,您也可以有总记录,就像在总账中一样。

当多个主数据记录具有相同的维度代码但值不同时,可以设置优先级。还可以阻止发布维度的组合。

维度是分析数据和构建系统以避免错误输入的有力工具。然而,确定这些组合并维护设置需要大量的时间和特殊技能。

当我们讨论报告可能性时,我们将看到更多关于维度的内容。

预算

Microsoft Dynamics NAV 同样允许进行预算。我们可以创建自己的预算代码。预算代码可以是一年、一个部门,或者是我们想要尝试并最终丢弃的某个预算。

预算可以在总账科目上进行,也可以在任何维度上进行。

预算期间的决策非常重要。如果您想将月度预算与实际数字进行比较,创建年度预算就没有意义了。大多数公司使用月度预算。我们很可能还想为损益表账户创建预算,而不是为资产负债表创建预算。

预算

将预算导入和导出到 Excel 是一个非常重要的功能。在这里,我们可以轻松复制和粘贴,例如,每个月自动有相同的值。

创建预算条目

通过在列中简单地输入新的金额来创建预算条目。在 Microsoft Dynamics NAV 的早期版本中,一个内置机制会根据前一个值和新输入值之间的差异来处理条目的创建。

在 Microsoft Dynamics NAV 2009 中,这从角色定制客户端更改为 C/AL 代码。处理金额的矩阵页面对象是预算矩阵(9203)。此页面使用矩阵管理代码单元(9200)来模拟经典内置算法。

会计期间

虽然大多数公司的会计期间从 1 月 1 日到 12 月 31 日分为月份,但可能会有例外。

这由 Microsoft Dynamics NAV 支持,并在会计期间中设置:

会计期间

只要存在日期算法,我们就可以自由设置自己想要的发布期间。

会计期间

当适当的时候,也应该关闭发布期间。在关闭发布期间时,所有损益表总账科目都设置为零,利润/亏损发布到平衡账户。

会计期间

当我们运行此批处理时,总账会填充发布。不建议在此处进行更改。

截止日期

在关闭损益表后,仍然可以进行交易,但需要特殊发布日期,称为截止日期。当在发布日期前加上C字符时,系统会将其视为特殊交易并允许您发布。

截止日期

当筛选01-01-2013..31-12-2013时,系统将不包括结算日期的条目。筛选01-01-2013..C31-12-201301-01-2013..31-01-2014将包括结算日期的条目。

货币

除了有额外报告货币的可能性外,Microsoft Dynamics NAV 中的每笔交易都可以有自己的货币。交易会根据当前的汇率转换为本地货币LCY)。

处理货币很简单,只要汇率不变化。之后,它可能会变得复杂。汇率可以像你希望的那样频繁变化,但每天最多只能变化一次。在考虑实施每日汇率变化之前,你应该看看后果。

当你更改货币汇率时,系统中的所有内容都会进行调整,这可能导致你的系统中出现大量交易。更改货币汇率需要以下两个步骤:

  1. 输入新值。在我们的例子中,2010 年的新美元汇率是60货币

  2. 实施价值和生成条目。货币

合并

合并意味着将两个或多个公司的总账(部分)合并到一个合并公司中。要在 Microsoft Dynamics NAV 中处理合并,首先必须在 G/L 账户中填充合并账户。这些合并账户必须是合并公司中的有效账户。

合并公司是数据库中的一个“虚拟”公司,它仅存在于合并目的。合并公司为每个合并公司都有一个业务单元。

数据可以通过 XML 或 TXT 格式从数据库中导出。

合并

数据通过合并公司在业务单元列表中导入。

合并

另一个选项是从数据库内部使用导入数据库功能导入。

增值税申报表

大多数公司可以发出增值税申报表以收回支付给供应商的增值税,并支付从客户那里收到的增值税。这是在增值税申报表中完成的。这是一个简单的列表,我们可以根据增值税条目进行筛选。

增值税申报表

每个国家都有自己的增值税申报表,许多国家在这个应用领域有本地化。

数据分析

一些公司因为强制要求而做账,但对生成的信息处理很少,但你可以用系统创建的信息做很多事情。

在大公司中,使用分析工具通常是获得公司资产清晰视图的唯一方法。

账户总表

账户总表是一个报告工具。总账户提供了大量信息,通过应用限制总计(流量过滤器),我们可以缩小这些信息。

账户总表

此示例筛选总账账户编号大于或等于 6000 的记录,并将总计限制在 2014 年和部门 PROD。

小贴士

您可以通过单击页面名称账户表然后另存为视图来保存这些视图。选择一个有意义的名称,如 2010 年生产利润表,便于查找。

账户报表

对于高级报告需求,我们可以使用账户报表。就像增值税申报表一样,它允许我们在这种情况下对总账分录进行筛选。我们可以根据单个总账账户或使用总过滤。如果筛选变得复杂,我们可以对单个行进行求和并隐藏源行。我们还可以将多达四个维度应用于每个账户报表。

账户报表

账户报表还允许您定义列布局。您可以在每个计划中使用多个列布局,并在其他计划中重用列布局

账户报表

列布局可以包含公式和日期筛选器。我们可以按列显示预算或总账分录。

账户报表

注意

有关如何使用账户报表的宝贵信息,请参阅dynamicsnavfinancials.com/

按维度分析

如本章前面所述,Microsoft Dynamics NAV 允许在总账中发布无限数量的维度。为了分析这些信息,我们需要告诉系统要比较什么。这是通过按维度分析来完成的。

每个分析视图都有一个唯一的代码。分析视图可以针对临时需求生成并在之后丢弃,或者永久保留在系统中进行定期报告。分析视图生成冗余信息,这些信息始终可以丢弃并重新生成。

按维度分析

小贴士

建议使用单独系统上的数据库副本来使用分析视图,并在夜间更新它们。

当更新时,分析视图包含分析视图条目中筛选器内的所有数据。如果不妥善维护,这可能是一个包含大量数据的巨大表格。

分析视图的结果可以在矩阵中查看,其中所有值都可以用作行、列和筛选器。

按维度分析

在本例中,我们按区域和销售人员查看销售活动的结果。

设置

财务管理有一个单一的总账设置表,这很重要,因为许多这些设置字段将决定 Microsoft Dynamics NAV 的核心行为。

设置

我们将讨论设置选项,以了解它们的功能,并探索为应用程序创建灵活设置的可能性:

  • 允许过账从允许过账到:这些限制了人们在过账到总账时选择过账日期的自由。强烈建议启用此功能以避免过账日期为 01012090 而不是 01012009。

  • 登记时间:这允许您在用户每次登录和注销时在时间登记簿中创建条目。

  • 本地地址格式本地联系人地址格式:这指的是在本地国家打印地址的方式。在 Microsoft Dynamics NAV 中,对于本地值,最好将国家代码货币代码留空。

  • 发票舍入精度(LCY)发票舍入类型(LCY):这些定义了您的发票舍入是如何计算的。最接近是一种最佳实践,并允许您的客户轻松地在他们的系统中登记您的发票。

  • 允许在过账前删除总账科目:这允许您清理已关闭的财政年度。这个功能几乎很少使用,在使用此功能之前您应该咨询您的合作伙伴。

  • 检查总账科目使用情况:在删除之前,此选项检查总账科目是否在设置表中使用。

  • EMU 货币:这是在欧洲联盟中具有固定兑换率到欧元的货币。在打印报告时,LCY 代码字段用于指示公司的本地货币。

  • 不含增值税的付款折扣:这表明在应用付款折扣时是否计算增值税。当您选中此字段时,您需要考虑调整付款折扣字段,因为这将重新计算增值税。

  • 未实现增值税:只有当您的公司必须处理此问题时才应选中此选项。否则,将导致不必要的过账。这是仅在客户支付发票而不是在开具发票时才有效的增值税。

  • 预付款未实现增值税:只有当您的公司处理未实现增值税并且您想为预付款功能实施此选项时才应选中此选项。

  • 允许的最大增值税差异:此字段确定最大增值税差异金额。大多数情况下,增值税差异不会超过 0.01。

    小贴士

    您可以通过在增值税业务过账组中选择完整增值税来过账增值税差异。

  • 增值税舍入类型:这决定了增值税余数的计算方式。建议使用最接近

  • 账单/销售至增值税计算:这允许您更改增值税业务过账组的来源,无论是账单客户或销售客户以及付款供应商或购买供应商。

  • 打印增值税说明:此字段允许您的发票上的增值税始终以您的本地货币显示。

  • 银行账户号码:这几乎总是由人工确定的数字序列。大多数公司有最多 10 个银行账户。

  • Global Dimensions: 这决定了哪些维度直接记入总账条目和子管理。这些通常用于限制总计,应仔细考虑。

  • Shortcut Dimensions: 当您输入日记账和文档时,这些维度更容易访问。它们可以很容易地稍后切换。

  • Additional Reporting Currency: 这是一个对跨国公司有用的功能。记住,如果汇率发生变化,这需要额外的努力。您可以稍后更改它,但将启动一个批处理作业,如果您有大型数据库,这可能需要很长时间。

  • VAT Exchange Rate Adjustment: 这使得在报告货币汇率变化时重新计算增值税成为可能。在激活之前,请仔细考虑这一点。它很可能会生成难以分析和使用的信息。

  • Appln Rounding Precision: 这可以用来允许在应用不同货币时进行舍入差异。

  • Pmt. Disc. Tolerance Warning: 当此字段被勾选时,每当有差异被记入时,都会出现警告。

  • Pmt. Disc. Tolerance Posting: 这决定了支付容差金额是记入特殊账户还是正常折扣账户。

  • Payment Discount Grace Period: 这可以用来决定当人们支付延迟一两天时,是否仍然扣除折扣金额。

  • Payment Tolerance Warning: 当容差金额记入总账时,此选项将显示警告。

  • Payment Tolerance Posting: 这决定了是否使用特殊总账账户来记入此金额。

  • Payment Tolerance %: 这决定了容差百分比。要更改此值,将使用一个批处理函数来更新开放条目。

  • Max. Payment Tolerance Amount: 此字段设置金额的最大值,因此如果百分比设置为 5%,则开出的 100,000 欧元的发票的容差金额不能超过 5,000 欧元。

定制财务管理

由于财务管理受政府监管,并且标准功能已经非常完整,因此这个应用领域不太可能有很多变化,尽管我们有一些功能更改的例子。

注意

本章中的示例包含在我们在第二章中使用的对象中,一个示例应用

销售行描述到总账条目

当我们记入销售发票时,系统将根据销售行生成总账条目。为了避免创建过多的条目,它们被压缩。这是通过使用缓冲表,即发票记入缓冲来完成的。

销售行描述到总账条目

只有在前面列出的字段的组合下,才会创建一个总账分录记录。正如我们所看到的,描述不是其中之一。这导致总账分录具有销售头部的过账描述,这在会计师查看总账分录时常常令人困惑。

作为例子,我们将生成一张销售发票,其中包含一个总账科目行,销售这些书籍之一。

销售行描述到总账分录

当我们过账这张发票时,我们会得到这些总账分录。注意,描述已经消失了。

销售行描述到总账分录

要改变这种行为,我们必须更改发票过账缓冲表。描述字段需要作为唯一组合的一部分,因为分组是在Sales-Post Codeunit (80)中的UpdInvPostingBuffer函数中使用FIND命令完成的:

UpdInvPostingBuffer()
...
InvPostingBuffer[2] := InvPostingBuffer[1];
IF InvPostingBuffer[2].FIND THEN BEGIN
  InvPostingBuffer[2].Amount := 
    InvPostingBuffer[2].Amount + InvPostingBuffer[1].Amount;
  ...
  InvPostingBuffer[2].MODIFY;
END ELSE
  InvPostingBuffer[1].INSERT;

这需要以下两个步骤:

  1. 我们需要将描述字段添加到表中。销售行描述到总账分录

  2. 我们需要将这个新字段添加到键中。销售行描述到总账分录

    小贴士

    Microsoft Dynamics NAV 中的键只能包含 252 个字节,所以请小心不要向这个表添加太多字段。

当这项工作完成时,需要更改填充缓冲表的方式。这是在Invoice Post. Buffer (49)表本身的PrepareSales函数中完成的:

PrepareSales()
CLEAR(Rec);
Type := SalesLine.Type;
"System-Created Entry" := TRUE;
...
"Job No." := SalesLine."Job No.";
"VAT %" := SalesLine."VAT %";
"VAT Difference" := SalesLine."VAT Difference";
//* Description >>>
Description := SalesLine.Description;
//* Description <<<
IF Type = Type::"Fixed Asset" THEN BEGIN
  ...
END;

我们将要做的最后一个更改是在销售文档的过账程序中。这是我们之前在第二章中讨论的,一个示例应用中的 Sales-Post 代码单元(80):

IF Invoice THEN BEGIN
  // Post sales and VAT to G/L entries from posting buffer
  LineCount := 0;
  IF InvPostingBuffer[1].FIND('+') THEN
    REPEAT
      LineCount := LineCount + 1;
      Window.UPDATE(3,LineCount);

      GenJnlLine.INIT;
      GenJnlLine."Posting Date" := "Posting Date";
      GenJnlLine."Document Date" := "Document Date";
//* Posting Description now from buffer table >>>
//      GenJnlLine.Description := "Posting Description";
      GenJnlLine.Description :=
        InvPostingBuffer[1].Description;
//* Posting Description <<<
      GenJnlLine."Reason Code" := "Reason Code";

我们现在将使用缓冲表中的新字段,而不是销售头部的过账描述。当我们再次过账相同的发票时,这是更改后的结果:

销售行描述到总账分录

这使得阅读总账变得容易得多。

小贴士

如果我们有大额发票并且有不同的描述,这个更改可能会导致我们的系统创建更多的总账分录。在过账程序中创建额外的总账分录需要更多时间,从而导致过账交易运行时间更长,数据库更大。

总账分录中的额外字段

虽然总账分录表有很多信息,但一些公司希望在过账过程中添加额外的字段并填充这些字段。

对于这个例子,我们将使用来自第二章,一个示例应用中的网球场应用数据库。对于这个业务,将网球场编号作为总账分录中的一个字段来分析可能非常有用。

第一步是向总账分录表添加字段,并确保我们有与源表的关系。

总账分录中的额外字段

我们已经了解到总账分录是从总账生成的,因此我们还需要在那里添加这个字段。这可以通过复制和粘贴来完成。

总账分录中的额外字段

最后一步是确保我们将信息从日记账移动到账簿分录表。就像在我们的示例挤压应用程序中一样,这是在通用日记账过账行代码单元(12)中完成的,只是这个代码单元有更多的代码。

我们需要找到创建总账分录的地方,并将我们的字段添加到那里。这是在InitGLEntry函数中完成的,如下所示:

InitGLEntry()
...

GLEntry.INIT;
GLEntry."Posting Date" := GenJnlLine."Posting Date";
GLEntry."Document Date" := GenJnlLine."Document Date";
GLEntry."Document Type" := GenJnlLine."Document Type";
GLEntry."Document No." := GenJnlLine."Document No.";
...
GLEntry."Source Code" := GenJnlLine."Source Code";
//* Squash App. >>>
GLEntry."Squash Court No." := GenJnlLine."Squash Court No.";
//* Squash App. <<<
IF GenJnlLine."Account Type" = ...

在 Microsoft Dynamics NAV 中,要添加一个字段到财务过账过程,这所有都是必需的。当然,除非我们使用它,否则这样做是没有意义的,所以一个合乎逻辑的下一步可能是将这个新字段添加到我们之前示例中的发票过账缓冲表。

这显示了在 Microsoft Dynamics NAV 中结合解决方案是多么容易。

与财务管理集成

虽然它不太可能对财务管理产生重大变化,但在新的过账程序中创建总账分录可能是必要的。

在上一章中,我们简要指出,在 Microsoft Dynamics NAV 过账交易时,实际的日记账行记录永远不会真正插入到数据库中。它们用作过账期间的临时容器来存储数据。执行实际的INSERT操作需要定义日记账模板名称、日记账批次名称和行号,这可能会在数据库中引起锁定。

让我们创建一个新的代码单元,用于创建总账交易。

创建总账交易

在创建代码单元后,我们需要设置两个变量,这是将事物过账到总账的最低要求。

创建总账交易

前面的截图显示了两个变量:

  • GenJnlLine:这是对通用日记账行表(81)的引用。

  • GenJnlPostLine通用日记账过账行代码单元(12)创建总账分录、登记簿和其他财务分录。

C/AL 代码

创建新的总账分录需要一些必填字段。总账日记账行中的所有其他字段要么是基本条目的可选字段,要么在更高级的过账中是必填的,正如我们稍后将了解到的。

我们将首先将此代码写入OnRun触发器,如下所示:

OnRun() 
GenJnlLine.INIT;
GenJnlLine."Posting Date" := WORKDATE;
GenJnlLine.Description := 'Test Entry';
GenJnlLine."Document No." := 'PACKT';
GenJnlLine."Account No." := '6120';
GenJnlLine.Amount := 100;
GenJnlPostLine.RunWithCheck(GenJnlLine);

如果我们执行此 C/AL 代码,我们将收到以下错误消息,这表明我们的交易将导致账户余额不平衡:

C/AL 代码

我们可以通过在相同的OnRun触发器中创建一个-100的平衡交易来修复这个问题,如下所示:

GenJnlLine.INIT;
GenJnlLine."Posting Date" := WORKDATE;
GenJnlLine.Description := 'Test Entry';
GenJnlLine."Document No." := 'PACKT';
GenJnlLine."Account No." := '6120';
GenJnlLine.Amount := -100;
GenJnlPostLine.RunWithCheck(GenJnlLine, TempJnlLineDim);

在执行代码单元后,我们可以导航到我们的文档编号,以查看我们创建的总账分录:

C/AL 代码

这是一个如何与财务管理集成的非常简单的示例;让我们创建一个更高级的示例。

高级条目

我们将创建一个新的带有维度的客户账簿分录。为此,我们应该将我们创建的 C/AL 部分之一更改为以下代码:

GenJnlLine.INIT;
GenJnlLine."Posting Date" := WORKDATE;
GenJnlLine.Description := 'Test Entry';
GenJnlLine."Document No." := 'PACKT2';
GenJnlLine."Account Type" := GenJnlLine."Account Type"::Customer;
GenJnlLine."Account No." := '10000';
GenJnlLine.Amount := 100;
GenJnlPostLine.RunWithCheck(GenJnlLine);

但当我们执行此 C/AL 代码时,我们收到以下错误信息:

高级条目

这意味着我们需要实现维度。让我们将以下 C/AL 代码添加到常规程序中:

...
GenJnlLine.Amount := 100;
GenJnlLine."Dimension Set ID" := 3;
GenJnlPostLine.RunWithCheck(GenJnlLine, TempJnlLineDim);

这将使用维度集条目3,其中包含此交易所需的维度。

小贴士

MSDN 上的这篇文章msdn.microsoft.com/en-us/library/jj552498(v=nav.71).aspx解释了维度集在 Microsoft Dynamics NAV 2013 架构中的应用。

现在,当我们导航到PACKT时,我们看到系统已创建了一个客户账簿条目和一个详细客户账簿条目

高级条目

看,学,爱

在 Microsoft Dynamics NAV 中,有许多如何与财务管理集成的示例。以下是一个创建总账分录的有趣代码单元列表:

  • 销售过账(80)

  • 采购过账(90)

  • 作业计算在制品(1000)

  • 检查管理(367)

  • 销售预付款(442)

  • 库存过账到总账(5802)

  • 服务过账日记账管理(5987)

好吧,进去看看这些代码单元,了解微软是如何进行集成的。

摘要

在本章中,我们探讨了 Microsoft Dynamics NAV 的财务核心。理解条目流程很重要,因为它是设置过账组的方式。定期检查子管理是否与总账平衡是很重要的。

如果系统设置正确,报告的可能性将提供很大的洞察力。在运行系统中更改设置选项时要小心。

在下一章中,我们将探讨这个模块的相反面;关系管理。虽然财务管理系统很严格,但关系管理系统是灵活且可扩展的。

第四章: 关系管理

关系管理软件通常是 ERP 应用所取得的成果。

在早期,每个人在桌子上都有一个名片夹,里面装有电话号码和地址,销售人员总是能记住谁是好客户,哪些客户总是付款晚或利润低。

RM 软件的引入完全改变了这一点,使我们能够在一个地方维护所有公司的联系人,并非常容易地分析销售数据。

关系管理自 Microsoft Dynamics 2.0 版本以来一直是其一部分,并在 3.0 版本中经历了巨大的变化和改进。当前的 Microsoft Dynamics NAV RM 软件与那个版本基本相同,除了与 Microsoft Outlook 的集成在每一版本中都在变化。

在本章中,我们将深入探讨这个非常完整的模块。阅读本章后,你将很好地理解概念以及如何维护主数据和分析交易数据。

我们还将在关系管理部分进行一些应用程序更改。

公司如何运作

在传统的会计软件中,我们将客户和供应商作为业务关系用于发票,但公司有许多我们希望在系统中注册的其他关系。

此外,公司或人员可以与我们公司有多个关系。最好的例子是我与 Microsoft 的关系。像所有人一样,我使用该软件,因此我是客户,无论是在我的商业还是个人生活中。另一方面,Microsoft 雇佣我来教授研讨会和进行演示,这使我成为供应商。作为 MVP,我与他们的关系完全不同。他们给我颁发奖项,邀请我参加特别活动,并允许我访问公司商店。他们还要求我在未来的版本中提供建议,因此对他们来说,我是他们的顾问。

因此,一个人或公司可以在 RM 中扮演不同的角色。Microsoft Dynamics NAV 能够处理所有这些,同时保持单一的数据输入和维护点。

与财务应用不同,RM 具有更大的灵活性。财务应用的功能和规则由政府法规定义,并且公司必须遵守。公司不是被迫使用 RM,但一旦实施,每个人都会理解其好处,并且不愿意放弃它。

联系人

RM 应用的起点是联系人表。这是我们存储我们所知道每个人的地址、电话号码、电子邮件地址等的地方。

当我们打开联系人列表时,我们会看到公司和人员被分组以便于快速浏览。

联系人

截止文本在此截图中的参考并不重要

如我们在前面的章节中学到的,Microsoft Dynamics NAV 中的一个页面基于一个单一的表,这意味着公司和人员必须存储在同一个表中。

当我们打开联系人卡时,我们可以清楚地看到这一点。类型字段表示联系人是一个商业实体还是个人,以及该个人是否属于一家公司。公司编号字段指的是类型为公司的联系人。这是一个一对多关系,意味着如果一个物理人与多家公司有联系,他或她需要为每个有关系的公司维护一个。

以下截图来自 Microsoft Dynamics NAV 中的联系人卡:

联系人

让我们浏览一下选项卡并查看一些重要字段:

  • 编号:这是一个由数字序列确定的唯一键值。公司和个人的编号相同。

  • 类型:这表示此联系人是一个人还是一家公司。

  • 公司名称:当联系人是个人且与公司相关联时,会自动填充该公司的名称。

  • 姓名:这是联系人的姓名。如果联系人是一个人,我们可以点击姓名旁边的AssistEdit按钮来打开姓名详情。姓名会根据我们输入的单词数量自动分解为名、中名和姓。然而,如果我们的联系人有一个更复杂的名字,如“Walter van den Broek”,这在荷兰人中很典型,系统就无法将其分解。联系人

  • 地址:输入联系人的居住街道或办公室所在的街道。

    小贴士

    总是最好在这里输入邮政地址,因为这将用于所有文件。对于访问地址,请使用备用地址功能。

  • 邮政编码 & 城市:这些字段通过邮政编码表连接,如果维护了该表,则可以填充另一个字段,这是一个可选功能。

    注意

    大多数公司为其国家维护邮政编码表,并手动输入外国国家的邮政编码。大多数国家提供邮政编码/城市列表出售或作为网络服务,这将加快数据录入并防止人们输入错误的主数据。

  • 搜索姓名:此字段会自动填充姓名字段,并允许您更快地搜索联系人,因为您可以在引用联系人时输入此字段而不是编号字段。

  • (移动电话)、传真电传号:这是指该公司的电话和传真号码。移动电话字段还允许您与该联系人开始互动。

  • 销售人员:这是此联系人的主要销售人员。如果此联系人晋升为客户,销售人员姓名将打印在订单表和发票上。

  • 称呼代码:这个特殊字段指的是如何称呼此联系人。称呼代码表允许您构建诸如“亲爱的布朗夫人”之类的短语。我们将在后续部分了解更多关于称呼代码的内容。

  • 电子邮件:此字段包含联系人的电子邮件地址。通过按电子邮件按钮 ![联系人],我们可以直接发送电子邮件。

  • 主页:这里是联系人网站的 URL。我们可以通过点击URL按钮 ![联系人] 访问网站。

  • 通信类型:当我们在一个交互中创建 Microsoft Word 文档时使用此字段。它指示我们发送的是副本、电子邮件还是传真。

  • 货币代码和增值税登记号:当此联系人晋升为客户或供应商时,货币代码和增值税登记号将从这里继承。

  • 地区代码:此字段可以在段中用于过滤地理区域。

问候代码

当我们进行邮件合并时,我们希望信件以“亲爱的哈利”或“亲爱的布朗夫人”等开头。这可以通过问候代码来完成。

我们可以创建尽可能多的代码,但一个联系人只能使用一个。这是 CRONUS 演示数据库中的列表。

问候代码

对于公司有一个问候代码,但大多数都是针对个人的。当我们查看已婚女性未婚的公式时,我们会看到这个屏幕:

问候代码

我们可以输入正式和非正式代码。问候可以有最多五个变量指向职位名称名字中间名姓氏首字母公司名称

当我们查看 Karen Friske 的结果时,它将是“亲爱的 Karen 女士”或“嗨,Karen”。

在本章末尾,我们将探讨如何创建额外的问候类型。

替代地址

正如我们本章前面所说的,最佳实践是在联系人表中使用地址字段作为邮政地址,因为这将打印在所有文件上。

联系人替代地址卡表中,我们可以为联系人添加尽可能多的其他地址。

替代地址

小贴士

虽然这些代码与任何事物无关,但最好有一个规则。始终为家庭或办公地址使用相同的代码。我们可以在稍后打印标签或段时使用此规则。

替代地址也可以有一个有效的开始日期和结束日期,以控制当前哪个替代地址是活跃的。

与客户和供应商的关系

在第二章中,一个示例应用,你看到联系人表是客户、供应商和银行账户主数据表的伞状数据。

每个类型的公司联系人都可以晋升为这些表之一。好处是所有地址信息字段都有一个单一的维护位置,并且可以继承。这也允许我们分析销售数据到关系管理,正如我们将在本章后面讨论段时看到的。

当我们创建主数据时,会使用不同的数字序列。在本章末尾,我们将探讨如何在代码中更改这一点。

类型:人员 的联系人无法创建为 客户供应商银行账户

重复项

当输入新联系人时,系统可以搜索重复联系人。在 重复搜索字符串设置 表中,我们可以启用对八个字段的过滤:名称名称 2地址地址 2邮政编码城市电话号码增值税登记号

对于每个字段,我们可以设置在搜索重复项时应使用哪一部分。我们可以使用 第一个最后一个 以及长度,这对于 名称地址城市 字段很有用。使用 第一个 与字段的全长进行搜索将查找精确匹配,这对于 邮政编码电话号码增值税登记号 很有用。

营销设置 表中,我们可以指定应导致警告的匹配标准百分比,如下面的截图所示:

重复项

对于每个联系人,系统将在 Cont. Duplicate Search String table (5086) 中保存这些值。

当我们输入新的联系人时,系统也会生成相同的字符串,并将这些字符串与数据库中的字符串进行比较。当有匹配时,系统将显示一个带有重复联系人的警告。

配置文件

联系人表具有非常有限的数量字段,并且不允许我们添加太多灵活的信息。这就是配置文件被使用的地方。

配置文件允许用户创建无限数量的额外信息源,这些信息源可以手动或自动填充。

让我们看看一个示例配置文件:

配置文件

这个配置文件是为 公司 类型的联系人准备的。它包含 问题答案 行。一个问题可以有一个或多个答案,我们可以定义尽可能多的问题和答案。最后一列显示了有多少联系人具有此配置文件答案。

配置文件是从 联系人卡片 中使用的。

配置文件

当我们点击这个选项时,会打开一个新页面,我们可以选择所需的配置文件并回答问题。

配置文件

答案显示在 联系人卡片 窗口的 子页中。

自动配置文件

配置文件也可以根据公式自动回答。这是通过使用 自动联系人分类 选项和设置 问题详情 来实现的。

自动配置文件

配置文件问题详情 是固定的,并且是硬编码的。它们取决于联系人与客户或供应商之间的关系,如本章前面所述。

自动配置文件

我们不会描述所有可能性,因为在线帮助已经很好地涵盖了这一点。

当问题设置好时,答案应该有一个从值到值,以便系统选择正确的答案。

为了生成答案,使用了一个名为更新联系分类的批处理作业,我们可以根据配置文件进行筛选。

自动配置文件

交互

我们与联系人的交互时刻有很多种。无论是电话、邮件还是发送发票,我们都可以在 Microsoft Dynamics NAV 中注册它们。

与配置文件一样,有一些交互是自动生成的,也有一些是手动生成的。手动交互是通过向导创建的。

所有交互都与一个交互模板代码相关。系统允许我们定义无限量的代码。交互代码还将决定其余向导的行为。

交互

交互可以是入站出站,由我们或他们发起。这些是信息字段。

向导操作字段决定了向导是否会生成邮件合并文档,允许我们附加之前创建的文档,或者什么都不做。邮件合并允许我们创建一个包含联系表所有字段的 Word 文档。

让我们创建一个交互并看看是如何操作的。要创建一个交互,我们在联系卡片或列表中选择创建交互,然后从过程操作中点击创建交互按钮。这将打开以下向导:

交互

第一页询问我们想要启动哪种类型的交互。让我们创建一个备忘录:

交互

这是下一步,因为我们的交互代码定义了我们将生成邮件合并:

交互

我们现在可以在 Microsoft Word 中创建备忘录,所有必要的字段都已经填写完毕。

交互

关闭 Microsoft Word 后,我们继续下一步,当填写所有字段后,我们可以完成向导。这将把交互保存在数据库中,并打印备忘录,因为我们选择硬拷贝作为通信类型

注意

也可能推迟交互并在稍后重新启动。

自动交互

一些交互也可以自动生成,例如,每次我们打印发票或发货单时。

每笔交易使用的交互代码在交互模板设置中定义。对于每次打印,我们希望生成一个交互日志条目,我们需要设置一个代码。

自动交互

小贴士

在打印大量文档时要小心,因为交互日志条目表可能会锁定较长时间,迫使数据库中的其他用户等待直到过程完成。为了避免这种情况,请启用此表上的自动递增,如本博客中所述markbrummel.wordpress.com/2014/05/25/tip-14-autoincrement-interaction-log-entries/

其他自动创建的交互日志条目是由段创建的,我们将在本章后面讨论。

完成的交互

完成后,交互操作将连接到联系人,并可用于分析目的。也可以从交互操作中开始一个待办事项。我们将在下一段中讨论这一点。

待办事项

待办事项是关系管理模型中的最低级别活动。它们最好与微软 Outlook 中的掩码或会议进行比较。

我们可以直接在系统中或从另一个事件中创建待办事项。我们可以从刚刚创建的交互操作中创建一个待办事项。让我们这样做。

待办事项

此处的参考是标签,创建待办事项,因此侧面的截断文本是合适的。

当我们点击交互日志条目中的创建待办事项时,系统会显示一个向导,它将引导我们完成这个过程,就像交互向导一样。

待办事项

待办事项有三种类型,标准(空白)、会议电话。向导中的步骤取决于我们选择哪种类型。让我们选择一个会议

下一步要求参会者提供会议信息,并允许创建邀请模板,然后这将再次创建一个交互日志条目。

待办事项

注意

要执行此步骤,待办事项组织者应有一个有效的电子邮件地址。这可以在销售人员中设置。

下一步只要求提供一个位置,因此我们将点击完成

当我们现在打开来自销售与市场部门的待办事项时,我们可以打开按日查看的销售人员矩阵,它显示了刚刚创建的会议。

待办事项

截断文本不是截图中所引用部分的组成部分。

当我们讨论机会和** Outlook 集成**时,我们将看到更多关于待办事项的内容。

机会

当我们讨论配置文件时,我们已经可以看到关系管理与应用程序的 ERP 部分紧密集成。机会也是如此。

机会使我们能够管理我们从潜在客户那里收到的所有报价请求,创建一个将引导我们达成或失去交易的流程。这然后允许我们分析赢和输的交易,并根据这些信息改变我们的业务。

我们可以分析销售流程,并对我们的未来订单位置做出适当的判断,从而允许我们及时安排产能。

工作流程

我们创建的每个机会都将遵循系统中的销售周期。这将一步一步地引导我们完成这个过程。

让我们看看 CRONUS 数据库中的销售周期。

定义了四个销售周期。最重要的字段是概率计算公式。这将确定系统如何计算具有此代码的所有机会的当前价值。我们可以通过打开销售周期的统计窗口来查看计算当前价值,如下面的截图所示:

工作流程

有四种选择可供选择:乘法加法成功率百分比完成百分比Opportunity Entry (5093)表中的UpdateEstimates函数计算这个:

UpdateEstimates()
IF SalesCycleStage.GET("Sales Cycle Code","Sales Cycle Stage") 
THEN BEGIN
  SalesCycle.GET("Sales Cycle Code");
  CASE SalesCycle."Probability Calculation" OF
    SalesCycle."Probability Calculation"::Multiply:
      BEGIN
        "Probability %" := "Chances of Success %" * 
                      SalesCycleStage."Completed %" / 100;
      END;
    SalesCycle."Probability Calculation"::Add:
      BEGIN
        "Probability %" := ("Chances of Success %" + 
                      SalesCycleStage."Completed %") / 2;
      END;
    SalesCycle."Probability Calculation"::"Chances of Success %":
      BEGIN
        "Probability %" := "Chances of Success %";
      END;
    SalesCycle."Probability Calculation"::"Completed %":
      BEGIN
        "Probability %" := SalesCycleStage."Completed %";
      END;
  END;
  "Completed %" := SalesCycleStage."Completed %";
  "Calcd. Current Value (LCY)" := "Estimated Value (LCY)" * 
                           "Probability %" / 100;
END;

概率计算首先计算一个概率百分比字段,然后这将导致所需的计算当前价值

销售阶段

每个销售周期都有不同的阶段,将引导我们完成销售过程。

销售阶段

机会的当前销售阶段定义了完成百分比字段。我们可以通过允许跳过选项来决定销售阶段是否是强制性的。所需的报价将迫使我们为这个机会分配一个销售报价,正如我们稍后创建机会时将看到的。

活动代码

每个销售阶段都有一个活动代码。这将定义在销售过程中为我们创建哪些待办事项。

活动代码

这是一个非常强大的工具,使销售人员能够为每个销售过程创建一个工作流程。

让我们创建一个机会,看看系统里会发生什么。

创建机会

机会始于选择一个现有联系人或创建一个新的联系人。从联系卡,我们可以导航到相关信息 | 联系 | 机会 | 列表

这将带我们到一个与该联系相关的机会过滤列表。

  1. 在这里,我们可以选择创建机会创建机会

  2. 这将打开一个向导,将引导我们完成这个过程。在第一个窗口中,我们输入描述Sell Chairs并点击下一步进入第二步。创建机会

  3. 在此步骤中,我们选择销售周期代码FIRSTSMALL并选择完成

  4. 选择下一步将允许我们输入更多信息,例如分配销售活动,并激活第一阶段。我们现在将跳过这一步,并在本章后面讨论活动。

    当我们现在打开创建的机会时,信息应该看起来像以下截图。因为我们还没有激活第一阶段,所以没有活动行。

    创建机会

    截止文本不是此截图中的参考部分

  5. 让我们激活第一阶段并看看会发生什么。我们通过导航到操作 | 功能 | 更新来完成。我们进入一个向导,在那里我们选择第一创建机会

  6. 我们将连续点击下一步两次并进入向导的第三步。

  7. 在这一步,我们应该输入估计的销售价值和成功几率(%)以获得这笔交易。这对于计算我们之前讨论的计算估计值非常重要。创建机会

  8. 当我们点击完成时,我们回到机会页面,看到当前值为260,00创建机会

    截止文本不是此截图中的参考部分

    由于这个销售周期的概率计算是添加,公式是:

    "Probability %" := ("Chances of Success %" +  
      SalesCycleStage."Completed %") / 2;
    
    "Calcd. Current Value (LCY)" := "Estimated Value (LCY)" * 
      "Probability %" / 100;
    

    这将导致 (50 + 2) / 2 = 26 和 1000 * 26 / 100 = 260

  9. 现在我们导航到相关信息 | 机会 | 待办事项,我们看到系统为我们创建了两个我们必须完成的待办事项。创建机会

    截止文本不是此截图中的参考部分

  10. 这将帮助我们记住我们的日常任务,并允许管理层看到没有遗忘任何事情。这个销售周期中的下一阶段是资格展示创建机会

  11. 我们可以通过再次进入向导并选择下一步来进入这些阶段。创建机会

  12. 在连续点击下一步两次后,我们进入第三步。由于我们的待办事项之一是验证机会的质量,我们现在可以说例如成功的几率是 80%。创建机会

  13. 我们将选择取消现有打开的待办事项复选框以确保我们的工作流程已更新。您将看到计算当前值已增加到425,00创建机会

  14. 当我们进入下一阶段时,我们会得到以下错误消息,告诉我们分配报价是进入下一步的强制要求:创建机会

销售报价

要将销售报价分配给机会,我们从机会卡导航到操作 | 功能 | 分配销售报价。这将打开一个新的销售报价,其中所有字段都从机会中填充。

销售报价

备注

要将报价分配给没有销售客户编号的联系人,我们需要使用销售客户模板代码。这可以在常规快速选项卡上激活显示更多字段选项时使用。

我们将选择两个家具项目并填写数量行折扣百分比字段。

当我们现在更新机会时,我们可以使用 796,80 的报价金额,这将导致第三步中的计算当前值为 478,08,第四步为 557,76。

备注

要将机会更新到第 4 步,销售人员应该有一个有效的电子邮件地址,这可以在销售人员中设置。

成交

第 5 步是我们示例中使用的销售周期阶段的最后一步。现在我们需要告诉系统这笔交易是赢还是输。为此,我们从机会卡导航到操作 | 功能 | 关闭。我们将选择并点击下一步

成交

在选择了一个有效的原因和销售金额后,我们可以关闭交易。

成交

系统现在为这个联系人创建一个客户,并使用这个编号更新报价。我们需要手动将报价提升为订单。

创建段落

段落允许我们切割和分解系统中的数据,以创建一个筛选后的联系人列表。然后,可以使用这些信息创建一个交互,例如邮寄或启动销售活动。

由于 Microsoft Dynamics NAV 关系管理集成到 ERP 系统中,我们可以对 RM 和 ERP 数据进行筛选。

让我们创建一个新的段落并查看其可能性:

创建段落

段落有一个编号描述字段。编号可以使用数字序列定义。

交互选项卡上,我们选择交互模板代码。我们将选择一个生成 Word 文档的交互,这样我们就可以使用段落的邮件合并功能。

单位成本(LCY)对于确定该段落的总成本非常重要,尤其是在我们与活动一起使用时,正如我们在本章后面将要看到的。

添加联系人

在定义了我们的段落之后,我们现在可以通过导航到操作 | 功能 | 联系人 | 添加联系人来开始通过筛选系统中的联系人信息。

添加联系人

这将打开一个选择窗口,允许我们在应用程序的不同部分进行筛选:

  • 选项:这进一步分为四个类别:

    • 允许现有联系人:如果您运行多个选择并勾选此选项,则每次联系人位于选择中时,系统都会创建新的段落行。

    • 展开公司:当你选择此选项时,系统将为选择中的公司添加相关的人员。

    • 允许相关公司:当选择展开公司时,此选项将删除具有一个或多个人员的公司记录。

    • 忽略排除:联系人可以在段落下被忽略。勾选此标志将忽略此字段。

  • 联系人:在这里,我们可以直接在联系人表的全部字段上进行筛选。例如,所有在荷兰的联系人。

  • 配置文件:这允许我们根据任何配置文件答案进行筛选。当我们使用自动配置文件答案时,例如,我们可以根据特定的营业额或利润值筛选客户。

  • 邮寄组:我们可以将任何段落到邮寄组中保存,以便轻松重复使用之前生成的筛选器。

  • 交互日志条目:我们可以根据特定的交互代码筛选联系人。例如,过去一年内有销售发票的每个人。

  • 工作职责:如果我们想向所有经理发送邮件,我们选择匹配的工作职责代码。

  • 行业组:这允许我们筛选特定行业的公司。

  • 业务关系:默认情况下,用于与客户、供应商和银行账户集成,但也可以通过额外信息进行扩展。

  • 值输入:这可能是最强大的过滤器,我们可以根据相关联系人的特定项目编号和过账日期进行筛选。

精细/减少联系人

在添加了所有荷兰的联系人之后,我们可能想要精细或减少这个列表,这可以通过与添加联系人相同的筛选方式完成。精细会检查分段中的联系人是否匹配特定的筛选标准,而减少则会移除所有匹配标准的分段中的联系人。

我们将减少带有 Waalwijk 城市的分段。

分段标准

我们现在可以通过导航到相关信息 | 分段 | 标准来询问系统我们使用了哪些标准。

分段标准

这允许我们查看我们做了什么,也可以撤销最后一步操作或保存标准。

分段标准

邮寄组

另一个重用分段的方法是将邮寄组应用于分段中的所有联系人。要开始这个操作,我们点击分段卡中的操作选项卡上的应用邮寄组

邮寄组

这将为分段中的每个联系人创建一个联系人邮寄组表记录。

日志分段

当分段完成时,应该进行记录。记录分段将在我们的分段中启动邮件合并过程并创建。

日志分段

备注

使用此选项也将打印此示例中的信件。为了练习,可能需要启用 PDF 打印机或关闭打印机并删除打印作业。

如果需要,系统可以直接生成一个后续分段,如果我们想使用这个分段进行营销活动。

营销活动

大多数有营销部门的大公司都有销售营销活动来提高销售额。这些通常是某些商品对客户更有吸引力的时期。

在 Microsoft Dynamics NAV 中,我们可以管理销售价格,并从成本和利润的角度查看特定营销活动的结果。

让我们打开一个营销活动并查看它包含的信息:

营销活动

每个营销活动都有一个唯一的编号字段,可以使用数字序列和描述来创建。编号字段应仔细选择,因为它将在整个应用程序中使用,该营销活动被使用的地方。

状态代码选项可以自定义定义,但不会影响业务逻辑。开始日期结束日期字段对于定价信息很重要。特殊价格和折扣只在这些期间内有效。

通过发票选项卡,我们可以看到活动与维度集成。这为我们提供了强大的选项,可以为每个活动定义一个维度代码,并创建一个分析视图来分析 Microsoft Dynamics NAV 财务部分的结果,就像我们在上一章中讨论的那样。

定价

Microsoft Dynamics NAV 允许为活动设置特殊项目定价。如果从活动生成销售订单,系统将自动使用特殊价格。

通过导航到相关信息 | 活动 | 销售价格,我们可以输入此活动的定价信息。

定价

这个价格表与我们在第二章中讨论的示例应用中的方式完全相同,一个示例应用

细分市场

为了为活动选择客户或潜在客户,我们需要创建一个或多个细分市场。这些细分市场应通过活动编号字段与活动连接。与这些细分市场相关的人员将获得特定的价格和折扣。

这些细分市场也用于创建此活动的交互日志条目和待办事项。我们需要通过发送信件、传真、电子邮件甚至电话,让我们的目标群体意识到这个活动的存在。

激活

通过激活活动,系统将所有联系人添加到活动组中,并创建交互日志条目。

交互日志条目将用于计算活动的成本。每次交互都有一个特定的成本,所有成本加起来就是活动上的总金额。

当机会来临时,我们可以将其指向特定的活动。这个机会的价值也用于估计活动的成功程度。

活动也将通过附加到活动的维度复制到销售文档中。这使我们能够进一步分析结果。

Outlook 集成

销售人员经常在路上,无法访问 ERP 系统,而 Microsoft Dynamics NAV 没有离线模式。为了解决这个问题,Microsoft Dynamics NAV 与 Microsoft Outlook 集成。这使得销售人员可以离线查看联系人和任务,并在可能的情况下与后台系统同步。

如果销售人员使用带有 Microsoft Outlook 的 Windows Mobile 手机,他们甚至可以在设备上拥有所有 Microsoft Dynamics NAV 信息。

使用用户定义的视图还可以使我们能够将其他 Microsoft Dynamics NAV 数据同步到 Microsoft Outlook,例如,带有余额字段当前值的客户表或带有当前库存的项目表。

我们将在第九章,接口中讨论与 Microsoft Outlook 接口的可能性。

邮件记录

Microsoft Dynamics NAV 还具有读取交换共享文件夹的能力,例如 info@ 邮箱。对于每封电子邮件,系统都可以生成交互日志条目和待办事项。

设置

在实施关系管理之前,我们应该正确设置选项。这可以在营销设置中完成。

设置

让我们来看看所有字段:

  • 附件存储类型:交互日志条目中的附件可以存储在数据库中(嵌入式)或文件系统中(磁盘文件)。强烈建议将它们存储在文件系统中。

  • 附件存储位置:如果我们选择在文件系统中存储附件,这就是我们指定路径的地方。

  • 索引模式:在执行联系人搜索时,应将其设置为自动。这可能在性能上略有不足,并可能导致数据库变得更大。

  • 继承:在输入个人资料时,可以继承所属公司的销售员代码、地区代码、国家/地区代码、语言代码、地址详情和通信详情。

  • 默认值:新联系人可以获取默认销售员代码、地区代码、国家/地区代码、语言代码或通信类型。公司和个人的默认问候代码不同。

  • 默认销售周期代码:每个新的机会都将自动获得此代码。

  • 合并字段语言 ID:这定义了 Word 合并字段是使用本地语言还是英语。

  • 同步:在这里,我们输入客户、供应商和银行账户的默认业务关系代码。

  • 维护重复搜索字符串:如果使用重复联系人功能,请检查此字段。

  • 自动搜索重复项:如果系统在输入新联系人时应自动搜索,请使用此选项。

  • 搜索命中百分比:这决定了从重复搜索字符串设置中匹配的行数百分比,应达到以作为重复联系人的标准。

定制关系管理

RM 是一个完整的模块,通常不会高度定制或垂直化。然而,我们将描述一些可能的更改以及如何集成附加组件,在我们的案例中是 squash 应用程序与关系管理。

本章中的所有示例都是第二章下载的对象的一部分,一个示例应用

问候公式类型

默认情况下,系统有两种问候公式类型,正式和非正式,允许我们打印“亲爱的布朗夫人”,或“亲爱的安吉拉”,但如果我们想打印“致布朗夫人”,怎么办呢?

为了做到这一点,我们首先需要向问候公式表中的问候类型字段添加一个选项。

问候公式类型

支持公式

接下来,我们希望在打印联系封面时使用公式。这使用了来自代码单元 365 的格式地址功能。

此代码单元是 Dynamics NAV 中完成所有地址格式化的唯一点。

联系人的格式化是在ContactAddrAlt函数中完成的。我们应该进行以下更改:

ContactAddrAlt()
...
  ContIdenticalAddress:
    WITH ContAltAddr DO BEGIN
      GET(Cont."Company No.",CompanyAltAddressCode);
      FormatAddr(
        AddrArray,"Company Name","Company Name 2",
        Cont.Name,Address,"Address 2",
        City,"Post Code",County,"Country/Region Code");
    END;
  (Cont.Type=Cont.Type::Person) AND
  (Cont."Company No." <> ''):
    WITH Cont DO
      FormatAddr(
//        AddrArray,ContCompany.Name,ContCompany."Name 2",
//        Name,Address,"Address 2",
        AddrArray,ContCompany.Name,ContCompany."Name 2",
        GetSalutation(5, Cont."Language Code"),Address,
        "Address 2",City,"Post Code",County,
        "Country/Region Code") 

小贴士

在进行更改之前,始终注释掉原始代码行。这将使您始终可以回到标准代码,并在升级此解决方案到新版本时提供帮助。

大多数 NAV 合作伙伴和开发人员都有自己的文档和注释方式。这里的例子是最低注释要求。我们将在第十章应用设计中讨论对象版本控制。

获取问候函数

在我们的修改中,我们使用Contact table (5050)中的GetSalutation函数代替名称字段。让我们看看这个函数并分析它做了什么:

GetSalutation()
IF NOT SalutationFormula.GET("Salutation Code",LanguageCode,
  SalutationType) 
THEN
  ERROR(Text021,LanguageCode,"No.");

SalutationFormula.TESTFIELD(Salutation);

CASE SalutationFormula."Name 1" OF
  SalutationFormula."Name 1"::"Job Title":
    NamePart[1] := "Job Title";
  SalutationFormula."Name 1"::"First Name":
    NamePart[1] := "First Name";
  SalutationFormula."Name 1"::"Middle Name":
    NamePart[1] := "Middle Name";
  SalutationFormula."Name 1"::Surname:
    NamePart[1] := Surname;
  SalutationFormula."Name 1"::Initials:
    NamePart[1] := Initials;
  SalutationFormula."Name 1"::"Company Name":
    NamePart[1] := "Company Name";
END;

CASE SalutationFormula."Name 2" OF
  ...
END;
...
FOR i := 1 TO 5 DO
  IF NamePart[i] = '' THEN BEGIN
    SubStr := '%' + FORMAT(i) + ' ';
    IF STRPOS(SalutationFormula.Salutation,SubStr) > 0 THEN
      SalutationFormula.Salutation :=
        DELSTR(SalutationFormula.Salutation,STRPOS(SalutationFormula.Salutation,SubStr),3);
  END;

EXIT(STRSUBSTNO(SalutationFormula.Salutation,NamePart[1],
  NamePart[2],NamePart[3],NamePart[4],NamePart[5]))

该函数使用两个参数:SalutationTypeLanguageCode。使用这些值和联系人的问候代码,它检查是否存在有效的公式。由于我们只添加了一个新选项,代码仍然有效,因为在数据库级别,选项字段被转换为整数

注意

为了文档目的,我们也可以在这个函数中实现新的选项值。这样做的不利之处在于,我们进行了一个技术上不必要的修改,但需要维护和升级。

根据公式的顺序,必要的名称字段被组合并用作函数的返回值。

设置问候公式

如果我们要使用我们新的问候公式,我们首先需要设置它。我们将为 F-MAR 进行此操作,以使用 CT100191 Megan Sherman 从 American Wood Exports 进行测试。

设置问候公式

测试解决方案

在添加新公式后,我们将使用报告操作中的联系封面选项从联系卡打印封面。结果将如下所示:

测试解决方案

客户和供应商编号

最终用户的一个常见要求是在从联系信息创建客户或供应商时保持相同的编号。

这可以通过在联系表中的CreateCustomer函数中添加一行代码来完成:

CreateCustomer()
...

CLEAR(Cust);
Cust.SetInsertFromContact(TRUE);
//* Maintain Contact No. >>>
Cust."No." := "No.";
//* Maintain Contact No. <<<
Cust.INSERT(TRUE);
Cust.SetInsertFromContact(FALSE);

这之所以有效,是因为通过填充“编号”字段,OnInsert触发器中的数字序列功能不会启动:

OnInsert()
IF "No." = '' THEN BEGIN
  SalesSetup.GET;
  SalesSetup.TESTFIELD("Customer Nos.");
  NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.",
    xRec."No. Series",0D,"No.","No. Series");
END;
...

禁用直接创建客户和供应商

当使用此选项时,应禁用直接创建客户或供应商。这可以通过从销售与应收账款设置采购与应付账款设置中移除编号序列来实现。这将在创建客户或供应商时导致运行时错误消息。

在公司间共享联系信息

当更多公司在 Microsoft Dynamics NAV 中进行管理时,它们通常拥有相同的所有者或所有者群体,他们希望他们的联系数据跨越他们的公司。

这可以通过在所有公司之间共享一些表并更改一些业务逻辑来实现。

共享表

默认情况下,Microsoft Dynamics NAV 将为每个公司创建每个表的单独实例。这可以通过表设计器中的DataPerCompany属性来更改。

共享表

以下列表应跨数据库共享,因为它们包含主要联系人信息和客户与供应商数据的链接:

  • 5050 - 联系人

  • 5051 - 联系人备用地址

  • 5052 - 联系人备用地址日期范围

  • 5053 - 业务关系

  • 5054 - 联系人业务关系

这将允许我们在所有公司中重用联系人数据。其他表可以共享,但可能有用。

通过共享联系人配置文件答案表,其他公司可以了解客户在集团中的表现。

分段表可以共享,以便在公司内切片和切块信息。这也需要共享标准表。

小贴士

当你共享配置文件或分段表时,计算它们的报告应在数据库中为每个公司单独启动。

营销活动和机会不应共享,因为它们与 ERP 系统接口。永远不要共享财务表,如价值录入或文档表。

交互日志条目可以共享,但我们应该意识到,当我们处于错误的公司时,大多数与销售和采购文档相关的表关系将不起作用。

业务关系

当在各个公司之间共享联系人时,我们感兴趣的是看到哪些公司在哪些公司中是客户和供应商。我们还想在联系人信息更改时维护这些表。

这意味着除了共享联系人业务关系表外,我们还应该添加一个表示公司的字段,并将此字段添加到主键中。

业务关系

C/AL 代码修改

要使此自定义功能正常工作,我们需要 C/AL 代码理解我们想要做什么。它需要理解我们添加了公司。让我们查看所有需要更改以使此功能正常工作的函数。

我们在第二章一个示例应用中看到的创建客户和供应商记录的功能也应该进行检查,例如,联系人表中的CreateCustomer函数。

CreateCustomer()
...

ContBusRel.RESET;
ContBusRel.SETRANGE("Contact No.","No.");
ContBusRel.SETRANGE("Link to Table",ContBusRel."Link to Table"::Customer);
//* Company Sharing >>>
ContBusRel.SETRANGE(Company, COMPANYNAME);
//* Company Sharing <<<
IF ContBusRel.FIND('-') THEN
  ERROR(
    Text019,

...

在 C/AL 代码的稍上方:

ContBusRel."Contact No." := ContComp."No.";
ContBusRel."Business Relation Code" := RMSetup."Bus. Rel. Code for Customers";
ContBusRel."Link to Table" := ContBusRel."Link to Table"::Customer;
//* Company Sharing >>>
ContBusRel.Company := COMPANYNAME;
//* Company Sharing <<<
ContBusRel."No." := Cust."No.";
ContBusRel.INSERT(TRUE); 

我们还应该检查维护数据完整性的代码,即我们在第二章一个示例应用中讨论的CustVendBank-Update Codeunit (5055)

UpdateCustomer()
WITH Cust DO BEGIN
//* Company Sharing >>>
 CHANGECOMPANY(COMPANYNAME);
//* Company Sharing <<<
  GET(ContBusRel."No.");
  ...
END; 

在这里,我们使用CHANGECOMPANY C/AL 命令来更改特定变量的公司。

注意

还有更多受影响的函数,例如联系表中的UpdateQuotes函数。在实施此功能之前,请分析您的数据库。

编号系列

对于一个正常工作的系统,我们最后应该做的更改是创建编号系列功能的新实例。

由于编号系列是一个独立的对象集合,因此这可以相对容易地实现。

在对象设计器中,我们应该过滤这一组对象,并将它们导出到一个.txt文件。

  • 表格(308):编号系列

  • 表格(309):编号系列行

  • 表格(310):编号系列关系

  • 报告(21):编号系列

  • 报告(22):编号系列检查

  • 代码单元(396):NoSeriesManagement

  • 页面(456):编号系列

  • 页面(457):编号系列行

  • 页面(458):编号系列关系

在这个文件中,我们可以重新编号并重命名它们,以便我们得到如下内容:

  • 表格(123456721):编号系列(共享)

  • 表格(123456722):编号系列行(共享)

  • 表格(123456723):编号系列(共享)

  • 报告(123456721):编号系列(共享)

  • 报告(123456722):编号系列检查(共享)

  • 代码单元(123456721):NoSeriesManagement(共享)

  • 页面(123456721):编号系列(共享)

  • 页面(123456722):编号系列行(共享)

  • 页面(123456723):编号系列关系(共享)

其中,表格应该是DataPerCompany编号。

最终步骤

当我们有了共享编号系列功能时,我们可以在现有的对象中实现这一点。

  1. 在营销设置表中,联系号码字段应将表格关系更改为共享编号系列表以及联系表中的编号系列字段。

  2. 联系表中的NoSeriesMgt变量应从NoSeriesManagement移动到SharedNoSeriesMgt

替代方法

在公司之间共享联系信息是许多公司已经实施过的更改,并且可以被认为是安全的。由于财务或运营信息,Microsoft Dynamics NAV 中的其他表格更难以共享。

在标准应用程序中,一个典型的例子是项目表(27)。这个表包含一个字段Cost is Adjusted (29),当运行成本调整时使用。如果这个表格将在所有公司之间共享,将会在运行此功能时产生一个主要问题。我们将在第五章 生产 中讨论成本调整。

对于这个问题,有两个常见的解决方案:

  • 共享主项目:我们可以创建一个新的表格,称为主项目。这个表格在所有公司之间共享,包含我们共享的信息,如描述和定价。当这个表格中的数据发生变化时,应该启用一个类似于CustVendBank-Update Codeunit (5055)的机制,该机制使用CHANGECOMPANY C/AL 函数更新其他公司的项目。

  • 外部同步:我们可以实现一些将一个公司中做的更改导出到 XML 文件的功能。一个应用程序服务器可以在后台运行并读取此 xml 文件,并将这些更改应用到数据库中的其他公司或甚至其他数据库。

第一个使用主项目的解决方案很像标准应用程序中联系人的工作方式,是观察、学习和在定制解决方案中使用经过验证的数据结构的完美示例。

添加联系人到段

我们在关系管理中实施的最后一个更改是在段中添加到添加联系人功能的一个表。

我们已经看到它已经完整,但一个垂直解决方案可能希望在这里集成其账簿条目表。

对于这个例子,我们将使它能够从第二章的示例应用程序中筛选压榨账簿条目,一个示例应用程序

扩展报告

第一步是将压榨账簿条目作为DataItem添加到添加联系人报告(5198)。我们将从值条目复制功能,因为这具有相似的功能。

小贴士

总是寻找可比较的标准应用程序功能来学习。永远不要只是复制粘贴,而是学习它是如何完成的,并应用你自己的知识。

扩展报告

我们不能从其他联系人业务关系DataItem复制粘贴表关系,因为压榨球员是联系人,而不是公司。我们的表关系应该是Contact No.=FIELD(No.)

我们在联系人业务关系表中的代码告诉我们,我们需要两个新的布尔类型变量,SquashFiltersSkipSquashLedgerEntry

ContactBusinessRelation2 - OnPreDataItem()
IF ContactOK AND ((GETFILTERS<>'') OR SquashFilters) THEN
  ContactOK := FALSE
ELSE
  CurrReport.BREAK;

ContactBusinessRelation2 - OnAfterGetRecord()
SkipSquashLedgerEntry := FALSE;
IF NOT SquashFilters THEN BEGIN
  ContactOK := TRUE;
  SkipSquashLedgerEntry := TRUE;
  CurrReport.BREAK;
END;

SquashFiltersOnPreReport触发器中确定:

Report - OnPreReport()
ItemFilters := "Value Entry".HASFILTER;

//* Squash >>>
SquashFilters := "Squash Ledger Entry".HASFILTER;
//* Squash <<<
...

压榨账簿条目DataItem中的代码应如下所示:

Squash Ledger Entry - OnPreDataItem()
IF SkipSquashLedgerEntry THEN
  CurrReport.BREAK;

CASE ContactBusinessRelation2."Link to Table" OF
  ContactBusinessRelation2."Link to Table"::"Squash Player": 
  BEGIN
    SETRANGE("Squash Player No.",
      ContactBusinessRelation2."No.");
  END;
  ELSE
    CurrReport.BREAK;
END;
Squash Ledger Entry - OnAfterGetRecord()
ContactOK := TRUE;

IF ContactOK THEN
  CurrReport.BREAK;

确保我们根据我们的联系人业务关系实例进行筛选,并且根据我们与压榨球员表的链接进行筛选。

ContactOK表示所有与该压榨账簿条目相连的联系人将被插入。

实施标准筛选器

为了支持标准筛选器功能,我们需要对添加联系人报告和SegCriteriaManagement代码单元进行两个更改。

添加联系人报告中,我们在OnPreReport触发器中添加此 C/AL 代码。这将调用SegCriteriaManagement 代码单元(5062)

OnPreReport()
...
SegCriteriaManagement.InsertCriteriaFilter(
  "Segment Header".GETFILTER("No."),DATABASE::"Value Entry",
  "Value Entry".GETFILTERS,"Value Entry".GETVIEW(FALSE));
//* Squash >>>
SegCriteriaManagement.InsertCriteriaFilter(
  "Segment Header".GETFILTER("No."),
    DATABASE::"Squash Ledger Entry",
    "Squash Ledger Entry".GETFILTERS,
    "Squash Ledger Entry".GETVIEW(FALSE));
//* Squash <<<

SegCriteriaManagement代码单元中,我们在SegCriteriaFilter函数中添加此代码,这将需要一个新局部变量Squash Ledger Entry

SegCriteriaFilter()
...

CASE TableNo OF
   ...
//* Squash Ledger Entry >>>
  DATABASE::"Squash Ledger Entry":
    BEGIN
      SquashLedgEntry.SETVIEW(View);
      EXIT(SquashLedgEntry.GETFILTERS);
    END;
//* Squash Ledger Entry <<<
END;

测试解决方案

现在,我们可以通过尝试将所有类型的成员压榨球员添加到来测试解决方案:

测试解决方案

结果是一个包含所需压榨球员的段。

测试解决方案

注意

此更改也需要实施到减少/细化功能,该功能与添加联系人报告类似。

摘要

在本章中,我们深入探讨了微软 Dynamics NAV 的关系管理功能。我们学习了它是如何与系统的 ERP 部分集成的。关系管理对于分析销售数据非常有用。通过配置文件,我们可以根据营业额和利润数字进行筛选,并在各个部分中使用它们。

交互日志条目使我们能够跟踪与我们的商业伙伴的所有接触时刻。Outlook 集成可用于销售人员远程工作并与系统同步。

营销活动和机会帮助我们跟踪报价过程,并使我们的销售工作更加高效。

最后,我们查看了一些将关系管理系统更改以满足我们公司特定需求的常见要求。

在接下来的章节中,我们将从第五章“生产”和第六章“贸易”开始,探讨微软 Dynamics NAV 的 ERP 部分。

第五章。生产

之前的章节介绍了 Microsoft Dynamics NAV 的关键概念以及财务应用程序和 CRM 的详细信息。这些横向模块可以在大多数行业中实施,而无需进行大的结构性改变。

在本章中,我们将讨论三种在 Microsoft Dynamics NAV 中使用标准功能和定制功能实现生产的方法。

我们将讨论项目跟踪和项目成本,以及使用哪些程序和对象来确保应用程序中正确运行。对于制造,我们将讨论一般概念和数据模型,而不是深入到每个功能的细节。

我们还将讨论配套,这在有限的几个国家如北美、法国和澳大利亚可用,但很可能会在未来版本中转移到全球版本。

在本章结束时,我们将探讨五个不同的垂直行业,并突出这些行业中两个特定功能,这些功能在标准产品中尚未实现,并讨论如何解决这些问题。

阅读本章后,您将更好地理解 Microsoft Dynamics NAV 中生产的概念,以及这些概念如何与应用程序的其他部分相匹配,以及如果它不符合您的流程时,如何跳出思维定势。

什么是生产?

生产是使用原材料或预制件和资源创建新产品的过程。

我们今天所了解的生产方式在工业革命前几个世纪就已经开始,工匠和助手使用自然界和农民生产的原材料来创造产品。今天,这种方法的生产仍然存在于许多奢侈品中,如定制家具或服装。

工业革命将生产转变为制造业,随着机器和大规模生产的引入,这使得生产不再那么依赖于工匠和体力劳动。

什么是生产?

计算机在制造公司的引入使得生产更复杂的物品成为可能,并使制造更加灵活。

要为生产公司实施 Microsoft Dynamics NAV,了解贵公司正在使用哪种生产级别至关重要。

生产方法

在本章中,我们将区分以下三种生产方法:

  • 组装生产:当生产将项目组合成一个新的项目,而不改变项目或产生废料时,我们将称之为组装。

  • 制造生产:这是在 Microsoft Dynamics NAV 中要实现的最复杂的生产方法。原材料被组合成一种或多种产品,并产生废料。

  • 专业生产:这些通常是单件或小批量生产的物品。系统应支持生产过程的基本原则,但还应足够灵活以适应公司。

原材料

我们消费的每一件产品最初都是原材料,如棉花、铁矿石、木材和石油,然后经过加工用于生产过程。其他原材料是水和空气或水果和蔬菜。所有原材料都是由大自然生产的。某些原材料(如木材、棉花、水果和蔬菜)的生产可以受到人类的影响。其他原材料更为有限,如铁矿石、石油和水。

基本生产原则

在进入 Microsoft Dynamics NAV 之前,我们将讨论一些重要的术语,这些术语对于理解 ERP 中的生产概念至关重要。

物料清单

物料清单定义了组装或制造一个项目所需的组件。物料清单中的组件也是项目,因此在创建新的物料清单之前,系统中必须创建所有组件项目。

注意

在 Microsoft Dynamics NAV 中,有两个独立的物料清单定义,一个用于组装,另一个用于制造。

物料需求计划

物料需求计划MRP)于 20 世纪 60 年代作为一种生产排程的计算方法被引入,并很快被制造资源计划MRP II)所取代。

虽然 ERP 取代了 MRP,但 MRP 仍然是 ERP 应用的关键部分。

Microsoft Dynamics NAV 内置了 MRP 算法,但也允许开发者使用内置数据模型创建自己的算法。MRP 分析相关需求,即来自组件生产订单的需求。

输入垃圾,输出垃圾

运行 MRP 算法的最大风险是输入垃圾,输出垃圾GIGO)原则。为了做好计划,系统中的数据必须绝对正确,否则计划将包含错误。

例如,如果销售订单中的交货日期没有正确输入,规划算法就没有机会给出正确的结果。输入的垃圾(错误的日期)将导致输出的垃圾(错误的计划)。

主生产计划

主生产计划MPS)是用于生产计划和排程的术语。MPS 用于决策,连接供需。它分析独立需求,即来自销售订单、服务订单和生产预测的需求。

项目成本核算

对于制造公司来说,能够计算真实的项目成本和盈利能力至关重要。项目的成本包括其构成的所有组件的成本,以及任何使用的机器的生产时间和成本。

在生产公司中,在项目甚至可以制造和销售之前,就会产生高昂的成本。需要购买和安装机器,并且可能需要建造新的制造工厂。

项目跟踪

项目跟踪是一个相对较新的概念,它是由于需要能够追溯供应链中项目的原始生产批次而产生的。当某个特定项目出现问题时,了解同一批次生产的其他项目是否也存在同样的问题,甚至可能需要召回所有项目,是非常有趣的。

质量控制

在生产过程中,尤其是在结束时,质量控制是一个关键阶段。项目可能被完全拒绝,或者可能需要额外的处理。

在质量控制中,会对项目进行检查以发现错误。这种检查的方式取决于生产过程。在汽车行业中,所有汽车都是单独检查的,而在化工行业中,会取出批次的一部分进行检查,假设其余批次的质量相同。

质量控制始终在生产过程的最后,但也可能在每个主要生产过程之间。有时,制造的项目取决于质量控制的成果。在这种情况下,每个质量级别都由一个特殊的物品编号来表示。

能源和废物

在制造项目时,明显的组件是材料清单中的项目。随着我们的环境变得越来越脆弱,在这个过程中使用更少的能源和产生更少的废料变得越来越至关重要。随着回收利用变得越来越重要,这些组件对生产成本和计划的压力也越来越大。

运营管理协会

要了解更多关于生产的信息,研究运营管理协会APICS)提供的资料是很有趣的。APICS 是一个在全球范围内被公认为制造标准领先权威的组织,类似于 W3C 被认为是 XML 标准的权威。

注意

更多关于 APICS 的信息可以在 www.apics.org/ 找到。

入门

让我们通过两个脚本来生成一个新的项目,并带有材料清单:一个用于组装过程,另一个用于制造。

我们将为这些产品设置项目成本和项目跟踪。

注意

这些示例是使用未经更改的 CRONUS W1 Microsoft Dynamics NAV 2013 Release 2 数据库创建的。

组装

在我们公司,我们希望开始生产办公椅。这些椅子由五个轮子、一个底座、一个座椅和两个扶手组成。我们将创建这四个组件作为新的项目,并为最终产品创建一个新项目。

所有项目都将采用不同的成本计算方法,以展示成本变化的影响。最终产品将支持带有一年保修期的序列号项目跟踪。

设计模式

在进入应用之前,我们将看看如何在 Microsoft Dynamics NAV 中解决这个问题。以下图表说明了这个过程:

设计模式

它从在数据库中创建组件和最终产品作为项目,并使用BOM 组件表连接它们开始。BOM 组件还可以包含资源

如果组件有库存,我们可以使用组装文档来创建产品。在过帐组装文档时,组件被消耗,产品被创建。在这个过程中,系统将创建资源账本条目项目账本条目价值条目

价值条目可以通过库存过帐到总账程序过帐到总账,无论是手动还是实时。这完成了整个过程。

项目

对于这个例子,我们需要创建五个项目,四个组件和一个最终产品。我们将为组件分配一个估计的单位成本,为最终产品分配一个单位价格,如下表所示:

编号 描述 基本单位 单位成本 单位价格 成本方法
第五章-C1 第五章 轮子 PCS 5
第五章-C2 第五章 底座 PCS 60
第五章-C3 第五章 座位 PCS 120
第五章-C4 第五章 车载扶手 PCS 35
第五章-P1 第五章 办公椅 PCS 500

注意

在实际实施中,我们永远不会为每个项目设置具有如此多不同成本方法的材料清单。这完全是出于解释每个成本方法做什么以及 Microsoft Dynamics NAV 在技术上能够处理这种情况的目的。

项目成本

项目成本确定项目成本的计算方法。我们将为每个项目分配不同的成本方法。让我们简要讨论在 Microsoft Dynamics NAV 中可用的成本方法:

  • 先进先出(FIFO):先入先出。使用最旧项目账本条目的成本。

  • 后进先出(LIFO):后入先出。使用最新项目账本条目的成本。

    小贴士

    当使用先进先出(FIFO)或后进先出(LIFO)时,如果使用批号进行项目跟踪,成本将在批号内应用。也就是说,与特定批号相关的成本将被使用。

  • 平均:每次我们购买项目时,总成本除以总数量。结果用作单位成本。

  • 标准:用户将手动定义单位成本。在开票时,所有采购价格的偏差都作为利润或损失过帐。

  • 特定:这始终与项目跟踪和序列号结合使用。每个序列号使用其独特的单位成本。

    注意

    成本方法与仓库拣选方法无关,但仅适用于财务成本计算。

项目跟踪

我们的所有椅子都将获得一个带有一年保修期的序列号。这使得我们能够在椅子出现问题时追踪所有单个椅子返回工厂的情况。

项目跟踪

在 Microsoft Dynamics NAV 中,可以对一组物品的个别序列号和批号进行项目跟踪。

序列号批号是项目日记账条目表(32)中的字段。这将导致每个序列号或批号在表中创建一个单独的记录。当使用序列号时,这可能导致表大小的巨大增加。

批号和项目号在文档录入过程中保存在预订条目表(337)中。预订条目可以分配给 Microsoft Dynamics NAV 中的任何表,例如销售行、项目日记账行或生产订单。

当过账文档并创建项目日记账条目时,预订条目被移除,并替换为具有与对应项目日记账条目相同的条目号字段的跟踪规范记录。

注意

用于项目跟踪的预订条目不应与销售和采购过程中的正常预订条目混淆。

Microsoft Dynamics NAV 中项目跟踪的过程如图所示:

项目跟踪

从技术上讲,Microsoft Dynamics NAV 中的项目跟踪非常复杂,并且只有在仔细分析后,经验丰富的开发者才应更改。

我们将在第六章贸易中更详细地讨论预订过程。

物料清单

当创建项目时,定义了成本计算方法并设置了项目跟踪。下一步是为办公椅创建物料清单。这可以通过在项目清单中的物料清单选项或卡片页面中的装配清单来完成。

物料清单定义了将用于创建一个新最终产品的组件项目和资源。

物料清单

我们使用之前讨论过的组件,包括五个轮子、一个底座、座椅和两个扶手。

物料清单

计算标准成本

我们为物料清单选择的组件具有单位成本。这些项目共同决定了我们最终产品的单位成本。

我们可以通过在用于物料清单的相同计算标准成本菜单中选择Calc. Standard Cost来计算办公椅的标准成本。

注意

计算选项中的Calc. Standard Cost名称不应与成本计算方法混淆。此功能应适用于所有成本计算方法。

计算标准成本

标准成本现在是通过组件的单位成本、间接费用率和间接成本百分比来计算的。我们将在示例中不使用后者。

((5*5) + (1*60) + (1*120) + (2*35)) = 275

创建库存

在我们能够组装椅子之前,我们需要购买组件。为此,我们将创建一个包含八个采购条目的采购订单。我们将以系统中的单位成本不同的价格购买组件,这样我们可以展示成本方法的影响。

创建库存

采购订单将被接收并开票。

注意

如果只收到采购订单而没有开票,示例可能不起作用,因为在 CRONUS 数据库中未激活预期成本过账到总账。

调整成本项目条目

我们刚刚创建、接收和开票的采购订单的单位成本与我们最初在项目中设置的单位成本不同。

根据项目的成本方法,这将对单位成本产生影响。当我们查看我们创建的项目的新单位成本时,我们可以看到这受到了采购订单的影响。然而,这些值是不正确的。系统只采用单位成本的第一次更改。

调整成本项目条目

为了纠正这一点,我们需要运行调整成本项目条目(报告 795)批量作业。这将根据成本方法确定新的单位成本。

调整成本项目条目

在标准成本未受交易影响的情况下,FIFO、LIFO 和平均的单位成本已经重新计算。

注意

调整成本项目条目报告应定期在您的数据库中运行。即使数据库设置为使用预期成本过账和自动成本过账。

将库存成本过账到总账

Microsoft Dynamics NAV 支持将库存成本过账到总账。这使得会计师能够有一个单一的数据分析点,而不是打印库存报告并手动使用数字编制管理报告。

这可以通过使用将库存成本过账到总账功能(报告 1002)来完成,如下面的截图所示:

将库存成本过账到总账

检查,检查,再检查

要检查价值条目和总账是否同步,我们可以运行库存评估(报告 1001)。这将显示库存价值与过账到总账的金额。

检查,检查,再检查

重新计算标准单位成本

我们为办公椅计算的标准单位成本是 275。这是基于我们的购买价格假设。现在我们已经真正购买了并收到了组件,我们可以根据实际价格计算新的单位成本。

在这个例子中,价格仍然是275,因为所有购买项目的总价是550。库存使我们能够用这些材料制作两把椅子:

(550 / 2) = 275

注意

使用这种计算方法,可以检查计算单位成本算法的结果。

汇总订单

现在我们有了库存的组件和正确的单位成本,我们可以创建一把椅子。我们将使用装配订单来完成这项工作。

在装配订单中,我们需要为每个我们想要组装的项目创建一行。组件在过帐订单时自动使用。我们将使用采购代理角色中心(9007)来完成这项工作。

装配订单

装配订单

在为办公椅创建文档并尝试过帐文档时,我们将收到一个错误消息,因为我们首先需要指定序列号。

装配订单

指定序列号

序列号和批号可以使用项目跟踪行选项设置。这会打开项目跟踪行页面(6510)。此页面能够在注册过程中的预留条目以及如果项目日记账条目已经创建,还可以显示跟踪规范。

我们将手动创建一个新的序列号。Microsoft Dynamics NAV 还支持系统生成的序列号。

装配订单

我们现在可以过帐这个装配订单,并将有一把带有序列号的办公椅在库存中。

再次检查成本

创建办公椅改变了组件项目的库存,因此可能会影响我们项目的成本计算。然而,当我们现在检查我们的项目时,单位成本并没有改变。

检查成本(再次)

即使如此,根据当前的库存,单位成本可能不同。记住我们使用了 5 个轮子,使用 FIFO 成本为 4,以及一个座椅使用 LIFO 成本为 70。

使用调整成本条目过帐到总分类帐选项来运行:

检查成本(再次)

单位成本已更改,现在显示我们使用了前五个轮子使用 FIFO,留下库存中的其他五个轮子,价值为5。我们使用了最后一个座椅使用 LIFO,留下库存中的第一个座椅,价值为70

当我们运行库存评估时,我们可以看到生产第一把椅子的实际成本是267,50,但我们过帐的是275

检查成本(再次)

再次重新计算单位成本

当我们为我们的办公椅运行计算单位成本时,我们可以看到新的成本将是282,50

(5*5) + (1*70) + (1* 117,50) + (2*35) = 282,50

与价值为267,50的第一把椅子一起,我们匹配了价值为 550 的采购发票。

标准成本工作表

我们需要纠正第一把椅子的成本,它目前在库存中,以获得正确的库存价值。我们可以使用标准成本工作表来完成这项工作,如下面的截图所示:

标准成本工作表

此工作表允许我们通过在选择实施标准成本更改选项时在项目重估日记账中创建条目来纠正旧价值条目。这将创建一个新的价值条目,并带有变化值以跟踪变化。

标准成本工作表

项目重估日记账

最后一步是过账项目重估日记账并运行过账库存成本到总账程序。

项目重估日记账

注意

我们还需要运行计算标准成本,因为办公室椅子批次将重置单位成本为267,50

结果

当我们现在运行库存评估报告时,我们可以看到库存中的办公室椅子价值为267,50,剩余库存为282,50

结果

概述 10 个步骤的项目成本计算

我们在示例中执行的所有步骤都可以总结在这个 10 步骤流程图中:

概述 10 个步骤的项目成本计算

让我们简要看看步骤:

  1. 我们首先创建新项目并设置成本计算方法。

  2. 然后我们为生产项目创建物料清单。

  3. 运行计算标准成本程序以获取生产项目的单位成本。

  4. 购买生产所需的项目。

  5. 运行调整成本项目条目程序。

  6. 使用过账库存成本到总账程序将价值条目与总账条目同步。

  7. 如有需要,重新计算标准成本。

  8. 创建一个组装订单并过账文档。

  9. 运行标准成本工作表以更改标准成本。

  10. 运行项目重估日记账以实施旧值条目的标准成本。

制造

组装管理模块在 2013 年版本的 Microsoft Dynamics NAV 产品中添加,并取代了自 1995 年 Windows 版本引入以来存在的 BOM 日记账。两者都使我们能够从其他项目创建新项目。

在 2.01 版本中,Navision 引入了 Navision 制造的第一版。这是一个与 Navision 财务分开的产品,由于其复杂性,仅对认证合作伙伴可用。

在 3.00 版本中,制造成为标准 Navision attain 包的一部分,对所有合作伙伴可用。制造为 Microsoft Dynamics NAV 用户提供了比仅从物料清单创建项目更多的功能。生产订单可以使用工作中心、机床、工具和产能日历来安排。

在计划运行中,可以使用制造至库存或制造至订单策略来安排项目的生产。

系统计算所需的 BOM 组件,并在库存不足的情况下使用复杂的需求和供应过程创建采购订单。如果我们将其放入设计模式中,它看起来像这样:

制造

生产订单是流程的中心,由具有生产 BOM的项目创建,这些项目要么在销售订单中,要么库存不足。

计划运行填充计划工作表,该工作表基于请购单行表(246)。计划工作表可用于创建生产订单和采购订单。

在生产过程中,消耗日记用于记录从生产 BOM中使用的子项,而输出日记在完成时创建新项目。或者,这些步骤可以在生产日记中合并。

让我们通过使用生产计划员角色中心(9010)的下一个示例来演示这一点。

我们将使用原材料、机器和资源来制作桃花心木英式桌子。

项目、机器和工作中心

对于桌子,我们需要桃花心木、绿色皮革、胶水、清漆和把手。制作一张桌子,木匠需要四天时间和一个带工具的木工单位。油漆工需要在喷漆室里用一天时间给桌子上漆,而组装部门将组件装箱,并需要四小时完成。

我们需要以下项目来完成此示例:

No. 描述 基础单位 补货系统 单位成本 单位价格 制造政策
CHAPTER5-P1 桃花心木英式桌子 PCS 生产订单 286,25 999 定制生产
CHAPTER5-C1 桃花心木原木 PCS 采购 100 -
CHAPTER5-C2 绿色皮革 PCS 采购 60 -
CHAPTER5-C3 胶水 CAN 采购 15 -
CHAPTER5-C4 桃花心木清漆 CAN 采购 25 -
CHAPTER5-C5 英式桌子把手 PCS 采购 10 -

以下列表是针对机器中心的:

No. 名称 工作中心编号 容量 效率
01-CARP 带工具的标准木工单位 400 1 100
02-喷漆 喷漆室 300 1 100
03-PACK 包装部门 200 1 100

容量

计划运行和需求工作表将使用已定义的容量。每个工作中心机器中心都定义了容量。

容量存储在日历条目中,这些条目是通过代码单元CalendarManagement (99000755)和报告计算工作中心日历 (99001046)以及计算机器中心日历 (99001045)创建的。容量基于并发容量,来自机器或工作中心,以及分配的工厂日历。

就像交互日志条目一样,日历条目是直接插入的,而不是先通过日记。

容量

日历条目

当正确配置时,计划工作表将计算生产订单和每个操作的起始和结束日期,以便满足销售订单行上的交货日期。

生产物料清单

设置制造的生产物料清单与组装功能没有太大区别,但它包含额外的功能。

生产 BOM 使用其自己的标题记录,包括编号系列、描述和搜索描述。状态字段用于确定产品是否为新品、认证产品、开发中或已关闭。与版本控制结合使用,使我们能够在产品的生命周期内维护多个 BOM。

生产物料清单

物料清单的组件以行保存,并支持使用废料。在运行 MRP 和计算单位成本时,会计算废料百分比

路由

路由设置决定了生产一个项目所需的时间以及在该过程中使用哪些工作中心和机床中心。

路由

路由设置提供了高级功能,如并行和串联规划以及设置时间。在我们的例子中,我们将保持简单,仅使用运行时间

测试和低级代码

我们现在几乎准备好开始测试我们的制造项目了。我们已经设置了项目和机床中心,计算了日历条目,并设置了路由。

处理过程的最后一步是计算低级代码。这个字段既存储在项目表中,也存储在生产 BOM 表中,它决定了项目在 BOM 排名中的位置。低级代码为零表示这是一个父项目,一或更高表示是一个子项目或子项目的子项目。

注意

低级代码的最大值可以是 50,但在现实中这将会非常难以操作,并且对系统性能不利。

小贴士

如果你收到一个错误,表明你已经超过了 50 级,请检查生产 BOM 以确保没有循环引用。可能存在一个父项目消耗一个消耗父项目的子项目。

低级代码可以自动或手动计算。对于自动计算,应激活动态低级代码功能。

由于在将其附加到项目之前,NAV(Navision)能够创建一个生产 BOM,因此动态低级代码并不总是准确的。在规划运行之前,运行计算低级代码(代码单元 99000793)是一个良好的实践。

测试和低级代码

然而,激活动态低级代码可能会影响您系统的性能,因此对于大多数安装,最好定期使用代码单元计算低级代码(99000853)来计算它。

模拟、销售订单或库存

在 Microsoft Dynamics NAV 中,有三种创建生产订单的方法。最简单的方法是逐个手动输入。这甚至可以是一个模拟生产订单,以测试是否已按要求设置好一切。

手动订单录入非常耗时,且制造公司很少使用。他们大多数使用 MRP 程序来规划订单。当使用外部应用程序完成时,界面将创建生产订单。

Microsoft Dynamics NAV 中的 MRP 算法支持两种策略,即按库存生产按订单生产

按库存生产

按库存生产,也称为按库存构建,通常用于销售给贸易公司的高容量物品。当使用这种制造策略时,应使用重新订购策略。重新订购策略将在第六章中讨论,贸易

按订单生产

按订单生产通常用于汽车等需求驱动的物品。将这些物品存入库存非常昂贵。制造过程是在物品售出后开始的。

然而,大多数使用按订单生产的公司已经预留了时间槽,以便可以安排这些物品,因此生产能力已经被预留,但物品尚未确定。

当使用按订单生产时,MRP 运行将为所有销售订单创建生产订单。在我们的例子中,我们将使用这种制造策略。

销售订单

对于我们的例子,我们需要一个或多个英文办公桌的销售订单。

注意

在选择位置时要小心,因为这将是制造办公桌的位置。

销售订单

计算 MPS 和 MRP

Microsoft Dynamics NAV 中的计划运行会在需求或计划工作表中创建行。这种工作表结构在销售/采购/生产过程中非常重要。这个工作表可以为销售订单创建采购订单和生产订单。

需求与计划与分包工作表

需求工作表可以显示不同的用户界面(页面),允许用户执行不同的任务。

需求与计划与分包工作表

需求工作表没有像其他分录一样的一般后行程序。每种工作表类型都使用不同的过程。以下架构显示了需求过程是如何结合在一起的:

需求与计划与分包工作表

贸易部分将在第六章中讨论,贸易。在这一章中,我们将重点关注规划(MPS 和 MRP)过程和制造部分。

提示

需求工作表流程允许我们创建自己的流程来准备数据,使用自定义设置生成工作表行,甚至可以在新的 C/AL 对象中构建自己的规划算法来创建需求行。

库存配置文件偏移

Microsoft Dynamics NAV 中 MRP 计算的实际核心是代码单元库存配置文件偏移(99000854),它从我们的架构中的代码单元 Calc. Item Plan - Plan Wksh.(5431)中调用。

这个代码单元不容易理解,并且只有在仔细分析后才能由专业开发者更改。该过程在计算期间使用库存配置文件缓冲表来构建信息,并从函数 CalculatePlanFromWorksheet 开始:

库存配置表偏移

原子编码

在标准 Microsoft Dynamics NAV 应用程序中,此图像中的代码单元是原子编码(也称为工作流程编码)的完美示例。使用这种编程风格,您将代码分解为具有功能命名的函数,并在调用函数时省略任何编程。这使得您的代码更容易被他人阅读,并且维护成本更低。

让我们看看这个代码单元中的函数:

  • InitVariables: 此函数用于清除和初始化在此代码单元中使用的变量。

  • DemandtoInvProfile: 在这里,系统为可能需要物品的销售订单、服务订单和生产订单在库存配置表(Inventory Profile)中创建记录。

  • ForecastConsumption: 如果在制造设置中使用使用位置预测,则根据生产预测在库存配置表缓冲区中创建额外的需求行。

  • BlanketOrderConsump: 在计算期间内,对于具有发货日期未完成数量的空白销售订单,将插入额外的需求。

  • SupplytoInvProfile: 当前库存、采购订单和生产订单被添加到库存配置表中作为可能的供应。

  • UnfoldItemTracking: 如果物品使用物品跟踪,此函数确保批号和序列号匹配。

    注意

    在此函数中,Microsoft 开发者使用了一个技巧,即当临时表具有更多维度时,两个表中的值是相同的。这篇博客文章markbrummel.wordpress.com/2014/06/01/tip-27-using-temp-tables-in-arrays/解释了这是如何工作的。

  • FindCombination: 此函数为每个需要补充的 SKU 创建临时的库存单位记录。如果物品没有任何 SKU,系统将创建一个临时的 SKU 记录。

  • PlanItem: 这是根据库存配置表中的信息和设置创建实际需求行的位置。

  • CommitTracking: 此函数将存储在临时记录变量中的信息保存到数据库中的实际数据,用于预订条目和操作消息。

计划计算

让我们为我们的英文桌面运行计划工作表,看看我们会得到哪些计划行。

计算计划

MPS 和 MRP 运行是从计划工作表开始的。我们需要输入开始日期和结束日期。在我们的示例中,使用 CRONUS 数据库,我们可以使用当前的工作日期。

当主生产计划(MPS)和物料需求计划(MRP)运行完成后,我们可以开始执行执行操作消息的过程以创建生产订单

生产订单工作流程

生产订单现在已创建并准备好启动。第一个状态是计划中确定计划。在计划状态期间,Microsoft Dynamics NAV 可以自动更改生产订单。

一旦生产订单发布,它就不能再自动更改。

生产订单工作流程

要发布生产订单,组件必须可用。在我们的测试场景中,这还不成立,因为我们创建了新的项目,这些项目尚未购买。

生产订单工作流程

让我们看看我们如何做到这一点。

采购订单

要创建采购订单,我们将使用订单计划工作表来展示另一种规划方法。这将为我们刚刚发布的生产订单创建需求行。

采购订单

一旦创建了需求行,我们需要在供应来源字段中指定一个供应商编号,然后开始创建订单过程以生成采购订单

采购订单

采购订单可以被接收。这允许我们发布生产订单

采购订单

完成生产

当生产订单完成时,最终产品应该存放在库存中,组件应该被消耗。这个过程被称为冲销。

这是通过使用消耗日记产出日记来完成的,可以自动和手动进行。

注意

消耗和产出日记的替代方案是生产日记,它将功能合并到一个日记中。

当项目卡片中的冲销方法设置为“向前”,“向后”或“拣选+向前”,“拣选+向后”时,消耗日记会自动过账。

当使用“向前”时,消耗日记在生产订单发布时过账,“向后”将在生产订单设置为完成时过账。“拣选+向前”和“拣选+向后”可以与仓库管理位置结合使用。

专业生产

我们在第章开头讨论的最后一种生产方法是专业生产。

这些是在小批量生产或每次投入生产时规格差异很大的项目。对于这些公司来说,每次项目更改时创建物料清单的工作量太大,与它们获得的额外信息相比。

作业

尽管如此,这些公司仍然希望注册他们的生产订单并清楚地了解他们的库存。大多数进行此类生产的公司都在使用 Microsoft Dynamics NAV 的作业功能。

我们将在第八章中讨论这个问题,咨询,我们将以不同组件组成的定制计算机系统为例。

垂直行业实施

Microsoft Dynamics NAV 被用于许多不同的垂直行业。垂直行业通常需要特定的功能。而不是试图在标准产品中实现所有这些功能,Microsoft Dynamics NAV 支持框架并允许开发人员设计和创建垂直功能。

对于这些功能,80/20 规则适用;微软提供 80%的框架,这需要我们 20%的时间来实现。缺失的 20%的功能开发需要 80%的预算时间。

在本章中,我们将讨论微软 Dynamics NAV 在五个不同垂直行业中的生产应用。对于每个行业,我们将讨论两个特定的垂直功能以及它们如何被解决。

备注

大多数行业都有由认证的微软 Dynamics NAV 合作伙伴设计的、在多个地点实施的有力附加解决方案。强烈建议查看这些附加解决方案,而不是重新发明轮子,重写已经存在的附加组件。

时尚

时尚行业的一般挑战是尺码和颜色。每个商品可以从 XXS 到 XXXL 生产并销售,从粉色到橙色再到绿色,同时保持商品不变。

这需要创造性地使用变体,这些变体在市场上的可用垂直解决方案中被大量使用。

材料清单

要使用具有变体的制造,材料清单结构应该改变,因为这是默认在项目级别存在的。然而,每个尺码使用不同数量的布料,而不同颜色的布料通常在原材料中以另一个项目编号表示。

解决这个问题的方法可能是在项目级别将材料清单移动到库存单位级别。一个 SKU 支持成本和库存的变体。

发货工作表

时尚公司为一系列产品生产商品。客户有预留一系列产品的可能性,以便生产经理确定生产数量。基于这些数字和额外的安全库存,生产订单被创建。一旦生产订单完成,公司需要决定谁将获得第一批商品。这可以最好地描述为一种反向的按订单生产机制。

为了在微软 Dynamics NAV 中实现这一点,我们可以创建一个工作表,为每个生产订单和销售订单的组合创建行。对于将要发货的每个销售订单,我们可以从发货工作表中创建一个仓库拣选发货

我们将在第六章贸易中讨论库存单位、仓库拣选和发货。

汽车

在汽车行业,微软 Dynamics NAV 主要被汽车制造供应商使用,这些公司使用原材料制造预制件。

工具和折旧

在这些公司中,生产件批准流程(PPAP)以及工具折旧同样非常重要,因为生产过程开始之前在工具上的初始投资很高。

为了支持这一点,需要为工具和 BOM 流程开发额外的功能。例如,路由工具表(表 99000802)可以连接到固定资产(表 5600)。

项目跟踪

当汽车的一个部件出现问题,能够看到其他汽车上由同一工厂和工具使用相同基础材料制造的相同部件是很重要的。

在 Microsoft Dynamics NAV 中,可以使用单个批号用于组件或最终产品,并追溯其来源。无法简单地移动组件的批号到最终产品,或从组件的批号复制信息,如容器号或质量代码到最终产品。

为了支持这一点,我们需要改变项目跟踪流程。一个很好的开始地方是项目日记,其中预订条目被移动到项目账簿条目。

药品

当由制造药品的公司使用时,正确使用批号的到期日期非常重要。

批号和到期日期

在 Microsoft Dynamics NAV 中,到期日期在项目账簿条目仓库条目中定义。

无法为批次定义一个单一的到期日期。这可以通过将此字段添加到批号信息表(6505)中来实现。此表允许公司预先定义在生产过程中使用的批号。

默认情况下,到期日期是根据文档日期乘以项目表中的到期计算字段(27)来计算的。

可以使用批号信息表来保存关于特定生产批次的额外信息。

质量控制

质量控制在大多数生产过程中都很重要,但在处理药品时可能更为重要。通常,批次的一小部分被用于质量控制。

在 Microsoft Dynamics NAV 中,我们可以在生产订单评级质量度量表(5413)中定义质量度量。然而,这些值仅作为生产订单的信息保存。

为了增强质量控制,我们可以在生产订单中创建一个质量检查文档,从而添加一个文档结构。这些信息应保存在批号信息表中。

当批次没有达到所需的质量时,应启动一个工作流程。此工作流程将引导用户通过一个可以做出决策的过程。有时,质量可以改善,项目仍然可以使用。有时,项目编号甚至取决于产品的质量。

质量控制位于消耗日记和输出日记之间。在最终质量检查期间,使用 BOM 项目,但最终项目尚未可用。

食品

在食品行业,一切关乎到期日期和新鲜产品。库存永远不会很高,流通率非常高。

零库存

因此,新鲜食品公司应该在到期日期即将关闭或已过期后,能够将某些批号的库存清零。

这通常可以通过实物库存日记来完成。使用批号手动进行这项工作对于每天都要做这项工作的人来说可能是一项相当繁重的工作,因此对于这个垂直解决方案,我们可以创建一个功能来完成这项工作。此功能将创建一个带有激活的物理库存字段(83)的项目日记行,并创建预订条目以跟踪并自动过账。

订单调度

新鲜食品公司使用每天的生产流程,这些流程从预定时间开始。每天,工厂开始生产过程,但生产数量可以根据订单而有所不同。

这可以通过按订单制造政策来完成,但我们需要确保在计算过程开始时不会有新的销售订单。

这可以通过创建一个订单调度策略来实现。可以为每个项目创建新的销售行,直到某个时间。当时间过去后,销售人员将收到错误信息。这允许生产计划员每天在固定时间开始计算过程,并知道销售订单数量是可以信赖的。

家具

家具行业是一个庞大且历史悠久的行业,它存在于工业革命和计算机引入之前。

我们可以将家具行业大致分为两部分。第一部分已经将生产标准化,使用尺寸和颜色矩阵,这可以与时尚行业相比较。当购买一张桌子或厨房时,客户可以从不同的尺寸和颜色中进行选择。根据选择数量的多少,产品要么是备货生产(IKEA),要么是按订单生产。

第二部分是仍然生产定制项目的家具制造商。在这些制造商处,一张桌子或厨房可以有任何尺寸或颜色。对于这些公司来说,为每个定制项目创建物料清单几乎是不可能的,因此他们使用预定义的计算和项目类别。

在本书的示例中,我们将讨论第二类。

计算

为定制家具的公司提供计算材料和资源使用量的可能性,无论是在项目类别层面还是在实际项目层面。为此,我们可以创建一个包含这些数据和发布模型的计算模块。

计算

该计算模块的基本结构在第八章《咨询》中进行了解释,我们将此功能整合到了微软 Dynamics NAV 的工作功能中。

库存

我们的家具公司使用了一种结合了独特的产品特定项目和大多数产品中使用的库存项目的组合。

这些项目将组合成最终产品,并在产品完成时应被消耗。例如,当计算模块与工作集成时,当工作完成时就可以刷新组件。这种功能可以与生产订单完成后发布消耗日记账的功能相比较。

可以使用实物库存日记账和库存盘点来每周更新库存项目。这使得我们能够使用将在第六章“贸易”中讨论的请购单工作表和重新订购策略。

摘要

在本章中,我们讨论了如何在 Microsoft Dynamics NAV 产品中实施三种生产方法。我们介绍了项目跟踪和项目成本的概念。我们通过不同的请购单工作表对制造过程进行了巡览,并讨论了 MRP 的解决方案。

最后,我们探讨了如何为不同的垂直行业实施生产。在下一章中,我们将更深入地了解 Microsoft Dynamics NAV 中的贸易流程。

第六章:贸易

在上一章中,我们讨论了如何使用 Microsoft Dynamics NAV 通过标准应用程序以及定制解决方案来帮助我们简化生产流程。我们讨论了五个垂直行业以及如何为它们适配应用程序。

在本章中,我们将讨论如何使用销售和采购文档以及如何与内置的仓库管理和预留流程集成来为这些公司使用 Microsoft Dynamics NAV。

本章的重点是介绍应用程序的设计方式,以及如何更改或增强设计。了解如何在 Microsoft Dynamics NAV 中创建和处理销售和采购文档的基本知识是先决条件。

我们将使用与上一章中讨论的相同垂直行业(汽车、时尚、药品、食品和家具)的例子。阅读完本章后,您将很好地理解如何在贸易公司中实施 Microsoft Dynamics NAV。

流程

一家贸易公司购买和销售商品而不对其进行更改。主要活动包括采购、储存、包装、销售和运输,如下面的截图所示:

流程

在这些公司中,管理库存非常重要。拥有库存对于按时交付和不必对客户说“不”至关重要。

批发与零售

传统上,贸易公司分为批发公司和零售公司。批发公司向企业销售,而零售公司向消费者销售。Microsoft Dynamics NAV 支持两者,在设计(表和记账结构)方面,没有太大的区别。

对于应用程序来说,批发和零售之间最大的区别是交易量。虽然批发公司的总营业额可能比零售商高得多,但零售商通常有更多、更小的交易。从应用程序设计角度来看,保持解决方案的性能可能是一个挑战。

高量交易系统中的另一个问题是数据的可追溯性。每当出现问题,了解问题开始的地方以及有多少数据受到影响非常重要。在低交易系统中,手动查找这一点更容易。

销售和采购

传统上,销售人员使用纸质订单表。他们会写下客户名称和地址以及所需的项目或服务。

销售和采购

纸质订单表

在 Microsoft Dynamics NAV 中,纸质文件被销售和采购文件所取代,使用标题来记录一般订单信息,使用行来登记项目和服务的详细信息。

记账过程将文档中的信息分解为日记账并予以记账,因此最终用户无需担心这一点。应用程序重复使用我们在前面章节中讨论过的相同记账程序。

让我们通过绘制这个表格和事务方案来了解文档和日记是如何联系在一起的:

销售和采购

第一步是创建文档。当我们创建这个销售文档销售抬头销售行)时,没有任何事物被过账。我们只是在系统中输入可以随时更改的信息。

当我们启动代码单元 Sales-Post(80)时,系统将为我们创建所有日记并过账。当我们销售一个项目时,系统将创建一个项目日记行,当我们销售一个资源时,系统创建一个资源日记行,依此类推。

发票过账缓冲区用于在总账分录行中创建条目。我们已经在第三章 财务管理 中讨论了这一功能。

Microsoft Dynamics NAV 允许我们创建四种不同类型的已过账销售文档:发票、发货、贷项通知和退货收据。我们将在本章后面讨论所有这些类型。

事务镜像

Microsoft Dynamics NAV 中销售和采购的独特概念是事务结构的镜像。一旦我们了解了销售交易是如何相互关联的,理解采购的结构就会变得容易。

让我们通过比较表 37 销售行表 39 采购行中的第一个字段来演示这一点,如下面的截图所示:

事务镜像

尽管这两个表使用不同的术语,但它们的字段编号相同,服务于相同的过程,例如,字段 18,要发货数量(销售)和要接收数量(采购)。

一些字段是不同的,因为它们在两个流程中都没有意义,例如,采购中的单价(本位币)(字段 31)和销售中的客户价格组(字段 42)。

采购流程也使用相同的过账方法。采购抬头(38)和采购行(39)表在采购收货、发票、贷项通知和退货单据中使用代码单元 Purch.-Post(90)进行过账。

让我们更详细地看看销售流程。

销售

销售流程支持六种文档类型,这些类型被归入两个表中,即销售抬头(36)和销售行(37)。

每个流程都可以有自己的编号序列,并且拥有一个特殊的卡片和列表页面,但它们都共享相同的企业逻辑。让我们讨论一下文档类型:

  • 报价:当客户想要了解购买条款和条件时,我们可以制作报价单。这将显示所有计算,例如定价和增值税。

  • 框架订单:这是一种预订单状态。当使用时,我们与客户达成协议,但不知道确切的发货日期。

  • 订单:这是实际订单文档的用途。

  • 发票:这可以有两种用法;直接使用,如果没有销售订单,如果公司只在总账科目上直接开票,或者我们可以使用发票来开票一个或多个装运。

  • 贷项通知单:当我们对总账科目进行贷记时,可以使用贷项通知单。

  • 退货订单:如果客户退货,我们可以使用退货订单来逆转库存流程。

订单

主要流程是订单。其他文档类型是为了支持这一流程而设计的。销售订单可以直接创建,也可以通过报价或总订单创建。报价和总订单之间有两个区别:

  • 报价只能完全转移到销售订单中,不能分批。例如,100 件的总订单可以分成 10 次发货,每次 10 件,不同的发货日期。

  • 拥有报价的客户有说“是”或“否”的可能性。当答案是“否”时,将不会有交易。因此,正如我们在上一章中讨论的那样,报价不用于供需计算。总订单是一个真实订单。客户最终应该购买之前同意的完整数量。因此,总订单用于供需计算。

报价到订单和总订单到订单

虽然报价和总订单存储在同一个表中,但记录实际上是物理删除并使用另一种文档类型插入的。这是在代码单元销售报价到订单(86)和总销售订单到订单(87)中完成的。

当在比较工具(如 Beyond Compare 或 Araxis)中比较这些代码单元时,我们可以看到它们有很多相似之处。它们都创建一个新的销售订单。

报价到订单

当将报价移动到订单时,完整的报价会被复制然后删除。报价可以从 CRM 中的机会创建,正如我们在第四章“关系管理”中讨论的那样。因此,当这种情况发生时,机会会被更新。

总订单到订单

总订单可以分批移动。因此,业务逻辑被实现来计算剩余数量。总订单与 CRM 之间没有链接,也无法从报价中创建总订单。

创建新的销售订单

为了理解本章的示例,我们将讨论销售订单最重要的字段。销售文档包含一个表头和多个行。

虽然销售表头表包含更多静态信息登记,但销售行有更多真实业务逻辑,例如价格计算、库存可用性和增值税。我们将讨论这种业务逻辑是如何规范化的。

创建新的销售订单

销售表头

所有文档类型都有唯一的编号。销售表头表的主键字段是文档类型编号

小贴士

使用对最终用户有意义的数字序列代码非常有用,例如,2013 年的销售订单 12 为 SO13-0012,2014 年的销售报价 312 为 SQ14-0312。

销售单据包含以下两个不同的客户编号字段:

  • 销售客户编号:这是主要客户编号字段,它定义了请求创建订单的客户。此客户编号用于计算折扣。

  • 账单客户编号:默认情况下,销售客户编号也将收到发票。通过将此字段更改为另一个客户,这将使包含其他客户详细信息的发票打印出来。

销售单据包含一些用于不同目的的日期:

  • 过账日期:此日期用于过账到各种分类账

  • 文档日期:此日期用于应收账款

  • 发货日期:此日期用于计算或库存可用性

  • 到期日期:这是账单客户预期支付发票的最后日期

销售行

每个销售单据可以包含几乎无限数量的销售行。默认情况下,销售行编号为 10000、20000、30000,依此类推。

编号使用销售行页面上的AutoSplitKey属性进行,增量不能更改。当用户在两个现有行之间插入新记录时,程序将计算新的编号,使其正好位于旧值之间,例如,10000、15000、17500、18750、19375、19687、19843、19921、19960、19980、19990、19995、19997、19998、19999 和 20000。如果没有更多空间,系统将生成一个运行时错误消息,如下面的截图所示:

销售行

主数据选项

销售行可以包含对由类型字段定义的六种主数据类型的引用。这些类型是:文本(空白选项)、总账科目项目资源固定资产费用(项目)。

我们在这里指定的类型决定了在过账此销售单据时将使用哪个日记账。然而,每行都可以包含财务信息,这些信息将通过过账缓冲表处理到总账。

在下一章中,我们将讨论如何向此过程添加新类型。

销售行字段

要创建新的销售行并启动 Microsoft Dynamics NAV 中的重要业务逻辑,我们需要了解以下字段:

  • 类型:这定义了销售行使用的和最终在过账期间使用的日记账

    注意

    在创建销售行后,如果类型字段发生变化,则记录将被清除,字段将获取其默认值。

  • 编号:这是对所使用的主数据类型唯一编号的实际引用

    注意

    编号字段更改时,将使用之前的数量使用新主数据重新创建销售行。

  • 数量:用于计算发票的销售额以及物品的情况,以及库存变化的实际数量

  • 未清数量待发票数量待发货数量:这些字段设计用于部分发货和发票订单

  • 单价单位成本(本位币):这些字段用于计算销售额和利润

  • 行折扣百分比行折扣金额:这些字段用于确定折扣

验证流程

在更改表之前,理解销售行表中的特定验证流程函数非常重要。此流程基于最终用户创建销售行的正常方式。

要创建销售行,只需填充四个字段,行就准备好使用。在设置类型并选择编号后,最终用户在数量字段中输入,如果需要,还可以在单价字段中输入。

让我们分析OnValidate触发器中的 C/AL 代码,这些字段可以计算销售行。

注意

当更改这些 C/AL 程序时,请确保使用我们在第一章中讨论的测试附近测试远程执行清理方法,Microsoft Dynamics NAV 简介

编号 | 字段 6

OnValidate触发器中的 C/AL 代码首先进行初始测试,以确定更改是否允许。之后,记录被清除,并应用编号字段和数量字段的旧值,如下所示:

TempSalesLine := Rec;
INIT;
Type := TempSalesLine.Type;
"No." := TempSalesLine."No.";
IF "No." = '' THEN
  EXIT;
IF Type <> Type::" " THEN
  Quantity := TempSalesLine.Quantity;

然后,如果需要,销售行从销售头继承值,并计算日期字段,如下所示:

"Sell-to Customer No." := SalesHeader."Sell-to Customer No.";
"Currency Code" := SalesHeader."Currency Code";
...

"Promised Delivery Date" := SalesHeader."Promised Delivery Date";
...

UpdateDates;

小贴士

当最终用户为编号字段选择值时,销售行中不存在销售头信息。我们不能使用客户信息进行表关系。

当完成这项操作后,我们会看到一个CASE语句,其中获取了主数据。这将是我们将新添加的字段从主数据移动到销售行表的地方。

CASE Type OF
  Type::" ":
      ...
  Type::"G/L Account":
      ...
  Type::Item:
      ...
 Type::Resource:
      ...
  Type::"Fixed Asset":
      ...
  Type::"Charge (Item)":
      ...
END;

当完成这项操作后,将计算数量和单价。

IF Type <> Type::" " THEN BEGIN
  IF Type <> Type::"Fixed Asset" THEN
    VALIDATE("VAT Prod. Posting Group");
  VALIDATE("Unit of Measure Code");
  IF Quantity <> 0 THEN BEGIN
    InitOutstanding;
    IF "Document Type" IN ["Document Type"::"Return Order","Document Type"::"Credit Memo"] THEN
      InitQtyToReceive
    ELSE
      InitQtyToShip;
    UpdateWithWarehouseShip;
  END;
 UpdateUnitPrice(FIELDNO("No."));
END;

后者是分析中非常重要的。在此函数之后,将执行其他代码,但这对于本例来说并不重要。

数量 | 字段 15

就像编号字段一样,数量字段首先检查更改是否允许。当完成这项操作后,以下 C/AL 代码部分很重要:

IF Type = Type::Item THEN BEGIN
 UpdateUnitPrice(FIELDNO(Quantity));
  ...
  CheckApplFromItemLedgEntry(ItemLedgEntry);
END ELSE
  VALIDATE("Line Discount %");

在前面的 C/AL 代码中,我们应再次注意UpdateUnitPrice函数以及行折扣百分比字段的验证。

单价 | 字段 22

此字段有很少的 C/AL 代码。当手动更改单价时,C/AL 代码将触发行折扣百分比字段:

TestStatusOpen;
VALIDATE("Line Discount %");

在进入此字段之前,让我们首先看看我们在数量编号字段中注意到的UpdateUnitPrice函数。

UpdateUnitPrice

UpdateUnitPrice函数执行以下 C/AL 代码:

IF (CalledByFieldNo <> CurrFieldNo) AND (CurrFieldNo <> 0) THEN
  EXIT;

GetSalesHeader;
TESTFIELD("Qty. per Unit of Measure");

CASE Type OF
  Type::Item,Type::Resource:
    BEGIN
      PriceCalcMgt.FindSalesLineLineDisc(SalesHeader,Rec);
      PriceCalcMgt.FindSalesLinePrice(SalesHeader,Rec,
        CalledByFieldNo);
    END;
END;
VALIDATE("Unit Price");

在进行检查后,我们在第二章“一个示例应用”中讨论的销售价格计算例程被执行。这是销售价格计算管理代码单元 7000。

当这样做之后,它验证了我们已经分析过的单价字段。这引导我们到一个单独的点;行折扣百分比的OnValidate触发器。

行折扣百分比 | 字段 27

在此OnValidate触发器中的 C/AL 代码首先根据单价计算行折扣金额,然后开始执行UpdateAmounts函数,如下所示:

TestJobPlanningLine;
TestStatusOpen;
"Line Discount Amount" :=
  ROUND(
    ROUND(Quantity * "Unit Price",Currency."Amount Rounding Precision") *
    "Line Discount %" / 100,Currency."Amount Rounding Precision");
"Inv. Discount Amount" := 0;
"Inv. Disc. Amount to Invoice" := 0;
UpdateAmounts;

UpdateAmounts

UpdateAmounts函数完成了销售行的创建,这也是我们的探索结束的地方。

在此函数中执行的两个最重要的其他功能是用于增值税计算的UpdateVATAmounts和用于客户信用额度的CustCheckCreditLimit.SalesLineCheck(Rec)的信用额度检查。

增值税计算

Microsoft Dynamics NAV 中的增值税计算没有在一个应用程序区域中规范化,而是在每个地方重新开发。这使得增值税计算成为更改最复杂的应用程序区域之一。

代码克隆

增值税计算不仅发生在销售行、采购行和总账行表中,还发生在更具体的函数表中,如服务行。这是通过复制整个 C/AL 代码来完成的。

这种现象在计算机科学中被称为代码克隆。尽管代码克隆简化了应用程序设计,但它被认为是不良实践,应始终避免。在这种情况下,如果增值税在通用引擎中进行计算会更好。

因此,强烈建议不要更改 Microsoft Dynamics NAV 中的增值税计算。

小贴士

如果在定制解决方案中需要增值税计算,可以使用总账行作为临时表来完成。通过填写必要的字段并开始计算,我们可以使用结果,而无需将增值税计算复制到我们自己的解决方案中。

开票

在 Microsoft Dynamics NAV 中,销售订单可以直接从文档中发货和开票。

然而,并非所有公司都有合并的发货和开票流程。一些公司先发货,然后发送发票,大多数情况下使用合并开票。

预付款

除了将开票时刻与发货时刻分开外,Microsoft Dynamics NAV 还允许预付款流程。这个预付款流程旨在在正常开票流程之上工作。这意味着它不会替换发票,而是创建一个额外的发票。

预付款

这张发票不是在代码单元销售过账(80)中创建的,而是在销售过账预付款(461)中创建的。

注意

在 Microsoft Dynamics NAV 中使用预付款将始终为每个销售订单生成至少两张发票。

当订单最终开票时,预付款发票会从发票金额中扣除。

小贴士

微软设计的此解决方案教会我们并展示了,为了生成已过账的销售发票,并不特别需要从代码单元销售-过账 80 开始。

合并开票

货物合并开票可以手动完成或使用批量报告。

手动

要在销售发票上手动合并运输,我们可以使用销售-获取运输代码单元(64)

此代码单元可以从销售发票子页(47)上的操作启动,并显示尚未完全开票的销售运输行。

手动

然而,C/AL 代码并不完全在代码单元内;过程从代码单元开始,并运行页面。然后页面再次在代码单元中启动一个功能。

手动

批量

合并运输报告(295)可以用于批量创建多个运输的单一发票。它的工作方式与我们创建的合并发票报告类似,见第二章,一个示例应用

创建发票销售行的 C/AL 代码已标准化,并在代码单元销售-获取运输(64)和合并运输报告(295)中均使用。该功能位于销售运输行表(111)中,并命名为InsertInvLineFromShptLine

注意

要启用合并运输,客户表中的布尔字段合并运输(87)应设置为。此值将继承到销售订单文档的销售表头中。

信用 memo 和退货订单

信用 memo 和退货订单文档类型用于撤销订单流程。

采购

在我们能够发货我们销售的商品之前,我们首先需要购买或生产它们。我们在上一章中讨论了生产过程,所以让我们专注于采购过程。

技术上,销售和采购流程是镜像交易,应用程序设计类似。采购表头具有相同的文档类型:报价、订单、发票、信用 memo、总订单和退货订单,以及相同的过账过程。

因此,我们不会讨论相似之处,而是讨论不同之处。

资源

在 Microsoft Dynamics NAV 中,无法购买资源。当我们仔细查看类型字段(5)时,我们可以看到选项留空:

资源

降价运输

当销售不在库存中的商品时,可以从供应商处购买商品并将它们直接运送给客户。这个过程称为降价运输。

此过程可以手动处理,并使用需求工作表。

手动

要手动创建降价运输,应首先使用从相应销售订单的销售至客户编号作为运输地址创建采购订单:

手动

当这样做后,我们可以从采购订单上的操作中的代码单元 Purch.-Get Drop Shpt. (76) 开始。此功能将显示所有针对此销售客户编号的销售订单列表,无论是否可以进行直送。

如果我们选择一个没有标记为直送的销售行的销售订单,我们会得到以下错误消息:

手册

在检索销售信息后,销售行和采购行表通过填充采购订单编号采购订单行编号销售订单编号销售订单行编号字段相互连接。

手册

这些字段在销售行和采购行表中编号为 71 和 72。

需求工作表

在我们讨论计划过程时,我们在上一章介绍了需求工作表。需求工作表也可以用于直送功能:

需求工作表

这将启动获取销售订单报告(698),它将过滤所有标记为直送的销售行,并在需求工作表表中创建一行。

这行可以通过执行操作消息来处理。此功能还将通过字段 71 和 72 将销售订单连接到采购订单。

注意

手动直送和使用需求工作表的自定义 AL 代码没有规范化(代码克隆)。这意味着在一个方法中做的更改也应该在另一个方法中执行,并且需要维护两次。

文档发布和批准流程

在销售和采购文档流程中,有一个用于发布和批准文档的工作流程。这由一个状态字段和两个流程来处理。

状态

销售表头和采购表头中的状态字段(120)表示流程的状态。有四个选项:开放发布待批准待预付款

这两个状态字段,开放发布,是必须使用的。待批准待预付款是可选的。

我们在本章前面已经讨论了预付款。

发布文档

在可以过账之前,必须发布文档。这是通过代码单元发布销售文档(414)和发布采购文档(415)来完成的。如您所猜,这些代码单元几乎完全相同。

在将状态设置为发布之前,代码单元执行了多项测试。让我们讨论一些这些检查:

  • 测试近似的典型例子,客户编号不应为空:

    TESTFIELD("Sell-to Customer No.");
    
  • 至少应该有一条销售行带有“数量”:

    SalesLine.SETRANGE("Document Type","Document Type");
    SalesLine.SETRANGE("Document No.","No.");
    SalesLine.SETFILTER(Type,'>0');
    SalesLine.SETFILTER(Quantity,'<>0');
    IF NOT SalesLine.FIND('-') THEN
      ERROR(Text001,"Document Type","No.");
    
  • 测试完成后,实施一些最终计算。这些计算是跨越各个销售行的文档计算:

    SalesSetup.GET;
    IF SalesSetup."Calc. Inv. Discount" THEN BEGIN
      CODEUNIT.RUN(CODEUNIT::"Sales-Calc. Discount",SalesLine);
      GET("Document Type","No.");
    END;
    
  • 以下代码单元计算发票折扣:

    SalesLine.SetSalesHeader(Rec);
    SalesLine.CalcVATAmountLines(0,Rec,SalesLine,TempVATAmountLine0);
    SalesLine.CalcVATAmountLines(1,Rec,SalesLine,TempVATAmountLine1);
    SalesLine.UpdateVATOnLines(0,Rec,SalesLine,TempVATAmountLine0);
    SalesLine.UpdateVATOnLines(1,Rec,SalesLine,TempVATAmountLine1);
    
  • 在发布流程结束时,完成增值税计算。

  • 发布文档也会计算销售行上的金额含税金额字段。

手动与自动发布

默认情况下,Microsoft Dynamics NAV 会自动发布文档。发布代码单元销售-post(80)和采购-post(90)包含以下 C/AL 代码:

IF (Status = Status::Open) OR (Status = Status::"Pending Prepayment") THEN BEGIN
  TempInvoice := Invoice;
  TempShpt := Ship;
  TempReturn := Receive;
  GetOpenLinkedATOs(TempAsmHeader);
  CODEUNIT.RUN(CODEUNIT::"Release Sales Document",SalesHeader);
  TESTFIELD(Status,Status::Released);
  Status := Status::Open;
  Invoice := TempInvoice;
  Ship := TempShpt;
  Receive := TempReturn;
  ReopenAsmOrders(TempAsmHeader);  
  MODIFY;
  COMMIT;
  Status := Status::Released;
END;

此代码通过启动发布代码单元临时发布文档,然后将状态设置回Open,修改记录并提交事务。然后,状态被设置为已发布

无论之后出现任何错误,状态仍然将是Open,因为那是COMMIT之前的状态。

文档审批

在发布过程之上是一个文档审批工作流。这个功能是为在我们已经讨论的功能之上运行的,是可选的。

删除销售和采购文档

在我们应用程序的生命周期中,将创建许多文档。可能会有这样一天,当这些文档超过需要维护的点时。

数据删除

部门角色中心IT 管理部分,我们可以找到一个数据删除部分,它是为 IT 管理员清理数据而设计的,如下面的截图所示:

数据删除

当使用获取发货行合并开票对销售订单进行开票时,销售订单不会被自动删除,也不会完全处理批量订单。

在数据库中保留旧订单可能会导致大表。由于这些文档表在整天的许多人的操作中频繁插入和修改,这可能会导致数据库中不必要的开销。

删除发货和发票

Microsoft Dynamics NAV 允许用户在打印时删除已发布的发货和发票。

删除发货和发票

虽然应该仔细考虑,但对于某些公司来说,定期清理这些数据可能是必要的。大多数公司在将项目交付给客户后,永远不会查看发货。

如果这些表的大小达到大约 50-100 GB,清理这些表将对系统的性能和可维护性产生积极影响。

小贴士

在设计业务分析报告时,永远不要使用销售发货表头或行表中的数据,因为它们可能会被删除。始终使用总账分录表。

库存管理

在 Microsoft Dynamics NAV 中,库存是通过项目分录价值分录在位置上保持的。在此基础上,我们可以使用库存单位为每个项目、位置和变体设置不同的库存设置。

让我们从查看 Microsoft Dynamics NAV 中库存的设计模式开始:

库存管理

通过使用仓库管理可以扩展库存的保持。这是在基本项目库存功能之上运行的。

项目

项目表存储库存管理的母数据,就像总账账户对财务管理所做的那样。

物品

在这个表中,我们可以为每个单独的项目进行设置,例如定价、库存和生产策略,以及跟踪选项。

位置

位置表定义了执行哪种级别的库存管理。一个位置可以是某个地方的物理仓库,或者是一个仓库的一部分,如果使用不同的仓库策略。

如果我们查看位置卡,我们会看到我们可以设置的内容:

位置

让我们详细看看这些设置:

  • 通用:在这里,我们可以指定仓库的物理位置。我们还可以指定使用在途。当指定了这一点时,我们只能使用调拨订单将库存移动到这个位置。

  • 仓库:在这个选项卡中,我们指定想要使用哪种级别的仓库管理功能。如果一切留空,当使用此位置时,不会创建任何仓库条目。

  • 托盘:这个选项卡包含大多数库存活动的默认托盘,例如收货发货。在创建仓库文件时,这些值可以更改。

  • 托盘策略:这个选项卡包含一些更高级的仓库管理选项。

变体

项目变体是 Microsoft Dynamics NAV 的一个强大功能。它使我们能够将项目拆分为不同的类别,而无需创建新的项目。

变体代码在项目账簿条目中维护,并在应用时使用。让我们看看一个例子,看看如何使用它。

我们公司销售 T 恤。我们有三种尺寸;小号、中号和大号,以及四种颜色;白色、黑色、红色和蓝色。这使得我们能够创建以下十二种独特的变体代码:

Size and color
S-WHITE
M-WHITE
L-WHITE

当我们购买或生产这些 T 恤时,我们需要指定变体代码,该代码将继承到项目账簿条目中。

如果我们销售或转移这些项目之一,我们可以指定相同的变体代码。Microsoft Dynamics NAV 将使用此变体代码在搜索库存时。

变体

库存单位

有时,同一项目可以有多个单位成本、补货系统或生产方法。为了支持这一点,我们可以使用库存单位。

库存单位指的是现有的项目、位置和变体。这三个字段是唯一的键。让我们看看一个例子,看看如何使用它。我们的 T 恤需要有不同的单位成本。为了做到这一点,我们需要为每个我们刚刚创建的变体创建一个 SKU:

库存单位

当我们现在为同一项目创建两个具有不同变体代码的采购订单行时,我们可以看到每个变体的最后直接成本都不同。

注意

库存单位是 Microsoft Dynamics 的一个非常强大的功能。它允许你在创建项目后更改其设置,每个设置使用变体代码。确保变体代码是自解释的。

SKU 函数的创建

当一个项目有多种变体和位置时,为每个组合创建 SKU 可能是一个相当大的挑战。为了帮助这个过程,我们可以使用创建库存单位报告(5706)。

新创建的 SKU 将继承来自项目的所有必要字段。之后,我们可以进入并修改必要的单个 SKU 记录:

创建 SKU 函数

销售定价

项目的基准单价可以在项目表中设置。这是一个静态字段,用于创建新的销售文档时。为了使用更灵活的单价,我们可以使用销售价格销售折扣功能:

销售定价

更多有关定价的信息可以在第一章、Microsoft Dynamics NAV 简介和第二章、一个示例应用中找到。

项目账目条目应用

当库存被创建和使用时,系统将应用并关闭正负项目账目条目,使它们相互对应。这使得我们能够追踪库存。

应用被保存在项目应用条目表(339)中。让我们看看处理项目应用的 C/AL 代码。

项目应用 C/AL 例程

项目应用是在代码单元 Item Jnl.-Post Line (22)中的ApplyItemLedgEntry函数中完成的。该函数从检查是否使用了预留开始。使用预留会改变库存应用的方式。我们将在本章后面讨论预留。

ApplyItemLedgEntry
...

CLEAR(OldItemLedgEntry);
...
REPEAT
  ItemJnlLine.CALCFIELDS("Reserved Qty. (Base)");
  IF ItemJnlLine."Assemble to Order" THEN BEGIN
    ItemJnlLine.TESTFIELD("Reserved Qty. (Base)");
    ItemJnlLine.TESTFIELD("Applies-to Entry");
  END ELSE
    IF ItemJnlLine."Reserved Qty. (Base)" <> 0 THEN BEGIN
      IF ItemLedgEntry."Applies-to Entry" <> 0 THEN
        ItemLedgEntry.FIELDERROR(
          "Applies-to Entry",Text99000000);
    END;
    ...
  END ELSE
    StartApplication := TRUE;

如果没有进行预留,系统将启动应用代码。这允许两种可能性:手动应用和自动应用。

当用户在项目日记账行中填写应用到条目字段时,进行手动应用。这也用于用户更改应用时。

IF StartApplication THEN BEGIN
  ItemLedgEntry.CALCFIELDS("Reserved Quantity");
  IF ItemLedgEntry."Applies-to Entry" <> 0 THEN BEGIN
    IF FirstApplication THEN BEGIN
      FirstApplication := FALSE;
      OldItemLedgEntry.GET(ItemLedgEntry."Applies-to Entry");
      OldItemLedgEntry.TESTFIELD("Item No.",ItemLedgEntry."Item No.");
      OldItemLedgEntry.TESTFIELD("Variant Code",ItemLedgEntry."Variant Code");

      OldItemLedgEntry.TESTFIELD(Positive,NOT ItemLedgEntry.Positive);
      OldItemLedgEntry.TESTFIELD("Location Code",ItemLedgEntry."Location Code");

在这种情况下,系统会检查我们所指定的项目账目条目是否符合要求。当应用自动完成时,系统将根据相同的要求搜索最佳的项目账目条目。

END ELSE BEGIN
  IF FirstApplication THEN BEGIN
    FirstApplication := FALSE;
    ItemLedgEntry2.SETCURRENTKEY("Item No.",Open,"Variant Code",
      Positive,"Location Code","Posting Date");
    ItemLedgEntry2.SETRANGE("Item No.",ItemLedgEntry."Item No.");
    ItemLedgEntry2.SETRANGE(Open,TRUE);
    ItemLedgEntry2.SETRANGE("Variant Code",ItemLedgEntry.
      "Variant Code");
    ItemLedgEntry2.SETRANGE(Positive,NOT ItemLedgEntry.Positive);
    ItemLedgEntry2.SETRANGE("Location Code",
      ItemLedgEntry."Location Code");

    IF ItemLedgEntry."Job Purchase" = TRUE THEN BEGIN
      ItemLedgEntry2.SETRANGE("Job No.",ItemLedgEntry."Job No.");
      ItemLedgEntry2.SETRANGE("Job Task No.",
        ItemLedgEntry."Job Task No.");
      ...
    END;
    IF ItemTrackingCode."SN Specific Tracking" THEN
      ItemLedgEntry2.SETRANGE("Serial No.",
        ItemLedgEntry."Serial No.");
    IF ItemTrackingCode."Lot Specific Tracking" THEN
      ItemLedgEntry2.SETRANGE("Lot No.",ItemLedgEntry."Lot No.");

    IF Location.GET(ItemLedgEntry."Location Code") THEN
      IF Location."Use As In-Transit" THEN
        ItemLedgEntry2.SETRANGE("Transfer Order No.",
          ItemLedgEntry."Transfer Order No.");

    IF Item."Costing Method" = Item."Costing Method"::LIFO THEN
      EntryFindMethod := '+'
    ELSE
      EntryFindMethod := '-';
    IF NOT ItemLedgEntry2.FIND(EntryFindMethod) THEN
      EXIT;

实际的应用条目是在InsertApplEntry函数中创建的。

应用项目账目的要求

为了将项目账目条目应用到另一个项目账目条目,需要考虑某些要求。我们可以从 C/AL 代码中读取这些要求:

  • 项目编号在两个条目中应该是相同的。

  • 旧的项目账目条目应该是开放的。当一个项目账目条目完全应用后,布尔字段开放被设置为False

  • 变体代码和位置代码应该是相同的。

  • 布尔字段正数应该有一个相反的符号。这导致无法将一个负数条目应用于另一个负数条目。

其他要求基于系统设置的条件。例如,如果项目使用批号序列号,这也应该匹配。

当系统定义了过滤器时,它会尝试找到第一条记录。搜索方法取决于成本方法。如果成本方法是后进先出法(LIFO),系统将尝试在过滤器中找到最后一条记录。对于所有其他成本方法,它将找到第一条。

我们还可以看到,当使用批号时,应用程序和成本计算是在批号内完成的。

价值条目

在 Microsoft Dynamics NAV 中,库存的物理信息与财务信息分开存储。这些信息存储在一对多关系中,这意味着一个项目账簿条目可以有多个价值条目。

这使我们能够在时间维度和成本类型维度中详细指定价值信息。

直接成本

每个项目账簿条目至少以一个直接成本类型的价值条目开始。这定义了库存的初始价值。在库存生命周期内,项目账簿条目可以获取以下四种其他类型的价值条目:

  • 重估:当启动项目重估批次且项目的价值与直接成本不同时,使用此条目类型。

  • 舍入:有时,库存调整会导致舍入问题。舍入被存储为特殊条目类型以实现可追溯性。

  • 间接成本:当在项目卡片上使用间接成本百分比时,系统将为间接成本金额创建额外的价值条目。

  • 差异:当项目使用标准成本时,发票金额与标准成本之间的差异被保存为一种条目类型差异。

价值条目和总账条目

价值条目和总账条目通过 G/L - 项目账簿关系表(5823)相互链接。每个总账条目都链接到一个或多个价值条目。这实现了可追溯性,并有助于审计员分析系统。

调拨单

要将库存从一个位置移动到另一个位置,可以在项目日记账行中进行负数和正数的调整,但我们也可以使用调拨单,如下面的截图所示:

调拨单

调拨单为每个位置创建项目账簿条目,并维护价值条目的链接。

这意味着如果我们从蓝色位置移动 100 个物品到绿色位置,但尚未收到采购发票,当过账发票时,系统将为移动的库存创建价值条目。让我们尝试一个新的项目。

示例

我们将使用的项目是牛仔裤。第一步是按照以下方式创建项目:

  1. 我们只定义编号描述基本计量单位过账组

  2. 现在,我们在蓝色位置创建一个数量为 10 的新采购订单。

  3. 我们收到采购订单。

  4. 使用新的调拨订单,我们将库存从蓝色移动到红色

    这将导致五个项目账务分录和五个价值项,但由于我们尚未收到采购发票,总成本为零。

    示例

  5. 现在,我们创建一个新的采购发票并获取收货行。我们使用单位成本10示例

    这将导致原始项目账务分录的值项输入。

  6. 为了创建调拨的价值项,我们需要运行调整成本 - 项目分录报告(795)。这将导致所有项目账务分录都有相同的价值项:示例

需求工作表

对于贸易公司来说,拥有刚好足够的库存非常重要;既不多也不少。为了做到这一点,我们可以使用需求日记账以及项目上的重新订购策略。

重新订购策略

重新订购策略告诉系统如何计算项目订购的时机和数量。微软 Dynamics NAV 使用以下四种不同的重新订购策略:

  • 固定重新订购数量:每次我们运行需求日记账时,系统都将购买相同、固定的数量。此数量在重新订购数量字段中指定。

  • 最大数量:系统将购买尽可能多的项目以满足最大库存字段的值。

  • 订单:对于每个销售订单,将创建一个采购订单。这自动启用了此项目的预留过程。

  • 批次:此选项将计算交付未完成销售订单所需的必要库存。

数量是在代码单元库存配置偏移(99000854)中的CalcReorderQty函数中计算的。

扩展重新订购策略

微软 Dynamics NAV 中的订购策略算法非常静态,一些贸易公司需要更多的灵活性。

一个例子是季节性的,取决于天气。玩具店在圣诞节期间需要额外的库存,而园艺工具店在春季达到顶峰。在这些高峰期间,与一年中的其他时间相比,交货时间和可用性也不同。

虚拟库存

贸易公司的一个新兴趋势是虚拟库存。这是我们无法控制的库存,但可以出售给我们的客户。计算机行业经常使用这种方法。每个人都可以为计算机设备创建一个网站,并使用大型批发公司的库存。

注意

为了使这可行,信息应该是实时且可靠的。在微软 Dynamics NAV 中,我们可以通过使用 Web 服务来解决这个问题。

仓库管理

使用库存管理,我们可以使用位置来查看库存在哪里。对于一些贸易公司来说,这已经足够好了,但有些人希望更具体地了解物品在仓库中的位置。

对于此,我们可以使用 Microsoft Dynamics NAV 中的仓库管理系统WMS)功能。WMS 使我们能够指定每个位置内的区域和货位。

仓库管理中的另一个功能是将销售发货单和采购收货单合并到仓库文件中的可能性。使用这些文件,仓库员工可以同时为多个订单拣选或入库,从而实现更高效的物流方式。

仓库策略级别

仓库管理可以从非常简单到高度复杂地使用和实施。为了展示 Microsoft Dynamics NAV 中 WMS 的应用设计,我们将讨论以下五个可能的实施级别。对于每个级别,我们将展示表格和过账模型。

  • 货位码:在销售和采购单据中使用此字段可以使系统开始创建仓库条目。

  • 仓库收货和发货:这允许我们将销售发货单和采购收货单合并到一个仓库文件中。我们无法使用拣选和入库活动。

  • 仓库入库和拣选:对于每一张采购收货单或销售发货单,我们都可以创建一个入库或拣选日记账。

  • 仓库收货和发货 + 使用入库工作表:这允许我们实施真正的两步仓库流程,并在临时位置接收物品,并创建入库单据将物品移动到仓库的存储位置。

  • 定向入库和拣选:这是 Microsoft Dynamics NAV 中 WMS(仓库管理系统)功能的完整选项。我们使用收货单发货单入库单拣选单。Microsoft Dynamics NAV 将建议货位码。我们还可以使用区域中转码头等。

位置设置

位置表(14)中的设置选项启用或禁用 Microsoft Dynamics NAV 中的 WMS 选项。这是在仓库选项卡上完成的,如下面的截图所示。每个级别都需要一个特殊的设置组合。

位置设置

让我们看看不同的级别:

  • 第 1 级:启用货位强制

  • 第 2 级:启用接收、发货和货位强制

  • 第 3 级:启用入库、拣选和货位强制

  • 第 4 级:启用接收、发货、入库、拣选、货位强制和入库工作表使用

  • 第 5 级:启用接收、发货、入库、拣选、货位强制和定向入库和拣选

仓库员工

在我们开始之前,当前用户应被设置为仓库员工。

仓库员工

这可以通过在仓库员工表中创建一个新记录(7301)来完成。

注意

每个用户都可以在每个位置成为仓库员工,并且只能在他们被分配的位置执行仓库操作。

货位代码 | 第 1 级

实施 WMS 的起始级别是使用货位表。这是通过在位置上启用货位强制字段来完成的。货位代码字段在所有必要的表中都可用,例如采购行、销售行和项目日记账行。

当使用货位代码时,代码单元 Item Jnl.-Post Line (22)将创建一个仓库日记账行并启动 Whse. Jnl.-Register Line 代码单元(7301)。这将导致创建仓库条目(7312)和货位内容(7302)。

示例

我们将创建一个新的位置,ORANGE,并使用BIN1作为货位。该位置使用货位强制选项,如下面的截图所示:

示例

货位强制

在一个新的采购订单中,我们现在可以选择这个新的货位代码并过账收货。系统现在在货位内容表中创建一个新的记录,使我们能够查看创建的仓库条目

示例

货位内容

每次首次使用货位时,Microsoft Dynamics NAV 将创建一个货位内容记录。货位内容记录既不是主数据也不是账簿条目或文档。它是 Microsoft Dynamics NAV 哲学中的一种特殊类型的表。

处理货位内容的 C/AL 代码可以在代码单元 Whse. Jnl.-Register Line (7301)中找到。要查看在任何时刻用于项目的哪些货位,我们可以从项目卡打开货位内容,如下面的截图所示:

Bin 内容

可以通过单击数量(基础)字段来显示仓库条目。

收货和发货 | 第 2 级

当我们在位置上启用要求接收要求发货时,我们可以开始使用仓库收货和发货文档。这些文档允许我们在一份文档中接收或发货多个采购或销售订单。

让我们看看在应用程序中是如何操作的:

收货和发货 | 第 2 级

仓库请求

所有仓库文档都以仓库请求表(5765)的记录开始。这些记录在销售或采购文档发布时创建。

仓库收货或发货可以通过以下三种方式创建:

  • 使用从采购和销售订单的创建仓库收货创建仓库发货选项

  • 使用获取源文档报告(5723)

  • 使用仓库收货发货卡上的获取源文档选项

前两种选项将为每个销售或采购文档创建一个新的仓库文档。后者允许我们在一个仓库文档中合并订单。

仓库请求

局限性

仅使用仓库收据和发货单据基本上只是在销售和采购单据上增加了一层。过账程序Whse. Post Shipment (5763)Whse. Post Receipt (5760)实际上并没有将东西过账到仓库;它们只是将货位代码写回到销售行和采购行表中。技术上,这使用了与第 1 级相同的 C/AL 代码。

我们可以通过查看例如 Codeunit Whse. Post Receipt (5760)的InitSourceDocumentLines函数来了解这是如何完成的:

InitSourceDocumentLines
WhseRcptLine2.COPY(WhseRcptLine);
WITH WhseRcptLine2 DO BEGIN
  CASE "Source Type" OF
    DATABASE::"Purchase Line":
      BEGIN
        PurchLine.SETRANGE("Document Type","Source Subtype");
        PurchLine.SETRANGE("Document No.","Source No.");
        IF PurchLine.FIND('-') THEN
          REPEAT
              ...
              IF PurchLine."Bin Code" <> "Bin Code" THEN BEGIN
                PurchLine."Bin Code" := "Bin Code";
                ModifyLine := TRUE;
              END;
              ...
            IF ModifyLine THEN
              PurchLine.MODIFY;

当源表更新时,系统会使用代码单元销售-post (80)和 Purch. Post (90)创建一个正常的采购收据或销售发货。

搬运和拣选 | 第 3 级

除了创建仓库收据或发货单据外,我们还可以直接从销售或采购订单创建搬运或拣选。

要启用此功能,我们需要在位置卡片上激活要求搬运要求拣选选项。

搬运和拣选 | 第 3 级

仓库请求

仓库请求记录与第 2 级完全相同,但系统不是创建仓库收据或发货单,而是直接创建仓库活动头和行。

仓库活动

仓库活动头和行表是内部 Microsoft Dynamics NAV 仓库文档。有五种类型的仓库活动文档,如下所示:

  • 搬运:此文档用于将物品从收货货位移动到搬运货位。该文档由仓库收据生成。

  • 拣选:此文档用于将物品从存储货位移动到发货货位。该文档由仓库发货生成。

  • 移动:这是一个内部文档,用于在仓库内部移动物品。

  • Invt. Put-away:此文档用于接收物品并将它们直接放入仓库的永久货位。该文档由仓库请求创建。

  • Invt. Pick:此文档用于直接从仓库一步发货物品。该文档由仓库请求创建。

当只在位置上使用要求搬运要求拣选选项时,使用文档类型Invt. Put-AwayInvt. Pick。这将确保采购订单和销售订单将通过启动代码单元销售-post (80)和 Purch. Post (90)来处理。

第 2 级和第 3 级比较

第 2 级和第 3 级设置选项都是一步仓库实施。在接收物品时,我们必须提供物品将被存储的存储货位,直到它被售出。没有额外的步骤。

使用仓库收据和发货单据允许我们在一个仓库单据上合并销售和采购单据。这不能通过直接搬运和拣选来完成。使用直接搬运和拣选,我们可以将一个销售行或采购行拆分成多个货位。这不能通过仓库收据和发货单据来完成。

原因在于仓库入口的创建方式。第 2 级使用货位字段在项目日记账行中创建仓库入口。

使用第 3 级,仓库入口是通过代码单元 Whse.-Activity-Post(7324)创建的。货位代码不会写回销售行或采购行。这意味着我们也不能在采购收货单和销售发货单的货位代码字段中使用。

第 4 级 – 收货与上架工作表

大多数仓库使用两步收货和发货流程。第一步是在收货位置接收物品,这通常靠近卸货码头。然后,物品被存放在其仓库位置,直到需要用于生产或销售过程。这是第二步。

要启用此两步流程,我们可以通过在位置卡中使用要求接收发货上架和拣选 + 货位强制 + 使用上架工作表选项来组合第 2 级和第 3 级。

这允许我们首先执行第 2 级中讨论的仓库收货和发货。当我们处理此文档时,它不仅过账销售订单和采购订单,还会在仓库上架请求表(7324)中生成记录。

第 4 级 – 收货与上架工作表

仓库活动登记簿与仓库活动过账

当使用仓库工作表处理仓库上架请求时,它将导致仓库活动表头和行。在这种情况下,系统将使用我们在上一节关于第 3 级讨论的上架和拣选文档类型。

从技术上讲,第 3 级和第 4 级的文档是相同的,但存在以下两个差异:

  • 在两步仓库设置中,物品已经位于仓库入口。这意味着我们必须移动它们。这会导致出现两个新的仓库入口,同时在仓库文档中也有两条记录。

  • 两步仓库文档未过账,而是进行了登记。这意味着系统将只创建仓库入口,而不再更新销售和采购文档。

第 5 级 – 指导上架和拣选

将仓库收货和发货与上架和拣选结合起来,完成了 Microsoft Dynamics NAV 中 WMS 的表格和过账图。但还有其他选项可以丰富功能。

这些选项之一是指导上架和拣选。当此选项被激活时,系统可以并会帮助我们找到每个仓库活动的正确货位。

区域和默认货位

让我们从定义区域和默认货位开始。区域是一组货位。通常,它们在物理上彼此靠近,但更重要的是,它们共享一些属性。

对于每个区域,我们需要指定是否允许接收、发货、上架和拣选。这可以在货位类型列表中完成。

在定义货位时,建议使用逻辑名称,例如 R-01-001 用于收货行第一层第一个:

区域和默认货位

默认货位是在位置卡货位选项卡上设置的。这些货位可以在每个文档中随时更改。

货位计算

货位计算是在使用模板的拣货单文档中进行的。此模板定义了查找正确货位以存储项目的规则:

货位计算

查找选项存储在拣货模板行表(7308)中;它们如下所示:

  • 查找固定货位:系统将尝试找到固定的货位。固定货位通常为特定项目预留。

  • 查找浮动货位:这将尝试找到第一个可用的货位。

  • 查找相同项目:这将过滤出已经包含此项目的可用货位。

  • 查找单位匹配:如果仓库的部分区域设计为处理特定类型的载体,例如欧元或美国托盘,则可以使用此选项。

  • 查找低于最小库存量的货位:使用此选项查找未完全使用的货位。如果不与查找相同项目一起使用此选项,可能会导致同一货位中有两个项目。

  • 查找空货位:此选项将确保我们找到空货位。

处理货位计算的 C/AL 代码位于创建拣货单代码单元(7313)中。让我们看看:

Code()
IF Location."Directed Put-away and Pick" THEN BEGIN
  BinType.CreateBinTypeFilter(BinTypeFilter,2);
  REPEAT
    QtyToPutAwayBase := RemQtyToPutAwayBase;
    IF NOT (PutAwayTemplLine."Find Empty Bin" OR 
      PutAwayTemplLine."Find Floating Bin") OR
      PutAwayTemplLine."Find Fixed Bin" OR
      PutAwayTemplLine."Find Same Item" OR
      PutAwayTemplLine."Find Unit of Measure Match" OR
      PutAwayTemplLine."Find Bin w. Less than Min. Qty"
    THEN BEGIN
        //Calc Availability per Bin Content
        IF FindBinContent("Location Code","Item No.",
          "Variant Code",WarehouseClassCode) 
        THEN
          REPEAT
            ...
          UNTIL (BinContent.NEXT(-1) = 0) OR EverythingHandled
      END ELSE BEGIN

        //Calc Availability per Bin
        IF FindBin("Location Code",WarehouseClassCode) THEN
          REPEAT
            IF Bin.Code <> "Bin Code" THEN BEGIN
              ...
            END;
          UNTIL (Bin.NEXT(-1) = 0) OR EverythingHandled
      END
    UNTIL (PutAwayTemplLine.NEXT = 0) OR EverythingHandled;

对于拣货模板行表中的每条记录,系统将尝试找到货位。这意味着如果第一个模板行的规则失败,它将使用第二个模板行,依此类推。

两个选项“查找空货位”和“查找浮动货位”消除了使用其他选项的需要。如果这些选项为true,系统将调用FindBin函数。对于其他选项,它将使用FindBinContent函数。

实施和定制仓库管理

由于在 Microsoft Dynamics NAV 中设置 WMS 有多种方式,因此在实施开始时做出正确的决策非常重要。将系统从一个策略转移到另一个策略是一项相当大的挑战。

因此,讨论所有可能性并将它们与您公司的运作方式进行比较非常重要。

备注

在实施 WMS 软件时,一个常见的错误是试图用计算机系统解决程序问题。简单规则是:“如果没有计算机系统也能工作,那么使用计算机系统肯定也能工作”。

由于数据流非常复杂,特别是对于 Microsoft Dynamics NAV 标准,定制和更改 WMS 应非常谨慎。

预留

在 Microsoft Dynamics NAV 中,可以对库存进行预留。这可以帮助我们更有效地管理库存。让我们通过一个客户场景讨论预留过程。

我们的一位客户在 2015 年 1 月 22 日订购了 100 件黑色 M 码 T 恤。目前,我们库存中有 120 件,因此我们可以无任何问题地发货。客户希望他们在 11 月 18 日收到。我们输入了发货日期的销售订单并发布了订单。

第二天,另一位客户打电话要 40 件黑色 M 号 T 恤。我们的库存仍然是 120 件。这位客户希望在 5 月 31 日收到。我们输入销售订单时没有警告。最后,我们将为 90 件相同的 T 恤创建一个新的销售订单,交货日期为 7 月 25 日。现在,我们得到以下错误消息:

预订

如果我们现在回到第二个销售订单并重新输入数量,我们会得到一个类似的消息。

检查可用期计算

这种情况发生的原因在于 Microsoft Dynamics NAV 计算总需求的方式:

检查可用期计算

这是一个两步法,首先,需求计算到销售行的发货日期,其次,使用在公司信息表中定义的日期公式调用Lookahead函数。

用于计算Lookahead的 C/AL 代码可以在代码单元“可承诺”中的QtyAvailabletoPromise函数中找到(5790)。

QtyAvailabletoPromise
Item.CALCFIELDS(Inventory,"Reserved Qty. on Inventory");
ScheduledReceipt := CalcScheduledReceipt(Item);
GrossRequirement := CalcGrossRequirement(Item);

IF FORMAT(LookaheadDateFormula) <> '' THEN BEGIN
  GrossRequirement :=
    GrossRequirement +
    CalculateLookahead(
      Item,PeriodType,
      AvailabilityDate + 1,

如果这个Lookahead功能不够详细,我们可以开始使用预订流程。

始终与可选预订

预订选项可以在项目级别和客户级别激活,并可以设置为永不可选始终,如下面的截图所示:

始终与可选预订对比

让我们看看这些选项的含义:

  • 永远不:对此项或客户的预订是不可能的。如果该项被始终预订,而客户被永不预订,则该项获胜。

  • 可选:可以为该客户预订项目;然而,销售人员和国库员工可以决定取消预订。

  • 始终:没有适当的预订,无法发货。如果需求大于供应,销售人员和国库员工必须手动决定谁得到什么。

预订录入

Microsoft Dynamics NAV 使用预订录入(337)表来存储预订录入。预订录入可以连接到所有未结单据和日记账以及过账条目。这是通过以下源字段完成的:

  • 源类型:这是一个整数字段,表示记录链接到的表,例如,37 表示销售行,5406 表示生产订单行。

  • 源子类型:这是一个选项字段,当记录与销售行、采购行记录或生产订单的状态相关联时,它与文档类型字段相关联。

  • 源 ID:这是与记录所链接的文档编号的链接。

  • 源批次名称:如果记录与日记账相关联,则此字段表示日记账批次名称。如果使用此字段,则源 ID 为空,反之亦然。

  • 源生产订单行:当记录用于生产订单行或组件时,此字段表示生产订单行号。

  • 源参考编号:这是一个整数字段,用于将记录链接到文档、日记或生产组件中的行号。如果行与账目条目链接,则此字段表示条目编号字段。

在 Microsoft Dynamics NAV 中,有四种类型的预留条目,由预留状态字段表示:

  • 预留:这些是真实的预留条目,这意味着当前或未来的部分库存被预留用于生产订单或销售订单。如果商品使用始终预留选项,则无法绕过这一点。如果预留是可选的,则可能有人仍在其他过程中使用这些商品。

  • 跟踪:此选项由 Microsoft Dynamics NAV 中的订单跟踪策略选项使用。这是一个“水下”过程,可以自动链接供需。跟踪状态表示存在供应和需求。

  • 过剩:此选项用于讨论的第五章、“生产”和订单跟踪策略。可以通过使用项目跟踪字段的值来识别记录。对于订单跟踪策略记录,此设置为,对于项目跟踪,设置为序列号批号以及序列号

  • 潜在客户:当使用项目跟踪时,潜在客户预留记录表示内部日记操作,例如,将序列号分配给项目日记行。

创建预留

让我们进入应用程序并创建一个预留,以查看数据库中的条目。

我们将使用一个新的商品来完成这项操作。该商品应具有描述、基本计量单位和通用生产、增值税产品和库存过账组。预留的默认值为可选,我们将在此示例中使用它。默认的成本计算方法是 FIFO,我们也将使用它。

  1. 为了展示预留的实际价值,我们应该创建两个不同日期和单位成本的销售订单。使用 FIFO,系统通常会将销售订单应用于第一个项目账目条目。我们将对第二个项目账目条目进行预留,以展示对项目成本和应用的 影响。

  2. 当完成这项操作后,我们可以创建一个新的销售订单,包含一个销售行,其中包含商品和一半的库存,并从功能选项卡中选择预留

  3. 在此屏幕上,我们可以通过单击可预留来查看可用的库存:创建预留

  4. 在这里,我们选择第二个收货,导航到操作 | 功能 | 预留。然后,我们关闭屏幕。

  5. 销售订单行中,我们现在可以看到预留数量50创建预留

让我们通过从对象设计器运行表来查看数据库中创建的预留条目

创建预订

预订类型的预订条目总是使用相同条目编号的两行。第一个条目的源类型链接到销售行表(37),第二个使用项目日记账条目表(32)。

我们发货并开具销售订单的发票,查看我们项目的项目日记账条目

创建预订

我们可以看到微软 Dynamics NAV 使用了第二个项目日记账条目,但成本是500,而不是第二个条目中的600

为了纠正这个问题,我们运行了“调整成本 - 项目条目”(795)报告,并再次查看项目日记账条目价值条目,以确认已经进行了修正。

创建预订

订单跟踪策略

我们已经看到预订条目不仅用于库存和项目跟踪的预订过程,还用于平衡供需。这是微软 Dynamics NAV 内部的一个选项,允许我们内部链接库存。

这些条目用于供需计算以创建需求工作表。

示例

让我们复制我们的预订测试项目,以查看预订和项目跟踪之间的差异。这个新项目应该有订单跟踪策略跟踪和操作消息。

我们将创建两个数量均为50的采购订单,但不接收它们,并为相同的项目创建一个数量为80的销售订单。

示例

如果我们现在从销售行功能中选择订单跟踪,我们会看到系统匹配供需。

让我们看看预订条目

示例

我们可以看到微软 Dynamics NAV 现在使用过剩跟踪类型。我们剩余的 20 个项目没有与需求链接。

让我们开始为这个项目的需求工作表,看看微软 Dynamics NAV 能利用这些信息做什么。

补货

让我们将项目的重新订购策略改为批量,并为该项目运行需求工作表

补货

这将导致建议将两个采购订单合并为一个不同数量的文档。

补货

自动递增

在微软 Dynamics NAV 2013 中,预订条目表被重新设计,以使用自动递增功能来确定唯一编号。这提高了应用程序的性能并减少了锁定。

垂直行业交易

微软 Dynamics NAV 被用于许多不同的垂直行业,这些行业通常需要特定的功能。而不是试图在标准产品中实现所有这些功能,微软 Dynamics NAV 支持框架,并允许开发者设计和创建垂直功能。

对于这些功能,80/20 规则适用。Microsoft 提供了 80%的框架,这需要我们 20%的时间来实现。缺失的 20%的功能开发需要 80%的预算时间。

在本节中,我们将讨论 Microsoft Dynamics NAV 在五个不同垂直行业中的使用情况。对于每个行业,我们将讨论两个特定的垂直功能以及它们如何被解决。

注意

大多数行业都有由认证的 Microsoft Dynamics NAV 合作伙伴设计的可靠的附加解决方案,这些解决方案已在多个地点实施。强烈建议查看这些附加解决方案,而不是重新发明轮子并重写已经存在的附加组件。

时尚

时尚行业在季节中有贸易期。在春季,商店需要订购下一个冬季的系列,而在秋季,他们购买夏季服装。

销售订单

每个系列的销售订单都作为正常销售订单创建,但发货日期在将来,有时是六个月或更长时间。当使用变体时,应该为每个变体、尺寸和颜色创建单独的销售行。

对于销售人员来说,输入这些信息可能相当麻烦,因此我们可以使用模板销售行来加快主要物品的输入,并隐藏单个尺寸。

使用一个矩阵,其中x轴代表尺寸,y轴代表颜色,销售人员可以快速输入数量。当矩阵关闭时,我们可以更新隐藏的销售行。这些隐藏的销售行用于计算生产订单,如第五章中所述,生产

预订

当生产订单从工厂返回时,仓库和销售人员需要决定哪个客户先获得这些物品。这可以通过发货日期来完成,但如果一个客户提前下单,即提前六个月,而另一个客户下单太晚,但有更早或相同的发货日期,这可能并不完全公平。

这是我们开始使用预订的地方。预订已经支持变体,但 Microsoft Dynamics NAV 的自动预订功能可能并不完全符合我们的期望。

更改此功能是一个复杂任务。AutoReserve 的 C/AL 代码位于预订管理代码单元(99000845)中,但应由经验丰富的开发者进行更改。

幸运的是,预订是建立在正常库存、生产、采购和销售流程之上的。如果我们更改算法,我们可以移除当前的预订并重新测试代码,以查看新创建的预订条目是否良好。这个过程应该非常小心地进行,使用足够小的数据集以便使用 Microsoft Excel 进行分析。

汽车

许多汽车经销商公司和车库使用 Microsoft Dynamics NAV,因为该垂直行业有一些强大的附加产品可用。

在 Microsoft Dynamics NAV 支持的正常贸易流程之上,这些公司有额外的业务需求。让我们讨论其中两个。

车辆信息

每辆售出的车辆都需要进行配置和订购。配置应存储在数据库中,以供未来的维护和保修使用。

我们可以将此与标准产品中的序列号或批号信息表进行比较。我们可以创建一个新的主数据表称为车辆,并为每辆我们配置或销售的汽车在此表中创建一个记录。我们为车辆创建的编号可以用作项目账簿条目中的序列号。

对于维护,我们可以有一个车辆日志,每次汽车回来进行维修时,都会创建车辆账簿条目。这有助于我们跟踪历史记录,并应包括诸如里程数等信息。这个解决方案的技术设计可以与我们在第二章中创建的“Squash 应用”进行比较,一个示例应用

零件管理

在汽车行业,使用正确的部件至关重要。不同的部件可以用于不同类型的汽车,甚至往往是品牌无关的。

许多供应商以数字格式提供他们的产品组合,使我们能够与他们创建接口。应将零件定义为项目,使用标准功能,如替代品。由于许多零件可能价格昂贵且周转速度慢,因此保持库存可能非常昂贵,因此应保持最低库存。

零件可以连接到车辆类型。例如,汽车内部后视镜可以用于五种类型的汽车。当服务工程师想要更换这样的后视镜时,他/她可以使用所有可用零件的过滤项目列表。

药品/药品

在药店或其他药品供应商处,通常不仅允许每位客户购买任何商品。他们不能向健康人出售治疗心脏心律失常的药品。

即使某人被允许使用某种药品,通常也限于特定的剂量。人们通常为这些药品的保险费用投保,但大多数保险公司要求有自付费用。

药品卡

Microsoft Dynamics NAV 不支持项目监管。为了支持这一点,我们应该创建新的功能,将项目链接到客户,同时允许我们输入剂量和频率。

从这个模板中,我们可以定期创建销售订单和发货单。每次我们发货药品时,都需要更新这个模板。

自付费用发票

当客户需要支付部分药品作为自付费用时,我们需要系统为一份销售订单创建两个销售发票。

使用 Microsoft Dynamics NAV 中的标准预付款功能,这是可能的。我们可以向客户发送预付款发票,并使用合并开票处理向保险公司开具其他发票。预付款将从发票金额中自动扣除,但项目上的价值条目将保持完整。

然而,标准系统不允许我们向另一个“账单至客户编号”创建预付款发票。这需要设计和开发。

食品

当时尚公司每年有两个或三个大型订购时刻,客户会仔细考虑订购什么时,大多数食品公司都有大量日常订单流程。

此订购过程通常通过电话或传真完成,零售商打电话给呼叫中心员工,告诉他们第二天要发货的内容。

系列产品

大多数食品公司使用一系列产品。这个系列可能会随着季节变化而变化,或包含特殊行动项目,但通常很稳定,因为这是大多数消费者想要的;比如周一吃肉丸,周二吃猪排,等等。

为了节省宝贵的时间,每天创建具有相同项目的新的销售订单,我们可以在夜间让系统完成这项工作。

这可以通过使用标准客户销售代码来完成。Microsoft Dynamics NAV 中的此标准功能允许我们创建包含多个项目或其他由销售流程支持的主数据的模板销售订单。它还支持在创建销售订单时可以调整的固定数量。

可以使用 Get. Std. Cust Sales Codes 功能从客户销售代码创建销售订单,如下面的截图所示:

系列产品

销售订单创建

这个功能可以在 作业调度器 中安排,以便为第二天创建新的销售订单。我们将在第九章 Chapter 9. 接口中讨论作业调度器。

快速订单录入

当零售商联系呼叫中心完成销售订单时,订单录入人员应该能够快速找到正确的销售行。如果系列包含 150 个项目,这可能相当具有挑战性。

这可以通过实施快速订单录入功能来解决,该功能允许用户在销售抬头中输入项目编号和数量。这些值将在正确的销售行中更新,并为下一次输入留空。

使用此功能,最终用户始终可以从同一位置工作,无需搜索正确的销售行。

家具

家具贸易公司面临的问题与时尚公司类似,但有一些关键区别。

与服装相比,办公椅和桌子的选项要多得多,而且当销售时,大多数消费者购买的是具有相同规格的少量产品,而不是不同尺寸的集合。

变体配置

家具的价格取决于配置,比如我们想要哪种布料作为座椅,哪种扶手类型,或者甚至是轮子的类型。这种配置还决定了商品编号。

一把办公椅或一张桌子可以有高达 1200 种可能的组合。我们不希望将这些组合注册为商品或甚至作为变体。

大多数家具供应商提供在线系统或小型外部软件包来确定组合。一旦确定了组合,我们就可以创建一个新的变体代码,或者查看变体是否已经存在,然后创建销售订单。

一次性商品

家具零售商通常拥有许多可以销售的系列,包含数千种商品。这些系列中的大多数商品将永远不会被售出,或者仅作为一次性商品售出,仅向一位客户销售一次。

在这种情况下,创建一个带有商品成本和库存价值的商品没有意义,但我们仍然希望对商品有一些可追溯性。这需要两个解决方案:

  • 我们可以创建一个系列商品,每次我们销售一个与另一个商品相似但不完全相同的商品时,例如,一个脚部颜色不同的台灯,我们都可以重复使用这个商品。在销售行中,我们为销售人员创建了一个输入供应商/系列和商品类别的可能性。然后系统应该搜索模板商品。

  • 另一个解决方案是从销售订单中创建一个新的商品运行时。销售人员也将选择供应商/系列和商品类别,系统应该显示一个包含数据库中已有商品的列表。如果商品尚未创建,系统应使用帖子组模板创建商品。销售人员可以立即使用它,然后我们就有了对我们销售的商品的可追溯性。

摘要

通过本章,我们结束了探索微软 Dynamics NAV 中围绕商品的生产和贸易功能的旅程。我们讨论了微软 Dynamics NAV 中销售和采购文档的应用设计以及它们是如何相互映射的。我们讨论了不同的文档类型以及它们如何从报价或一揽子订单到订单、发票或退货订单,再到贷项通知单协同工作。

销售和采购行验证方法帮助我们通过一个与最终用户创建这些行的方式相链接的特殊函数结构来计算定价、库存和增值税。

销售和采购订单有一个强制性的发布流程,可以通过文档审批和预付款进行扩展。商品有两个层次的库存流程,使用位置上的商品账目条目和仓库中的托盘和区域条目。我们可以使用调拨订单将商品从一个位置移动到另一个位置,以及使用仓库文件将商品从一个托盘移动到另一个托盘,从一个区域移动到另一个区域。仓库设置在位置上,可以有不同的层级。设置层级应与仓库中的实际流程相匹配。

在 Microsoft Dynamics NAV 中,项目应用和成本核算与预订流程紧密相连。预订录入表为库存流程添加了一个新的层级,将文档、日记账和录入项联系起来,以平衡供需。当使用预订流程时,它可以覆盖成本核算方法。

在本章末尾,我们讨论了在垂直解决方案中实施的不同方法,以及需要解决哪些差距以及如何解决。这展示了 Microsoft Dynamics NAV 标准交易结构的灵活性和强大功能。

在下一章中,我们将将其提升到一个新的水平,我们将为 Microsoft Dynamics NAV 设计并构建一个真实的垂直解决方案,这将使我们能够为卡车路线创建组合销售发货。我们还将基于 Microsoft Dynamics NAV 创建一个新的解决方案,将应用程序作为开发环境,以构建与应用程序方法相关的新事物。

第七章。仓储与物流

在前面的章节中,我们探讨了公司在生产和贸易业务中如何使用 ERP。所有这些公司共同努力,将成品运送到零售店,最终消费者可以在那里购买。

在这个过程中,产品会在公司之间移动。这需要使用不同类型的运输工具,如卡车、船只、火车和飞机。可能还需要将产品存储在仓库中,直到它们被售出或运送到商店。

越来越多的公司决定外包物流而不是拥有自己的运输。在这种情况下,物流可以成为供应链的一个独立部分。本章讨论了这一过程及其对 ERP 系统的影响。

物流公司的特定方面之一是,他们处理的产品不是他们的财产。尽管它们是消费者产品总成本的一部分,但他们并不关心其库存的详细价值。物流公司将其仓库处理、存储和运输作为服务出售。

Microsoft Dynamics NAV 没有内置功能来处理这个问题,因此在本章中,我们将讨论如何设计一个应用程序来完成这项工作。

对于这项业务有几种附加解决方案,在现实世界中,这些附加组件应被视为潜在解决方案进行评估。在本章中,我们将讨论如何设计和创建一个基本框架,以便轻松扩展而不会增加太多复杂性。

注意

本章提供的对象不应在实际客户场景中实现。它们仅用于本章的示例目的。

阅读本章后,您将更好地了解如何设计一个稳固的附加解决方案,以及如何将其集成到标准的 Microsoft Dynamics NAV 产品中。

如何阅读本章

在本章中,我们将展示如何设计 Microsoft Dynamics NAV 的附加组件。在这个例子中,我们为一家仓储与物流公司创建了一个解决方案。这是因为它与 Microsoft Dynamics NAV 中现有的功能(仓储)相似,并且是构建在标准应用程序功能之上的好例子。

我们将首先分析业务流程,然后讨论为什么我们不会使用标准应用程序功能,并解释我们新应用程序将拥有的模块。

下一步是更深入地研究这些模块,并为每个模块定义设计模式。然后我们将像前几章一样遍历应用程序,并逆向工程它来解释所有部件是如何设计的。

要做到这一点,我们需要下载并安装应用程序。随着本章的深入,我们将讨论在 Microsoft Dynamics NAV 开发环境中可以打开和分析的大部分对象。

小贴士

随着我们在本章中的进展,打开对象以了解更多信息。这些对象功能丰富,本书无法详细讨论所有内容。

章节对象

在本章中,需要一些对象和动态链接库DLL)文件。附录

流程

为了为特定市场设计一个稳固的解决方案,我们首先需要分析业务流程,看看标准产品与流程的契合点和差距在哪里。

将使用此解决方案的公司是物流提供商。这些公司不买卖产品,而是销售物流服务,如运输和仓储。

在供应链中可能存在多个时刻需要这些公司。产品通常在全球各地的公司制造,然后运送到其他地方的消费者手中。产品可以覆盖很长的距离,如下面的图表所示:

流程

使用标准功能

与许多 ERP 系统一样,Microsoft Dynamics NAV 是为人们处理自己的产品而设计的,并支持我们在前几章中看到的成本核算过程。对于物流服务提供商来说,这种库存控制和估值功能不是必需的,因为产品不是他们的财产。这意味着他们可能希望使用仓库功能而不需要项目账簿条目,这在 Microsoft Dynamics NAV 中是非常困难的。

物流服务提供商还提供运输解决方案。他们将取走产品并将其交付给客户。这个过程包括在路线上结合不同的停靠点,从而实现更经济的运输方式。此功能在 Microsoft Dynamics NAV 中不可用。

定义模块

在本章中,我们将在 Microsoft Dynamics NAV 之上设计三个新的模块,这些模块相互集成,并且仍然可以单独使用。这些模块还通过销售与采购文档与标准应用程序集成。

定义模块

仓储

应用程序的第一部分是仓储模块。这使我们能够接收和发送产品,并在仓库内部移动它们。此模块的设计与我们在第六章中讨论的标准应用程序中的仓库文档非常相似,即贸易

物流

物流模块支持路线规划,将产品运送给消费者。这集成到存储模块中,但也可以从标准应用的销售发货文件中使用。

对于这个模块的设计,我们参考了在第五章“生产”中讨论的 Microsoft Dynamics NAV 生产订单。路线和发货有一个状态字段,表示进度,类似于生产订单。

开票

存储和运输服务将定期或当产品离开仓库时向客户开票。

为了做到这一点,我们将使用标准的 Microsoft Dynamics NAV 开票解决方案,但我们将在物流解决方案和开票功能之间添加一个新的收入与支出模块。

我们已经探讨了工作分录的设计以及它们的开票方式。这将在下一章中讨论。

存储应用

在一个仓储仓库中,产品时刻在进出。仓储公司与生产工厂的一个主要区别在于,仓储公司并不关心他们具体拥有哪些产品;他们关心的是存储所需的空间量。这个业务是销售存储处理、存储空间和运输服务。

对于我们的应用,我们假设我们的仓库有一个收货区域和一个发货区域,一个中间的临时区域和一个大批量存储区域。如果我们简化仓库,它可能看起来像以下平面图:

存储应用

让我们详细看看各个部分:

  • 收货单:当产品到达时,它们首先从卡车上卸载到收货区域。这通常位于卸货码头附近,以便卡车在产品卸载并检查装货文件后能快速移动到下一个目的地。从收货地点,产品应尽可能快地被储存起来,因为另一辆卡车可能即将到来,我们需要空间。现在产品可以前往临时区域或大批量区域。

  • 临时区域:临时区域是一个中间区域,产品可以在此储存,但一旦仓库过于繁忙,无法在大量区域妥善储存,且我们需要收货区域的空间时,产品会迅速离开仓库。

  • 发货:当产品离开仓库时,它们首先会被移动到发货区域。这使我们能够在卡车到达时快速装载,并轻松地将装货文件与实际产品进行比较。

  • 大批量存储:当我们预计产品将在仓库中停留较长时间时,它们将被储存在大批量区域,在那里我们可以定义货架。根据系统设置,一个货架可以容纳一个或多个产品。

文档

第一步是通过创建收据文档来登记将要进入我们仓库的内容。在以前的日子里,我们经常通过电话或传真接收这些信息,但今天大多数公司使用接口,如 EDI 和 Web 门户来处理。这使我们避免在系统中输入信息时出错,并允许我们自动填充收据文档。

收据文档将被组合成上架文档,以记录从一个区域到另一个区域的转移。软件还将建议一个货架来存储产品。

当产品离开仓库时,我们的客户也会注册一个出货文档。在他们调用时,我们将开始订单拣选过程并合并出货。拣选文档将告诉我们产品存储在哪个货架上。

顺便说一句,可能还需要在仓库内移动产品。这将在内部移动文档中记录。

存储文档与物流文档结构相关联,我们将在本章后面讨论物流时看到这一点。

看,学,爱

在第二章中,一个示例应用程序,我们学习了如何使用日记和条目设计模式来注册使用。在本章中,我们将继续这一做法并添加一些文档设计模式结构。

为了设计我们的应用程序,我们将查看现有 Microsoft Dynamics NAV 组件的设计方式,并重用它们。

小贴士

community.dynamics.com/nav/w/designpatterns/default.aspx,你可以找到数十种 Microsoft Dynamics NAV 设计模式,包括本章中使用的许多模式。

日记

我们应用程序的核心是存储日记,它是由与之前挤压日记相同的模板创建的。区别在于仓库工作人员使用文档而不是日记。

文档

我们将支持我们之前讨论的五种文档类型,即收据、出货、上架、拣选和移动。这些文档可以由最终用户手动创建,也可以自动创建。我们还将提供一个接口结构,允许客户注册收据和出货。

由于所有文档都具有相同的结构和大部分相同的字段,它们都位于同一张表中以共享业务逻辑。

小贴士

对于多种文档类型使用相同的表可以更容易地在应用程序中共享业务逻辑。

这也是在我们在第六章中讨论的标准 Microsoft Dynamics NAV 销售和采购文档应用程序中完成的,贸易

管理数据

为了定义我们在仓库中存储的内容,我们将使用一个名为Product的新表,它与标准系统中的 Item 表类似。通过创建一个新表,我们将提高我们解决方案的可升级性,这将帮助我们更好地控制自己的应用程序,换句话说,不太可能受到微软在标准产品中实施的变化的影响。

绘制设计模式

如果我们将这些信息组合成表格和事务结构,它将看起来像以下图表:

绘制设计模式

实际库存保存在Storage Entries中。通过筛选仓库代码、区域代码或货架号,可以计算库存。

共享表

Storage & Logistics附加应用程序也有一些共享表。为附加程序的每个部分创建产品或仓库表是没有意义的。我们选择共享用于角色中心定义的设置和提示表。Storage & Logistics 应用程序有四个角色中心。

提示

通过共享提示表,将相同的提示放置在不同的角色中心上要容易得多。如果我们为每个角色中心创建一个表,我们每次更改请求都需要复制和粘贴提示定义到表中。

入门

在我们的场景中,我们将为 CRONUS International Ltd.公司运送和接收产品,我们为该公司提供运输服务。我们在奥地利、比利时、捷克共和国、丹麦、德国、大不列颠、冰岛、荷兰、挪威、瑞典、斯洛文尼亚、斯洛伐克和美国都有仓库。

每个仓库都有与本章前面解释的基本布局相同。从仓库出发,我们规划路线将产品运送到消费者。初始化应用程序并重新启动应用程序后,角色中心应该看起来像这样:

入门

让我们详细看看各个部分:

  • 活动:此窗口显示仓库地面的工作流程

  • 我的产品:这包含我们库存中的所有客户产品

  • 我的区域:这使我们能够看到我们的仓库中库存在哪里

开启余额

开启余额是使用存储日记账创建的。通过使用日记账创建开启条目,我们确保遵循业务规则。

在我们的设计中,我们决定不允许最终用户直接在批量位置注册库存。我们首先接收它,然后创建一个上架单将其移动到批量位置。我们将在本章后面讨论存储单据时看到这是如何完成的。

注意

查看Storage & Logistics Setup (123456701)页面,了解这是如何在CreateOpeningBalance()函数中完成的。

产品

产品是我们客户所持有的物品的参考,我们将其保存在库存中。它们包含一个账单客户编号和一个客户物品编号。这使得我们能够,例如,为两个不同的客户保留编号为70000的物品。

我们还可以查看和设置此产品的存储价格,我们将在稍后用于开票。

产品

仓库

仓库是一个有地址的物理建筑。为了将产品从一个仓库移动到另一个仓库,我们需要将它们运走,创建一个路线,然后在另一个建筑中实际接收它们。

仓库

区域

区域是仓库的一部分,用于特定的存储活动。在我们的例子中,我们有收据、暂存、批量运输和装运区域。为了将产品从一个区域移动到另一个区域,我们应该创建一个上架、移动或拣选文件。

区域

货架

货架是区域的一个特定部分。货架的特定代码通常表示其在仓库中的位置。例如,我们的仓库有两排,A 和 B,每排有 18 行和 8 层,每个货架可以存放一个托盘。

注册工作表

仓库流程从接收产品开始。为了在产品抵达码头时节省时间,我们要求客户提前注册他们的产品。这是在存储注册工作表中完成的。

在我们的应用程序中,我们模拟了与客户 CRONUS International Ltd 的界面。我们可以直接从角色中心启动界面。

  1. 我们从角色中心启动CRONUS 存储导入收据报告,如下截图所示:注册工作表

  2. 系统弹出并要求输入存储注册代码

  3. 我们将从列表中选择CRONUS并开始导入过程。

  4. 在此之后,我们打开注册工作表注册工作表

  5. 当我们现在打开注册工作表时,我们看到 CRONUS 今天将发送给我们什么。这使我们能够准备我们的业务,可能移动一些产品,并安排资源。注册工作表

  6. 我们现在可以注册这个工作表,这将为我们创建收据文件。

存储文件

我们使用文件来确定哪个产品放在哪里。在系统中手动创建这些文件需要大量工作,因此在我们的应用程序中,这是自动完成的。

收据

默认情况下,所有接收到的产品都存储在收据区域。这个区域没有货架。如果需要,我们可以更改区域代码。

在我们注册收据文件后,我们在收据位置有库存:

收据

由于这是一个相对较小的区域,我们需要尽快将产品移动到批量位置。这是通过上架文件完成的。

上架

上架文档用于将产品从收货区域移动到批量区域。存储条目告诉我们收货区域有什么,因此我们将该信息复制到一个新的上架文档中。这些文档可以手动创建,并且基于文档上的仓库信息,我们可以将数据拉入文档。

另一个要求是有一个自动化的流程,根据收货区域的所有内容创建上架文档。

  1. 为了提供这项功能,我们创建了收货到上架(123456715)报告。这个仅处理报告读取收货区域的存储条目,并基于某些预定义的规则创建上架文档。上架

  2. 报告根据类型为收货和具有库存的存储条目进行筛选。上架

  3. 它为每个仓库创建一个上架文档,建议仓库中的第一个上架区域。对于每个StorageEntry,将启动CreateLine函数。让我们看看这个 C/AL 代码:

    CreateLineCreateLine()
    
    FindOrCreateStorageHdr;
    Region2.SETRANGE("Warehouse Code");
    Region2.SETRANGE("Put-Away", TRUE);
    Region2.FINDFIRST;
    
    WITH StorageEntry DO BEGIN
      NextLineNo := NextLineNo + 10000;
      StorageLn."Document Type" := StorageHdr."Document Type";
      StorageLn."Document No." := StorageHdr."No.";
      StorageLn."Line No." := NextLineNo;
      StorageLn."No." := "Product No.";
      StorageLn."Warehouse Code" := "Warehouse Code";
      StorageLn."Region Code" := Region2.Code;
    
  4. 第一步是检查是否需要创建一个新的存储文档。我们为每个WarehouseStorageDate创建一个新文档。

  5. 然后,系统通过区域表进行筛选,以找到上架区域。对于每个StorageEntry,创建一个StorageLine

  6. 运行报告后,我们的上架文档看起来像这样:上架

  7. 建议的区域代码BULK,而应用区域代码RECEIPT

  8. 如果我们现在尝试注册此文档,我们将收到错误,因为我们没有输入任何货架,因为在这个区域这是强制性的。

    注意

    此检查在Storage Jnl.-Check Line代码单元中完成。通过将这些检查移动到这个代码单元,我们确保这些规则在每个过账中都是强制性的。

  9. 由于我们依赖系统来跟踪我们的库存,我们还可以让它为我们建议可用的货架。这也是通过批量报告123456716 生成上架货架完成的。让我们设计这个报告,并查看StorageLineDataItem中的 C/AL 代码:

    Storage Line - OnAfterGetRecord()
    
    Counter := Counter + 1;
    Window.UPDATE(1,"Document No.");
    Window.UPDATE(2,ROUND(Counter / CounterTotal * 10000,1));
    
    Shelf.SETRANGE("Warehouse Code", "Storage Line"."Warehouse Code");
    Shelf.SETRANGE("Region Code", "Region Code");
    Shelf.SETRANGE(Inventory, 0);
    Shelf.SETRANGE("Blocked by Storage", FALSE);
    Shelf.FINDFIRST;
    
    "Shelf No." := Shelf."No.";
    MODIFY;
    
  10. 对于上架文档中的每个StorageLine,它通过基于InventoryBlockedByStorage的可用性进行筛选,找到另一个货架。

  11. BlockedByStorage字段是一个返回值为真的流程字段,如果货架上使用了仓库文档,将阻止两台叉车停在同一个货架上。

  12. 当执行此报告时,我们可以注册此上架文档,并可以看到使用总账分录操作从产品卡片生成的存储条目上架

在这里,我们可以看到上架文档已将其条目应用于收货条目。由于我们移动了所有东西,原始条目已关闭,剩余数量设置为零。

此功能与我们创建在第二章中类似,即当将发票条目应用于预订时。

运输

一段时间后,当产品存放在库存中,客户可能会发送请求要求运输。运输文档使用与收据文档相同的界面发送。

运输

CRONUS 存储导入运输选项

运行CRONUS 存储导入运输报告将创建存储登记表,我们可以检查并将其注册到运输文档,就像之前收据文档那样。

系统为每个收货地址创建一个运输文档。

运输

现在,我们必须开始将产品从存储区域移动到运输区域的过程。

挑选

将要运输的产品需要使用挑选文档从散装区或预存区挑选。与上架功能一样,我们的应用程序设计提供了一个支持此过程的自动化流程。

创建文档时,我们使用批处理报告 123456717 挑选,如下截图所示:

挑选

此报告可以将运输合并为一个或多个挑选文档:

Storage Line - OnAfterGetRecord()

Counter := Counter + 1;
Window.UPDATE(1,"Document No.");
Window.UPDATE(2,ROUND(Counter / CounterTotal * 10000,1));

Product.GET("No.");
Product.SETRANGE("Warehouse Filter Code", "Warehouse Code");
Product.CALCFIELDS(Inventory);
IF Quantity > Product.Inventory THEN
  ERROR(Text001, Quantity, Product.Inventory, "No.");

QtyToPick := Quantity;

StorageEntry.SETCURRENTKEY("Product No.");
StorageEntry.SETRANGE("Warehouse Code", "Storage Header"."Warehouse Code");
StorageEntry.SETRANGE("Product No.", "No.");
StorageEntry.SETRANGE(Open, TRUE);
IF StorageEntry.FINDSET(TRUE) THEN REPEAT
  StorageEntry.CALCFIELDS("Blocked by Storage");
  IF NOT StorageEntry."Blocked by Storage" THEN BEGIN
    IF QtyToPick >= StorageEntry.Quantity THEN
      QtyToPick := QtyToPick - StorageEntry.Quantity
    ELSE BEGIN
      StorageEntry.Quantity := QtyToPick;
      QtyToPick := 0;
    END;
    CreateLine(StorageEntry);
  END;
UNTIL (StorageEntry.NEXT = 0) OR (QtyToPick = 0);

IF QtyToPick > 0 THEN
  ERROR(Text002, "No.");

首先,系统检查产品是否存放在这个仓库中。如果在这里,它开始浏览存储条目以寻找可用的货架。在这里,我们也使用由存储流程阻塞字段来避免两名员工争夺同一产品。

我们应用程序中的一个功能需求是避免有一半的运输需要挑选并阻止运输区域不完整。如果挑选可用库存不足,系统将显示错误。

挑选创建后,我们更新运输单上的挑选状态字段。在以下截图中,我们可以看到有三个挑选行附加到这个运输单上:

挑选

当我们点击3时,系统打开行。双击行将打开挑选文档。

小贴士

要影响双击事件,将RETURN快捷键分配到页面上的一个操作。

在挑选文档注册后,运输状态变为完全挑选。我们可以看到挑选行已注册:

挑选

在可以注册运输之前,最后一步是使用挑选文档中的应用至存储条目编号更新存储行。为此步骤,我们设计了一个专门的报告更新存储运输(123456718),可以从存储运输文档启动,如下截图所示:

挑选

在此之后,可以注册运输。产品现在已经离开我们的仓库,正在前往客户的路上。

物流应用程序

类似于标准应用程序中的生产订单,我们物流应用程序中的流程是状态驱动的,而不是交易驱动的。这就是为什么这个应用程序部分没有条目日记。表格可以有存档副本,但它们不是正常注册或过账常规的一部分。

注意

对于本章本部分的示例,我们应该将默认角色中心更改为物流角色中心(123456700),在配置文件表(2000000072)中。

绘制设计模式

如果我们查看物流应用程序的结构,我们可以看到典型的过账交易是缺失的。该应用程序使用基于在表格触发器中定义的事件的状态驱动工作流程。

绘制设计模式

物流运输和运输详情与仓库的运输有很多相似之处。我们选择将它们移动到新表格中的原因如下:

  • 安全性:在 Microsoft dynamics NAV 中,表格级别对于安全性来说最为重要。如果我们共享这个表格,将无法设置用户以访问物流而不访问仓库或反之亦然。

  • 锁定:如果两个部门使用同一表格进行不同的目的,它们很可能有不同的锁定机制。例如,在物流中,运输单绑定到路线对象。仓库运输单绑定到其他运输文档。在主流程中以不同方式过滤同一表格将显著增加阻塞和死锁的概率。

  • 表格大小:存储文档在创建后不久就会被注册。大多数文档在创建当天就会被删除并移动到注册的表格中。物流运输的周期较长。将产品从我们的仓库运送到客户那里需要更长的时间,在这个过程中,由于外部事件,许多事情可能会出错。运输表格可能会像制造或标准 Microsoft Dynamics NAV 产品中的作业一样定期清理。

开始

要开始物流流程,我们可以手动创建一些运输单,但应用程序也提供了对销售运输单和仓库运输单的接口。

让我们从物流角色中心活动中的合并运输(销售)选项开始,生成一些可以工作的数据。

运输

物流运输是产品从一个物理地址移动到另一个物理地址。

在我们的示例中,运输单是从我们的仓库到客户那里创建的,但运输单也可以是从另一个地址到客户。跟踪运输单的状态对于计划者来说非常重要。一旦所有必填字段都被检查,运输单就会立即进入准备发货状态。

当运输组合成路线时,运输状态变为运输。在此阶段,产品将从仓库中取出。当发生这种情况时,取货日期时间将被填写。这是从路线中完成的。

交付后,交付日期时间被填写,状态设置为已发货

运输

规划者可以遵循他们在角色中心中的工作流程来跟踪运输。

路线

运输被组合成路线。对于规划者进行产品规划,确保运输详情正确非常重要。产品的长度、宽度、高度和重量决定了它们是否可以装入卡车、船只、飞机或火车。

我们的示例附加系统有一个报告将运输组合成路线。如果路线中的运输具有相同的地址信息,它们将被组合成停靠点。

组合运输

组合运输在运输至路线和仓库(123456701)报告中完成。运输按仓库分组。对于每个仓库,都会创建一个新的路线。

对于每个运输,系统创建一个路线停靠点。停靠点有不同的类型,取货、交付、取货分组和交付分组。然后每个运输都会得到一个取货和交付停靠点:

Shipment - OnAfterGetRecord()

IF Route.Description <> Warehouse.Name THEN BEGIN
  Route."No." := '';
  Route.Description := Warehouse.Name;
  Route."Shipment Date" := WORKDATE;
  Route.Status := Route.Status::Planned;

  Route."Bill-to Customer No." := "Bill-to Customer No.";
  Route."Bill-to Name" := "Bill-to Name";

  Route.INSERT(TRUE);
  i := 0;
END;

i := i + 10000;

RouteStop."Route No." := Route."No.";
RouteStop."Line No." := i;
RouteStop.Type := RouteStop.Type::Pickup;
RouteStop.VALIDATE("Shipment No.", "No.");
RouteStop.INSERT;

i := i + 10000;

RouteStop."Route No." := Route."No.";
RouteStop."Line No." := i;
RouteStop.Type := RouteStop.Type::Delivery;
RouteStop.VALIDATE("Shipment No.", "No.");
RouteStop.INSERT;

在创建路线并将运输分配到停靠点后,启动了一个分组和优化算法。这是代码单元路径优化器(123456700)

路径优化器

我们示例中的算法旨在通过计算每个地址与仓库的距离来找到将产品运送到地址的最佳路线。路线从距离我们仓库最近的地址开始,并以距离最远的地址结束。

这只是一个简单算法的示例。每个公司都将有自己的算法需要实现:

RouteStopPickup.SETRANGE("Route No.", Route."No.");
RouteStopPickup.SETRANGE(Type, RouteStopPickup.Type::Pickup);
RouteStopPickup.FINDFIRST;

RouteStopDelivery.SETRANGE("Route No.", Route."No.");
RouteStopDelivery.SETRANGE(Type, RouteStopDelivery.Type::Delivery);
RouteStopDelivery.FINDSET;
REPEAT
  Window.UPDATE(2, RouteStopDelivery."Shipment No.");

  IF NOT Optimizer.GET(RouteStopDelivery.Name) THEN BEGIN
    CLEAR(BingMapMgt);
    BingMapMgt.CalculateRoute('', RouteStopPickup.Latitude, RouteStopPickup.Longitude,'', RouteStopDelivery.Latitude,
      RouteStopDelivery.Longitude, Optimizer."Distance (Distance)",Optimizer."Activity Time", Optimize::Distance);

    Optimizer.Name := RouteStopDelivery.Name;
    Optimizer.Latitude := RouteStopDelivery.Latitude;
    Optimizer.Longitude := RouteStopDelivery.Longitude;
    Optimizer.INSERT;
  END;

UNTIL RouteStopDelivery.NEXT = 0;

距离的计算是通过调用 Bing Maps 的 Web 服务来完成的。这已在第九章接口中解释。

每个距离都存储为记录到优化器表中,这是一个辅助表。在这个代码单元中,这是一个临时变量。

临时表具有多项优势,使它们的使用变得有趣。由于它们不存储在数据库中,与真实表相比,它们具有更好的性能。这也对并发性有益,因为不会有锁定。

小贴士

临时表可以自由使用。在使用时,它们不会被检查在许可文件中。要创建和修改定义,仍然需要一个有效的许可。视频www.youtube.com/watch?v=QHn5oEOJv0Q展示了如何使用临时数据集。

在生成距离后,所有取货运输都被组合成一个停靠点,通过将它们分配到相同的序列号值:

RouteStopGroup.INIT;
RouteStopGroup."Route No." := Route."No.";
RouteStopGroup."Line No." := 10;
RouteStopGroup.Type := RouteStopGroup.Type::"Pickup Group";
RouteStopGroup."Sequence No." := 10;
RouteStopGroup.Name := RouteStopPickup.Name;
RouteStopGroup.INSERT;

RouteStopPickup.MODIFYALL("Sequence No.", 10);

通过对距离辅助表按距离排序,我们可以轻松地为配送站点分配正确的序列号。对于每个序列号值,我们还会在站点表中生成一个分组记录:

Optimizer.SETCURRENTKEY("Distance (Distance)");
Optimizer.ASCENDING(FALSE);
Optimizer.FIND('-');
REPEAT
  RouteStopGroup.INIT;
  RouteStopGroup."Route No." := Route."No.";
  RouteStopGroup."Line No." := Sequence;
  RouteStopGroup.Type := 
    RouteStopGroup.Type::"Delivery Group";
  RouteStopGroup."Sequence No." := Sequence;
  RouteStopGroup.Name := Optimizer.Name;
  RouteStopGroup.INSERT;

  RouteStopDelivery.SETRANGE(Name, Optimizer.Name);
  RouteStopDelivery.MODIFYALL("Sequence No.", Sequence);

  Sequence := Sequence + 10;
  IF (xLongitude <> Optimizer.Longitude) OR 
    (xLatitude <> Optimizer.Latitude) 
  THEN BEGIN
    IF xLongitude + xLatitude <> 0 THEN BEGIN
      CLEAR(BingMapMgt);
      BingMapMgt.CalculateRoute('', xLatitude, xLongitude,'', 
        Optimizer.Latitude, Optimizer.Longitude, 
        RouteStopGroup.Distance, RouteStopGroup.Time, 
        Optimize::Distance);
      RouteStopGroup.MODIFY;
    END;
    xLongitude := Optimizer.Longitude;
    xLatitude := Optimizer.Latitude;
  END;
UNTIL Optimizer.NEXT = 0;

在优化路线后,它应该看起来像以下截图所示。我们在仓库取走两批货物,并将它们运送到国家内的两个地址。

路线优化器

路线跟进

在路线过程中,规划师需要跟踪司机。这将导致货件状态更新。

在我们的解决方案中,规划师应填写完成日期时间字段。此字段在货件中使用流程字段自动更新。

事件

货件的特殊状态是事件。如果由于任何原因我们无法交付货件,它应该被送回仓库并再次发货。根据事件的原因,我们可能需要开票额外服务。

事件可以发生在停泊组或单个货件上,并且可以具有不可交付已关闭其他状态。规划师可以添加额外注释。

事件

没有事件的其他货件将获得新的状态,而事件将移动到角色中心的另一个位置。

事件

跟进

规划师可以通过角色中心跟踪事件。未处理的事件,保持状态开放,直到有人决定如何处理它。

跟进

发票应用程序

在第二章中,一个示例应用,我们介绍了附加解决方案的发票。对于本章的解决方案,我们将更进一步。

我们公司开票不同的物流服务,例如:

  • 存储收货和发货的处理成本

  • 我们保存库存期间的存储成本

  • 将产品运输到最终消费者的运输成本

所有这些成本都需要合并到一张发票中。一些客户可能需要每月开票或每周开票,对于偶然的客户,我们直接开票。这需要一个特殊的模块来处理开票。

注意

对于本章本节中的示例,默认角色中心配置文件表(2000000072)中应更改为收入与支出角色中心(123456761)

让我们看看需要开具发票的过程:

发票应用程序

收入和支出

我们希望一次性向客户开票的所有内容,我们存储在一个新的表中,我们将称之为收入与支出。这是一个容器,它们将在这里保存,直到为该客户完成定期开票。

收入与支出记录可以由最终用户手动创建或由系统自动创建。让我们看看它们:

收入和支出

要创建新的收入与支出记录,我们需要填写以下字段:

  • 收入与支出代码:这是指收入与支出组。

  • 类型:这可以是收入或支出。前者将用于销售发票,后者预留用于未来在采购发票上使用,如果我们决定雇佣其他公司来处理我们的物流。

  • 描述:这是将在销售发票上打印的描述。

  • 数量:这是我们完成的服务数量。例如,存储天数或路线中的公里数或英里数。

  • 单位成本/总价:这可以用来计算服务的利润。

  • 单价/总价:这是客户将在销售发票上看到的单价。

  • 计量单位代码:这是指计算方法,如BOXKMMILESDAY

  • 适用文档子类型:这是指存储表头、已注册存储表头、物流运输或物流路线。如有必要,这可以扩展以容纳其他附加组件。

  • 适用文档(行)编号:这是指该收入与支出记录所属的存储和物流文档。

  • 适用过账编号:这是指存储发票过账。

开票

在创建收入与支出后,我们可以开始开票流程。为此,在微软 Dynamics NAV 的开票部分进行了一些小的更改,例如,我们选择了一种与第二章,“一个示例应用”略有不同的方法。

销售行

销售行表(37)进行了一些小的修改。我们为收入添加了一个额外的类型,并为编号字段实现了表关系:

销售行

这使我们能够在不首先创建收入与支出的情况下,在销售发票上创建新条目。

销售行还引用了收入与支出过账编号应用至字段。这使得我们能够在销售过账代码单元中创建收入与支出日记账行

代码单元销售过账(80)

销售过账代码单元只有一处更改以填充收入与支出日记账:

OnRun()

        ...
        SalesLine.Type::Income:          //* Chapter 7
          PostIncome;

PostIncome()

IF SalesLine."Qty. to Invoice" = 0 THEN 
  EXIT;

WITH IncExpJnlLn DO BEGIN
  INIT;
  "Posting Date" := "Posting Date";
  ...
  "Source Code" := SrcCode;
  "Posting No. Series" := "Posting No. Series";
  "Dimension Set ID" := SalesLine."Dimension Set ID";
  IncExpJnlPostLine.RunWithCheck(IncExpJnlLn);
END;

这是以与资源日记账相同的方式进行,然而,我们将创建日记账行的代码移动到了一个函数中,这提高了我们代码的可读性和可升级性。

小贴士

由于销售行已填充了所有过账组金额字段,标准应用程序将自动生成总账分录、增值税分录和客户账簿分录。

定价方法

我们的可选解决方案有三个级别的自动价格计算,这在很大程度上是相同的。我们可以为存储文档、物流运输和路线计算价格。

让我们以存储价格为例,看看这是如何操作的。

存储价格

存储价格表中,我们可以为不同的存储活动注册价格。

存储价格

当价格计算完成后,系统将在这个表中筛选以找到最佳匹配的价格。例如,如果一个产品有一个没有仓库代码的收据价格,这个价格将在所有仓库中使用,但如果填充了一个仓库代码,这个仓库将有特殊的价格。

价格可以区分成收据、发货、拣选、上架、移动和存储。前几种选项用于存储文件,后几种则在计算存储成本时使用。

收入与支出代码决定了将为这种组合创建哪种类型的收入与支出。存储文件可以包含多个收入与支出,例如,一个正常的收据行和一个海关盈余。

计算

收入与支出是使用价格计算管理代码单元创建的,这是我们熟悉从第二章,“一个示例应用”中,但这次我们不会更新单价,而是创建收入与支出。

存储的计算是在代码单元 123456710 中完成的:

FindStorageLinePrice

WITH StorageLine DO BEGIN
  Product.GET("No.");
  StorageLinePriceExists(StorageHeader, StorageLine);
  CreateIncExp(StorageHeader,StorageLine,TempStoragePrice);

END;

FindStorageLinePrice函数将调用标准的StorageLinePriceExists函数来找到符合标准的存储价格。对于过滤器中的所有存储价格,它将调用CreateIncExp函数:

CreateIncExp()

IncExp.SETRANGE("Applies-to Document Type", IncExp."Applies-to Document Type"::"Storage Header");
IncExp.SETRANGE("Applies-to Document No.", StorageHeader."No.");
IncExp.SETRANGE("Applies-to Document Line No.", StorageLine."Line No.");
IncExp.DELETEALL;

WITH StoragePrice DO BEGIN
  FoundStoragePrice := FINDSET;
  IF FoundStoragePrice THEN BEGIN
    REPEAT
      IncExpCode.GET(StoragePrice."Income & Expense Code");
      IncExp.INIT;
      IncExp."Entry No." := 0;           //* For Autoincrement
      IncExp.Type := IncExpCode.Type;
      IncExp."Income & Expense Code" := 
        "Income & Expense Code";
      IncExp.Description := Description;
      IncExp.Quantity := StorageLine.Quantity;
      IncExp."Unit Cost" := IncExpCode."Unit Cost";
      IncExp."Total Cost" := IncExp.Quantity * 
        IncExp."Unit Cost";
      IncExp."Unit Price" := StoragePrice."Unit Price";
      IncExp."Total Price" := IncExp.Quantity * 
        IncExp."Unit Price";
      IncExp."Applies-to Document Type" := 
        IncExp."Applies-to Document Type"::"Storage Header";
      IncExp."Applies-to Document No." := StorageHeader."No.";
      IncExp."Applies-to Document Line No." := 
        StorageLine."Line No.";
      IncExp."Bill-to Customer No." := 
        StorageHeader."Bill-to Customer No.";
      IncExp."Gen. Prod. Posting Group" := 
        IncExpCode."Gen. Prod. Posting Group";
      IncExp."VAT Prod. Posting Group" := 
        IncExpCode."VAT Prod. Posting Group";
      IncExp.INSERT;
    UNTIL NEXT = 0;
  END;
END;

每个价格将创建一个单独的收入与支出记录。

提示

收入与支出表设置为自动递增。这意味着 SQL Server 将为我们生成条目编号。这使得多个用户可以同时在此表中生成条目而不会相互阻塞。

结果

当系统或最终用户生成新文件时,价格将自动计算。用户可以在事实盒中看到总成本和价格,并在必要时更改、删除或添加记录,如下面的截图所示:

结果

定期开票

我们提供的一项服务是存储。这意味着有时产品可能在我们仓库中存放几天甚至几周或几个月。我们的客户将根据他们使用我们仓库空间的时间进行收费。

每次我们在仓库接收产品或将产品移动到另一个区域或货架时,都会创建一个存储条目以跟踪。对于开票,我们也会创建存储发票条目。这主要是因为库存处理和开票是由不同的人在不同的时间完成的。当我们开始开票流程时,产品可以发货给客户。

存储发票条目使用从存储条目的存储日期继承的从存储日期创建。存储发票条目还有一个到存储日期,保持空白,直到产品离开仓库或移动到可能具有另一个价格的其他位置。收入和支出代码确定将开票的价格,并在发布存储单据时确定。

批处理报告存储开票(123456703)用于创建收入和支出。让我们看看这是如何完成的。

周期性开票

报告只有一个存储发票条目 DataItem,它根据Open=Yes进行筛选。

在报告中,所有存储发票条目首先被移动到缓冲表,然后稍后处理。实施此类解决方案有两个重要原因:

  • 更改记录集:此报告根据存储发票条目进行筛选,这些条目可以开票。当存储发票条目完全开票后,我们希望更改此值。这意味着我们使用的记录集在处理过程中正在改变。这是 SQL Server 后端无法处理的事情,这将导致非常差劲的性能。通过首先将所有记录移动到缓冲表,筛选将在维护在服务层而不是 SQL Server 上的虚拟表上进行。

  • 锁定:如果我们根据开放条目进行筛选并修改我们的数据集,这将导致锁定比必要的更多记录。在非聚集索引上筛选将导致 SQL Server 转到范围锁定而不是行锁定。通过使用聚集索引逐个读取实际的存储发票条目,我们将确保 SQL Server 只锁定我们用于此过程的记录,允许其他用户在此表末尾继续创建新记录。

处理缓冲区

在处理缓冲区时,我们首先检查此条目是否之前已经开过票。如果是这样,我们从上一个日期开始开票;如果不是,我们使用从存储日期。

然后,我们检查产品是否已经离开仓库或已被移动。如果是这样,我们可以通过开票直到这个日期来关闭此条目;否则,我们将开票直到工作日。

小贴士

用户可以更改系统的工日期并以此方式影响系统的行为,开票直到另一个日期。

ProcessBuffer()

StorageInvEntry.LOCKTABLE;

WITH TempStorageInvEntry DO
  IF FIND('-') THEN REPEAT
    StorageInvEntry.GET("Entry No.");

    IF "Last Invoice Date" <> 0D THEN
      FromDate := "Last Invoice Date"
    ELSE
      FromDate := "From Storage Date";

    IF "To Storage Date" <> 0D THEN
      StorageInvEntry."Last Invoice Date" := "To Storage Date"
    ELSE
      StorageInvEntry."Last Invoice Date" := WORKDATE;

    Date.SETRANGE("Period Type", Date."Period Type"::Datum);
    Date.SETRANGE("Period No.", 1, 5);
    Date.SETRANGE("Period Start", FromDate, 
      StorageInvEntry."Last Invoice Date");
    IncExp."Entry No." := 0;
    IncExp."Income & Expense Code" := "Income & Expense Code";
    IncExp.Type := IncExp.Type::Income;
    IncExp.Description := STRSUBSTNO(Text000, FromDate, 
      StorageInvEntry."Last Invoice Date");
    IncExp.Quantity := Date.COUNT;
    IncExp."Unit Cost" := "Unit Cost";
    IncExp."Total Cost" := IncExp.Quantity * "Total Cost";
    IncExp."Unit Price" := "Unit Price";
    IncExp."Total Price" := IncExp.Quantity * "Unit Price";
    IncExp."Global Dimension 1 Code" := 
      "Global Dimension 1 Code";
    IncExp."Global Dimension 2 Code" := 
      "Global Dimension 2 Code";
    IncExp."Bill-to Customer No." := "Bill-to Customer No.";
    IncExpCode.GET(IncExp."Income & Expense Code");
    IncExp."Gen. Prod. Posting Group" := 
      IncExpCode."Gen. Prod. Posting Group";
    IncExp."VAT Prod. Posting Group" := 
      IncExpCode."VAT Prod. Posting Group";
    IncExp."Unit of Measure Code" := 
       IncExpCode."Unit of Measure Code";
    IncExp."Applies-to Entry No." := "Entry No.";
    IncExp.INSERT;

    StorageInvEntry.Open := "To Storage Date" <> 0D;
    StorageInvEntry.MODIFY;
  UNTIL NEXT = 0;

我们代码中的下一步是计算两个日期之间的工作日数。这将防止我们的客户为周六和周日支付存储费用。我们通过使用虚拟日期表来完成这项工作。此表包含从公元 0000 年 1 月 1 日到 9999 年 12 月 31 日的所有日期、周、月、季度和年份,并在日期计算中非常有用。

使用这个结果,我们现在可以创建稍后将要开票的收入和支出记录。如果到存储日期已填写,我们将关闭存储发票条目。

组合开票

我们使用的数据库模型允许我们将我们对客户提供的所有服务进行开票。我们可以为客户创建一张包含处理、存储和运输成本的发票。

这是通过批量报告 123456704 合并仓储与物流来完成的,该报告与第二章中的报告完全相同,一个示例应用

附加组件灵活性

本章中我们创建的附加组件绝对还没有准备好供真正的公司使用,但它展示了如何创建一个可以很容易被他人扩展的灵活解决方案。

大多数现代物流服务提供商向客户提供其他服务,例如增值物流、项目跟踪和第三方及第四方物流。

增值物流

当一家公司提供增值物流服务时,他们不仅保留产品在库存中,还提供围绕这一服务的其他服务,例如展示包装。

这可以最好地与微软 Dynamics NAV 中的制造过程相比较。一个称为物料清单的项目列表被组合成一个新的产品。然后,这个新产品被发送给客户。

当展示不再需要时,例如,当营销活动结束时,展示需要从客户那里取回并拆卸成原始产品。

增值物流

在我们的解决方案中,这可以通过创建一个 VAL 区域来实现,产品将被移动到该区域。

项目跟踪

我们的客户还想知道他们产品的位置,它们在哪个仓库,以及哪个产品被发送给了哪个客户。这在食品和医药行业中尤为重要,以便在出现问题的情况下能够召回大量产品。

要在我们的解决方案中实现这一点,需要一些更改。首先,我们需要在库存条目中实现跟踪代码,其次,当我们将产品发送出我们的仓库时,我们需要实现某种跟踪条目,因为我们的物流解决方案目前没有任何条目,只有状态字段。

项目跟踪

第三方和第四方物流

在我们的示例数据库中,我们计划在路线上安排运输,并使用我们自己的卡车将货物运送到最终客户。这被称为第二方物流。如果我们要用我们自己的卡车处理我们自己的产品,那么第一方物流将是这种情况。

下图显示了如果外包并合并,物流的日益复杂化:

第三方和第四方物流

如果我们提供第三方物流,我们将使用其他公司为我们客户提供部分服务。然后我们将告诉他们要处理服务的哪一部分,并在完成后向我们报告。涉及的第三方不知道整个交易的所有细节。

如果我们提供第三方物流服务,我们将把整个仓库或路线外包给另一家公司。我们只会告诉他们哪些产品应该移动到何处,他们会处理这些事情,而无需我们知道细节。

很常见,第三和第四方物流是混合使用的,但它们通常由不同公司之间的接口来处理。

摘要

在本章中,我们与之前章节相比,从完全不同的角度审视了 Microsoft Dynamics NAV 产品。

目标并不是为 Microsoft Dynamics NAV 设计一个坚不可摧的存储和物流附加解决方案,因为这需要远超过一个章节的内容。本章中的信息旨在展示如何在 Microsoft Dynamics NAV 之上集成新功能。我们分析了业务流程,设计了新的数据和事务模型来处理这些流程,并在产品中实现了这些。

为了我们的解决方案,我们设计了两种新的文档结构和两种新的日记和条目结构。我们通过创建一个易于扩展的框架,紧密遵循 Microsoft Dynamics NAV 的标准方法。我们还花了一些时间研究如何防止数据库中的不必要的锁定以及如何避免更改筛选后的数据集。

最后,我们查看了一些示例,说明我们的附加解决方案如何被增强以更好地满足市场上的其他需求。

本章并未结束。本章提供的 C/AL 对象可以用来研究,以便更好地理解各个部分是如何组合在一起的。

在下一章中,我们将设计一个在 Microsoft Dynamics NAV 中的应用程序。我们将探讨它如何被用于咨询公司,使用工作模块并扩展新功能以满足特定需求。

第八章. 咨询

在本章中,我们将学习微软 Dynamics NAV 如何适应咨询公司。大多数咨询公司都有与项目相关的流程。他们承担需要一定时间才能完成的大型项目。一些咨询公司还购买和销售物品。

对于项目,咨询公司需要跟踪使用的资源和物品。有时他们可以开票他们一对一花费的资源小时,但大多数时候他们也会承担固定价格项目的风险。在这种情况下,了解项目是否预算得当,并确保资金不会在途中损失,就更加重要。

有许多类型的公司以这种方式运作。例如,会计公司和律师事务所,但也有很多建筑公司也是这样运作的。

对于本章,我们将使用一个例子,一个我们所有人都非常熟悉的公司,无论是作为员工、客户,甚至可能是所有者。我们将研究微软 Dynamics NAV 合作伙伴的商业流程。

在我们的案例中,合作伙伴销售微软 Dynamics NAV 的新项目许可证。他们还帮助现有客户进行升级和支持。最后,他们还销售基础设施解决方案,内部组装服务器和桌面系统。

我们将讨论四种不同的项目场景,并看看微软 Dynamics NAV 如何被用来支持这些场景。为此,我们将沿途进行一些修改。本章所需的对象可以从http://ftp.packtpub.com/chapter8.fob下载。阅读本章后,您将很好地理解微软 Dynamics NAV 中作业模块的可能性和局限性,它如何与产品其他部分相匹配,以及如何安全地扩展。

流程

微软 Dynamics NAV 合作伙伴的两个主要流程是实施新项目和为现有客户提供支持、升级等服务。第三个流程是销售基础设施和组装计算机系统,但这是一种额外服务,而不是核心业务。

为了支持项目(工作),公司需要人员、软件许可证和硬件。人员(资源)需要在项目上仔细规划,因为它们是公司最不灵活的部分。硬件(项目)和软件许可证(总账账户)将从像微软这样的供应商处购买。

项目可以分为大型项目和小型项目。大型项目通常是新的实施和升级。小型项目通常是实施小型功能并帮助用户解决常规支持问题。

开票可以以各种方式完成。新的实施和小型项目可以按计费小时计费,而升级则以固定价格出售。对于硬件,我们将使用项目。许可证直接开票到总账。

大型项目也有需要维护的预算和规划。如果预算已完全使用,且规划里程碑尚未达到,则应创建一个新的预算以完成项目。

为了支持这一过程,我们将使用带有一些定制的作业功能。在 Microsoft Dynamics NAV 中,项目被称为作业,因此我们将从现在开始使用这个术语。

注意

微软为第五版完全重新设计了作业模块。在本章中,我们将使用许多新功能,在旧版本的 Microsoft Dynamics NAV 中,我们会进行定制。

适配

作业模块的注册可以使用 Microsoft Dynamics NAV 的标准功能以及预算和规划来完成。

标准软件还允许我们按固定价格和时间材料开票作业。我们还可以为我们的作业购买物品。在 Microsoft Dynamics NAV 2013 中,应用程序中增加了一个时间表模块,我们将在此章节中使用并解释它。

空缺

Microsoft Dynamics NAV 中的作业模块通常被引用为一个几乎总是需要一些更改的框架。幸运的是,它被设计得很容易更改,我们将这样做以支持我们的流程。

资源组

虽然许多公司是这样工作的,但为资源组进行预算是不可能的。我们将为此创建一个解决方案。我们还将使其能够查看计划、使用和开票的总小时数。

项目计算

我们将创建一个计算系统组装的解决方案。由于硬件规格变化非常快,我们不希望为可能只销售一次或两次的系统创建新项目。

问题登记

我们的支持团队需要一个单一的地方来登记所有客户的所有支持问题,并跟踪其工作流程。为此,我们还将创建登记和跟踪问题的功能。

入门

在我们开始创建任何新的作业之前,我们应该查看以下 Microsoft Dynamics NAV 作业模块的数据和发布模型:

入门

起始点是包含工作任务工作计划行的工作表,我们可以用它们进行预算和规划。每个工作都可以有自己的价格。

工作计划行通过标准的 Microsoft Dynamics NAV 销售功能进行开票,然后创建工作分录

有多少作业

第一步是设置一个新的作业。设置作业可能有不同的角度。这取决于我们如何使用系统。最低要求是每个客户账单至少有一个作业。这使我们能够进行开票。一些公司就是这样使用作业的,将其用作预开票引擎。

另一个角度可以是为客户做的每个项目设置新的工作。在我们的案例中,这始于基本的 Dynamics NAV 实施。当这项工作完成时,我们关闭工作。如果客户有任何新的要求,我们需要启动一个新的工作。这样我们可以更好地跟踪每个客户未解决的问题。这种方法的不利之处在于,每次都需要做一些工作来设置新的工作。

大多数公司最终会采取中间方案。为较大的工作设置新工作,并为支持问题设置工作是很常见的。这也允许我们为每个工作设置不同的计费策略。我们将使用这种策略。

工作卡

让我们来看看工作卡和其中重要的字段:

工作卡

让我们更详细地看看这些字段:

  • 编号:这是工作的唯一编号。我们可以为此使用不同的编号系列策略,从简单的顺序编号到为不同工作类型或手动编号的链接编号系列。

  • 描述:此字段应包含对工作内部使用的逻辑描述。大多数人会在这个字段中搜索,所以请确保有某些命名规则。这将使未来搜索旧工作更容易。

  • 开票客户编号:每个工作有一个开票客户。如果我们想为一个工作向多个客户开票,我们需要定制应用程序。

  • 搜索描述:默认情况下,此字段将填充描述字段的值,但在需要的情况下可以更改为其他值。

  • 负责人:这是一个信息字段,指示谁负责这项工作。

  • 已锁定:如果此字段被勾选,则无法为此工作创建新条目。我们使用此字段用于关闭的工作。

  • 工作发布组:这指的是用于在制品WIP)过账的 G/L 账户。不同类型的工作或 WIP 方法可能有不同的 G/L 账户。

  • 在制品方法:每个工作可以有一个在制品方法。我们将在本章稍后简要讨论这一点。

  • 状态:工作有一组有限的状态字段。唯一可用的状态值是计划报价订单完成

    小贴士

    大多数公司希望订单阶段有更多的子状态。为此,最佳方法是添加一个新的状态字段,使其与标准状态字段相映射。这需要最小化对应用程序的更改,同时创建新的工作流程可能性。

  • 允许计划/合同行:如果此字段未被勾选,则无法创建具有计划合同选项的计划行。当创建计划行时,它们将被拆分为计划和合同行。

  • 开始日期结束日期:这些是信息字段,仅用于计算该工作的货币汇率。

  • 外贸:在“工作”模块中,可以以不同于本地货币的其他货币发送计算和创建发票。这将增加实施的复杂性,应谨慎使用。

工作任务和计划行

当创建工作后,下一步是创建工作任务和计划行。它们可以用不同的方式使用。

使用工作任务行,我们可以将工作分解成更小的部分,然后进行调度和计费。工作任务越详细,我们就能更好地衡量工作的进度。但维护这些任务所需的工作量也会更多。平衡是成功的关键词。

工作任务和计划行

创建工作任务和计划行

工作任务可以与账户表具有相同的结构,这意味着实际的任务行可以使用开始和结束总计行进行分组。每个级别都可以缩进以提高可读性。

工作计划行是每个工作任务的具体行。这定义了我们将要做什么以及如何计费。工作计划行可以链接到主数据类型资源、项目、总账科目或文本。

小贴士

工作任务和工作计划行可以非常容易地从其他工作中复制。这使我们能够重用它们,甚至为常用组合创建模板工作。

工作计划线中的线型定义了其计费方式。共有三种类型:

  • 计划:此行上的金额仅用于预算目的。在计费时,我们需要过账一个或多个将要计费的作业日志行,或者我们可以创建另一个带有发票金额的工作计划行。计划行应在按时间和材料计费时使用。

  • 合同:此行将以确切金额进行计费。然而,这些金额不会显示在预算中。当在计划中计费固定价格的工作时,例如,在签订合同时支付 50%,在完成工作后支付 50%。

  • 计划和合同:此行将以与合同行完全相同的方式进行计费,但金额也会显示在预算中。

工作日志

当工作任务和工作计划行设置完成后,我们可以开始工作。在工作过程中,我们将消耗公司资源和物品。这应使用工作日志进行登记。工作日志是我们第一章中制定的日记账分录图的最底层,Microsoft Dynamics NAV 简介,并使用其他日记账创建资源、项目和总账分录。

在创建工作日志行时,以下几个字段对于流程特别重要:

工作日志

让我们更详细地看看这些字段:

  • 行类型:它具有与作业计划行计划、合同计划和合同都有的相同的选项。当作业日记账行应该开具发票时,类型应该是合同。当作业日记账行是固定价格的一部分时,行类型应留空。然后行类型是计划,系统将创建此类型的额外作业计划行,这可能会破坏我们为客户的预算,因为它们已经被创建。

  • 单位成本和单位价格:这些字段将确定作业的成本以及如果行类型是合同,将向客户开具发票的价格。这些信息也用于在制品的计算。

作业示例

让我们通过一些不同的作业场景来了解我们如何使用这项功能。

章节对象

章节对象包含我们将在本章中讨论的更改以及我们将使用的示例作业。按照附录中所述,导入chapter8.fob后,运行页面 123.456.700 作业附加设置并运行初始化应用程序

章节对象

当此操作完成后,请重新启动角色定制客户端。现在你应该能看到项目管理角色中心。

新的实施

实施 Microsoft Dynamics NAV 2013 不是一个容易的任务,在我们可以使用该产品之前,有许多事情需要处理。我们将为 Packt Publishing 实施 Microsoft Dynamics NAV。本例中的任务是EXAMPLE1

对于实施,我们将创建各种工作任务组。实施过程中的每一部分都会分配一个代码。由于排序是基于这个字段进行的,我们将使用数字和逻辑名称创建代码,例如,0200\. SETUP0210\. FIN

小贴士

在数字中留出足够的空间,以便在需要时添加额外的行。这将避免重命名,这对于数据库引擎来说是一个昂贵的任务,用户将不得不等待它完成。

我们的顾问将帮助客户安装系统,协助设置,并将旧系统的数据转换过来。当这一切完成后,我们将帮助他们进行测试,并使用 Microsoft Dynamics NAV 进行培训。顾问将在系统中作为资源设置,这些资源随后将被输入到作业计划行中。

当一切按预期工作,我们可以安排一个上线周末,并帮助他们使用系统。

新的实施

对此类工作进行发票是通过预算完成的。我们将预先计算我们认为必要的工时数量,并从那里开始。在作业过程中,我们需要测量使用的预算,并将其与进度进行比较。

预算

预算是通过作业计划行创建的。在这个工作的这个阶段,我们还不知道哪个资源将被用于作业任务,甚至可能由多个资源完成。这就是为什么我们想在预算中使用资源组。

注意

这在标准应用程序中是不可能的,因此我们创建了一个修改,我们将在本章末尾讨论。

预算

这些作业计划行的 行类型计划。这意味着这些行仅用于预算和计划目的。系统将根据作业日记账中过账的实际消耗开具发票。

基础设施

要使用 Microsoft Dynamics NAV 2013,Packt 出版公司需要一个新的基础设施。他们的现有系统不符合 Microsoft Dynamics NAV 2013 的要求。

对于这个工作,我们可以在实施作业中创建新的作业任务行,但为了更清晰的概述,我们将创建一个新的作业,EXAMPLE2

我们的公司开发和销售自己的计算机系统。我们可以构建服务器和台式机系统。由于没有系统是完全相同且可用组件经常更换,我们不希望为每个系统创建一个项目和物料清单。相反,我们使用一个计算系统,我们将它作为定制添加到 Dynamics NAV 中,这样我们就可以确定一个系统的价格。对于像开关、路由器、打印机和笔记本电脑等其他产品,我们使用从供应商处购买的物品。

这个工作的作业任务和作业计划行看起来是这样的:

基础设施

作业任务和作业计划行

在这个工作中,安装成本是 资源组,行类型为计划,就像上一个工作一样,所以我们根据作业的实际小时数开具发票。

其他行是 计划和合同 类型。这意味着我们将根据预算开具发票。这些任务的作业日记账行应以空白行类型进行过账。

升级

我们的客户要求从 Navision 版本 3.70 升级到 Microsoft Dynamics NAV 2013。我们可以以固定价格完成这项工作,但我们需要收取分析系统的费用。

对于 EXAMPLE3 任务,我们可以从有限的作业任务行开始,仅用于报价。当客户同意进行升级时,我们可以添加新的作业任务行。

报价和升级都是固定价格,并直接过账到总账。这并不意味着我们不能让我们的资源使用作业日记账登记实际小时数,但 行类型 应该为空白。

升级的一部分不是以固定价格完成的。系统需要重新设计,转换为 SQL Server 2014,并且客户希望获得额外的培训和支援。

升级

升级和重新设计

升级中的固定价格部分分三个阶段开具发票。当工作开始时,我们开具 50%的发票,当我们交付测试系统时,我们开具 40%的发票,最后在上线三个月后开具 10%的发票。

这是通过计划和合同行类型的使用完成的。

支持团队

对于支持团队,我们的政策是每位客户每年创建一个工作。我们将使用此工作,EXAMPLE4,来开具许可证维护和支持问题的发票。

支持问题可以是客户向我们打电话的小问题,如更改报告或页面,或者实施只需要几天工作的新功能。

每个问题和新功能都将作为一个工作任务行创建。新功能将由销售该功能的账户经理创建。然后我们可以决定是否以固定价格、使用合同行或按时间和材料使用计划行进行开具发票。

我们的支持团队也需要使用工作系统,但我们不希望他们为每个支持电话手动创建一个新的工作任务行,我们还想让他们轻松查看所有客户的未决问题。为此,我们创建了一个新的问题登记系统,我们将在本章末尾讨论。

系统中的每个问题都与一个工作任务相关联。当支持工程师创建一个新问题时,将自动为他们生成工作任务行,他们可以在我们的时间和计费系统中使用它。

时间表

对于我们示例中的所有工作,拥有坚实的资源小时登记至关重要。在标准的 Microsoft Dynamics NAV 工作应用中,资源需要为每个工作、工作任务和过账日期的组合过账工作日记,或者我们可以使用在 2013 版中引入的新时间表应用。

小贴士

msdn.microsoft.com/en-us/library/hh175112(v=nav.71).aspx的 MSDN 页面上,您可以找到有关如何在 Microsoft Dynamics NAV 中设置和使用时间表的更多信息。

设计模式

时间表应用位于资源日记行之上,并使用资源和工作任务创建。

设计模式

对于负责工作的人员有一个审批流程,允许他们进行更正。

设计模式

时间表是为每周创建的。时间表是通过一个过程生成的,而不是由用户生成。之后,资源可以为每个工作任务行创建时间表行并填写每周每天的小时数。

设计模式

如果我们查看前面的时间表,我们可以看到在它更新后,周三缺少 2 小时。

采购

对于某些工作,可能有必要为该工作专门采购项目。在 Microsoft Dynamics NAV 2013 中,工作模块与我们在第五章中查看的请购单工作表、生产和第六章中查看的贸易模块集成。然而,在这个例子中,我们将像这样手动创建采购订单:

采购

当为工作采购时,应使用工作编号工作任务编号字段。如果我们将工作行类型设置为合同,此项目将被开具给客户发票。通常,这不需要,因为它应该已经在工作计划行中。

项目成本与在制品比较

在过账此采购单据并从采购发票导航后,我们可以看到系统已为此项目创建了两个价值条目:

项目成本与在制品比较

这对于我们在前几章讨论的成本计算非常重要。

注意

为工作采购的项目不计入库存,而是用于在制品的计算。

开票

当我们工作中的所有内容都按照要求设置好,并使用工作日记来过账使用情况时,创建发票是一项简单的任务。

在工作管理角色中心,我们可以查看是否有工作需要开具发票。这是通过在工作计划行计划日期字段上使用流程过滤器来完成的。

开票

每个工作计划行都有一个计划日期。这可以用来安排我们的资源,但对于发票安排来说更有用。每个准备开具发票的工作计划行应该在计划日期字段中获得发票日期。

然后,可以使用批量报告工作创建销售发票(1093)来创建发票,但也可以使用报告工作建议开票(1011)来预览发票。此报告可以从每个工作开始。

开票

工作建议开票

销售发票行使用与工作计划行相同的描述创建。为了在发票上向客户澄清信息,我们可以使用额外的文本行。

当创建并发送发票时,工作任务将更新为实际的发票信息。

在制品计算

由于大多数工作不是在一天或一周内完成的,因此及时了解每个工作的状态很重要。这可以通过数量和财务来衡量。在数量上,我们可以通过查看工作任务页面来了解预算的使用情况。对于财务进度,我们可以计算在制品。

在制品计算我们在工作中产生的成本和开具的发票,并为这些创建工作在制品条目。如果需要,这些可以过账到总账。在制品金额取决于在制品方法。

在 Microsoft Dynamics NAV 中,在制品的计算基于在“工作在制品方法”表中设置的组合成本和销售。

让我们创建一个示例并计算五种示例方法的在制品(WIP)。

代码 识别成本 识别销售
COMPLETED CONTRACT 完成时 完成时
销售成本 销售成本 合同(发票价格)
COST VALUE 成本价值 合同(发票价格)
PERC. OF COMPLETION 使用(总成本) 完成百分比
SALES VALUE 使用(总成本) 销售价值

我们创建了一个总价格为 1000、总成本为 500 的工作。我们使用了价值 500 的 4 个资源小时,成本为 250。我们没有开具任何发票。

识别成本 识别销售 在制品成本 在制品销售
成本价值 125 0 125 0
销售价值 250 250 0 250
销售成本 250
完成百分比 250 250 0 250
完成的合同 250

现在,我们向客户开具了花费的小时数的发票。我们开具了 500 的发票。

识别成本 识别销售 在制品成本 在制品销售
成本价值 375 500 -125 0
销售价值 250 250 0 -250
销售成本 500 500 -250
完成百分比 250 250 0 -250
完成的合同 250 -500

在最后一个示例中,我们将使用一个成本为 250 但我们无法开具发票的项目。现在我们有 500 的成本和 500 的销售。

识别成本 识别销售 在制品成本 在制品销售
成本价值 500 500 0 0
销售价值 250 250 0 -250
销售成本 500 500 0
完成百分比 500 500 0 0
完成的合同 500 -500

当在制品(WIP)为正值时,意味着我们完成的工作量超过了已开具发票的工作量。当在制品(WIP)为负值时,意味着我们开具的发票金额超过了已完成的工作量。

每个使用 Microsoft Dynamics NAV 的公司都应自行决定使用哪种在制品(WIP)方法。在制品(WIP)方法可以针对每个工作项目变化,甚至在工作过程中变化。

在制品过账到总账

一些会计人员希望将在制品金额过账到总账。这样做的好处是将所有财务信息集中在一个地方,以便更容易进行报告。

在制品过账到总账的 G/L 账户已在工作过账组中设置。当将在制品过账到总账时,总会有一个冲销过账。当公司进行月度报告时,在制品在月底最后一天过账,并在下个月的第一天进行冲销。

更改工作

在本章中,我们使用了一些对工作功能性的更改,以便使其适用于 CRONUS 国际有限公司销售 Microsoft Dynamics NAV。

数量预算

对于一些公司来说,了解一项工作所需的总小时数和使用的小时数比确切的金额更为重要。

为了这个目的,我们在工作任务表中创建了新的流程字段:

数量预算

流场定义非常特殊。

Sum("Job Planning Line"."Quantity (Base)" 
  WHERE (Job No. = FIELD(Job No.),
         Job Task No. = FIELD(Job Task No.),
 Job Task No. = FIELD(FILTER(Totaling)),
         Contract Line = CONST(Yes),
         Planning Date = FIELD(Planning Date Filter)))

数量预算

总计字段用于 End-Total 类型的行。ValueIsFilter 属性确保该字段将被解释为过滤器而不是值。

结果在作业任务页面(1002)中可见。

数量预算

ValueIsFilter 属性的结果

资源组

对于调度,我们在 Job Planning Lines 以及 Calculations 中实现了使用资源组的可能性。这是通过添加两个新字段,Add-on TypeAdd-on No. 来实现的:

资源组

这些字段替换了页面上的标准 TypeNo. 字段,使用户可以选择这些新选项。新字段的标题与替换字段相匹配。

Add-on No. - OnValidate()
CASE "Add-on Type" OF
  "Add-on Type"::Resource, "Add-on Type"::Item, "Add-on Type"::"G/L Account", "Add-on Type"::Text:
    BEGIN
      VALIDATE(Type, "Add-on Type");
      VALIDATE("No.", "Add-on No.");
    END;
  "Add-on Type"::"Resource Group":
    BEGIN
      TESTFIELD("Line Type", "Line Type"::Schedule);
      VALIDATE(Type, Type::Text);
      VALIDATE("No.", '');
      ResGroup.GET("Add-on No.");
      Description := ResGroup.Name;
      "Resource Group No." := ResGroup."No.";
      GetJob;
      ResCost.SETRANGE(Type, 
        ResPrice.Type::"Group(Resource)");
      ResCost.SETRANGE(Code, ResGroup."No.");
      IF ResCost.FINDFIRST THEN BEGIN
        "Unit Cost" := ROUND(
            CurrExchRate.ExchangeAmtLCYToFCY(
              "Currency Date","Currency Code",
              ResCost."Unit Cost","Currency Factor"),
            UnitAmountRoundingPrecision);

在 C/AL 代码中,我们可以确保当用户选择标准产品中可用的值时,执行正常代码。如果用户选择资源组,我们执行自己的业务逻辑。

为了确保一切按预期工作,我们在后台使用 Text 类型。Line Type 设置为 Schedule,因为我们不希望对资源组进行开票,我们只想进行预算。

Unit CostUnit Price 是使用支持资源组使用的资源成本和资源价格表计算的,这是从版本 5.0 之前的作业功能继承而来的。

作业计划列表页面(1007)已更改,以显示我们的附加字段而不是正常字段。

要完全完成这个功能,我们还需要更改显示作业计划行和创建作业日记行时创建作业计划行的 C/AL 代码。这在本章的示例代码中没有完成。

计算

一些使用作业功能的公司需要灵活的计算。在我们的示例中,我们用它来计算计算机系统的价格,但其他示例还有书籍出版商或建筑公司。

他们想知道在不确切知道使用了哪些螺丝、铰链或刨花板颜色的情况下,创建一个产品的成本是多少。对于这些公司,我们设计了一个简单但有效的计算模块。

在我们的数据库中,有两个示例计算:服务器和台式系统。

计算

计算是通过使用编号系列和行号进行标题/行构建设计的。计算行是项目。

当创建一个新的计算时,一些行会自动插入。这是在 OnInsert 触发器中调用的 C/AL 函数中完成的。

OnInsert 触发器还将从我们的设置表中复制 Hours 的默认 Unit Price

OnInsert()
CalcSetup.GET;

IF "No." = '' THEN BEGIN
  CalcSetup.TESTFIELD("Calculation Nos.");
  NoSeriesMgt.InitSeries(CalcSetup."Calculation Nos.",xRec."No. Series",0D,"No.","No. Series");
END;

"Unit Price Hours (LCY)" := CalcSetup."Unit Price Hours";
InitLines;

InitLines 函数为每个标记为 Calculation Item 的项目创建一个计算行。这是一个我们添加到项目表中的新字段:

InitLines()
CalcLn.RESET;

i := 0;
Item.SETRANGE("Calculation Item", TRUE);
IF Item.FINDSET THEN REPEAT
  i += 10000;
  CalcLn."Calculation No." := "No.";
  CalcLn."Line No." := i;
  CalcLn.VALIDATE("Item No.", Item."No.");
  CalcLn.INSERT;
UNTIL Item.NEXT = 0;

在计算中,我们可以选择从每个项目使用多少,系统将计算成本和价格,同时也会计算所需的工时数量。单位成本单位价格来自项目表。工时是从一个新字段计算得出的,我们在项目表中也添加了分钟

Calculate()
CalcLn.RESET;
CalcLn.SETRANGE("Calculation No.","No.");
CalcLn.CALCSUMS("Unit Cost", "Unit Price", Profit, Hours);
CalcLn.FIND('-');
CalcLn.MODIFYALL(Changed,Calculated::Calculated);

CalcLn.CALCSUMS("Unit Cost", "Unit Price", Hours);

"Unit Cost" := CalcLn."Unit Cost";
"Unit Price" := CalcLn."Unit Price";
Profit := "Unit Price" - "Unit Cost";
Hours := CalcLn.Hours;

Correct;
"Total Price Hours (LCY)" := "Hours (After Correction)" * "Unit Price Hours (LCY)";
"Total Price" := "Total Price Hours (LCY)" + 
  "Unit Price (After Correction)";
Calculated := Calculated::Calculated;
MODIFY;

Correct()
"Unit Price (After Correction)" := "Unit Price" + ("Unit Price" * ("Correction % Items" / 100));
"Profit (After Correction)" := 
  "Unit Price (After Correction)" - "Unit Cost";
"Hours (After Correction)" := 
  Hours + (Hours * ("Correction % Hours" / 100));

当我们现在使用Calculate函数时,系统将为要创建的此产品生成总单位成本单位价格工时。通过允许用户以百分比纠正工时和用量,系统增加了灵活性。

计算可以在与之前资源组相同的方式下用于职位计划行;唯一的区别是我们使用后台的 G/L 账户类型来开具固定价格的发票。让我们看看职位计划行中Add-On No.字段的OnValidate触发器中的 C/AL 代码:

Add-on No. - OnValidate()
CASE "Add-on Type" OF
  "Add-on Type"::Resource ... "Add-on Type"::Text:
      ...
  "Add-on Type"::"Resource Group":  
      ...
  "Add-on Type"::Calculation:
    BEGIN
      Calc.GET("Add-on No.");
      IF Calc."Turnover Account No." = '' THEN BEGIN
        TESTFIELD("Line Type", "Line Type"::Schedule);
        VALIDATE(Type, Type::Text);
        VALIDATE("No.", '');
      END ELSE BEGIN
        TESTFIELD("Line Type", 
          "Line Type"::"Both Schedule and Contract");
        VALIDATE(Type, Type::"G/L Account");
        VALIDATE("No.", Calc."Turnover Account No.");

      END;
      Description := Calc.Description;
      GetJob;

为了完成这个功能,我们将创建一个方法来使用计算中的工时进行资源规划。这可以通过使用没有单位成本和单位价格的计划类型的职位计划行来完成。

问题登记

对于我们的支持团队,我们已经实施了一个问题登记解决方案。这允许他们拥有一个简单的应用程序,他们可以在其中为所有客户登记问题,并跟踪状态,而无需进入和退出每个工作。

问题登记

问题登记是一个带有数字序列和行号的标题/行构造。这些行可以用来表述问题和答案。

当支持工程师创建一个新的问题时,系统将自动创建职位任务。让我们看看执行此操作的 C/AL 代码:

CreateJobTask()
TESTFIELD("Job No.");
TESTFIELD("Job Task No.", '');

OldJobTask.SETRANGE("Job No.", "Job No.");
OldJobTask.SETRANGE("Job Task Type", 
  OldJobTask."Job Task Type"::Posting);
IF OldJobTask.ISEMPTY THEN
  OldJobTask.SETRANGE("Job Task Type",
    OldJobTask."Job Task Type"::"Begin-Total");
OldJobTask.FINDLAST;

JobTask."Job No." := "Job No.";
JobTask."Job Task No." := INCSTR(OldJobTask."Job Task No.");
JobTask.Description := Description;
JobTask."Job Task Type" := JobTask."Job Task Type"::Posting;
JobTask.INSERT(TRUE);
CODEUNIT.RUN(CODEUNIT::"Job Task-Indent Direct", JobTask);

"Job Task No." := JobTask."Job Task No.";

系统在职位中搜索最后一条类型的Job TaskPosting的任务。如果找不到,它将搜索最后的Begin-Total行。

假设这一行存在,我们使用INCSTR函数来增加数字,创建一个新的Job Task行。描述被复制到Job Task中。支持工程师现在可以在这个Job Task上登记他们的工时。

这段 C/AL 代码非常简单,但展示了即使不触及任何标准 Microsoft Dynamics NAV 对象,一个小型解决方案也能多么有效。这是一种非常安全的发展方式。

摘要

在本章中,我们学习了如何实现 Microsoft Dynamics NAV 的职位功能。我们还讨论了设置职位和职位任务的不同的策略。

我们以创造性的方式使用职位计划行创建了几个不同的发票方法示例。在为工作购买物品时,这些物品不用于成本计算,但在我们详细讨论的进行中工作计算中。

当一切按预期完成时,发票会自动生成。最后,我们在不改变标准应用程序的前提下,为工作模块设计了一些小改进。这是关于微软 Dynamics NAV 功能性的最后一章。我们已经讨论了应用程序的所有可能性以及如何在不冒风险或破坏任何东西的情况下进行更改。

在下一章中,我们将探讨微软 Dynamics NAV 如何与其他应用程序进行接口。

第九章。接口

当 1995 年发布第一个 Windows 版本的 Microsoft Dynamics NAV 时,系统非常封闭。可以使用平面文本文件导入和导出数据,这基本上就是全部。这些平面文本文件被放在软盘上,通过邮政邮件发送。互联网和电子邮件刚刚兴起,大容量 U 盘还是一个梦想,而在这本书的前一个版本 2009 年发布时,OneDrive 和 Azure 正在被发明。

从那时起,世界发生了巨大的变化。互联网、电子邮件、SQL Server、.NET 和 Azure 改变了我们思考与应用程序接口的方式,我们仍在不断变化。今天,Microsoft Dynamics NAV 2013 拥有一个完全开放的数据库,支持广泛的接口可能性,我们将在本章中学习这些。

Navision 的 1.0 版本在 Windows 95 上运行,后来成为行业标准,超过十年,Windows 是唯一严肃的平台。今天,商业人士使用 iPad 和 Android 平板电脑。Microsoft Dynamics NAV 2013 是少数几个可以在所有设备上跨平台运行的 ERP 平台之一,甚至支持使用 Google 或 Facebook 凭证登录。

在本章中,我们首先将讨论可用的接口技术和标准产品中的接口。然后我们将讨论接口方法以及如何创建可靠的接口。

在本章结束时,我们将创建一些示例界面,并看看未来将如何进一步改进界面。

在阅读本章后,您将很好地理解产品默认支持哪些接口,应使用哪些接口技术,以及如何设计一个稳固的 B2B 接口。

界面类型

讨论接口时,我们通常从技术开始,但在那之前,需要回答一些其他基本问题,例如以下问题:

  • 是否需要导入、导出或两者都要?

  • 是手动启动还是自动启动?

  • 接口是定时器驱动还是事件驱动?

让我们来讨论这些问题。

导入和导出

第一个问题是要不要只从 Microsoft Dynamics NAV 导出数据,还是它也会导入需要处理的数据到系统中。

在导入和导出过程中,数据处理可以通过终端用户手动启动,使用数据拉取或数据推送。界面也可以是事件驱动(实时)或定时器驱动(异步)。

导入和导出

手动

当接口是手动时,第一个应用程序有一个导出过程,另一个应用程序有一个导入过程。终端用户首先手动启动导出过程,然后在另一个应用程序中手动启动导入过程,通常将数据保存到平面文件中。这是一种经典的接口方法。

手动接口的一个例子是从 Microsoft Dynamics NAV 导出网上银行信息或将 XBRL 文件发送给您的会计师。

数据拉取

当使用数据拉取导出数据时,接口从外部应用程序启动。该应用程序将从数据库中读取数据并进行处理。

当使用数据拉取导入数据时,接口从应用程序启动,该应用程序从另一个应用程序读取并处理数据。

数据推送

如果接口使用数据推送,导出应用程序将数据写入其他数据源。当其他应用程序中的数据不需要进一步处理时,使用此方法。一个典型的例子是将 Microsoft Dynamics NAV 中的数据导出到 Microsoft Office 应用程序,如 Word 或 Excel。

事件驱动与定时器驱动

当数据推送或数据拉取与事件或定时器的使用结合时,不再需要最终用户干预。此时,接口将自动运行。

我们将在本章后面讨论接口方法时详细讨论这些方法。

接口技术

在 Microsoft Dynamics NAV 中,有广泛的方法用于接口。每种方法对某些类型的接口很有用,而对其他类型的接口则不太有用。我们将在 C/SIDE 开发平台中讨论所有可用的方法。

文件

平面文件和 XML 文件都受 Microsoft Dynamics NAV 支持。自 1995 年产品推出以来,平面文件一直可用,使用数据端口为经典客户端提供服务。

在版本 3.60 中引入了 XML 支持,作为数据端口的额外选项。版本 4.0 引入了 XMLPort 对象,用于替换导入和导出 XML 文件的端口。

目前在 Microsoft Dynamics NAV 2013 中,XMLPort 对象用于 XML 和平面文件。此外,C/AL 有一个 FILE 对象,可以用来直接访问文件,而无需使用 XMLPort 对象。

自动化控制

在 Microsoft Dynamics NAV 中,Microsoft COM 和 ActiveX 的实现被称为自动化控制。

自动化控制或 ActiveX 允许软件应用程序作为另一个应用程序的嵌入部分被重用。大多数 Microsoft 应用程序都支持以这种方式使用。例如,Microsoft Office、Windows 脚本宿主和 ActiveX 数据对象 (ADO)。

Microsoft Dynamics NAV 支持自动化控制。消费自动化控制是通过暴露方法和属性的接口来完成的。

最常用且通用的接口是 iUnknown。这也是 Microsoft Dynamics NAV 支持的唯一自动化控制接口。如果自动化控制使用其他接口,应在 Visual Studio 中创建一个包装器,将接口转换为 iUnknown。当自动化控制需要通过表单控件嵌入时,也应创建包装器。

注意

关于 iUnknown 接口和 COM 技术的更多信息可以在 en.wikipedia.org/wiki/IUnknown 找到。

事件

大多数自动化控制允许数据推送。使用自动化控制的触发事件,当其他应用程序发生某些事件时,也可以在 Microsoft Dynamics NAV 中启动业务逻辑。

局限性

在 Microsoft Dynamics NAV 2013 中,自动化控制只能从客户端使用。在服务器端运行的任何代码都不能使用自动化控制对象。

DotNet 互操作性

.NET 的支持被引入作为自动化控制的替代品。在 C/AL 编程语言中,可以直接使用大量 .NET 对象。它们可以在服务器端和客户端中使用。

在标准应用程序中,大多数自动化接口都被 .NET 接口所取代,例如 Excel 接口,我们将在本章后面讨论。

在 Microsoft Dynamics NAV 中使用 .NET 存在局限性,通常通过在 C# 中创建包装 DLL 对象来解决。Excel 接口也是这样的例子。

注意

在 C/AL 中学习 .NET 的一个好地方是www.vjeko.com。局限性在vjeko.com/blog/top-10-things-i-miss-in-net-interoperability-in-nav-2013中讨论。

客户端可扩展性

使用页面对象,由于页面对象不提供所见即所得(WYSIWYG)功能或允许开发者确定控件位置,用户界面的允许创造性非常有限。每个客户端都决定如何渲染 UI,开发者无法控制。这可以通过客户端可扩展性来解决。这项技术允许使用 Visual Studio 和 .NET 提供的所有 UI 功能,然而,在开发跨平台应用时,应使用 JavaScript。

注意

请参考www.youtube.com/watch?v=WErBd1mlZFM了解如何开始使用 Microsoft Dynamics NAV 的 JavaScript 插件。

Open Database Connectivity (ODBC)/ADO

开放数据库连接ODBC)于 1992 年开发,旨在允许所有类型的数据库以统一的方式交换数据。ADO 是 ODBC 的继任者,于 1996 年开发。

Microsoft Dynamics NAV 的 ADO 和 ODBC 允许在应用程序数据库中读取和写入,以及读取和写入其他数据库。

注意

使用 ADO 和 ODBC 更高级的功能需要基本的 T-SQL 语句知识。有关 ADO 和 ODBC 之间的区别,请参阅www.differencebetween.com/difference-between-odbc-and-vs-ado/

从 Microsoft Dynamics NAV 读取

要从数据库中读取数据,您只需在您使用的 Windows 机器上安装有效的 ODBC 驱动程序,并拥有登录到数据库的凭据。

让我们创建一个示例来展示如何使用 Excel 从 Microsoft Dynamics NAV 导入数据。

  1. 打开 Microsoft Excel,选择数据,然后从其他来源中选择从 SQL Server,如图所示:从 Microsoft Dynamics NAV 读取

    从 SQL Server

  2. 选择一个服务器名称和有效的凭据从 Microsoft Dynamics NAV 读取

  3. 选择一个数据库和您想要查看的表。在我们的示例中,我们将选择客户表。然后选择完成确定从 Microsoft Dynamics NAV 读取

  4. 现在我们已经将 Microsoft Dynamics NAV 数据导入 Excel 中。从 Microsoft Dynamics NAV 读取

    注意

    由于流字段不是 SQL Server 数据库中的实际字段,我们无法在 ODBC/ADO 中使用它们。

向 Microsoft Dynamics NAV 写入

建议不要使用 ODBC 直接将数据写入 Microsoft Dynamics NAV 数据库,因为这不符合最佳实践。原因在于此接口级别缺少业务逻辑。

当通过 ODBC 写入时,我们直接针对 SQL Server,而不允许 C/AL 业务逻辑验证我们创建的数据。C/AL 数据通常确保我们开发业务规则的数据完整性。使用 C/ODBC 驱动程序对本地数据库也是如此。

注意

为了解决这个问题,数据可以保存在一个特殊的接口缓冲表,并通过应用程序服务器或从用户界面启动的 C/AL 事务进行处理。

与其他数据库通信

要使用 ODBC 从 Microsoft Dynamics NAV 读取和写入其他数据库中的数据,建议使用ActiveX 数据对象ADO)。ADO 是 Microsoft 技术,允许使用 ActiveX 接口通过 ODBC 进行连接。使用 ADO 允许我们读取和写入另一端的数据库。

我们甚至可以使用 ADO 连接到 Microsoft Dynamics NAV SQL Server 数据库,并从 C/AL 代码中运行 SQL 语句。

注意

我们将在本章的接口方法部分使用 ADO。

SQL Server 接口

由于 Microsoft Dynamics NAV 运行在 SQL Server 数据库之上,我们可以使用 SQL Server 中所有可用的技术来获取数据。这提供了超出本书范围的大量选项,但让我们简要讨论其中的一些:

  • 链接服务器:在 SQL Server 中,可以设置链接服务器。这允许我们将查询发送到其他数据库,如其他 SQL Server、MS Access 或 Oracle,并基于这些数据创建视图。

  • 视图:SQL Server 中的视图是一个具有固定结果集的保存查询,可以将其视为一个表。在 C/Side 中,我们可以使用视图作为表的源数据,通过 Linked Object 属性创建页面或报告。

  • SQL Server Integration Services:这是 SQL Server 与其他数据库集成的首选组件,取代了 DTS 包。使用 SSIS 需要具备良好的 SQL Server 和 Microsoft Dynamics NAV 的知识和技能。

  • 报表服务:这是一个基于服务器的报表平台,可以与 SharePoint 集成,允许用户根据 T-SQL 查询设计 RDL 报表。

  • 分析服务:这是微软对其客户 OLAP、BI 和数据挖掘需求的回应。

    小贴士

    另一个我们可以使用的 SQL Server 组件是 SQL Server Agent。该组件允许我们安排直接在数据库上运行的接口任务。

Microsoft 消息队列

Microsoft 消息队列 (MSMQ) 允许应用程序集成那些运行在不可靠连接上的异步应用程序。这种接口技术在需要从 Microsoft Dynamics NAV 获取信息并将信息发送回数据库的网站上非常受欢迎。

.NET 互操作性的引入使得在 Microsoft Dynamics NAV 中使用 MSMQ 变得更加容易。使用 System.Messaging.MessageQueue 只需要几行 C/AL 代码就可以在队列上发布消息。

应用程序服务器

MSMQ 总是与使用应用程序服务器结合使用,以处理网站发送回的请求。

应用程序服务器

Web 用户可以是使用 Web 解决方案进行工时登记的员工、PDA 或使用 Web 商店的客户。

注意

mibuso.com/blogs/ara3n/2011/01/10/using-ado-on-rtc-in-nav/ 的这篇博客文章中解释了如何使用 .NET 开始使用 MSMQ。

Web 服务

当涉及到实时接口时,Web 服务是首选的技术。Web 服务允许您使用来自其他应用程序的应用程序功能库。

Microsoft Dynamics NAV 2013 允许您使用 SOAP 和 OData 协议将所有 C/AL 代码暴露为 Web 服务。

消费 Web 服务比暴露 Web 服务要困难得多。没有标准框架来做这件事。最常用的两种解决方案是使用 XMLDOM .NET 互操作对象进行消费,或者使用 Visual Studio .dll 中的服务引用将 Web 服务包装起来。

暴露 NAV Web 服务

在 Microsoft Dynamics NAV 2013 中,每个 页面 对象和大多数代码单元都可以暴露为 Web 服务。这可以通过使用 Web 服务表 (2000000076) 来实现。

暴露 NAV Web 服务

要发布 Web 服务,选择对象类型和对象 ID,找到一个唯一的服务名称。然后选择 已发布 复选框。

当发布 Web 服务时,URL 会显示出来,这使得查找它变得更加容易。

消费 Microsoft Dynamics NAV Web 服务

要消费 Web 服务,生成一个地址,http://<Server>:<WebServicePort>/<ServerInstance>/WS/<CompanyName>/,这个地址从其他应用程序中调用。

消费 Microsoft Dynamics NAV Web 服务

注意

SystemService Web 服务始终可用,并返回可用公司名称的列表。

标准应用程序接口

我们讨论了 Microsoft Dynamics NAV 所有可用的界面技术。让我们看看这些是如何在标准产品中实现的。

在这本书中,我们不会深入探讨每个界面,因为这几乎需要另一本书。我们只会简要讨论在标准应用程序中找到所有讨论的技术的地方,并指出可以找到白皮书或网站的地方。

平文件的一个例子是导出联系人。Microsoft Dynamics NAV 允许我们使用XMLPort导出我们的联系人。

此功能的XMLPort编号为(5050),使用格式变量文本。其他选项有XmlFixed Text,如以下截图所示:

标准应用程序界面

XMLPorts具有类似于页面的节点结构。XMLPort以整数表作为第一个数据类型开始,后面跟着联系人表字段。

注意

更多关于编程XMLPorts的信息可以在Programming Dynamics NAV 2013David A. StudebakerChristopher D. StudebakerPackt Publishing中找到。

Office 集成

Microsoft Dynamics NAV 和 Microsoft Office 集成,以便与 Word、Excel 和 Outlook 一起使用。我们首先将讨论标准的 Word 和 Excel 集成,稍后讨论替代方案。最后,我们将简要讨论 Outlook 集成的可能性。

Word 和 Excel 集成

在 Microsoft Dynamics NAV 中,每个表单或页面都可以导出到 Word 和 Excel。这项内置技术由用户界面自动提供,无需开发者付出任何努力。

Word 和 Excel 集成

Excel 和 Word 的默认存在

样式表工具

为了在布局上更加灵活,Microsoft 为 Microsoft Dynamics NAV 和 Word 发布了一个样式表工具。此工具允许用户轻松生成样式表。

注意

样式表工具版本 3.0 可以从www.mibuso.com/dlinfo.asp?FileID=1543下载。

样式表工具

样式表工具提供的说明书对如何创建样式表有很好的描述。

样式表工具

将操作添加到操作设计器(在适当的组下)并设置以下属性:

Expanded    Type    SubType    Name                Caption
0           Action             <Action680>         Style Sheets

OnAction触发器应包含以下行:

StyleSheetDataMgt.LoadStylesheetDataRTC(GETPOSITION,
  CURRENTKEY,PAGE::"<<PageName>>");

这里,StyleSheetDataMgt是类型为 codeunit,682(样式表数据管理)的变量。

注意

当这个操作在一个页面上完成时,可以轻松地复制并粘贴到其他页面上。确保你更改页面名称。

高级 Excel 集成

当需要将来自应用程序不同部分的信息导出到 Excel 进行合并时,使用样式表并不是理想的方法。

为了支持这一点,可以使用Excel Buffer表(370)。此表可以填充数据,然后使用简单的 C/AL 命令将其发送到 Excel。

这在应用程序的几个部分中使用,例如,导入和导出我们在 第三章 中讨论的预算,财务管理

让我们创建一个示例代码单元,该代码单元使用 Excel 缓冲区表导出数据:

  1. 创建一个新的代码单元,并定义一个类型为记录 Excel 缓冲区的全局变量。这需要一个临时变量。同时,定义其他变量,如以下截图所示:高级 Excel 集成

    注意

    临时记录变量不存储在数据库中;它们存储在客户端内存中。这允许多个用户创建相同的记录而不会互相阻塞。由于所有处理都在没有网络和数据库的情况下完成,因此这也更快。

  2. 创建一个新的 EnterCell 函数,参数如以下截图所示:高级 Excel 集成

  3. 放置将处理接口的 C/AL 代码:

    OnRun()
    ExcelBufTemp.CreateBook(Cust.TABLECAPTION);
    
    Cust.FIND('-');
    REPEAT
      RowNo := RowNo + 1;
      EnterCell(RowNo, 1, Cust."No.", FALSE, FALSE, '');
    
      FormAddr.Customer(Addr, Cust);
      EnterCell(RowNo, 2, Addr[1], FALSE, FALSE, '');
      EnterCell(RowNo, 3, Addr[2], FALSE, FALSE, '');
      EnterCell(RowNo, 4, Addr[3], FALSE, FALSE, '');
      EnterCell(RowNo, 5, Addr[4], FALSE, FALSE, '');
      EnterCell(RowNo, 6, Addr[5], FALSE, FALSE, '');
      EnterCell(RowNo, 7, Addr[6], FALSE, FALSE, '');
      EnterCell(RowNo, 8, Addr[7], FALSE, FALSE, '');
      EnterCell(RowNo, 9, Addr[8], FALSE, FALSE, '');
    
    UNTIL Cust.NEXT = 0;
    
    ExcelBufTemp.WriteSheet(Cust.TABLECAPTION,COMPANYNAME,USERID);
    ExcelBufTemp.CloseBook;
    ExcelBufTemp.OpenExcel;
    ExcelBufTemp.GiveUserControl;
    EnterCell()
    ExcelBufTemp.INIT;
    ExcelBufTemp.VALIDATE("Row No.",RowNo);
    ExcelBufTemp.VALIDATE("Column No.",ColumnNo);
    ExcelBufTemp."Cell Value as Text" := CellValue;
    ExcelBufTemp.Formula := '';
    ExcelBufTemp.Bold := Bold;
    ExcelBufTemp.Underline := UnderLine;
    ExcelBufTemp.NumberFormat := NumberFormat;
    ExcelBufTemp.INSERT;
    

此 C/AL 代码将浏览数据库中的客户,并使用地址格式(365)代码单元格式化地址。

客户编号 字段和结果数组 Addr[] 被保存在 Excel 缓冲区表中。最后,我们启动 C/AL 函数,根据数据生成 Excel 工作表。

高级 Excel 集成

C/AL 函数结果

Outlook 集成

Microsoft Dynamics NAV 2013 允许与 Microsoft Outlook 进行不同级别的接口:

  1. 角色中心上的 Outlook 部分。

  2. 使用 ExtendedDatatype 属性从页面发送电子邮件。

  3. 使用邮件(397)或 SMTP 邮件(400)代码单元发送电子邮件。

  4. 使用 Outlook 集成 Web 服务同步联系人和待办事项。

  5. 使用 电子邮件 - 记录 功能从 Exchange 读取电子邮件。

  6. Microsoft Dynamics NAV 2013 R2 可以连接到 Office 365。

Outlook 部分

在角色中心中,可以激活 Outlook 系统组件。这允许用户直接在角色中心查看他们的电子邮件、日程安排和任务。

Outlook 部分

此功能是 Windows 客户端内置的,不能使用 C/AL 代码来更改。

扩展数据类型属性

当一个表中的 文本 字段使用 ExtendedDatatype 属性时,Windows 客户端将自动允许用户直接将电子邮件发送到字段中指定的地址。

扩展数据类型属性

这也是 Windows 客户端内置的功能,不能通过 C/AL 代码来影响。

扩展数据类型属性

邮件和 SMTP 邮件代码单元

在引入 ExtendedDatatype 属性之前,Microsoft Dynamics NAV 的电子邮件是通过 Automation Control 包装器 DLL 发送到 Microsoft Outlook 的。这在代码单元 397 中处理,并且仍然可以直接从 C/AL 代码发送电子邮件。

代码单元 SMTP 邮件(400)允许我们直接发送电子邮件到 SMTP 服务器。

Outlook 同步

Microsoft Outlook 可以用作 Microsoft Dynamics NAV 的离线客户端。当两个系统都可用连接时,每个表都可以同步到 Microsoft Outlook。使用 Outlook 的离线功能,用户可以在路上查看数据,甚至更改信息或创建新数据。

这是通过本章前面讨论的 Outlook 同步 Web 服务完成的。

Outlook 同步

微软对这一功能进行了很好的文档记录。

Exchange 集成

为了读取传入的电子邮件,Microsoft Dynamics NAV 提供了与 Exchange 公共文件夹的集成。这些邮箱中的信息可以在 Microsoft Dynamics NAV 中读取和使用。

接口处理是通过作业队列和应用服务器(NAS)完成的。

在我们之前在第四章关系管理中讨论的营销设置中,我们可以设置交换集成的参数。

Exchange 集成

交互日志条目

从 Microsoft Exchange 读取的每封电子邮件都显示在 Microsoft Dynamics NAV 中的交互日志条目中。

Office 365

从版本 2013 R2 开始,Microsoft Dynamics NAV 可以集成到 Office 365 中。这是一个外观上的集成,这意味着从最终用户的角度来看,应用程序是相同的;然而,应用程序不共享数据。

Microsoft Dynamics NAV 可以配置为接受 Office 365 凭据,这使得用户只需登录一次就可以使用两个平台变得非常容易。

Office 365

使用 Office 365 凭据登录

SharePoint

Microsoft Dynamics NAV 2013 RTM 附带了一个特殊的 SharePoint 客户端。然而,由于在 R2 中直接停止了这项服务,我们不会讨论它。

在 Microsoft Dynamics NAV 2013 R2 中,Web 客户端符合 SharePoint 规范。要结合使用 SharePoint 和 Microsoft Dynamics NAV 2013 R2,可以添加连接到 Web 客户端的 Web 部件。

可以连接到 SharePoint 的本地版本和 SharePoint Online。

客户端插件

Microsoft Dynamics NAV 2013 附带一个用于 Microsoft Connect 的客户端插件。使用 Connect 控件的一个示例页面对象是 Connect (9175)。

客户端插件

让我们看看这是如何完成的。

客户端插件

此页面的页面类型是CardPart,它没有源表。页面上唯一的控件是参数,这是一个返回 Text (350)类型值的函数。

ControlAddIn属性指向在启动此页面时将使用的插件。此插件将替换页面上的原始控件。

Parameters函数中,创建了一个字符串,将信息输入到连接插件中,使其能够显示对当前角色有趣的信息。这是通过组合其他 C/AL 函数来完成的:

Parameters()
InitCurrentRoleValues;

EXIT(Add(Version) + Add(Locale) + Add(Role) + Add(RoleID) + 
  Add(Serial));

Add()
EXIT(Parameter + Separator);

Version()
EXIT('version=' + FORMAT(ApplicationManagement.ApplicationVersion + ':' + 
  ApplicationManagement.ApplicationBuild,0,XMLFormat));

Locale()
// Windows Language ID
EXIT('locale=' + FORMAT(CurrentLanguageID,0,XMLFormat));

Role()
// Profile ID (Any text entered in Profile ID)
EXIT('role=' + FORMAT(DELCHR(CurrentRole,'=',Separator),0,
  XMLFormat));

RoleID()
// Role Center ID (Page ID)
EXIT('roleid=' + FORMAT(CurrentRoleID,0,XMLFormat));

Serial()
// License ID
EXIT('serial=' + FORMAT(SERIALNUMBER,0,XMLFormat));

Separator()
EXIT(';');

XMLFormat()
EXIT(9);

InitCurrentRoleValues()
CurrentLanguageID := GLOBALLANGUAGE;
CurrentRoleID := ApplicationManagement.DefaultRoleCenter;
CurrentRole := FORMAT(CurrentRoleID);
...

在 第七章 中的 存储和物流,客户端扩展性和 Bing Maps 用于在地图上显示路线的停靠点。

可用的库存储在客户端插件表中(2000000069)。

接口方法

因此,我们现在已经讨论了 Microsoft Dynamics NAV 中的接口类型、接口技术和内置接口。

让我们设计和开发一个新的企业对企业接口。我们将使用 第七章 中的对象,存储和物流,来创建接口。

场景

我们的一位客户现在希望通过电子邮件发送发货,而不是通过传真。电子邮件将包含一个预定义格式的 Excel 文件。

设计

让我们回顾一下在 第七章 中的解决方案物流部分所设计的数据库模型,存储和物流

设计图

该过程从注册表开始。从一个注册项中,我们生成发货,发货被组合成一个带有停靠点的路线。

因此,我们需要将数据从 Excel 表格移动到注册表中。

映射

当客户向我们提供一个包含信息的 Excel 表格时,他们很少会使用与我们的表格完全相同的字段。因此,我们需要创建一个映射。Excel 表格中的每个字段都需要映射到一个字段,并且需要识别和讨论缺失的字段。

我们从客户那里得到的 Excel 表格看起来像这样:

映射图

让我们尝试将此信息映射到我们的物流注册工作表表中,如下所示:

字段编号 字段名称 数据类型 长度 映射字段
1 注册批次 代码 10 -
2 行号 整数 -
6 发货日期 日期 日期
8 产品编号 代码 20 商品代码
10 描述 文本 50 描述
12 单位 文本 10 -
16 数量 小数 托盘数
20 长度 小数 长度
21 宽度 小数 宽度
22 高度 小数 高度
31 总重量 小数 -
32 净重 小数 重量
36 每包裹单位数 小数 -
37 单位体积 小数 -
53 目的地名称 文本 50 交货地点
55 目的地地址 文本 50 地址
57 目的地市 文本 30 城市
58 目的地联系人 文本 50 -
59 目的地邮政编码 代码 20 邮政编码
60 目的地县 文本 30 -
61 目的地国家/地区代码 代码 10 -

Excel 表格中的大多数字段都可以映射到我们表中的一个字段。

空缺

在 NAV 中需要的某些字段没有由 Excel 表格填充。对于某些字段来说,这是可以接受的,例如,注册批次行号字段由导入决定。

一些其他字段更难。计量单位毛重每包裹单位数单位体积在 Excel 表中留空,但它们都在 NAV 中需要。

对于这些字段,我们需要与客户达成一致。他们需要指定这些字段,或者告诉我们它们是否有默认值。让我们看看我们的差距并填补它们:

  • 计量单位:对于这个客户来说,始终是“托盘”

  • 体积:这可以通过长度 x 宽度 x 高度来计算

  • 毛重:我们同意这等于净重

  • 每包裹单位数:这始终是 1

如果它不起作用怎么办

将外部数据读取到数据库中只是创建可靠界面的一个步骤。

但如果客户联系我们并说,“我们发送了包含 10 行的文件,而装运文件显示 9 行”。当我们检查我们的数据库时,装运确实显示了 9 行,但没有办法检查我们是否导入了原始的 10 行。在这个阶段,导入的登记行被删除,并生成了装运。

如果发生这种情况,我们需要可追溯性。在设计良好的界面中,我们应该始终创建一个与导入数据完全匹配的表。这允许我们首先检查是否一切匹配。

从这个表中的数据可以进行处理,但不应该从数据库中删除,并定期清理。这允许我们检查是否有错误发生。

我们将在一个更高级的示例中演示这一点。

场景

我们存储和物流附加功能的实现需要一个与射频应用实时接口。射频扫描仪用于拣选过程。射频应用使用自己的数据库系统,其中包含我们应该填充和之后读取的表。

场景

射频应用有三个表。我们的界面需要将数据导出到拣选行表,并且需要从剩下的两个表导入数据,已完成拣选异常

界面类型

这是一个导入导出界面,将使用拣选行的数据推送和已完成拣选及异常的数据拉取。界面将由定时器驱动。每分钟我们将轮询新数据。

界面技术

对于这个界面,我们将使用本章讨论的多种技术的组合。主要技术是 DotNet 互操作性。

活动数据对象

拣选数据库在 SQL Server 上运行,因此我们将使用 ADO 连接到数据库并发送 T-SQL 语句来读取和写入数据。

日志记录

在这个界面中,我们将启用两种类型的日志记录。第一种日志将是在 Microsoft Dynamics NAV 中复制 RF 表并用作缓冲区。第二种日志将维护一个记录,我们将保存我们生成的所有 T-SQL 语句的副本。这将使我们能够在出现问题时查看我们生成了什么。

设计模式

让我们看看我们将为这个项目开发接口的设计:

设计模式

接口将由一个 应用服务器 控制。每分钟它将执行一个代码单元,检查是否有新的 存储行 需要导出。这些行将首先移动到 RF 拣选行 缓冲表,然后使用 ADO 和 T-SQL 移动到 RF 数据库。来自 RF 数据库的 新完成的拣选异常 将使用相同的技术移动到 Microsoft Dynamics NAV,然后可以进行处理。

解决方案

为了运行接口,我们创建了三个代码单元和一个表。SQL 语句表用于记录每个接口会话。

解决方案

RF NAS 定时器 (123.456.730) 代码单元是从代码单元 ApplicationManagement 中的 NASHandler 函数启动的。它使用一个无限循环。

解决方案

让我们看看实现这一功能的所需 C/AL 代码:

OnRun()

RFInt.CreateConnectionString;
RFLoop(600000);

RFLoop(MilisecondsBetweenPolls : Integer)
WHILE TRUE DO BEGIN
  IF NOT CODEUNIT.RUN(CODEUNIT::"RF Helper") THEN
    ParseError;
  COMMIT;
  MaxMilisecondsSleep := 10000;
  FOR Count := 1 TO MilisecondsBetweenPolls DIV MaxMilisecondsSleep DO
    SLEEP(MaxMilisecondsSleep);
  SLEEP(MilisecondsBetweenPolls MOD MaxMilisecondsSleep);
END;

ParseError()
SELECTLATESTVERSION;
RFIntSetup.GET;
SynchID := RFIntSetup."Synchronization ID";

SQLStat.INIT;
SQLStat."SQL Statement 1" := 'ERROR : ' + GETLASTERRORTEXT;

SQLStat.Bold := TRUE;
SQLStat.SessionID := SynchID;
SQLStat.Type := SQLStat.Type::Error;
SQLStat.INSERT(TRUE);
COMMIT;

SLEEP 函数被用来确保接口每分钟只运行一次。通过将 SLEEP 函数分解成更小的间隔,可以在 SLEEP 命令之间停止执行这个 C/AL 代码的 Windows 服务。

提示

GETLASTERRORTEXT 是一个 C/AL 函数,它返回系统生成的最后一个错误消息。它可以与 IF CODEUNIT.RUN 语法结合使用来捕获运行时错误。

RF 辅助器 (123.456.732) 代码单元是一个包装代码单元,用于错误捕获和保持可读性。

在接口的每次运行中,我们创建一个新的 SQL 语句 ID,我们可以根据它来追踪任何错误:

OnRun()
SELECTLATESTVERSION;
RFIntSetup.GET;
RFIntSetup."Synchronisation ID" := RFIntSetup."Synchronisation ID" + 1;
RFIntSetup.MODIFY;
SynchID := RFIntSetup."Synchronisation ID";

SQLStat.INIT;
SQLStat."SQL Statement 1" := 
  '-SYNCHRONISATION STARTED- ID = ' + FORMAT(SynchID) + ' -';
SQLStat.Bold := TRUE;
SQLStat.SessionID := SynchID;
SQLStat.Type := SQLStat.Type::StartStop;
SQLStat.INSERT(TRUE);

COMMIT;

CLEAR(RFInterface);
RFInterface.SetSynchID(SynchID);

StorageLn.LOCKTABLE;
IF StorageLn.FINDSET THEN REPEAT
  RFInterface.CreatePickLines(StorageLn);
UNTIL StorageLn.NEXT = 0;

COMMIT;

CLEAR(RFInterface);
RFInterface.SetSynchID(SynchID);
RFInterface.ReadFinishedPicks;

COMMIT;

CLEAR(RFInterface);
RFInterface.SetSynchID(SynchID);
RFInterface.ReadExceptions;

COMMIT;

SQLStat.INIT;
SQLStat."SQL Statement 1" := 
  '-SYNCHRONISATION STOPPED- ID = ' + FORMAT(SynchID) + ' -';
SQLStat.Bold := TRUE;
SQLStat.SessionID := SynchID;
SQLStat.Type := SQLStat.Type::StartStop;
SQLStat.INSERT(TRUE);

COMMIT;

然后触发三个接口函数以同步所需的三个表。

COMMIT

在执行每个命令后,我们执行 COMMIT 语句。这将确保数据库中所有内容都存储到那个点。这是必要的,因为我们所创建的 ADO 语句都在我们的事务之外。如果我们的接口运行回滚,它可能会同步已经同步的数据。

RF 接口 (123.456.731)。在这里,实际的 ADO 同步是在这个代码单元中完成的。这个代码单元是 SingleInstance。这将保持 ADO 连接在 NAS 会话期间保持活跃:

CreateConnectionString()
IF ConnActive THEN EXIT;

RFIntSetup.GET;
Database := RFIntSetup."Database Name";
Server := RFIntSetup."Server Name";

ConnString := 'Data Source=' + Server + ';' + 'Initial Catalog=' + Database + ';Trusted_Connection=True;';

SaveReadSQL('Connection ' + ConnString + ' opened on ' + FORMAT(CURRENTDATETIME),TRUE,0,0,0, '');

SQLCon := SQLCon.SqlConnection(ConnString);

SQLCon.Open;
ConnActive := TRUE;

CloseConnectionString()
SQLCon.Close;

SaveReadSQL('Connection closed on ' + FORMAT(CURRENTDATETIME),TRUE,0,0,1, '');

CLEAR(SQLReader);
CLEAR(SQLCommand);
CLEAR(SQLCon);
ConnActive := FALSE;

对于接口,我们使用了三个 DotNet 变量。

COMMIT

让我们更详细地看看三个 DotNet 变量:

  • SQLConnection: 这用于与数据库建立连接并执行 T-SQL 语句

  • SQLCommand: 可以使用这个来读取 SELECT 语句的结果集

  • SQLReader: 读取器用于读取数据,并在 ADO 和 C/Side 之间转换数据类型

写入数据

RF 应用需要从存储行表中获取数据。我们首先创建了一个映射到 RF 应用的映射,就像我们在本章前面使用 Excel 接口所做的那样。

这个映射被保存在一个缓冲表中以供追踪:

CreatePickLines()
CreateConnectionString;

SaveReadSQL('CreatePickLines',TRUE, 1, 8388608, 3, '');

PickID := COPYSTR(StorageLn."Document No." + FORMAT(StorageLn."Line No."), 1, 20);

SaveReadSQL('Pick Document : '+PickID,TRUE,3,16711680,7,'');

WITH RFPickLines DO BEGIN
  "Pick Code" := PickID;
  Quantity := StorageLn.Quantity;
  "Terminal ID" := 1;
  "Display 1" := StorageLn.Description;
  "Display 2" := 'Warehouse ' + StorageLn."Warehouse Code";
  "Display 3" := 'Region ' + StorageLn."Region Code";
  "Display 4" := 'Shelf ' + StorageLn."Shelf No.";
  INSERT;
  SQLStatement := 'INSERT INTO [RF Pick Lines]' +
                  '([Pick Code],'+
                   '[Quantity],'+
                   '[Terminal ID],'+
                   '[Display 1],'+
                   '[Display 2],'+
                   '[Display 3],'+
                   '[Display 4])'+
                   'VALUES('+
                   Quote + PickID + Quote      +','+
                   FORMAT(Quantity)            +','+
                   '1'                         +','+
                   Quote + "Display 1" + Quote +','+
                   Quote + "Display 2" + Quote +','+
                   Quote + "Display 3" + Quote +','+
                   Quote + "Display 4" + Quote + ')';
END;

ExecuteSQL(SQLStatement);

StorageLn.Exported := CURRENTDATETIME;
StorageLn.MODIFY;

实际数据是通过 INSERT 命令移动到 RF 数据库的。

注意

为了避免两次导出相同的数据,我们需要跟踪我们导出了什么。最简单的方法是创建一个名为 Exported 的新字段。将此字段设置为 DateTime 也使得应用程序的可追溯性成为可能。

读取数据

当从射频数据库读取数据时,我们也会为数据发送一个 T-SQL SELECT 查询。我们使用 SQLReader.Read 来浏览结果集中的记录。

对于结果集中的每条记录,我们在缓冲表中创建一条记录,然后我们可以使用它来更新存储行中的信息。

当读取数据时,我们不希望两次导入相同的数据。为了避免这种情况,我们需要在表中存储一个唯一标识符,这样我们就可以记住上次运行时我们停在了哪里:

ReadFinishedPicks()
CreateConnectionString;

SaveReadSQL('ReadFinishedPicks',TRUE, 1, 8388608, 3, '');

RFIntSetup.GET;
LastSync := RFIntSetup."Last Finished Pick";

SQLCommand := SQLCon.CreateCommand();
SQLCommand.CommandText := SaveReadSQL('SELECT ' +
              '[Reference Entry No],' +
              '[Terminal ID],' +
              '[Duration],' +
              '[Ready Date Time]' +
    ' FROM [RF Finished Pick] WHERE [Reference Entry No] > ' + 
      LastSync,FALSE,2,0,2, ''));

WHILE SQLReader.Read() DO BEGIN
  RFFinishedPick.INIT;
  RFFinishedPick."Reference Entry No." := 
    ReadInteger('Reference Entry No');
  RFFinishedPick."Terminal ID" := ReadInteger('Terminal ID');
  RFFinishedPick.Duration := ReadInteger('Duration');
  RFFinishedPick."Ready Date Time" := 
    ReadDateTime('Ready Date Time');
  RFFinishedPick.INSERT;
END;

RFIntSetup."Last Finished Pick" := Quote + FORMAT(RFFinishedPick."Reference Entry No.") + Quote;
RFIntSetup.MODIFY;

AdoRecordSet.Close;

在我们的示例中,这个唯一标识符是 参考条目号

记录,记录,再记录

尽管大部分日志记录是通过缓冲表完成的,但我们还希望存储每次接口运行时的一般过程。这是通过 SQL 语句表来完成的。SQL 语句以及其他事件都存储在那里。

通过使用 COMMIT 功能,我们可以通过查看此表中的最后一条记录来确切地看到它停止的位置。我们可以解决导致接口停止的问题,并重新启动接口而不会丢失数据。

注意

除非有非常好的理由,否则永远不要使用 COMMIT 语句。C/SIDE 通常会为您处理事务,当事情出错时可以完全回滚。在正常的 C/SIDE 事务中创建 COMMIT 语句将阻止 C/SIDE 回滚。

测试

让我们测试我们刚刚设计和开发的接口。为了做到这一点,我们需要在存储行表中记录,RF 数据库需要存在于某个地方。

RF 数据库

要测试我们为该解决方案创建的对象,RF 数据库应该存在于您的系统上。此数据库可以使用 T-SQL 脚本创建,并且应在 Microsoft SQL Server 机器上执行。

注意

RF database.sql 脚本是为此本书下载的对象文件的一部分。

在 SQL Server Management Studio 中打开脚本,然后单击 执行

RF 数据库

测试

即使 C/AL 代码可以在 Windows 客户端运行,我们也会在经典客户端运行测试。这样做的原因是接口将在 NAS 上运行,它将以与经典客户端相同的方式执行 C/AL 代码。使用经典客户端的另一个原因是,这是 DBA 执行所有任务的接口。

要开始测试运行,从对象设计器打开页面 RF 接口设置(123.456.780)。

确保数据库和服务器是正确的。服务器应该是执行 SQL 脚本的 SQL Server 实例。

注意

ADO 连接使用 Windows 账户 NT AUTHORITY\NETWORK SERVICE和受信任连接。此用户应有权从 RF 数据库中插入和读取数据。

测试

要开始测试运行,请点击测试按钮。

查看结果

如果一切顺利,结果应该会在日志和缓冲表以及 RF 数据库中显示。让我们检查一下所有这些。

SQL 语句

可以通过在RF 接口设置表单上点击日志按钮或从对象设计器打开SQL 语句(123.456.781)表单来打开 SQL 语句日志。

SQL 语句

SQL 语句

表单上的信息显示接口在此运行期间所执行的操作。

缓冲表

当我们从对象设计器打开缓冲表时,我们可以看到接口已将数据从存储行表移动到 RF 拣选行表。

缓冲表

RF 完成拣选RF 异常也填充了来自 RF 数据库的记录。

RF 数据库

最后要检查的是 RF 数据库中的数据。现在两个数据库中的数据应该完全相同。

RF 数据库

这可以从 SQL Server Management Studio 进行检查。

接口面向未来

随着技术的不断发展,接口将变得越来越重要。新技术和更快的互联网连接将使我们能够更好地集成我们的应用程序,但也将使最终用户更容易访问。

云支持的 Microsoft Dynamics NAV

随着版本 2013 R2 的发布,Microsoft Dynamics NAV 现在已支持云。这意味着该产品正式支持在 Microsoft Azure 平台上运行。

摘要

在本章中,我们探讨了 Microsoft Dynamics NAV 如何与其他应用程序接口。

我们讨论了接口的基础知识、导入与导出、数据拉取与数据推送。接口可以手动执行或由定时器或事件执行。

Microsoft Dynamics NAV 支持广泛的接口技术,如文件、自动化控制、.NET、ODBC、ADO 和 Web 服务。

也可能使用 SQL Server 技术进行集成。应用程序服务器(NAS)通常用于与其他系统接口,例如,使用 Microsoft 消息队列活动数据对象ADO)。

产品附带的各种接口已被讨论,包括所有与 Microsoft Office、Exchange 和 SharePoint 的接口。

我们设计和开发了两个业务对业务接口;一个是从 Microsoft Excel 手动导入数据,另一个是使用 ADO 和定时器自动导入和导出数据到另一个数据库。

在设计接口时,可靠性和可追溯性是关键要素。在下一章中,我们将讨论应用程序设计方法和原则。

第十章:应用程序设计

在 Microsoft Dynamics NAV 中,技术和功能是相辅相成的。如果不了解标准组件如何组合在一起,就不可能设计出好的变更或增强应用程序。有了这些知识,我们现在可以开始设计自己的应用程序。

在这本书中,我们讨论了 Microsoft Dynamics NAV 的应用程序设计。我们讨论了设计模式、它是如何工作的以及为什么它会以这种方式工作。我们在详细示例和概念层面上对系统进行了几个小和大变更的设计。

对于这一章,我们将把我们在本书中学到的所有内容整合起来,转化为良好的应用程序设计概念。

我们还将讨论如何处理 Microsoft Dynamics NAV 实施项目以及如何维护应用程序。这需要根据项目包含的定制程度采取不同的方法。

应用程序生命周期

设计应用程序不仅仅是分析流程和开发新对象。这些阶段只是冰山一角。

一旦您的应用程序被设计和开发,很可能会有一家或多家公司开始使用它。当这种情况发生时,您的软件将开始其生命周期中的新阶段。让我们看看 Microsoft Dynamics NAV 应用程序的生命周期:

应用程序生命周期

应用程序的开发阶段从适合/差距分析开始,然后是我们在前面章节中进行的设计构建步骤。当这一切完成时,您的应用程序的维护阶段就开始了。

维护阶段从实施和将软件投入生产开始。第一次发生这种情况,将是贵公司中的 Microsoft Dynamics NAV 实施。一旦完成,您的系统将进入实际的生命周期,其中将对应用程序进行持续的改进。

由于 Microsoft Dynamics NAV 产品的灵活性,这是一个非常特别的程序,在这个过程中很容易遇到许多陷阱。

我们将讨论一些重要的指导原则。共有六个类别:设计以使用、维护、支持、升级、性能和分析。

设计以使用

设计软件不是一个目标,而是一种支持公司开展业务的方式。这使得可用性成为设计应用程序时最重要的关注领域之一。

当谈到可用性时,首先想到的是用户界面。Microsoft Dynamics NAV 2013 有两个常用的界面,即 Windows 客户端和新的 Web 客户端。

页面

页面对象用于定义用户界面。它们在显示方式上非常严格。然而,它们有很多优点。让我们看看一些设计选项:

  • 选项卡:页面有可以同时打开的垂直选项卡,这使得将字段移动到第一个选项卡变得不那么理想。

  • 嵌入式列表:页面的另一个优点是用户总是首先看到嵌入式列表页面,然后继续到卡片,选择记录后在新窗口控件中打开。

  • 重要性:在页面上,可以将控件提升为在选项卡关闭时显示或作为附加控件,以便最终用户必须特别使它们可见。在设计应用程序时,请谨慎使用此功能。

  • 个性化:如果没有限制,所有页面都可以由最终用户个性化,甚至卡片页面。这使得在为公司、部门或最终用户实施时定制页面变得更加容易。个性化不会改变对象定义,也不需要开发者。

让我们根据以下截图中的元素进行浏览,该截图来自第七章,“存储和物流”中的示例附加解决方案:

页面

让我们更详细地看看字段:

  • 操作:可以在页面上执行的所有交易都是操作。一些操作由系统生成,而其他操作由开发者定义。用户可以选择他们想要强调的操作,这使得他们更容易开始使用应用程序。

  • 事实框:每个页面都可以附上无限数量的事实框。事实框可以用来显示关于记录的详细信息。在第七章,“存储和物流”中,路线页面是一个很好的例子,我们可以看到在 Bing 地图中的路线和停靠点的详细信息。

  • 强调:页面上的控件可以被强调为有限的颜色组合、粗体和斜体。

  • 客户端扩展性:页面上的控件可以被.net dll接管。.NET 控件将使用.dll的内容并渲染信息。我们在第九章,“接口”中讨论了客户端扩展性。

  • Web 服务:所有页面都可以公开为 Web 服务。这使得在 Visual Studio、Borland Delphi 或其他可以消费 Web 服务的开发工具中创建自己的用户界面成为可能。

角色中心

当谈到 Microsoft Dynamics NAV 2013 的可用性时,角色中心是应用程序的核心。角色中心是最终用户开始他们工作日并定期返回的地方。让我们讨论本书中创建的角色中心。

Squash 应用程序

Squash Court Role Center 是在第二章,“一个示例应用程序”中创建的,看起来是这样的:

Squash 应用程序

截图的目的是熟悉应用程序中的不同部分

应用屏幕分为两个部分,菜单部分和角色中心部分。

菜单部分是通过将角色中心与主菜单中的操作合并而创建的。通过点击左侧角落的部门,最终用户可以根据安全设置访问整个应用程序。

注意

更多关于主页项的信息可以在博客markbrummel.wordpress.com/2014/07/02/tip-26-grouping-in-the-homeitems/上找到。

角色中心分为左右两部分。左侧通常包含活动和 Microsoft Outlook 的快捷方式。右侧包含指向我的列表页面的快捷方式,显示常用记录和笔记。最终用户可以自定义角色中心并移动部分。

存储和物流

此应用程序有四个不同的角色中心。我们将讨论存储角色中心(123.456.726)。其他角色中心包括物流角色中心(123.456.700)、经理日志和存储角色中心(123.456.756)以及收入和支出角色中心(123.456.761)。

存储和物流

活动存储页面上,员工可以直接访问从堆栈中按日期筛选的文档。从菜单选项中,用户可以创建新文档或打开工作表和日志。

我们设计了两个简短列表页面,我的产品我的区域我的产品可以通过点击小闪电按钮并选择管理列表来更改,如图所示:

存储和物流

我的区域页面基于区域表构建。用户不能更改此列表。页面使用SourceTableTemporary属性和ShowAsTree。这允许用户展开和折叠仓库。

报告

标准的 Microsoft Dynamics NAV 应用程序中的报告是典型的 ERP 报告,显示所需的信息,仅此而已。

设计报告需要特殊技能,并不像看起来那么简单。当从标准应用程序更改报告布局时,最佳实践是保留原始报告不变,并修改保存的副本。

我们将在设计以分析部分进一步讨论报告。

设计以维护

软件被设计和开发出来,却从未改变的情况很少发生。创建的对象通常在应用程序的生命周期中会多次更改。

对现有对象的更改可能是在对象原始开发之后相当长一段时间内进行的。这时,即使更改是由原始开发者进行的,也很难记住某些更改是如何和为什么进行的。

因此,以统一的方式开发非常重要。这将使开发者更容易阅读彼此的代码,或者在数月或数年后理解自己的代码。

到目前为止,编写外部文档是显而易见的,但我们应该意识到这并不总是这样做,并关注更明显、更简单的方法。一个设计良好、构建良好的应用程序应该是自我文档化的。这是通过遵循一些简单的指南来实现的。

命名

在创建新对象时,遵循产品的命名指南非常重要。字段和变量名应该能够自我解释。

小贴士

msdn.microsoft.com/en-us/library/ee414213.aspx上的这篇 MSDN 文章描述了 Microsoft Dynamics NAV 中命名约定的更多细节。

单数和复数

表名应该是单数。这将使 C/AL TABLECAPTION命令返回一个可用的值。让我们看看项目表(27)的例子:

OnDelete()
...
ItemJnlLine.SETRANGE("Item No.","No.");
IF ItemJnlLine.FIND('-') THEN
  ERROR(Text023,TABLECAPTION,"No.",ItemJnlLine.TABLECAPTION);

前面的命令将生成以下错误消息,表标题为单数:

单数和复数

列表页面应该是复数,因为它们包含多个记录,而卡片页面是单数。

保留词

不应在对象中作为字段、变量和函数的名称使用保留词。

注意

微软在msdn.microsoft.com/en-us/library/ee414230.aspx上发布了一份保留词列表。

列表中缺少一个非常重要的保留词,那就是Action。这个保留词用于IF Page.RUNMODAL = ACTION::OK then

名称和缩写

使用标准命名和缩写是应用程序的强项之一,这使得新开发者容易学习。

这里有一些例子:

  • <<表名>>编号:这是对表关系中字段的标准化引用。如果字段与客户有关,该字段称为客户编号;如果与供应商有关,我们使用供应商编号。在我们的示例应用程序中,我们使用了产品编号Squash Player 编号等。

  • 行号:这个字段名总是在流行的 Header/Line 和 Journal 构造中使用。这个字段始终使用页面中的自动拆分键属性。

  • 条目编号:这个字段名总是用于条目和登记表,如 G/L 条目和客户账簿条目。

  • 名称和描述:这是个人或产品的标准命名。

  • 数量/数量:这是衡量数量的标准名称和缩写。

  • (LCY):这是本地货币的缩写。

  • 应缴税额百分比:当字段表示百分比时,这个符号应该在字段名中。

    注意

    一份命名约定列表可以在 MSDN 上找到msdn.microsoft.com/en-us/library/ee414213.aspx

数量与质量

有一个可以应用于软件数量和质量的一般规则,即当向产品添加更多功能时,很难保持一定水平的质量。

数量与质量

为了避免在你的解决方案中出现这种情况,确保你不仅仅在一次发布中将潜在客户的全部需求添加到产品中,而应使用发布策略,确保每次开发、测试和实施小块功能。

松散耦合

在开发附加产品时,将其划分为更小的部分非常重要。这将使多个开发者能够同时工作在应用程序上并发布应用程序的部分。附加产品的每个部分都有自己的框架,与其他附加部分或标准产品交互。

这正是我们在存储和物流示例附加应用程序中所做的。该附加应用程序有三个主要功能区域:存储、物流和收入与支出。这三个区域共享相同的主数据。

每个区域都通过迷你接口与其他应用程序部分交互。使用这个概念在升级到新版本时也将带来巨大的好处,我们将在 设计以升级 部分讨论。

设计以支持

提供支持有不同的级别。一级支持通常由客户现场的 IT 部门人员或对 IT 有感觉的人执行。一般的一级支持问题涉及过滤器、缺失数据等。

二级支持通常是软件中的小错误或设置或主数据中缺少的东西。根据客户的不同,这可能会由内部 IT 部门解决或升级到合作伙伴。

作为开发者,你很可能会在需要调试或逆向工程以找到错误的情况下使用三级支持。

因此,在错误达到开发者之前,其他人已经花费时间分析问题但没有成功。软件的开发应该以这种方式进行,即三级支持使更改或发生最小化。

当使用在 设计以使用设计以维护 部分讨论的指南时,二级支持分析问题将变得更加容易。

二级支持

支持中出现的大部分问题都在二级。一级支持工程师通常非常熟悉系统,而三级支持工程师通常是软件的原始开发者。

二级支持人员需要能够进入数据库并分析问题,而无需改变他们的思维方式。

让我们简要总结一下这个特定主题的一般指南:

  • 快捷键:尽可能使用标准快捷键。例如,使用 F9 进行过账和注册,以及 Ctrl + F7 进行账簿条目。避免使用保留快捷键,如 F8(复制上一个)和 Alt+ F3(过滤到此值)。

  • 屏幕布局:避免过于创意的屏幕布局。屏幕上过多的信息通常表明设计不佳,并且难以支持。典型的例子是多个子页面和基于业务逻辑子页面的元素隐藏。

  • 变量命名:如“设计以维护”部分所述,良好的命名在查看他人的设计时会产生巨大差异。这始于尝试使用微软的标准应用程序命名约定。

  • C/AL 位置:在将 C/AL 代码放置在对象中时,Microsoft Dynamics NAV 非常灵活。页面支持使用 C/AL 代码,以至于可以在那里编写整个过账程序。除非不可能,否则 C/AL 编码应在表或代码单元中进行。

  • 使用函数:当您的 C/AL 代码超出屏幕大小,最佳实践是创建一个函数。这将使原始代码对他人更易读。为您的函数使用一个有意义的名称,这样代码就会自我文档化。一个例子可以在我们讨论的代码单元“登记时间表”(75000)中找到,即第八章,“咨询”。

  • 全局变量与局部变量:在 C/AL 中,变量可以是全局的也可以是局部的。微软没有严格的指导方针说明何时使用哪种。在查看标准应用程序时的一般规则是使用全局变量,除非变量仅在函数中使用——那么它可以是局部的。

    小贴士

    当使用与全局变量同名的地方变量时,编译器不会给出警告。系统将始终首先使用局部变量。

设计以升级

在设计您的应用程序时,这可能不是您首先意识到的事情,但总会有需要将其升级到新版本的时候。

当升级您的应用程序时,我们可以将这项任务分为两部分。第一部分是建立在标准应用程序之上的附加组件部分,即与标准应用程序松散耦合的新表、页面和代码单元。这部分通常很容易升级。另一部分是在基础应用程序中进行的更改。这些更改通常更难迁移到新版本。

微软是否更改了我的(引用的)对象

当分析升级任务时,微软是否更改了您的引用对象是关键问题。如果您修改的对象没有被微软更改,升级将很容易。如果微软稍微更改了对象,我们可能需要分析这些更改以确定我们是否需要做出相应的更改。

每次发布,微软都倾向于重新设计应用程序的一部分。如果您的解决方案与微软重新设计的部分集成,那么将附加组件向前推进将是一项更大的任务。

小贴士

要查看微软在新版本中做出的设计更改,分析升级工具对象以了解其影响。

这里有一些常见重新设计的例子。

CRM(版本 2.0)

在 2.0 版本中,Navision 引入了我们在第四章关系管理中讨论的当前 CRM 应用程序。最重要的变化是在实现新功能的同时,将公司联系人和个人合并到一个表中。

维度(版本 3.x)

在 3.0 版本中,Navision 引入了我们现在所知道的维度解决方案。在此之前,当前的全球维度 1 和 2 被称为部门代码项目代码

货位码(版本 3.x)

随着 WMS 的引入,货位码字段的用法发生了变化。货位码曾经是项目总账条目表(32)中的一个字段,现在移动到了仓库条目。

库存估值(版本 3.x)

在 Microsoft Dynamics NAV 中,没有哪段代码像库存估值解决方案那样变化得如此频繁。尽量在你的附加应用程序中避免更改这一点。

项目跟踪(版本 3.6 和 4.0)

与库存估值一样,项目跟踪也经历了多次变更。在旧版本中,有项目跟踪条目和项目总账条目,而在新版本中,它们被合并到一个表中,正如在第六章贸易中讨论的那样。

虽然这不是一个功能性的变更,但 4.0 版本中 MenuSuite 的引入导致了大量的升级工作。

MenuSuite 不支持 C/AL 代码。这意味着所有日记账都需要为此版本进行更改。

作业(版本 5.0)

如第七章仓储和物流中所述,在 5.0 版本中,作业功能已经发生了变化。在先前的版本中,预算是通过预算条目和阶段、任务和步骤表来完成的。

作业日记账行和作业总账条目没有变化,但在作业过账时,新的作业任务表已成为必填字段。

当没有其他方法时,可以取出作业对象并重新编号为自定义表。这允许您以最小的影响升级到新版本。升级后,可以开始一个新的项目,以完全迁移到新的作业功能。

维度(版本 2013)

2013 版本引入了存储维度的新的设计模式。虽然设计要好得多,但实现这个新模式需要大量的重新设计。

项目成本(几乎所有版本)

在 Microsoft Dynamics NAV 的几乎每个新版本中,项目成本都得到了改进。项目成本的变化升级到新版本很困难,几乎总是需要重新设计。

文档

虽然应用程序的许多部分在升级时不会有问题,但在需要重新设计时,拥有外部文档是有用的。

本文档应包含实施该功能的业务原因信息。有了这些信息,就可以进行新的适配/差距分析。

小贴士

可以将 Microsoft Word 和 Visio 文件等外部文档链接到 C/Side 对象。这样,当开发者需要时,很容易找到文档。

分离运营和财务信息

在存储和物流应用程序中,我们选择了一个可以轻松升级到 Microsoft Dynamics NAV 新版本的数数据和交易模型。

这是通过创建独立的模块,将数据相互移动来实现的。

设计以执行

如果性能不足,所有好的应用程序都是无用的。在设计应用程序时,性能是非常重要的。

当谈到性能时,有两个典型的问题。第一个问题是应用程序整体性能慢,后者是性能良好的应用程序,但用户相互阻塞或创建死锁。

这两个问题都有它们自己的分析和解决方法。我们不会详细讨论这个过程,而是解释如何一般性地避免这些情况。

OLTP 与 OLAP

在任何 ERP 系统中,平衡在线事务处理OLTP)与在线分析处理OLAP)都很重要。当使用 Microsoft Dynamics NAV 时,这一点尤为重要。原因是它的独特数据过账模型,在处理交易的同时创建分析数据。

在实时创建这种分析信息可能具有优势,但当过账交易花费太多时间时,可能并不值得。

分析信息的例子包括维度信息和分析视图条目,但还包括增值税分录和价值分录。尽管它们提供了关于业务的重要信息,但我们处理交易时并不总是需要它们立即出现。

其他分析信息的例子包括二级键和SumIndexFields。所有这些信息将在创建主记录时创建。如果一个总账分录表有 32 个二级键和 15 个SumIndexFields,将需要相当长的时间将这些信息写入数据库。

快速过账交易

良好的性能始于快速的交易。有几种方法可以实现这一点。主要的方法包括:清理未使用的索引和应用程序设置。

清理未使用的索引

数据库中每个二级SumIndexFields字段都需要维护,无论是否使用。Microsoft Dynamics NAV 允许最终用户创建自己的计划来维护这一点。

创建这样的索引计划是一项相当复杂的工作,因此应该由经验丰富的功能开发者来完成。

注意

在 Microsoft Dynamics 5 SP 1 之前的版本中,未使用SumIndexFields的开销与较新版本相比显著更高。

应用程序设置

所有这一切都始于一个稳固的应用程序设置。在 Microsoft Dynamics NAV 中,一些设置功能会在过账交易时导致系统创建更多的分析信息。

例如,更新在过账时用于分析视图。此功能将在创建总账过账条目时同时更新分析视图条目。

应用程序设置

其他例子是库存设置中的自动成本选项。当它们被激活时,每次创建项目账簿条目时,成本都会进行调整。

应用程序设置

作业队列

微软 Dynamics NAV 附带一个出色的多线程作业队列系统。这个过程被称为应用程序服务器,可以在报表和代码单元对象中执行 C/AL 代码。

作业队列任务的例子包括创建分析视图条目、过账库存评估的调整成本,甚至过账销售和采购文件。

背景过账

背景过账是在微软 Dynamics NAV 2013 中引入的,允许您使用作业队列过账文档。这意味着启动过账例程的用户不需要等待此过程完成后再开始新任务。

日期压缩和清理

当一个表中的记录数量超过正常比例时,开始考虑进行数据维护可能是有用的。这是所有 ERP 系统中的正常程序,微软 Dynamics NAV 具有执行此操作的能力。

日期压缩

微软 Dynamics NAV 中的大多数条目表都可以按日期压缩。这意味着所有具有相同值的条目将被替换为一个新条目。之后,详细信息将丢失。

日期压缩

数据压缩

通过更改压缩报告,可以轻松实现保存详细信息的操作。详细信息可以保存在原始表的副本中。

小贴士

数据库的总大小对性能的影响最小。更重要的是,我们在事务中写入的表的大小。

数据清理

微软 Dynamics NAV 允许在创建它的财政年度关闭时删除大部分数据。

可以删除的数据示例包括销售发货和采购收据。它们可以被删除或移动到副本表中。

清理数据可以防止当您的公司在使用微软 Dynamics NAV 一段时间后,交易变得缓慢。数据清理通常在产品使用 5 年后,以及数据库大小超过 100 吉字节时开始。

锁定、块和死锁

微软 Dynamics NAV 产品对阻塞和死锁非常敏感。这与过账模型、本地数据库的继承以及条目表中使用的编号都有关系。

块和死锁主要由数据库中的锁引起。锁定是数据库用来确保数据一致性的机制。

本地服务器与 SQL Server

最初,微软 Dynamics NAV 有一个专有的(本地)数据库。这个数据库不支持行级锁定,只支持表锁定。

Microsoft Dynamics NAV 2013 不再支持此数据库,并且仅在支持行级锁的 SQL Server 上运行。然而,当前的数据和事务模型是为表锁定设计的。

在 SQL Server 上使用行级锁的好处最好在有许多用户在相同数据库中创建文档的系统上体验。数据库中的大多数过账事务都是隔离的,这意味着一次只有一个用户可以从应用程序的任何位置过账文档。

锁定始终针对单个公司执行;除非如第四章中所述共享表,关系管理,来自公司 A 的用户无法锁定公司 B 的用户。

锁定原则

在 Microsoft Dynamics NAV 中,锁定从LOCKTABLE命令开始。使用此命令将生成应用程序生成的 T-SQL 语句,以发出UPDLOCK提示,如果没有此语句,则发出READUNCOMMITTED

让我们创建一个示例来展示如何进行锁定。

  1. 对于这个例子,我们创建一个新的代码单元锁定 A(60000)。该代码单元有一个全局变量 Cust,类型为 Record 18:

    OnRun()
    Cust.LOCKTABLE;
    Cust.GET('10000');
    IF CONFIRM('Maintain Lock in database') THEN;
    
  2. 我们启动这个代码单元并保持确认窗口开启。锁定原则

  3. 现在,我们转到 Windows 客户端,打开客户10000客户卡片并尝试更改名称。10 秒后,我们将收到以下消息:锁定原则

出现此错误消息的原因是其他用户对记录执行了独占锁定。如果我们移动到客户20000,这是数据库中的下一个记录,我们可以安全地更改名称。此记录未锁定。

死锁

让我们进一步分析这个例子并模拟一个死锁。如果用户尝试以不同的顺序锁定彼此的记录,就会发生死锁。

死锁

让我们更详细地看看:

  • 用户 A 读取并锁定客户 10000

  • 用户 B 读取并锁定客户 20000

  • 用户 A 尝试读取并锁定客户 20000,一个阻塞事件开始

  • 当用户 B 现在尝试锁定客户 10000 时,发生死锁

为了演示死锁,我们创建了两个代码单元死锁 A(60001)和死锁 B(60002)。我们需要在同一个 SQL Server 数据库上有两个会话来完成此操作。在一个客户端启动死锁 A:

OnRun()
Cust.LOCKTABLE;
Cust.GET('10000');
IF CONFIRM('Start another client and run codeunit 60002') THEN
  LockOtherCust;

LockOtherCust()
Cust2.GET('20000');
IF CONFIRM('Maintain Lock') THEN;

在另一个客户端启动死锁 B:

OnRun()
Cust.LOCKTABLE;
Cust.GET('20000');
IF CONFIRM('Select Yes on the other client') THEN
  LockOtherCust;

LockOtherCust()
Cust2.GET('10000');
IF CONFIRM('Did the deadlock happen?') THEN;

然后,在两个确认框中选择。现在,其中一个客户端应该会死锁。

小贴士

SQL Server 每 5 秒检查一次死锁,并终止对数据库影响最小的回滚事务。这就是为什么用户有时会体验到死锁速度慢,有时又快的原因。

死锁

错误消息令人困惑,因为它让我们相信我们已经锁定了整个表,这并不正确。

LockOtherCust函数从客户表中读取一个记录,并使用另一个变量。这个新变量Cust2并没有明确发出LOCKTABLE命令。这证明了LOCKTABLE是一个对所有此类变量都有效的交易命令。

Microsoft Dynamics NAV 中的阻塞和死锁

标准应用程序设计了几种内置的阻塞事件。这是为了确保数据库的完整性,并避免死锁。

Microsoft Dynamics NAV 中的两个主要隔离表是 G/L 分录表(17)和项目分录表(32)。

Codeunit 12 在创建 G/L 分录之前显示了这些代码行:

StartPosting(GenJnlLine : Record "Gen. Journal Line")
WITH GenJnlLine DO BEGIN
  GlobalGLEntry.LOCKTABLE;
  IF GlobalGLEntry.FINDLAST THEN BEGIN

我们之前讨论过的 Codeunit Sales-Post 80 和 Purch.-Post 90 都有 G/L 分录表上的可选隔离。

LOCAL LockTables()
SalesLine.LOCKTABLE;
ItemChargeAssgntSales.LOCKTABLE;
PurchOrderLine.LOCKTABLE;
PurchOrderHeader.LOCKTABLE;
GetGLSetup;
IF NOT GLSetup.OptimGLEntLockForMultiuserEnv THEN BEGIN
  GLEntry.LOCKTABLE;
  IF GLEntry.FINDLAST THEN;
END;.

在现实生活中,这意味着公司中没有人可以同时过账到总账。同样的情况也适用于项目账。

注意

blogs.msdn.com/b/nav/archive/2012/10/17/g-l-entry-table-locking-redesign-in-microsoft-dynamics-nav-2013.aspx的博客条目中解释了在过账文档时使用可选的 G/L 锁定功能。

这强调了快速交易和生成分析数据在单独批次中的重要性。

对开发的影响

如果我们将所有这些知识对您开发的影响进行总结,它强调了设计您自己的应用程序结构以与标准应用程序接口的重要性。

当更改和实施标准应用程序时,尽量减少过账过程中的开销。

创建压缩程序,并允许最终用户定期清理记录。在下一节中,我们将讨论如何设计以分析,并允许最终用户在过账交易之外批量生成分析数据。

设计以分析

在 Microsoft Dynamics NAV 中进行分析应始终在(总账)分录记录上进行。有许多类型的分录记录是在交易期间或批量创建的。

避免在文档表上构建分析。在数据库中删除旧数据时,应该始终能够删除而不丢失数据分析的基本信息。

报告设计

在 Microsoft Dynamics NAV 中设计报告是从生成数据集开始的。这是通过表关系构建的,可能会变得相当复杂。

报告设计

当数据集被定义后,第二步是定义布局。创建报告布局超出了本书的范围。

数据集大的报告难以维护,并且由于数据库引擎需要在将信息组合到视图中之前读取所有信息,因此存在性能缓慢的风险。

这可以通过先准备数据然后运行报告来解决。这种方法在数据仓库中相当常见。数据准备可以在作业队列中运行的计划批次中完成。

版本和对象管理

在进行软件开发时,讨论版本管理是不可避免的。Microsoft Dynamics NAV 在这方面很灵活,允许开发者就这个问题做出自己的决定,而不是强迫他们采用一种版本管理方式。

什么是版本

在 Microsoft Dynamics NAV 中,有两种确定版本的方法。第一种也是最简单的方法是在对象每次更改时更改其版本。初始发布的版本是 1.00,每次更改都会递增到 1.01、1.02,依此类推。重大更改将导致版本 2.00。

在 Microsoft Dynamics NAV 中,另一种更常见的方法是将一组对象的版本号分组在一起。当这样做时,应用程序在每次发布时都会增加版本号。这意味着如果一个对象的版本号为 1.01,如果它在 1.02 和 1.03 版本中没有更改,它可以跳到 1.04。

版本编号

在 Microsoft Dynamics NAV 中存在版本编号的规则,尽管这些规则在多年中已经发生了变化。

当前版本原则允许我们使用字母和数字。字母表示产品和国家代码,数字表示版本、子版本和服务包编号。

让我们通过一个示例对象来澄清这一点。代码单元 Whse.-Printed (5779):

NAV W1 3. 70. 01
该对象最后更改的服务包
该对象最后更改的子版本
该对象最后更改的版本
本地化版本
产品名称

如果 Microsoft 在 2013 年的服务包 1 中更改此对象,新的版本号将是 NAVW17.00.01。

合并版本

一个对象可以有多个版本,但每个产品或国家只能有一个版本。本地化对象获得版本 NAVW13.70.01,NAVNL6.00.01。这意味着尽管全球产品团队没有更改对象,但它已被荷兰本地化团队更改。

创建版本

Microsoft Dynamics NAV 中的版本管理是手动进行的。版本号是对象表(2000000001)中的一个可编辑字段,可以自由更改。开发一个工具来做这件事很容易,许多合作伙伴已经在渠道中做到了这一点。

该解决方案的数据和事务模型应该看起来像这样:

创建版本

流程从更改请求开始。这可能包括修复一个小错误或创建一个新功能。对于这个更改请求,需要修改对象。

每个修改过的对象都附带了更改请求。我们可以同时发布多个更改请求。发布中的所有对象都将从发布中获得版本号,这可以在对象表中自动更新。

通过在数据库中保存更改请求和发布信息,我们还将生成文档,这将帮助未来的开发者找到关于对象更改原因的信息。

跟踪对象更改

可以使用 SQL Server 中的触发器跟踪对象更改。所有 C/Side 对象都存储在对象表(2000000001)中。

要将对象更改与更改请求关联起来,开发者应向系统告知他们目前正在处理的请求。这将使我们能够有一个安全可靠的跟踪机制来执行版本管理。

注意

要查看跟踪对象更改的完整解决方案,请访问 dynamicsuser.net/blogs/stryk/archive/2009/05/18/object-auditing.aspx

保存旧版本

为了查看更改,保存版本副本非常有用。除了将文件保存到磁盘上的明显可能性之外,我们还可以使用外部工具。这些工具之一是 Microsoft Team Foundation Server。这是 Visual Studio 家族的一部分,从 2013 版本开始,它可在云端使用,这使得与 Microsoft Dynamics NAV 结合使用非常容易。

保存旧版本

注意

视频博客在 www.youtube.com/user/SorenKlemmensen/video 中演示了如何开始使用 Visual Studio 在线结合 Microsoft Dynamics NAV。

开发方法

开发软件时,有许多方法,如 Prince2、极限编程或微软解决方案框架。

大多数这些方法都适用于 Microsoft Dynamics NAV,但它们应该被正确使用。由于产品的灵活性,很容易省略过程中应该有的步骤。

在 Microsoft Dynamics NAV 中,快速创建和修改商业软件非常容易。这是该解决方案最强大的卖点,但也是最大的陷阱。

一个示例方法

当最终用户请求对应用程序进行更改时,大多数经验丰富的开发者会倾向于直接进入应用程序并创建它,最好是生产数据库中,而不需要文档。这不是软件开发所期望的方式。

然而,Microsoft Dynamics NAV 是一个非常适合原型设计和快速应用程序设计的开发环境。本书中的所有示例应用程序最初都是通过原型设计构建的,然后通过测试最终完成。

如果我们为 Microsoft Dynamics NAV 设计一个合适的发展方法,我们会发现应用程序生命周期完美地符合我们的方法。

在实施微软 Dynamics NAV 时,在开发过程的每一步都让最终用户参与是非常重要的。

一个示例方法

适应性/差距分析

在适应性/差距阶段,通常快速规范就足以描述用户希望系统执行的操作,并在应用程序中生成可能的解决方案。这份文档不应超过两到三页。在原型设计阶段,遇到高级理解是很正常的。如果在初始分析中找到这些,可能会浪费宝贵的时间,而且无论如何都找不到它们。

原型设计

使用快速规范,开发者可以创建一个草案解决方案,而不必过于详细。这应该足以展示解决方案完成后的样子。这通常会导致新的问题和想法,这些应该被仔细考虑并纳入完整规范,或者首先构建一个新的原型。

开发

根据原型后的更改量,开发通常可以从已经完成的工作开始。在这个阶段,所有细节都应该被处理并测试。

在微软 Dynamics NAV 中开发时,没有为开发者准备的完整清单,但我们可以尝试根据以下字段创建一个:

  • 标题/翻译:确保所有对象都填写了所需的标题和翻译。

  • 表关系:确保所有表关系都已设置妥当,并且也要检查总账分录和行表,因为它们经常被遗忘。

  • 修改和删除触发器:如果用户修改或删除记录会发生什么。确保在OnModifyOn Delete C/AL触发器中妥善处理所有内容。OnRename应该由 C/Side 自动处理。重命名具有许多表关系的表可能会导致数据库严重锁定。如果用户不应重命名记录,这可以通过在OnRename触发器中放置一个ERROR命令来阻止。

  • LookupPageIDDrillDownPageID:即使在运行 Windows 客户端时,分配查找和钻取页面 ID 也很重要。查找页面用于表关系,而钻取页面用于从 SUM 流字段钻取时使用。

  • CardPageID:当用户选择菜单项或提示时,Windows 客户端总是启动一个列表页面。双击一行将打开相关的卡片页面。这由列表页面上的CardPageID属性控制。

  • 字段组:在输入值时显示表关系记录,Windows 客户端不会直接使用LookupPageID,而是首先显示一个下拉列表。列表中的字段定义在相关的表字段组中。每个表只能有一个名为DropDown字段组,如下截图所示:开发

  • 操作:操作应该对最终用户有意义。请确保您使用逻辑名称,并避免创建仅针对超级用户且会混淆最终用户的菜单按钮和操作。操作应放置在正确的容器中。仅推广所有最终用户都将频繁使用的操作。

  • 快捷键:始终分配带符号的(&)快捷键,并避免双重快捷键。在使用功能键如F3F9时,请遵循 Microsoft 标准约定。

  • 压缩过账:如果您的解决方案将生成大量潜在数据,请确保提供压缩、过账或清理例程,以便最终用户可以定期维护数据。

  • 权限:您的解决方案是否需要在系统中设置额外的权限?在交付解决方案时,请确保记录这一点。

  • 未使用变量:确保在 C/AL 对象中不要留下未使用的变量。虽然它们不会破坏功能,但会使软件未来的维护变得更加复杂。

  • 查找命令锁定:在发布软件之前,请务必检查正确查找命令的使用。使用错误的命令并将锁定留给数据库引擎可能会导致额外的性能开销。

关于这些功能的更详细信息在书籍《Programming Microsoft Dynamics NAV 2013》中有解释,作者是 David A. Studebaker、Christopher D. Studebaker,由 Packt Publishing 出版。

测试

测试可能是应用程序设计中最重要的但被低估的任务之一。

测试涉及以下三个条件:

  • 软件是否符合原始要求?如果不满足,继续测试就没有意义了。

  • 它是否按预期工作?这包括故意尝试破坏解决方案。如果软件不是“猴子测试”过的,那么在使用时肯定会出错。这里适用墨菲定律:“可能出错的事情,一定会出错。”

  • 它是否适合应用程序的其他部分?软件是否易于使用且直观?一个无错误但难以使用的解决方案将非常昂贵来维护。

应使用测试性框架自动进行测试。这允许开发者在每次进行更改时重新运行完整的应用程序测试。

注意

博客blogs.msdn.com/b/nav/archive/2012/11/07/application-test-toolset-for-microsoft-dynamics-nav-2013.aspx解释了如何安装和使用 Microsoft Dynamics NAV 2013 的测试性框架。

测试也应手动进行,由喜欢做这项工作且有时间的人来完成。如果有人被要求测试软件,而他正忙于正常工作,那么错误滑过的可能性相当高。

注意

使用测试性框架进行测试是获得 Microsoft Dynamics 认证过程的一个强制性部分。

修复错误的成本随着软件的发展而增加。越早修复错误,越好。

测试

实施

在更改开发和测试完成后,文档应该最终确定。这可以是面向最终用户的手册或面向未来开发人员和支持工程师的技术参考。

最终用户应该接受培训以使用该软件。

维护和支持

软件实施和用户培训完成后,解决方案进入维护和支持阶段。在这个阶段,应用经理需要负责处理解决方案生成数据,分析它,并定期清理数据。

如果最终用户要求对解决方案进行更改,循环又重新开始。

项目

实施像微软 Dynamics NAV 这样的 ERP 产品不仅仅是安装软件包并开始使用它。贵公司的每个部分都必须做出决定,如何将他们的工作与软件集成。这往往会导致贵公司工作方式的一个有趣的新面貌。

标准化、定制化或两者兼而有之

有几种实施 Dynamics NAV 的方法。做出决定选择哪种类型的实施非常重要,并相应地调整实施。

标准化、定制化或两者兼而有之

与 1995 年首次推出时相比,微软 Dynamics NAV 2013 是一个成熟的 ERP 软件包,包含了我们在本书中讨论的所有内置功能。在这个标准产品之上,经销商构建了水平和垂直解决方案,称为附加产品。这两个结合为不能使用标准产品但足够灵活使用垂直解决方案的公司提供了强大的解决方案。

附加产品

垂直解决方案多年前开始作为一家决定实施微软 Dynamics NAV 的公司定制的解决方案。与实施合作伙伴一起,这些公司已经根据他们的需求定制了产品。

许多这些附加产品现在已经成为适合垂直行业的成熟软件解决方案。

在购买附加解决方案时,向经销商提出一些问题是个好主意:

  • 释放程序是什么?一个可靠的附加解决方案有一个释放程序。大多数经销商每半年或更早进行定期发布。如果在软件中发现错误,应该有一个热修复。大多数经销商都有他们支持的发布。确保了解哪些版本仍然受到支持。

  • 我如何升级到垂直解决方案的新版本?如果发布了新的垂直解决方案版本,应该有一个升级程序。这应该由经销商明确记录并测试。

  • 我是否可以修改软件?大多数附加产品经销商不建议客户修改软件。这样做的原因是修复错误和升级的复杂性增加。

  • 如果我改变软件怎么办?如果附加解决方案无论如何都进行了定制,它基本上是从受支持的附加解决方案降级为定制数据库。对于大多数经销商来说,支持这些定制解决方案是困难的。

定制

虽然不建议总是定制附加解决方案,但定制微软 Dynamics NAV 不应被视为不良实践。

在微软 Dynamics NAV 中的定制影响可能产生的差异,可以与定制或量身定做的西装相提并论。拥有一个完全符合组织的 ERP 软件包的好处可能比解决方案拥有成本的增加更重要。

总拥有成本

微软 Dynamics NAV 的总拥有成本高度依赖于定制的程度。由经验丰富的顾问完成的一个或两个优秀的附加产品,非定制实施将对您的公司影响较小,并且易于维护和支持。

定制的程度越高,保持应用程序运行的成本就越高。这本身并不是坏事。如果您的公司有独特的方式做生意,它可能需要一个支持这种独特性的 ERP 软件包。

可重复性之路程序

可重复性之路R2R)是由微软引入,以帮助合作伙伴提高可重复性。尽管该计划主要侧重于市场营销,但它说明了将相同的微软 Dynamics NAV 软件包重新销售给多个客户的趋势。

成功路线图

在微软 Dynamics NAV 中设计一个稳固的应用程序,始于对标准应用程序功能及其设计理念的深入了解。

其次,我们需要仔细分析我们想要支持的业务流程,并逐步实施新功能以确保良好的质量,因为解决方案变得更大、更成熟。

使用与微软 Dynamics NAV 类似的数据和发布模型,并尝试保持类似的用户界面。这将使最终用户更容易采用您的解决方案,并且软件更容易维护和支持。

最后但同样重要的是,在您的数据库中执行良好的维护。定期压缩和清理数据,以确保系统现在和未来的稳定性能。

摘要

在这本书中,我们介绍了标准微软 Dynamics 的功能和技术设计,以及如何扩展应用程序以取得成功。

这本书尚未完成。出版后,我们将定期撰写基于本书信息的文章、技巧和窍门,发布在www.brummelds.com

关于本书中发布的信息有任何问题或评论,也可以在那里发布和讨论。

附录 A. 安装指南

使用本书,我们提供可以使用 Microsoft Dynamics NAV 2013 发布 2 W1 演示版安装的开发示例。

此演示版本可以从 msdn.microsoft.com 下载。

许可证

微软对在 Microsoft Dynamics NAV 中使用和开发有非常严格的许可证规定。

为了教育目的,允许您使用编号为 123.456.700 至 123.456.799 的新对象使用 MSDN 许可证来开发新对象。

安装 Microsoft Dynamics NAV

从 MSDN 网站下载产品 CD 后,启动 setup.exe 文件。从安装选项中选择安装演示,如下面的截图所示:

安装 Microsoft Dynamics NAV

更改许可证

安装完成后,我们可以使用开发环境和 Windows 客户端。我们使用开发环境进行管理和开发。用户界面是使用 Windows 客户端创建的。

每个 Microsoft Dynamics NAV 服务器实例都在许可证文件上运行。此文件确定我们在系统中的访问权限。安装的演示许可证允许我们访问所有功能,但不能访问 C/AL 代码。

要访问所有 C/AL 代码,我们需要官方合作伙伴开发许可证。要获取此许可证,我们必须注册为合作伙伴并开始作为分销商。如果我们不希望这样做,我们可以使用 MSDN 许可证。

MSDN 许可证将允许访问为本书开发的所有新对象。使用此许可证无法访问基础应用程序更改示例。

要更改许可证,导航到经典客户端,打开工具菜单,并选择许可证信息,如下面的截图所示:

更改许可证

许可证信息

这将打开许可证信息屏幕,我们可以选择上传,这将打开一个文件对话框,我们可以选择 MSDN 许可证。

注意

要在经典客户端上启用许可证文件,请重启应用程序。

重启服务层

要在角色定制客户端上启用许可证文件,我们需要重启服务层。这可以通过 Windows 控制面板中的服务窗口完成,如下面的截图所示:

重启服务层

安装对象

这本书包含三个 Microsoft Dynamics NAV 对象文件、两个 DLL 文件、一个 SQL Server 脚本和一些安装辅助文件。具体如下:

  • Chapter2-4.fob:此文件包含用于第二章、“一个示例应用程序”、第三章、“财务管理”和第四章、“关系管理”中的网球场示例。

  • Chapter7-9.fob:此文件包含用于第七章,存储和物流的存储和物流应用程序以及第九章,接口的示例接口。我们需要额外的 SQL Server 脚本以运行 ADO 示例。

  • Chapter8.fob:此文件包含第八章的作业扩展,咨询。本章还需要安装额外的 DLL 文件。

  • RF database.sql:这是在第九章,接口中使用的 SQL Server 脚本,用于创建 RD 数据库并创建示例数据。

  • MSDN.flf:这是我们可以用来访问编号从 123.456.700 到 123.456.799 的自定义对象的 MSDN 许可证。

  • NavMaps.dllVEControl.dll:这是我们需要的动态链接库文件,用于第七章,存储和物流

  • Pin1.gif – Pin5.gif:这些是显示在必应地图上的图标。

导入 FOB 文件

要安装对象,首先在经典客户端中打开对象设计器,通过从工具菜单中选择对象设计器Shift + F12)选项,如图所示:

导入 FOB 文件

当对象设计器处于活动状态时,文件菜单显示一些附加选项:

导入 FOB 文件

文件菜单选项

我们选择导入,这将打开一个文件对话框。现在,选择您想要导入的.fob文件。

如果一切正常,以下对话框应该出现:

导入 FOB 文件

选择,此对话框确认导入。

安装动态链接库文件

为了支持必应地图客户端插件、地理编码和距离计算,我们提供了以下两个 DLL 文件、五个 GIF 文件和一些安装支持文件:

  • NavMaps.dll

  • VEControl.dll

  • pin1.gif

  • pin2.gif

  • pin3.gif

  • pin4.gif

  • pin5.gif

  • RegisterDll.bat

这些文件现在应放置在这个文件夹中:

安装动态链接库文件

注册 NavMaps.dll

要注册此 DLL,我们使用RegAsm。该命令在RegisterDll.bat文件中预定义,我们可以执行它。

注册 VEControl.dll

要注册可视化地图控件,我们在客户端插件表(2000000069)中添加引用。为此,我们可以从对象设计器中运行该表,如图所示:

注册 VEControl.dll

从对象设计器运行表

posted @ 2025-10-27 09:04  绝不原创的飞龙  阅读(28)  评论(0)    收藏  举报