/// <summary>
            /// Convert unit of measure for item availabilities.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="itemAvailableQuantities">The item availabilities.</param>
            /// <param name="itemUnits">The desired item unit of measures.</param>
            /// <returns>The converted item available quantities.</returns>
            internal static IEnumerable <ItemAvailableQuantity> ConvertUnitOfMeasure(RequestContext context, IEnumerable <ItemAvailableQuantity> itemAvailableQuantities, IEnumerable <ItemUnit> itemUnits)
            {
                List <ItemAvailableQuantity> convertedItemAvailableQuantities = new List <ItemAvailableQuantity>();

                IEnumerable <ItemUnit>         distinctItemUnits  = itemUnits.Distinct();
                IEnumerable <ItemUnitQuantity> itemUnitQuantities = itemAvailableQuantities.Select(itemAvailableQuantity => itemAvailableQuantity.GetItemUnitQuantity());
                Dictionary <ItemUnitConversion, UnitOfMeasureConversion> conversions = GetUnitOfMeasureConversions(context, itemUnitQuantities, distinctItemUnits);

                ILookup <ItemVariantInventoryDimension, ItemUnit> itemUnitLookupByItem = distinctItemUnits.ToLookup(itemUnit => itemUnit.GetItem());

                foreach (ItemAvailableQuantity itemAvailableQuantity in itemAvailableQuantities)
                {
                    bool hasInventoryUnit = false;
                    foreach (ItemUnit itemUnit in itemUnitLookupByItem[itemAvailableQuantity.GetItem()])
                    {
                        ItemUnitConversion      itemUnitConversion = ChannelAvailabilityHelper.GetItemUnitConversion(itemAvailableQuantity.GetItemUnitQuantity(), itemUnit);
                        UnitOfMeasureConversion unitOfMeasureConversion;
                        if (!conversions.TryGetValue(itemUnitConversion, out unitOfMeasureConversion))
                        {
                            if (!StringDataHelper.Equals(itemAvailableQuantity.UnitOfMeasure, itemUnit.UnitOfMeasure))
                            {
                                context.Notify(new UnableToConvertUnitOfMeasureNotification(itemUnitConversion));
                            }

                            if (!hasInventoryUnit)
                            {
                                convertedItemAvailableQuantities.Add(itemAvailableQuantity);
                                hasInventoryUnit = true;
                            }
                        }
                        else
                        {
                            ItemAvailableQuantity convertedItemAvailableQuantity = new ItemAvailableQuantity();
                            convertedItemAvailableQuantity.CopyPropertiesFrom(itemAvailableQuantity);
                            convertedItemAvailableQuantity.AvailableQuantity = unitOfMeasureConversion.Convert(itemAvailableQuantity.AvailableQuantity);
                            convertedItemAvailableQuantity.UnitOfMeasure     = itemUnitConversion.ToUnitOfMeasure;
                            convertedItemAvailableQuantities.Add(convertedItemAvailableQuantity);
                        }
                    }
                }

                return(convertedItemAvailableQuantities.AsEnumerable());
            }
