protected void PrepareLimPar()
        {
            coParLowLim = GetUniquePar <ParFormula>(DefPar.Common.LowLim);
            coParUpLim  = GetUniquePar <ParFormula>(DefPar.Common.UpLim);
            coParThres  = GetUniquePar <ParFormula>(DefPar.Common.Threshold);
            ParCateg coParLimpriority = GetUniquePar <ParCateg>(DefPar.Common.Limpriority);

            // the following tries to optimise by getting the values here, if they are read-time-available (e.g. 600#y, GetSystemYear, ...)
            // and by checking for implausible values (e.g. upLim = 10, lowLim = 100) if already possible
            if (coParLowLim != null)
            {
                if (coParLowLim.IsGlobal(true))
                {
                    lowLim = coParLowLim.GetGlobalValue(); coParLowLim = null;
                }
                else
                {
                    doRunTimeLimCheck = true;
                }
            }
            if (coParUpLim != null)
            {
                if (coParUpLim.IsGlobal(true))
                {
                    upLim = coParUpLim.GetGlobalValue(); coParUpLim = null;
                }
                else
                {
                    doRunTimeLimCheck = true;
                }
            }
            bool checkThres = coParThres != null;

            if (coParThres != null)
            {
                if (coParThres.IsGlobal(true))
                {
                    thres = coParThres.GetGlobalValue(); coParThres = null;
                }
                else
                {
                    doRunTimeThresCheck = true;
                }
            }
            if (coParLimpriority != null)
            {
                limPri = coParLimpriority.GetCateg();
            }

            if (!doRunTimeLimCheck) // both limits available (might be double.MinValue and/or double.MaxValue, but does no harm here)
            {
                CheckLimits(ref lowLim, ref upLim);

                if (checkThres && !doRunTimeThresCheck) // threshold and both limits available
                {
                    if (!CheckThreshold(thres, lowLim, upLim))
                    {
                        thres = lowLim;
                    }
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// checks if the standard footnote-parameters, i.e. #_LowLim, #_UpLim, #_Level, are applied on the parameter
        /// if so, stores them in lowLim, upLim and alternativeTU (see above)
        /// also provides the "cleaned" value (e.g. yem#1 -> yem)
        /// note that the footnote-parameters are assessed via the (mother)function (parameter fun)
        /// also note that the only other (not here handled) footnote-parameters are 'amount', which is handled by ParFormula
        /// and query parameters which will not issue a "missing" (see below) but let the query check if it can use the footnote
        /// </summary>
        protected void ExtractStandardFootnotes(string value, out string cleanedValue, FunBase fun)
        {
            List <string> numbers = GetFootnoteNumbers(value, out cleanedValue); // it's unlikely, but yem#1#3 is possible (see GetFootnoteNumbers)

            if (numbers.Count == 0)
            {
                return;                     // no footnotes
            }
            // limits (see remark in _FunInSpineBase_Lim wrt not using a common implementation for limits)
            pLowLim = fun.GetFootnotePar <ParFormula>(DefPar.Footnote.LowLim, numbers);
            if (pLowLim != null)
            {
                if (pLowLim.IsGlobal(true))
                {
                    lowLim = pLowLim.GetGlobalValue(); pLowLim = null;
                }
                else
                {
                    doRunTimeLimCheck = true;
                }
            }

            pUpLim = fun.GetFootnotePar <ParFormula>(DefPar.Footnote.UpLim, numbers);
            if (pUpLim != null)
            {
                if (pUpLim.IsGlobal(true))
                {
                    upLim = pUpLim.GetGlobalValue(); pUpLim = null;
                }
                else
                {
                    doRunTimeLimCheck = true;
                }
            }

            ParCateg pLimpriority = fun.GetFootnotePar <ParCateg>(DefPar.Footnote.LimPriority, numbers);

            if (pLimpriority != null)
            {
                limPri = pLimpriority.GetCateg();
            }

            if (!doRunTimeLimCheck) // both limits available (might be double.MinValue and/or double.MaxValue, but does no harm here)
            {
                CheckLimits(ref lowLim, ref upLim);
            }

            // alternative TU
            ParBase parAltTU = fun.GetFootnotePar <ParBase>(DefPar.Footnote.Level, numbers);

            if (parAltTU != null)
            {
                alternativeTU = parAltTU.xmlValue;
            }

            // if none of the standard footnotes is defined in the function #x points to nowhere ...
            if (GetType() == typeof(ParQuery)) // ... except for queries, which may have own footnotes
            {
                return;                        // (this is slightly negligent, as it accepts e.g. IsMarried#1 without respective footnote-parameter)
            }
            if (pLowLim == null && lowLim == double.MinValue &&
                pUpLim == null && upLim == double.MaxValue &&
                parAltTU == null)
            {
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = true, message = $"{description.Get()}: missing footnote parameter for {value}"
                });
            }
        }
        protected override double DoFunWork(HH hh, List <Person> tu)
        {
            // first make a run-time copy of all bands (for thread-safety)
            List <Band> rtBands = new List <Band>(bands.Count);

            bands.ForEach((b) => rtBands.Add(b.Clone() as Band));
            double safeBaseThreshold = baseThreshold;
            double safeQuotient      = quotient;

            // do the rouding and check the threshold & quotient
            double baseVal = basePar.GetValue(hh, tu);

            if (!double.IsNaN(roundBase))
            {
                baseVal = Math.Round(baseVal / roundBase) * roundBase;
            }
            if (baseThresholdPar != null && !baseThresholdPar.IsGlobal(true))
            {
                safeBaseThreshold = baseThresholdPar.GetValue(hh, tu);
            }
            if (quotientPar != null && !quotientPar.IsGlobal(true))
            {
                safeQuotient = quotientPar.GetValue(hh, tu);
            }

            if (baseVal < safeBaseThreshold)
            {
                return(0);                              // if base is under the threshold, return 0
            }
            // calculate and check the limits - we only need to calculate the upLim of each Band at runtime, except for Band 1
            if (doRunTimeLimCheck)
            {
                if (rtBands[0].pLowLim != null)
                {
                    rtBands[0].lowLim = rtBands[0].pLowLim.GetValue(hh, tu);
                }
                foreach (Band band in rtBands)
                {
                    if (band.pUpLim != null)
                    {
                        band.upLim = band.pUpLim.GetValue(hh, tu);
                    }
                }
                CheckGroupLimits(rtBands, description.funID, hh);
            }

            // then check if we need to calculate any rates/amounts
            if (doRunTimeRateCheck)
            {
                foreach (Band band in rtBands)
                {
                    if (band.pRate != null)
                    {
                        band.rate = band.pRate.GetValue(hh, tu);
                    }
                    else if (band.pAmount != null)
                    {
                        band.amount = band.pAmount.GetValue(hh, tu);
                    }
                }
            }

            baseVal = baseVal / safeQuotient;

            double tax = 0;

            if (simpleProg)     // apply the max rate/amount to all income
            {
                if (rtBands.Count > 0 && baseVal > rtBands.Last().upLim)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = true, runTimeErrorId = description.funID,
                        message   = $"{description.Get()}: '{DefPar.SchedCalc.Base}' exceeds '{DefPar.SchedCalc.Band_UpLim}' of highest band ({baseVal}>{rtBands.Last().upLim}) - '{(double.IsNaN(rtBands.Last().rate) ? DefPar.SchedCalc.Band_Amount : DefPar.SchedCalc.Band_Rate)}' of highest band is applied"
                    });
                }

                // get the highest band
                Band hb = null;
                foreach (Band b in rtBands)
                {
                    if (b.lowLim <= baseVal)
                    {
                        hb = b;
                    }
                    if (b.upLim > baseVal)
                    {
                        break;
                    }
                }
                if (hb == null)
                {
                    tax = 0;
                }
                else if (!double.IsNaN(hb.rate))
                {
                    tax = baseVal * hb.rate;
                }
                else
                {
                    tax = hb.amount;
                }
            }
            else                                                 // apply each rate/amount to the equivalent band
            {
                double prevLim = Math.Max(rtBands[0].lowLim, 0); // start taxing at the lowest limit of the first band
                foreach (Band b in rtBands)
                {
                    if (prevLim > baseVal)
                    {
                        break;
                    }
                    if (!double.IsNaN(b.rate))
                    {
                        tax += (Math.Max(Math.Min(baseVal, b.upLim) - prevLim, 0)) * b.rate;
                    }
                    else
                    {
                        tax += b.amount;
                    }
                    prevLim = b.upLim;
                }
            }

            tax = tax * safeQuotient;
            return(tax);
        }
        protected override void PrepareNonCommonPar()
        {
            if (GetParGroups(DefPar.SchedCalc.Band_XXX).Count < 1)
            {
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $"{description.Get()}: SchedCalc must have at least one band"
                });
                return;
            }
            foreach (KeyValuePair <int, List <ParBase> > bandParList in GetParGroups(DefPar.SchedCalc.Band_XXX))
            {
                Band band = new Band();
                // Prepare the Band Limits
                band.band    = bandParList.Key;
                band.pLowLim = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_LowLim, bandParList.Value);
                if (band.pLowLim != null)   // if there is a lowLim
                {
                    if (band.pLowLim.IsGlobal(true))
                    {
                        band.lowLim = band.pLowLim.GetGlobalValue(); band.pLowLim = null;
                    }
                    else
                    {
                        doRunTimeLimCheck = true;
                    }
                }
                band.pUpLim = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_UpLim, bandParList.Value);
                if (band.pUpLim != null)   // if there is a lowLim
                {
                    if (band.pUpLim.IsGlobal(true))
                    {
                        band.upLim = band.pUpLim.GetGlobalValue(); band.pUpLim = null;
                    }
                    else
                    {
                        doRunTimeLimCheck = true;
                    }
                }
                // Prepare the Band Rate/Amount
                band.pRate   = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_Rate, bandParList.Value);
                band.pAmount = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_Amount, bandParList.Value);
                if (band.pRate != null && band.pAmount != null) // Rate and Amount cannot co-exist
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $"{description.Get()}: Rate and Amount cannot co-exist in group {band.band}"
                    });
                }
                if (band.pRate == null && band.pAmount == null) // Rate and Amount cannot both be missing
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $"{description.Get()}: Rate and Amount cannot both be missing in group {band.band}"
                    });
                }
                if (band.pRate != null)
                {
                    if (band.pRate.IsGlobal(true))
                    {
                        band.rate = band.pRate.GetGlobalValue(); band.pRate = null;
                    }
                    else
                    {
                        doRunTimeRateCheck = true;
                    }
                }
                if (band.pAmount != null)
                {
                    if (band.pAmount.IsGlobal(true))
                    {
                        band.amount = band.pAmount.GetGlobalValue(); band.pAmount = null;
                    }
                    else
                    {
                        doRunTimeRateCheck = true;
                    }
                }
                bands.Add(band);
            }

            // check that all required limits exist
            for (int i = 1; i < bands.Count; i++)
            {
                // if both limits are missing then produce an error
                if (bands[i - 1].upLim == double.MaxValue && bands[i - 1].pUpLim == null && bands[i].lowLim == double.MinValue && bands[i].pLowLim == null)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $"{description.Get()}: Band {bands[i - 1].band}: insufficient definition of upper limit. Use parameters 'band_uplim' or 'band_lowlim'(+1)."
                    });
                }
                // if both limits are there, then produce an error
                else if ((bands[i - 1].upLim != double.MaxValue || bands[i - 1].pUpLim != null) && (bands[i].lowLim != double.MinValue || bands[i].pLowLim != null))
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $"{description.Get()}: Double definition of upper limit as 'Band_UpLim {bands[i - 1].band}' and 'Band_LowLim {bands[i].band}'."
                    });
                }
                // else, make sure that the upper limit always exist (copy from Group+1 lower limit if required)
                else if (bands[i - 1].upLim == double.MaxValue && bands[i - 1].pUpLim == null)
                {
                    if (bands[i].pLowLim == null)
                    {
                        bands[i - 1].upLim = bands[i].lowLim;                           // copy the litteral limit if it exists
                    }
                    else
                    {
                        bands[i - 1].pUpLim = bands[i].pLowLim;                         // else copy the parameter to be run at run-time
                    }
                }
            }

            // if possible, check that limits are also in order
            if (!doRunTimeLimCheck)
            {
                CheckGroupLimits(bands);
            }

            // prepare the other parameters
            basePar = GetUniquePar <ParFormula>(DefPar.SchedCalc.Base);
            if (basePar == null)
            {
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $"{description.Get()}: Missing required parameter Base"
                });
            }

            ParBool doAverageRatesPar = GetUniquePar <ParBool>(DefPar.SchedCalc.Do_Average_Rates);

            doAverageRates = doAverageRatesPar != null && doAverageRatesPar.GetBoolValue();
            ParBool simpleProgPar = GetUniquePar <ParBool>(DefPar.SchedCalc.Simple_Prog);

            simpleProg = simpleProgPar != null && simpleProgPar.GetBoolValue();

            quotientPar = GetUniquePar <ParFormula>(DefPar.SchedCalc.Quotient);
            if (quotientPar != null)
            {
                if (quotientPar.IsGlobal(true))
                {
                    quotient = quotientPar.GetGlobalValue(); quotientPar = null;
                }
            }

            baseThresholdPar = GetUniquePar <ParFormula>(DefPar.SchedCalc.BaseThreshold);
            if (baseThresholdPar != null)
            {
                if (baseThresholdPar.IsGlobal(true))
                {
                    baseThreshold = baseThresholdPar.GetGlobalValue(); baseThresholdPar = null;
                }
            }

            ParNumber roundBasePar = GetUniquePar <ParNumber>(DefPar.SchedCalc.Round_Base);

            if (roundBasePar != null)
            {
                roundBase = roundBasePar.GetValue();
            }
        }
Exemple #5
0
 /// <summary>
 /// does condition refer to variables which are equal for each person, this is necessary for checking RunCond
 /// strong = true: condition may only contain global queries and numbers, i.e. no global variables (which may change at run-time)
 /// i.e. strong means is-read-time-available
 /// </summary>
 internal bool IsGlobal(bool strong = false)
 {
     return(parFormula.IsGlobal(strong));
 }