diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDTO.cs new file mode 100644 index 000000000..0a9ec1c80 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDTO.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +/// +/// 注塑发料任务 +/// +[Display(Name = "注塑发料任务")] +public class InjectionJobDTO : SfsJobDTOBase +{ + /// + /// 叫料请求类型 + /// + [Display(Name = "叫料请求类型")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string RequestType { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 要货单号 + /// + [Display(Name = "要货单号")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string InjectionRequestNumber { get; set; } + + /// + /// 车间 + /// + [Display(Name = "车间")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Workshop { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDetailDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDetailDTO.cs new file mode 100644 index 000000000..363521370 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/DTOs/InjectionJobDetailDTO.cs @@ -0,0 +1,112 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionJobDetailDTO : SfsJobRecommendFromDetailDTOBase, IHasToLocation +{ + + /// + /// 请求库位 + /// + [Display(Name = "请求库位")] + public string RequestLocationCode { get; set; } + + /// + /// 到库位 + /// + [Display(Name = "到库位")] + public string ToLocationCode { get; set; } + + /// + /// 到库区 + /// + [Display(Name = "到库区")] + public string ToLocationArea { get; set; } + + /// + /// 到库位组 + /// + [Display(Name = "到库位组")] + public string ToLocationGroup { get; set; } + + /// + /// 到ERP库位 + /// + [Display(Name = "到ERP库位")] + public string ToLocationErpCode { get; set; } + + /// + /// 到仓库 + /// + [Display(Name = "到仓库")] + public string ToWarehouseCode { get; set; } + + /// + /// 在途库库位 + /// + [Display(Name = "在途库库位")] + public string OnTheWayLocationCode { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + public DateTime ExpiredTime { get; set; } + + /// + /// 工序 + /// + [Display(Name = "工序")] + public string Operation { get; set; } + + /// + /// 配送方式 + /// + [Display(Name = "配送方式")] + public EnumDistributionType DistributionType { get; set; } + + /// + /// 取整方式 + /// + [Display(Name = "取整方式")] + public EnumTruncType TruncType { get; set; } + + /// + /// 取整后数量 + /// + [Display(Name = "取整后数量")] + public decimal RoundedQty { get; set; } + + /// + /// 计划拆分规则 + /// + [Display(Name = "计划拆分规则")] + public EnumPlannedSplitRule PlannedSplitRule { get; set; } + + /// + /// 计划开始时间 + /// + [Display(Name = "计划开始时间")] + public DateTime PlanBeginTime { get; set; } + + /// + /// 每次配送数量 + /// + [Display(Name = "每次配送数量")] + public decimal DeliveryQty { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/IInjectionJobAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/IInjectionJobAppService.cs new file mode 100644 index 000000000..a238cf57b --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/IInjectionJobAppService.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public interface IInjectionJobAppService + : ISfsJobAppServiceBase +{ + Task> CheckJobExistByItemCodeAndLocationCode(string itemCode, string locationCode); + + Task CancelByMaterialRequestAsync(string injectionNumber); + + Task> GetListByTypeAsync(SfsJobRequestInputBase requestInput, string requestType, + bool includeDetails = false, CancellationToken cancellationToken = default); + + Task> GetByRequestNumberAsync(string requestNumber); + + /// + /// 保存拆箱时涉及的明细修改 + /// + /// + Task SaveDetail_SplitPackingAsync(SplitPacking_UpdateJobDetailInput input); + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/InjectionJobPermissions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/InjectionJobPermissions.cs new file mode 100644 index 000000000..31b455675 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/InjectionJobPermissions.cs @@ -0,0 +1,27 @@ +using Volo.Abp.Authorization.Permissions; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public static class InjectionJobPermissions +{ + + public const string Default = StorePermissions.GroupName + "." + nameof(InjectionJob); + public const string Create = Default + "." + StorePermissions.CreateStr; + public const string Update = Default + "." + StorePermissions.UpdateStr; + public const string Delete = Default + "." + StorePermissions.DeleteStr; + + //自动发料任务 + public const string AutoInjectionJob = StorePermissions.GroupName + "." + nameof(AutoInjectionJob); + + public static void AddInjectionJobPermission(this PermissionGroupDefinition permissionGroup) + { + var InjectionJobPermission = permissionGroup.AddPermission(Default, StorePermissionDefinitionProvider.L(nameof(InjectionJob))); + InjectionJobPermission.AddChild(Create, StorePermissionDefinitionProvider.L(StorePermissions.CreateStr)); + InjectionJobPermission.AddChild(Update, StorePermissionDefinitionProvider.L(StorePermissions.UpdateStr)); + InjectionJobPermission.AddChild(Delete, StorePermissionDefinitionProvider.L(StorePermissions.DeleteStr)); + + permissionGroup.AddPermission(AutoInjectionJob, StorePermissionDefinitionProvider.L(nameof(AutoInjectionJob))); + + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobCheckInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobCheckInput.cs new file mode 100644 index 000000000..45aff0d7e --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobCheckInput.cs @@ -0,0 +1,6 @@ +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionJobCheckInput : SfsJobCheckInputBase +{ + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobDetailInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobDetailInput.cs new file mode 100644 index 000000000..38890b9a9 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobDetailInput.cs @@ -0,0 +1,134 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionJobDetailInput : SfsJobRecommendFromDetailInputBase, IHasToLocation +{ + + /// + /// 请求库位 + /// + [Display(Name = "请求库位")] + [Required(ErrorMessage = "{0}是必填项")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string RequestLocationCode { get; set; } + + /// + /// 到库位 + /// + [Display(Name = "到库位")] + [Required(ErrorMessage = "{0}是必填项")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ToLocationCode { get; set; } + + /// + /// 到库区 + /// + [Display(Name = "到库区")] + public string ToLocationArea { get; set; } + + /// + /// 到库位组 + /// + [Display(Name = "到库位组")] + public string ToLocationGroup { get; set; } + + /// + /// 到ERP库位 + /// + [Display(Name = "到ERP库位")] + [Required(ErrorMessage = "{0}是必填项")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ToLocationErpCode { get; set; } + + /// + /// 到仓库 + /// + [Display(Name = "到仓库")] + [Required(ErrorMessage = "{0}是必填项")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ToWarehouseCode { get; set; } + + /// + /// 在途库库位 + /// + [Display(Name = "在途库库位")] + public string OnTheWayLocationCode { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + [Required(ErrorMessage = "{0}是必填项")] + public DateTime ExpiredTime { get; set; } + + /// + /// 工序 + /// + [Display(Name = "工序")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Operation { get; set; } + + /// + /// 配送方式 + /// + [Display(Name = "配送方式")] + public EnumDistributionType DistributionType { get; set; } + + /// + /// 取整方式 + /// + [Display(Name = "取整方式")] + public EnumTruncType TruncType { get; set; } + + /// + /// 取整后数量 + /// + [Display(Name = "取整后数量")] + public decimal RoundedQty { get; set; } + + /// + /// 计划拆分规则 + /// + [Display(Name = "计划拆分规则")] + public EnumPlannedSplitRule PlannedSplitRule { get; set; } + + /// + /// 计划开始时间 + /// + [Display(Name = "计划开始时间")] + public DateTime PlanBeginTime { get; set; } + + /// + /// 每次配送数量 + /// + [Display(Name = "每次配送数量")] + public decimal DeliveryQty { get; set; } + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobEditInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobEditInput.cs new file mode 100644 index 000000000..ebc95b6c7 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/InjectionJobs/Inputs/InjectionJobEditInput.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionJobEditInput : SfsJobCreateUpdateInputBase, ISfsJobCreateInput +{ + #region Create + /// + /// 上游任务编号 + /// + [Display(Name = "上游任务编号")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string UpStreamJobNumber { get; set; } + + /// + /// 要货单号 + /// + [Display(Name = "要货单号")] + [Required(ErrorMessage = "{0}是必填项")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string InjectionRequestNumber { get; set; } + + /// + /// 叫料请求类型 + /// + [Display(Name = "叫料请求类型")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string RequestType { get; set; } + + /// + /// 任务类型 + /// + [Display(Name = "任务类型")] + [Required(ErrorMessage = "{0}是必填项")] + public EnumJobType JobType { get; set; } + + /// + /// 是否自动完成 + /// + [Display(Name = "是否自动完成")] + [Required(ErrorMessage = "{0}是必填项")] + public bool IsAutoComplete { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + [Required(ErrorMessage = "{0}是必填项")] + public DateTime ExpiredTime { get; set; } + + /// + /// 任务明细 + /// + [Display(Name = "任务明细")] + [Required(ErrorMessage = "{0}是必填项")] + public List Details { get; set; } = new(); + + /// + /// 车间 + /// + [Display(Name = "车间")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Workshop { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + #endregion +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDTO.cs new file mode 100644 index 000000000..96f533b19 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDTO.cs @@ -0,0 +1,54 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionNoteDTO : SfsStoreDTOBase, IHasJobNumber, IHasRequestNumber +{ + + /// + /// 任务ID + /// + [Display(Name = "任务ID")] + public string JobNumber { get; set; } + + /// + /// 车间 + /// + [Display(Name = "车间")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Workshop { get; set; } + + /// + /// 请求代码 + /// + [Display(Name = "请求代码")] + public string RequestNumber { get; set; } + + /// + /// 叫料请求类型 + /// + [Display(Name = "叫料请求类型")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string RequestType { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + + /// + /// 已确认 + /// + [Display(Name = "已确认")] + public bool Confirmed { get; set; } + + /// + /// 确认时间 + /// + [Display(Name = "确认时间")] + public DateTime? ConfirmTime { get; set; } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDetailDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDetailDTO.cs new file mode 100644 index 000000000..e36d271b3 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/DTOs/InjectionNoteDetailDTO.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionNoteDetailDTO : SfsStoreRecommendFromDetailWithFromToDTOBase +{ + + /// + /// 发料时间 + /// + [Display(Name = "发料时间")] + public DateTime IssueTime { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + public DateTime ExpiredTime { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string WorkStation { get; set; } + + /// + /// 在途库库位 + /// + [Display(Name = "在途库库位")] + public string OnTheWayLocationCode { get; set; } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/IInjectionNoteAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/IInjectionNoteAppService.cs new file mode 100644 index 000000000..8d88bd51d --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/IInjectionNoteAppService.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public interface IInjectionNoteAppService : ISfsStoreMasterReadOnlyAppServiceBase +{ + Task CreateAsync(InjectionNoteEditInput input); + + Task ConfirmAsync(Guid id); + + Task ConfirmAsync(string number); + + Task> GetListByTypeAsync(SfsStoreRequestInputBase requestInput, + string requestType, bool includeDetails = false, CancellationToken cancellationToken = default); + + Task> GetListUnConfirmedByTypeAsync(string requestType); +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/InjectionNotePermissions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/InjectionNotePermissions.cs new file mode 100644 index 000000000..ec202d6be --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/InjectionNotePermissions.cs @@ -0,0 +1,30 @@ +using Volo.Abp.Authorization.Permissions; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public static class InjectionNotePermissions +{ + + public const string Default = StorePermissions.GroupName + "." + nameof(InjectionNote); + public const string Create = Default + "." + StorePermissions.CreateStr; + public const string Update = Default + "." + StorePermissions.UpdateStr; + public const string Delete = Default + "." + StorePermissions.DeleteStr; + + //自动发料记录 + public const string AutoInjectionNote = StorePermissions.GroupName + "." + nameof(AutoInjectionNote); + + //直接发料 + public const string DirectInjectionNote = StorePermissions.GroupName + "." + nameof(DirectInjectionNote); + + public static void AddInjectionNotePermission(this PermissionGroupDefinition permissionGroup) + { + var injectionNotePermission = permissionGroup.AddPermission(Default, StorePermissionDefinitionProvider.L(nameof(InjectionNote))); + injectionNotePermission.AddChild(Create, StorePermissionDefinitionProvider.L(StorePermissions.CreateStr)); + injectionNotePermission.AddChild(Update, StorePermissionDefinitionProvider.L(StorePermissions.UpdateStr)); + injectionNotePermission.AddChild(Delete, StorePermissionDefinitionProvider.L(StorePermissions.DeleteStr)); + + permissionGroup.AddPermission(AutoInjectionNote, StorePermissionDefinitionProvider.L(nameof(AutoInjectionNote))); + permissionGroup.AddPermission(DirectInjectionNote, StorePermissionDefinitionProvider.L(nameof(DirectInjectionNote))); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteDetailInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteDetailInput.cs new file mode 100644 index 000000000..48714bb23 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteDetailInput.cs @@ -0,0 +1,53 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionNoteDetailInput : SfsStoreRecommendFromDetailWithFromToInputBase +{ + + /// + /// 发料时间 + /// + [Display(Name = "发料时间")] + public DateTime IssueTime { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + public DateTime ExpiredTime { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string WorkStation { get; set; } + + /// + /// 在途库库位 + /// + [Display(Name = "在途库库位")] + public string OnTheWayLocationCode { get; set; } + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteEditInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteEditInput.cs new file mode 100644 index 000000000..410903705 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteEditInput.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionNoteEditInput : SfsStoreCreateOrUpdateInputBase +{ + #region Base + /// + /// 已确认 + /// + [Display(Name = "已确认")] + public bool Confirmed { get; set; } + #endregion + + #region Create + /// + /// 发料记录号 + /// + [Display(Name = "发料记录号")] + public string Number { get; set; } + + /// + /// 任务ID + /// + [Display(Name = "任务ID")] + [Required(ErrorMessage = "{0}是必填项")] + public string JobNumber { get; set; } + + /// + /// 车间 + /// + [Display(Name = "车间")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Workshop { get; set; } + + /// + /// 明细列表 + /// + [Display(Name = "明细列表")] + public List Details { get; set; } + + /// + /// 请求号码 + /// + [Display(Name = "请求号码")] + public string RequestNumber { get; set; } + + /// + /// 叫料请求类型 + /// + [Display(Name = "叫料请求类型")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string RequestType { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + #endregion +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteImportInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteImportInput.cs new file mode 100644 index 000000000..2876aa4ee --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Notes/InjectionNotes/Inputs/InjectionNoteImportInput.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionNoteImportInput : SfsStoreImportInputBase, IHasJobNumber, IHasRequestNumber +{ + /// + /// 任务ID + /// + [Display(Name = "任务ID")] + public string JobNumber { get; set; } + + /// + /// 车间 + /// + [Display(Name = "车间")] + public string Workshop { get; set; } + + public string RequestNumber { get; set; } + + /// + /// 叫料请求类型 + /// + [Display(Name = "叫料请求类型")] + public string RequestType { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + [Required(ErrorMessage = "{0}是必填项")] + public bool UseOnTheWayLocation { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Permissions/StorePermissionDefinitionProvider.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Permissions/StorePermissionDefinitionProvider.cs index b45ea84a6..c99e2abe8 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Permissions/StorePermissionDefinitionProvider.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Permissions/StorePermissionDefinitionProvider.cs @@ -26,8 +26,10 @@ public class StorePermissionDefinitionProvider : PermissionDefinitionProvider storeGroup.AddPurchaseReturnNotePermission(); storeGroup.AddPutawayNotePermission(); storeGroup.AddMaterialRequestPermission(); + storeGroup.AddInjectionRequestPermission(); storeGroup.AddContainerRequestPermission(); storeGroup.AddIssueNotePermission(); + storeGroup.AddInjectionNotePermission(); storeGroup.AddContainerNotePermission(); storeGroup.AddUnplannedReceiptNotePermission(); storeGroup.AddUnplannedIssueNotePermission(); @@ -90,6 +92,7 @@ public class StorePermissionDefinitionProvider : PermissionDefinitionProvider storeGroup.AddInspectJobPermission(); storeGroup.AddPutawayJobPermission(); storeGroup.AddIssueJobPermission(); + storeGroup.AddInjectionJobPermission(); storeGroup.AddContainerJobPermission(); storeGroup.AddDeliverJobPermission(); storeGroup.AddPurchaseReturnJobPermission(); diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDTO.cs new file mode 100644 index 000000000..b57dece0c --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDTO.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionRequestDTO : SfsStoreRequestDTOBase, IHasNumber +{ + /// + /// 叫料类型 + /// + [Display(Name = "叫料类型")] + public string Type { get; set; } + + /// + /// 备料计划单号 + /// + [Display(Name = "备料计划单号")] + public string PreparationPlanNumber { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + public string ProdLine { get; set; } + + /// + /// 车间 + /// + [Display(Name = "车间")] + public string Workshop { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDetailDTO.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDetailDTO.cs new file mode 100644 index 000000000..6116135fb --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/DTOs/InjectionRequestDetailDTO.cs @@ -0,0 +1,94 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionRequestDetailDTO : SfsStoreDetailWithQtyDTOBase + +{ + + /// + /// 目标库位 + /// + [Display(Name = "目标库位")] + public string ToLocationCode { get; set; } + + /// + /// 来源库区 + /// + [Display(Name = "来源库区")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string FromLocationArea { get; set; } + + // /// + // /// 在途库库位 + // /// + // public string OnTheWayLocationCode { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + public DateTime ExpiredTime { get; set; } + + /// + /// 目标ERP储位 + /// + [Display(Name = "目标ERP储位")] + public string ToLocationErpCode { get; set; } + + /// + /// 已发数量 + /// + [Display(Name = "已发数量")] + public decimal IssuedQty { get; set; } + + /// + /// 已收数量 + /// + [Display(Name = "已收数量")] + public decimal ReceivedQty { get; set; } + + /// + /// 明细状态 + /// + [Display(Name = "明细状态")] + public EnumStatus Status { get; set; } + + /// + /// 请求未发 + /// + [Display(Name = "请求未发")] + [NotMapped] + public decimal ToBeIssuedQty => Qty - IssuedQty; + + /// + /// 已发未收 + /// + [Display(Name = "已发未收")] + [NotMapped] + public decimal ToBeReceivedQty => IssuedQty - ReceivedQty; + + /// + /// 请求未收 + /// + [Display(Name = "请求未收")] + [NotMapped] + public decimal NotFinishQty => Qty - ReceivedQty; + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/IInjectionRequestAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/IInjectionRequestAppService.cs new file mode 100644 index 000000000..bde30d873 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/IInjectionRequestAppService.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public interface IInjectionRequestAppService + : ISfsStoreRequestMasterAppServiceBase + +{ + Task CreateAndHandleAsync(InjectionRequestEditInput input); + + /// + /// 根据备料计划生成 叫料请求 + /// + /// + Task CreateAndHandleByPreparationPlan(string productionPlanNumber); + + Task CreateAndHandleByAPIAsync(InjectionRequestEditInput input); + + /// + /// 根据类型获取叫料请求 + /// + /// + /// 叫料请求类型 + /// + /// + /// + Task> GetListByTypeAsync(SfsStoreRequestInputBase requestInput, + string type, bool includeDetails = false, CancellationToken cancellationToken = default); + + Task> GetListByTypeAsync(string type); + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/InjectionRequestPermissions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/InjectionRequestPermissions.cs new file mode 100644 index 000000000..1757aaada --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/InjectionRequestPermissions.cs @@ -0,0 +1,27 @@ +using Volo.Abp.Authorization.Permissions; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public static class InjectionRequestPermissions +{ + + public const string Default = StorePermissions.GroupName + "." + nameof(InjectionRequest); + public const string Create = Default + "." + StorePermissions.CreateStr; + public const string Update = Default + "." + StorePermissions.UpdateStr; + public const string Delete = Default + "." + StorePermissions.DeleteStr; + + //自动叫料申请 + public const string AutoInjectionRequest = StorePermissions.GroupName + "." + nameof(AutoInjectionRequest); + + public static void AddInjectionRequestPermission(this PermissionGroupDefinition permissionGroup) + { + var InjectionRequestPermission = permissionGroup.AddPermission(Default, StorePermissionDefinitionProvider.L(nameof(InjectionRequest))); + InjectionRequestPermission.AddChild(Create, StorePermissionDefinitionProvider.L(StorePermissions.CreateStr)); + InjectionRequestPermission.AddChild(Update, StorePermissionDefinitionProvider.L(StorePermissions.UpdateStr)); + InjectionRequestPermission.AddChild(Delete, StorePermissionDefinitionProvider.L(StorePermissions.DeleteStr)); + + permissionGroup.AddPermission(AutoInjectionRequest, StorePermissionDefinitionProvider.L(nameof(AutoInjectionRequest))); + + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestDetailInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestDetailInput.cs new file mode 100644 index 000000000..f1e0eb41b --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestDetailInput.cs @@ -0,0 +1,85 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionRequestDetailInput : SfsStoreDetailWithQtyInputBase +{ + /// + /// 目标库位 + /// + [Display(Name = "目标库位")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ToLocationCode { get; set; } + + /// + /// 来源库区 + /// + [Display(Name = "来源库区")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string FromLocationArea { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + [Display(Name = "工作中心")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + [Display(Name = "过期时间")] + public DateTime ExpiredTime { get; set; } + + /// + /// 状态 + /// + [Display(Name = "状态")] + public EnumRequestStatus RequestStatus { get; set; } = EnumRequestStatus.New; + + /// + /// ERP储位 + /// + [Display(Name = "ERP储位")] + public string ToLocationErpCode { get; set; } + + /// + /// 已发数量 + /// + [Display(Name = "已发数量")] + public decimal IssuedQty { get; set; } + + /// + /// 已收数量 + /// + [Display(Name = "已收数量")] + public decimal ReceivedQty { get; set; } + + /// + /// 明细状态 + /// + [Display(Name = "明细状态")] + public EnumStatus Status { get; set; } + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } + + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestEditInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestEditInput.cs new file mode 100644 index 000000000..7277bf50c --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestEditInput.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +public class InjectionRequestEditInput : SfsStoreRequestCreateOrUpdateInputBase +{ + #region Base + /// + /// 车间 + /// + [Display(Name = "车间")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Workshop { get; set; } + + /// + /// 生产线 + /// + [Display(Name = "生产线")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string ProdLine { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + public bool UseOnTheWayLocation { get; set; } + #endregion + + #region Create + /// + /// 要货单号 + /// + [Display(Name = "要货单号")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string Number { get; set; } + + /// + /// 叫料类型 + /// + [Display(Name = "叫料类型")] + public string Type { get; set; } + + /// + /// 备料计划单号 + /// + [Display(Name = "备料计划单号")] + [StringLength(SfsEfCorePropertyConst.CodeLength, ErrorMessage = "{0}最多输入{1}个字符")] + public string PreparationPlanNumber { get; set; } + + /// + /// 明细列表 + /// + [Display(Name = "明细列表")] + public List Details { get; set; } = new List(); + #endregion +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestImportInput.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestImportInput.cs new file mode 100644 index 000000000..e661693ef --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Requests/InjectionRequests/Inputs/InjectionRequestImportInput.cs @@ -0,0 +1,53 @@ +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Application.Contracts; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application.Contracts; + +[Display(Name = "叫料申请")] +public class InjectionRequestImportInput : SfsStoreImportInputBase +{ + /// + /// 叫料类型 + /// + [Display(Name = "叫料类型")] + [Required(ErrorMessage = "{0}是必填项")] + [ImporterHeader(Name = "叫料类型")] + [ExporterHeader(DisplayName = "叫料类型")] + [ValueMapping("人工拉动", EnumMaterialRequestType.Issue_Manual)] + public string Type { get; set; } + + /// + /// 物品代码 + /// + [Display(Name = "物品代码")] + [Required] + public string ItemCode { get; set; } + + /// + /// 目标库位 + /// + [Display(Name = "目标库位")] + [Required] + public string ToLocationCode { get; set; } + + /// + /// 来源库区 + /// + [Display(Name = "调出库区")] + [Required] + public string FromLocationArea { get; set; } + + /// + /// 数量 + /// + [Display(Name = "数量")] + [Required(ErrorMessage = "{0}是必填项")] + public decimal Qty { get; set; } + + /// + /// 备注 + /// + [Display(Name = "备注")] + public string Remark { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAppService.cs new file mode 100644 index 000000000..a8ff9b2bd --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAppService.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.ObjectMapping; +using Win_in.Sfs.Basedata.Domain.Shared; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; +using Win_in.Sfs.Wms.Store.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application; + +[Authorize] +[Route($"{StoreConsts.RootPath}injection-job")] +public class InjectionJobAppService + : SfsJobAppServiceBase, + IInjectionJobAppService +{ + private readonly IInjectionJobManager _injectionJobManager; + + public InjectionJobAppService( + IInjectionJobRepository repository, IInjectionJobManager injectionJobManager + ) : base(repository, injectionJobManager) + { + _injectionJobManager = injectionJobManager; + } + + /// + /// 根据物品和库位 检查是否存在发料任务 + /// + /// + /// + /// + /// + [Authorize] + [HttpGet("check-job-exist")] + public virtual async Task> CheckJobExistByItemCodeAndLocationCode(string itemCode, + string locationCode) + { + var entities = await _repository.GetListAsync(c => + c.Details.Any(p => + (p.ItemCode == itemCode && p.RecommendFromLocationCode == locationCode) || + (p.ItemCode == itemCode && p.ToLocationCode == locationCode)) + && (c.JobStatus == EnumJobStatus.Open || c.JobStatus == EnumJobStatus.Doing), true).ConfigureAwait(false); + var dtos = ObjectMapper.Map, List>(entities); + return dtos; + } + + [HttpPost("cancel-by-request/{injectionNumber}")] + public virtual async Task CancelByMaterialRequestAsync(string injectionNumber) + { + var entities = await _repository.GetListAsync(p => p.InjectionRequestNumber == injectionNumber).ConfigureAwait(false); + foreach (var entity in entities) + { + await _injectionJobManager.CancelAsync(entity).ConfigureAwait(false); + } + } + + [HttpPost("invalid")] + public override async Task CancelAsync(Guid id) + { + var injectionJob = await _repository.GetAsync(id).ConfigureAwait(false); + if (injectionJob == null) + { + throw new UserFriendlyException($"未找到ID为 {id} 的任务"); + } + + await _injectionJobManager.CancelAsync(injectionJob).ConfigureAwait(false); + + } + + /// + /// 根据叫料请求类型获取发料任务 + /// + /// + /// + /// 叫料请求类型: + /// 人工拉动:Issue_Manual; + /// 线边拉动:Issue_WIP; + /// + /// + /// + /// + [HttpPost("by-type/{requestType}")] + public virtual async Task> GetListByTypeAsync(SfsJobRequestInputBase requestInput, + string requestType, bool includeDetails = false, CancellationToken cancellationToken = default) + { + Expression> expression = p => p.RequestType == requestType; + if (requestInput.Condition.Filters?.Count > 0) + { + expression = expression.And(requestInput.Condition.Filters.ToLambda()); + } + + return await GetPagedListAsync(expression, requestInput.SkipCount, requestInput.MaxResultCount, + requestInput.Sorting, includeDetails, cancellationToken).ConfigureAwait(false); + + } + + [HttpPost("by-request-number/{requestNumber}")] + public virtual async Task> GetByRequestNumberAsync(string requestNumber) + { + var entitys = await _repository.GetListAsync(p => p.InjectionRequestNumber == requestNumber).ConfigureAwait(false); + return ObjectMapper.Map, List>(entitys); + } + + /// + /// 保存拆箱时涉及的明细修改 + /// + /// + [HttpPost("save-detail-split-packing")] + public virtual async Task SaveDetail_SplitPackingAsync(SplitPacking_UpdateJobDetailInput input) + { + var job = await _repository.FindAsync(p => p.Number == input.Number).ConfigureAwait(false); + InjectionJobDetail detail = job.Details.FirstOrDefault(p => p.RecommendPackingCode == input.FromPackingCode ); /*&& p.HandledQty == input.FromQty*/ + if (detail == null) + { + //throw new UserFriendlyException($"根据HandledPackingCode={input.FromPackingCode}取InjectionJobDetail表为空!"); + throw new UserFriendlyException($"根据RecommendPackingCode={input.FromPackingCode}取InjectionJobDetail表为空!"); + } + //插入目标箱 + var newDetail = CommonHelper.CloneObj(detail); + newDetail.SetId(GuidGenerator.Create()); + newDetail.RecommendPackingCode = input.ToPackingCode; + newDetail.RecommendQty = input.ToQty; + newDetail.HandledPackingCode = detail.HandledPackingCode.HasValue() ? input.ToPackingCode : null; //源实际实际箱码有值,则新记录实际箱码有值 + newDetail.HandledQty = detail.HandledQty > 0 ? input.ToQty : 0; + //newDetail.CreationTime = CommonHelper.CurTime; + job.Details.Add(newDetail); + //修改源箱 + detail.RecommendQty = input.FromQty - input.ToQty; + detail.HandledQty = detail.HandledQty > 0 ? input.FromQty - input.ToQty : 0; + var entity = await _repository.UpdateAsync(job).ConfigureAwait(false); + var ret = ObjectMapper.Map(entity); + return ret; + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAutoMapperProfile.cs new file mode 100644 index 000000000..c1b17e043 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Jobs/InjectionJobs/InjectionJobAutoMapperProfile.cs @@ -0,0 +1,30 @@ +using AutoMapper; +using Volo.Abp.AutoMapper; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application; + +public partial class StoreApplicationAutoMapperProfile : Profile +{ + private void InjectionJobAutoMapperProfile() + { + CreateMap(); + + CreateMap(); + + CreateMap() + ; + + CreateMap() + ; + + CreateMap() + .IgnoreAuditedObjectProperties() + .Ignore(x => x.MasterID) + .Ignore(x => x.TenantId) + .Ignore(x => x.Number) + .Ignore(x => x.Id); + + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAppService.cs new file mode 100644 index 000000000..c4bad1cfb --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAppService.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Entities; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Event; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; +using Win_in.Sfs.Wms.Store.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application; + +[Authorize] +[Route($"{StoreConsts.RootPath}injection-note")] +public class InjectionNoteAppService : + SfsStoreWithDetailsAppServiceBase, + IInjectionNoteAppService +{ + private readonly IInjectionNoteManager _injectionNoteManager; + + public InjectionNoteAppService( + IInjectionNoteRepository repository, + IInjectionNoteManager injectionNoteManager + ) : base(repository) + { + _injectionNoteManager = injectionNoteManager; + } + + [HttpPost("")] + //[Authorize(InjectionNotePermissions.Create)] + public override async Task CreateAsync(InjectionNoteEditInput input) + { + var entity = ObjectMapper.Map(input); + await _injectionNoteManager.CreateAsync(entity).ConfigureAwait(false); + var dto = ObjectMapper.Map(entity); + return dto; + } + + /// + /// 确认对应的记录单 + /// + /// + /// + [HttpPost("confirm/{id}")] + public virtual async Task ConfirmAsync(Guid id) + { + var injectionNote= await _repository.GetAsync(id).ConfigureAwait(false); + injectionNote.Confirmed = true; + injectionNote=await _repository.UpdateAsync(injectionNote).ConfigureAwait(false); + await LocalEventBus.PublishAsync(new SfsConfirmedEntityEventData(injectionNote), false).ConfigureAwait(false); + return ObjectMapper.Map(injectionNote); + } + + [HttpPost("confirm-by-number/{number}")] + public virtual async Task ConfirmAsync(string number) + { + var entity = await _repository.FindAsync(p => p.Number == number).ConfigureAwait(false); + Check.NotNull(entity, nameof(InjectionNote)); + var result = await _injectionNoteManager.ConfirmAsync(entity.Id).ConfigureAwait(false); + var dto = ObjectMapper.Map(result); + return dto; + } + /// + /// 根据叫料请求类型获取发料记录 + /// + /// + /// + /// 叫料请求类型: + /// 人工拉动:Issue_Manual; + /// 线边拉动:Issue_WIP; + /// + /// + /// + /// + [HttpPost("by-type/{requestType}")] + public virtual async Task> GetListByTypeAsync(SfsStoreRequestInputBase requestInput, + string requestType, bool includeDetails = false, CancellationToken cancellationToken = default) + { + Expression> expression = p => p.RequestType == requestType; + if (requestInput.Condition.Filters?.Count > 0) + { + expression = expression.And(requestInput.Condition.Filters.ToLambda()); + } + + return await GetPagedListAsync(expression, requestInput.SkipCount, requestInput.MaxResultCount, + requestInput.Sorting, includeDetails, cancellationToken).ConfigureAwait(false); + } + + [HttpGet("list/un-confirmed/{requestType}")] + public virtual async Task> GetListUnConfirmedByTypeAsync(string requestType) + { + var entities = await _repository.GetListAsync(c => !c.Confirmed && c.RequestType == requestType) + .ConfigureAwait(false); + + var dtos = ObjectMapper.Map, List>(entities); + + return dtos; + } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAutoMapperProfile.cs new file mode 100644 index 000000000..937b4edcb --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/InjectionNotes/InjectionNoteAutoMapperProfile.cs @@ -0,0 +1,31 @@ +using AutoMapper; +using Volo.Abp.AutoMapper; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application; + +public partial class StoreApplicationAutoMapperProfile : Profile +{ + private void InjectionNoteAutoMapperProfile() + { + CreateMap() + .ReverseMap(); + + CreateMap(); + + CreateMap() + .IgnoreAuditedObjectProperties() + .Ignore(x => x.MasterID) + .Ignore(x => x.TenantId) + .Ignore(x => x.Number) + .Ignore(x => x.Id); + + CreateMap() + .IgnoreAuditedObjectProperties() + .Ignore(x => x.TenantId) + .Ignore(x => x.Number) + .Ignore(x => x.Id); + ; + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/ProductRecycleNotes/ProductRecycleNoteAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/ProductRecycleNotes/ProductRecycleNoteAutoMapperProfile.cs index bed3ee5c7..97e507c24 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/ProductRecycleNotes/ProductRecycleNoteAutoMapperProfile.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Notes/ProductRecycleNotes/ProductRecycleNoteAutoMapperProfile.cs @@ -15,6 +15,8 @@ public partial class StoreApplicationAutoMapperProfile : Profile CreateMap(); + CreateMap(); + CreateMap() .IgnoreAuditedObjectProperties() .Ignore(x => x.MasterID) diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAppService.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAppService.cs new file mode 100644 index 000000000..e3d7400db --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAppService.cs @@ -0,0 +1,503 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using DocumentFormat.OpenXml.Office.PowerPoint.Y2021.M06.Main; +using IdentityModel; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.ObjectMapping; +using Win_in.Sfs.Basedata.Application.Contracts; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Shared.Event; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; +using Win_in.Sfs.Wms.Store.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Application; + +[Authorize] +[Route($"{StoreConsts.RootPath}injection-request")] +public class InjectionRequestAppService : SfsStoreRequestAppServiceBase, + IInjectionRequestAppService +{ + private readonly IInjectionRequestManager _injectionRequestManager; + + private readonly IPreparationPlanManager _preparationPlanManager; + + private readonly IItemStoreRelationAppService _itemStoreRelationApp; + + private readonly IAreaAppService _areaApp; + + private readonly ILocationAppService _locationAppService; + + private readonly IItemBasicAppService _itemBasicAppService; + private readonly IProductionLineAppService _productionLineAppService; + private readonly IInjectionJobAppService _issueJobAppService; + public InjectionRequestAppService( + IInjectionRequestRepository repository, + IInjectionRequestManager injectionRequestManager, + IPreparationPlanManager preparationPlanManager, + IItemStoreRelationAppService itemStoreRelationApp, + IAreaAppService areaApp, + ILocationAppService locationAppService, + IItemBasicAppService itemBasicAppService, + IProductionLineAppService productionLineAppService, + IInjectionJobAppService issueJobAppService) + : base(repository, injectionRequestManager) + { + _injectionRequestManager = injectionRequestManager; + _preparationPlanManager = preparationPlanManager; + _itemStoreRelationApp = itemStoreRelationApp; + _areaApp = areaApp; + _locationAppService = locationAppService; + _itemBasicAppService = itemBasicAppService; + _productionLineAppService = productionLineAppService; + _issueJobAppService = issueJobAppService; + } + + #region 东阳使用 + + /// + /// 用来重写 导入数据时可以加工数据 + /// + /// + /// + protected override async Task> ImportProcessingEntityAsync( + Dictionary dictionary) + { + var addList = dictionary.Where(p => p.Value == EntityState.Added).Select(p => p.Key); + + foreach (var injectionRequest in addList) + { + injectionRequest.Worker = CurrentUser.GetUserName(); + injectionRequest.CreatorId = CurrentUser.Id; + if (injectionRequest.Type == EnumTransSubType.Issue_Manual.GetDisplayName()) + { + injectionRequest.Type = EnumTransSubType.Issue_Manual.ToString(); + } + + foreach (var detail in injectionRequest.Details) + { + var locationDto= await _locationAppService.GetByCodeAsync(detail.ToLocationCode).ConfigureAwait(false); + CheckLocation(locationDto,detail.ToLocationCode); + + var itemBasicDto=await _itemBasicAppService.GetByCodeAsync(detail.ItemCode).ConfigureAwait(false); + CheckItemBasic(itemBasicDto,detail.ItemCode); + + detail.ToLocationArea = locationDto.AreaCode; + detail.ToLocationErpCode = locationDto.ErpLocationCode; + detail.ToLocationGroup = locationDto.LocationGroupCode; + detail.ToWarehouseCode = locationDto.WarehouseCode; + detail.ItemDesc1 = itemBasicDto.Desc1; + detail.ItemDesc2= itemBasicDto.Desc2; + detail.ItemName= itemBasicDto.Name; + detail.Uom = itemBasicDto.BasicUom; + } + } + + return dictionary; + } + + public override async Task HandleAsync(Guid id) + { + var entity=await _repository.GetAsync(id).ConfigureAwait(false); + await LocalEventBus.PublishAsync(new SfsHandledEntityEventData(entity), false).ConfigureAwait(false); + return ObjectMapper.Map(entity); + } + + #region 校验 + + private void CheckItemBasic(ItemBasicDTO ItemBasicDto, string itemCode) + { + if (ItemBasicDto == null) + { + throw new UserFriendlyException($"物品代码为【{itemCode}】不存在"); + } + } + + private void CheckLocation(LocationDTO LocationDto, string LocationCode) + { + if (LocationDto == null) + { + throw new UserFriendlyException($"库位代码为【{LocationCode}】不存在"); + } + + if (LocationDto.Type != EnumLocationType.WIP) + { + throw new UserFriendlyException($"库位代码【{LocationCode}】不是【{EnumLocationType.WIP.GetDisplayName()}】类型"); + } + } + + + #endregion + + #endregion + + [HttpPost("")] + //[Authorize(InjectionRequestPermissions.Create)] + public override async Task CreateAsync(InjectionRequestEditInput input) + { + foreach (var item in input.Details) + { + if (item.Qty <= 0) + { + throw new UserFriendlyException($"{item.ItemCode} 物料的需求量必须大于 0"); + } + } + + //检验备料计划是否存在要料请求 + if (!string.IsNullOrWhiteSpace(input.PreparationPlanNumber)) + { + var exist = await CheckExistByPreparationPlanAsync(input.PreparationPlanNumber).ConfigureAwait(false); + if (exist.Count > 0) + { + throw new UserFriendlyException($"{input.PreparationPlanNumber} 备料计划已存在要料申请"); + } + } + + foreach (var item in input.Details) //赋值生产线 + { + var location = await LocationAclService.GetByCodeAsync(item.ToLocationCode).ConfigureAwait(false); + item.ProdLine = location.LocationGroupCode; + input.ProdLine = location.LocationGroupCode; + } + + var entity = ObjectMapper.Map(input); + + var result = await _injectionRequestManager.CreateAsync(entity).ConfigureAwait(false); + + var dto = ObjectMapper.Map(result); + return dto; + } + + /// + /// 创建并且执行叫料请求 + /// + /// + /// + /// + [HttpPost("create-and-handle-api")] + //[Authorize(InjectionRequestPermissions.Create)] + public virtual async Task CreateAndHandleByAPIAsync(InjectionRequestEditInput input) + { + foreach (var item in input.Details) + { + if (item.Qty <= 0) + { + throw new UserFriendlyException($"{item.ItemCode} 物料的需求量必须大于 0"); + } + } + + + input.AutoSubmit = true; + input.AutoAgree = true; + input.AutoHandle = true; + input.AutoCompleteJob = false; + input.DirectCreateNote = false; + + foreach (var detailInput in input.Details) + { + var toLocationDto= await _locationAppService.GetByCodeAsync(detailInput.ToLocationCode).ConfigureAwait(false); + CheckLocation(toLocationDto, detailInput.ToLocationCode); + var itemBasicDto = await _itemBasicAppService.GetByCodeAsync(detailInput.ItemCode).ConfigureAwait(false); + CheckItemBasic(itemBasicDto, detailInput.ItemCode); + detailInput.ItemDesc1 = itemBasicDto.Desc1; + if(detailInput.PositionCode.Contains('W')) + { + detailInput.RecommendType = EnumRecommendType.W; + } + else if(detailInput.PositionCode.Contains('Q')) + { + detailInput.RecommendType = EnumRecommendType.Q; + } + else if(detailInput.PositionCode.Contains('K')) + { + detailInput.RecommendType = EnumRecommendType.K; + } + else + { + detailInput.RecommendType = EnumRecommendType.None; + } + + var productionLineDto = await _productionLineAppService.GetByLocationGroupCodeAsync(toLocationDto.LocationGroupCode).ConfigureAwait(false); + + detailInput.ToLocationCode = toLocationDto.ErpLocationCode; + if (productionLineDto != null) + { + detailInput.ProdLine = productionLineDto.Code; + } + } + + var entity = ObjectMapper.Map(input); + foreach (var detail in entity.Details) + { + var toLocationDto = await _locationAppService.GetByCodeAsync(detail.ToLocationCode).ConfigureAwait(false); + var productionLineDto = await _productionLineAppService.GetByLocationGroupCodeAsync(toLocationDto.LocationGroupCode).ConfigureAwait(false); + + detail.ToLocationArea= toLocationDto.AreaCode; + detail.ToLocationErpCode = toLocationDto.ErpLocationCode; + detail.ToLocationGroup = toLocationDto.LocationGroupCode; + detail.ToWarehouseCode= toLocationDto.WarehouseCode; + } + + entity.UseOnTheWayLocation = false; + + var result = await _injectionRequestManager.CreateBynNumberAsync(entity).ConfigureAwait(false); + + var dto = ObjectMapper.Map(result); + + return dto; + } + + /// + /// 创建并且执行叫料请求 + /// + /// + /// + /// + [HttpPost("create-and-handle")] + //[Authorize(InjectionRequestPermissions.Create)] + public virtual async Task CreateAndHandleAsync(InjectionRequestEditInput input) + { + foreach (var item in input.Details) + { + if (item.Qty <= 0) + { + throw new UserFriendlyException($"{item.ItemCode} 物料的需求量必须大于0"); + } + } + + //检验备料计划是否存在要料请求 + if (!string.IsNullOrWhiteSpace(input.PreparationPlanNumber)) + { + var exist = await CheckExistByPreparationPlanAsync(input.PreparationPlanNumber).ConfigureAwait(false); + if (exist.Count > 0) + { + throw new UserFriendlyException($"{input.PreparationPlanNumber} 备料计划已存在要料申请"); + } + } + + foreach (var detailInput in input.Details) //赋值生产线 + { + var toLocationDto = await _locationAppService.GetByCodeAsync(detailInput.ToLocationCode).ConfigureAwait(false); + CheckLocation(toLocationDto, detailInput.ToLocationCode); + var itemBasicDto = await _itemBasicAppService.GetByCodeAsync(detailInput.ItemCode).ConfigureAwait(false); + CheckItemBasic(itemBasicDto, detailInput.ItemCode); + + var location = await LocationAclService.GetByCodeAsync(detailInput.ToLocationCode).ConfigureAwait(false); + detailInput.ProdLine = location.LocationGroupCode; + input.ProdLine = location.LocationGroupCode; + input.Worker = input.Worker; + } + + input.AutoSubmit = true; + input.AutoAgree = true; + input.AutoHandle = true; + input.AutoCompleteJob = false; + input.DirectCreateNote = false; + + var entity = ObjectMapper.Map(input); + + var result = await _injectionRequestManager.CreateAsync(entity).ConfigureAwait(false); + + var dto = ObjectMapper.Map(result); + + return dto; + } + + /// + /// 根据备料计划生成 叫料请求 + /// + /// + [HttpPost("create-and-handle-by-preparation-plan/{number}")] + public virtual async Task CreateAndHandleByPreparationPlan(string number) + { + var prodPreparationPlan = await _preparationPlanManager.GetByNumberAsync(number).ConfigureAwait(false); + + var injectionRequestCreateInput = new InjectionRequestEditInput { Worker = CurrentUser.GetUserName() }; + + foreach (var preparationPlanDetail in prodPreparationPlan.Details) + { + var detail = new InjectionRequestDetailInput + { + ItemCode = preparationPlanDetail.ItemCode, + ItemName = preparationPlanDetail.ItemName, + ItemDesc1 = preparationPlanDetail.ItemDesc1, + ItemDesc2 = preparationPlanDetail.ItemDesc2, + Uom = preparationPlanDetail.Uom, + Qty = preparationPlanDetail.Qty, + StdPackQty = preparationPlanDetail.StdPackQty, + ToLocationCode = preparationPlanDetail.ToLocationCode + }; + + injectionRequestCreateInput.Details.Add(detail); + } + + var dto = await CreateAndHandleAsync(injectionRequestCreateInput).ConfigureAwait(false); + + return dto; + } + + /// + /// 检验备料计划是否存在要料请求 + /// + /// + /// + private async Task> CheckExistByPreparationPlanAsync(string preparationPlanNumber) + { + var entities = await _repository.GetListAsync(c => c.PreparationPlanNumber == preparationPlanNumber) + .ConfigureAwait(false); + return entities; + } + + /// + /// 根据类型获取叫料请求 + /// + /// + /// + /// 叫料请求类型: + /// 人工拉动:Issue_Manual; + /// 线边拉动:Issue_WIP; + /// + /// + /// + /// + [HttpPost("by-type/{type}")] + public virtual async Task> GetListByTypeAsync( + SfsStoreRequestInputBase requestInput, + string type, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Expression> expression = p => p.Type == type; + if (requestInput.Condition.Filters?.Count > 0) + { + expression = expression.And(requestInput.Condition.Filters.ToLambda()); + } + + return await GetPagedListAsync(expression, requestInput.SkipCount, requestInput.MaxResultCount, + requestInput.Sorting, includeDetails, cancellationToken).ConfigureAwait(false); + } + + #region 导入 + + + /// + /// 导入验证 + /// + /// + /// + /// + protected override async Task ValidateImportModelAsync(InjectionRequestImportInput model, + List validationRresult) + { + _ = new Dictionary(); + _ = await CheckItemBasicAsync(model, validationRresult).ConfigureAwait(false); + _ = await CheckLocationAsync(model, validationRresult).ConfigureAwait(false); + await CheckAreaAsync(model, validationRresult).ConfigureAwait(false); + await CheckStoreRelationAsync(model, validationRresult).ConfigureAwait(false); + } + + #region 校验 + + protected override async Task ValidateImportEntities(Dictionary dict) + { + foreach (var entity in dict.Keys) + { + var tranType = await TransactionTypeAclService + .GetByTransTypeAsync(EnumTransType.Issue, EnumTransSubType.None).ConfigureAwait(false); + + Check.NotNull(tranType, "事务类型", "事务类型不存在"); + + entity.AutoCompleteJob = tranType.AutoCompleteJob; + entity.AutoSubmit = tranType.AutoSubmitRequest; + entity.AutoAgree = tranType.AutoAgreeRequest; + entity.AutoHandle = tranType.AutoHandleRequest; + entity.DirectCreateNote = tranType.DirectCreateNote; + } + + return await base.ValidateImportEntities(dict).ConfigureAwait(false); + } + + protected async Task CheckItemBasicAsync(InjectionRequestImportInput importInput, + List validationRresult) + { + var item = await _itemBasicAppService.GetByCodeAsync(importInput.ItemCode).ConfigureAwait(false); + if (item == null) + { + validationRresult.Add(new ValidationResult($"物品代码{importInput.ItemCode}不存在", new[] { "物品代码" })); + } + else if (item.StdPackQty == 0) + { + validationRresult.Add( + new ValidationResult($"物品代码{importInput.ItemCode}的物品信息中标准包装等于0或不存在", new[] { "标准包装" })); + } + + return item; + } + + protected async Task CheckLocationAsync(InjectionRequestImportInput importInput, + List validationRresult) + { + var location = await _locationAppService.GetByCodeAsync(importInput.ToLocationCode).ConfigureAwait(false); + if (location == null) + { + validationRresult.Add(new ValidationResult($"目标库位{importInput.ToLocationCode}不存在", new[] { "目标库位" })); + } + + return location; + } + + protected async Task CheckAreaAsync(InjectionRequestImportInput importInput, + List validationRresult) + { + var area = await _areaApp.GetByCodeAsync(importInput.FromLocationArea).ConfigureAwait(false); + if (area == null) + { + validationRresult.Add(new ValidationResult($"调出库区{importInput.FromLocationArea}不存在", new[] { "调出库区" })); + } + } + + protected async Task CheckStoreRelationAsync(InjectionRequestImportInput importInput, + List validationRresult) + { + var itemStoreRelation = await _itemStoreRelationApp + .GetFirstAsync(importInput.ItemCode, importInput.ToLocationCode).ConfigureAwait(false); + if (itemStoreRelation == null) + { + validationRresult.Add(new ValidationResult( + $"物品代码{importInput.ItemCode}与目标库位{importInput.ToLocationCode}不存在对应关", new[] { "物品库位对应关系" })); + } + } + + #endregion + + #endregion + + /// + /// 根据类型 获取叫料申请 + /// + /// + /// + [HttpGet("list/by-type/{type}")] + public virtual async Task> GetListByTypeAsync(string type) + { + var entities = await _repository.GetListAsync(c => c.Type == type).ConfigureAwait(false); + + var dtos = ObjectMapper.Map, List>(entities); + + return dtos; + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAutoMapperProfile.cs new file mode 100644 index 000000000..de78a07de --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/Requests/InjectionRequests/InjectionRequestAutoMapperProfile.cs @@ -0,0 +1,74 @@ +using AutoMapper; +using Volo.Abp.AutoMapper; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Application; + +public partial class StoreApplicationAutoMapperProfile : Profile +{ + private void InjectionRequestAutoMapperProfile() + { + CreateMap() + .ReverseMap(); + + CreateMap() + .ReverseMap(); + + CreateMap() + .IgnoreAuditedObjectProperties() + .Ignore(x => x.ToLocationArea) + .Ignore(x => x.ToLocationGroup) + .Ignore(x => x.ToWarehouseCode) + .Ignore(x => x.MasterID) + .Ignore(x => x.TenantId) + .Ignore(x => x.Number) + .Ignore(x => x.Id); + + CreateMap() + .IgnoreAuditedObjectProperties() + .ForMember(x => x.Type, y => y.MapFrom(t => t.Type.ToString())) + .Ignore(x => x.PreparationPlanNumber) + .Ignore(x => x.ProdLine) + .Ignore(x => x.Workshop) + .Ignore(x => x.UseOnTheWayLocation) + .Ignore(x => x.Details) + .Ignore(x => x.Remark) + .Ignore(x => x.TenantId) + .Ignore(x => x.Number) + .Ignore(x => x.RequestStatus) + .Ignore(x => x.ConcurrencyStamp) + .Ignore(x => x.ExtraProperties) + .Ignore(x => x.ActiveDate) + .Ignore(x => x.Remark); + + CreateMap() + .IgnoreAuditedObjectProperties() + .ForMember(x => x.Status, y => y.MapFrom(t => EnumStatus.Open)) + .Ignore(x => x.ToLocationErpCode) + .Ignore(x => x.ToWarehouseCode) + .Ignore(x => x.ToLocationArea) + .Ignore(x => x.ToLocationGroup) + .Ignore(x => x.ItemName).Ignore(x => x.ItemDesc1).Ignore(x => x.ItemDesc2) + .Ignore(x => x.ProdLine) + .Ignore(x => x.WorkStation) + .Ignore(x => x.ExpiredTime) + .Ignore(x => x.IssuedQty) + .Ignore(x => x.ReceivedQty) + .Ignore(x => x.ToBeIssuedQty) + .Ignore(x => x.ToBeReceivedQty) + .Ignore(x => x.NotFinishQty) + .Ignore(x => x.StdPackQty) + .Ignore(x => x.Uom) + .Ignore(x => x.TenantId) + .Ignore(x => x.MasterID) + .Ignore(x => x.Number) + .Ignore(x => x.Id) + .Ignore(x => x.Remark); + CreateMap() + .IgnoreAuditedObjectProperties() + .Ignore(x => x.RequestStatus) + .Ignore(x => x.ConcurrencyStamp).Ignore(x => x.Id); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/StoreApplicationAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/StoreApplicationAutoMapperProfile.cs index 7fa33e59e..1f5b385d9 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/StoreApplicationAutoMapperProfile.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application/StoreApplicationAutoMapperProfile.cs @@ -14,6 +14,7 @@ public partial class StoreApplicationAutoMapperProfile : Profile InspectRequestAutoMapperProfile(); ItemTransformRequestAutoMapperProfile(); MaterialRequestAutoMapperProfile(); + InjectionRequestAutoMapperProfile(); ProductionReturnRequestAutoMapperProfile(); ProductReceiptRequestAutoMapperProfile(); UnplannedIssueRequestAutoMapperProfile(); @@ -60,6 +61,7 @@ public partial class StoreApplicationAutoMapperProfile : Profile InventoryTransferNoteMapperProfile(); IsolationNoteAutoMapperProfile(); IssueNoteAutoMapperProfile(); + InjectionNoteAutoMapperProfile(); ContainerNoteAutoMapperProfile(); ItemTransformNoteAutoMapperProfile(); JisDeliverNoteAutoMapperProfile(); diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain.Shared/Settings/StoreSettings.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain.Shared/Settings/StoreSettings.cs index bcf1e32b1..729511a4b 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain.Shared/Settings/StoreSettings.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain.Shared/Settings/StoreSettings.cs @@ -112,6 +112,12 @@ public static class StoreSettings public const string EnableMultipleCreateIssueJob = $"{Default}.{nameof(EnableMultipleCreateIssueJob)}"; } + public static class InjectionRequest + { + private const string Default = $"{GroupName}.{nameof(InjectionRequest)}"; + public const string EnableMultipleCreateInjectionJob = $"{Default}.{nameof(EnableMultipleCreateInjectionJob)}"; + } + public static class Cache { diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobManager.cs new file mode 100644 index 000000000..7be96ee4a --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobManager.cs @@ -0,0 +1,10 @@ +using System; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public interface IInjectionJobManager : IJobManager +{ + Task GetAsync(Expression> expression); +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobRepository.cs new file mode 100644 index 000000000..aeeac0878 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/IInjectionJobRepository.cs @@ -0,0 +1,6 @@ +namespace Win_in.Sfs.Wms.Store.Domain; + +public interface IIssueJobRepository : ISfsJobRepositoryBase +{ + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJob.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJob.cs new file mode 100644 index 000000000..8f22c73b2 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJob.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Win_in.Sfs.Shared.Domain.Entities; + +namespace Win_in.Sfs.Wms.Store.Domain; + +/// +/// 注塑发料任务 +/// +[Display(Name = "注塑发料任务")] +public class InjectionJob : SfsJobAggregateRootBase +{ + /// + /// 叫料请求类型 + /// + [IgnoreUpdate] + public string RequestType { get; set; } + + /// + /// 生产线 + /// + [IgnoreUpdate] + public string ProdLine { get; set; } + + /// + /// 要货单号 + /// + [IgnoreUpdate] + public string InjectionRequestNumber { get; set; } + + /// + /// 车间 + /// + [IgnoreUpdate] + public string Workshop { get; set; } + + /// + /// 使用在途库 + /// + [Display(Name = "使用在途库")] + [IgnoreUpdate] + public bool UseOnTheWayLocation { get; set; } + + /// + /// 任务明细 + /// + [IgnoreUpdate] + public override List Details { get; set; } = new List(); + + /// + /// 设置任务明细的实际库位和实际数量 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task BuildDetail(Guid id, string handledLocationCode, string handledLocationErpCode, + string handledWarehouseCode, decimal handledQty, string handledSupplierBatch, DateTime handledArriveDate, DateTime handledProduceDate, DateTime handledExpireDate, + string handledContainerCode, string handledLot, string handledPackingCode) + { + var detail = GetDetail(id); + detail.HandledFromLocationCode = handledLocationCode; + detail.HandledFromLocationErpCode = handledLocationErpCode; + detail.HandledFromWarehouseCode = handledWarehouseCode; + detail.HandledQty = handledQty; + detail.HandledSupplierBatch = handledSupplierBatch; + detail.HandledArriveDate = handledArriveDate; + detail.HandledProduceDate = handledProduceDate; + detail.HandledExpireDate = handledExpireDate; + detail.HandledContainerCode = handledContainerCode; + detail.HandledLot = handledLot; + detail.HandledPackingCode = handledPackingCode; + await Task.CompletedTask.ConfigureAwait(false); + } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobDetail.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobDetail.cs new file mode 100644 index 000000000..5d37078d1 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobDetail.cs @@ -0,0 +1,108 @@ +using System; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public class InjectionJobDetail : SfsJobRecommendFromDetailEntityBase, IHasToLocation +{ + /// + /// 请求库位 + /// + public string RequestLocationCode { get; set; } + + /// + /// 到库位 + /// + public string ToLocationCode { get; set; } + + /// + /// 到库区 + /// + public string ToLocationArea { get; set; } + + /// + /// 到库位组 + /// + public string ToLocationGroup { get; set; } + + /// + /// 到ERP库位 + /// + public string ToLocationErpCode { get; set; } + + /// + /// 到仓库 + /// + public string ToWarehouseCode { get; set; } + + /// + /// 在途库库位 + /// + public string OnTheWayLocationCode { get; set; } + + /// + /// 生产线 + /// + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + public DateTime ExpiredTime { get; set; } + + /// + /// 工序 + /// + public string Operation { get; set; } + + /// + /// 配送方式 + /// + public EnumDistributionType DistributionType { get; set; } + + /// + /// 取整方式 + /// + public EnumTruncType TruncType { get; set; } + + /// + /// 取整后数量 + /// + public decimal RoundedQty { get; set; } + + /// + /// 计划拆分规则 + /// + public EnumPlannedSplitRule PlannedSplitRule { get; set; } + + /// + /// 计划开始时间 + /// + public DateTime PlanBeginTime { get; set; } + + /// + /// 每次配送数量 + /// + public decimal DeliveryQty { get; set; } + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } + + public void SetId(Guid id) + { + this.Id = id; + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobManager.cs new file mode 100644 index 000000000..a88e5c213 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/InjectionJobs/InjectionJobManager.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Volo.Abp.Users; +using Volo.Abp.Validation; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public class InjectionJobManager : SfsJobManagerBase, IInjectionJobManager +{ + + public InjectionJobManager( + IInjectionJobRepository repository + ) : base(repository) + { + } + + /// + /// 执行任务 发料任务 + /// + /// + /// + /// + /// + public override async Task CompleteAsync(InjectionJob input, ICurrentUser user) + { + var entity = await Repository.FindAsync(input.Id).ConfigureAwait(false); + + foreach (var detail in input.Details) + { + //发料子任务 赋值实际转移 + await entity.BuildDetail(detail.Id, + detail.HandledFromLocationCode, + detail.HandledFromLocationErpCode, + detail.HandledFromWarehouseCode, + detail.HandledQty, + detail.HandledSupplierBatch, + detail.HandledArriveDate, + detail.HandledProduceDate, + detail.HandledExpireDate, + detail.HandledContainerCode, + detail.HandledLot, + detail.HandledPackingCode).ConfigureAwait(false); + } + + return await base.CompleteAsync(entity, user).ConfigureAwait(false); + } + + public override void CheckDetails(InjectionJob entity, AbpValidationResult result) + { + var details = entity.Details; + foreach (var detail in details) + { + if (detail.HandledFromLocationCode == null) + { + result.Errors.Add(new ValidationResult($"{detail.HandledFromLocationCode} 不能为空")); + } + + } + } + + public override async Task> GetWorkingListByPackingAsync(string packingCode) + { + return await Repository.GetListAsync(c => c.Details.Any(p => p.RecommendPackingCode == packingCode) && c.JobStatus != EnumJobStatus.Closed && c.JobStatus != EnumJobStatus.Cancelled, true).ConfigureAwait(false); + + } + + public override async Task> GetWorkingListByContainerAsync(string containerCode) + { + return await Repository.GetListAsync(c => c.Details.Any(p => p.RecommendContainerCode == containerCode) && c.JobStatus != EnumJobStatus.Closed && c.JobStatus != EnumJobStatus.Cancelled, true).ConfigureAwait(false); + + } + + public async Task GetAsync(Expression> expression) + { + return await Repository.FindAsync(expression).ConfigureAwait(false); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/IssueJobs/IIssueJobRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/IssueJobs/IIssueJobRepository.cs index aeeac0878..f507ca852 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/IssueJobs/IIssueJobRepository.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Jobs/IssueJobs/IIssueJobRepository.cs @@ -1,6 +1,6 @@ namespace Win_in.Sfs.Wms.Store.Domain; -public interface IIssueJobRepository : ISfsJobRepositoryBase +public interface IInjectionJobRepository : ISfsJobRepositoryBase { } diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteManager.cs new file mode 100644 index 000000000..281cc8cba --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteManager.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public interface IInjectionNoteManager : ISfsStoreManager +{ + Task ConfirmAsync(Guid id); + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteRepository.cs new file mode 100644 index 000000000..fb894c737 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/IInjectionNoteRepository.cs @@ -0,0 +1,6 @@ +namespace Win_in.Sfs.Wms.Store.Domain; + +public interface IInjectionNoteRepository : ISfsStoreRepositoryBase +{ + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNote.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNote.cs new file mode 100644 index 000000000..582852d31 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNote.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Volo.Abp; +using Win_in.Sfs.Shared.Domain.Entities; + +namespace Win_in.Sfs.Wms.Store.Domain; + +/// +/// 注塑发料记录 +/// +public class InjectionNote : SfsStoreAggregateRootBase, IHasJobNumber, IHasRequestNumber +{ + /// + /// 任务ID + /// + [IgnoreUpdate] + public string JobNumber { get; set; } + + /// + /// 车间 + /// + [IgnoreUpdate] + public string Workshop { get; set; } + + /// + /// 明细列表 + /// + [IgnoreUpdate] + public override List Details { get; set; } = new List(); + + [IgnoreUpdate] + public string RequestNumber { get; set; } + + /// + /// 叫料请求类型 + /// + [IgnoreUpdate] + public string RequestType { get; set; } + + /// + /// 使用在途库 + /// + [IgnoreUpdate] + public bool UseOnTheWayLocation { get; set; } + + /// + /// 确认时间 + /// + [Display(Name = "确认时间")] + [IgnoreUpdate] + public DateTime? ConfirmTime { get; set; } + + /// + /// 已确认 + /// + [Display(Name = "已确认")] + public bool Confirmed { get; set; } + + public void Confirm(DateTime confirmTime) + { + + CheckStatus(Confirmed); + Confirmed = true; + ConfirmTime = confirmTime; + } + + private static void CheckStatus(bool confirmed) + { + if (confirmed) + { + throw new UserFriendlyException($"当前状态为 【已确认】 ,无法再次确认!"); + } + + } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteDetail.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteDetail.cs new file mode 100644 index 000000000..9e9d44882 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteDetail.cs @@ -0,0 +1,43 @@ +using System; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public class InjectionNoteDetail : SfsStoreRecommendFromDetailWithFromToEntityBase +{ + + /// + /// 发料时间 + /// + public DateTime IssueTime { get; set; } + + /// + /// 过期时间 + /// + public DateTime ExpiredTime { get; set; } + + /// + /// 生产线 + /// + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + public string WorkStation { get; set; } + + /// + /// 在途库库位 + /// + public string OnTheWayLocationCode { get; set; } + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteManager.cs new file mode 100644 index 000000000..c75194264 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Notes/InjectionNotes/InjectionNoteManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Volo.Abp; +using Volo.Abp.Uow; +using Win_in.Sfs.Shared.Event; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public class InjectionNoteManager : SfsStoreManagerBase, IInjectionNoteManager +{ + + public InjectionNoteManager( + IInjectionNoteRepository repository + ) : base(repository) + { + } + + [UnitOfWork] + public virtual async Task ConfirmAsync(Guid id) + { + var entity = await Repository.FindAsync(id).ConfigureAwait(false); + Check.NotNull(entity, EntityClassName); + entity.Confirm(Clock.Now); + await PublishConfirmedAsync(entity).ConfigureAwait(false); + return await Repository.UpdateAsync(entity).ConfigureAwait(false); + } + private async Task PublishConfirmedAsync(InjectionNote entity) + { + try + { + await LocalEventBus.PublishAsync(new SfsConfirmedEntityEventData(entity), false).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogDebug($"{nameof(InjectionNote)} Confirmed Event:{ex.Message}", null); + Console.WriteLine(ex.Source); + throw; + } + } + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestManager.cs new file mode 100644 index 000000000..bf4efb85c --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestManager.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Win_in.Sfs.Wms.Store.Domain; + +using Win_in.Sfs.Shared.Domain; + +public interface IInjectionRequestManager : ISfsStoreRequestManager, + IBulkImportService +{ + + Task UpdateDetailsAsync(InjectionRequest entity); + + Task CompleteAsync(string number); + + Task CreateBynNumberAsync(InjectionRequest entity); +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestRepository.cs new file mode 100644 index 000000000..ce92572ec --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/IInjectionRequestRepository.cs @@ -0,0 +1,9 @@ +using Win_in.Sfs.Shared.Domain; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public interface IInjectionRequestRepository : ISfsStoreRepositoryBase, + ISfsBulkRepositoryBase +{ + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequest.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequest.cs new file mode 100644 index 000000000..c6058c8ae --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequest.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Win_in.Sfs.Shared.Domain.Entities; + +namespace Win_in.Sfs.Wms.Store.Domain; + +/// +/// 注塑申请 +/// +public class InjectionRequest : SfsStoreRequestAggregateRootBase +{ + /// + /// 叫料类型 + /// + [Display(Name = "叫料类型")] + [IgnoreUpdate] + public string Type { get; set; } + + /// + /// 备料计划单号 + /// + [IgnoreUpdate] + public string PreparationPlanNumber { get; set; } + + /// + /// 生产线 + /// + [IgnoreUpdate] + public string ProdLine { get; set; } + + /// + /// 车间 + /// + [IgnoreUpdate] + public string Workshop { get; set; } + + /// + /// 使用在途库 + /// + public bool UseOnTheWayLocation { get; set; } + + /// + /// 明细列表 + /// + [IgnoreUpdate] + public override List Details { get; set; } = new List(); +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestDetail.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestDetail.cs new file mode 100644 index 000000000..7fab50d6e --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestDetail.cs @@ -0,0 +1,105 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Win_in.Sfs.Shared.Domain; +using Win_in.Sfs.Shared.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Domain; + +/// +/// 注塑申请明细 +/// +public class InjectionRequestDetail : SfsStoreDetailWithQtyEntityBase, IHasToLocation +{ + /// + /// 到库位 + /// + public string ToLocationCode { get; set; } + + /// + /// 到库区 + /// + public string ToLocationArea { get; set; } + + /// + /// 到库位组 + /// + public string ToLocationGroup { get; set; } + + /// + /// 到ERP库位 + /// + public string ToLocationErpCode { get; set; } + + /// + /// 到仓库 + /// + public string ToWarehouseCode { get; set; } + + /// + /// 来源库区 + /// + public string FromLocationArea { get; set; } + + // /// + // /// 在途库库位 + // /// + // public string OnTheWayLocationCode { get; set; } + + /// + /// 生产线 + /// + public string ProdLine { get; set; } + + /// + /// 工作中心 + /// + public string WorkStation { get; set; } + + /// + /// 过期时间 + /// + public DateTime ExpiredTime { get; set; } + + /// + /// 已发数量 + /// + public decimal IssuedQty { get; set; } + + /// + /// 已收数量 + /// + public decimal ReceivedQty { get; set; } + + /// + /// 明细状态 + /// + public EnumStatus Status { get; set; } + + /// + /// 请求未发 还未发送的数量 + /// + [NotMapped] + public decimal ToBeIssuedQty => Qty - IssuedQty; + + /// + /// 已发未收 + /// + [NotMapped] + public decimal ToBeReceivedQty => IssuedQty - ReceivedQty; + + /// + /// 请求未收 + /// + [NotMapped] + public decimal NotFinishQty => Qty - ReceivedQty; + + /// + /// 位置码 + /// + public string PositionCode { get; set; } + + /// + /// 推荐类型 + /// + public EnumRecommendType RecommendType { get; set; } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestManager.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestManager.cs new file mode 100644 index 000000000..53b2f4cee --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Domain/Requests/InjectionRequests/InjectionRequestManager.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DocumentFormat.OpenXml.Math; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Shared.Event; +using static Win_in.Sfs.Wms.Store.Domain.Shared.StoreSettings; + +namespace Win_in.Sfs.Wms.Store.Domain; + +public class InjectionRequestManager + : SfsStoreRequestManagerBase + , IInjectionRequestManager +{ + private readonly IInjectionRequestRepository _repository; + private readonly IIssueJobRepository _issueJobRepository; + + public InjectionRequestManager( + IInjectionRequestRepository repository + , IIssueJobRepository issueJobRepository + ) : base(repository) + { + _repository = repository; + _issueJobRepository = issueJobRepository; + } + + /// + /// 创建 + /// + /// + /// + public virtual async Task CreateBynNumberAsync(InjectionRequest entity) + { + var number = string.IsNullOrEmpty(entity.Number) ? await GenerateNumberAsync(nameof(InjectionRequest), entity.ActiveDate).ConfigureAwait(false) : entity.Number; + entity.SetIdAndNumberWithDetails(GuidGenerator, number); + entity.Submit(); + entity.Agree(); + entity.RequestStatus = EnumRequestStatus.Partial; + await LocalEventBus.PublishAsync(new SfsHandledEntityEventData(entity), false) + .ConfigureAwait(false); + await _repository.InsertAsync(entity).ConfigureAwait(false); + return entity; + } + + public virtual async Task UpdateDetailsAsync(InjectionRequest newEntity) + { + var oldEntity = await Repository.FindAsync(newEntity.Id, true).ConfigureAwait(false); + if(oldEntity!=null) + { + foreach (var newDetail in newEntity.Details) + { + oldEntity.ReplaceDetail(newDetail.Id, newDetail); + } + + foreach (var detail in oldEntity.Details) + { + SetMaterialRequestDetailStatus(detail); + } + + await SetMaterialRequestStatus(oldEntity).ConfigureAwait(false); + + await Repository.UpdateAsync(oldEntity).ConfigureAwait(false); + } + + } + + private void SetMaterialRequestDetailStatus(InjectionRequestDetail detail) + { + if (detail.ReceivedQty >= detail.Qty)//执行的时候 实际收料 多余 要料数 + { + detail.Status = EnumStatus.Close; + } + else + { + detail.Status = EnumStatus.Open; + } + } + + private async Task SetMaterialRequestStatus(InjectionRequest materialRequest) + { + if (materialRequest.Details.All(p => p.Status == EnumStatus.Close)) + { + materialRequest.RequestStatus = EnumRequestStatus.Completed; + } + else + { + var issueJobs = await _issueJobRepository.GetListAsync(t => t.MaterialRequestNumber == materialRequest.Number).ConfigureAwait(false); + if (issueJobs.Count > 0) + { + if (issueJobs.All(t => t.JobStatus is EnumJobStatus.Done or EnumJobStatus.Closed or EnumJobStatus.Cancelled)) + { + if (materialRequest.Details.All(p => p.ReceivedQty >= p.Qty)) + { + materialRequest.RequestStatus = EnumRequestStatus.Completed; + } + else + { + materialRequest.RequestStatus = EnumRequestStatus.Partial; + } + } + else + { + materialRequest.RequestStatus = EnumRequestStatus.Partial; + } + } + else + { + materialRequest.RequestStatus = EnumRequestStatus.Partial; + } + } + } + + public virtual async Task CompleteAsync(string number) + { + var entity = await GetByNumberAsync(number).ConfigureAwait(false); + if (entity != null && !entity.Details.Any(p => p.ToBeIssuedQty > 0)) + { + await CompleteAsync(entity).ConfigureAwait(false); + } + } + + + #region 导入 + + /// + /// 执行导入 + /// + public virtual async Task ImportDataAsync(List mergeEntities, List deleteEntities = null) + { + if (deleteEntities != null && deleteEntities.Count > 0) + { + await _repository.BulkDeleteAsync(deleteEntities).ConfigureAwait(false); + } + + await CreateManyAsync(mergeEntities).ConfigureAwait(false); + } + + #endregion + +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/IStoreDbContext.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/IStoreDbContext.cs index 6ce3b9881..3e88393d3 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/IStoreDbContext.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/IStoreDbContext.cs @@ -18,6 +18,7 @@ public interface IStoreDbContext : IEfCoreDbContext public DbSet TransferRequests { get; } public DbSet ProductReceiptRequests { get; } public DbSet MaterialRequests { get; } + public DbSet InjectionRequests { get; } public DbSet ContainerRequests { get; } public DbSet DeliverRequests { get; } public DbSet InspectRequests { get; } @@ -69,6 +70,7 @@ public interface IStoreDbContext : IEfCoreDbContext public DbSet InventoryTransferNotes { get; } public DbSet WarehouseTransferNotes { get; } public DbSet IssueNotes { get; } + public DbSet InjectionNotes { get; } public DbSet ContainerNotes { get; } public DbSet UnplannedReceiptNotes { get; } public DbSet UnplannedIssueNotes { get; } @@ -94,6 +96,7 @@ public interface IStoreDbContext : IEfCoreDbContext public DbSet InspectJobs { get; } public DbSet PutawayJobs { get; } public DbSet IssueJobs { get; } + public DbSet InjectionJobs { get; } public DbSet ContainerJobs { get; } public DbSet DeliverJobs { get; } public DbSet JisDeliverJobs { get; } diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobDbContextModelCreatingExtensions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..49690e16a --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobDbContextModelCreatingExtensions.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Shared.EntityFrameworkCore; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public static class InjectionJobDbContextModelCreatingExtensions +{ + public static void ConfigureInjectionJob(this ModelBuilder builder, StoreModelBuilderConfigurationOptions options) + { + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(StoreDbProperties.JobDbTablePrefix + nameof(InjectionJob), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsBase(); + //Configure Job base properties + b.ConfigureJob(); + //Properties + b.Property(q => q.RequestType).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.InjectionRequestNumber).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.Workshop).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ProdLine).HasMaxLength(SfsPropertyConst.CodeLength); + //Relations + b.HasMany(q => q.Details).WithOne().HasForeignKey(d => d.MasterID).IsRequired(); + //Indexes + b.HasIndex(q => new { q.Number }).IsUnique(); + }); + + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(StoreDbProperties.JobDbTablePrefix + nameof(InjectionJobDetail), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsBase(); + //Configure Job base properties + b.ConfigureJobRecommendFromDetail(); + //Properties + b.Property(q => q.ExpiredTime).IsRequired(); + b.Property(q => q.ToLocationCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationErpCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToWarehouseCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationArea).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationGroup).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RequestLocationCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ProdLine).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.WorkStation).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.Operation).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.DistributionType).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.TruncType).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.PlannedSplitRule).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.OnTheWayLocationCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.PositionCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RecommendType).HasMaxLength(SfsPropertyConst.CodeLength).HasConversion(); + + //Relations + //None + + //Indexes + //b.HasIndex(q => new { q.PackingCode }).IsUnique(); + }); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobEfCoreRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobEfCoreRepository.cs new file mode 100644 index 000000000..3f08eb44e --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Jobs/InjectionJobs/InjectionJobEfCoreRepository.cs @@ -0,0 +1,11 @@ +using Volo.Abp.EntityFrameworkCore; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public class InjectionJobEfCoreRepository : SfsJobEfCoreRepositoryBase, IInjectionJobRepository +{ + public InjectionJobEfCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteDbContextModelCreatingExtensions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..debf08f37 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteDbContextModelCreatingExtensions.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public static class InjectionNoteDbContextModelCreatingExtensions +{ + public static void ConfigureInjectionNote(this ModelBuilder builder, StoreModelBuilderConfigurationOptions options) + { + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(options.TablePrefix + nameof(InjectionNote), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsStoreBase(); + + //Properties + b.Property(q => q.RequestNumber).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RequestType).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.Workshop).HasMaxLength(SfsPropertyConst.NameLength); + b.Property(q => q.Remark).HasMaxLength(SfsPropertyConst.RemarkLength); + + //Relations + b.HasMany(q => q.Details).WithOne().HasForeignKey(d => d.MasterID).IsRequired(); + + //Indexes + b.HasIndex(q => new { q.Number }).IsUnique(); + }); + + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(options.TablePrefix + nameof(InjectionNoteDetail), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsStoreBase(); + //Configure Sfs store detail properties + b.ConfigureSfsStoreDetailBase(); + + //Properties + b.Property(q => q.IssueTime).IsRequired(); + b.Property(q => q.ExpiredTime).IsRequired(); + b.Property(q => q.ProdLine).HasMaxLength(SfsPropertyConst.NameLength); + b.Property(q => q.WorkStation).HasMaxLength(SfsPropertyConst.NameLength); + b.Property(q => q.OnTheWayLocationCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.FromStatus).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.ToStatus).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.PositionCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RecommendType).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + + //Relations + + //Indexes + b.HasIndex(q => new { q.Number, q.FromPackingCode, q.FromLocationCode, q.ToLocationCode }).IsUnique(); + b.HasIndex(q => new { q.FromPackingCode }); + }); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteEfCoreRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteEfCoreRepository.cs new file mode 100644 index 000000000..26e8dc61c --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Notes/InjectionNotes/InjectionNoteEfCoreRepository.cs @@ -0,0 +1,11 @@ +using Volo.Abp.EntityFrameworkCore; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public class InjectionNoteEfCoreRepository : SfsStoreEfCoreRepositoryBase, IInjectionNoteRepository +{ + public InjectionNoteEfCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestDbContextModelCreatingExtensions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..f34683a95 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestDbContextModelCreatingExtensions.cs @@ -0,0 +1,67 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public static class InjectionRequestDbContextModelCreatingExtensions +{ + public static void ConfigureInjectionRequest(this ModelBuilder builder, StoreModelBuilderConfigurationOptions options) + { + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(options.TablePrefix + nameof(InjectionRequest), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsStoreBase(); + + //Properties + b.Property(q => q.Type).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.PreparationPlanNumber).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ProdLine).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.Workshop).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RequestStatus).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + + //Relations + b.HasMany(q => q.Details).WithOne().HasForeignKey(d => d.MasterID).IsRequired(); + + //Indexes + b.HasIndex(q => new { q.Number }).IsUnique(); + }); + + builder.Entity(b => + { + //Configure table & schema name + b.ToTable(options.TablePrefix + nameof(InjectionRequestDetail), options.Schema); + //Configure ABP properties + b.ConfigureByConvention(); + //Configure Sfs base properties + b.ConfigureSfsStoreBase(); + //Configure Sfs store detail properties + b.ConfigureSfsStoreDetailBase(); + //Properties + b.Property(q => q.ToLocationCode).IsRequired().HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationErpCode).IsRequired().HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToWarehouseCode).IsRequired().HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationArea).IsRequired().HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ToLocationGroup).IsRequired().HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.ExpiredTime).IsRequired(); + b.Property(q => q.ProdLine).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.WorkStation).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.IssuedQty).HasPrecision(18, 6); + b.Property(q => q.ReceivedQty).HasPrecision(18, 6); + b.Property(q => q.Status).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + b.Property(q => q.PositionCode).HasMaxLength(SfsPropertyConst.CodeLength); + b.Property(q => q.RecommendType).HasMaxLength(SfsPropertyConst.NameLength).HasConversion(); + + //Relations + + //Indexes + b.HasIndex(q => new { q.Number, q.ItemCode, q.ToLocationCode }).IsUnique(); + b.HasIndex(q => new { q.ItemCode }); + }); + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestEfCoreRepository.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestEfCoreRepository.cs new file mode 100644 index 000000000..7b5e20a16 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/Requests/InjectionRequests/InjectionRequestEfCoreRepository.cs @@ -0,0 +1,11 @@ +using Volo.Abp.EntityFrameworkCore; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.EntityFrameworkCore; + +public class InjectionRequestEfCoreRepository : SfsStoreEfCoreRepositoryBase, IInjectionRequestRepository +{ + public InjectionRequestEfCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContext.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContext.cs index d33e47fe6..667258241 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContext.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContext.cs @@ -18,6 +18,7 @@ public class StoreDbContext : AbpDbContext, IStoreDbContext public DbSet TransferRequests { get; set; } public DbSet ProductReceiptRequests { get; set; } public DbSet MaterialRequests { get; set; } + public DbSet InjectionRequests { get; set; } public DbSet ContainerRequests { get; set; } public DbSet DeliverRequests { get; set; } public DbSet InspectRequests { get; set; } @@ -69,6 +70,7 @@ public class StoreDbContext : AbpDbContext, IStoreDbContext public DbSet InventoryTransferNotes { get; set; } public DbSet WarehouseTransferNotes { get; set; } public DbSet IssueNotes { get; set; } + public DbSet InjectionNotes { get; set; } public DbSet ContainerNotes { get; set; } public DbSet UnplannedReceiptNotes { get; set; } public DbSet UnplannedIssueNotes { get; set; } @@ -94,6 +96,7 @@ public class StoreDbContext : AbpDbContext, IStoreDbContext public DbSet InspectJobs { get; set; } public DbSet PutawayJobs { get; set; } public DbSet IssueJobs { get; set; } + public DbSet InjectionJobs { get; set; } public DbSet ContainerJobs { get; set; } public DbSet DeliverJobs { get; set; } public DbSet JisDeliverJobs { get; set; } diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContextModelCreatingExtensions.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContextModelCreatingExtensions.cs index a325299a5..54acd711b 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContextModelCreatingExtensions.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreDbContextModelCreatingExtensions.cs @@ -54,6 +54,7 @@ public static class StoreDbContextModelCreatingExtensions builder.ConfigureProductReceiptRequest(options); builder.ConfigurePurchaseReceiptRequest(options); builder.ConfigureMaterialRequest(options); + builder.ConfigureInjectionRequest(options); builder.ConfigureDeliverRequest(options); builder.ConfigureContainerRequest(options); builder.ConfigureInspectRequest(options); @@ -87,6 +88,7 @@ public static class StoreDbContextModelCreatingExtensions builder.ConfigureInventoryTransferNote(options); builder.ConfigureWarehouseTransferNote(options); builder.ConfigureIssueNote(options); + builder.ConfigureInjectionNote(options); builder.ConfigureContainerNote(options); builder.ConfigureUnplannedReceiptNote(options); builder.ConfigureUnplannedIssueNote(options); @@ -113,6 +115,7 @@ public static class StoreDbContextModelCreatingExtensions builder.ConfigureInspectJob(options); builder.ConfigurePutawayJob(options); builder.ConfigureIssueJob(options); + builder.ConfigureInjectionJob(options); builder.ConfigureContainerJob(options); builder.ConfigureDeliverJob(options); builder.ConfigureJisDeliverJob(options); diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreEntityFrameworkCoreModule.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreEntityFrameworkCoreModule.cs index 24dc4c2c4..d9d854595 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreEntityFrameworkCoreModule.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.EntityFrameworkCore/StoreEntityFrameworkCoreModule.cs @@ -54,6 +54,7 @@ public class StoreEntityFrameworkCoreModule : AbpModule context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); + context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); @@ -88,6 +89,7 @@ public class StoreEntityFrameworkCoreModule : AbpModule context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); + context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); @@ -114,6 +116,7 @@ public class StoreEntityFrameworkCoreModule : AbpModule context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); + context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); context.Services.AddTransient(); @@ -174,6 +177,8 @@ public class StoreEntityFrameworkCoreModule : AbpModule orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); + options.Entity(orderOptions => + orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => @@ -222,6 +227,8 @@ public class StoreEntityFrameworkCoreModule : AbpModule orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); + options.Entity(orderOptions => + orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => @@ -293,6 +300,8 @@ public class StoreEntityFrameworkCoreModule : AbpModule orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); + options.Entity(orderOptions => + orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Details)); options.Entity(orderOptions => diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Jobs/InjectionJobAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Jobs/InjectionJobAutoMapperProfile.cs new file mode 100644 index 000000000..4665215e3 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Jobs/InjectionJobAutoMapperProfile.cs @@ -0,0 +1,92 @@ +using System; +using AutoMapper; +using Volo.Abp.AutoMapper; +using Win_in.Sfs.Shared.Application; +using Win_in.Sfs.Wms.Inventory.Application.Contracts; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Event; + +public partial class StoreEventAutoMapperProfile : Profile +{ + private void InjectionJobAutoMapperProfile() + { + + CreateMap() + .ForMember(x => x.JobNumber, y => y.MapFrom(d => d.Number)) + .ForMember(x => x.RequestNumber, y => y.MapFrom(d => d.InjectionRequestNumber)) + .ForMember(x => x.ActiveDate, y => y.MapFrom(d => DateTime.Now)) + .ForMember(x => x.Worker, y => y.MapFrom(d => d.CompleteUserName)) + .Ignore(x => x.Confirmed) + .Ignore(x => x.Number) + .Ignore(x => x.ConfirmTime); + + CreateMap() + .ForMember(x => x.FromPackingCode, y => y.MapFrom(d => d.HandledPackingCode)) + .ForMember(x => x.ToPackingCode, y => y.MapFrom(d => d.HandledPackingCode)) + .ForMember(x => x.SupplierBatch, y => y.MapFrom(d => d.HandledSupplierBatch)) + .ForMember(x => x.ArriveDate, y => y.MapFrom(d => d.HandledArriveDate)) + .ForMember(x => x.ProduceDate, y => y.MapFrom(d => d.HandledProduceDate)) + .ForMember(x => x.ExpireDate, y => y.MapFrom(d => d.HandledExpireDate)) + .ForMember(x => x.FromLot, y => y.MapFrom(d => d.HandledLot)) + .ForMember(x => x.ToLot, y => y.MapFrom(d => d.HandledLot)) + .ForMember(x => x.ToContainerCode, y => y.MapFrom(d => d.HandledContainerCode)) + .ForMember(x => x.FromContainerCode, y => y.MapFrom(d => d.HandledContainerCode)) + .ForMember(x => x.FromStatus, y => y.MapFrom(d => d.Status)) + .ForMember(x => x.ToStatus, y => y.MapFrom(d => d.Status)) + .ForMember(x => x.Qty, y => y.MapFrom(d => d.HandledQty)) + .ForMember(x => x.IssueTime, y => y.MapFrom(d => DateTime.Now)) + .ForMember(x => x.FromLocationCode, y => y.MapFrom(d => d.HandledFromLocationCode)) + .ForMember(x => x.FromLocationArea, y => y.MapFrom(d => d.HandledFromLocationArea)) + .ForMember(x => x.FromLocationGroup, y => y.MapFrom(d => d.HandledFromLocationGroup)) + .ForMember(x => x.FromLocationErpCode, y => y.MapFrom(d => d.HandledFromLocationErpCode)) + .ForMember(x => x.FromWarehouseCode, y => y.MapFrom(d => d.HandledFromWarehouseCode)) + .ForMember(x => x.ToLocationCode, y => y.MapFrom(d => d.ToLocationCode)) + .Ignore(x => x.ToLocationArea) + .Ignore(x => x.ToLocationGroup) + .Ignore(x => x.ToLocationErpCode) + ; + + CreateMap() + .MapExpectInOutFrom() + .Ignore(x => x.Worker) + .Ignore(x => x.SerialNumber) + .Ignore(x => x.ExtraProperties) + ; + + CreateMap() + .MapExpectInOutFrom() + .Ignore(x => x.Worker) + .Ignore(x => x.SerialNumber) + .Ignore(x => x.ExtraProperties); + + CreateMap() + .ForMember(x => x.RequestNumber, y => y.MapFrom(d => d.InjectionRequestNumber)) + .Ignore(x => x.Confirmed) + .Ignore(x => x.JobNumber) + .Ignore(x => x.ActiveDate) + ; + CreateMap() + .ForMember(x => x.Qty, y => y.MapFrom(d => d.HandledQty)) + .ForMember(x => x.IssueTime, y => y.MapFrom(d => DateTime.Now)) + .ForMember(x => x.FromPackingCode, y => y.MapFrom(d => d.HandledPackingCode)) + .ForMember(x => x.ToPackingCode, y => y.MapFrom(d => d.HandledPackingCode)) + .ForMember(x => x.FromContainerCode, y => y.MapFrom(d => d.HandledContainerCode)) + .ForMember(x => x.ToContainerCode, y => y.MapFrom(d => d.HandledContainerCode)) + .ForMember(x => x.FromLot, y => y.MapFrom(d => d.HandledLot)) + .ForMember(x => x.ToLot, y => y.MapFrom(d => d.HandledLot)) + .ForMember(x => x.SupplierBatch, y => y.MapFrom(d => d.HandledSupplierBatch)) + .ForMember(x => x.ArriveDate, y => y.MapFrom(d => d.HandledArriveDate)) + .ForMember(x => x.ProduceDate, y => y.MapFrom(d => d.HandledProduceDate)) + .ForMember(x => x.ExpireDate, y => y.MapFrom(d => d.ExpiredTime)) + .ForMember(x => x.FromLocationCode, y => y.MapFrom(d => d.HandledFromLocationCode)) + .ForMember(x => x.FromLocationArea, y => y.MapFrom(d => d.HandledFromLocationArea)) + .ForMember(x => x.FromLocationGroup, y => y.MapFrom(d => d.HandledFromLocationGroup)) + .ForMember(x => x.FromLocationErpCode, y => y.MapFrom(d => d.HandledFromLocationErpCode)) + .ForMember(x => x.FromWarehouseCode, y => y.MapFrom(d => d.HandledFromWarehouseCode)) + .ForMember(x => x.FromStatus, y => y.MapFrom(d => d.Status)) + .ForMember(x => x.ToStatus, y => y.MapFrom(d => d.Status)) + ; + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Requests/InjectionRequestAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Requests/InjectionRequestAutoMapperProfile.cs new file mode 100644 index 000000000..2d2aef453 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/AutoMapperProfiles/Requests/InjectionRequestAutoMapperProfile.cs @@ -0,0 +1,134 @@ +using AutoMapper; +using Volo.Abp.AutoMapper; +using Win_in.Sfs.Shared.Application; +using Win_in.Sfs.Wms.Inventory.Application.Contracts; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; + +namespace Win_in.Sfs.Wms.Store.Event; + +public partial class StoreEventAutoMapperProfile : Profile +{ + private void InjectionRequestAutoMapperProfile() + { + CreateMap() + .ForMember(x => x.InjectionRequestNumber, y => y.MapFrom(d => d.Number)) + .ForMember(x => x.RequestType, y => y.MapFrom(d => d.Type)) + .Ignore(x => x.WarehouseCode) + .Ignore(x => x.UpStreamJobNumber) + .Ignore(x => x.JobType) + .Ignore(x => x.IsAutoComplete) + .Ignore(x => x.ExpiredTime) + .Ignore(x => x.JobDescription) + .Ignore(x => x.JobStatus) + .Ignore(x => x.WorkGroupCode) + .Ignore(x => x.Priority) + .Ignore(x => x.PriorityIncrement) + .Ignore(x => x.AcceptUserId) + .Ignore(x => x.AcceptTime) + .Ignore(x => x.AcceptUserName) + .Ignore(x => x.CompleteUserId) + .Ignore(x => x.CompleteUserName) + .Ignore(x => x.CompleteTime) + .Ignore(x => x.Details) + ; + + CreateMap() + .ForMember(x => x.RequestLocationCode, y => y.MapFrom(d => d.ToLocationCode)) + .Ignore(x => x.RecommendFromLocationArea) + .Ignore(x => x.RecommendFromLocationGroup) + .Ignore(x => x.HandledFromLocationArea) + .Ignore(x => x.HandledFromLocationGroup) + .Ignore(x => x.RecommendFromWarehouseCode) + .Ignore(x => x.HandledFromWarehouseCode) + .Ignore(x => x.OnTheWayLocationCode) + .Ignore(x => x.DistributionType) + .Ignore(x => x.RoundedQty) + .Ignore(x => x.Operation) + .Ignore(x => x.ExpiredTime) + .Ignore(x => x.TruncType) + .Ignore(x => x.PlanBeginTime) + .Ignore(x => x.PlannedSplitRule) + .Ignore(x => x.DeliveryQty) + .Ignore(x => x.Status) + .Ignore(x => x.RecommendContainerCode) + .Ignore(x => x.StdPackQty) + .Ignore(x => x.RecommendPackingCode) + .Ignore(x => x.HandledContainerCode) + .Ignore(x => x.HandledPackingCode) + .Ignore(x => x.RecommendSupplierBatch) + .Ignore(x => x.RecommendProduceDate) + .Ignore(x => x.RecommendArriveDate) + .Ignore(x => x.RecommendExpireDate) + .Ignore(x => x.HandledFromLocationCode) + .Ignore(x => x.HandledFromLocationErpCode) + .Ignore(x => x.HandledUom) + .Ignore(x => x.RecommendFromLocationErpCode) + .Ignore(x => x.HandledExpireDate) + .Ignore(x => x.HandledLot) + .Ignore(x => x.HandledArriveDate) + .Ignore(x => x.HandledProduceDate) + .Ignore(x => x.HandledQty) + .Ignore(x => x.RecommendQty) + .Ignore(x => x.Uom) + .Ignore(x => x.HandledSupplierBatch) + .Ignore(x => x.RecommendFromLocationCode) + .Ignore(x => x.RecommendLot) + .IgnoreIHasRecommendAndHandledFrom(); + + CreateMap() + .ForMember(x => x.RecommendArriveDate, y => y.MapFrom(d => d.ArriveDate)) + .ForMember(x => x.RecommendContainerCode, y => y.MapFrom(d => d.ContainerCode)) + .ForMember(x => x.RecommendExpireDate, y => y.MapFrom(d => d.ExpireDate)) + .ForMember(x => x.RecommendFromLocationCode, y => y.MapFrom(d => d.LocationCode)) + .ForMember(x => x.RecommendFromLocationErpCode, y => y.MapFrom(d => d.LocationErpCode)) + .ForMember(x => x.RecommendFromWarehouseCode, y => y.MapFrom(d => d.WarehouseCode)) + .ForMember(x => x.RecommendFromLocationArea, y => y.MapFrom(d => d.LocationArea)) + .ForMember(x => x.RecommendFromLocationGroup, y => y.MapFrom(d => d.LocationGroup)) + .ForMember(x => x.RecommendLot, y => y.MapFrom(d => d.Lot)) + .ForMember(x => x.RecommendPackingCode, y => y.MapFrom(d => d.PackingCode)) + .ForMember(x => x.RecommendProduceDate, y => y.MapFrom(d => d.ProduceDate)) + .ForMember(x => x.RecommendQty, y => y.MapFrom(d => d.Qty)) + .ForMember(x => x.RecommendSupplierBatch, y => y.MapFrom(d => d.SupplierBatch)) + .ForMember(x => x.Uom, y => y.MapFrom(d => d.Uom)).Ignore(x => x.HandledArriveDate) + .Ignore(x => x.HandledFromLocationArea) + .Ignore(x => x.HandledFromLocationGroup) + .Ignore(x => x.ToLocationErpCode) + .Ignore(x => x.ToWarehouseCode) + .Ignore(x => x.ToLocationArea) + .Ignore(x => x.ToLocationGroup) + .Ignore(x => x.HandledFromWarehouseCode) + .Ignore(x => x.RequestLocationCode) + .Ignore(x => x.ToLocationCode) + .Ignore(x => x.ProdLine) + .Ignore(x => x.WorkStation) + .Ignore(x => x.HandledContainerCode) + .Ignore(x => x.HandledExpireDate) + .Ignore(x => x.HandledFromLocationCode) + .Ignore(x => x.HandledFromLocationErpCode) + .Ignore(x => x.HandledLot) + .Ignore(x => x.HandledPackingCode) + .Ignore(x => x.HandledProduceDate) + .Ignore(x => x.HandledQty) + .Ignore(x => x.HandledSupplierBatch) + .Ignore(x => x.HandledUom) + .Ignore(x => x.Remark) + .Ignore(x => x.OnTheWayLocationCode) + .Ignore(x => x.DistributionType) + .Ignore(x => x.RoundedQty) + .Ignore(x => x.Operation) + .Ignore(x => x.ExpiredTime) + .Ignore(x => x.TruncType) + .Ignore(x => x.PlanBeginTime) + .Ignore(x => x.PlannedSplitRule) + .Ignore(x => x.DeliveryQty) + .Ignore(x => x.RequestLocationCode) + .Ignore(x => x.ToLocationCode) + .Ignore(x => x.ProdLine) + .Ignore(x => x.WorkStation) + .Ignore(x => x.PositionCode) + .Ignore(x => x.RecommendType) + .IgnoreIHasRecommendAndHandledFrom(); + + } +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Jobs/InjectionJobEventHandler.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Jobs/InjectionJobEventHandler.cs new file mode 100644 index 000000000..9499c8fa1 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Jobs/InjectionJobEventHandler.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.EventBus; +using Volo.Abp.Uow; +using Win_in.Sfs.Basedata.Application.Contracts; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Shared.Event; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; +using Win_in.Sfs.Wms.Store.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Event.BusinessJob; + +public class InjectionJobEventHandler : + StoreEventHandlerBase + , ILocalEventHandler> +{ + private const EnumTransType TransType = EnumTransType.Issue; + + private readonly IInjectionNoteAppService _injectionNoteAppService; + private readonly ILocationAppService _locationAppService; + + public InjectionJobEventHandler(IInjectionNoteAppService injectionNoteAppService, ILocationAppService locationAppService) + { + _injectionNoteAppService = injectionNoteAppService; + _locationAppService = locationAppService; + } + + /// + /// 执行后 + /// + /// + /// + [UnitOfWork] + public virtual async Task HandleEventAsync(SfsCompletedEntityEventData eventData) + { + var entity = eventData.Entity; + var injectionNote = await BuildInjectionNoteAsync(entity).ConfigureAwait(false); + await _injectionNoteAppService.CreateAsync(injectionNote).ConfigureAwait(false); + } + + #region 私有 + + /// + /// 创建补料记录实体 + /// + /// + /// + private async Task BuildInjectionNoteAsync(InjectionJob entity) + { + var injectionNoteCreateInput = ObjectMapper.Map(entity); + injectionNoteCreateInput.JobNumber = entity.Number; + var locationCodes = injectionNoteCreateInput.Details.Select(p => p.ToLocationCode).Distinct().ToList(); + var locations = await _locationAppService.GetByCodesAsync(locationCodes).ConfigureAwait(false); + + injectionNoteCreateInput.Details.RemoveAll(p => p.Qty == 0); + + foreach (var detail in injectionNoteCreateInput.Details) + { + var location = locations.First(p => p.Code == detail.ToLocationCode); + await RemovePackingCodeAndContainerCodeAndLotAsync(detail, location.Type).ConfigureAwait(false); //去箱 去托 去批 + + detail.ToLocationArea = location.AreaCode; + detail.ToLocationGroup = location.LocationGroupCode; + detail.ToLocationErpCode = location.ErpLocationCode; + detail.ToWarehouseCode = location.WarehouseCode; + } + + return injectionNoteCreateInput; + } + + /// + /// 去除箱码 托码 批次 + /// + private async Task RemovePackingCodeAndContainerCodeAndLotAsync(InjectionNoteDetailInput injectionNoteDetail, + EnumLocationType locationType) + { + switch (locationType) + { + case EnumLocationType.WIP: + { + //用开关控制 发料到线边后去除箱码和托码 ??? + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToWip.IsRemovePackingCode) + .ConfigureAwait(false)) + { + injectionNoteDetail.ToPackingCode = ""; + } + + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToWip.IsRemoveContainerCode) + .ConfigureAwait(false)) + { + injectionNoteDetail.ToContainerCode = ""; + } + + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToWip.IsRemoveLot).ConfigureAwait(false)) + { + injectionNoteDetail.ToLot = ""; + } + + break; + } + case EnumLocationType.SEMI: + { + //用开关控制 发料到后去除箱码和托码 ??? + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToSemi.IsRemovePackingCode) + .ConfigureAwait(false)) + { + injectionNoteDetail.ToPackingCode = ""; + } + + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToSemi.IsRemoveContainerCode) + .ConfigureAwait(false)) + { + injectionNoteDetail.ToContainerCode = ""; + } + + if (await SettingManager.IsTrueAsync(StoreSettings.Issue.ToSemi.IsRemoveLot).ConfigureAwait(false)) + { + injectionNoteDetail.ToLot = ""; + } + + break; + } + } + } + + #endregion +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Requests/InjectionRequestEventHandler.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Requests/InjectionRequestEventHandler.cs new file mode 100644 index 000000000..40d749e08 --- /dev/null +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Requests/InjectionRequestEventHandler.cs @@ -0,0 +1,275 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentValidation.Validators; +using Volo.Abp; +using Volo.Abp.EventBus; +using Volo.Abp.SettingManagement; +using Win_in.Sfs.Basedata.Application.Contracts; +using Win_in.Sfs.Shared.Domain.Shared; +using Win_in.Sfs.Shared.Event; +using Win_in.Sfs.Wms.Inventory.Application.Contracts; +using Win_in.Sfs.Wms.Store.Application.Contracts; +using Win_in.Sfs.Wms.Store.Domain; +using Win_in.Sfs.Wms.Store.Domain.Shared; + +namespace Win_in.Sfs.Wms.Store.Event.BusinessRequest; + +public class InjectionRequestEventHandler + : StoreEventHandlerBase + , ILocalEventHandler> + , ILocalEventHandler> + , ILocalEventHandler> + , ILocalEventHandler> + , ILocalEventHandler>> +{ + private readonly IInjectionJobAppService _injectionJobAppService; + private readonly IProductionLineAppService _productionLineAppService; + private readonly IInjectionRequestManager _injectionRequestManager; + private readonly ILocationAppService _locationAppService; + private readonly IBalanceAppService _balanceAppService; + + public InjectionRequestEventHandler( + IInjectionJobAppService injectionJobAppService + , IProductionLineAppService productionLineAppService + , IInjectionRequestManager injectionRequestManager + , ILocationAppService locationAppService + , IBalanceAppService balanceAppService) + { + _injectionJobAppService = injectionJobAppService; + _productionLineAppService = productionLineAppService; + _injectionRequestManager = injectionRequestManager; + _locationAppService = locationAppService; + _balanceAppService = balanceAppService; + } + + /// + /// 创建后 + /// + /// Event data + public virtual async Task HandleEventAsync(SfsCreatedEntityEventData eventData) + { + var entity = eventData.Entity; + + //if (entity.AutoSubmit) + //{ + // await _injectionRequestManager.SubmitAsync(entity).ConfigureAwait(false); + //} + } + + /// + /// 批量创建后 + /// + /// Event data + public virtual async Task HandleEventAsync(SfsCreatedEntityEventData> eventData) + { + var entitys = eventData.Entity; + foreach (var entity in entitys) + { + if (entity.AutoSubmit) + { + await _injectionRequestManager.SubmitAsync(entity).ConfigureAwait(false); + } + + } + } + + /// + /// 执行后 + /// + /// + /// + public virtual async Task HandleEventAsync(SfsHandledEntityEventData eventData) + { + var entity = eventData.Entity; + var injectionJobs = await BuildInjectionJobAsync(entity).ConfigureAwait(false); + if (injectionJobs.Any()) + { + await _injectionJobAppService.CreateManyAsync(injectionJobs).ConfigureAwait(false); + } + } + + /// + /// 驳回后 + /// + /// + /// + public virtual async Task HandleEventAsync(SfsAbortedEntityEventData eventData) + { + + } + + /// + /// 完成后 + /// + /// + /// + public virtual async Task HandleEventAsync(SfsCompletedEntityEventData eventData) + { + _ = eventData.Entity; + // await _injectionJobAppService.CompleteByInjectionRequestAsync(entity.Number); + + await Task.CompletedTask.ConfigureAwait(false); + } + + #region 私有 + + private async Task> BuildInjectionJobAsync + (InjectionRequest injectionRequest) + { + var jobs = new List(); + + var transactionType = await TransactionTypeAclService.GetByTransTypeAsync(EnumTransType.Issue, EnumTransSubType.None).ConfigureAwait(false);//库存事务 + + var toLocationCodes = injectionRequest.Details.Select(p => p.ToLocationCode).Distinct().ToList();//所有发送库位的集合 + var toLocations = await _locationAppService.GetByCodesAsync(toLocationCodes).ConfigureAwait(false);//所有库位的集合 + + var injectionRequestDetails = injectionRequest.Details.Where(p => p.ToBeIssuedQty > 0);//所有还没发送物品的集合 + foreach (var injectionRequestDetail in injectionRequestDetails)//如果有还有剩余未叫料的数量 则创建新的任务 + { + var toLocation = toLocations.FirstOrDefault(p => p.Code == injectionRequestDetail.ToLocationCode);//判断目标库位是否存在 + Check.NotNull(toLocation, "库位代码", $"库位 {injectionRequestDetail.ToLocationCode} 不存在"); + + //创建详情 + var jobDetails = await BuildInjectionJobDetailInputsAsync(injectionRequest, injectionRequestDetail, transactionType, toLocation.LocationGroupCode).ConfigureAwait(false); + if (!jobDetails.Any()) + { + continue; + } + + var fromLocationCode = jobDetails[0].RecommendFromLocationCode; + var fromLocation = await _locationAppService.GetByCodeAsync(fromLocationCode).ConfigureAwait(false); + var job = jobs.FirstOrDefault(p => p.WorkGroupCode == fromLocation?.WorkGroupCode); + if (job == null || job.Details.Any(p => p.ToLocationCode != injectionRequestDetail.ToLocationCode)) + { + job = BuildInjectionJobCreateInput(injectionRequest, fromLocation); + jobs.Add(job); + } + job.Details.AddRange(jobDetails); + if (injectionRequestDetail.ToBeIssuedQty < 0) + { + injectionRequestDetail.Status = EnumStatus.Close; + } + } + + jobs = jobs.Where(p => p.Details.Any()).ToList(); + + var openRequestDetails = + injectionRequest.Details.Where(p => p.Status != EnumStatus.Close).ToList(); + + if (!openRequestDetails.Any()) + { + return jobs; + } + + var enableMultipleCreateInjectionJob = await SettingManager.IsTrueAsync(StoreSettings.InjectionRequest.EnableMultipleCreateInjectionJob).ConfigureAwait(false); + if (enableMultipleCreateInjectionJob) + { + injectionRequest.Partial(); + } + else + { + var sb = new StringBuilder(); + foreach (var openRequestDetail in openRequestDetails) + { + sb.AppendLine($"{openRequestDetail.ItemCode}请求数量 {openRequestDetail.Qty},可用库存数量 {openRequestDetail.IssuedQty}"); + } + throw new UserFriendlyException($"{sb} 可用库存数量不足, 无法生成发料任务"); + } + return jobs; + } + + private InjectionJobEditInput BuildInjectionJobCreateInput(InjectionRequest injectionRequest, LocationDTO fromLocation) + { + InjectionJobEditInput job; + job = ObjectMapper.Map(injectionRequest); + job.JobType = EnumJobType.IssueJob; + job.JobStatus = EnumJobStatus.Open; + job.WorkGroupCode = fromLocation.WorkGroupCode; + job.WarehouseCode = fromLocation.WarehouseCode; + job.ProdLine = fromLocation.LocationGroupCode; + job.Worker = injectionRequest.Worker; + job.InjectionRequestNumber = injectionRequest.Number; + return job; + } + + private async Task> BuildInjectionJobDetailInputsAsync(InjectionRequest injectionRequest, + InjectionRequestDetail injectionRequestDetail, TransactionTypeDTO transactionType, string toLocationGroupCode) + { + var jobDetails = new List(); + var input = new RecommendBalanceRequestInput() + { + ItemCode = injectionRequestDetail.ItemCode, + Qty = injectionRequestDetail.ToBeIssuedQty, + LocationTypes = transactionType.OutLocationTypes, + LocationAreas = new List { injectionRequestDetail.FromLocationArea }, + Statuses = transactionType.OutInventoryStatuses, + }; + //获取推荐库存 + var recommendList = await _balanceAppService.GetRecommendBalancesAsync(input).ConfigureAwait(false); + //没有推荐库存时 跳过此明细 不生成任务 + if (recommendList.Count != 0) + { + foreach (var recommend in recommendList) + { + //拿走需求量 + var detail = await BuildInjectionJobDetailAsync(injectionRequestDetail, recommend, toLocationGroupCode).ConfigureAwait(false); + if (injectionRequest.UseOnTheWayLocation) + { + //获取在途库 + var locationDto = await _locationAppService.GetFirstByTypeAsync(EnumLocationType.TRANSPORT).ConfigureAwait(false); + + detail.OnTheWayLocationCode = locationDto.Code; + } + + jobDetails.Add(detail); + injectionRequestDetail.IssuedQty += recommend.Qty; + await _injectionRequestManager.UpdateDetailsAsync(injectionRequest).ConfigureAwait(false); + + } + } + return jobDetails; + } + + private async Task BuildInjectionJobDetailAsync(InjectionRequestDetail injectionRequestDetail, BalanceDTO balance, string toLocationGroupCode) + { + ProductionLineDTO prodLine = await _productionLineAppService.GetByLocationGroupCodeAsync(toLocationGroupCode).ConfigureAwait(false); + + var detail = ObjectMapper.Map(balance); + detail.RequestLocationCode = injectionRequestDetail.ToLocationCode; + detail.WorkStation = injectionRequestDetail.WorkStation; + detail.ExpiredTime = injectionRequestDetail.ExpiredTime; + detail.PositionCode = injectionRequestDetail.PositionCode; + detail.RecommendType = injectionRequestDetail.RecommendType; + + detail.RecommendPackingCode = balance.PackingCode; + detail.RecommendContainerCode = balance.ContainerCode; + detail.RecommendSupplierBatch = balance.SupplierBatch; + detail.RecommendProduceDate = balance.ProduceDate; + detail.RecommendExpireDate = balance.ExpireDate; + detail.RecommendLot = balance.Lot; + detail.RecommendProduceDate = balance.ProduceDate; + detail.RecommendArriveDate = balance.ArriveDate; + detail.RecommendFromLocationArea = balance.LocationArea; + detail.RecommendFromLocationCode = balance.LocationCode; + detail.RecommendFromLocationErpCode = balance.LocationErpCode; + detail.RecommendFromLocationGroup = balance.LocationGroup; + detail.RecommendFromWarehouseCode = balance.WarehouseCode; + detail.RecommendQty = balance.Qty; + detail.Uom = balance.Uom; + + detail.ToLocationCode = injectionRequestDetail.ToLocationCode; + detail.ToLocationErpCode = injectionRequestDetail.ToLocationErpCode; + detail.ToLocationArea = injectionRequestDetail.ToLocationArea; + detail.ToWarehouseCode = injectionRequestDetail.ToWarehouseCode; + detail.ToLocationGroup = injectionRequestDetail.ToLocationGroup; + + detail.ProdLine = prodLine == null ? toLocationGroupCode : prodLine.Code; + await Task.CompletedTask.ConfigureAwait(false); + return detail; + + } + + #endregion +} diff --git a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/StoreEventAutoMapperProfile.cs b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/StoreEventAutoMapperProfile.cs index 40de8aa9b..bbbd1fbd7 100644 --- a/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/StoreEventAutoMapperProfile.cs +++ b/be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/StoreEventAutoMapperProfile.cs @@ -35,6 +35,7 @@ public partial class StoreEventAutoMapperProfile : Profile DeliverJobAutoMapperProfile(); InspectJobAutoMapperProfile(); IssueJobAutoMapperProfile(); + InjectionJobAutoMapperProfile(); JisDeliverJobAutoMapperProfile(); ProductionReturnJobAutoMapperProfile(); ProductReceiveJobAutoMapperProfile(); @@ -69,6 +70,7 @@ public partial class StoreEventAutoMapperProfile : Profile JisDeliverNoteAutoMapperProfile(); JisProductReceiptNoteAutoMapperProfile(); MaterialRequestAutoMapperProfile(); + InjectionRequestAutoMapperProfile(); ProductionReturnRequestAutoMapperProfile(); ProductionReturnNoteAutoMapperProfile(); ProductReceiptNoteAutoMapperProfile();