package gw.policy
uses com.guidewire._generated.security.AuditPermKeys
uses com.guidewire._generated.security.CancellationPermKeys
uses com.guidewire._generated.security.IssuancePermKeys
uses com.guidewire._generated.security.PolicyChangePermKeys
uses com.guidewire._generated.security.PolicyPeriodPermKeys
uses com.guidewire._generated.security.ReinstatementPermKeys
uses com.guidewire._generated.security.RenewalPermKeys
uses com.guidewire._generated.security.RewriteNewAccountPermKeys
uses com.guidewire._generated.security.RewritePermKeys
uses com.guidewire._generated.security.SubmissionPermKeys
uses com.guidewire.pl.system.security.PermissionKey
uses entity.Contact
uses gw.account.AccountQueryBuilder
uses gw.api.archive.PCArchivingUtil
uses gw.api.database.IQuery
uses gw.api.database.IQueryBeanResult
uses gw.api.database.QueryOptimizers
uses gw.api.locale.DisplayKey
uses gw.api.productmodel.Product
uses gw.api.system.PCConfigParameters
uses gw.api.util.DisplayableException
uses gw.contact.ContactQueryBuilder
uses gw.contact.PolicyContactRoleQueryBuilder
uses gw.job.JobQueryBuilder
uses gw.job.audit.AuditInformationQueryBuilder
uses gw.search.EntitySearchCriteria
uses gw.search.NameCriteriaImpl
uses pcf.AssignUserRoleOwnerPopup
/**
* Search criteria supporting the policy/job search pages.
*
* Note: Name searches must search both contact and revisioned data
* for the periods. This is done as a UNION query where the one branch
* restricts against the referenced contact in any period and the other
* branch restricts against the revisioned data in any bound period.
*/
@Export
class PolicyPeriodSearchCriteria extends EntitySearchCriteria implements gw.search.NameCriteria {
/**
* Enumerates the different ways to search for a PolicyPeriod with the PolicyPeriodSearchCriteria and encapsulates
* the varying behavior.
*/
enum PolicyPeriodSearchType {
Cancellation("Web.PolicySearch.PolicyPeriodSearchType.Cancellation", CancellationPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_CANCELLATION),
FinalAudit("Web.PolicySearch.PolicyPeriodSearchType.FinalAudit", AuditPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_AUDIT, TC_FINALAUDIT),
Issuance("Web.PolicySearch.PolicyPeriodSearchType.Issuance", IssuancePermKeys.VIEW, TC_JOBEXCLUSIVE, TC_ISSUANCE, null, null),
Policy("Web.PolicySearch.PolicyPeriodSearchType.Policy", PolicyPeriodPermKeys.VIEW, TC_POLICYEXCLUSIVE, null, null, \ pps -> entity.Policy.finder.findPolicyByPolicyNumber(pps.PolicyNumber)),
PolicyChange("Web.PolicySearch.PolicyPeriodSearchType.PolicyChange", PolicyChangePermKeys.VIEW, TC_JOBEXCLUSIVE, TC_POLICYCHANGE),
PremiumReport("Web.PolicySearch.PolicyPeriodSearchType.PremiumReport", AuditPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_AUDIT, TC_PREMIUMREPORT),
Reinstatement("Web.PolicySearch.PolicyPeriodSearchType.Reinstatement", ReinstatementPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_REINSTATEMENT),
Renewal("Web.PolicySearch.PolicyPeriodSearchType.Renewal", RenewalPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_RENEWAL),
Rewrite("Web.PolicySearch.PolicyPeriodSearchType.Rewrite", RewritePermKeys.VIEW, TC_JOBEXCLUSIVE, TC_REWRITE),
RewriteNewAccount("Web.PolicySearch.PolicyPeriodSearchType.RewriteNewAccount", RewriteNewAccountPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_REWRITENEWACCOUNT),
Submission("Web.PolicySearch.PolicyPeriodSearchType.Submission", SubmissionPermKeys.VIEW, TC_JOBEXCLUSIVE, TC_SUBMISSION)
static property get CopyPolicyDataValues() : List {
return {Policy, Submission, PolicyChange, Renewal, Rewrite, RewriteNewAccount}
}
var _displaykeyString : String
var _permissionKey : PermissionKey as readonly PermissionKey
var _multipleAssignmentCategory : UserRoleConstraint as readonly MultipleAssignmentCategory
var _jobSubtype : typekey.Job as readonly JobSubtype
var _auditScheduleType : AuditScheduleType as readonly AuditScheduleType
var _mapPolicyPeriodSummaryToUserRoleOwner : block(PolicyPeriodSummary) : UserRoleOwner = \ pps -> pps.Job
private construct(displayKeyString : String,
permissionKey: PermissionKey,
multipleAssignmentCategory : UserRoleConstraint,
jobSubtype: typekey.Job,
auditScheduleType : AuditScheduleType = null,
mapOverride : block(PolicyPeriodSummary) : UserRoleOwner = null) {
_displaykeyString = displayKeyString
_permissionKey = permissionKey
_multipleAssignmentCategory = multipleAssignmentCategory
_jobSubtype = jobSubtype
_auditScheduleType = auditScheduleType
if (mapOverride != null) {
_mapPolicyPeriodSummaryToUserRoleOwner = mapOverride
}
}
/**
* Maps a PolicyPeriodSummary array to an UserRoleOwner array. By default, this maps
* to the period's job, but other enums (e.g. Policy) may behave differently.
*/
function mapPolicyPeriodSummariesToUserRoleOwners(policyPeriodSummaries : PolicyPeriodSummary[]) : UserRoleOwner[] {
return policyPeriodSummaries.map(\pps -> _mapPolicyPeriodSummaryToUserRoleOwner(pps))
}
property get DateFieldsSearchType() : List {
return AuditSearch ? DateFieldsToSearchType.TF_AUDITSEARCHTYPES.TypeKeys : DateFieldsToSearchType.TF_NONAUDITSEARCHTYPES.TypeKeys
}
property get AuditSearch() : boolean {
return _jobSubtype == TC_AUDIT
}
property get PolicySearch() : boolean {
return this == Policy
}
override function toString() : String {
return DisplayKey.get(_displaykeyString)
}
}
/**
* If true, the search criteria uses producer code security to limit the results to jobs and policy periods that
* are viewable by the user.
*/
var _secure : boolean as Secure = false
/**
* If true, includes archived policy periods in the search.
*/
var _includeArchived: boolean as IncludeArchived = false
/**
* Account number to search on.
*/
var _accountNumber: String as AccountNumber
/**
* PolicyNumber to search on.
*/
var _policyNumber: String as PolicyNumber
/**
* Search on policy periods of a specific Product.
*/
var _product: Product as Product // The Product for which to search
var _asOfDate: java.util.Date as AsOfDate // POLICY ONLY: As Of Date for which to search. Only applies to policy searches; ignored for job searches.
var _primaryInsuredCity: String as PrimaryInsuredCity // Primary Insured city for which to search
var _primaryInsuredState: State as PrimaryInsuredState // Primary Insured state for which to search.
var _primaryInsuredPostalCode: String as PrimaryInsuredPostalCode // Primary Insured postal code for which to search
var _primaryInsuredCountry : Country as PrimaryInsuredCountry // Primary Insured country for which to search.
var _primaryInsuredPhone : String as PrimaryInsuredPhone // PrimaryInsured Phone number for which to search
var _primaryInsuredCityKanji : String as PrimaryInsuredCityKanji // Primary Insured city in Kanji for which to search
var _jobNumber: String as JobNumber // Job number for which to search.
var _jobOpen: boolean as JobOpen = false // Search for only open jobs if true. Search for all jobs otherwise.
var _multiVersionSearch: boolean as MultiVersionSearch = false // Search multiple versions of a job instead of only searching for the selected one. Ignored for policy searches. // todo mky: change this to "OnlyJobSelectedVersion"
var _searchType: PolicyPeriodSearchType as SearchObjectType = Policy // Distinguishes between various job types (e.g., Submission, Cancellations) and Policies // todo mky rename to SearchType
var _permissiveSearch : boolean as PermissiveSearch = true // Use the most permissive search possible. May cause performance problems if set to true. Setting this to false may break legacy behavior.
var _state: Jurisdiction as State // State that policy-associated contacts are in.
var _ignoreSubmissionPolicyNumber : boolean as IgnoreSubmissionPolicyNumber = true // If true, ignores the PolicyNumber field if the SearchObjectType is set to Submission
var _cancellationReasonCode: ReasonCode as CancelReasonCode // Cancellation reason codes; filter on CancelSource
var _cancellationRefundCalcMethod: CalculationMethod as CancelRefundCalcMethod // The method use to calculate the amount of refund due
var _cancellationSource : CancellationSource as CancelSource // Party that initiated cancellation (carrier or insures)
var _dateCriteria : DateCriteria as DateCriteria = new DateCriteria() // use the non-persistent entity and maybe replace it in the future with a Gosu class
delegate _nameCriteria represents gw.search.NameCriteria
var _taxId : String as TaxId // The contact's tax identification number.
var _name : String as Name // The last name or the first name.
var _nameKanji : String as NameKanji
var _accountHolderContact : Contact as AccountHolder // The account holder for which to search. If specified, any name criteria will be ignored.
var _actualAuditMethod : AuditMethod as ActualAuditMethod // The actual audit method used in auditInformation
var _producer : Organization as Producer // The organization for which to search
var _producerCode : ProducerCode as ProducerCode //Searching against the producer code
var _excludedPolicyPeriod : PolicyPeriod as ExcludedPolicyPeriod // A policy period to exclude from the search results.
var _assignedRisk : boolean as AssignedRisk // Flag for policy/risk assigned by state requirement
var _uwCompany : UWCompany as UWCompany //Underwriting company that insures this policy
var _rejectReason : ReasonCode as RejectReason // The reason that this job was declined
var _nonRenewalCode : NonRenewalCode as NonRenewalCode // NonRenewal reason codes
var _securityUser = User.util.CurrentUser
var _archiveEnabled = PCArchivingUtil.isArchiveEnabled()
construct() {
_nameCriteria = new NameCriteriaImpl()
}
static property get MaxSearchResults() : int {
return PCConfigParameters.PolicySearchMaxResult.Value
}
internal property set SecurityUser(value : User) { // internally accessible for testing
_securityUser = value
}
internal property get SecurityUser() : User { // internally accessible for testing
return _securityUser
}
internal property get ArchiveEnabled() : boolean { // internally accessible for testing
return _archiveEnabled
}
internal property set ArchiveEnabled(value : boolean) { // internally accessible for testing
_archiveEnabled = value
}
override protected property get InvalidSearchCriteriaMessage(): String {
return null
}
override protected property get MinimumSearchCriteriaMessage(): String {
return MeetsMinimumSearchCriteria ? null : DisplayKey.get("Web.Policy.MinimumSearchCriteria")
}
protected property get MeetsMinimumSearchCriteria() : boolean {
return _nameCriteria.OfficialId.NotBlank
or _accountNumber.NotBlank
or _policyNumber.NotBlank
or _primaryInsuredPhone.NotBlank
or (_producer != null and not User.util.CurrentUser.ExternalUser) // Producer is force-populated for external users and therefore ignored
or _producerCode != null
or _jobNumber.NotBlank
or AnyKanjiNameNotBlank
or MeetsMinimumCompanyNameCriteria
or (MeetsMinimumPersonNameCriteria and (_nameCriteria.LastNameExact or MeetsMinimumLocationCriteria or _permissiveSearch))
}
protected property get MeetsMinimumCompanyNameCriteria() : boolean {
return _nameCriteria.CompanyName.NotBlank and (_nameCriteria.CompanyNameExact or _nameCriteria.CompanyName.length >= 5 or _permissiveSearch)
}
protected property get MeetsMinimumPersonNameCriteria() : boolean {
return isNotBlankAndLongEnoughOrExactMatching(_nameCriteria.FirstName, 3, _nameCriteria.FirstNameExact)
and isNotBlankAndLongEnoughOrExactMatching(_nameCriteria.LastName, 3, _nameCriteria.LastNameExact)
}
protected property get MeetsMinimumLocationCriteria() : boolean {
return (PrimaryInsuredCityNotBlank and _primaryInsuredState != null) or _primaryInsuredPostalCode.NotBlank
}
protected property get AnyKanjiNameNotBlank() : boolean {
return _nameCriteria.CompanyNameKanji.NotBlank or _nameCriteria.FirstNameKanji.NotBlank or _nameCriteria.LastNameKanji.NotBlank
}
protected property get PrimaryInsuredCityNotBlank() : boolean {
return _primaryInsuredCity.NotBlank or _primaryInsuredCityKanji.NotBlank
}
protected function isNotBlankAndLongEnoughOrExactMatching(value: String, minLength: int, exactMatching: boolean) : boolean {
return value.NotBlank and (value.length >= minLength or exactMatching)
}
protected override function doSearch(): IQueryBeanResult {
var normalBuilder = makeQueryBuilderForNormalSearches()
var revisionedBuilder = makeQueryBuilderForRevisionedNameSearches()
var archivedBuilder = makeQueryBuilderForArchivedNameSearches()
var results = unionResultSet(unionResultSet(normalBuilder.build(), revisionedBuilder?.build()), archivedBuilder?.build()).select()
return limitResultCount(results)
}
/**
* Add the normal restrictions, and then search for name and primary insured address criteria on the contact table
* via the policy contact roles.
*/
protected function makeQueryBuilderForNormalSearches() : PolicyPeriodSummaryQueryBuilder {
var builder = configurePolicyPeriodSummaryQueryBuilder(new PolicyPeriodSummaryQueryBuilder())
if (HasAnyNameCriteria or HasPrimaryInsuredAddressCriteria) {
// The normal builder adds name and primary insured address criteria on the contact table via the policy contact roles.
var contactQueryBuilder = new ContactQueryBuilder()
if (HasAnyNameCriteria) {
configureFromNameCriteria(contactQueryBuilder)
}
if (HasPrimaryInsuredAddressCriteria) {
configureContactQueryBuilderWithPrimaryInsuredAddress(contactQueryBuilder)
}
builder.withPolicyContactRole(new PolicyContactRoleQueryBuilder().withSubtype(TC_POLICYPRINAMEDINSURED).withContactDenorm(contactQueryBuilder))
}
return builder
}
/**
* Make a query builder to search for revisioned name searches. It returns null if there's no need for such searches,
* which happens when there are no revisioned fields specified in the name criteria.
* If it creates a query builder, it adds the normal restrictions, and then searches for the name criteria within
* the policy contact roles' internal fields. It then adds the primary insured address criteria on the contact table
* via the policy contact roles.
*/
protected function makeQueryBuilderForRevisionedNameSearches() : PolicyPeriodSummaryQueryBuilder {
var builder : PolicyPeriodSummaryQueryBuilder
if (HasAnyNameCriteriaOtherThanOfficialID) {
var policyContactRoleQueryBuilder = _nameCriteria.configureFromNameCriteria(new PolicyContactRoleQueryBuilder().withSubtype(TC_POLICYPRINAMEDINSURED)) // this method doesn't handle OfficialID
builder = configurePolicyPeriodSummaryQueryBuilder(new PolicyPeriodSummaryQueryBuilder())
.withPolicyContactRole(policyContactRoleQueryBuilder)
if (HasPrimaryInsuredAddressCriteria or _nameCriteria.OfficialId.NotBlank) {
var contactQueryBuilder = new ContactQueryBuilder()
policyContactRoleQueryBuilder.withContactDenorm(contactQueryBuilder)
if (HasPrimaryInsuredAddressCriteria) {
configureContactQueryBuilderWithPrimaryInsuredAddress(contactQueryBuilder)
}
if (_nameCriteria.OfficialId.NotBlank) {
contactQueryBuilder.withOfficialId(_nameCriteria.OfficialId)
}
}
}
return builder
}
/**
* Make a query builder that includes archived policies. It returns null if there's no need for such searches.
* If it creates a query builder, it adds the normal restrictions, and then searches for the name and primary
* insured address criteria on the contact table via the primary named insured contact denorm (this is different
* from how the normal searches get to the contact table).
*/
protected function makeQueryBuilderForArchivedNameSearches() : PolicyPeriodSummaryQueryBuilder {
var builder : PolicyPeriodSummaryQueryBuilder
if ((HasAnyNameCriteria or HasPrimaryInsuredAddressCriteria) and _includeArchived) {
var contactQueryBuilder = new ContactQueryBuilder()
if (HasAnyNameCriteria) {
configureFromNameCriteria(contactQueryBuilder)
}
if (HasPrimaryInsuredAddressCriteria) {
configureContactQueryBuilderWithPrimaryInsuredAddress(contactQueryBuilder)
}
builder = configurePolicyPeriodSummaryQueryBuilder(new PolicyPeriodSummaryQueryBuilder())
.withPrimaryNamedInsuredContactDenorm(contactQueryBuilder)
}
return builder
}
protected function configureContactQueryBuilderWithPrimaryInsuredAddress(contactQueryBuilder : ContactQueryBuilder) : ContactQueryBuilder {
return contactQueryBuilder.withCityDenormStarting(_primaryInsuredCity)
.withCityKanjiDenormStarting(_primaryInsuredCityKanji)
.withStateDenorm(_primaryInsuredState)
.withPostalCodeDenormStarting(_primaryInsuredPostalCode)
.withCountryDenorm(_primaryInsuredCountry)
.withWorkPhone(_primaryInsuredPhone)
}
protected function unionResultSet(query1 : IQuery, query2 : IQuery) : IQuery {
if (query1 == null and query2 == null) {
throw new java.lang.IllegalArgumentException("query1 and query2 cannot both be null")
}
if (query1 == null) {
return query2
}
if (query2 == null) {
return query1
}
var union = query1.union(query2)
if (PCConfigParameters.DisableIndexFastFullScanForPolicySearch.Value) {
union.getRawQuery().addOptimizer(QueryOptimizers.createDisableIndexFastFullScan())
}
if (PCConfigParameters.DisableHashJoinPolicySearch.Value) {
union.getRawQuery().addOptimizer(QueryOptimizers.createDisableHashJoin())
}
if (PCConfigParameters.DisableSortMergeJoinPolicySearch.Value) {
union.getRawQuery().addOptimizer(QueryOptimizers.createDisableSortMergeJoin())
}
return union;
}
protected function limitResultCount(results : IQueryBeanResult) : IQueryBeanResult {
if (results.getCountLimitedBy(MaxSearchResults + 1) > MaxSearchResults) {
throw new DisplayableException((new com.guidewire.pl.system.locale.DisplayKey("Java.Search.TooManyResults")).localize({MaxSearchResults}))
}
return results
}
protected function configurePolicyPeriodSummaryQueryBuilder(builder : PolicyPeriodSummaryQueryBuilder) : PolicyPeriodSummaryQueryBuilder {
if (_searchType == Policy) {
builder.withMostRecentModel(true) // in force
.withAsOfDate(_asOfDate)
} else {
var jobQueryBuilder = new JobQueryBuilder()
.isOpen(_jobOpen ? true : null) // we either search for open jobs, or don't care
.withJobNumber(_jobNumber)
.cast(_searchType.JobSubtype)
builder.withJob(jobQueryBuilder)
.withOnlyJobSelectedVersion(!_multiVersionSearch)
.withExcludedPolicyPeriod(_excludedPolicyPeriod)
if (_searchType == Cancellation) {
jobQueryBuilder.withCancellationReasonCode(_cancellationReasonCode)
.withCancellationSource(_cancellationSource)
builder.withRefundCalcMethod(_cancellationRefundCalcMethod)
}
if (_searchType == Submission) {
jobQueryBuilder.withSubmissionReasonCode(_rejectReason)
}
if (_searchType == Renewal) {
jobQueryBuilder.withNonRenewalCode(_nonRenewalCode)
}
if (_searchType.JobSubtype == TC_AUDIT) {
var auditInformationQueryBuilder = new AuditInformationQueryBuilder()
jobQueryBuilder.withAuditInformation(auditInformationQueryBuilder)
auditInformationQueryBuilder.withAuditScheduleType(_searchType.AuditScheduleType)
.withActualAuditMethod(_actualAuditMethod)
// the date criteria stuff would be cleaner without the non-persistent entity, but go with it for now
if (_dateCriteria.Set) {
var dateRange = _dateCriteria.getDateRangeToSearch()
if (_dateCriteria.DateFieldToSearch == TC_AUDITDUEDATE) {
auditInformationQueryBuilder.withDueDateWithin(dateRange.BeginDate, dateRange.EndDate)
} else if (_dateCriteria.DateFieldToSearch == TC_AUDITPERIODENDDATE) {
auditInformationQueryBuilder.withAuditPeriodEndDateWithin(dateRange.BeginDate, dateRange.EndDate)
}
}
} else {
if (_dateCriteria.Set) {
if (_dateCriteria.DateFieldToSearch == TC_EFFECTIVEDATE) {
var dateRange = _dateCriteria.getDateRangeToSearch()
builder.withEditEffectiveDateWithin(dateRange.BeginDate, dateRange.EndDate)
} else if (_dateCriteria.DateFieldToSearch == TC_CREATIONDATE) {
var dateRange = _dateCriteria.getDateRangeToSearch()
if (_searchType == Submission) {
jobQueryBuilder.withSubmissionDateWithin(dateRange.BeginDate, dateRange.EndDate)
} else {
jobQueryBuilder.withCreateTimeWithin(dateRange.BeginDate, dateRange.EndDate)
}
}
}
}
}
builder.withProducer(_producer).withProducerCode(_producerCode)
if (_secure) {
builder.withProducerCodeSecurityFor(_securityUser, _searchType.PermissionKey)
}
if (_searchType != Submission or !_ignoreSubmissionPolicyNumber) {
builder.withPolicyNumber(_policyNumber)
}
if (_product != null or _accountNumber != null) {
var policyQueryBuilder = new PolicyQueryBuilder()
policyQueryBuilder.withProduct(_product)
if (_accountNumber != null) {
policyQueryBuilder.withAccount(new AccountQueryBuilder().withAccountNumber(_accountNumber))
}
builder.withPolicy(policyQueryBuilder)
}
// Use ANY(ARRAY()) function on PostgreSQL when searching on Contact but not PolicyNumber or AccountNumber
if (_accountNumber.NotBlank or _policyNumber.NotBlank) {
builder.withUseAnyArrayForPolicyContactRoleSearch(false)
}
builder.withBaseState(_state)
.withAssignedRisk(_assignedRisk)
.withUWCompany(_uwCompany)
if (!_includeArchived and _archiveEnabled) {
builder.withPolicyTerm(new PolicyTermQueryBuilder().withPolicyTermArchiveState(TC_NOTARCHIVED))
}
return builder
}
protected property get HasPrimaryInsuredAddressCriteria() : boolean {
return _primaryInsuredCity.NotBlank
or _primaryInsuredCityKanji.NotBlank
or _primaryInsuredPostalCode.NotBlank
or _primaryInsuredState != null
or _primaryInsuredCountry != null
or _primaryInsuredPhone.NotBlank
}
//---------- Miscellaneous functionality
/**
* Used to reset the search for search items, typically after a search failure.
*/
function resetForSearchItems(policyPeriodSearchType : PolicyPeriodSearchType, searchText : String) {
this.SearchObjectType = policyPeriodSearchType
if (policyPeriodSearchType == Policy) {
this.PolicyNumber = searchText
this.JobNumber = null
} else {
this.PolicyNumber = null
this.JobNumber = searchText
}
}
property get DateFieldsSearchType() : List {
return _searchType.DateFieldsSearchType
}
/**
* Returns the appropriate PolicySearch LV mode to use in the UI.
*/
property get ResultsLVMode() : String {
return AuditSearch ? "Audit" : this.SearchObjectType.name()
}
property get AuditSearch() : boolean {
return _searchType.AuditSearch
}
property get PolicySearch() : boolean {
return _searchType.PolicySearch
}
static function getIncludeArchivedLabel(value : boolean): String {
return value ? DisplayKey.get("Web.PolicySearch.ArchivedChoices.IncludeArchived") : DisplayKey.get("Web.PolicySearch.ArchivedChoices.ExcludeArchived")
}
function jumpToAssignUserRoleOwnerPopup(periodSummaries: PolicyPeriodSummary[], role: typekey.UserRole) {
AssignUserRoleOwnerPopup.push(_searchType.mapPolicyPeriodSummariesToUserRoleOwners(periodSummaries), role)
}
}