예제 #1
0
            private void AddTaxCode(RequestContext context, TaxableItem taxableItem, TaxCodeInterval taxCodeInterval, Dictionary <string, TaxCode> codes, SalesTransaction transaction)
            {
                TaxCode code = this.GetTaxCode(context, taxableItem, taxCodeInterval, transaction);

                codes.Add(code.Code, code);
                this.transactionTaxCodes.Add(code);
            }
예제 #2
0
 /// <summary>
 /// Calculates the tax amount properties on this taxable item.
 /// </summary>
 /// <param name="taxableItem">The taxable item.</param>
 private static void CalculateTax(TaxableItem taxableItem)
 {
     taxableItem.TaxAmount                = taxableItem.TaxLines.Sum(t => t.IsExempt ? decimal.Zero : t.Amount);
     taxableItem.TaxAmountExclusive       = taxableItem.TaxLines.Sum(t => (t.IsExempt || t.IsIncludedInPrice) ? decimal.Zero : t.Amount);
     taxableItem.TaxAmountInclusive       = taxableItem.TaxLines.Sum(t => (t.IsExempt || !t.IsIncludedInPrice) ? decimal.Zero : t.Amount);
     taxableItem.TaxAmountExemptInclusive = taxableItem.TaxLines.Sum(t => (t.IsExempt && t.IsIncludedInPrice) ? t.Amount : decimal.Zero);
 }
예제 #3
0
 internal static void AddToTaxItems(SalesTransaction salesTransaction, TaxableItem taxableItem)
 {
     // If a taxgroup has been assigned to the saleItem
     if (taxableItem.ItemTaxGroupId != null)
     {
         AddToTaxItems(salesTransaction, taxableItem.TaxLines);
     }
 }
예제 #4
0
            /// <summary>
            /// Gets the tax code.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="taxCodeInterval">The tax code interval.</param>
            /// <param name="transaction">Current transaction.</param>
            /// <returns>The tax code object.</returns>
            protected virtual TaxCode GetTaxCode(RequestContext context, TaxableItem taxableItem, TaxCodeInterval taxCodeInterval, SalesTransaction transaction)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                return(new TaxCode(context, taxableItem, taxCodeInterval, this.TaxContext, transaction));
            }
예제 #5
0
            protected virtual ReadOnlyCollection <TaxCode> GetTaxCodes(TaxableItem taxableItem, RequestContext context, SalesTransaction transaction)
            {
                ThrowIf.Null(taxableItem, "taxableItem");
                ThrowIf.Null(context, "context");

                try
                {
                    Dictionary <string, TaxCode> codes = new Dictionary <string, TaxCode>();

                    // If the line has an EndDate specified (usually because it's a Returned line),
                    // then use that value to calculate taxes, otherwise use BeginDate
                    TaxDateAndGroups key = new TaxDateAndGroups(this.GetTaxDate(taxableItem), taxableItem.SalesTaxGroupId, taxableItem.ItemTaxGroupId);

                    ReadOnlyCollection <TaxCodeInterval> taxCodeIntervals = null;
                    if (key.IsNoTax)
                    {
                        taxCodeIntervals = new List <TaxCodeInterval>().AsReadOnly();
                    }
                    else if (!this.TaxContext.TaxCodeInternalsLookup.TryGetValue(key, out taxCodeIntervals))
                    {
                        // Shouldn't get here.
                        taxCodeIntervals = this.GetTaxCodeIntervals(context, taxableItem.SalesTaxGroupId, taxableItem.ItemTaxGroupId, key.TaxDate);
                    }

                    foreach (TaxCodeInterval taxCodeInterval in taxCodeIntervals)
                    {
                        if (codes.ContainsKey(taxCodeInterval.TaxCode))
                        {
                            // Add a new 'value' entry for an existing tax code
                            var taxInterval = new TaxInterval(taxCodeInterval.TaxLimitMinimum, taxCodeInterval.TaxLimitMaximum, taxCodeInterval.TaxValue);
                            codes[taxCodeInterval.TaxCode].TaxIntervals.Add(taxInterval);
                        }
                        else
                        {
                            this.AddTaxCode(context, taxableItem, taxCodeInterval, codes, transaction);
                        }
                    }

                    // Link any taxes which rely on other taxes
                    foreach (TaxCode tax in codes.Values)
                    {
                        if (!string.IsNullOrEmpty(tax.TaxOnTax) &&
                            (tax.TaxBase == TaxBase.PercentPerTax || tax.TaxBase == TaxBase.PercentPerGross) &&
                            codes.Keys.Contains(tax.TaxOnTax))
                        {
                            tax.TaxOnTaxInstance = codes[tax.TaxOnTax];
                        }
                    }

                    return(this.SortCodes(codes));
                }
                catch (Exception ex)
                {
                    RetailLogger.Log.CrtServicesTaxCodeProviderTaxServiceGetTaxCodesFailure(ex);
                    throw;
                }
            }
