/// <summary> /// 先按BC方式拆单,BC拆完仍有剩余时,全部放到一个子订单中(里面的费用全设为MaxValue) /// </summary> /// <param name="productList"></param> /// <returns></returns> public SplitedOrder Split(List <ProductEntity> productList /*, int totalQuantity*/) { var result = new SplitedOrder(); var peTuple = SplitProductEntity(productList, (decimal)this.BcConfig.TotalPriceLimit); // 额度(2万)以后按BC拆单 var subOrder = CreateSubOrder(); peTuple.Item1.ForEach(pe => subOrder.ProList.AddRange(pe.OrderInfo)); subOrder.TaxCost = CalculateTax(peTuple.Item1); result.AddSubOrder(subOrder); if (peTuple.Item2.Count > 0) { // 超过额度的不拆了,卖不了 var invalidSubOrder = new SubOrder("-1", null, null, null, null, null, peTuple.Item2.SelectMany(pe => pe.OrderInfo).ToList()) { LogisticsUnitPrice = int.MaxValue, LogisticsCost = int.MaxValue, TaxCost = int.MaxValue, }; result.AddSubOrder(invalidSubOrder); } result.OrderList.ForEach(so => so.LogisticsCost = this.CalculateFreight(so.CalculateTotalWeight())); result.OrderList.ForEach(so => so.CalculateTotalPrice()); return(result); }
private SplitedOrder SplitOrder(string orderId, List <ProductEntity> productList, List <Product> badProductList, int totalQuantity, SplitPrinciple splitPrinciple) { Debug.Assert(splitPrinciple != SplitPrinciple.LogisticsFirst); var result = new SplitedOrder(); List <List <RuleEntity> > ruleOptions = null; switch (splitPrinciple) { case SplitPrinciple.SpeedFirst: break; case SplitPrinciple.QuanlityFirst: case SplitPrinciple.PriceFirst: default: ruleOptions = splitConfig.GetRuleEntities(splitPrinciple, productList); break; } if (ruleOptions != null && ruleOptions.Count > 0) { var splitResults = ruleOptions.Select(rules => SplitOnce(SplitPackage.SplitV1.Common.Common.CloneProductEntityList(productList), rules, splitPrinciple)).ToList(); Tuple <SplitedOrder, bool, List <ProductEntity> > optimal = null; switch (splitPrinciple) { case SplitPrinciple.QuanlityFirst: optimal = splitResults.OrderBy(t => t.Item2 ? 0 : 1) .ThenBy(t => t.Item1.OrderList.Count) .ThenBy(t => t.Item1.CalculateLogisticsAndTaxCost()) .FirstOrDefault(); break; case SplitPrinciple.PriceFirst: case SplitPrinciple.LogisticsFirst: default: optimal = splitResults.OrderBy(t => t.Item2 ? 0 : 1) .ThenBy(t => t.Item1.CalculateLogisticsAndTaxCost()) .ThenBy(t => t.Item1.OrderList.Count) .FirstOrDefault(); break; } result = optimal != null ? optimal.Item1 : result; } else { // BC方式,计算跨境综合税 result = this.ReturnRemainPackage(productList); } if (badProductList != null && badProductList.Any()) { var subOrder = new SubOrder("-2", null, null, null, null, null, badProductList) { LogisticsUnitPrice = int.MaxValue, LogisticsCost = int.MaxValue, TaxCost = int.MaxValue, }; result.AddSubOrder(subOrder); } result.OrderId = orderId; return(result); }
private SplitedOrder ReturnRemainPackage(List <ProductEntity> remainProducts) { var result = new SplitedOrder(); var productList = remainProducts.SelectMany(o => o.OrderInfo).ToList(); var invalidSubOrder = new SubOrder() { Id = "-1", TotalWeight = productList.Sum(o => o.Weight * o.Quantity), TotalPrice = productList.Sum(o => o.ProPrice * o.Quantity), ProList = productList }; result.AddSubOrder(invalidSubOrder); return(result); }
/// <summary> /// 循环拆解商品项 /// </summary> /// <param name="productList"></param> /// <param name="withTax"></param> /// <returns>可拆解出的子订单,未能拆解的商品</returns> public Tuple <SplitedOrder, List <ProductEntity> > Split(List <ProductEntity> productList, bool withTax) { var result = new SplitedOrder(); var productEntities = productList.Where(pe => ruleItemDic.ContainsKey(pe.PTId)).ToList(); var subOrder = SplitOneSubOrder(productEntities, withTax); while (subOrder != null && subOrder.ProList.Count > 0) { result.AddSubOrder(subOrder); subOrder = SplitOneSubOrder(productEntities, withTax); } productList.RemoveAll(pe => pe.OrderInfo.Count <= 0); result.OrderList.ForEach(so => so.CalculateTotalPrice()); result.OrderList.Where(so => withTax).ToList().ForEach(so => CalculateTax(so)); return(Tuple.Create(result, productList)); }
private SplitedOrder SplitOrder(string orderId, List <ProductEntity> productList, List <Product> badProductList, int totalQuantity, SplitPrinciple splitPrinciple) { Debug.Assert(splitPrinciple != SplitPrinciple.LogisticsFirst); var result = new SplitedOrder(); List <List <RuleEntity> > ruleOptions = null; switch (splitPrinciple) { case SplitPrinciple.SpeedFirst: // BC break; //case SplitPrinciple.LogisticsFirst: case SplitPrinciple.QuanlityFirst: case SplitPrinciple.PriceFirst: default: ruleOptions = splitConfig.GetRuleEntities(splitPrinciple, productList); break; } if (ruleOptions != null && ruleOptions.Count > 0) { var splitResults = ruleOptions.Select(rules => SplitOnce(SplitPackage.Split.Common.Common.CloneProductEntityList(productList), rules, splitPrinciple)).ToList(); var msgs = Enumerable.Repeat(string.Format("Spliter.SplitOrder({0}, {1}, {2}, {3}) alternative:", "orderId=" + orderId, "productList.Count=" + productList.Count, "totalQuantity=" + totalQuantity, "splitPrinciple=" + splitPrinciple), 1) .Concat(splitResults.Select(ret => string.Format(" ({0}, {1}, {2})", ret.Item1, ret.Item2, "[" + string.Join(", ", ret.Item3) + "]"))); LogHelper.Logger.Info(string.Join(Environment.NewLine, msgs)); Tuple <SplitedOrder, bool, List <ProductEntity> > optimal = null; switch (splitPrinciple) { case SplitPrinciple.QuanlityFirst: optimal = splitResults.OrderBy(t => t.Item2 ? 0 : 1) .ThenBy(t => t.Item1.OrderList.Count) .ThenBy(t => t.Item1.CalculateLogisticsAndTaxCost()) .FirstOrDefault(); break; case SplitPrinciple.PriceFirst: case SplitPrinciple.LogisticsFirst: default: optimal = splitResults.OrderBy(t => t.Item2 ? 0 : 1) .ThenBy(t => t.Item1.CalculateLogisticsAndTaxCost()) .ThenBy(t => t.Item1.OrderList.Count) .FirstOrDefault(); break; } result = optimal != null ? optimal.Item1 : result; } else { // BC方式,计算跨境综合税 result = bcRuleEntity.Split(productList); } if (badProductList != null && badProductList.Any()) { var subOrder = new SubOrder("-2", null, null, null, null, null, badProductList) { LogisticsUnitPrice = int.MaxValue, LogisticsCost = int.MaxValue, TaxCost = int.MaxValue, }; result.AddSubOrder(subOrder); } result.OrderId = orderId; return(result); }
// 拆包,把当前Rule能装的包全拆 public Tuple <SplitedOrder, List <ProductEntity> > Split(List <ProductEntity> productList, bool withTax) { var result = new SplitedOrder(); var index = productList.FindIndex(pe => pe.PTId == this.PTId); if (index >= 0) { var pe = productList[index]; var restProducts = new List <Product>(); var subOrder = CreateSubOrder(); for (int j = 0; j < pe.OrderInfo.Count; j++) { var restProduct = pe.OrderInfo[j]; while (restProduct.Quantity > 0) { var pl = FindSubLevel(restProduct); var quantityLimit = (pl != null ? pl.MaxQuantity : this.MaxQuantity) - subOrder.CalculateTotalQuantity(); var weightLimit = (pl != null ? pl.MaxWeight : this.MaxWeight) - subOrder.CalculateTotalWeight(); var priceLimit = (withTax ? this.MaxPrice : (pl != null ? (decimal)pl.MaxPrice : this.TaxThreshold)) - subOrder.CalculateTotalPrice(); var productTuple = SplitProduct(restProduct, quantityLimit, weightLimit, priceLimit); if (productTuple.Item1.Quantity <= 0) // 一个都分配不出去,要么是当前Rule不满足,要么是当前SubOrder满了,测试一下是哪种情况 { var testProduct = SplitProduct(restProduct, this.MaxQuantity, this.MaxWeight, this.MaxPrice); if (testProduct.Item1.Quantity <= 0) // Rule不满足 { break; } else // 满了,换下一个子包 { result.AddSubOrder(subOrder); subOrder = CreateSubOrder(); } } else // 分配了部分或全部 { subOrder.ProList.Add(productTuple.Item1); restProduct = productTuple.Item2; if (restProduct.Quantity > 0 || subOrder.CalculateTotalQuantity() >= this.MaxQuantity) // 当前子包满了(有剩余,或者子包的数量达到上限) { result.AddSubOrder(subOrder); subOrder = CreateSubOrder(); } } } if (restProduct.Quantity > 0) // 如果分配不出去了,但还有剩下,那放回原队列,分配给下一家物流 { restProducts.Add(restProduct); } } if (subOrder.ProList.Count > 0) { result.AddSubOrder(subOrder); subOrder = CreateSubOrder(); } if (restProducts.Any()) // 最后还有剩余,把剩余放回队列 { productList[index].OrderInfo = restProducts; } else // 没有剩余,从队列移除 { productList.RemoveAt(index); } } result.OrderList.ForEach(so => so.CalculateTotalPrice()); result.OrderList.Where(so => withTax).ToList().ForEach(so => CalculateTax(so)); return(Tuple.Create(result, productList)); }