/// <summary>
        /// Gets the tax code.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="taxableItem">The taxable item.</param>
        /// <returns>The taxcode object</returns>
        protected override TaxCode GetTaxCode(SqlDataReader reader, ITaxableItem taxableItem)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            if ((TaxTypes)reader["TAXTYPE_IN"] == TaxTypes.None)
            {
                return(base.GetTaxCode(reader, taxableItem));
            }
            else
            {
                return(new TaxCodeIndia(
                           reader["TAXCODE"] as string,
                           taxableItem,
                           reader["TAXITEMGROUP"] as string,
                           reader["TAXCURRENCYCODE"] as string,
                           (decimal)reader["TAXVALUE"],
                           (decimal)reader["TAXLIMITMIN"],
                           (decimal)reader["TAXLIMITMAX"],
                           ((int)reader["EXEMPTTAX"] == 1),
                           (TaxBase)reader["TAXBASE"],
                           (TaxLimitBase)reader["TAXLIMITBASE"],
                           (TaxCalculationMode)reader["TAXCALCMETHOD"],
                           reader["TAXONTAX"] as string,
                           reader["TAXUNIT"] as string,
                           (decimal)reader["TAXMIN"],
                           (decimal)reader["TAXMAX"],
                           (decimal)reader["ABATEMENTPERCENT_IN"],
                           (TaxTypes)reader["TAXTYPE_IN"],
                           this));
            }
        }
Example #2
0
 public TaxCode(
     string code,
     ITaxableItem lineItem,
     string taxGroup,
     string currency,
     decimal value,
     decimal limitMin,
     decimal limitMax,
     bool exempt,
     TaxBase taxBase,
     TaxLimitBase limitBase,
     TaxCalculationMode method,
     string taxOnTax,
     string unit,
     decimal collectMin,
     decimal collectMax,
     bool groupRounding,
     TaxCodeProvider provider)
     : this(value, limitMin, limitMax, provider)
 {
     this.Code                 = code;
     this.LineItem             = lineItem;
     this.TaxGroup             = taxGroup;
     this.Currency             = currency;
     this.Exempt               = exempt;
     this.TaxBase              = taxBase;
     this.TaxLimitBase         = limitBase;
     this.TaxCalculationMethod = method;
     this.TaxOnTax             = taxOnTax;
     this.Unit                 = unit;
     this.CollectLimitMax      = collectMax;
     this.CollectLimitMin      = collectMin;
     this.TaxGroupRounding     = groupRounding;
 }
Example #3
0
        /// <summary>
        /// Gets the tax code.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="taxableItem">The taxable item.</param>
        /// <returns>The taxcode object</returns>
        protected virtual TaxCode GetTaxCode(SqlDataReader reader, ITaxableItem taxableItem)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            return(new TaxCode(
                       reader["TAXCODE"] as string,
                       taxableItem,
                       reader["TAXITEMGROUP"] as string,
                       reader["TAXCURRENCYCODE"] as string,
                       (decimal)reader["TAXVALUE"],
                       (decimal)reader["TAXLIMITMIN"],
                       (decimal)reader["TAXLIMITMAX"],
                       ((int)reader["EXEMPTTAX"] == 1),
                       (TaxBase)reader["TAXBASE"],
                       (TaxLimitBase)reader["TAXLIMITBASE"],
                       (TaxCalculationMode)reader["TAXCALCMETHOD"],
                       reader["TAXONTAX"] as string,
                       reader["TAXUNIT"] as string,
                       (decimal)reader["TAXMIN"],
                       (decimal)reader["TAXMAX"],
                       ((int)reader["TAXGROUPROUNDING"] == 1),
                       this));
        }
Example #4
0
        public void CalculateTax(ITaxableItem taxableItem, IRetailTransaction retailTransaction)
        {
            var codes = GetTaxCodes(taxableItem);

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

            foreach (TaxCode code in codes)
            {
                lineTaxResult.TaxAmount += code.CalculateTaxAmount(codes);

                // 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);
        }
Example #5
0
        protected static void SetLineItemTaxRate(ITaxableItem taxableItem, LineTaxResult lineTaxResult)
        {
            // Ignore any portion of the TaxAmount that is 'Exempt' when computing the rate.
            decimal amount = lineTaxResult.TaxAmount - lineTaxResult.ExemptAmount;

            SetLineItemTaxRate(taxableItem, amount);
        }
Example #6
0
        public override decimal GetBasePriceForTaxIncluded(ITaxableItem taxableItem, ReadOnlyCollection <TaxCode> codes)
        {
            if (taxableItem == null)
            {
                throw new ArgumentNullException("taxableItem");
            }

            // Even though we use TaxIncludedInPrice, we have to adopt NetAmountPerUnit to mimic the Fiscal Printer behavior
            return(taxableItem.NetAmountPerUnit);
        }
        protected override ReadOnlyCollection <TaxCode> GetTaxCodes(ITaxableItem taxableItem)
        {
            // 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).Where(c =>
            {
                var codeIndia = c as TaxCodeIndia;

                return codeIndia == null || SupportedTax(codeIndia);
            }).ToList <TaxCode>()));
        }
