ERP系统的单据具备标准的功能,这里的单据可翻译为Bill,Document,Entry,具备相似的工具条操作界面。通过设计可复用的基类,子类只需要继承基类窗体即可完成单据功能的程序设计。先看标准的销售合同单据界面:

本篇通过销售合同单据功能,依次讲解编程要点,供参考。

1 新增 Insert

窗体有二种状态,一种是编辑状态,别一种是数据浏览状态,区别在于编辑状态的窗体数据被修改(dirty),在窗体关闭时需要保存数据。点击工具条的新增(Insert)按钮,窗体进入编辑状态。新增状态需要对窗体所编辑的单据设置默认值。一般我们在实体映射文件中设置默认值,参考下面的例子代码:

public partial class SalesContractEntity
{
protected override void OnInitialized()
{
base.OnInitialized(); // Assign default value for new entity
if (Fields.State == EntityState.New)
{
#region DefaultValue
// __LLBLGENPRO_USER_CODE_REGION_START DefaultValue
this.Fields[(int) SalesContractFieldIndex.Closed].CurrentValue = false;
// __LLBLGENPRO_USER_CODE_REGION_END
#endregion
}
}
}
 
 

也可以考虑在窗体中做默认值设定。当遇到这样一种场景,两个功能对应同一个实体类型,则需要在界面中根据需要初始化值,参考下面的程序片段。

protected override EntityBase2 Add()
{
base.Add();
this._inventoryMovement = new InventoryMovementEntity();
this._inventoryMovement.TranType = GetStringValue(InventoryTransactionType.Movement); return this._inventoryMovement;
}
 
 

2 保存 Save

窗体基类检测到界面中的控件值被修改过,窗体状态变为编辑状态,点击保存按钮执行保存方法。保存方法的主要内容是将数据源控件(BindingSource)所绑定的控件值更新到它映射的实体中,再调用窗体保存方法保存实体。

protected override EntityBase2 Save(EntityBase2 entityToSave, EntityCollection entitiesToDelete)
{
SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToSave;
this._salesContractEntity = this._salesContractEntityManager.SaveSalesContract( SalesContractEntity, entitiesToDelete, SeriesCode);
return this._salesContractEntity;
}
 

entityToSave是数据源控件绑定的实体类型,在保存完成后,这个值再次绑定到数据源控件中。

3  删除 Delete

窗体只有在浏览状态时,才可以点击删除按钮,删除按钮的内容是获取窗体数据源控件绑定的实体,调用窗体删除方法。

protected override void Delete(EntityBase2 entityToDelete)
{
base.Delete(entityToDelete);
SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToDelete;
this._salesContractEntityManager.DeleteSalesContract(SalesContractEntity);
}




对实体的任何操作,都会跑实体验证类型,比如在保存时,需要验证主键值是否已经保存过,参考下面的代码。

public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
{
base.ValidateEntityBeforeSave(involvedEntity);
SalesContractEntity salesContract = (SalesContractEntity)involvedEntity;
    if (string.IsNullOrEmpty(salesContract.ContractNo))
throw new EntityValidationException("Contract No. is required");
if (string.IsNullOrEmpty(salesContract.CustomerNo))
throw new EntityValidationException("Customer No. is required"); if (salesContract.IsNew)
{
ISalesContractManager salesContractManager = CreateProxyInstance<ISalesContractManager>();
if (salesContractManager.IsSalesContractExist(salesContract.ContractNo))
throw new RecordDuplicatedException(salesContract.ContractNo, "Cotract No. is already used");
}
}
 
 

4 复制 Clone

窗体支持两种复制方法,复制当前加载的值,复制其它对象的值。对象值复制完成后,需用重置新的对象的主键值,让它为空或是为默认值,供用户修改。复制其它对象的值需要弹出对象选择窗体。这两种复制都需要注意复制完后,重置对象初始化默认值。因为复制时采用的是深拷贝,没有跑对象初始化值。

