|
|
@@ -45,6 +45,8 @@ using Amazon.Runtime.Internal.Transform;
|
|
|
using System.IO.Compression;
|
|
|
using System.Collections;
|
|
|
using System.Data.SqlTypes;
|
|
|
+using WkHtmlToPdfDotNet;
|
|
|
+using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
|
|
|
|
|
namespace Business.ResourceExamineManagement
|
|
|
{
|
|
|
@@ -1011,7 +1013,7 @@ namespace Business.ResourceExamineManagement
|
|
|
List<long> icitemIds = returnlist.Select(c => c.item_id).ToList();
|
|
|
var stocklist = _ic_item_stock.GetManyByCondition(p => p.factory_id == factoryid && icitemIds.Contains(p.icitem_id)).Result;
|
|
|
//取当前订单的物料库存占用记录
|
|
|
- var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.bang_id == bangid && p.order_id == orderid).Result;
|
|
|
+ //var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.bang_id == bangid && p.order_id == orderid).Result;
|
|
|
//计算剩余库存
|
|
|
foreach (var item in returnlist)
|
|
|
{
|
|
|
@@ -1023,84 +1025,309 @@ namespace Business.ResourceExamineManagement
|
|
|
//当前物料的库存数量
|
|
|
decimal stockQty = stocklist.Where(s => s.icitem_id == item.item_id).Sum(p => p.sqty.GetValueOrDefault());
|
|
|
//获取当前订单其他订单行当前物料的占用数量
|
|
|
- decimal otherStockQty = occupylist.Where(s => s.icitem_id == item.item_id).Sum(p => p.quantity);
|
|
|
+ //decimal otherStockQty = occupylist.Where(s => s.icitem_id == item.item_id).Sum(p => p.quantity);
|
|
|
//当前订单行物料库存情况
|
|
|
- item.sqty = stockQty - otherStockQty;
|
|
|
+ //item.sqty = stockQty - otherStockQty;
|
|
|
+ item.sqty = stockQty;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
- public void calcTest(List<BomChildExamineDto> returnlist, long bangid, long orderid)
|
|
|
+ public void calcTest(List<BomChildExamineDto> returnlist, long bangid, long orderid,decimal count)
|
|
|
{
|
|
|
//占用情况
|
|
|
- var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.icitem_id == bangid && p.order_id == orderid).Result;
|
|
|
+ List<ic_item_stockoccupy> sklist = new List<ic_item_stockoccupy>();
|
|
|
+ //var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.icitem_id == bangid && p.order_id == orderid).Result;
|
|
|
|
|
|
//第一级
|
|
|
+ returnlist = returnlist.OrderBy(s => s.num).ToList();
|
|
|
var childList = returnlist.Where(s => s.parent_id == returnlist[0].id && s.type == 0).ToList();
|
|
|
+
|
|
|
+ //1.如果主料够的时候,不需要显示替代料的平铺视图,如果主料不够,显示替代料的平铺视图。
|
|
|
+ //2.替代策略和替代方式,影响到的是甲乙组概念,替代按主料有限,取代按组的优先级。A与B的替代,则A和B各自会存在一个组。
|
|
|
+ List<long> calcIds = new List<long>();
|
|
|
+ //先处理下最顶级的产品需要数量
|
|
|
+ returnlist[0].needCount = returnlist[0].qty * count;
|
|
|
+ //returnlist[0].satisfyNum = returnlist[0].sqty;
|
|
|
+ returnlist[0].lack_qty = returnlist[0].needCount - returnlist[0].sqty;
|
|
|
+ /*if (returnlist[0].lack_qty < 0)
|
|
|
+ {
|
|
|
+ //库存满足
|
|
|
+ returnlist[0].stock_state = 1;
|
|
|
+ returnlist[0].lack_qty = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ returnlist[0].stock_state = 0;
|
|
|
+ }*/
|
|
|
+ foreach (var item in returnlist)
|
|
|
+ {
|
|
|
+ if (item.level == 1)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //循环平铺整个资源检查的物料库存情况、缺料情况,子集缺料需要用父级缺料*子集使用数量-
|
|
|
+ CaclMaterialShortage(returnlist, item, count);
|
|
|
+ }
|
|
|
+
|
|
|
//这是从上往下展开计算缺料和可制
|
|
|
- calcTest2(returnlist[0], childList, returnlist, occupylist);
|
|
|
- returnlist[0].kz = childList.Min(s => s.kz);//得到最小可制数量。
|
|
|
+ calcTest2(returnlist[0], childList, returnlist, sklist);
|
|
|
+ //returnlist[0].kz = childList.Min(s => s.kz);//得到最小可制数量。
|
|
|
//再加个循环,来根据替代关系里的检查结果,根据规则明确使用和生成占用关系。
|
|
|
+
|
|
|
+
|
|
|
foreach (var item in childList)
|
|
|
{
|
|
|
- //提取群组关系
|
|
|
- var sublist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num && s.level == item.level).OrderBy(c => c.substitute_all_num).ToList();
|
|
|
- int idx;
|
|
|
- List<BomChildExamineDto> select = new List<BomChildExamineDto>();
|
|
|
- switch (item.substitute_strategy)
|
|
|
+ if (item.haveicsubs == 1)
|
|
|
{
|
|
|
- case 0://整批
|
|
|
- //循环检查哪一批可以直接使用,不需要采购
|
|
|
- for (idx = 0; idx < 99; idx++)
|
|
|
- {
|
|
|
- var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
|
|
|
- if (list.Any())
|
|
|
- {
|
|
|
- if (list.Where(s => s.stock_state != 1 && s.stock_state != 2 && s.stock_state != 3).Count() == 0)
|
|
|
- {
|
|
|
- //只满足充足或可制
|
|
|
- //无缺料情况
|
|
|
- select = list;
|
|
|
- break;
|
|
|
- }
|
|
|
- idx++;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- idx = 99;
|
|
|
- }
|
|
|
- }
|
|
|
- //如果都需要采购的情况下,则默认使用优先级最高的
|
|
|
- if (select.Count() == 0)
|
|
|
- {
|
|
|
- //如果为空,则默认使用优先级为0的集合作为替代关系
|
|
|
- select = sublist.Where(s => s.substitute_all_num == 0).ToList();
|
|
|
- select.ForEach(s => { s.is_use = true; s.stock_state = 4; });
|
|
|
- }
|
|
|
+ //如果有替代关系,根据群组来明确使用哪个群组的替代料。按整批和混用逻辑来算
|
|
|
+ // 如果有群组替代,就移除掉被检查过的记录 item.icitem_ids
|
|
|
+ CalcStrategy(item, returnlist, bangid, sklist);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //直接占用库存,缺料就生成采购
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //平铺每个物料需要总数
|
|
|
+ public void CaclMaterialShortage(List<BomChildExamineDto> returnlist, BomChildExamineDto item, decimal count)
|
|
|
+ {
|
|
|
+ var parent = returnlist.Find(s => s.id == item.parent_id);
|
|
|
+ //当前物料总共需要数量
|
|
|
+ item.needCount = parent.needCount * item.qty;
|
|
|
+ /*//当前库存可以满足的数量
|
|
|
+ item.satisfyNum = item.sqty;
|
|
|
+ //总需要数量减去库存量,得出缺料数量
|
|
|
+ item.lack_qty = item.needCount - item.satisfyNum;
|
|
|
+ //如果不满足,计算子物料,或者计算替代料
|
|
|
+ if (item.lack_qty < 0)
|
|
|
+ {
|
|
|
+ //库存满足
|
|
|
+ item.stock_state = 1;
|
|
|
+ item.lack_qty = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ item.stock_state = 0;
|
|
|
+ }*/
|
|
|
+ }
|
|
|
+
|
|
|
+ public void RecalculationStock(BomChildExamineDto item, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist) {
|
|
|
+ //再计算一边占用情况,这里根据父级产品额缺料量*当前子料的使用数量-子料库存量。得出当前子物料的缺料数量
|
|
|
+ var parent = returnlist.Find(s => s.id == item.parent_id);
|
|
|
+ decimal stockQty = sklist.Where(s => s.icitem_id == item.item_id).Sum(p => p.quantity);
|
|
|
+ item.sqty = item.sqty - stockQty;
|
|
|
+ item.lack_qty = parent.lack_qty * item.qty - item.sqty;
|
|
|
+ item.lack_qty = item.lack_qty > 0 ? item.lack_qty : 0;
|
|
|
+ item.stock_state = item.lack_qty > 0 ? 0 : 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void CalcStrategy(BomChildExamineDto item, List<BomChildExamineDto> returnlist,long bangid, List<ic_item_stockoccupy> sklist)
|
|
|
+ {
|
|
|
+ //提取群组关系
|
|
|
+ var sublist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num && s.level == item.level).OrderBy(c => c.substitute_all_num).ToList();
|
|
|
+ List<BomChildExamineDto> select = new List<BomChildExamineDto>();
|
|
|
+ var parent = returnlist.Find(s => s.id == item.parent_id);
|
|
|
+ switch (item.substitute_strategy)
|
|
|
+ {
|
|
|
+ case 0://整批
|
|
|
+ WholeBatchCheck(sublist, returnlist, sklist, select);
|
|
|
+ //如果都需要采购的情况下,则默认使用优先级最高的
|
|
|
+ WholeBatch(sublist, returnlist, sklist, select, bangid, parent);
|
|
|
+ break;
|
|
|
+ case 1://混用
|
|
|
+ MixedUse(sublist, returnlist, sklist, bangid, parent);
|
|
|
+ break;
|
|
|
+ case 2://整批加混用
|
|
|
+ WholeBatchCheck(sublist, returnlist, sklist, select);
|
|
|
+ if (select.Count() == 0)
|
|
|
+ {
|
|
|
+ //走混用
|
|
|
+ MixedUse(sublist, returnlist, sklist, bangid, parent);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //走整批
|
|
|
+ WholeBatch(sublist, returnlist, sklist, select, bangid, parent);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //整批判断
|
|
|
+ public void WholeBatchCheck(List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, List<BomChildExamineDto> select)
|
|
|
+ {
|
|
|
+ for (int idx = 0; idx < 99; idx++)
|
|
|
+ {
|
|
|
+ var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
|
|
|
+ if (list.Any())
|
|
|
+ {
|
|
|
+ foreach (var s in list)
|
|
|
+ {
|
|
|
+ RecalculationStock(s, returnlist, sklist);
|
|
|
+ }
|
|
|
+ if (list.Where(s => s.stock_state != 1).Count() == 0)
|
|
|
+ {
|
|
|
+ //只满足充足或可制
|
|
|
+ //无缺料情况
|
|
|
+ select = list;
|
|
|
+ select.ForEach(s => { s.is_use = true; });
|
|
|
break;
|
|
|
- case 1://混用
|
|
|
- for (idx = 0; idx < 99; idx++)
|
|
|
+ }
|
|
|
+ idx++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ idx = 99;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ //整批占用
|
|
|
+ public void WholeBatch(List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, List<BomChildExamineDto> select, long bangid, BomChildExamineDto parent) {
|
|
|
+ if (select.Count() == 0)
|
|
|
+ {
|
|
|
+ //如果为空,则默认使用优先级为0的集合作为替代关系
|
|
|
+ select = sublist.Where(s => s.substitute_all_num == 0).ToList();
|
|
|
+ select.ForEach(s => { s.is_use = true; });
|
|
|
+ }
|
|
|
+ //占用库存
|
|
|
+ foreach (var slt in select)
|
|
|
+ {
|
|
|
+ ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
|
|
|
+ itemStockoccupyDto.bang_id = bangid;
|
|
|
+ itemStockoccupyDto.icitem_id = slt.item_id;
|
|
|
+ if (slt.lack_qty > 0)
|
|
|
+ {
|
|
|
+ itemStockoccupyDto.quantity = slt.sqty;
|
|
|
+ //库存不够的时候,根据属性生成采购和委外。
|
|
|
+ var lack_qty = slt.lack_qty - itemStockoccupyDto.quantity;
|
|
|
+ if (slt.erp_cls == 1)
|
|
|
+ {
|
|
|
+ slt.make_qty = lack_qty;
|
|
|
+ /*var childList = returnlist.Where(s => s.parent_id == slt.id).ToList();
|
|
|
+ if (childList.Count() > 0)
|
|
|
{
|
|
|
- var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
|
|
|
- if (list.Any())
|
|
|
- {
|
|
|
- foreach (var hy in list)
|
|
|
- {
|
|
|
- //如果自己是可制,并且有子集,则取出自己父级的缺料数量和可制数量,使用掉自己的末级。
|
|
|
+ CalcStrategy(slt, returnlist, bangid, sklist);
|
|
|
+ }*/
|
|
|
+ }
|
|
|
+ else if (slt.erp_cls == 2)
|
|
|
+ {
|
|
|
+ //生成委外工单
|
|
|
+ slt.Subcontracting_qty = lack_qty;
|
|
|
+ }
|
|
|
+ else if (slt.erp_cls == 3)
|
|
|
+ {
|
|
|
+ //生成采购订单
|
|
|
+ slt.purchase_qty = lack_qty;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ itemStockoccupyDto.quantity = slt.needCount;
|
|
|
+ if (parent != null)
|
|
|
+ { //如果不缺料的情况下,则占用掉父级缺料乘以当前子集使用料数量
|
|
|
+ itemStockoccupyDto.quantity = parent.lack_qty * slt.qty - slt.sqty;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sklist.Add(itemStockoccupyDto);
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
- idx++;
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ //混用占用数据
|
|
|
+ public void MixedUse(List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, long bangid, BomChildExamineDto parent) {
|
|
|
+
|
|
|
+ decimal parent_lack = 0;
|
|
|
+ if (parent != null)
|
|
|
+ {
|
|
|
+ parent_lack = parent.lack_qty;
|
|
|
+ }
|
|
|
+ for (int idx = 0; idx < 99; idx++)
|
|
|
+ {
|
|
|
+ if (parent_lack <= 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
|
|
|
+ if (list.Any())
|
|
|
+ {
|
|
|
+ foreach (var s in list)
|
|
|
+ {
|
|
|
+ RecalculationStock(s, returnlist, sklist);
|
|
|
+ }
|
|
|
+ decimal minMake = 9999999;
|
|
|
+ foreach (var hy in list)
|
|
|
+ {
|
|
|
+ //混用先使用掉当前要用的数量
|
|
|
+ //得到库存最小数量,去占用,然后剩余的丢第二个循环里去占用。
|
|
|
+ decimal make = hy.sqty / hy.qty;
|
|
|
+ if (minMake > make)
|
|
|
+ {
|
|
|
+ minMake = make;
|
|
|
}
|
|
|
- break;
|
|
|
- case 2://整批加混用
|
|
|
- break;
|
|
|
+ }
|
|
|
+ decimal use_p_num = 0;
|
|
|
+ if (parent_lack > minMake) //20>10
|
|
|
+ {
|
|
|
+ use_p_num = minMake;
|
|
|
+ parent_lack -= use_p_num;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ use_p_num = parent_lack;
|
|
|
+ parent_lack = 0;
|
|
|
+ }
|
|
|
+ //根据混用逻辑,去占用物料
|
|
|
+ foreach (var zy in list)
|
|
|
+ {
|
|
|
+ ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
|
|
|
+ itemStockoccupyDto.bang_id = bangid;
|
|
|
+ itemStockoccupyDto.icitem_id = zy.item_id;
|
|
|
+ itemStockoccupyDto.quantity = use_p_num * zy.qty; ;
|
|
|
+ }
|
|
|
+ idx++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
|
|
|
+
|
|
|
+ public void Sockoccupy(BomChildExamineDto item, List<BomChildExamineDto> returnlist, long bangid, decimal kznun)
|
|
|
+ {
|
|
|
+ ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
|
|
|
+ itemStockoccupyDto.bang_id = bangid;
|
|
|
+ if (item.stock_state == 1)
|
|
|
+ {
|
|
|
+ //成品库存占用
|
|
|
+ //slt.needCount
|
|
|
+ //可制物料占用
|
|
|
+ //slt.kz;如果是BOM,就展开BOM占用子物料
|
|
|
+ itemStockoccupyDto.icitem_id = item.item_id;
|
|
|
+ itemStockoccupyDto.quantity = item.needCount;
|
|
|
+ }
|
|
|
+ else if (item.stock_state == 2 || item.stock_state == 3)
|
|
|
+ {
|
|
|
+ itemStockoccupyDto.quantity = item.sqty;
|
|
|
+ var num = item.lack_qty;//可制满足的情况下,直接占用可制。
|
|
|
+ var sltChild = returnlist.Where(s => s.parent_id == item.id).ToList();
|
|
|
+ if (sltChild.Count() > 0)
|
|
|
+ {
|
|
|
+ //有子集,则代表每条明细的库存加可制,才是父级的可制。
|
|
|
+ foreach (var c in sltChild)
|
|
|
+ {
|
|
|
+ Sockoccupy(c, returnlist, bangid, num);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //如果存在外购或者委外
|
|
|
+ else if (item.stock_state == 4 || item.stock_state == 5)
|
|
|
+ {
|
|
|
+ //根据最小颗粒度
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //计算物料是否缺料
|
|
|
public void calcTest2(BomChildExamineDto parent, List<BomChildExamineDto> bzlist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sockoccupyList)
|
|
|
{
|
|
|
//从第二级开始循环
|
|
|
@@ -1116,7 +1343,21 @@ namespace Business.ResourceExamineManagement
|
|
|
//循环计算群组,看哪个群组满足要求,然后使用此群组,将群组的库存和子物料占用掉。
|
|
|
//计算此次群组是否有符合
|
|
|
MaterialCalc(sublist, returnlist, sockoccupyList);
|
|
|
-
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //根据占用情况计算库存
|
|
|
+ Calczykc(item, sockoccupyList);
|
|
|
+ //如果有子集,则丢入循环,判断下库存可制等信息。
|
|
|
+ calcTest2(item, childList, returnlist, sockoccupyList);
|
|
|
+ /*item.kz = childList.Min(s => s.kz);
|
|
|
+ if (item.kz >= item.lack_qty)
|
|
|
+ {
|
|
|
+ item.stock_state = 3;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ item.stock_state = 0;
|
|
|
+ }*/
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1131,14 +1372,14 @@ namespace Business.ResourceExamineManagement
|
|
|
for (int i = 0; i <= maxIdx; i++)
|
|
|
{
|
|
|
var group = sublist.Where(s => s.substitute_all_num == i).ToList();
|
|
|
- int boolCount = 0;//代表某一颗物料无需采购,可以自制,则集合+1。
|
|
|
+ // int boolCount = 0;//代表某一颗物料无需采购,可以自制,则集合+1。
|
|
|
|
|
|
//如果替代料库存不够,但是可制够,则也考虑使用优先级最高
|
|
|
foreach (var g in group)
|
|
|
{
|
|
|
//根据占用情况计算库存
|
|
|
Calczykc(g, sockoccupyList);
|
|
|
- if (g.stock_state != 1)
|
|
|
+ /*if (g.stock_state != 1)
|
|
|
{
|
|
|
//判断此料是否BOM,如果是BOM,就考虑自制是否足够,此处递归检查子集
|
|
|
var gChildList = returnlist.Where(s => s.parent_id == g.id).ToList();
|
|
|
@@ -1148,10 +1389,19 @@ namespace Business.ResourceExamineManagement
|
|
|
//如果是自制,则考虑自制够不够
|
|
|
if (g.erp_cls == 1)
|
|
|
{
|
|
|
+ foreach (var cc in gChildList)
|
|
|
+ {
|
|
|
+ //如果子物料是BOM,则当前物料的可制数量应该是子物料的库存加子物料的可制除以子物料的使用量
|
|
|
+ if (returnlist.Where(s => s.parent_id == cc.id).Count() > 0)
|
|
|
+ {
|
|
|
+ cc.kz = Math.Floor((cc.sqty + cc.kz) / g.qty);
|
|
|
+ }
|
|
|
+ }
|
|
|
g.kz = gChildList.Min(s => s.kz);//得到最小可制数量。
|
|
|
+
|
|
|
//todo:如果是群组替代,可制数量应该跟随着主料来,不应该受限于最小可制单元。
|
|
|
g.stock_state = gChildList.Max(s => s.stock_state);
|
|
|
- if (g.lack_qty < g.kz)
|
|
|
+ if (g.lack_qty <= g.kz)
|
|
|
{
|
|
|
g.stock_state = 2;
|
|
|
boolCount++;
|
|
|
@@ -1166,10 +1416,10 @@ namespace Business.ResourceExamineManagement
|
|
|
}
|
|
|
else {
|
|
|
boolCount++;
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
|
|
|
- if (boolCount == group.Count())
|
|
|
+ /*if (boolCount == group.Count())
|
|
|
{
|
|
|
//如果检查集合满足条数与群组里的物料条数相同,则代表这个群组的数量是满足的。
|
|
|
group.ForEach(s =>
|
|
|
@@ -1177,7 +1427,7 @@ namespace Business.ResourceExamineManagement
|
|
|
s.is_show = true;
|
|
|
});
|
|
|
break;//如果已经找到合适的替代群组关系,并且都不需要采购,则直接不继续检查了。
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
}
|
|
|
|