Example #8
0
        protected static void SetLineItemTaxRate(ITaxableItem taxableItem, decimal taxAmount)
        {
            decimal extendedPrice = (taxableItem.Price * taxableItem.Quantity);

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

            taxableItem.TaxRatePct += ((taxAmount * 100) / extendedPrice);
        }
Example #9
0
        /// <summary>
        /// Simple version of TaxIncluded algorithm for Tax Code collections that are not based on:
        /// - intervals,
        /// - limits,
        /// - collection limits
        /// - total invoice
        /// </summary>
        /// <param name="lineItem">The taxable item.</param>
        /// <param name="codes">The codes.</param>
        /// <returns>base price</returns>
        private static decimal GetBasePriceSimpleTaxIncluded(
            ITaxableItem 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));
        }
Example #10
0
        /// <summary>
        /// Get MRP of the sales line item.
        /// </summary>
        /// <param name="taxableItem">sales line item</param>
        /// <returns>MRP of the sales line item</returns>
        public static decimal GetMRP(ITaxableItem taxableItem)
        {
            SaleLineItem saleItem = taxableItem as SaleLineItem;

            if (saleItem == null)
            {
                return(decimal.Zero);
            }

            decimal mrp = GetMRPFromTradeAgreement(saleItem);

            if (mrp != decimal.Zero)
            {
                return(mrp);
            }

            return(GetMRPFromItemMaster(saleItem.ItemId));
        }
Example #11
0
        private void AddTaxCode(CacheKey cacheKey, SqlDataReader reader, ITaxableItem taxableItem, Dictionary <string, TaxCode> codes)
        {
            TaxCode code = GetTaxCode(reader, taxableItem);

            codes.Add(code.Code, code);

            if (!taxCodeCache.ContainsKey(cacheKey))
            {
                taxCodeCache[cacheKey] = new List <TaxCode>()
                {
                    code
                }
            }
            ;
            else
            {
                taxCodeCache[cacheKey].Add(code);
            }
        }
Example #12
0
        public virtual decimal GetBasePriceForTaxIncluded(ITaxableItem taxableItem, ReadOnlyCollection <TaxCode> codes)
        {
            // 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));
            }
        }
 public TaxCodeIndia(
     string code,
     ITaxableItem lineItem,
     string taxGroup,
     string currency,
     decimal value,
     decimal limitMin,
     decimal limitMax,
     bool exempt,
     TaxBase taxBase,
     TaxLimitBase limitBase,
     TaxCalculationMode method,
     string taxOnTax,
     string unit,
     decimal collectMin,
     decimal collectMax,
     decimal abatementPercent,
     TaxTypes taxType,
     TaxCodeProvider provider)
     : base(value, limitMin, limitMax, provider)
 {
     this.Code                 = code;
     this.LineItem             = lineItem;
     this.TaxGroup             = taxGroup;
     this.Currency             = currency;
     this.Exempt               = exempt;
     this.TaxBase              = taxBase;
     this.TaxLimitBase         = limitBase;
     this.TaxCalculationMethod = method;
     this.TaxOnTax             = taxOnTax;
     this.Unit                 = unit;
     this.CollectLimitMax      = collectMax;
     this.CollectLimitMin      = collectMin;
     this.AbatementPercent     = abatementPercent;
     this.TaxType              = taxType;
 }
Example #14
0
        /// <summary>
        /// Return a sum of all the currently applied tax amounts
        /// </summary>
        /// <param name="lineItem"></param>
        /// <returns></returns>
        private static decimal SumAllTaxAmounts(ITaxableItem lineItem)
        {
            decimal allTaxAmounts = lineItem.TaxLines.Sum(t => t.Exempt ? decimal.Zero : t.Amount);

            return(allTaxAmounts);
        }