예제 #6
0
            private DateTimeOffset GetTaxDate(TaxableItem taxableItem)
            {
                DateTimeOffset taxDate = (taxableItem.EndDateTime <= NoDate) ? taxableItem.BeginDateTime : taxableItem.EndDateTime;

                if (taxDate == DateTimeOffset.MinValue)
                {
                    taxDate = this.TaxContext.NowInChannelTimeZone;
                }

                return(taxDate);
            }
            /// <summary>
            /// Retrieves a list of TaxCodes for the given sale line item.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="context">The context.</param>
            /// <param name="transaction">Current transaction.</param>
            /// <returns>Tax codes applicable with the taxableItem.</returns>
            /// <remarks>
            /// No user input or variable in the SQL query. No SQL injection threat.
            /// </remarks>
            protected override ReadOnlyCollection <TaxCode> GetTaxCodes(TaxableItem taxableItem, RequestContext context, SalesTransaction transaction)
            {
                // Only use codes that are not processed as India tax codes, or those that are and are
                // supported.
                return(new ReadOnlyCollection <TaxCode>(base.GetTaxCodes(taxableItem, context, transaction).Where(c =>
                {
                    var codeIndia = c as TaxCodeIndia;

                    bool isChargeLine = taxableItem is ChargeLine;
                    return codeIndia == null || SupportedTax(isChargeLine, codeIndia, context);
                }).ToList <TaxCode>()));
            }
예제 #8
0
            /// <summary>
            /// Calculates the tax amount properties on this taxable item.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            public static void CalculateTax(TaxableItem taxableItem)
            {
                if (taxableItem == null)
                {
                    throw new ArgumentNullException("taxableItem");
                }

                taxableItem.TaxAmount                = taxableItem.TaxLines.Sum(t => t.IsExempt ? decimal.Zero : t.Amount);
                taxableItem.TaxAmountExclusive       = taxableItem.TaxLines.Sum(t => (t.IsExempt || t.IsIncludedInPrice) ? decimal.Zero : t.Amount);
                taxableItem.TaxAmountInclusive       = taxableItem.TaxLines.Sum(t => (t.IsExempt || !t.IsIncludedInPrice) ? decimal.Zero : t.Amount);
                taxableItem.TaxAmountExemptInclusive = taxableItem.TaxLines.Sum(t => (t.IsExempt && t.IsIncludedInPrice) ? t.Amount : decimal.Zero);
            }
예제 #9
0
            /// <summary>
            /// Gets the duty taxes.
            /// </summary>
            /// <param name="lineItem">The line item.</param>
            /// <param name="codes">The codes.</param>
            /// <returns>Sum of duty taxes applicable on the line.</returns>
            private static decimal GetApplicableDutyTaxes(TaxableItem lineItem, IEnumerable <TaxCode> codes)
            {
                decimal dutyTaxSum = 0;

                if (lineItem != null && !codes.IsNullOrEmpty())
                {
                    var dutyTaxes = codes.Where(c => c.IsTaxIncludedInTax);
                    dutyTaxSum = dutyTaxes.Sum(dt => dt.Value * Math.Abs(lineItem.Quantity));
                }

                return(dutyTaxSum);
            }