예제 #2
0
            private static Dictionary <string, decimal> GetSalesLineInventoryQuantities(RequestContext context, IEnumerable <SalesLine> salesLines, Dictionary <ItemUnitConversion, UnitOfMeasureConversion> unitOfMeasureConversions)
            {
                // Convert quantities from sales to inventory unit of measure and calculate total converted quantities.
                Dictionary <string, decimal> salesLineInventoryQuantities = new Dictionary <string, decimal>();

                foreach (SalesLine salesLine in salesLines)
                {
                    decimal salesLineInventoryQuantity = 0;
                    UnitOfMeasureConversion unitOfMeasureConversion;
                    ItemUnitConversion      itemUnitConversion = salesLine.GetItemUnitConversion();

                    if (string.IsNullOrWhiteSpace(salesLine.SalesOrderUnitOfMeasure))
                    {
                        var notification = new EmptySalesUnitOfMeasureNotification(salesLine.LineId);
                        context.Notify(notification);
                    }
                    else if (string.IsNullOrWhiteSpace(salesLine.InventOrderUnitOfMeasure))
                    {
                        var notification = new EmptyInventoryUnitOfMeasureNotification(salesLine.ItemId);
                        context.Notify(notification);
                    }
                    else if (string.Equals(salesLine.SalesOrderUnitOfMeasure, salesLine.InventOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase))
                    {
                        salesLineInventoryQuantity = salesLine.Quantity;
                    }
                    else if (unitOfMeasureConversions.TryGetValue(itemUnitConversion, out unitOfMeasureConversion))
                    {
                        salesLineInventoryQuantity = unitOfMeasureConversion.Convert(salesLine.Quantity);
                    }
                    else
                    {
                        var notification = new UnableToConvertUnitOfMeasureNotification(itemUnitConversion);
                        context.Notify(notification);
                    }

                    salesLineInventoryQuantities[salesLine.LineId] = salesLineInventoryQuantity;
                }

                return(salesLineInventoryQuantities);
            }
            /// <summary>
            /// Convert unit of measure for item reservations.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="itemReservations">The item reservations.</param>
            /// <param name="itemUnitConversions">The item unit conversions.</param>
            /// <exception cref="DataValidationException">Thrown if there is no conversion rules defined for any of item reservations.</exception>
            internal static void ConvertUnitOfMeasure(RequestContext context, IEnumerable <ItemReservation> itemReservations, IEnumerable <ItemUnitConversion> itemUnitConversions)
            {
                Collection <DataValidationFailure> validationFailures = new Collection <DataValidationFailure>();

                Dictionary <ItemUnitConversion, UnitOfMeasureConversion> conversions = GetUnitOfMeasureConversions(context, itemUnitConversions);
                IEnumerator <ItemReservation>    itemReservationEnumerator           = itemReservations.GetEnumerator();
                IEnumerator <ItemUnitConversion> itemUnitConversionEnumerator        = itemUnitConversions.GetEnumerator();

                while (itemReservationEnumerator.MoveNext() && itemUnitConversionEnumerator.MoveNext())
                {
                    ItemUnitConversion itemUnitConversion = itemUnitConversionEnumerator.Current;
                    if (!itemUnitConversion.IsNop)
                    {
                        UnitOfMeasureConversion unitOfMeasureConversion;
                        if (conversions.TryGetValue(itemUnitConversion, out unitOfMeasureConversion))
                        {
                            ItemReservation itemReservation = itemReservationEnumerator.Current;
                            itemReservation.Quantity = unitOfMeasureConversion.Convert(itemReservation.Quantity);
                        }
                        else
                        {
                            DataValidationFailure validationFailure = new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_UnitOfMeasureConversionNotFound);
                            validationFailure.ErrorContext = string.Format(CultureInfo.InvariantCulture, "No conversion rules defined for ItemUnitConversion:{0}", itemUnitConversion);
                            validationFailures.Add(validationFailure);
                        }
                    }
                }

                if (validationFailures.Count > 0)
                {
                    throw new DataValidationException(
                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_AggregateValidationError,
                              validationFailures,
                              string.Format("There are {0} item reservations whose unit of measures cannot be converted.", validationFailures.Count));
                }
            }
