/// <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); }
/// <summary> /// 根据给予的组合规则进行拆单操作 /// </summary> /// <param name="productList">订单商品明细</param> /// <param name="rules">按层面整合的符合规则</param> /// <param name="withTax">是否收税</param> /// <returns>可拆解的订单,未能拆解的订单</returns> private Tuple <SplitedOrder, List <ProductEntity> > Split(List <ProductEntity> productList, List <List <IProductRuleEntity> > rules, bool withTax) { var result = new SplitedOrder(); var restProductList = productList; for (int i = 0; i < rules.Count && restProductList.Count > 0; i++) { var l = rules[i]; for (int j = 0; j < l.Count && restProductList.Count > 0; j++) { var r = l[j]; while (restProductList.Count > 0) { var so = r.Split(restProductList, withTax); result.AddSubOrderRange(so.Item1); if (so.Item1.OrderList.Count == 0 || so.Item2.Count <= 0) { // 拆不出来,此规则不适用 || 全拆完了 break; } restProductList = so.Item2; } } } result.OrderList.ForEach(o => o.LogisticsCost = this.CalculateFreight(o.CalculateTotalWeight())); return(Tuple.Create(result, restProductList)); }
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); }
public Tuple <string, SplitedOrder> SplitWithOrganization1(SplitWithExpRequest1 request) { var validResult = this.ValidRequire(request); if (!validResult.Item1) { return(Tuple.Create <string, SplitedOrder>(validResult.Item2, null)); } LogHelper.Logger.Info("Call Spliter.SplitWithOrganization(): " + request); SplitedOrder result = this.spliter.SplitWithOrganization1(request.OrderId.ToString(), request.ProList, request.TotalQuantity, request.logistics); return(Tuple.Create(string.Empty, result)); }
private Tuple <SplitedOrder, List <ProductEntity> > SplitOnceWithOrganization(List <ProductEntity> productList, List <RuleEntity> rules, SplitPrinciple splitPrinciple) { var splitedOrder = new SplitedOrder(); var restProductList = productList; for (int i = 0; i < rules.Count && restProductList.Count > 0; i++) { var tuple = rules[i].Split(restProductList, splitPrinciple, false); splitedOrder.AddSubOrderRange(tuple.Item1); restProductList = tuple.Item2; } return(Tuple.Create(splitedOrder, restProductList)); }
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 Tuple <SplitedOrder, bool, List <ProductEntity> > SplitOnce(List <ProductEntity> productList, List <RuleEntity> rules, SplitPrinciple splitPrinciple) { var splitedOrder = new SplitedOrder(); var restProductList = productList; for (int i = 0; i < rules.Count && restProductList.Count > 0; i++) { var tuple = rules[i].Split(restProductList, splitPrinciple, false); splitedOrder.AddSubOrderRange(tuple.Item1); restProductList = tuple.Item2; } bool isTax = false; if (splitPrinciple != SplitPrinciple.LogisticsFirst && restProductList.Count > 0) { // 没拆完,按含税方式拆一遍 //for (int i = 0; i < rules.Count && restProductList.Count > 0; i++) //{ // var tuple = rules[i].Split(restProductList, splitPrinciple, true); // isTax |= tuple.Item1.OrderList.Count > 0; // splitedOrder.AddSubOrderRange(tuple.Item1); // restProductList = tuple.Item2; //} //if (restProductList.Count > 0) { // 还有剩余就按BC拆 isTax = true; //var subOrder = new SubOrder("-1", null, null, null, null, restProductList.SelectMany(pe => pe.OrderInfo).ToList()) //{ // LogisticsUnitPrice = decimal.MaxValue, // LogisticsCost = decimal.MaxValue, // TaxCost = decimal.MaxValue, //}; //splitedOrder.AddSubOrder(subOrder); splitedOrder.AddSubOrderRange(bcRuleEntity.Split(restProductList)); restProductList.Clear(); } } return(Tuple.Create(splitedOrder, isTax, restProductList)); }
private Tuple <SplitedOrder, bool, List <ProductEntity> > SplitOnce(List <ProductEntity> productList, List <RuleEntity> rules, SplitPrinciple splitPrinciple) { var splitedOrder = new SplitedOrder(); var restProductList = productList; for (int i = 0; i < rules.Count && restProductList.Count > 0; i++) { var tuple = rules[i].Split(restProductList, splitPrinciple, false); splitedOrder.AddSubOrderRange(tuple.Item1); restProductList = tuple.Item2; } bool isTax = false; if (splitPrinciple != SplitPrinciple.LogisticsFirst && restProductList.Count > 0) { isTax = true; splitedOrder.AddSubOrderRange(this.ReturnRemainPackage(restProductList)); restProductList.Clear(); } return(Tuple.Create(splitedOrder, isTax, restProductList)); }
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)); }