package gw.plugin.reinsurance
uses gw.api.locale.DisplayKey
uses gw.assignment.AssignmentUtil
uses gw.reinsurance.agreement.RIProgramValidation
uses gw.reinsurance.risk.RIRiskValidation
uses gw.validation.PCValidationContext
uses gw.pl.currency.MonetaryAmount
uses gw.api.system.PCLoggerCategory
uses java.util.List
@Export
class PCReinsuranceCedingPlugin extends PCReinsuranceCedingPluginBase {
var _logger = PCLoggerCategory.REINSURANCE_CEDING_PLUGIN
construct() { }
/**
* Add a PolicyPeriod to the work queue in order to get its cedings calculated.
* This is currently only used for initial ceding, not a full recalculation.
*
* All ceding calculation is done via a work queue. In order to trigger the calculation,
* the entity which needs calculations (e.g. a whole PolicyPeriod in the case of a
* branch promotion) must be added to the queue.
*
* @param period The PolicyPeriod for which ceding calculations are needed
*/
override function enqueueForCeding(period : PolicyPeriod, reason : RIRecalcReason, comment : String) {
if (period.AllReinsurables.Count > 0) {
createWorkItem(period, reason, comment, false, period.Bundle)
_logger.info("Added policy ${period.PolicyNumber}, branch ${period.BranchName}, reason ${reason}, comment ${comment} to queue for ceding")
}
}
/**
* Calculate the cedings for the given PolicyPeriod.
*
* This method is called by the work queue.
*
* @param period The PolicyPeriod for which ceding calculations are needed
* @param recalculateAll true if all cedings on this period should be recalculated
* @param reason Reason this ceding calculation is being done
* @param comment If this is a recalculation, an optional comment explaining the recalc
*/
override function calculateCedingForPeriod(period : PolicyPeriod, recalculateAll : boolean, reason : RIRecalcReason, comment : String, updateUser : User) {
if (reason == null) {
// Catch this now rather than waiting for DBNullConstraintException
throw "Reason for ceding calcuation may not be null"
}
_logger.info("Ceding calculation requested for policy ${period.PolicyNumber}, branch ${period.BranchName}, reason ${reason}")
if (recalculateAll) {
var periodInBundle = period.regenerateRisks()
// continue to cede even if there are invalid attachments
// better to cede invalid amounts than nothing at all
validateAttachments(periodInBundle, updateUser)
periodInBundle.Bundle.commit()
}
var newCedings = processCedings(period, reason, comment)
// Print out Cedings
newCedings.each( \ c -> {
_logger.debug("Cedings against ${c.Cost.ActualAmount} for coverage ${c.Cost}")
c.Cedings.each(\ t -> _logger.debug(" -> ceding ${t.CedingRate}% (${t.CededPremium}) - ${t.Agreement.Commission}% commisson (${t.Commission}) to agreement ${t.Agreement.Name} for dates ${t.EffectiveDate} - ${t.ExpirationDate}"))
})
}
protected function validateAttachments(period : PolicyPeriod, activityUser : User) {
var context = new PCValidationContext(ValidationLevel.TC_DEFAULT)
period.AllReinsurables.each(\ r -> {
for (ririsk in r.RIVersionList.AllVersions) {
ririsk.updateCedingOnAllAttachments()
//validate net retention
if (ririsk.FacRINeeded.IsPositive) {
//check for UW approval
var uwIssue = period.UWIssuesActiveOnly.firstWhere(\ u -> u.IssueKey == r.UWIssueKey and u.IssueType.Code == "RINetRetention")
if (uwIssue == null or (uwIssue.ApprovalValue != null and ririsk.FacRINeeded > new MonetaryAmount(uwIssue.ApprovalValue))) {
createActivity(period, activityUser, DisplayKey.get("Web.Reinsurance.RIRisk.Validation.InvalidAttachmentForRisk", r), DisplayKey.get("UWIssue.Reinsurance.NetRetentionGreaterThanTarget.ShortDesc"))
break;
}
}
//validate attachments
var validator = new RIRiskValidation(context, ririsk)
validator.validate()
if (validator.Result.Errors.HasElements) {
var desc = ""
validator.Result.ErrorMessages.each(\ o -> {desc += (o + "\n")})
createActivity(period, activityUser, DisplayKey.get("Web.Reinsurance.RIRisk.Validation.InvalidAttachmentForRisk", r), desc)
break;
}
}
})
}
private function createActivity(period : PolicyPeriod, activityUser : User, subject :String, description : String) {
var activity = ActivityPattern.finder.getActivityPatternByCode("notification").createJobActivity(period.Bundle, period.Job,
subject, description, null, null, null, null, null)
if (not activity.assignUserAndDefaultGroup(activityUser)) {
activity.assign( AssignmentUtil.DefaultUser.DefaultAssignmentGroup, AssignmentUtil.DefaultUser )
}
}
protected function processCedings(period : PolicyPeriod, reason : RIRecalcReason, comment : String) : List {
return new PCCedingCalculator().processCedings(period, reason, comment)
}
// old signature...some tests still rely on this.
protected function processCedings(period : PolicyPeriod, fullRecalc : boolean, reason : RIRecalcReason, comment : String) : List {
return new PCCedingCalculator().processCedings(period, reason, comment)
}
/**
* Sample optimization - if dirty programs only have policy attachments and the Policy does not start within any program, we can skip recalculation
*/
override function shouldRecalculateCeding(workItem : RICedingWorkItem) : boolean {
if (workItem typeis RIProgramChangeCedingWorkItem) {
if(workItem.ChangedPrograms.hasMatch(\ r -> r.Program.LossDateAttachmentTreaties.HasElements)) {
return true
} else {
var periodStart = workItem.PolicyPeriod.PeriodStart
return (workItem.ChangedPrograms.hasMatch(\ r -> r.Program.EffectiveDate.beforeOrEqual(periodStart) and r.Program.CedingRecalcExpirationDate.after(periodStart)))
}
}
return true
}
override function userResponsibleForProgramChange(period : PolicyPeriod, dirtyPrograms : RIProgram[]) : User {
var program = period.RIRiskVersionLists*.AllVersions*.PolicyAttachmentProgram.firstWhere(\ r -> dirtyPrograms.contains(r))
if (program == null) {
program = period.RIRiskVersionLists*.AllVersions*.LossDateAttachmentProgram.firstWhere(\ r -> dirtyPrograms.contains(r))
}
return program == null ? period.UpdateUser : program.ResponsibleUser
}
override function logErrorForInvalidPrograms(programs : List) {
var context = new PCValidationContext(ValidationLevel.TC_DEFAULT)
programs.each(\ r -> {
var validator = new RIProgramValidation(context, r)
validator.validate()
if (validator.Result.Errors.HasElements) {
//create warning in log
_logger.warn(DisplayKey.get("Web.Reinsurance.Program.Validation.InvalidProgram", r.DisplayName))
}
})
}
}