# Cancellation Plugin



The Cancellation Plugin allows you to add [retention charges](/features/billing/retention-charges) to invoices during the policy cancellation process.

Retention charges can be specified as positive or negative amounts, providing the flexibility needed to implement minimum earned premium plans, short-rate penalties, fees, or refunds when policies are cancelled.

The Cancellation Plugin is triggered by pricing requests for [policy transactions](/features/policy-management/policy-transactions) with a `transactionCategory` of `cancellation`, as long as certain criteria are met. See the [Execution](#CancellationExecution) section for more information.

Implementation [#implementation]

Create a new Java class in the `src/main/java/com/socotra/deployment/customer` folder. All plugin code must be contained within this folder. We named our class `CancellationPluginImpl.java` in the example below, but you can name your class whatever you'd like.

Implement the `CancellationPlugin` interface, and override the method corresponding to your target product type.

For example, the following class contains a method that adds a retention charge to invoices when commercial auto policies are cancelled:

```java
public class CancellationPluginImpl implements CancellationPlugin {
    private static final Logger log = LoggerFactory.getLogger(CancellationPluginImpl.class);

    // Add a retention charge to invoices when commercial auto policies are cancelled

    @Override
    public CancellationPluginResponse cancel(CommercialAutoRequest commercialAutoRequest) {
        CommercialAutoSegment currentSegment = commercialAutoRequest.segment();

        return CancellationPluginResponse.builder()
                .retentionCharges(
                        RatingSet.builder()
                                .ok(true)
                                .ratingItems(List.of(
                                        RatingItem.builder()
                                                .elementLocator(currentSegment.element().locator())
                                                .chargeType(ChargeType.minimumPremium)
                                                .amount(BigDecimal.valueOf(50))
                                                .build()
                                ))
                                .build())
                .build();
    }
}
```

The method argument contains the following fields:

* `policy` - The policy
* `segment` - The current segment
* `transaction` - The policy transaction
* `charges` - A list of default pro-rated retention `charges` calculated by the system before the Cancellation Plugin was executed. See the [Execution](#CancellationExecution) section for more information.

The method returns a `CancellationPluginResponse` object, which contains a list of `RatingItem` objects. Each `RatingItem` object represents a retention charge and contains the following fields:

* `elementLocator` - The locator of the element related to the retention charge
* `chargeType` - The retention charge type
* `amount` - The retention charge amount

Retention charge types refer to <ApiLink name="ChargeRef" /> configuration objects with a `handling` value of `retention` and an `invoicing` value of `next`. ChargeRef names must be included in the list of `charges` in the <ApiLink name="ProductRef" /> configuration object for your target policy.

Retention charges can be associated with any element within a policy. Only one charge per retention charge type can be associated with each element.

Here's an example of a ChargeRef configuration object called `MinimumPremium`:

```json
{
	"category": "premium",
	"handling": "retention",
	"invoicing": "next"
}
```

Here's an example of the `charges` field in a ProductRef configuration object called `CommercialAuto`:

```json
{
	"charges": ["MinimumPremium"]
}
```

Data Fetcher [#data-fetcher]

The [Data Fetcher](/configuration/plugins/overview#PluginDataFetcher) can provide additional data required to calculate retention charges. The following methods are especially useful for this purpose:

* `getTermCharges()` - Retrieves all non-zero charges for issued transactions within a term
* `getTermSubsegmentSummaries()` - Retrieves a summary of each segment in a term, including the effective duration, effective charges, and extension [data](/configuration/data-extensions/overview)

<span id="CancellationExecution" />

Execution [#execution]

The Cancellation Plugin is triggered when policies are cancelled, as long as the following criteria are met:

* The cancellation takes effect during a coverage segment rather than a gap segment, meaning it is not a re-cancellation that takes effect after an existing cancellation
* The cancellation is not the re-application of an existing reversed cancellation for another [out-of-sequence](/features/policy-management/out-of-sequence-transactions) aggregate transaction

Policies can be cancelled via the [Policy Transactions API](/api/policy-management/policy-transactions), or as a result of a [delinquency](/features/billing/delinquency) if the delinquency plan has been configured to create a policy transaction with a `transactionCategory` of `cancellation`.

The Cancellation Plugin is triggered when [stateless invoice previews](/features/preview-operations#StatelessPreview) are generated. This allows you to preview retention charges.

The system begins the cancellation pricing process by first calculating default pro-rated retention charges, then executing the Cancellation Plugin.

After the plugin has executed successfully, the system calculates the final retention charges that will appear on the next invoice containing [applicable uninvoiced installments](/features/billing/retention-charges) by adding up the default pro-rated retention charges and the retention charges added by the Cancellation Plugin.

Finally, the system persists the calculation results.

If plugin execution fails, or the plugin returns an `ok` value of `false`, the cancellation pricing process will stop, cancellation pricing data will not be persisted, and the policy transaction will remain in the `validated` state. The Cancellation Plugin must be triggered again to restart the cancellation pricing process.

Output Validation [#output-validation]

The system automatically performs the following validation checks on each `RatingItem` object returned by the Cancellation Plugin:

* `amount` is a required field and must be a positive or negative value
* An `amount` of `0` will not result in a retention charge
* `rate` and `rateDifference` will be ignored and overwritten by the system
* `chargeType` must be included in the list of `charges` in the <ApiLink name="ProductRef" /> configuration object for your target policy

Plugin Considerations [#plugin-considerations]

* Minimum earned premium and minimum premium are distinct concepts enforced by different plugins at different points in the [policy lifecycle](/features/policy-management/policy-transactions#Process). Minimum earned premiums are enforced via the Cancellation Plugin when policies are cancelled, ensuring that a minimum amount is retained for a policy. Minimum premiums are enforced via the [Rating Plugin](/configuration/plugins/rating) when policies are issued, ensuring the initial policy premium meets a minimum threshold.
* Using the Cancellation Plugin does not guarantee an effective minimum earned premium for a term. Depending on how transactions are issued, it is possible for the effective premium for a given term to be less than the minimum earned premium as enforced by the plugin. For example, a policy cancellation could take effect on the second day of a term and then get reinstated on the last day of the term.
* Any minimum earned premium determinations made at the time of plugin execution are subject to change before the cancellation effective date due to the potential creation of flat charges via API requests.

Example [#example]

The following example is based on the Prism configuration. Contact your Socotra representative for more information.

```java
// Enforce a minimum earned premium of $100 when a policy is cancelled

public class CancellationPluginImpl implements CancellationPlugin {
    private static final Logger log = LoggerFactory.getLogger(CancellationPluginImpl.class);

    @Override
    public CancellationPluginResponse cancel(CommercialAutoRequest commercialAutoRequest) {
        Transaction transaction = commercialAutoRequest.transaction();
        CommercialAutoSegment currentSegment = commercialAutoRequest.segment();
        Collection<Charge> cancellationCharges = commercialAutoRequest.charges();

        if (cancellationCharges == null) {
            cancellationCharges = List.of();
        }

        log.info("All cancellation charges: {}", cancellationCharges);

        Map<ULID, Collection<Charge>> termCharges = DataFetcher.getInstance().getTermCharges(commercialAutoRequest.policy().latestTermLocator());

        log.info("All term charges: {}", termCharges);

        BigDecimal totalPremium = termCharges.values().stream()
                .flatMap(Collection::stream)
                .map(Charge::amount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalCancellationPremium = cancellationCharges.stream()
                .map(Charge::amount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal earnedPremium = totalPremium.add(totalCancellationPremium);
        BigDecimal minimumEarnedPremium = BigDecimal.valueOf(100L);
        BigDecimal retentionCharge = minimumEarnedPremium.subtract(earnedPremium);

        log.info("Total premium: {}, cancellation premium: {}, earned: {}, retention: {}",
                totalPremium,
                totalCancellationPremium,
                earnedPremium,
                retentionCharge);

        if (retentionCharge.compareTo(BigDecimal.ZERO) > 0) {
            log.info("Adding minimum earned retention charge to transaction {}, segment {}, element {}",
                    transaction.locator(),
                    currentSegment.locator(),
                    currentSegment.element().locator()
            );

            return CancellationPluginResponse.builder()
                    .retentionCharges(
                            RatingSet.builder()
                                    .ok(true)
                                    .ratingItems(List.of(
                                            RatingItem.builder()
                                                    .elementLocator(currentSegment.element().locator())
                                                    .chargeType(ChargeType.minimumPremium)
                                                    .amount(retentionCharge)
                                                    .build()
                                    ))
                                    .build())
                    .build();
        } else {
            return createEmptyRatingSet();
        }
    }

    private static CancellationPluginResponse createEmptyRatingSet() {
        return CancellationPluginResponse.builder()
                .retentionCharges(
                        RatingSet.builder()
                                .ok(true)
                                .ratingItems(List.of())
                                .build())
                .build();
    }
}
```

Next Steps [#next-steps]

* [Automation Plugin](/configuration/plugins/automation)

See Also [#see-also]

* [Plugins Overview](/configuration/plugins/overview)
* [Retention Charges](/features/billing/retention-charges)
* [Policy Transactions](/features/policy-management/policy-transactions)
* [Policy Transactions API](/api/policy-management/policy-transactions)
* [Policy Lifecycle](/features/policy-management/policy-transactions#Process)
* [Delinquency](/features/billing/delinquency)
* [Plugin Data Fetcher](/configuration/plugins/overview#PluginDataFetcher)