protected override object Clone(Dictionary<string, string> refNo)
{
base.Clone(refNo); string receiptRefNo;
refNo.TryGetValue("ContractNo", out receiptRefNo); if (string.IsNullOrEmpty(receiptRefNo))
{
using (ILookupForm lookup = GetLookupForm("SalesContractLookup"))
{ if (!AllowViewAllTransaction)
lookup.PredicateBucket = new RelationPredicateBucket(SalesContractFields.CreatedBy == Shared.CurrentUser.UserId); lookup.SetCurrentValue(CurrentRefNo); if (lookup.ShowDialog() != DialogResult.OK)
                   return null;
receiptRefNo = lookup.GetFirstSelectionValue();
}
} if (!string.IsNullOrEmpty(receiptRefNo))
{
this._salesContractEntity = this._salesContractEntityManager.CloneSalesContract(receiptRefNo);
return this._salesContractEntity;
} return null;
}
 

注意到这些方法全部是以override重写的方式出现,被基类调用。每个方法都运行在后台线程控件BackgroundWorker线程中,所以都不能操作界面控件。

5  记录浏览 Record Navigator

主要用于工具条的前四个按钮,分别对应第一笔数据,前一笔数据,下一笔数据,最后一笔数据。工具条浏览需要设定窗体的NavigateBindingSource属性,传入一个空白的BindingSource控件或是一个装载页面所有数据的BindingSource控件,工具条浏览方法重写参考下面的程序片段。

protected override void InitNavigator(InitNavigatorArgs args)
{
base.InitNavigator(args);
args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending);
args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == false);
}

6 记录过帐 Record Post

主要用于业务单据过帐逻辑,基类的方法为空方法,不同的业务单据有不同的逻辑定义,没有可复用的代码。

protected override void Post(EntityBase2 entityToPost)
{
base.Post(entityToPost);
SalesContractEntity resignEntity = entityToPost as SalesContractEntity;
_salesContractEntityManager.PostSalesContract(resignEntity);
}

这里的过帐可以理解为确认,批核,不可更改的意思。在有些系统中叫送审,审核。

7  打印 Print

一般在设计视图绑定当前窗体对应的水晶报表文件以及要传入的参数。也可以通过重写打印方法传入传数值。

protected override void Print(ref Dictionary<string, SelectionFormula> selectionFormulas, ref List<FormulaField> formulaFields, ref List<ParameterField> parameterFields)
{
base.Print(ref selectionFormulas, ref formulaFields, ref parameterFields);
}
 

重写方法常用于动态指定报表文件,或是动态的参数值。不推荐在代码中这样写,这样做导致每次都需要重新编译和分发程序,推荐在水晶报表中做公式,在分发时只需要拷贝文件即可。

