/// <summary> /// Calculates the tax for the last item. /// </summary> /// <param name="request">The request.</param> /// <returns>The response.</returns> private static CalculateTaxServiceResponse CalculateTax(CalculateTaxServiceRequest request) { ThrowIf.Null(request, "request"); TaxHelpers.SetSalesTaxGroup(request.RequestContext, request.Transaction); SalesTaxOverrideHelper.CalculateTaxOverrides(request.RequestContext, request.Transaction); // Consider active (non-void) lines for tax. // Need to recalculate tax on return-by-receipt lines because we cannot reconstruct tax lines from return transaction lines alone. // A few key information like IsExempt, IsTaxInclusive, TaxCode are not available on return transaction line. foreach (var saleItem in request.Transaction.ActiveSalesLines) { saleItem.TaxRatePercent = 0; saleItem.TaxLines.Clear(); } var totaler = new SalesTransactionTotaler(request.Transaction); totaler.CalculateTotals(request.RequestContext); ClearChargeTaxLines(request.Transaction); TaxContext taxContext = new TaxContext(request.RequestContext); TaxCodeProvider defaultProvider = GetTaxProvider(request.RequestContext, taxContext); defaultProvider.CalculateTax(request.RequestContext, request.Transaction); return(new CalculateTaxServiceResponse(request.Transaction)); }
/// <summary> /// Load all registered TaxCodeProviders /// </summary> protected void LoadProviders() { ITaxProvider defaultProvider; NetTracer.Information("Tax.LoadProviders(): Functions.CountryRegion: {0}", Functions.CountryRegion); //Add default provider switch (Functions.CountryRegion) { case SupportedCountryRegion.IN: defaultProvider = new TaxCodeProviderIndia(Application); break; case SupportedCountryRegion.BR: defaultProvider = new TaxCodeProviderBrazil(Application); break; default: defaultProvider = new TaxCodeProvider(Application); break; } //Load providers from config file Providers = ProviderLoader.Load(defaultProvider); }
/// <summary> /// Get the tax code provider. /// </summary> /// <param name="context">The request context.</param> /// <param name="taxContext">Tax context.</param> /// <returns>The tax code provider.</returns> private static TaxCodeProvider GetTaxProvider(RequestContext context, TaxContext taxContext) { TaxCodeProvider taxCodeProvider; switch (context.GetChannelConfiguration().CountryRegionISOCode) { case CountryRegionISOCode.IN: taxCodeProvider = new TaxCodeProviderIndia(taxContext); break; default: taxCodeProvider = new TaxCodeProvider(taxContext); break; } return(taxCodeProvider); }
/// <summary> /// Sorts the specified tax codes by priority. /// </summary> /// <param name="codes">The tax codes.</param> /// <returns>An ordered collection of tax codes.</returns> protected override ReadOnlyCollection <TaxCode> SortCodes(Dictionary <string, TaxCode> codes) { if (codes == null) { throw new ArgumentNullException("codes"); } // Return codes to be processed in the following order: // Non-India codes // India codes ordered by Id return(new ReadOnlyCollection <TaxCode>( codes.Values.OrderBy(code => { TaxCodeIndia codeIndia = code as TaxCodeIndia; if (codeIndia != null) { return codeIndia.Formula.Id + MaxPriorityTaxCode + 1; } else { return TaxCodeProvider.TaxCodePriority(code); } }).ToList())); }
/// <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)); }