|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using NetTopologySuite.Geometries;
|
|
|
|
using Omu.ValueInjecter;
|
|
|
|
using Volo.Abp;
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
|
using Volo.Abp.Domain.Services;
|
|
|
|
using Win_in.Sfs.Basedata.Boms;
|
|
|
|
|
|
|
|
using Win_in.Sfs.Basedata.Domain.Caches;
|
|
|
|
using Win_in.Sfs.Basedata.Domain.Shared;
|
|
|
|
using Win_in.Sfs.Shared.Domain;
|
|
|
|
using Win_in.Sfs.Shared.Domain.Shared;
|
|
|
|
|
|
|
|
namespace Win_in.Sfs.Basedata.Domain;
|
|
|
|
|
|
|
|
public class BomManager : DomainService, IBomManager
|
|
|
|
{
|
|
|
|
private readonly IBomRepository _repository;
|
|
|
|
private readonly IItemBasicRepository _itemBasicRepository;
|
|
|
|
|
|
|
|
|
|
|
|
public BomManager(IBomRepository repository, IItemBasicRepository itemBasicRepository)
|
|
|
|
{
|
|
|
|
_repository = repository;
|
|
|
|
_itemBasicRepository = itemBasicRepository;
|
|
|
|
InitBomComponent();
|
|
|
|
}
|
|
|
|
private void InitBomComponent()
|
|
|
|
{
|
|
|
|
int count = _repository.CountAsync().Result;
|
|
|
|
if (Cache.Boms.Count!=count)
|
|
|
|
{
|
|
|
|
Cache.Boms=_repository.ToListAsync().Result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 获取对应的Bom信息
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="productItemCode"></param>
|
|
|
|
/// <param name="validTime"></param>
|
|
|
|
/// <param name="isRecursive"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public virtual async Task<List<Bom>> GetRecursiveListAsync(string productItemCode, DateTime validTime, bool isRecursive = true)
|
|
|
|
{
|
|
|
|
List<Bom> totalBoms = new List<Bom>();
|
|
|
|
|
|
|
|
var boms = await _repository.GetListAsync(p => p.Product == productItemCode
|
|
|
|
&& (p.BeginTime <= validTime)
|
|
|
|
&& (p.EndTime >= validTime)).ConfigureAwait(false);
|
|
|
|
|
|
|
|
if (isRecursive)
|
|
|
|
{
|
|
|
|
foreach (var bom in boms)
|
|
|
|
{
|
|
|
|
if (bom.Component == bom.Product)
|
|
|
|
{
|
|
|
|
totalBoms.AddRange(boms);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//获取下级Bom,如果有就继续,没有就完事
|
|
|
|
await GetListWithValidTimeAsync(totalBoms, bom, validTime, 1).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
foreach (var bom in boms)
|
|
|
|
{
|
|
|
|
|
|
|
|
var item = await _itemBasicRepository.FindAsync(p => p.Code == bom.Component).ConfigureAwait(false);
|
|
|
|
if (item is { IsPhantom: true })
|
|
|
|
{
|
|
|
|
await GetRecursiveListAsync(bom.Component, validTime, isRecursive).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
totalBoms.Add(bom);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return totalBoms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 递归获取bom
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="totalBoms"></param>
|
|
|
|
/// <param name="bom"></param>
|
|
|
|
/// <param name="validTime"></param>
|
|
|
|
/// <param name="multipleQty"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
private async Task GetListWithValidTimeAsync(List<Bom> totalBoms, Bom bom, DateTime validTime, decimal multipleQty)
|
|
|
|
{
|
|
|
|
|
|
|
|
var subboms = await _repository.GetListAsync(p => p.Product == bom.Product
|
|
|
|
&& p.BeginTime <= validTime
|
|
|
|
&& p.EndTime >= validTime).ConfigureAwait(false);
|
|
|
|
|
|
|
|
if (subboms == null || subboms.Count == 0)
|
|
|
|
{
|
|
|
|
//根据bom多层放大倍数,第一次传过来的是1
|
|
|
|
bom.ComponentQty *= multipleQty;
|
|
|
|
totalBoms.Add(bom);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
foreach (var subbom in subboms)
|
|
|
|
{
|
|
|
|
//获取下级Bom,如果有就继续,没有就完事
|
|
|
|
await GetListWithValidTimeAsync(totalBoms, subbom, validTime, subbom.ComponentQty * multipleQty).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 执行导入
|
|
|
|
/// </summary>
|
|
|
|
public virtual async Task ImportDataAsync(List<Bom> mergeEntities, List<Bom> deleteEntities = null)
|
|
|
|
{
|
|
|
|
if (deleteEntities != null && deleteEntities.Count > 0)
|
|
|
|
{
|
|
|
|
await _repository.BulkDeleteAsync(deleteEntities).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
Cache.Boms.Clear();
|
|
|
|
await _repository.BulkMergeAsync(mergeEntities).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 所有子物料号
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="product"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public virtual async Task<List<Bom>> GetListOfProductAsync(string product)
|
|
|
|
{
|
|
|
|
return await _repository.GetListAsync(p => p.Product == product).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual async Task<List<Bom>> GetListAsync()
|
|
|
|
{
|
|
|
|
return await _repository.GetListAsync().ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public virtual async Task<List<Bom>> GetListWithPhantomItemAsync(string productItemCode, string mfgOp, DateTime validTime, bool onlyFromProductionPlan)
|
|
|
|
{
|
|
|
|
var where = BuildExpression(productItemCode, mfgOp, validTime, onlyFromProductionPlan);
|
|
|
|
|
|
|
|
var entities = await (await _repository.GetDbSetAsync().ConfigureAwait(false)).AsNoTracking().Where(where).ToListAsync().ConfigureAwait(false);
|
|
|
|
var phantomItemBoms = await GetPhantomItemBomsAsync(entities, mfgOp, validTime, onlyFromProductionPlan).ConfigureAwait(false);
|
|
|
|
|
|
|
|
return phantomItemBoms;
|
|
|
|
}
|
|
|
|
private static Expression<Func<Bom, bool>> BuildExpression(string productItemCode, string mfgOp, DateTime validTime, bool onlyFromProductionPlan)
|
|
|
|
{
|
|
|
|
Expression<Func<Bom, bool>> where = p => p.Product == productItemCode
|
|
|
|
// && p.MFGOp == mfgOp
|
|
|
|
&& p.ComponentQty > 0
|
|
|
|
&& (p.BeginTime <= validTime)
|
|
|
|
&& (p.EndTime >= validTime);
|
|
|
|
|
|
|
|
if (onlyFromProductionPlan)
|
|
|
|
{
|
|
|
|
where = where.And(p => p.DistributionType == EnumDistributionType.FromProductionPlan);
|
|
|
|
}
|
|
|
|
|
|
|
|
return where;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task<List<Bom>> GetPhantomItemBomsAsync(List<Bom> productBoms, string mfgOp, DateTime validTime, bool onlyFromProductionPlan)
|
|
|
|
{
|
|
|
|
var result = new List<Bom>();
|
|
|
|
|
|
|
|
foreach (var bom in productBoms)
|
|
|
|
{
|
|
|
|
var item = await _itemBasicRepository.FirstOrDefaultAsync(p => p.Code == bom.Component).ConfigureAwait(false);
|
|
|
|
if (item == null)
|
|
|
|
{
|
|
|
|
throw new UserFriendlyException($"代码为 {bom.Component} 的物品信息不存在.");
|
|
|
|
}
|
|
|
|
if (item.IsPhantom)
|
|
|
|
{
|
|
|
|
var where = BuildExpression(item.Code, mfgOp, validTime, onlyFromProductionPlan);
|
|
|
|
var query = (await _repository.GetDbSetAsync().ConfigureAwait(false)).AsNoTracking().Where(where);
|
|
|
|
Console.WriteLine(query.ToString());
|
|
|
|
var phantomItemBoms = await query.ToListAsync().ConfigureAwait(false);
|
|
|
|
//递归获取下层BOM的子零件为虚零件的BOM
|
|
|
|
var nextLevelBoms = await GetPhantomItemBomsAsync(phantomItemBoms, mfgOp, validTime, onlyFromProductionPlan).ConfigureAwait(false);
|
|
|
|
result.AddRange(nextLevelBoms);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.Add(bom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 所有父物料号
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="component"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public virtual async Task<List<Bom>> GetListOfComponentAsync(string component)
|
|
|
|
{
|
|
|
|
return await _repository.GetListAsync(p => p.Component == component).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 根据总成号、总成数量取所有子物料及其汇总数量
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="productCode">总成号</param>
|
|
|
|
/// <param name="productNum">总成数量</param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public virtual async Task<List<Bom>> GetMaterialTotalQtyAsync(string productCode, int productNum)
|
|
|
|
{
|
|
|
|
if (productNum < 1)
|
|
|
|
{
|
|
|
|
throw new UserFriendlyException("productNum参数值必须大于等于1");
|
|
|
|
}
|
|
|
|
var lst = await _repository.GetListAsync(p => p.Product == productCode).ConfigureAwait(false);
|
|
|
|
foreach (var item in lst)
|
|
|
|
{
|
|
|
|
item.ComponentQty = item.ComponentQty * productNum;
|
|
|
|
}
|
|
|
|
return lst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<BomComponent>> GetSubcomponentsRecursiveList(List<BomComponent> p_lst, EnumBomSelectedType p_type
|
|
|
|
)
|
|
|
|
{
|
|
|
|
List<BomComponent> list = new List<BomComponent>();
|
|
|
|
foreach (var item in p_lst)
|
|
|
|
{
|
|
|
|
var sublist= await GetSubcomponentsRecursive(item, p_type).ConfigureAwait(false);
|
|
|
|
list.AddRange(sublist);
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Bom操作
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="p_component"></param>
|
|
|
|
/// <param name="p_type"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public async Task<List<BomComponent>> GetSubcomponentsRecursive(BomComponent p_component, EnumBomSelectedType p_type
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// 创建用于存储最终结果、维度结果和树状结果的列表
|
|
|
|
List<BomComponent> lastList = new List<BomComponent>();
|
|
|
|
List<BomComponent> dimensionList = new List<BomComponent>();
|
|
|
|
|
|
|
|
// 调用异步方法GetSubcomponentsRecursive以获取树状结果,并使用lambda表达式将结果添加到lastList和dimensionList
|
|
|
|
var treeList = await GetSubcomponentsRecursive(p_component, 1, p_component.ComponentQty, p_component.Component,
|
|
|
|
(rs) => lastList.Add(rs)
|
|
|
|
,
|
|
|
|
(rs1) => {
|
|
|
|
dimensionList.Add(rs1);
|
|
|
|
}).ConfigureAwait(false);
|
|
|
|
|
|
|
|
// 根据用户指定的类型,返回相应的结果列表
|
|
|
|
List<BomComponent> returnList = new List<BomComponent>();
|
|
|
|
switch (p_type)
|
|
|
|
{
|
|
|
|
case EnumBomSelectedType.Last:
|
|
|
|
returnList = lastList;
|
|
|
|
break;
|
|
|
|
case EnumBomSelectedType.Tree:
|
|
|
|
returnList = treeList;
|
|
|
|
break;
|
|
|
|
case EnumBomSelectedType.Dimension:
|
|
|
|
returnList = dimensionList;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return returnList;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 层级、拆解、一维结构、树状结构
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="p_component">上级组件(初始时为根元素)</param>
|
|
|
|
/// <param name="level">层级一般为1</param>
|
|
|
|
/// <param name="sumQty">累计数量</param>
|
|
|
|
/// <param name="root">根</param>
|
|
|
|
/// <param name="p_actionLast">拆解到最终零件时</param>
|
|
|
|
/// <param name="p_actionDimension">树型转成一维表用</param>
|
|
|
|
/// <param name="bomList"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
|
|
private async Task<List<BomComponent>> GetSubcomponentsRecursive(BomComponent p_component, int level, decimal sumQty, string root, Action<BomComponent> p_actionLast,
|
|
|
|
Action<BomComponent> p_actionDimension
|
|
|
|
/* ,List<Bom> bomList =null*/
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// 初始化子零件列表
|
|
|
|
List<BomComponent> subComponents = new List<BomComponent>();
|
|
|
|
// 假设 GetComponentsByProduct 方法可获取某个物料号下的所有子零件
|
|
|
|
List<BomComponent> directSubComponents = await GetComponentsByProduct(p_component.Component).ConfigureAwait(false);
|
|
|
|
|
|
|
|
// 如果没有直接子零件且不是根元素,则执行最后操作回调函数
|
|
|
|
if (!directSubComponents.Any() && level != 1)
|
|
|
|
{
|
|
|
|
p_actionLast(p_component);
|
|
|
|
}
|
|
|
|
// 遍历直接子零件列表
|
|
|
|
foreach (var component in directSubComponents)
|
|
|
|
{
|
|
|
|
// 设置根元素和数量
|
|
|
|
component.Root = root;
|
|
|
|
component.SumQty = sumQty * component.ComponentQty;
|
|
|
|
component.ParentComponent = p_component.Component;
|
|
|
|
component.Level = level;
|
|
|
|
// 设置拆解路径
|
|
|
|
component.Path = (p_component.Component != root) ? p_component.Path + $"->{component.Component}" : p_component.Component + $"->{component.Component}";
|
|
|
|
// 递归获取子零件列表,并执行维度操作回调函数
|
|
|
|
component.SubComponents = await GetSubcomponentsRecursive(component, level + 1, sumQty * component.ComponentQty, component.Root, p_actionLast, p_actionDimension).ConfigureAwait(false);
|
|
|
|
p_actionDimension(component);
|
|
|
|
// 添加子零件到子零件列表
|
|
|
|
subComponents.Add(component);
|
|
|
|
}
|
|
|
|
// 返回子零件列表
|
|
|
|
return subComponents;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 读取子节点
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="product"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
private async Task<List<BomComponent>> GetComponentsByProduct(string product)
|
|
|
|
{
|
|
|
|
List<Bom> list = new List<Bom>();
|
|
|
|
if (Cache.Boms.Count == 0)
|
|
|
|
{
|
|
|
|
InitBomComponent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 如果缓存中有Bom列表,则从缓存中筛选符合条件的数据;否则从仓储中异步获取数据
|
|
|
|
list = Cache.Boms.Count > 0 ? Cache.Boms.Where(p => p.Product == product).ToList() :
|
|
|
|
await _repository.GetListAsync(p => p.Product == product).ConfigureAwait(false);
|
|
|
|
List<BomComponent> components = new List<BomComponent>();
|
|
|
|
foreach (var component in list)
|
|
|
|
{
|
|
|
|
BomComponent bomComponent = new BomComponent();
|
|
|
|
bomComponent.InjectFrom(component);
|
|
|
|
components.Add(bomComponent);
|
|
|
|
}
|
|
|
|
// 其他物料号的子零件信息类似添加
|
|
|
|
return components;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 获取所有bomtree
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="productCode"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public virtual async Task<List<Bom>> GetAllItemByCode(string productCode,Guid id)
|
|
|
|
{
|
|
|
|
List<Bom> boms = new List<Bom>();
|
|
|
|
var lst = await _repository.GetListAsync(p => p.Product == productCode).ConfigureAwait(false);
|
|
|
|
foreach (var bom in lst)
|
|
|
|
{
|
|
|
|
bom.Remark = id.ToString();
|
|
|
|
boms.Add(bom);
|
|
|
|
var results= await GetAllItemByCode(bom.Component,bom.Id).ConfigureAwait(false);
|
|
|
|
boms.AddRange(results);
|
|
|
|
}
|
|
|
|
return boms;
|
|
|
|
}
|
|
|
|
}
|