예제 #10
0
            /// <summary>
            /// Sets the line item tax rate.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="taxAmount">The tax amount.</param>
            protected static void SetLineItemTaxRate(TaxableItem taxableItem, decimal taxAmount)
            {
                if (taxableItem == null)
                {
                    throw new ArgumentNullException("taxableItem");
                }

                decimal extendedPrice = taxableItem.Price * taxableItem.Quantity;

                if (extendedPrice == decimal.Zero)
                {
                    extendedPrice = 1;
                }

                taxableItem.TaxRatePercent += (taxAmount * 100) / extendedPrice;
            }
예제 #11
0
            /// <summary>
            /// Simple version of TaxIncluded algorithm for tax code collections that are not based on:
            /// intervals, limits, collection limits and total invoice.
            /// </summary>
            /// <param name="lineItem">The taxable item.</param>
            /// <param name="codes">The collection of tax codes.</param>
            /// <returns>The base price.</returns>
            private static decimal GetBasePriceSimpleTaxIncluded(TaxableItem lineItem, ReadOnlyCollection <TaxCode> codes)
            {
                // accumulation of % based tax
                decimal fullLineTaxRate = decimal.Zero;

                // accumulation of amount based tax
                decimal fullLineUnitTax      = decimal.Zero;
                decimal nonExemptLineUnitTax = decimal.Zero;

                // 1. Determine sum of all AmountByUnit taxes (ref: AX\Classes\Tax.AmountExclTax() - line 222)
                decimal codeValue = decimal.Zero;

                // Reference dev item 5747
                foreach (TaxCode code in codes.Where(c => c.TaxBase == TaxBase.AmountByUnit))
                {
                    codeValue             = code.Calculate(codes, false); // Amount by units don't depend on basePrice
                    fullLineUnitTax      += codeValue;
                    nonExemptLineUnitTax += code.Exempt ? decimal.Zero : codeValue;
                }

                // 2. Determine sum of all tax rates for non-AmountByUnit taxes (ref: AX\Classes\Tax.AmountExclTax() - line 331)
                foreach (TaxCode code in codes.Where(c => c.TaxBase != TaxBase.AmountByUnit))
                {
                    if (code.TaxBase == TaxBase.PercentPerGross && string.IsNullOrEmpty(code.TaxOnTax))
                    {
                        // Sum all OTHER taxes...
                        codeValue = codes.Sum(c => (c.TaxBase == TaxBase.AmountByUnit) ? decimal.Zero : c.PercentPerTax());

                        // And then apply the Gross tax on top of that
                        codeValue *= code.PercentPerTax() / 100;

                        // Add this rate to the running total.
                        fullLineTaxRate += codeValue;
                    }
                    else
                    {
                        // Add this rate to the running total.
                        codeValue        = code.PercentPerTax();
                        fullLineTaxRate += codeValue;
                    }
                }

                // 3. Back calculate the Price based on tax rates, start with the Price that includes ALL taxes
                decimal taxBase = lineItem.NetAmountWithAllInclusiveTaxPerUnit - fullLineUnitTax;

                return((taxBase * 100) / (100 + fullLineTaxRate));
            }
예제 #12
0
            /// <summary>
            /// Sets the line item tax rate.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="lineTaxResult">The line tax result.</param>
            protected static void SetLineItemTaxRate(TaxableItem taxableItem, LineTaxResult lineTaxResult)
            {
                if (taxableItem == null)
                {
                    throw new ArgumentNullException("taxableItem");
                }

                if (lineTaxResult == null)
                {
                    throw new ArgumentNullException("lineTaxResult");
                }

                // Ignore any portion of the TaxAmount that is 'Exempt' when computing the rate.
                decimal amount = lineTaxResult.TaxAmount - lineTaxResult.ExemptAmount;

                SetLineItemTaxRate(taxableItem, amount);
            }
예제 #13
0
            /// <summary>
            /// Gets the tax code.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="taxCodeInterval">The tax code interval.</param>
            /// <param name="transaction">Current transaction.</param>
            /// <returns>The tax code object.</returns>
            protected override TaxCode GetTaxCode(RequestContext context, TaxableItem taxableItem, TaxCodeInterval taxCodeInterval, SalesTransaction transaction)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                TaxCodeIntervalIndia taxCodeIntervalIndia = taxCodeInterval as TaxCodeIntervalIndia;

                if (taxCodeIntervalIndia.TaxType == TaxTypeIndia.None)
                {
                    return(base.GetTaxCode(context, taxableItem, taxCodeInterval, transaction));
                }
                else
                {
                    return(new TaxCodeIndia(context, taxableItem, taxCodeIntervalIndia, this.TaxContext, transaction));
                }
            }
