コード例 #1
0
        /// <summary>
        /// Round tax code amounts at the Tax Group level
        /// </summary>
        /// <param name="lineItem"></param>
        /// <param name="taxGroup"></param>
        private static void RoundTaxGroup(BaseSaleItem lineItem, string taxGroup)
        {
            if (lineItem.TaxLines.Count > 0)
            {
                decimal roundedAmount = decimal.Zero;
                decimal roundedSum    = decimal.Zero;
                decimal rawSum        = decimal.Zero;
                decimal diff          = decimal.Zero;

                IRounding rounding      = TaxService.Tax.InternalApplication.Services.Rounding;
                string    storeCurrency = ApplicationSettings.Terminal.StoreCurrency;

                // Sum up raw and rounded tax amounts
                foreach (ITaxItem taxLine in lineItem.TaxLines.Where(t => (!t.Exempt) && string.Equals(taxGroup, t.TaxGroup)))
                {
                    // Accumulate rounded and unrounded/raw amounts.  Tax.Amount is the raw value.
                    roundedAmount = rounding.Round(taxLine.Amount, storeCurrency, true, 1);
                    rawSum       += taxLine.Amount;
                    roundedSum   += roundedAmount;

                    // Set Tax.Amount to the rounded amount
                    taxLine.Amount = roundedAmount;

                    // Compute the difference between the sums.
                    // If we have accumulated enough extra decimals to cause raw to round up, then we'll see a difference.
                    diff = rounding.Round(rawSum, storeCurrency, true, 1) - roundedSum;

                    // Apply the difference against the current line
                    if (diff != decimal.Zero)
                    {
                        taxLine.Amount += diff;
                        roundedSum     += diff;
                    }
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Get 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 in store currency].</param>
        /// <param name="calculateBasePrice">if set to <c>true</c> [Calculate the base price].</param>
        /// <param name="calculationBase">The calculation base.</param>
        /// <param name="limitBase">The limit base.</param>
        protected virtual void GetBases(ReadOnlyCollection <TaxCode> codes,
                                        bool taxInStoreCurrency,
                                        bool calculateBasePrice,
                                        out decimal calculationBase,
                                        out decimal limitBase)
        {
            decimal basePrice = decimal.Zero;

            if (calculateBasePrice)
            {
                basePrice = this.TaxIncludedInPrice ?
                            Provider.GetBasePriceForTaxIncluded(this.LineItem, codes) :
                            LineItem.NetAmountPerUnit;
            }

            //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.LineItem.RetailTransaction));
                    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.LineItem.Quantity == decimal.Zero) ? decimal.One : this.LineItem.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.LineItem.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.LineItem));
                }

                break;

            case TaxBase.AmountByUnit:
                calculationBase = 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.LineItem.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 = TaxService.Tax.InternalApplication.Services.Currency.CurrencyToCurrency(
                        ApplicationSettings.Terminal.StoreCurrency,
                        this.Currency,
                        limitBase);
                }

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

                if (saleLineItem != null &&
                    !string.Equals(this.Unit, this.LineItem.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase))
                {
                    UnitOfMeasureData uomData = new UnitOfMeasureData(
                        ApplicationSettings.Database.LocalConnection,
                        ApplicationSettings.Database.DATAAREAID,
                        ApplicationSettings.Terminal.StorePrimaryId,
                        TaxService.Tax.InternalApplication);
                    UnitQtyConversion uomConversion = uomData.GetUOMFactor(this.LineItem.SalesOrderUnitOfMeasure, this.Unit, saleLineItem);
                    calculationBase *= uomConversion.GetFactorForQty(this.LineItem.Quantity);
                }
            }
            else
            {
                // Convert base to Tax currency, if different
                if (!taxInStoreCurrency)
                {
                    calculationBase = TaxService.Tax.InternalApplication.Services.Currency.CurrencyToCurrency(
                        ApplicationSettings.Terminal.StoreCurrency,
                        this.Currency,
                        calculationBase);
                }

                // Base for limits/intervals is same for Calculations
                limitBase = calculationBase;
            }
        }
