03 października 2011

Triumwirat czyli podatki, dynamic i tuple

Progresywny podatek dochodowy w Izraelu


Zbliżają się wybory, zatem podatki to teraz gorący temat. Tak się złożyło, że Ayende Rahien na swoim blogu opublikował jedno z zadań rekrutacyjnych, które wykorzystywał do weryfikacji umiejętności potencjalnych kandydatów na programistę. Problem polegał na obliczeniu podatku dochodowego od danej kwoty, zakładając, że stawki podatku  i progi podatkowe w Izraelu są następujące.


Progi podatkowe Podatek
do kwoty 5,070 ILS 10%
od 5,071  do 8,660  14%
od 8,661 do 14,070 23%
od 14,071 do 21,240 30%
od 21,241 do 40,230 33%
powyżej 40,230 45%

Przykładowe odpowiedzi:

  • 5,000  ILS –> 500
  • 5,800  ILS –> 609.2
  • 9,000   ILS –> 1087.8
  • 15,000  ILS –> 2532.9
  • 50,000  ILS –> 15,068.1



Tuple w akcji


Problem nie jest skomplikowany, ale można użyć go do wypróbowania nowych mechanizmów, klas dostępnych w .NET Framework 4. Pierwsze rozwiązanie używa klasy Tuple, która umożliwia definiowania krotek z wieloma uporządkowanymi atrybutami. Wykorzystanie Tuple umożliwia proste zdefiniowanie progów podatkowych i procentu podatku.

public class TaxCalculator
{
    readonly List<Tuple<decimal, decimal, decimal>> taxRanges = new List<Tuple<decimal, decimal, decimal>>
        {
            new Tuple<decimal, decimal, decimal>(0m, 5070m, .10m),
            new Tuple<decimal, decimal, decimal>(5070m, 8660m, .14m),
            new Tuple<decimal, decimal, decimal>(8660m, 14070m, .23m),
            new Tuple<decimal, decimal, decimal>(14070m, 21240m, .30m),
            new Tuple<decimal, decimal, decimal>(21240m, 40230m, 0.33m),
            new Tuple<decimal, decimal, decimal>(40230m, decimal.MaxValue, .45m)
        };

    public decimal CalculateTax(decimal salary)
    {
        return taxRanges.Sum(tr=>CalculateTaxValuePerRange(salary, tr.Item1, tr.Item2, tr.Item3));
    }

    private decimal CalculateTaxValuePerRange(decimal salary, decimal bottomLimit, decimal upperLimit, decimal taxRate)
    {
        decimal calculatedTax = 0m;
        if(salary - upperLimit >= 0)
        {
            calculatedTax = (upperLimit - bottomLimit)*taxRate;
        }
        else if (salary - bottomLimit >= 0)
        {
            calculatedTax = (salary - bottomLimit)*taxRate;
        }

        return calculatedTax;
    }
}
   


Wersja z wzorcami i dynamic


Załóżmy, że przy obliczaniu podatku chcielibyśmy mieć możliwość wykorzystania typu decimal, double, lub własnego typu obliczeniowego. Możemy do tego wykorzystać wzorce, aczkolwiek nie umozliwiają one wykorzystywania operatorów artymetycznych. Do obejścia tego ograniczenia można wykorzystać typ dynamic (jest to alias typu Object), który umożliwia dzięki wsparciu kompilatora wywoływanie metod, operacji które będą dostępne dopiero w czasie wykonywania kodu (late binding).

public class TaxRange<T>
{
    public TaxRange(T bottomLimit, T upperLimit, T taxRate)
    {
        BottomLimit = bottomLimit;
        UpperLimit = upperLimit;
        TaxRate = taxRate;
    }

    public T BottomLimit { get; set; }

    public T UpperLimit { get; set; }

    public T TaxRate { get; set; }
}

public class TaxCalculator2<T>
{
    private readonly List<TaxRange<T>> _taxRanges;

    public TaxCalculator2(List<TaxRange<T>> taxRanges)
    {
        _taxRanges = taxRanges;
    }

    public T CalculateTax(T salary)
    {
        dynamic totalTaxAmount = 0;
        foreach (TaxRange<T> tr in _taxRanges)
        {
            totalTaxAmount += CalculateTaxValuePerRange(salary, tr);
        }
        return totalTaxAmount;
    }

    private T CalculateTaxValuePerRange(T salary, TaxRange<T> taxRange)
    {
        dynamic calculatedTax = 0;
        if ((dynamic)salary - taxRange.UpperLimit >= 0)
        {
            calculatedTax = ((dynamic)taxRange.UpperLimit - taxRange.BottomLimit) * taxRange.TaxRate;
        }
        else if ((dynamic)salary - taxRange.BottomLimit >= 0)
        {
            calculatedTax = ((dynamic)salary - taxRange.BottomLimit) * taxRange.TaxRate;
        }

        return calculatedTax;
    }
}

var taxRanges2 = new List<TaxRange<double>>
                                        {
    new TaxRange<double>(0, 5070, .10),
    new TaxRange<double>(5070, 8660, .14),
    new TaxRange<double>(8660, 14070, .23),
    new TaxRange<double>(14070, 21240, .30),
    new TaxRange<double>(21240, 40230, 0.33),
    new TaxRange<double>(40230, double.MaxValue, .45)
};

var taxCalculator2 = new TaxCalculator2<double>(taxRanges2);
Console.WriteLine(taxCalculator2.CalculateTax(50000));

Na koniec deserek, czyli podatek dochodowych w Polsce


W pewnym uproszczeniu (mamy ulgi, kwotę wolną od podatku, ZUS, składkę rentową, składkę zdrowotną) skala naszego podatku dochodowego wygląda następująco :


Progi podatkowe Podatek
do kwoty 85 528zł 18%
powyżej 85 528zł 32%

50,000 ILS (Izraelskich Szekli) jest to równowartość 44,022 PLN na dzień 03 października 2011 roku. Definicja progów podatkowych w Polsce wygląda następująco.

var polishTaxRanges = new List<TaxRange<double>>
{
    new TaxRange<double>(0, 85528, .18),
    new TaxRange<double>(85528, double.MaxValue, .32)
};

Podsumowując, z moich obliczeń wynika, że dla kwoty 44200 PLN w Polsce płacimy niższy podatek dochodowy niż ludzie mieszkający w Izraelu. Niestety nie świadczy to o tym, że płacimy niskie podatki.

var taxCalculator = new TaxCalculator();
var taxCalculator2 = new TaxCalculator2<double>(taxRanges2);
var taxCalculator3 = new TaxCalculator2<double>(polishTaxRanges);

Console.WriteLine("Tax for Izraeli salary 50,000 ILS is {0} PLN", taxCalculator.CalculateTax(50000) * .88m);
Console.WriteLine("Tax for Izraeli salary 50,000 ILS is {0} PLN", taxCalculator2.CalculateTax(50000) * 0.88);
Console.WriteLine("Tax for Polish salary 44,200 PLN is {0} PLN",taxCalculator3.CalculateTax(44200));



Hope this helps.

1 komentarz:

Uwaga: tylko uczestnik tego bloga może przesyłać komentarze.