예제 #1
0
        private async Task <VendorRule> GetVendorRule(VendorRuleModel ruleModel, string ruleDefinition, Guid userId, RuleType ruleType, Vendor vendor)
        {
            var criterion = ruleModel.Criteria != null
                ? new RuleCriterion
            {
                Operator = ExpressionType.And.ToString(),
                Children = await GetRuleCriteria(ruleModel.Criteria)
            }
                : null;

            var ruleEntity = new Rule(userId)
            {
                Name       = $"{vendor.Name}_{ruleType}_{ruleModel.Name}",
                Criterion  = criterion,
                Definition = ruleDefinition,
                Type       = ruleType
            };

            var vendorRule = new VendorRule
            {
                Name = ruleModel.Name,
                Rule = ruleEntity
            };

            return(vendorRule);
        }
            public async Task SkipFirstStagePresentation_When_SkipFirstPresentation_ShouldAddSkipFirstPresentationStageRule()
            {
                // Arrange
                var rule = new PgVendorRuleDefinition
                {
                    Splits = new PgVendorRuleSplit[0]
                };
                var ruleStr  = JsonConvert.SerializeObject(rule);
                var ruleMode = new VendorRuleModel
                {
                    Name                  = "Vendor payment rule 1",
                    Definition            = JsonConvert.DeserializeObject <Dictionary <string, dynamic> >(ruleStr),
                    SkipFirstPresentation = true
                };
                var vendor = new Vendor {
                    Name = "Vendor 1"
                };
                var skipFirstPresentationRule = new Rule
                {
                    Name       = Constants.Rules.Stage.SkipFirstPresentation,
                    Type       = RuleType.Stage,
                    Definition = JsonConvert.SerializeObject(new PgStageRuleDefinition())
                };

                _efContext.Rule.Add(skipFirstPresentationRule);
                _efContext.SaveChanges();

                // Act
                var vendorRules = await _pgVendorRuleBuilder.ValidateAndGetVendorRules(ruleMode, vendor, It.IsAny <Guid>());

                var parsedResults = vendorRules.Select(r =>
                                                       JsonConvert.DeserializeObject <PgPaymentRuleDefinition>(r.Rule.Definition))
                                    .ToArray();

                //Assert
                parsedResults.Should().HaveCount(2);
                vendorRules.Count(r => r.Rule.Type == RuleType.VendorStage).Should().Be(1);
                vendorRules.Count(r => r.Rule.Type == RuleType.VendorPayment).Should().Be(1);
                var stageRule = vendorRules.First(vr => vr.Rule.Type == RuleType.VendorStage);

                stageRule.Rule.Definition.Should().Be(skipFirstPresentationRule.Definition);
            }
예제 #3
0
        protected CompiledRule <TestRule> GetBudgetRegionCriterion(string regionName, decimal split)
        {
            const string regionFieldName = "BudgetRegion";
            var          rule            = new Rule
            {
                Type      = RuleType.CommonPayment,
                Criterion = new RuleCriterion
                {
                    FieldName   = regionFieldName,
                    Operator    = "Equal",
                    TargetValue = regionName
                },
                Definition = JsonConvert.SerializeObject(new Dictionary <string, dynamic>
                {
                    { "split", split }
                })
            };

            return(Service.GetCompiledRule <TestRule>(rule));
        }
