Browse Source

修改 注塑发料

dev_DY_CC
郑勃旭 1 year ago
parent
commit
ad8511a277
  1. 3
      be/Modules/BaseData/src/Win_in.Sfs.Basedata.Application.Contracts/ProductLineItems/IProductionLineItemAppService.cs
  2. 10
      be/Modules/BaseData/src/Win_in.Sfs.Basedata.Application/ProductionLineItems/ProductionLineItemAppService.cs
  3. 8
      be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Application.Contracts/Balances/IBalanceAppService.cs
  4. 252
      be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Application/Balances/BalanceAppService.cs
  5. 107
      be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Domain/Balances/BalanceManager.cs
  6. 25
      be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Domain/Balances/IBalanceManager.cs
  7. 4
      be/Modules/Shared/src/Win_in.Sfs.Shared.Domain.Shared/Enums/Store/EnumIssueType.cs
  8. 1
      be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/IssueJobs/InjectionJobs/Inputs/InjectionJobEditInput.cs
  9. 168
      be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Requests/InjectionRequestEventHandler.cs
  10. 2
      be/WZC2.sln.DotSettings

3
be/Modules/BaseData/src/Win_in.Sfs.Basedata.Application.Contracts/ProductLineItems/IProductionLineItemAppService.cs

@ -13,4 +13,7 @@ public interface IProductionLineItemAppService
{
Task<List<ProductionLineItemDTO>> GetByProductLineCodeAsync(string productLineCode);
Task<ProductionLineItemDTO> GetByProductLineCodeAndItemCodeAsync(string productLineCode,
string itemCode);
}

10
be/Modules/BaseData/src/Win_in.Sfs.Basedata.Application/ProductionLineItems/ProductionLineItemAppService.cs

@ -17,6 +17,7 @@ public class ProductionLineItemAppService :
ProductionLineItemEditInput, ProductionLineItemImportInput>, IProductionLineItemAppService
{
private new readonly IProductionLineItemRepository _repository;
public ProductionLineItemAppService(
IProductionLineItemRepository repository
, IDistributedCache<ProductionLineItemDTO> cache
@ -53,4 +54,13 @@ public class ProductionLineItemAppService :
var entityList = await _repository.GetListAsync(p => p.ProdLineCode == productLineCode).ConfigureAwait(false);
return ObjectMapper.Map<List<ProductionLineItem>, List<ProductionLineItemDTO>>(entityList);
}
[HttpPost("get-by-product-item")]
public virtual async Task<ProductionLineItemDTO> GetByProductLineCodeAndItemCodeAsync(string productLineCode,
string itemCode)
{
var entityList = await _repository
.FindAsync(p => p.ProdLineCode == productLineCode && p.ItemCode == itemCode).ConfigureAwait(false);
return ObjectMapper.Map<ProductionLineItem, ProductionLineItemDTO>(entityList);
}
}

8
be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Application.Contracts/Balances/IBalanceAppService.cs

@ -166,4 +166,12 @@ public interface IBalanceAppService
Task<List<BalanceDTO>> GetListByErpLocationCodeAndItemCodeAsync(string erplocationCode, string itemCode);
Task<List<BalanceDTO>> GetRecommendBalancesByLocationsAsync(RecommendBalanceRequestInput input);
Task<PagedResultDto<BalanceDTO>> GetBalancePagedListByFilterAsync(SfsInventoryRequestInputBase sfsRequestInput, bool includeDetails = false, CancellationToken cancellationToken = default);
/// <summary>
/// 获取可用库存列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
Task<List<BalanceDTO>> GetUsableListAsync(RecommendBalanceRequestInput input);
}

252
be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Application/Balances/BalanceAppService.cs