예제 #14
0
            /// <summary>
            /// Initializes a new instance of the <see cref="TaxCodeIndia"/> class.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="taxableItem">The taxable line item.</param>
            /// <param name="taxCodeInterval">The tax code interval.</param>
            /// <param name="taxContext">Tax context.</param>
            /// <param name="transaction">Current transaction.</param>
            public TaxCodeIndia(
                RequestContext context, TaxableItem taxableItem, TaxCodeIntervalIndia taxCodeInterval, TaxContext taxContext, SalesTransaction transaction)
                : base(context, taxableItem, taxCodeInterval, taxContext, transaction)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                if (taxCodeInterval == null)
                {
                    throw new ArgumentNullException("taxCodeInterval");
                }

                this.TaxType           = taxCodeInterval.TaxType;
                this.AbatementPercent  = taxCodeInterval.AbatementPercent;
                this.taxCodesInFormula = new List <string>();
            }
예제 #15
0
            /// <summary>
            /// Gets the base price for tax included.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="codes">The codes.</param>
            /// <param name="taxContext">The tax context.</param>
            /// <returns>The base price for tax included.</returns>
            public static decimal GetBasePriceForTaxIncluded(TaxableItem taxableItem, ReadOnlyCollection <TaxCode> codes, TaxContext taxContext)
            {
                // check to see if we can do the 'simple' Inclusive algorithm
                bool simpleBasis = codes.All(c =>
                                             (c.TaxBase == TaxBase.PercentPerNet || c.TaxBase == TaxBase.PercentGrossOnNet) &&
                                             (c.TaxLimitMin == decimal.Zero && c.TaxLimitMax == decimal.Zero));
                bool collectLimits      = codes.Any(c => (c.CollectLimitMax != decimal.Zero || c.CollectLimitMin != decimal.Zero));
                bool multiplePercentage = codes.Any(c => (c.TaxIntervals.Count > 1));

                if (simpleBasis && !collectLimits && !multiplePercentage)
                {
                    // Get base price for Simple TaxInclusive calculation
                    return(GetBasePriceSimpleTaxIncluded(taxableItem, codes));
                }
                else
                {
                    // Get base price for Full TaxInclusive calculation
                    return(GetBasePriceAdvancedTaxIncluded(taxableItem, codes, collectLimits, taxContext));
                }
            }
예제 #16
0
            /// <summary>
            /// Initializes a new instance of the <see cref="TaxCode"/> class.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="lineItem">The taxable line item.</param>
            /// <param name="interval">The tax code interval.</param>
            /// <param name="taxContext">Tax context.</param>
            /// <param name="transaction">Current transaction.</param>
            public TaxCode(RequestContext context, TaxableItem lineItem, TaxCodeInterval interval, TaxContext taxContext, SalesTransaction transaction)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                if (interval == null)
                {
                    throw new ArgumentNullException("interval");
                }

                this.Code                 = interval.TaxCode;
                this.TaxableEntity        = lineItem;
                this.TaxGroup             = interval.TaxItemGroup;
                this.Currency             = interval.TaxCurrencyCode;
                this.Exempt               = interval.IsTaxExempt;
                this.TaxBase              = (TaxBase)interval.TaxBase;
                this.TaxLimitBase         = (TaxLimitBase)interval.TaxLimitBase;
                this.TaxCalculationMethod = (TaxCalculationMode)interval.TaxCalculationMethod;
                this.TaxOnTax             = interval.TaxOnTax;
                this.Unit                 = interval.TaxUnit;
                this.RoundingOff          = interval.TaxRoundOff;
                this.RoundingOffType      = Rounding.ConvertRoundOffTypeToRoundingMethod(interval.TaxRoundOffType);
                this.CollectLimitMax      = interval.TaxMaximum;
                this.CollectLimitMin      = interval.TaxMinimum;
                this.TaxGroupRounding     = interval.IsGroupRounding;
                this.IsTaxIncludedInTax   = interval.IsTaxIncludedInTax;

                this.TaxIntervals = new Collection <TaxInterval>(new List <TaxInterval>(1));

                // should this be removed in favor of intervals?
                this.Value          = interval.TaxValue;
                this.TaxLimitMin    = interval.TaxLimitMinimum;
                this.TaxLimitMax    = interval.TaxLimitMaximum;
                this.RequestContext = context;
                this.Transaction    = transaction;
                this.TaxIntervals.Add(new TaxInterval(interval.TaxLimitMinimum, interval.TaxLimitMaximum, interval.TaxValue));

                this.TaxContext = taxContext;
            }