コード例 #3
0
        /// <summary>
        /// Given a set of promotion lines, tentative item price, and item, calculate the price after promotions are applied
        /// </summary>
        /// <param name="promotionLines">List of promotion configurations active for this item</param>
        /// <param name="price">Price of the item before the promotion, derived from trade agreement or base item price</param>
        /// <param name="saleItem">The sale item whose price is being determined</param>
        /// <returns>Unrounded price after applying all promotions</returns>
        public static decimal CalculatePromotionPrice(IEnumerable <PromotionInfo> promotionLines, decimal price, BaseSaleItem saleItem)
        {
            if (saleItem == null)
            {
                NetTracer.Warning("saleItem parameter is null");
                throw new ArgumentNullException("saleItem");
            }

            if (promotionLines == null || promotionLines.Count() == 0)
            {
                return(price);
            }

            decimal            promoPrice  = price;
            IList <PromoPrice> promoPrices = new List <PromoPrice>();

            foreach (PromotionInfo promo in promotionLines)
            {
                PromoPrice promoLine = new PromoPrice();
                promoLine.PromoId        = promo.PromoId;
                promoLine.Concurrency    = promo.Concurrency;
                promoLine.IsCompoundable = false;

                switch (promo.DiscountMethod)
                {
                case DiscountMethod.DiscountPercent:
                    promoLine.PercentOff     = promo.MaxDiscPct;
                    promoLine.IsCompoundable = true;
                    promoPrices.Add(promoLine);
                    break;

                case DiscountMethod.OfferPrice:
                    if (!saleItem.Transaction.TaxIncludedInPrice)
                    {
                        promoLine.AmountOff = price - promo.Price;
                        promoPrices.Add(promoLine);
                    }
                    break;

                case DiscountMethod.OfferPriceInclTax:
                    if (saleItem.Transaction.TaxIncludedInPrice)
                    {
                        promoLine.AmountOff = price - promo.PriceInclTax;
                        promoPrices.Add(promoLine);
                    }
                    break;

                case DiscountMethod.DiscountAmount:
                    promoLine.AmountOff      = promo.MaxDiscAmount;
                    promoLine.IsCompoundable = true;
                    promoPrices.Add(promoLine);
                    break;
                }
            }

            promoPrice = price - FindConcurrentPromoAmount(promoPrices, price);

            return(promoPrice);
        }
コード例 #4
0
        /// <summary>
        /// Calculate the tax bases calculationBase and limitBase (which is zero for India).
        /// </summary>
        /// <param name="basePrice">The base price.</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>
        /// <param name="calculationBase">The calculation base.</param>
        /// <param name="limitBase">The limit base.</param>
        protected override void GetBases(ReadOnlyCollection <TaxCode> codes, bool taxInStoreCurrency, bool calculateBasePrice, out decimal calculationBase, out decimal limitBase)
        {
            limitBase = decimal.Zero;

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

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

                if (saleLineItem != null &&
                    !string.Equals(this.Unit, this.LineItem.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase))
                {
                    UnitOfMeasureData uomData = new UnitOfMeasureData(
                        ApplicationSettings.Database.LocalConnection,
                        ApplicationSettings.Database.DATAAREAID,
                        ApplicationSettings.Terminal.StorePrimaryId,
                        TaxService.Tax.InternalApplication);
                    UnitQtyConversion uomConversion = uomData.GetUOMFactor(this.LineItem.SalesOrderUnitOfMeasure, this.Unit, saleLineItem);
                    calculationBase *= uomConversion.GetFactorForQty(this.LineItem.Quantity);
                }
                return;
            }

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

            case TaxableBases.MRP:
                calculationBase = GetMRP();
                break;

            default:
                calculationBase = decimal.Zero;
                break;
            }

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

            calculationBase *= Math.Abs(this.LineItem.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 = Formula.ParseExpression();

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

                if (taxItem != null)
                {
                    this.IsTaxOnTax = true;
                    this.TaxCodesInFormula.Add(taxItem.TaxCode);
                }

                decimal amount = taxItem == null ? decimal.Zero : taxItem.Amount * Math.Sign(this.LineItem.Quantity);

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

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

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

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

                default:
                    NetTracer.Error("GetBases(): Invalid operator in formula. tokens[{0}]: {1}", index - 1, tokens[index - 1]);
                    System.Diagnostics.Debug.Fail("Invalid operator in formula");
                    break;
                }
            }

            // Knock any abatement off of the taxable basis
            calculationBase *= (100 - AbatementPercent) / 100;
        }