예제 #4
0
            /// <summary>
            /// Calculate the tax bases calculationBase and limitBase (which is zero for India).
            /// </summary>
            /// <param name="codes">The tax codes.</param>
            /// <param name="taxInStoreCurrency">If set to <c>true</c> [tax in store currency].</param>
            /// <param name="calculateBasePrice">If set to <c>true</c> [Calculate the base price].</param>
            /// <returns>The calculation base as Item1 and The limit base as Item2 in Tuple.</returns>
            protected override Tuple <decimal, decimal> GetBases(ReadOnlyCollection <TaxCode> codes, bool taxInStoreCurrency, bool calculateBasePrice)
            {
                const decimal LimitBase = decimal.Zero;
                decimal       calculationBase;

                // For amount by unit calculation base is just the quantity.
                if (this.TaxBase == TaxBase.AmountByUnit)
                {
                    calculationBase = this.TaxableEntity.Quantity;

                    // If the tax is calculated in a different UOM, then convert if possible
                    // this is only applicable for lineItem taxes.
                    SalesLine salesLine = this.TaxableEntity as SalesLine;

                    if (salesLine != null && !string.Equals(this.Unit, this.TaxableEntity.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase))
                    {
                        ItemUnitConversion conversion = new ItemUnitConversion
                        {
                            FromUnitOfMeasure = this.TaxableEntity.SalesOrderUnitOfMeasure,
                            ToUnitOfMeasure   = this.Unit,
                            ItemId            = this.TaxableEntity.ItemId
                        };

                        var conversions = new List <ItemUnitConversion>();
                        conversions.Add(conversion);

                        var getUomConvertionDataRequest   = new GetUnitOfMeasureConversionDataRequest(conversions, QueryResultSettings.SingleRecord);
                        UnitOfMeasureConversion converter = this.RequestContext.Runtime
                                                            .Execute <GetUnitOfMeasureConversionDataResponse>(getUomConvertionDataRequest, this.RequestContext).UnitConversions.SingleOrDefault();

                        calculationBase *= converter.GetFactorForQuantity(this.TaxableEntity.Quantity);
                    }

                    return(new Tuple <decimal, decimal>(calculationBase, LimitBase));
                }

                // Determine the starting calculation base (includes the line price or not)
                switch (this.Formula.TaxableBasis)
                {
                case TaxableBasisIndia.LineAmount:
                    calculationBase = this.TaxableEntity.NetAmountWithAllInclusiveTaxPerUnit;
                    break;

                case TaxableBasisIndia.MaxRetailPrice:
                    calculationBase = this.GetItemMaxRetailPrice();
                    break;

                default:
                    calculationBase = decimal.Zero;
                    break;
                }

                if (this.TaxIncludedInPrice)
                {
                    calculationBase = GetBasePriceForTaxIncluded(calculationBase, codes, this.Formula, this.RequestContext);
                }

                calculationBase *= Math.Abs(this.TaxableEntity.Quantity);

                // Calculation expression is of the form: +[BCD]+[CVD]+[E-CESS_CVD]+[PE-C_CVD]+[SHE-C_CVD]
                // where the brackets are replaced with the delimiter char(164)
                // and BCD, CVD ... are tax codes.
                // The operator may be + - / *.
                string[] tokens = this.Formula.ParseExpression();

                for (int index = 1; index < tokens.Length; index += 2)
                {
                    TaxLine taxLine = (from line in this.TaxableEntity.TaxLines
                                       where line.TaxCode == tokens[index]
                                       select line).FirstOrDefault();

                    if (taxLine != null)
                    {
                        this.IsTaxOnTax = true;
                        if (!this.taxCodesInFormula.Contains(taxLine.TaxCode))
                        {
                            this.taxCodesInFormula.Add(taxLine.TaxCode);
                        }
                    }

                    decimal amount      = taxLine == null ? decimal.Zero : taxLine.Amount * Math.Sign(this.TaxableEntity.Quantity);
                    int     tokenNumber = index - 1;

                    switch (tokens[tokenNumber])
                    {
                    case "+":
                        calculationBase += amount;
                        break;

                    case "-":
                        calculationBase -= amount;
                        break;

                    case "*":
                        calculationBase *= amount;
                        break;

                    case "/":
                        calculationBase = amount == decimal.Zero ? calculationBase : calculationBase /= amount;
                        break;

                    default:
                        RetailLogger.Log.CrtServicesTaxCodeIndiaTaxServiceInvalidOperatorFoundInBaseCalculation(tokens[tokenNumber]);
                        break;
                    }
                }

                // Knock any abatement off of the taxable basis
                calculationBase *= (100 - this.AbatementPercent) / 100;
                return(new Tuple <decimal, decimal>(calculationBase, LimitBase));
            }