예제 #17
0
            /// <summary>
            /// Calculate tax on the given line item.
            /// </summary>
            /// <param name="taxableItem">The taxable item.</param>
            /// <param name="context">The context.</param>
            /// <param name="transaction">Current transaction.</param>
            private void CalculateTax(TaxableItem taxableItem, RequestContext context, SalesTransaction transaction)
            {
                ReadOnlyCollection <TaxCode> codes = this.GetTaxCodes(taxableItem, context, transaction);

                LineTaxResult lineTaxResult = new LineTaxResult
                {
                    HasExempt      = false,
                    TaxRatePercent = decimal.Zero,
                    TaxAmount      = decimal.Zero,
                    ExemptAmount   = decimal.Zero
                };

                foreach (TaxCode code in codes)
                {
                    var taxCodeAmount = code.CalculateTaxAmount(codes, this.taxCodeAmountRounder);
                    lineTaxResult.TaxAmount += taxCodeAmount;

                    // sum up the amounts that are exempt
                    if (code.Exempt)
                    {
                        lineTaxResult.HasExempt     = true;
                        lineTaxResult.ExemptAmount += lineTaxResult.TaxAmount;
                    }
                }

                // Set the 'virtual tax rate', if extended price is ZERO, then just add the full amount
                decimal extendedPrice = taxableItem.Price * Math.Abs(taxableItem.Quantity);

                if (extendedPrice == decimal.Zero)
                {
                    extendedPrice = decimal.One;
                }

                lineTaxResult.TaxRatePercent = (lineTaxResult.TaxAmount * 100) / extendedPrice;
                SetLineItemTaxRate(taxableItem, lineTaxResult);
            }
예제 #18
0
        private void TaxItems(ITaxSchedule schedule, List <ITaxable> items, IAddress address)
        {
            if (schedule == null)
            {
                return;
            }
            if (items == null)
            {
                return;
            }
            if (address == null)
            {
                return;
            }

            // Holds all taxes for the schedule summarized
            TaxableItem summaryForSchedule = new TaxableItem();

            summaryForSchedule.ScheduleId = schedule.TaxScheduleId();

            // Find all items that match the schedule
            List <ITaxable> itemsMatchingSchedule = new List <ITaxable>();

            foreach (ITaxable item in items)
            {
                if (item.TaxScheduleId() == schedule.TaxScheduleId())
                {
                    summaryForSchedule.Value         += item.TaxableValue();
                    summaryForSchedule.ShippingValue += item.TaxableShippingValue();
                    itemsMatchingSchedule.Add(item);
                }
            }

            // Now Apply all taxes in schedule to total price of all items
            foreach (ITaxRate rate in _app.OrderServices.Taxes.GetRates(_app.CurrentStore.Id, schedule.TaxScheduleId()))
            {
                rate.TaxItem(summaryForSchedule, address);
            }

            // Total Tax for all items on this schedule is calculated
            // Now, we assign the tax parts to each line item based on their
            // linetotal value. The last item should get the remainder of the tax
            decimal RoundedTotal = Math.Round(summaryForSchedule.TaxedValue, 2);

            decimal TotalApplied = 0M;

            for (int i = 0; i < itemsMatchingSchedule.Count(); i++)
            {
                ITaxable item = itemsMatchingSchedule[i];

                item.ClearTaxValue();
                if (i == itemsMatchingSchedule.Count() - 1)
                {
                    // last item
                    item.IncrementTaxValue(RoundedTotal - TotalApplied);
                }
                else
                {
                    decimal percentOfTotal = 0;
                    if (summaryForSchedule.TaxableValue() != 0)
                    {
                        percentOfTotal = item.TaxableValue() / summaryForSchedule.TaxableValue();
                    }
                    decimal part = Math.Round(percentOfTotal * summaryForSchedule.TaxedValue, 2);
                    item.IncrementTaxValue(part);
                    TotalApplied += part;
                }
            }
        }