解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计的更多相关文章

  1. 解析大型.NET ERP系统 单据编码功能实现

    单据编码是ERP系统中必备的功能,用于生成各种单据的流水号,常常借助于日期时间等字符来生成一个唯一的单据号码.从软件的角度来说,就是为生成数据表的主键值(参考编号),从用户的角度来说,就是给业务单据制 ...

  2. 解析大型.NET ERP系统 十三种界面设计模式

    成熟的ERP系统的界面应该都是从模板中拷贝出来的,各类功能的界面有规律可遵循.软件界面设计模式化或是艺术性的创作,我认可前者,模式化的界面客户容易举一反三,降低学习门槛.除了一些小部分的功能界面设计特 ...

  3. 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

    企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...

  4. 解析大型.NET ERP系统架构设计 Framework+ Application 设计模式

    我对大型系统的理解,从数量上面来讲,源代码超过百万行以上,系统有超过300个以上的功能,从质量上来讲系统应该具备良好的可扩展性和可维护性,系统中的功能紧密关联.除去业务上的复杂性,如何设计这样的一个协 ...

  5. 解析大型.NET ERP系统 权限模块设计与实现

    权限模块是ERP系统的核心模块之一,完善的权限控制机制给系统增色不少.总结我接触过的权限模块,以享读者. 1 权限的简明定义 ERP权限管理用一句简单的话来说就是:谁 能否 做 那些 事. 文句 含义 ...

  6. 解析大型.NET ERP系统 20条数据库设计规范

    数据库设计规范是个技术含量相对低的话题,只需要对标准和规范的坚持即可做到.当系统越来越庞大,严格控制数据库的设计人员,并且有一份规范书供执行参考.在程序框架中,也有一份强制性的约定,当不遵守规范时报错 ...

  7. 解析大型.NET ERP系统 设计异常处理模块

    异常处理模块是大型系统必备的一个组件,精心设计的异常处理模块可提高系统的健壮性.下面从我理解的角度,谈谈异常处理的方方面面.我的设计仅仅限定于Windows Forms,供参考. 1 定义异常类型 . ...

  8. 解析大型.NET ERP系统 业务逻辑设计与实现

    根据近几年的制造业软件开发经验,以我开发人员的理解角度,简要说明功能(Feature)是如何设计与实现的,供参考. 因架构的不同,技术实现上会有所差异,我的经验仅限定于Windows Form程序. ...

  9. 解析大型.NET ERP系统 通用附件管理功能

    大型系统具备一个通用的附件管理功能,对于单据中无法清晰表达的字段,用一个附件图片或附件文档表示是最好的方法了.比如物料清单附加一张CAD图纸,销售订单评审功能中附加客户的各种表格,通用附件功能对系统起 ...

随机推荐

  1. 01.SQLServer性能优化之---水平分库扩展

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 第一次引入文件组的概念:http://www.cnblogs.com/dunitian/ ...

  2. [APUE]进程控制(上)

    一.进程标识 进程ID 0是调度进程,常常被称为交换进程(swapper).该进程并不执行任何磁盘上的程序--它是内核的一部分,因此也被称为系统进程.进程ID 1是init进程,在自举(bootstr ...

  3. 告别被拒,如何提升iOS审核通过率(上篇)

    iOS审核一直是每款移动产品上架苹果商店时面对的一座大山,每次提审都像是一次漫长而又悲壮的旅行,经常被苹果拒之门外,无比煎熬.那么问题来了,我们有没有什么办法准确把握苹果审核准则,从而提升审核的通过率 ...

  4. ASP.NET MVC 请求路径相关参数的获取

    Request.ApplicationPath / Request.CurrentExecutionFilePath /Home/Index Request.FilePath /Home/Index ...

  5. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  6. DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对?

    写在前面 阅读目录: 具体业务场景 业务需求变化 "愚蠢"的应对 消息列表实现 消息详情页实现 消息发送.回复.销毁等实现 回到原点的一些思考 业务需求变化,领域模型变化了吗? 对 ...

  7. ABP文档 - EntityFramework 集成

    文档目录 本节内容: Nuget 包 DbContext 仓储 默认仓储 自定义仓储 特定的仓储基类 自定义仓储示例 仓储最佳实践 ABP可使用任何ORM框架,它已经内置了EntityFrame(以下 ...

  8. Entity Framework 延伸系列目录

    1.采用MiniProfiler监控EF与.NET MVC项目 2.采用EntityFramework.Extended 对EF进行扩展 3.EntityFramework执行存储过程中遇到的那些坑 ...

  9. JavaScript基础知识总结(二)

    JavaScript语法 二.数据类型 程序把这些量.值分为几大类,每一类分别叫什么名称,有什么特点,就叫数据类型. 1.字符串(string) 字符串由零个或多个字符构成,字符包括字母,数字,标点符 ...

  10. SSIS 包部署 Package Store 后,在 IS 中可以执行,AGENT 执行却报错

    可以执行 SSIS Package ,证明用 SSIS Package 的账户是可以执行成功的.SQL Server Agent 默认指定账号是 Network Service. 那么可以尝试一下将 ...