예제 #5
0
            /// <summary>
            /// Gets the bases for tax calculations.
            /// </summary>
            /// <param name="codes">The collection of tax codes.</param>
            /// <param name="taxInStoreCurrency">If set to <c>true</c>, tax amount is in the store's currency.</param>
            /// <param name="calculateBasePrice">If set to <c>true</c>, calculate the base price.</param>
            /// <returns>The calculation base and The limit base in <see cref="Tuple">Tuple</see>.</returns>
            protected virtual Tuple <decimal, decimal> GetBases(
                ReadOnlyCollection <TaxCode> codes,
                bool taxInStoreCurrency,
                bool calculateBasePrice)
            {
                decimal calculationBase;
                decimal limitBase;
                decimal basePrice = decimal.Zero;

                if (calculateBasePrice)
                {
                    basePrice = this.TaxIncludedInPrice ?
                                TaxCodeProvider.GetBasePriceForTaxIncluded(this.TaxableEntity, codes, this.TaxContext) :
                                this.TaxableEntity.NetAmountPerUnit + GetApplicableDutyTaxes(this.TaxableEntity, codes);
                }

                // 1. Get initial value for the Calculation Base
                switch (this.TaxBase)
                {
                case TaxBase.PercentPerTax:
                    // Base is the amount of the other tax
                    switch (this.TaxLimitBase)
                    {
                    case TaxLimitBase.InvoiceWithoutVat:
                    case TaxLimitBase.InvoiceWithVat:
                        calculationBase = Math.Abs(this.CalculateTaxOnTax(this.Transaction));
                        break;

                    case TaxLimitBase.UnitWithoutVat:
                    case TaxLimitBase.UnitWithVat:
                        // if this tax's Limit is per-unit, then we need to convert the existing tax amounts from per-line to per-unit
                        decimal quantity = (this.TaxableEntity.Quantity == decimal.Zero) ? decimal.One : this.TaxableEntity.Quantity;
                        calculationBase = Math.Abs(this.CalculateTaxOnTax()) / Math.Abs(quantity);
                        break;

                    default:
                        calculationBase = Math.Abs(this.CalculateTaxOnTax());
                        break;
                    }

                    break;

                case TaxBase.PercentPerGross:
                    // Base is the price + other taxes
                    calculationBase = basePrice;

                    // If the Limit base is NOT per-unit, then we need to factor in the line quanity
                    if (TaxLimitBase != TaxLimitBase.UnitWithoutVat && TaxLimitBase != TaxLimitBase.UnitWithVat)
                    {
                        calculationBase *= Math.Abs(this.TaxableEntity.Quantity);
                    }

                    if (!string.IsNullOrEmpty(this.TaxOnTax))
                    {
                        // Base is the Price + the amount of a single other tax
                        calculationBase += Math.Abs(this.CalculateTaxOnTax());
                    }
                    else
                    {
                        // Base is the Price + all other taxes
                        calculationBase += Math.Abs(TaxCode.SumAllTaxAmounts(this.TaxableEntity));
                    }

                    break;

                case TaxBase.AmountByUnit:
                    calculationBase = this.AmountPerUnitCalculationBase;
                    break;

                case TaxBase.PercentPerNet:
                case TaxBase.PercentGrossOnNet:
                default:
                    // Base is the Price
                    calculationBase = basePrice;

                    // If the Limit base is NOT per-unit, then we need to factor in the line quanity
                    if (TaxLimitBase != TaxLimitBase.UnitWithoutVat && TaxLimitBase != TaxLimitBase.UnitWithVat)
                    {
                        calculationBase *= Math.Abs(this.TaxableEntity.Quantity);
                    }

                    break;
                }

                // 3. Set Limit Base
                if (this.TaxBase == TaxBase.AmountByUnit)
                {
                    // Base for limits/intervals is base-quantity * price
                    limitBase = calculationBase * basePrice;

                    // Convert limit base to Tax currency, if different
                    if (!taxInStoreCurrency)
                    {
                        limitBase = this.TaxContext.TaxCurrencyOperations.ConvertCurrency(this.TaxContext.ChannelCurrency, this.Currency, limitBase);
                    }

                    // If the tax is calculated in a different UOM, then convert if possible
                    // this is only applicable for lineItem taxes.
                    SalesLine salesLine = this.TaxableEntity as SalesLine;

                    if (salesLine != null && !string.Equals(this.Unit, this.TaxableEntity.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase))
                    {
                        ItemUnitConversion conversion = new ItemUnitConversion
                        {
                            FromUnitOfMeasure = this.TaxableEntity.SalesOrderUnitOfMeasure,
                            ToUnitOfMeasure   = this.Unit,
                            ItemId            = this.TaxableEntity.ItemId
                        };

                        var conversions = new List <ItemUnitConversion>();
                        conversions.Add(conversion);

                        var getUomConvertionDataRequest   = new GetUnitOfMeasureConversionDataRequest(conversions, QueryResultSettings.SingleRecord);
                        UnitOfMeasureConversion converter = this.RequestContext.Runtime
                                                            .Execute <GetUnitOfMeasureConversionDataResponse>(getUomConvertionDataRequest, this.RequestContext).UnitConversions.SingleOrDefault();

                        calculationBase *= converter.GetFactorForQuantity(this.TaxableEntity.Quantity);
                    }
                }
                else
                {
                    // Convert base to Tax currency, if different
                    if (!taxInStoreCurrency)
                    {
                        calculationBase = this.TaxContext.TaxCurrencyOperations.ConvertCurrency(this.TaxContext.ChannelCurrency, this.Currency, calculationBase);
                    }

                    // Base for limits/intervals is same for Calculations
                    limitBase = calculationBase;
                }

                return(new Tuple <decimal, decimal>(calculationBase, limitBase));
            }