@ -1,16 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Domain.Repositories;
using Win_in.Sfs.Basedata.Application.Contracts;
using Win_in.Sfs.Shared.Application.Contracts;
@ -21,7 +19,6 @@ using Win_in.Sfs.Wms.Inventory.Domain;
using Win_in.Sfs.Wms.Inventory.Domain.Acl.ItemBasic;
using Win_in.Sfs.Wms.Inventory.Domain.Acl.Location;
using Win_in.Sfs.Wms.Inventory.Domain.Shared;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Win_in.Sfs.Wms.Inventory.Application;
@ -63,6 +60,48 @@ public class BalanceAppService
#region Update
/// <summary>
/// 库存余额更新物品基础信息
/// </summary>
[HttpPost("update/item-basic-info")]
public async Task UpdateItemBasicInfoAsync(BalanceUpdateItemBasicInfoDto balanceUpdateItemBasicInfoDto)
{
//停用,可能用其他处理方案
return;
// 物品编码
var itemCodes = balanceUpdateItemBasicInfoDto.BalanceUpdateItemBasicInfos?.Select(c => c.ItemCode);
itemCodes = itemCodes?.Where(t => string.IsNullOrWhiteSpace(t) == false);
if (itemCodes == null || itemCodes.Any() == false)
{
return;
}
// 获取库存余额
var entitys = await _repository.GetListAsync(p =>
itemCodes.Contains(p.ItemCode)).ConfigureAwait(false);
if (entitys.Count <= 0)
{
return;
}
// 库存余额更新物品基础信息
var entitysGroup = entitys.GroupBy(t => t.ItemCode);
foreach (var entity in entitysGroup)
{
var balanceUpdateItemBasicInfo =
balanceUpdateItemBasicInfoDto.BalanceUpdateItemBasicInfos.FirstOrDefault(t => t.ItemCode == entity.Key);
foreach (var item in entity)
{
item.ItemName = balanceUpdateItemBasicInfo.ItemName ?? item.ItemName;
item.ItemDesc1 = balanceUpdateItemBasicInfo.ItemDesc1 ?? item.ItemDesc1;
item.ItemDesc2 = balanceUpdateItemBasicInfo.ItemDesc2 ?? item.ItemDesc2;
}
}
await _repository.UpdateManyAsync(entitys).ConfigureAwait(false);
}
/// <summary>
/// 修改批次
/// </summary>
@ -74,14 +113,17 @@ public class BalanceAppService
/// <param name="expireDate"></param>
/// <returns></returns>
[HttpPost("update/batch")]
public virtual async Task UpdateBatchAsync(Guid id, string lot, string supplierBatch, DateTime arriveDate, DateTime produceDate, DateTime expireDate)
public virtual async Task UpdateBatchAsync(Guid id, string lot, string supplierBatch, DateTime arriveDate,
DateTime produceDate, DateTime expireDate)
{
var entity = await _repository.FindAsync(id).ConfigureAwait(false);
Check.NotNull(entity, EntityClassName);
var worker = CurrentUser.GetUserName();
var jobNumber = "";
var docNumber = "";
await _transferLogManager.ChangeBatchAsync(entity, lot, supplierBatch, arriveDate, produceDate, expireDate, worker, jobNumber, docNumber).ConfigureAwait(false);
await _transferLogManager
.ChangeBatchAsync(entity, lot, supplierBatch, arriveDate, produceDate, expireDate, worker, jobNumber,
docNumber).ConfigureAwait(false);
await _repository.UpdateAsync(entity).ConfigureAwait(false);
}
@ -109,7 +151,8 @@ public class BalanceAppService
/// <param name="containerCode"></param>
/// <returns></returns>
[HttpPost("update/location")]
public virtual async Task UpdateLocationAsync(Guid id, string warehouseCode, string locationCode, string containerCode)
public virtual async Task UpdateLocationAsync(Guid id, string warehouseCode, string locationCode,
string containerCode)
{
var entity = await _repository.FindAsync(id).ConfigureAwait(false);
Check.NotNull(entity, EntityClassName);
@ -258,6 +301,7 @@ public class BalanceAppService
#endregion Update
#region Get
/// <summary>
/// 获取库存余额带标准价格得
/// </summary>
@ -266,7 +310,8 @@ public class BalanceAppService
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("get-list")]
public virtual async Task<PagedResultDto<BalanceDTO>> GetBalancePagedListByFilterAsync(SfsInventoryRequestInputBase sfsRequestInput, bool includeDetails = false,
public virtual async Task<PagedResultDto<BalanceDTO>> GetBalancePagedListByFilterAsync(
SfsInventoryRequestInputBase sfsRequestInput, bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var result = await base.GetPagedListByFilterAsync(sfsRequestInput, includeDetails, cancellationToken);
@ -275,10 +320,11 @@ public class BalanceAppService
var std = await _stdCostPriceSheetAppService.GetByItemCode(item.ItemCode).ConfigureAwait(false);
item.StdCostPrice = std == null ? 0 : std.StdCostPrice;
}
return result;
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
@ -304,11 +350,11 @@ public class BalanceAppService
dto.ItemDesc2 = item.Desc2;
dto.Uom = item.BasicUom;
}
return dtos;
}
/// <summary>
///
/// </summary>
/// <param name="packingCode"></param>
/// <param name="itemCode"></param>
@ -317,14 +363,17 @@ public class BalanceAppService
/// <exception cref="NotImplementedException"></exception>
[HttpGet("item-status")]
[AllowAnonymous]
public virtual async Task<BalanceDTO> GetByItemLocationAndPackingAsync(string packingCode, string itemCode, string locationCode)
public virtual async Task<BalanceDTO> GetByItemLocationAndPackingAsync(string packingCode, string itemCode,
string locationCode)
{
var entity = await _repository.FirstOrDefaultAsync(c =>
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode).ConfigureAwait(false);
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode)
.ConfigureAwait(false);
if (entity == null)
{
throw new UserFriendlyException($"未找到箱码为 {packingCode},物料代码为 {itemCode},库位代码为 {locationCode} 的库存");
}
var dto = ObjectMapper.Map<Balance, BalanceDTO>(entity);
return dto;
}
@ -339,14 +388,17 @@ public class BalanceAppService
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
[HttpGet("item-loc-packing-status")]
public virtual async Task<BalanceDTO> GetByItemLocationPackingAndStatusAsync(string packingCode, string itemCode, string locationCode, EnumInventoryStatus status)
public virtual async Task<BalanceDTO> GetByItemLocationPackingAndStatusAsync(string packingCode, string itemCode,
string locationCode, EnumInventoryStatus status)
{
var entity = await _repository.FirstOrDefaultAsync(c =>
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode && c.Status == status).ConfigureAwait(false);
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode &&
c.Status == status).ConfigureAwait(false);
if (entity == null)
{
throw new UserFriendlyException($"未找到箱码为 {packingCode},物料代码为 {itemCode},库位代码为 {locationCode} 的库存");
}
var dto = ObjectMapper.Map<Balance, BalanceDTO>(entity);
return dto;
}
@ -407,8 +459,10 @@ public class BalanceAppService
&& p.Status == status).ConfigureAwait(false);
if (entity == null)
{
throw new UserFriendlyException($"未找到箱码为 {packingCode},物料代码为 {itemCode},库位代码为 {locationCode},状态为为 {status} 的库存");
throw new UserFriendlyException(
$"未找到箱码为 {packingCode},物料代码为 {itemCode},库位代码为 {locationCode},状态为为 {status} 的库存");
}
var dto = ObjectMapper.Map<Balance, BalanceDTO>(entity);
return dto;
}
@ -442,9 +496,11 @@ public class BalanceAppService
/// <param name="itemCode"></param>
/// <returns></returns>
[HttpGet("list/by-location-and-item")]
public virtual async Task<List<BalanceDTO>> GetListByLocationCodeAndItemCodeAsync(string locationCode, string itemCode)
public virtual async Task<List<BalanceDTO>> GetListByLocationCodeAndItemCodeAsync(string locationCode,
string itemCode)
{
var entitys = await _repository.GetListAsync(p => p.LocationCode == locationCode && p.ItemCode == itemCode).ConfigureAwait(false);
var entitys = await _repository.GetListAsync(p => p.LocationCode == locationCode && p.ItemCode == itemCode)
.ConfigureAwait(false);
var dtos = ObjectMapper.Map<List<Balance>, List<BalanceDTO>>(entitys);
return dtos;
}
@ -456,15 +512,17 @@ public class BalanceAppService
/// <param name="itemCode"></param>
/// <returns></returns>
[HttpGet("list/by-erplocation-and-item")]
public virtual async Task<List<BalanceDTO>> GetListByErpLocationCodeAndItemCodeAsync(string erplocationCode, string itemCode)
public virtual async Task<List<BalanceDTO>> GetListByErpLocationCodeAndItemCodeAsync(string erplocationCode,
string itemCode)
{
var entitys = await _repository.GetListAsync(p => p.LocationErpCode == erplocationCode && p.ItemCode == itemCode).ConfigureAwait(false);
var entitys = await _repository
.GetListAsync(p => p.LocationErpCode == erplocationCode && p.ItemCode == itemCode).ConfigureAwait(false);
var dtos = ObjectMapper.Map<List<Balance>, List<BalanceDTO>>(entitys);
return dtos;
}
/// <summary>
/// 根据发料任务需求,算出推荐的库存 (在获取时 已经添加预占用)
/// 根据发料任务需求,算出推荐的库存 (在获取时 已经去除了预占用)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
@ -480,7 +538,9 @@ public class BalanceAppService
var statuses = input.Statuses;
Logger.LogDebug(traceId + "|Input:" + input);
var balances = await _balanceManager.GetRecommendBalancesAsync(traceId, itemCode, qty, locationTypes, locationAreas, statuses).ConfigureAwait(false);
var balances = await _balanceManager
.GetRecommendBalancesAsync(traceId, itemCode, qty, locationTypes, locationAreas, statuses)
.ConfigureAwait(false);
var dtos = ObjectMapper.Map<List<Balance>, List<BalanceDTO>>(balances);
@ -488,7 +548,7 @@ public class BalanceAppService
}
/// <summary>
/// 根据发料任务需求的库位,算出推荐的库存 (在获取时 已经添加预占用)
/// 根据发料任务需求的库位,算出推荐的库存 (在获取时 已经去除了预占用)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
@ -510,23 +570,45 @@ public class BalanceAppService
{
input.LocationAreas = new List<string>();
}
input.LocationAreas.Add(locationDto.AreaCode);
if (input.LocationTypes == null)
{
input.LocationTypes = new List<EnumLocationType>();
}
input.LocationTypes.Add(locationDto.Type);
}
}
Logger.LogDebug(traceId + "|Input:" + input);
var balances = await _balanceManager.GetRecommendBalancesByLocationAsync(traceId, itemCode, qty, locations, statuses, input.IsPackingCode).ConfigureAwait(false);
var balances = await _balanceManager
.GetRecommendBalancesByLocationAsync(traceId, itemCode, qty, locations, statuses, input.IsPackingCode)
.ConfigureAwait(false);
var dtos = ObjectMapper.Map<List<Balance>, List<BalanceDTO>>(balances);
return dtos;
}
/// <summary>
/// 获取可用库存列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
[HttpGet("usable-list")]
public virtual async Task<List<BalanceDTO>> GetUsableListAsync(RecommendBalanceRequestInput input)
{
var inventoryBalances = await _balanceManager
.GetUsableListAsync(input.ItemCode, input.Locations, input.Statuses, true).ConfigureAwait(false);
var dtos = ObjectMapper.Map<List<Balance>, List<BalanceDTO>>(inventoryBalances);
return dtos;
}
/// <summary>
/// pda发料执行查询使用
/// </summary>
@ -539,7 +621,8 @@ public class BalanceAppService
//可以领料
if (input.IsEnablePick != null)
{
var locations = await _locationAclService.GetListByEnablePickAsync((bool)input.IsEnablePick).ConfigureAwait(false);
var locations = await _locationAclService.GetListByEnablePickAsync((bool)input.IsEnablePick)
.ConfigureAwait(false);
var codes = JsonSerializer.Serialize(locations.Select(c => c.Code));
input.Condition.Filters.Add(new Filter("LocationCode", codes, "In"));
}
@ -552,7 +635,7 @@ public class BalanceAppService
input.Condition.Filters.Add(new Filter("LocationCode", codes, "In"));
}
var dtos = await GetPagedListByFilterAsync(input, false).ConfigureAwait(false);
var dtos = await GetPagedListByFilterAsync(input).ConfigureAwait(false);
return dtos;
}
@ -562,38 +645,41 @@ public class BalanceAppService
/// <param name="balanceManyParameterRequestInput"></param>
/// <returns></returns>
[HttpPost("get-by-item-location-packing-container-lot-locationtype-status")]
public virtual async Task<List<BalanceDTO>> GetListByManyParameter(BalanceManyParameterRequestInput balanceManyParameterRequestInput)
public virtual async Task<List<BalanceDTO>> GetListByManyParameter(
BalanceManyParameterRequestInput balanceManyParameterRequestInput)
{
var input = new SfsInventoryRequestInputBase()
var input = new SfsInventoryRequestInputBase
{
MaxResultCount = 1000,
SkipCount = 0,
Sorting = string.Empty,
Condition = new Condition()
{
Filters = new List<Filter>()
}
Condition = new Condition { Filters = new List<Filter>() }
};
if (!string.IsNullOrWhiteSpace(balanceManyParameterRequestInput.ItemCode))
{
input.Condition.Filters.Add(new Filter("ItemCode", balanceManyParameterRequestInput.ItemCode));
}
if (!string.IsNullOrWhiteSpace(balanceManyParameterRequestInput.LocationCode))
{
input.Condition.Filters.Add(new Filter("LocationCode", balanceManyParameterRequestInput.LocationCode));
}
if (!string.IsNullOrWhiteSpace(balanceManyParameterRequestInput.PackingCode))
{
input.Condition.Filters.Add(new Filter("PackingCode", balanceManyParameterRequestInput.PackingCode));
}
if (!string.IsNullOrWhiteSpace(balanceManyParameterRequestInput.ContainerCode))
{
input.Condition.Filters.Add(new Filter("ContainerCode", balanceManyParameterRequestInput.ContainerCode));
}
if (!string.IsNullOrWhiteSpace(balanceManyParameterRequestInput.Lot))
{
input.Condition.Filters.Add(new Filter("Lot", balanceManyParameterRequestInput.Lot));
}
//筛选库存状态
if (balanceManyParameterRequestInput.InventoryStatuses.Count > 0)
{
@ -601,6 +687,7 @@ public class BalanceAppService
balanceManyParameterRequestInput.InventoryStatuses);
input.Condition.Filters.Add(new Filter("Status", Statuses, "In"));
}
//筛选库位类型
if (balanceManyParameterRequestInput.LocationTypes.Count > 0)
{
@ -622,51 +709,59 @@ public class BalanceAppService
/// <param name="listInput"></param>
/// <returns></returns>
[HttpPost("by-balances-request-many-parameter")]
public async Task<PagedResultDto<BalanceDTO>> GetListByLocationTypeAndInventoryStatusAsync(BalanceListByIssueInputByInventoryStatusAndLocationType listInput)
public async Task<PagedResultDto<BalanceDTO>> GetListByLocationTypeAndInventoryStatusAsync(
BalanceListByIssueInputByInventoryStatusAndLocationType listInput)
{
var input = new SfsInventoryRequestInputBase()
var input = new SfsInventoryRequestInputBase
{
MaxResultCount = listInput.pageSize,
SkipCount = (listInput.pageIndex - 1) * listInput.pageSize,
Sorting = listInput.sortBy,
Condition = new Condition()
{
Filters = new List<Filter>()
}
Condition = new Condition { Filters = new List<Filter>() }
};
if (!string.IsNullOrWhiteSpace(listInput.itemCode))
{
input.Condition.Filters.Add(new Filter("ItemCode", listInput.itemCode));
}
if (!string.IsNullOrWhiteSpace(listInput.locationCode))
{
input.Condition.Filters.Add(new Filter("LocationCode", listInput.locationCode));
}
if (!string.IsNullOrWhiteSpace(listInput.lot))
{
input.Condition.Filters.Add(new Filter("Lot", listInput.lot));
}
if (!string.IsNullOrWhiteSpace(listInput.packingCode))
{
input.Condition.Filters.Add(new Filter("PackingCode", listInput.packingCode));
}
if (!string.IsNullOrWhiteSpace(listInput.containerCode))
{
input.Condition.Filters.Add(new Filter("ContainerCode", listInput.containerCode));
}
if (listInput.locationTypes != null && listInput.locationTypes.Any())
{
var locationCodes = (await _locationAclService.GetListByTypesAsync(listInput.locationTypes).ConfigureAwait(false)).Select(t => t.Code).ToList();
var locationCodes =
(await _locationAclService.GetListByTypesAsync(listInput.locationTypes).ConfigureAwait(false))
.Select(t => t.Code).ToList();
if (locationCodes.Any())
{
input.Condition.Filters.Add(new Filter("LocationCode", JsonSerializer.Serialize(locationCodes), "In"));
}
}
if (listInput.inventoryStatuses != null && listInput.inventoryStatuses.Any())
{
input.Condition.Filters.Add(new Filter("Status", JsonSerializer.Serialize(listInput.inventoryStatuses), "In"));
input.Condition.Filters.Add(new Filter("Status", JsonSerializer.Serialize(listInput.inventoryStatuses),
"In"));
}
var balanceDTOs = await GetPagedListByFilterAsync(input, false).ConfigureAwait(false);
var balanceDTOs = await GetPagedListByFilterAsync(input).ConfigureAwait(false);
return balanceDTOs;
}
@ -678,19 +773,33 @@ public class BalanceAppService
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("by-hold-location-code-and-no-ok")]
public async Task<PagedResultDto<BalanceDTO>> GetByHoldLocationCodeAndNoOkAsync(SfsInventoryRequestInputBase sfsRequestDTO, bool includeDetails = false,
public async Task<PagedResultDto<BalanceDTO>> GetByHoldLocationCodeAndNoOkAsync(
SfsInventoryRequestInputBase sfsRequestDTO, bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var locationInfo = await _locationAclService.GetFirstByTypeAsync(EnumLocationType.HOLD).ConfigureAwait(false);
sfsRequestDTO.Condition.Filters.Add(new Sfs.Shared.Domain.Filter() { Action = "==", Column = "LocationCode", Logic = EnumFilterLogic.And.ToString(), Value = locationInfo.Code });
sfsRequestDTO.Condition.Filters.Add(new Sfs.Shared.Domain.Filter() { Action = "==", Column = "Status", Logic = EnumFilterLogic.And.ToString(), Value = ((int)EnumInventoryStatus.NOK).ToString(), });
sfsRequestDTO.Condition.Filters.Add(new Filter
{
Action = "==",
Column = "LocationCode",
Logic = EnumFilterLogic.And.ToString(),
Value = locationInfo.Code
});
sfsRequestDTO.Condition.Filters.Add(new Filter
{
Action = "==",
Column = "Status",
Logic = EnumFilterLogic.And.ToString(),
Value = ((int)EnumInventoryStatus.NOK).ToString()
});
Expression<Func<Balance, bool>> expression = sfsRequestDTO.Condition.Filters?.Count > 0
var expression = sfsRequestDTO.Condition.Filters?.Count > 0
? sfsRequestDTO.Condition.Filters.ToLambda<Balance>()
: p => true;
return await GetPagedListAsync(expression, sfsRequestDTO.SkipCount, sfsRequestDTO.MaxResultCount, sfsRequestDTO.Sorting, includeDetails, cancellationToken).ConfigureAwait(false);
return await GetPagedListAsync(expression, sfsRequestDTO.SkipCount, sfsRequestDTO.MaxResultCount,
sfsRequestDTO.Sorting, includeDetails, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@ -698,10 +807,12 @@ public class BalanceAppService
/// </summary>
/// <returns></returns>
[HttpPost("get-real-qty-by-packing-and-item-and-location-and-status")]
public async Task<BalanceDTO> GetRealQtyByPackingCodeAndItemCodeAndLocationCodeAndStatusAsync(string packingCode, string itemCode, string locationCode, EnumInventoryStatus status)
public async Task<BalanceDTO> GetRealQtyByPackingCodeAndItemCodeAndLocationCodeAndStatusAsync(string packingCode,
string itemCode, string locationCode, EnumInventoryStatus status)
{
var entity = await _repository.FirstOrDefaultAsync(c =>
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode && c.Status == status).ConfigureAwait(false);
c.PackingCode == packingCode && c.ItemCode == itemCode && c.LocationCode == locationCode &&
c.Status == status).ConfigureAwait(false);
if (entity == null)
{
throw new UserFriendlyException($"未找到箱码为 {packingCode},物料代码为 {itemCode},库位代码为 {locationCode} 的库存");
@ -714,7 +825,8 @@ public class BalanceAppService
}
[HttpGet("list-by-location-status")]
public async Task<List<BalanceDTO>> GetListByLocationCodeAndStatusAsync(string locationCode, EnumInventoryStatus status)
public async Task<List<BalanceDTO>> GetListByLocationCodeAndStatusAsync(string locationCode,
EnumInventoryStatus status)
{
var entities = await _repository.GetListAsync(
c => c.LocationCode == locationCode && c.Status == status
@ -728,6 +840,7 @@ public class BalanceAppService
#endregion Get
#region export
/// <summary>
/// 导出数据
/// </summary>
@ -760,47 +873,6 @@ public class BalanceAppService
return ExportImportService.Export(list, detailsProptyName: hasDetails ? nameof(EmptyDetail) : null);
}
#endregion
/// <summary>
/// 库存余额更新物品基础信息
/// </summary>
[HttpPost("update/item-basic-info")]
public async Task UpdateItemBasicInfoAsync(BalanceUpdateItemBasicInfoDto balanceUpdateItemBasicInfoDto)
{
//停用,可能用其他处理方案
return;
// 物品编码
var itemCodes = balanceUpdateItemBasicInfoDto.BalanceUpdateItemBasicInfos?.Select(c => c.ItemCode);
itemCodes = itemCodes?.Where(t => string.IsNullOrWhiteSpace(t) == false);
if (itemCodes == null || itemCodes.Any() == false)
{
return;
}
// 获取库存余额
var entitys = await _repository.GetListAsync(p =>
itemCodes.Contains(p.ItemCode)).ConfigureAwait(false);
if (entitys.Count <= 0)
{
return;
}
// 库存余额更新物品基础信息
var entitysGroup = entitys.GroupBy(t => t.ItemCode);
foreach (var entity in entitysGroup)
{
var balanceUpdateItemBasicInfo = balanceUpdateItemBasicInfoDto.BalanceUpdateItemBasicInfos.FirstOrDefault(t => t.ItemCode == entity.Key);
foreach (var item in entity)
{
item.ItemName = balanceUpdateItemBasicInfo.ItemName ?? item.ItemName;
item.ItemDesc1 = balanceUpdateItemBasicInfo.ItemDesc1 ?? item.ItemDesc1;
item.ItemDesc2 = balanceUpdateItemBasicInfo.ItemDesc2 ?? item.ItemDesc2;
}
}
await _repository.UpdateManyAsync(entitys).ConfigureAwait(false);
}
#endregion
}

107
be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Domain/Balances/BalanceManager.cs

@ -459,15 +459,13 @@ public class BalanceManager : DomainService, IBalanceManager
#region GetRecommendList
/// <summary>
/// 根据库位获取推荐库位
/// </summary>
/// <param name="traceId"></param>
/// <param name="itemCode"></param>
/// <param name="requestQty"></param>
/// <param name="validLocationTypes"></param>
/// <param name="validLocationAreas"></param>
/// <param name="validLocations"></param>
/// <param name="validStatuses"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
@ -770,6 +768,109 @@ public class BalanceManager : DomainService, IBalanceManager
#endregion
}
/// <summary>
/// 获取可用库存列表
/// </summary>
/// <param name="itemCode"></param>
/// <param name="validLocations"></param>
/// <param name="validStatuses"></param>
/// <param name="isRemovePackingCode"></param>
/// <returns></returns>
public virtual async Task<List<Balance>> GetUsableListAsync(string itemCode,
List<string> validLocations,
List<EnumInventoryStatus> validStatuses, bool isRemovePackingCode)
{
var recommendBalances = new List<Balance>();//返回可使用的推荐库存余额
var item = await _itemBasicAclService.GetByCodeAsync(itemCode).ConfigureAwait(false);
if (item == null) //物品是否存在
{
// throw new UserFriendlyException($"未找到代码为 {itemCode} 的物料记录");
return recommendBalances;
}
//构造查询条件
Expression<Func<Balance, bool>> expression = p => p.ItemCode == itemCode;
expression = expression.And(p => validStatuses.Contains(p.Status));
expression = expression.And(p => p.IsActive);
//如果物品的有效期不是无限的, 要过滤掉过期库存
if (item.ValidityUnit != EnumValidityUnit.Infinite)
{
expression = expression.And(p => p.ExpireDate > DateTime.Now);
}
//排除无箱码库存
if (isRemovePackingCode)
{
expression = expression.And(p => !string.IsNullOrEmpty(p.PackingCode));
}
var allBalances = await
(await _balanceRepository.GetDbSetAsync().ConfigureAwait(false))
.Where(expression)
.AsNoTracking()
.ToListAsync().ConfigureAwait(false);
var balanceLocationCodes = allBalances.Select(p => p.LocationCode).Distinct().ToList();
var locations = await _locationAclService.GetByCodesAsync(balanceLocationCodes).ConfigureAwait(false);
//筛选有效库位类型
if (validLocations.Any())
{
locations = locations.Where(p => validLocations.Contains(p.Code)).ToList();
}
var locationCodes = locations.Where(p => p.EnablePick).Select(p => p.Code).ToList();
if (!locationCodes.Any())
{
return recommendBalances;
}
//获取物品存储关系
var itemStoreRelationDict = await GetItemStoreRelationDictAsync(itemCode, locationCodes).ConfigureAwait(false);
//过滤掉无用的库位代码
locationCodes = itemStoreRelationDict.Keys.ToList();
locations = locations.Where(p => locationCodes.Contains(p.Code)).ToList();
var usableBalances = allBalances.Where(p => locationCodes.Contains(p.LocationCode)).ToList();
if (!usableBalances.Any())
{
return recommendBalances;
}
//读取该itemCode的预占用库存
var expectOuts = await
(await _expectOutRepository.GetDbSetAsync().ConfigureAwait(false))
.AsNoTracking()
.Where(p => p.ItemCode == itemCode
&& locationCodes.Contains(p.LocationCode)
&& validStatuses.Contains(p.Status))
.ToListAsync().ConfigureAwait(false);
var containerCodes = usableBalances
.Where(p => !string.IsNullOrEmpty(p.ContainerCode))
.Select(p => p.ContainerCode)
.ToList();
var expectOutContainerCodes = await
(await _expectOutRepository.GetDbSetAsync().ConfigureAwait(false))
.Where(p => containerCodes.Contains(p.ContainerCode))
.GroupBy(p => p.ContainerCode)
.Select(d => d.Key)
.ToListAsync().ConfigureAwait(false);
usableBalances
//扣减已占用库存
.DecreaseExpectOutQty(expectOuts, locations)
//去除不可拆箱 拆托的且有预占用的库存余额
.IgnoreExpectOutOfSameContainer(expectOutContainerCodes, itemStoreRelationDict, locations)
//过滤掉不允许拣料的库位
.FilterLocationEnablePickAsync(locations)
//排序库存余额 最终可用的余额集合
.SortByFifo();
return usableBalances;
}
private decimal GetRecommendQty(Guid traceId, decimal requestQty, decimal remainingQty, Balance usableBalance, LocationDTO location, ItemStoreRelationDTO itemStoreRelation)
{
var balanceQty = usableBalance.Qty;

25
be/Modules/Inventory/src/Win_in.Sfs.Wms.Inventory.Domain/Balances/IBalanceManager.cs

@ -9,13 +9,13 @@ namespace Win_in.Sfs.Wms.Inventory.Domain;
public interface IBalanceManager : IDomainService
{
/// <summary>
/// 入库
/// </summary>
/// <param name="transaction"></param>
/// <returns></returns>
Task<Balance> InboundAsync(Transaction transaction, ItemBasicDTO item, LocationDTO location);
/// <summary>
/// 出库
/// </summary>
@ -24,7 +24,8 @@ public interface IBalanceManager : IDomainService
Task<Balance> OutboundAsync(Transaction transaction, ItemBasicDTO item, LocationDTO location);
Task<List<Balance>> GetRecommendBalancesAsync(Guid traceId, string itemCode, decimal requestQty,
List<EnumLocationType> validLocationTypes, List<string> validLocationAreas, List<EnumInventoryStatus> validStatuses);
List<EnumLocationType> validLocationTypes, List<string> validLocationAreas,
List<EnumInventoryStatus> validStatuses);
Task<List<Balance>> GetListByLocationAsync(string locationCode);
@ -32,6 +33,22 @@ public interface IBalanceManager : IDomainService
Task<List<Balance>> GetListByItemAndLocationAsync(string itemCode, string locationCode);
Task<List<string>> GetItemCodesOfLocationAsync(string locationCode);
Task<Balance> ModifyAsync(Balance balance);
Task<List<Balance>> GetListByLocationTypeAndInventoryStatusAsync(string itemCode, EnumInventoryStatus enumInventoryStatus, List<EnumLocationType> enumLocationTypes);
Task<List<Balance>> GetRecommendBalancesByLocationAsync(Guid traceId, string itemCode, decimal requestQty, List<string> validLocations, List<EnumInventoryStatus> validStatuses,bool ispackingcode);
Task<List<Balance>> GetListByLocationTypeAndInventoryStatusAsync(string itemCode,
EnumInventoryStatus enumInventoryStatus, List<EnumLocationType> enumLocationTypes);
Task<List<Balance>> GetRecommendBalancesByLocationAsync(Guid traceId, string itemCode, decimal requestQty,
List<string> validLocations, List<EnumInventoryStatus> validStatuses, bool ispackingcode);
/// <summary>
/// 获取可用库存列表
/// </summary>
/// <param name="itemCode"></param>
/// <param name="validLocations"></param>
/// <param name="validStatuses"></param>
/// <param name="isRemovePackingCode"></param>
/// <returns></returns>
Task<List<Balance>> GetUsableListAsync(string itemCode,
List<string> validLocations,
List<EnumInventoryStatus> validStatuses, bool isRemovePackingCode);
}

4
be/Modules/Shared/src/Win_in.Sfs.Shared.Domain.Shared/Enums/Store/EnumIssueType.cs

@ -12,10 +12,10 @@ public enum EnumIssueType
/// <summary>
/// 按箱 叫料
/// </summary>
Box=1,
BoxQty=1,
/// <summary>
/// 按数量 叫料
/// </summary>
Num=2,
Qty=2,
}

1
be/Modules/Store/src/Win_in.Sfs.Wms.Store.Application.Contracts/Jobs/IssueJobs/InjectionJobs/Inputs/InjectionJobEditInput.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Win_in.Sfs.Shared.Domain;
using Win_in.Sfs.Shared.Domain.Shared;
using Win_in.Sfs.Shared.Domain.Shared.Enums.Store;
namespace Win_in.Sfs.Wms.Store.Application.Contracts;

168
be/Modules/Store/src/Win_in.Sfs.Wms.Store.Event/Requests/InjectionRequestEventHandler.cs

@ -76,11 +76,11 @@ public class InjectionRequestEventHandler
switch (entity.Type)
{
case nameof(EnumIssueType.Box):
case nameof(EnumIssueType.BoxQty):
injectionJobs =await CreateInjectionJobWithBoxQtyTypeAsync(entity).ConfigureAwait(false);
break;
case nameof(EnumIssueType.Num):
injectionJobs = await CreateInjectionJobWithBoxTypeAsync(entity).ConfigureAwait(false);
case nameof(EnumIssueType.Qty):
injectionJobs = await CreateInjectionJobWithQtyTypeAsync(entity).ConfigureAwait(false);
break;
}
@ -123,14 +123,11 @@ public class InjectionRequestEventHandler
/// <param name="injectionRequest"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<List<InjectionJobEditInput>> CreateInjectionJobWithBoxTypeAsync
private async Task<List<InjectionJobEditInput>> CreateInjectionJobWithQtyTypeAsync
(InjectionRequest injectionRequest)
{
var jobs = new List<InjectionJobEditInput>();
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); //所有库位的集合
@ -143,7 +140,7 @@ public class InjectionRequestEventHandler
//创建详情
var jobDetails =
await CreateInjectionJobDetailInputsWithBoxTypeAsync(injectionRequest, injectionRequestDetail,
await CreateInjectionJobDetailInputsWithQtyTypeAsync(injectionRequest, injectionRequestDetail,
toLocation.LocationGroupCode).ConfigureAwait(false);
if (!jobDetails.Any())
{
@ -155,7 +152,7 @@ public class InjectionRequestEventHandler
var job = jobs.FirstOrDefault(p => p.WorkGroupCode == fromLocation?.WorkGroupCode);
if (job == null || job.Details.Any(p => p.ToLocationCode != injectionRequestDetail.ToLocationCode))
{
job = await BuildInjectionJobCreateInputWithBoxTypeAsync(injectionRequest, fromLocation).ConfigureAwait(false);
job = await BuildInjectionJobCreateInputWithQtyTypeAsync(injectionRequest, fromLocation).ConfigureAwait(false);
jobs.Add(job);
}
@ -204,7 +201,7 @@ public class InjectionRequestEventHandler
/// <param name="injectionRequest"></param>
/// <param name="fromLocation"></param>
/// <returns></returns>
private async Task<InjectionJobEditInput> BuildInjectionJobCreateInputWithBoxTypeAsync(InjectionRequest injectionRequest,
private async Task<InjectionJobEditInput> BuildInjectionJobCreateInputWithQtyTypeAsync(InjectionRequest injectionRequest,
LocationDTO fromLocation)
{
InjectionJobEditInput job;
@ -235,7 +232,7 @@ public class InjectionRequestEventHandler
/// <param name="toLocationGroupCode"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<List<InjectionJobDetailInput>> CreateInjectionJobDetailInputsWithBoxTypeAsync(
private async Task<List<InjectionJobDetailInput>> CreateInjectionJobDetailInputsWithQtyTypeAsync(
InjectionRequest injectionRequest,
InjectionRequestDetail injectionRequestDetail, string toLocationGroupCode)
{
@ -269,7 +266,7 @@ public class InjectionRequestEventHandler
foreach (var recommend in recommendList)
{
//拿走需求量
var detail = await BuildInjectionJobDetailWithBoxTypeAsync(injectionRequestDetail, recommend, toLocationGroupCode)
var detail = await BuildInjectionJobDetailWithQtyTypeAsync(injectionRequestDetail, recommend, toLocationGroupCode)
.ConfigureAwait(false);
if (injectionRequest.UseOnTheWayLocation)
{
@ -297,7 +294,7 @@ public class InjectionRequestEventHandler
/// <param name="balance"></param>
/// <param name="toLocationGroupCode"></param>
/// <returns></returns>
private async Task<InjectionJobDetailInput> BuildInjectionJobDetailWithBoxTypeAsync(
private async Task<InjectionJobDetailInput> BuildInjectionJobDetailWithQtyTypeAsync(
InjectionRequestDetail injectionRequestDetail, BalanceDTO balance, string toLocationGroupCode)
{
//ProductionLineDTO prodLine = await _productionLineAppService.GetByLocationGroupCodeAsync(toLocationGroupCode).ConfigureAwait(false);
@ -340,7 +337,150 @@ public class InjectionRequestEventHandler
#region 按箱叫料
/// <summary>
/// 创建注塑任务
/// </summary>
/// <param name="injectionRequest"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<List<InjectionJobEditInput>> CreateInjectionJobWithBoxQtyTypeAsync
(InjectionRequest injectionRequest)
{
var inputJobs = new List<InjectionJobEditInput>();
//已用的库存的箱码集合
var useBalanceList = new List<string>();
var groupByItemCodeAndLocationCode =
injectionRequest.Details.GroupBy(p => new { p.ItemCode, p.ToLocationCode });
foreach (var locationCodeItemCodeGroup in groupByItemCodeAndLocationCode)
{
var inputDetails = injectionRequest.Details.Where(p =>
p.ItemCode == locationCodeItemCodeGroup.Key.ItemCode &&
p.ToLocationCode == locationCodeItemCodeGroup.Key.ToLocationCode);
var inputDetailTemplate = inputDetails.First();
//获取请求下 这个零件和这个库位一个需要多少箱
var sumBoxQty = inputDetails.Sum(p => p.BoxQty);
//获取生产线
var productionLineDto = await _productionLineAppService
.GetByLocationCodeAsync(inputDetails.First().ToLocationCode).ConfigureAwait(false);
if (productionLineDto == null)
{
throw new UserFriendlyException($"库位【{inputDetailTemplate.ToLocationCode}】没有对应的【生产线】");
}
var productLineCodeAndItemCode = (await _productionLineItemAppService
.GetByProductLineCodeAndItemCodeAsync(productionLineDto.Code, inputDetailTemplate.ItemCode)
.ConfigureAwait(false));
if (productLineCodeAndItemCode == null)
{
throw new UserFriendlyException(
$"物品代码【{inputDetailTemplate.ItemCode}】在生产线【{productionLineDto.Code}】中没有对应的【生产线物品关系】");
}
//获取可用库存
var input = new RecommendBalanceRequestInput
{
ItemCode = locationCodeItemCodeGroup.Key.ItemCode,
Qty = decimal.MaxValue,
Statuses = new EditableList<EnumInventoryStatus> { EnumInventoryStatus.OK },
Locations = JsonSerializer.Deserialize<List<string>>(productLineCodeAndItemCode
.RawLocationCodeListJson)
};
//所有可用库存
var usableList = await _balanceAppService.GetUsableListAsync(input).ConfigureAwait(false);
usableList=usableList.Where(p => !useBalanceList.Contains(p.PackingCode)).ToList();
var firstUsable = usableList.First();
useBalanceList.Add(firstUsable.PackingCode);
usableList.Remove(firstUsable);
for (int i = 0; i < sumBoxQty; i++)
{
var injectionJobEditInput =
await BuildInjectionJobCreateInputWithBoxQtyTypeAsync(injectionRequest, inputDetailTemplate,
firstUsable)
.ConfigureAwait(false);
inputJobs.Add(injectionJobEditInput);
}
}
return inputJobs;
}
/// <summary>
/// 构造注塑任务
/// </summary>
/// <param name="injectionRequest"></param>
/// <param name="injectionRequestDetail"></param>
/// <param name="balanceDtos"></param>
/// <returns></returns>
private async Task<InjectionJobEditInput> BuildInjectionJobCreateInputWithBoxQtyTypeAsync(InjectionRequest injectionRequest,
InjectionRequestDetail injectionRequestDetail,BalanceDTO balanceDtos)
{
InjectionJobEditInput job;
job = ObjectMapper.Map<InjectionRequest, InjectionJobEditInput>(injectionRequest);
job.JobType = EnumJobType.IssueJob;
job.JobStatus = EnumJobStatus.Open;
job.WorkGroupCode = injectionRequestDetail.ToLocationGroup;
job.WarehouseCode = injectionRequestDetail.ToWarehouseCode;
job.ProdLine = injectionRequestDetail.ProdLine;
job.Worker = injectionRequest.Worker;
job.InjectionRequestNumber = injectionRequest.Number;
job.Details.Add(await BuildInjectionJobDetailWithBoxQtyTypeAsync(injectionRequestDetail, balanceDtos).ConfigureAwait(false));
await Task.CompletedTask.ConfigureAwait(false);
return job;
}
/// <summary>
/// 构造注塑任务明细
/// </summary>
/// <param name="injectionRequestDetail"></param>
/// <param name="balance"></param>
/// <param name="toLocationGroupCode"></param>
/// <returns></returns>
private async Task<InjectionJobDetailInput> BuildInjectionJobDetailWithBoxQtyTypeAsync(
InjectionRequestDetail injectionRequestDetail, BalanceDTO balance)
{
//ProductionLineDTO prodLine = await _productionLineAppService.GetByLocationGroupCodeAsync(toLocationGroupCode).ConfigureAwait(false);
var detail = ObjectMapper.Map<BalanceDTO, InjectionJobDetailInput>(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.ProdLine = prodLine == null ? toLocationGroupCode : prodLine.Code;
detail.ProdLine = injectionRequestDetail.ToLocationCode;
await Task.CompletedTask.ConfigureAwait(false);
return detail;
}
#endregion

2
be/WZC2.sln.DotSettings

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/SuppressNullableWarningFix/Enabled/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>
Loading…
Cancel
Save