Example #15
0
        private static decimal GetBasePriceAdvancedTaxIncluded(
            ITaxableItem taxableItem, ReadOnlyCollection <TaxCode> codes, bool collectLimits)
        {
            // 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>();

            //
            // 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 -= 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 (IsStoreCurrency(code))
                        {
                            baseCur = TaxService.Tax.InternalApplication.Services.Currency.CurrencyToCurrency(
                                ApplicationSettings.Terminal.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 (IsStoreCurrency(code))
                {
                    taxCalc = TaxService.Tax.InternalApplication.Services.Rounding.TaxRound(
                        deductedTax[code.Code], code.Code);
                }
                else
                {
                    taxCalc = deductedTax[code.Code];
                }

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

            //5b. Determine base price
            return((endAmount - totalTax) * sign);
        }
Example #16
0
        protected virtual ReadOnlyCollection <TaxCode> GetTaxCodes(ITaxableItem taxableItem)
        {
            if (taxableItem == null)
            {
                throw new ArgumentNullException("taxableItem");
            }

            RetailTransaction transaction = (RetailTransaction)taxableItem.RetailTransaction;
            string            customerId  = string.Empty;

            // If the line has an EndDate specified (usually because it's a Returned line),
            // then use that value to calculate taxes, otherwise use BeginDate
            DateTime itemSaleDateTime = (taxableItem.EndDateTime <= NoDate) ? taxableItem.BeginDateTime : taxableItem.EndDateTime;

            if (transaction != null && transaction.Customer != null)
            {
                customerId = transaction.Customer.CustomerId;
            }

            CacheKey cacheKey = new CacheKey(taxableItem.ItemId,
                                             customerId,
                                             taxableItem.TaxGroupId,
                                             taxableItem.SalesTaxGroupId,
                                             itemSaleDateTime);

            if (taxCodeCache.ContainsKey(cacheKey))
            {
                List <TaxCode> taxCodes = taxCodeCache[cacheKey];

                // Update the lineItem object in cached Taxcode object (Everytime we get new SalesLine Object)
                taxCodes.ForEach(t => t.LineItem = taxableItem);

                return(SortCodes(taxCodes));
            }

            NetTracer.Information("TaxCodeProvider::GetTaxCodes(): Quering database.");

            SqlConnection connection = Application.Settings.Database.Connection;
            string        dataAreaId = Application.Settings.Database.DataAreaID;

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

                bool useDefaultTaxGroups = (cacheKey.TaxGroupID == null) || (cacheKey.SalesTaxGroupID == null);

                using (SqlCommand command = new SqlCommand())
                {
                    command.Connection = connection;

                    string sb = string.Format(@"SELECT DISTINCT {0}    FROM TAXGROUPHEADING  ", TaxSelectSqlText);

                    if (useDefaultTaxGroups)
                    {
                        // #1 Look in the DB for the default Customer/Store tax group mapping
                        if (String.IsNullOrWhiteSpace(cacheKey.CustomerID))
                        {
                            sb += @"INNER JOIN RETAILSTORETABLE ON 
                                TAXGROUPHEADING.TAXGROUP = RETAILSTORETABLE.TAXGROUP 
                                AND  RETAILSTORETABLE.STORENUMBER = @STOREID ";

                            command.Parameters.AddWithValue("@STOREID", ApplicationSettings.Terminal.StoreId);
                        }
                        else
                        {
                            sb += @"INNER JOIN CUSTTABLE ON 
                                TAXGROUPHEADING.TAXGROUP = CUSTTABLE.TAXGROUP 
                                AND CUSTTABLE.DATAAREAID = @DATAAREAID 
                                AND CUSTTABLE.ACCOUNTNUM = @CUSTOMERID ";

                            command.Parameters.AddWithValue("@CUSTOMERID", cacheKey.CustomerID);
                        }
                    }

                    sb += @"INNER JOIN TAXGROUPDATA ON 
                        TAXGROUPDATA.DATAAREAID = @DATAAREAID 
                        AND TAXGROUPHEADING.TAXGROUP = TAXGROUPDATA.TAXGROUP 

                        INNER JOIN TAXONITEM ON 
                        TAXONITEM.DATAAREAID = @DATAAREAID 
                        AND TAXGROUPDATA.TAXCODE = TAXONITEM.TAXCODE  ";

                    if (useDefaultTaxGroups)
                    {
                        // Join against the Item's default Item tax group
                        sb += @"INNER JOIN INVENTTABLEMODULE ON 
                            INVENTTABLEMODULE.DATAAREAID = @DATAAREAID 
                            AND INVENTTABLEMODULE.TAXITEMGROUPID = TAXONITEM.TAXITEMGROUP  ";
                    }

                    sb += @"INNER JOIN TAXDATA ON 
                        TAXDATA.DATAAREAID = @DATAAREAID 
                        AND TAXONITEM.TAXCODE = TAXDATA.TAXCODE 

                        INNER JOIN TAXTABLE ON 
                        TAXTABLE.DATAAREAID = @DATAAREAID 
                        AND TAXTABLE.TAXCODE = TAXDATA.TAXCODE 

                        LEFT JOIN TAXCOLLECTLIMIT ON 
                        TAXCOLLECTLIMIT.DATAAREAID = @DATAAREAID 
                        AND TAXCOLLECTLIMIT.TAXCODE = TAXDATA.TAXCODE 
                        AND (TAXCOLLECTLIMIT.TAXFROMDATE IS NULL 
                        OR @TRANSACTIONDATE >= TAXCOLLECTLIMIT.TAXFROMDATE 
                        OR TAXCOLLECTLIMIT.TAXFROMDATE < @NODATEBOUNDRY ) 
                        AND (TAXCOLLECTLIMIT.TAXTODATE IS NULL 
                        OR @TRANSACTIONDATE < DATEADD(d, 1, TAXCOLLECTLIMIT.TAXTODATE) 
                        OR TAXCOLLECTLIMIT.TAXTODATE < @NODATEBOUNDRY) 

                        WHERE (TAXGROUPHEADING.DATAAREAID = @DATAAREAID)  ";

                    command.Parameters.AddWithValue("@DATAAREAID", dataAreaId);

                    if (useDefaultTaxGroups)
                    {
                        sb += @"AND (INVENTTABLEMODULE.ITEMID = @ITEMID) 
                            AND (INVENTTABLEMODULE.MODULETYPE = @MODULETYPE)  ";

                        command.Parameters.AddWithValue("@ITEMID", cacheKey.ItemID);
                        command.Parameters.AddWithValue("@MODULETYPE", (int)ModuleType.Sales);
                    }
                    else
                    {
                        // Filter against the item's current Item Tax Group and Customer/Store tax group
                        sb += @"AND TAXONITEM.TAXITEMGROUP = @ITEMTAXGROUP 
                            AND TAXGROUPHEADING.TAXGROUP = @SALESTAXGROUP  ";

                        command.Parameters.AddWithValue("@SALESTAXGROUP", cacheKey.SalesTaxGroupID ?? string.Empty);
                        command.Parameters.AddWithValue("@ITEMTAXGROUP", cacheKey.TaxGroupID ?? string.Empty);
                    }

                    // Currently only evaluate taxes against the current time.
                    // Note that the date value of '1900-01-01 00:00.000' is the marker for "no boundry".
                    sb += @"AND 
                        ((@TRANSACTIONDATE >= TAXDATA.TAXFROMDATE OR TAXDATA.TAXFROMDATE < @NODATEBOUNDRY ) 
                        AND (@TRANSACTIONDATE < DATEADD(d, 1, TAXDATA.TAXTODATE) OR TAXDATA.TAXTODATE < @NODATEBOUNDRY)) ";

                    command.Parameters.AddWithValue("@NODATEBOUNDRY", NoDate);
                    command.Parameters.AddWithValue("@TRANSACTIONDATE", cacheKey.SaleDateTime);

                    sb += AddTaxSelectSqlCondition(command);

                    command.CommandText = sb.ToString();

                    if (connection.State != ConnectionState.Open)
                    {
                        connection.Open();
                    }

                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        string taxCodeKey = string.Empty;

                        while (reader.Read())
                        {
                            taxCodeKey = reader["TAXCODE"] as string;

                            if (codes.ContainsKey(taxCodeKey))
                            {
                                // Add a new 'value' entry for an existing tax code
                                codes[taxCodeKey].TaxIntervals.Add(
                                    new TaxInterval(
                                        (decimal)reader["TAXLIMITMIN"],
                                        (decimal)reader["TAXLIMITMAX"],
                                        (decimal)reader["TAXVALUE"]));
                            }
                            else
                            {
                                AddTaxCode(cacheKey, reader, taxableItem, codes);
                            }
                        }
                    }
                }

                // 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] as TaxCode;
                    }
                }

                return(SortCodes(codes.Values));
            }
            catch (Exception ex)
            {
                NetTracer.Error(ex, "GetTaxCodes() failed in an Exception");
                throw;
            }
            finally
            {
                if (connection.State == ConnectionState.Open)
                {
                    connection.Close();
                }
            }
        }