예제 #4
0
        private PaymentAmountResult CalculateStagePaymentAmountsWithRule(
            dataAccess.Entity.Rule paymentRule,
            string costStageKey,
            PgPaymentRule input,
            bool isProjection,
            CostStageRevisionTotalPayments previousPayments,
            bool isAipeProjection       = false,
            CostSectionTotals rawTotals = null,
            bool isNextStageCalc        = false
            )
        {
            var res = new PaymentAmountResult();
            var pgPaymentRuleData = JsonConvert.DeserializeObject <PgPaymentRuleDefinition>(paymentRule.Definition);

            res.IsDetailedSplit = pgPaymentRuleData.DetailedSplit;
            var     costStage = (CostStages)Enum.Parse(typeof(CostStages), costStageKey);
            decimal?totalCalculatedValue;

            res.CostCarryOverAmount = 0;
            // do the specific calculations here:
            // - sum up all the individual costs
            // -- if it is an AIPE projection (projection of any stage triggered in AIPE stage) - we only have TargetBudgetTotal, so use that anyways
            if (pgPaymentRuleData.DetailedSplit && !isAipeProjection)
            {
                // ADC-2243: run through the payment allocation rule to ensure the 'input' is correctly allocated
                //ADC-2711: check null and get 0
                res.InsuranceCostPayment = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.InsuranceTotal, costStage) ?? 0;
                var talentFeeCostPayment = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TalentFees, costStage);

                if (talentFeeCostPayment.HasValue)
                {
                    res.TalentFeeCostPayment = talentFeeCostPayment;
                }

                res.PostProductionCostPayment = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.PostProduction, costStage) ?? 0;
                res.ProductionCostPayment     = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Production, costStage) ?? 0;
                res.TechnicalFeeCostPayment   = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TechnicalFee, costStage) ?? 0;
                res.OtherCostPayment          = GetAllocatedSplitPayment(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Other, costStage) ?? 0;

                res.TargetBudgetTotalPayment = 0m;

                // AIPE specific handling of target budget
                if (costStage == CostStages.Aipe)
                {
                    res.TargetBudgetTotalPayment = input.TargetBudgetTotalCost * (pgPaymentRuleData.GetSplitByNameAndStage(Constants.CostSection.TargetBudgetTotal, costStage) ?? 0);
                }

                totalCalculatedValue =
                    res.InsuranceCostPayment
                    + res.OtherCostPayment
                    + res.PostProductionCostPayment
                    + res.ProductionCostPayment
                    + res.TargetBudgetTotalPayment
                    + res.TechnicalFeeCostPayment;

                //ADC-2711: if DPV then exclude postproduction cost due to it has already included into Othercost in GetAllocation method
                if (paymentRule.Type == RuleType.VendorPayment)
                {
                    totalCalculatedValue = totalCalculatedValue - res.PostProductionCostPayment;
                }

                // we only need carry over amounts for detailed splits - for non detailed splits all payments are already contained within TotalCost
                if (costStage == CostStages.Aipe)
                {
                    // we store it as a carry over for other stages to deduct from
                    // only if the calculated value is > 0 - otherwise no payment is made in this stage -> no carry over amount
                    res.CostCarryOverAmount = totalCalculatedValue > 0 ? input.TotalCostAmount - totalCalculatedValue : 0;
                }
                else if (input.IsAIPE)
                {
                    if (input.CostCarryOverAmount > 0)
                    {
                        if (input.CostCarryOverAmount - totalCalculatedValue > 0)
                        {
                            // deduct current payment from the "pot" and store the remainder
                            res.CostCarryOverAmount = input.CostCarryOverAmount - totalCalculatedValue;
                        }
                        else
                        {
                            res.CostCarryOverAmount = 0;
                        }
                    }

                    // we need to subtract current payment from previous target budget total payments to never lose them
                    totalCalculatedValue = totalCalculatedValue - input.CostCarryOverAmount;
                }
                else
                {
                    if (totalCalculatedValue < 0)
                    {
                        // carry over amount will be negative in non-aipe overpayment cases
                        res.CostCarryOverAmount = totalCalculatedValue;
                    }
                }
            }
            else if (isAipeProjection && costStage == CostStages.FinalActual)
            {
                // we pretend to pay the rest on AIPE FA stage since detailed rules won't have a split for Cost Total
                totalCalculatedValue = input.TotalCost;
            }
            else
            {
                totalCalculatedValue = input.TotalCost * (pgPaymentRuleData.GetSplitByNameAndStage(Constants.CostSection.CostTotal, costStage) ?? 0);
            }

            // - if the stage = Final Actual - return whatever result (even if negative)
            // -  otherwise - if <=0 return 0
            if (totalCalculatedValue < 0 && costStage != CostStages.FinalActual && costStage != CostStages.FinalActualRevision)
            {
                res.TotalCostAmountPayment = 0;
            }
            else
            {
                if ((costStage == CostStages.FinalActual || costStage == CostStages.FinalActualRevision) && input.CostCarryOverAmount < 0)
                {
                    // previous stage resulted in a negative calculation, despite us reporting 0, we still need to subtract that negative value
                    totalCalculatedValue += input.CostCarryOverAmount;
                }

                res.TotalCostAmountPayment = totalCalculatedValue;
            }

            res.TotalCostAmount = totalCalculatedValue;

            res.MatchedPaymentRule = paymentRule;
            res.IsProjection       = isProjection;
            res.StageName          = input.CostStages;

            CostSectionTotals totals = null;

            if (rawTotals != null)
            {
                //ADC-2711 overwrite line_item_full_cost values to display on payment summary screen
                if (paymentRule.Type == RuleType.VendorPayment && isNextStageCalc == false)
                {
                    var insuranceCostPaymentAllocation      = GetAllocation(input, paymentRule.Type, Constants.CostSection.InsuranceTotal) ?? 0;
                    var postProductionCostPaymentAllocation = GetAllocation(input, paymentRule.Type, Constants.CostSection.PostProduction) ?? 0;
                    var productionCostPaymentAllocation     = GetAllocation(input, paymentRule.Type, Constants.CostSection.Production) ?? 0;
                    var technicalFeeCostPaymentAllocation   = GetAllocation(input, paymentRule.Type, Constants.CostSection.TechnicalFee) ?? 0;
                    var otherCostPaymentAllocation          = GetAllocation(input, paymentRule.Type, Constants.CostSection.Other) ?? 0;
                    var talentFeePaymentAllocation          = GetAllocation(input, paymentRule.Type, Constants.CostSection.TalentFees) ?? 0;


                    var insuranceCostTotal      = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.InsuranceTotal, costStage) ?? 0;
                    var postProductionCostTotal = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.PostProduction, costStage) ?? 0;
                    var productionCostTotal     = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Production, costStage) ?? 0;
                    var technicalFeeCostTotal   = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TechnicalFee, costStage) ?? 0;
                    var otherCostTotal          = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Other, costStage) ?? 0;
                    var talentFeeCostTotal      = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TalentFees, costStage) ?? 0;

                    totals = new CostSectionTotals
                    {
                        // Recalculate allocated amount because it depends on the rule
                        InsuranceCostTotal      = GetDisplayedValueOnPaymentSummaryPage(insuranceCostPaymentAllocation, insuranceCostTotal),
                        PostProductionCostTotal = GetDisplayedValueOnPaymentSummaryPage(postProductionCostPaymentAllocation, postProductionCostTotal),
                        ProductionCostTotal     = GetDisplayedValueOnPaymentSummaryPage(productionCostPaymentAllocation, productionCostTotal),
                        TechnicalFeeCostTotal   = GetDisplayedValueOnPaymentSummaryPage(technicalFeeCostPaymentAllocation, technicalFeeCostTotal),
                        OtherCostTotal          = GetDisplayedValueOnPaymentSummaryPage(otherCostPaymentAllocation - postProductionCostPaymentAllocation, otherCostTotal - postProductionCostTotal),
                        TalentFeeCostTotal      = GetDisplayedValueOnPaymentSummaryPage(talentFeePaymentAllocation, talentFeeCostTotal),
                        TargetBudgetTotal       = input.TargetBudgetTotalCost ?? 0 + previousPayments.TargetBudgetTotalCostPayments,
                        TotalCostAmountTotal    = input.TotalCostAmount
                    };
                }
                else
                {
                    totals = new CostSectionTotals
                    {
                        // Recalculate allocated amount because it depends on the rule
                        InsuranceCostTotal      = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.InsuranceTotal, costStage) ?? 0,
                        PostProductionCostTotal = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.PostProduction, costStage) ?? 0,
                        ProductionCostTotal     = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Production, costStage) ?? 0,
                        TechnicalFeeCostTotal   = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TechnicalFee, costStage) ?? 0,
                        OtherCostTotal          = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Other, costStage) ?? 0,
                        TalentFeeCostTotal      = GetAllocatedAmount(rawTotals, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TalentFees, costStage) ?? 0,

                        TargetBudgetTotal    = input.TargetBudgetTotalCost ?? 0 + previousPayments.TargetBudgetTotalCostPayments,
                        TotalCostAmountTotal = input.TotalCostAmount
                    };
                }
            }

            var paymentTotals = new CostSectionTotals
            {
                // Recalculate allocated amount because it depends on the rule
                TalentFeeCostTotal      = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TalentFees, costStage) ?? 0,
                InsuranceCostTotal      = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.InsuranceTotal, costStage) ?? 0,
                PostProductionCostTotal = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.PostProduction, costStage) ?? 0,
                ProductionCostTotal     = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Production, costStage) ?? 0,
                TechnicalFeeCostTotal   = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.TechnicalFee, costStage) ?? 0,
                OtherCostTotal          = GetAllocatedAmount(input, paymentRule.Type, pgPaymentRuleData, Constants.CostSection.Other, costStage) ?? 0,

                TargetBudgetTotal    = input.TargetBudgetTotalCost ?? 0 + previousPayments.TargetBudgetTotalCostPayments,
                TotalCostAmountTotal = input.TotalCostAmount
            };

            res.TotalRemainingPayment = new PgPaymentRule
            {
                BudgetRegion          = input.BudgetRegion,
                ContentType           = input.ContentType,
                CostType              = input.CostType,
                CostStages            = input.CostStages,
                ProductionType        = input.ProductionType,
                DirectPaymentVendorId = input.DirectPaymentVendorId,
                IsAIPE                = input.IsAIPE,
                TotalCostAmount       = input.TotalCostAmount,
                TotalCost             = input.TotalCost,
                TargetBudgetTotalCost = input.TargetBudgetTotalCost,
                CostCarryOverAmount   = input.CostCarryOverAmount,

                // Recalculate remaining payment amount because allocated amount depends on macthed rule
                StageTotals        = totals,
                InsuranceCost      = paymentTotals.InsuranceCostTotal - previousPayments.InsuranceCostPayments,
                TalentFeeCost      = paymentTotals.TalentFeeCostTotal - previousPayments.TalentFeeCostPayments,
                PostProductionCost = paymentTotals.PostProductionCostTotal - previousPayments.PostProductionCostPayments,
                ProductionCost     = paymentTotals.ProductionCostTotal - previousPayments.ProductionCostPayments,
                TechnicalFeeCost   = paymentTotals.TechnicalFeeCostTotal - previousPayments.TechnicalFeeCostPayments,
                OtherCost          = paymentTotals.OtherCostTotal - previousPayments.OtherCostPayments
            };

            return(res);
        }