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));
        }
Beispiel #4
0
        /// <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));
        }