예제 #19
0
            private static decimal GetBasePriceAdvancedTaxIncluded(
                TaxableItem taxableItem, ReadOnlyCollection <TaxCode> codes, bool collectLimits, TaxContext taxContext)
            {
                // accumulation of amount based tax
                decimal fullLineUnitTax      = decimal.Zero;
                decimal nonExemptLineUnitTax = decimal.Zero;
                decimal codeValue            = decimal.Zero;

                // AX variables
                decimal endAmount = taxableItem.NetAmountWithAllInclusiveTaxPerUnit; // endAmount will be the final price w/o tax
                int     sign      = 1;

                // 3.
                decimal taxLimitMax = decimal.Zero;
                decimal taxLimitMin = decimal.Zero;
                decimal startAmount = decimal.Zero;

                // 3a...
                decimal taxCalc = decimal.Zero;
                decimal baseCur;

                // Tax Amount deducted for a given Code
                Dictionary <string, decimal> deductedTax = new Dictionary <string, decimal>();

                // 3b ...
                decimal percentTotal;
                decimal tmpBase;

                // 3c..
                // Whether or not a Code needs to be removed from the sum of percent rates
                Dictionary <string, bool> removePercent = new Dictionary <string, bool>();

                // 3d.
                decimal totalTax = decimal.Zero;

                // Whether or not the Code needs to be calculated
                Dictionary <string, bool> calcTax = new Dictionary <string, bool>();

                string storeCurrency = taxContext.ChannelCurrency;

                // Begin Tax included calculation
                // 0. Initialize the supporting collections
                foreach (TaxCode code in codes)
                {
                    deductedTax[code.Code]   = decimal.Zero;
                    removePercent[code.Code] = false;
                    calcTax[code.Code]       = true;
                }

                // 1. Remove all AmountByUnit taxes
                foreach (TaxCode code in codes.Where(c => c.TaxBase == TaxBase.AmountByUnit))
                {
                    codeValue             = code.Calculate(codes, false); // Reference dev item 5748.
                    fullLineUnitTax      += codeValue;
                    nonExemptLineUnitTax += code.Exempt ? decimal.Zero : codeValue;
                    calcTax[code.Code]    = false;
                }

                endAmount -= Math.Abs(nonExemptLineUnitTax);

                // 2. Record the sign, and then continue using the magnitude of endAmount
                sign      = (endAmount < decimal.Zero) ? -1 : 1;
                endAmount = Math.Abs(endAmount);

                // 3.
                while (startAmount < endAmount)
                {
                    // 3a Consider interval limits
                    taxCalc     = decimal.Zero;
                    taxLimitMax = decimal.Zero;

                    foreach (TaxCode code in codes)
                    {
                        if (code.TaxCalculationMethod == TaxCalculationMode.FullAmounts)
                        {
                            taxLimitMax = decimal.Zero;
                        }
                        else
                        {
                            if (code.IsStoreCurrency)
                            {
                                baseCur = taxContext.TaxCurrencyOperations.ConvertCurrency(storeCurrency, code.Currency, taxLimitMin);
                            }
                            else
                            {
                                baseCur = taxLimitMin;
                            }

                            baseCur += 1;

                            // if 'baseCur' falls into an interval
                            if (code.TaxIntervals.Exists(baseCur))
                            {
                                // get the Upper limit of the interval that 'baseCur'/'taxLimitMin' falls into
                                decimal amount = code.TaxIntervals.Find(taxLimitMin + 1).TaxLimitMax;
                                taxLimitMax = (amount != decimal.Zero && amount < endAmount) ? amount : endAmount;
                            }
                        }

                        taxCalc += deductedTax[code.Code];
                    }

                    // 3b. Sum up all the Tax Percentage Rates
                    percentTotal = 0;
                    tmpBase      = (taxLimitMax > decimal.Zero) ? taxLimitMax : endAmount;

                    foreach (TaxCode code in codes.Where(c => calcTax[c.Code]))
                    {
                        percentTotal += GetPercentPerTax(code, tmpBase, codes);
                    }

                    decimal taxMax;
                    decimal baseInclTax;
                    decimal baseExclTax;

                    // 3c.
                    // if this is the last interval??
                    if (taxLimitMax == decimal.Zero)
                    {
                        // Forward calculate taxes to see if we exceed the CollectLimit
                        foreach (TaxCode code in codes.Where(c => calcTax[c.Code]))
                        {
                            taxMax      = code.CollectLimitMax;
                            baseInclTax = endAmount - taxLimitMin - taxCalc;
                            baseExclTax = baseInclTax * 100 / (100 + percentTotal);

                            if (taxMax != decimal.Zero)
                            {
                                tmpBase = endAmount;

                                decimal percent = GetPercentPerTax(code, tmpBase, codes);

                                if ((deductedTax[code.Code] + (baseExclTax * percent / 100)) > taxMax)
                                {
                                    deductedTax[code.Code]   = taxMax;
                                    removePercent[code.Code] = true;
                                }
                            }
                        }

                        // 3d.
                        // Now remove any rates that exceed their LimitMax
                        foreach (TaxCode code in codes)
                        {
                            if (removePercent[code.Code] && calcTax[code.Code])
                            {
                                tmpBase            = endAmount;
                                percentTotal      -= GetPercentPerTax(code, tmpBase, codes);
                                calcTax[code.Code] = false;
                            }

                            taxCalc += deductedTax[code.Code];
                        }
                    }

                    // 4. Compute tax adjusted for limits
                    totalTax = decimal.Zero;
                    foreach (TaxCode code in codes.Where(c => c.TaxBase != TaxBase.AmountByUnit))
                    {
                        if (calcTax[code.Code])
                        {
                            tmpBase = (taxLimitMax > decimal.Zero) ? taxLimitMax : endAmount;

                            decimal percent = GetPercentPerTax(code, tmpBase, codes);

                            if (taxLimitMax > decimal.Zero && taxLimitMax < endAmount)
                            {
                                deductedTax[code.Code] += (taxLimitMax - taxLimitMin) * percent / 100;
                            }
                            else
                            {
                                baseInclTax             = endAmount - taxLimitMin - taxCalc;
                                baseExclTax             = baseInclTax * 100 / (100 + percentTotal);
                                deductedTax[code.Code] += baseExclTax * percent / 100;
                            }

                            taxMax = code.CollectLimitMax;

                            if (taxMax > decimal.Zero && deductedTax[code.Code] > taxMax)
                            {
                                deductedTax[code.Code] = taxMax;
                            }
                        }

                        totalTax += deductedTax[code.Code];
                    }

                    if (taxLimitMax > decimal.Zero)
                    {
                        taxLimitMin = taxLimitMax;
                        startAmount = taxLimitMin + totalTax;
                    }
                    else
                    {
                        startAmount = endAmount;
                    }
                } // END if( startAmount < endAmount)

                // 5a. Total up taxes
                foreach (TaxCode code in codes)
                {
                    if (collectLimits && (deductedTax[code.Code] < code.CollectLimitMin))
                    {
                        totalTax -= deductedTax[code.Code];
                        deductedTax[code.Code] = decimal.Zero;
                    }

                    if (code.IsStoreCurrency)
                    {
                        taxCalc = Rounding.RoundToUnit(deductedTax[code.Code], code.RoundingOff, code.RoundingOffType);
                    }
                    else
                    {
                        taxCalc = deductedTax[code.Code];
                    }

                    totalTax += taxCalc - deductedTax[code.Code];
                    deductedTax[code.Code] = taxCalc;
                }

                // 5b. Determine base price
                return((endAmount - totalTax) * sign);
            }
예제 #20
0
            /// <summary>
            /// Return a sum of all the currently applied tax amounts.
            /// </summary>
            /// <param name="lineItem">The line item.</param>
            /// <returns>The summation of all tax amount.</returns>
            private static decimal SumAllTaxAmounts(TaxableItem lineItem)
            {
                decimal allTaxAmounts = lineItem.TaxLines.Sum(t => t.IsExempt ? decimal.Zero : t.Amount);

                return(allTaxAmounts);
            }