public override async Task <bool> Run(CreateComposerTemplatesArgument arg, CommercePipelineExecutionContext context) { Condition.Requires(arg).IsNotNull($"{this.Name}: The argument can not be null"); //Create the template, add the view and the properties string itemId = $"Composer-{Guid.NewGuid()}"; var composerTemplate = new ComposerTemplate(GenericTaxesConstants.ComposerViewValue); composerTemplate.GetComponent <ListMembershipsComponent>().Memberships.Add(CommerceEntity.ListName <ComposerTemplate>()); composerTemplate.LinkedEntities = new List <string>() { "Sitecore.Commerce.Plugin.Catalog.SellableItem" }; composerTemplate.Name = "GenericTaxes"; composerTemplate.DisplayName = "Generic Taxes"; var composerTemplateViewComponent = composerTemplate.GetComponent <EntityViewComponent>(); var composerTemplateView = new EntityView { Name = "Generic Taxes", DisplayName = "GenericTaxes", DisplayRank = 0, ItemId = itemId, EntityId = composerTemplate.Id }; GenericTaxPolicy taxPolicy = context.GetPolicy <GenericTaxPolicy>(); AvailableSelectionsPolicy availableSelectionsPolicy = new AvailableSelectionsPolicy(); foreach (decimal whiteListEntry in taxPolicy.Whitelist) { availableSelectionsPolicy.List.Add(new Selection() { Name = whiteListEntry.ToString(), DisplayName = whiteListEntry.ToString(), }); } composerTemplateView.Properties.Add(new ViewProperty() { DisplayName = taxPolicy.TaxFieldName, Name = taxPolicy.TaxFieldName, OriginalType = "System.String", RawValue = string.Empty, Value = string.Empty, Policies = new List <Policy>() { availableSelectionsPolicy } }); composerTemplateViewComponent.View.ChildViews.Add(composerTemplateView); var persistResult = await this._commerceCommander.PersistEntity(context.CommerceContext, composerTemplate); return(await Task.FromResult(true)); }
/// <summary> /// Fills the Taxes Dictionary with the proper Tax Values from cart /// </summary> /// <param name="taxPolicy">current taxPolicy</param> /// <param name="context">context</param> /// <param name="arg">arg</param> /// <returns></returns> private IDictionary <decimal, decimal> FillTaxesDictionary( GenericTaxPolicy taxPolicy, CommercePipelineExecutionContext context, Cart arg) { Decimal defaultItemTaxRate = taxPolicy.DefaultItemTaxRate; IDictionary <decimal, decimal> taxesDictionary = InitializeTaxesDictionary(taxPolicy.Whitelist); var sellableItems = context.CommerceContext.GetEntities <SellableItem>(); foreach (var sellableItem in sellableItems) { var composerTemplateViewsComponent = sellableItem.GetComponent <ComposerTemplateViewsComponent>().Views .FirstOrDefault(element => element.Value.Equals(GenericTaxesConstants.ComposerViewValue)); var composerView = sellableItem.GetComposerView(composerTemplateViewsComponent.Key); // Extract the needed tax value from custom view property string taxValue = composerView?.Properties .FirstOrDefault(element => element.Name.Equals(taxPolicy.TaxFieldName)) ?.Value; // Cast the string with correct culture to decimal if (!decimal.TryParse(taxValue, NumberStyles.Any, this.CultureEn, out decimal taxValueAsDecimal) || !taxPolicy.Whitelist.Contains(taxValueAsDecimal)) { context.Logger.LogDebug(string.Format("{0} - Tax Rate: {1} is invalid or not whitelisted", this.Name, taxValue)); if (taxPolicy.UseDefaultTaxRateIfNoneIsSet) { taxValueAsDecimal = defaultItemTaxRate; } else { continue; } } var cartLine = arg.Lines .FirstOrDefault(element => element.GetComponent <CartProductComponent>().Id.Equals(sellableItem.ProductId)); Decimal adjustments = cartLine.Adjustments .Where((a => a.IsTaxable)) .Aggregate(Decimal.Zero, ((current, adjustment) => current + adjustment.Adjustment.Amount)); taxesDictionary.TryGetValue(taxValueAsDecimal, out decimal storedTaxRate); storedTaxRate += taxValueAsDecimal * (cartLine.Totals.SubTotal.Amount + adjustments); taxesDictionary[taxValueAsDecimal] = storedTaxRate; } return(taxesDictionary); }
/// <summary> /// Run /// </summary> /// <param name="arg">arg</param> /// <param name="context">context</param> /// <returns></returns> public override Task <Cart> Run(Cart arg, CommercePipelineExecutionContext context) { if (!ConditionsValid(arg, context)) { Task.FromResult(arg); } GenericTaxPolicy taxPolicy = context.GetPolicy <GenericTaxPolicy>(); context.Logger.LogDebug(string.Format("{0} - Policy: {1}", this.Name, taxPolicy.TaxCalculationEnabled)); IEnumerable <CartLineComponent> source = arg.Lines.Where(line => { if (taxPolicy.TaxExemptTagsEnabled && line.HasComponent <CartProductComponent>()) { return(line.GetComponent <CartProductComponent>().Tags .Select((t => t.Name)).Contains(taxPolicy.TaxExemptTag, StringComparer.InvariantCultureIgnoreCase)); } return(false); }); Decimal adjustmentLinesTotal = new Decimal(); Action <CartLineComponent> action = (l => adjustmentLinesTotal += l.Totals.SubTotal.Amount); source.ForEach(action); string currencyCode = context.CommerceContext.CurrentCurrency(); Decimal cartLevelAdjustments = arg .Adjustments .Where(p => p.IsTaxable) .Aggregate(Decimal.Zero, ((current, adjustment) => current + adjustment.Adjustment.Amount)); // Cart Adjustments if (cartLevelAdjustments > Decimal.Zero) { Decimal defaultCartTaxRate = taxPolicy.DefaultCartTaxRate; decimal cartLevelTaxRate = cartLevelAdjustments * defaultCartTaxRate; string taxName = $"TaxFee-CartAdjustments-{defaultCartTaxRate * 100}"; arg.Adjustments.Add(new CartLevelAwardedAdjustment { Name = taxName, DisplayName = taxName, Adjustment = new Money(currencyCode, cartLevelTaxRate), AdjustmentType = context.GetPolicy <KnownCartAdjustmentTypesPolicy>().Tax, AwardingBlock = this.Name, IsTaxable = false }); } IDictionary <decimal, decimal> taxesDictionary = FillTaxesDictionary(taxPolicy, context, arg); // Cart Lines foreach (decimal key in taxesDictionary.Keys) { decimal tax = taxesDictionary[key]; if (tax > Decimal.Zero) { string taxName = $"TaxFee-CartLines-{(key * 100)}%"; arg.Adjustments.Add(new CartLevelAwardedAdjustment { Name = taxName, DisplayName = taxName, Adjustment = new Money(currencyCode, tax), AdjustmentType = context.GetPolicy <KnownCartAdjustmentTypesPolicy>().Tax, AwardingBlock = this.Name, IsTaxable = false }); } } return(Task.FromResult(arg)); }
/// <summary> /// Run /// </summary> /// <param name="arg">arg</param> /// <param name="context"><context/param> /// <returns></returns> public override Task <Cart> Run(Cart arg, CommercePipelineExecutionContext context) { Condition.Requires(arg).IsNotNull(string.Format("{0}: The cart can not be null", this.Name)); Condition.Requires(arg.Lines).IsNotNull(string.Format("{0}: The cart lines can not be null", this.Name)); if (!arg.Lines.Any()) { Task.FromResult(arg); } List <CartLineComponent> list = arg.Lines .Where((line => { return(line != null ? line.HasComponent <FulfillmentComponent>() : false); })) .Select((l => l)) .ToList(); if (!list.Any()) { context.Logger.LogDebug(string.Format("{0} - No lines to calculate tax on", this.Name)); return(Task.FromResult(arg)); } string currencyCode = context.CommerceContext.CurrentCurrency(); GenericTaxPolicy taxPolicy = context.GetPolicy <GenericTaxPolicy>(); GlobalPricingPolicy pricePolicy = context.GetPolicy <GlobalPricingPolicy>(); context.Logger.LogDebug(string.Format("{0} - Policy:{1}", this.Name, taxPolicy.TaxCalculationEnabled)); Decimal defaultItemTaxRate = taxPolicy.DefaultItemTaxRate; context.Logger.LogDebug(string.Format("{0} - Item Tax Rate:{1}", this.Name, defaultItemTaxRate)); foreach (CartLineComponent cartLineComponent in list) { if (taxPolicy.TaxExemptTagsEnabled && cartLineComponent.HasComponent <CartProductComponent>()) { IList <Tag> tags = cartLineComponent.GetComponent <CartProductComponent>().Tags; Func <Tag, string> func = (t => t.Name); Func <Tag, string> selector = null; if (tags.Select(selector).Contains(taxPolicy.TaxExemptTag, StringComparer.InvariantCultureIgnoreCase)) { context.Logger.LogDebug(string.Format("{0} - Skipping Tax Calculation for product {1} due to exempt tag", (object)this.Name, (object)cartLineComponent.ItemId), Array.Empty <object>()); continue; } } Decimal num = cartLineComponent.Adjustments.Where((a => a.IsTaxable)).Aggregate(Decimal.Zero, ((current, adjustment) => current + adjustment.Adjustment.Amount)); context.Logger.LogDebug(string.Format("{0} - SubTotal:{1}", this.Name, cartLineComponent.Totals.SubTotal.Amount)); context.Logger.LogDebug(string.Format("{0} - Adjustment Total:{1}", this.Name, num)); //** Custom Implementation // Retrieve the sellable item from commerce context var sellableItem = context.CommerceContext.GetEntity <SellableItem>(); var composerTemplateViewsComponent = sellableItem.GetComponent <ComposerTemplateViewsComponent>().Views.FirstOrDefault(element => element.Value.Equals(GenericTaxesConstants.ComposerViewValue)); var composerView = sellableItem.GetComposerView(composerTemplateViewsComponent.Key); // Extract the needed tax value from custom view property string taxValue = composerView.Properties.FirstOrDefault(element => element.Name.Equals(taxPolicy.TaxFieldName)).Value; // Cast the string with correct culture to decimal if (!decimal.TryParse(taxValue, NumberStyles.Any, this.CultureEn, out decimal taxValueAsDecimal) || !taxPolicy.Whitelist.Contains(taxValueAsDecimal)) { context.Logger.LogDebug(string.Format("{0} - Tax Rate: {1} is invalid or not whitelisted", this.Name, taxValue)); if (taxPolicy.UseDefaultTaxRateIfNoneIsSet) { taxValueAsDecimal = defaultItemTaxRate; } else { continue; } } Money money = new Money(currencyCode, (cartLineComponent.Totals.SubTotal.Amount + num) * taxValueAsDecimal); if (pricePolicy.ShouldRoundPriceCalc) { money.Amount = Decimal.Round(money.Amount, pricePolicy.RoundDigits, pricePolicy.MidPointRoundUp ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven); } IList <AwardedAdjustment> adjustments = cartLineComponent.Adjustments; string taxName = $"TaxFee-{(taxValueAsDecimal * 100)}%"; CartLineLevelAwardedAdjustment awardedAdjustment = new CartLineLevelAwardedAdjustment { Name = taxName, DisplayName = taxName, Adjustment = money, AdjustmentType = context.GetPolicy <KnownCartAdjustmentTypesPolicy>().Tax, AwardingBlock = this.Name, IsTaxable = false, IncludeInGrandTotal = false }; adjustments.Add(awardedAdjustment); } return(Task.FromResult(arg)); }