Payment Funcionality migration strategy
Step 0. Review and adjustments to Payment Layer:
Summary
We need to ensure that existing Payment Layer functionality can be used by the Social Protection modules.
Great News:
Bills and Invoices are using generic keys. We should be able to use them with no issues with Social Benefit Packages.
Not so great news:
PaymentPlan and GenericPlan are using benefit_id marked as Products.
GenericPlans are using following relation:
benefit_plan = models.ForeignKey(Product, db_column="BenefitPlanID", on_delete=models.deletion.DO_NOTHING)
Currently existing calculation rules will not be compatible with the BenefitPlanPackages (that are actually Products).
GenericPlans are not used in calculation rules explicitly (ContributionPlans are used).
Possible Approaches
Fastest option:
One solution could be to mock existing structure in the BenefitPlan. However, it is unintuitive and probably will cost a lot of technical debt.
Best Option:
In the long term, the best option would be to not rely on ORM objects and managers in terms of CalculationRules, but rather python Interfaces (that would free us from anything stored in the database, Interface could be implemented for Products, BenefitPlans and others). Different calculation rules rely on other IMIS objects, like PHs or Insurees. Those also could be resolved by set of Interfaces and model proxies. Downside is that this solution is time consuming. (We would need to check if there's capacity for this - to be checked with Patrick and Seweryn).
Other Options:
In scope of coreMIS integration we can slightly refactor Payment Plans and remove GenericPlan-Product relation. ContributionPlans would still rely on Products but there would be another entity using BenefitPlan instead of Product.
Steps:
Migrate
benefit_plan field from GenericPlan to PaymentPlan. (It could be a new entity like ProductPlan [used explicitly by Products], using ContributionPlan will be simpler as we wouldn't have to make plenty of changes in CalculationRules).
Do research on what calculation rules match Social Protection requirements.
Create new calculation rules for social protection OR if there are some we can use then generify them with different Benefits if possible (follow approach with interfaces from previous point).
Create a new PaymentPlan model for the BenefitPlan (could be SocialBenefitPlan).
It’s not ideal as we would rather have single “PaymentPlan” interface which would be implemented by proxy models for BenefitPlan and PaymentPlan but currently there are plenty of imports to change and it would be rather big rework for calc rules
Downside:
This will obscure how Graphql and Service classes will looks like (we would combine 2 different models under one source and there would be plenty of ifs)
OR
To avoid downside from the previous option we could add a mandatory “Type” field to the PaymentPlan. Currently it could be “InsuranceProduct” or “SocialBenefit”. We still would need to alternate calculation rules and (for now) handle only the “InsuranceProduct” type or implement a solution with an interface. This would allow us to create different types of interfaces for different paymentPlan types. This will require migration from benefit_id from Product reference to generic foreign key.
Generic Payment Plan:
code = models.CharField(db_column="Code", max_length=255, blank=True, null=True)
name = models.CharField(db_column="Name", max_length=255, blank=True, null=True)
calculation = models.UUIDField(db_column="calculationUUID", null=False)
benefit_plan = models.ForeignKey(Product, db_column="BenefitPlanID", on_delete=models.deletion.DO_NOTHING)
periodicity = models.IntegerField(db_column="Periodicity", null=False)
benefit_plan_type = models.ForeignKey(ContentType, models.DO_NOTHING, null=False, unique=False)
benefit_plan_id = models.CharField(null=False) # object is referenced by uuid
benefit_plan = GenericForeignKey('benefit_plan_type', 'benefit_plan_id')
Then we need to decide if dispatching data is on CalcRule or Model site:
If on models site then:
Create Calculation Rule interface that is used (instead of things like Product.objects.get(uuid=payment_plan.benefit_id).specific_product_field it should be CalculationSubject.get_amount())
And there should be Proxy model for the PaymentPlan with implementation of this interface. E.g. PamentPlanCommisionCalcRule
However, as Calculation Rules can be added and should be easily added to the PaymentPlans (not the other way around), then we should add interface on CalcRule module.
For instance:
CalculationSubject():
Def get_amount(payment_plan):
If payment_plan.type == ‘product’:
Return product.benefit_plan.ceiling
Elif payment_plan.type == ‘benefitplan’:
Return payment_plan.json_ext[‘amount’]
Else:
Raise notImplemented()
Requirements
Create Payment Plans for benefit plans
User stories:
As a Payment Plan manager I want to be able to choose Benefit Type in the PaymentPlan view (at the moment Product or BenefitPlan).
As a Payment Plan manager I want to have a dropdown with calculation rules relevant only for a given type of benefit (e.g. fee for service can be available only for the Product).
As a Payment Plan manager I want to have dropdown with Products/Benefit Plans for selected Benefit Type
Use cases:
Use Case1. Create Payment Plan
Actors: Payment Plan Manager, Contribution Plan Module, Product CalculationRule
Preconditions: Modules Installed, Manager has rights to create new Plans
Main Flow:
Payment Plan Manager goes to Legal and Finance -> Payment Plans
Payment Plan Manager created new Payment Plan
Payment Plan Manager selects “Product” Benefit Type from dropdown
If only one Benefit Type is available it’s automatically selected
New dropdowns: CalculationRules and Product dropdown appear and contains relevant Calculation Rules and Insurance Products
Payment Plan Manager Selects Calculation rule and Product
Additional Parameters menu appears
Payment Plan Manager fills additional parameters menu.
All required field are filled and Payment Plan can be saved
Payment Plan Manager saves PaymentPlan and gets confirmation.
PaymentPlan is saved as InsureeProductPaymentPlan (type)
Alternative course:
For step 3:Payment Plan Manager selects “Benefit Plan” Benefit Type from dropdown
New dropdowns: CalculationRules and BenefitPlan dropdown appear and contains relevant Calculation Rules and Insurance Products
Payment Plan Manager Selects Calculation rule and Benefit Plan
Additional Parameters menu appears
Payment Plan Manager fills additional parameters menu.
All required field are filled and Payment Plan can be saved
Payment Plan Manager saves Payment Plan and gets confirmation.
PaymentPlan is saved as SocialBenefitPaymentPlan (type)
Postconditions: New PaymentPlan is available and listed
List SocialBenefitPaymentPlan Payment Plans
User stories:
As a Payment Plan Manager I want to filter Payment Plans based on type.
Questions:
How to determine what Calculation Rules refers to what BenefitType?
Additional Array in frontend contribution?
Configured on backend calc rule definition and used by frontend?
Step 1. Payment value
Summary
Payment value is used to determine what amount will be delivered to the beneficiary assuming that certain criteria are met. This will vary between different benefit plans and should be highly customizable.
We need to include (on the payment plan level) functionality that will allow us to determine what amount is to be paid for beneficiary in a single batch.
There is fixed batch (same value for all beneficiaries) and variable amount (based on several rules):
Value can be added to flat-rate amount based on certain conditions
2 types of amounts:
One counted up to ceiling, e.g. beneficiary meets plenty of rules that would triple their payment, however maximum they can get is still some predetermined max amount
One without limitations and don’t count towards the ceiling (because they could be transaction fees and don’t go directly to the beneficiary but are used to perform operations)
Could include a rule when variable amount include previous uncollected payments
Limits per single transaction payment - flag transaction and take action
Calculations for benefit plans can be done based on calculation rules, however execution is yet to be determined.
Under the social protection menu we can add a new View called for example “Payment Cycles” which will allow us to create a new Payment Cycle for a Payment Plan.
Possible Approaches
a) and b) can be implemented using a mix of calculation rules and custom filters. Additional Parameters for BenefitPlan would be an expandable list with input contained of custom filter, type of value and rate.
Example:
Flat value: | >=0 (base for all payments) | |
Max amount: | >0 and < flat value | |
Custom filter rule | Count to max False | Fixed Amount |
Custom filter rule | Count to max True | Calculated amount? |
c) This could be added as a new parameter in the Additional Parameters
Include previous uncollected payments? | True / False |
d) Transaction limits could be set on the plan level, we don’t have flows at the moment however this will rely on them (waiting for Patrick’s document and development of the multi level approval)
Development Note: Calculation Rules shouldn’t be implemented for the ORM Models but for Particular interfaces. This will allow us to handle different types of Benefit plans (group/individual) in scope of single rule and reduce complexity in the Payment Plan setup.
Flow
@startuml start :Trigger Payment Plan Batch; repeat :Fetch BenefitPlanPackage ; :payment=PaymentPlan.baseAmount maxAmount=PaymentPlan.ceiling-payment userVariableAmount=0; repeat :Get next Variable Amount Calculation; if (BenefitPlanContition not met (custom filter)) then (yes) elseif (Does amount apply for limitations) then (no) :payment += calculatedAmount; elseif (userVariableAmount exceed maxAmount) then (no) :userVariableAmount += calculatedAmount; endif repeat while (next BenefitPlanCondition available?) is (yes) if (maxAmount >= userVariableAmount) then (yes) :payment += userVariableAmount; else (no) :payment += maxAmount; endif if (include previous uncollected payments?) then (yes) repeat :Add uncollected payments; note left: Moving invoice from previous payment? repeat while (previous payment cycle exists) else (no) endif :create new invoice for calculated payment; if (exceeds payment limit) then (yes) :flag invoice to confirmation; :set invoice to SUSPENDED; else (no) endif repeat while (next benefit package available?) is (yes)
stop
@enduml
|
Questions:
How to handle uncollected payments? Simply adding them to paid amount might be unintuitive and it’d be good to keep track of the uncollected payments.
Requirements
Create Benefit Plan Payment Rules
User stories:
As a Payment Plan manager I want to have the ability to establish rules for calculating payments for beneficiaries based on fixed values and conditional amounts based on filters
As a Payment Plan manager I want to set payment ceiling for conditional amount
As a Payment Plan manager I want to determine if given conditional amount is used in calculation of ceiling
As a Payment Plan manager I want to determine payment limit per beneficiary
As a Payment Plan manager I want to determine if previous uncollected payments should be included in the run.
[TODO: Add Use Cases]
Approve Exceeded Payments
User stories
As an Accountant I want to list all payments that were suspended due to exceeded payment limit
As an Accountant I want to approve/reject exceeded payments
[TODO: Add Use Cases]
Note: This point doesn’t cover execution of the given payment plan. Execution will be done as a Payment Cycle (Payment Cycle can be treated as a Batch execution for a particular payment plan in a particular period).
Step 2. Payment Cycle
Summary
Payment cycle can be treated as an accounting period. It will be created based on the Payment Plan (its calculation rules as well as periodicity).
Each payment cycle has:
Start date and end date (in coreMIS) - used to flag payments as delivered on time
Rebates configuration,
amount to be received per beneficiary in the payroll,
for payrolls using the PPM App, the maximum allocated amount to be carried by the Payment Point Manager
Allows rejection of payment cycle
Allows for a maker-checker flow to approve payment cycles before they can be used.
Possible approaches
As we want to rely on the Payment Layer the Payment Cycle at its current form would serve a different purpose. Most of the setup and configuration will be done in the Payment Plan. Accordingly:
Start and end date - this would be based on the Payment Plan cycle. Payment Plan has periodicity calculated in the number of months. If we would like to keep the same approach then each cycle would be based on the periodicity and start date. We still need to make a decision on how to set the start date. It can be:
First day of month
N-th month day date chosen on the payment plan level
Be based on the BenefitPlan definition (starting from Date Valid From and lasts until Date Valid To)
Then creating new Payment Cycles would depend on the consecutive periods calculated on those criteria and already created payment plans.
For example:
Payment plan has periodicity = 3 and it’s for payment plan valid from 1st Jan 2020 until 31 Dec. 2022.
This will allow us to create 8 payment cycles in scope of this project (if cycle was created for e.g. 2 quarter of 2021 it will not be possible to create another one for this period).
Rebates would be configured on the Payment Plan level. They will be automatically applied to created invoices during creation of the PaymentCycle
Amount to be received will be calculated based on the payment plan definition.
As for maker-checker flow we could create the Payment Cycle in DRAFT state and all Invoices would also be set to DRAFT. Then after approval is done it can be worked on later on.
Questions:
Should there be any set of rules/conditions that have to be fulfilled before a given payment plan can be moved from the draft state. For instance: no blocked payments?
@startuml actor Accountant as A boundary PaymentCycleView as PPV control PaymentCycleControler as PC control PaymentPlanControler as PP entity InvoiceORM as I collections "Calculation Rules" as CL
A --> PPV: Create new payment cycle\n PPV --> PC: new PaymentCycle(paymentPlan, period) PC --> PP: triggerBatch(paymentPlan, cycle) PP --> CL: runBatch(benefitPlan, period) CL --> I: Create invoices CL --> PP: Return created invoices PP --> PC: Success(invoices) PC --> I: Add invoices to payment cycle PC --> PPV: Success PPV --> A: Success
@enduml
|
Example flow is based on triggering logic implemented in Step 1. and then making a relation between created invoices and newly created PaymentCycle.
Note: If we would like to take an approach when cycles are independent from benefit plans then invoices for benefit plans would become line items in invoices.
Note 2: This solution doesn’t include the possibility to reject payment cycles. We could say that payment cycle can be recreated if it’s not reconciled or that we can freely edit invoices in scope of the particular payment plan. It depends on the reason why given payment plan should be rejected
Requirements
Create new PaymentCycle
User Stories
As an accountant I want to be able to create new payment cycle based on the existing Payment Plans
As an accountant I want to make sure that invoices created for specific periods are not duplicated
[TODO: Add Use Cases]
Flow to approve payment cycles before they can be used
User Stories
As an payment manager I want to be able to approve payment cycle before payments are distributed
[TODO: Add Use Cases]
Reject PaymentCycle
Not Applicable at the moment
Step 3. Payment Points
Summary
Payment points are an aggregation method that allows splitting payrolls into smaller subgroups which results in better management of beneficiaries.
We need to be able to:
Create Payment Points
Use known criteria to create payment points automatically
Review the criteria used in creation of subgroups
Allows rejection of payment points
Approval flow
Suggested Solution
Choice will depend on how often criteria change and at which point payment points are determined on business level. We could create Payment Points on several levels:
Global - when Payment Points are independent from the Benefit Plans and can be shared
Benefit Plan - when Payment Points are know and fixed during the life of the social project and are shared between multiple payment plans
Payment Plan - when Payment Points are depending on the Payment Types and are different for multiple payment methods.
Payment Cycle - when each payment cycle has it’s own setup of the Payment Points
Place where Payment Points are set is not blocking as most likely the flow will remain the same.
Payment point manager will create a set of custom filter rules that will assign matching beneficiaries to the particular payment point. If filters are overlapping then either exception is thrown or beneficiary is assigned to the first matching group.
We need to confirm if “standard” custom filters will be sufficient or if we have to extend it. For example, we could create groups like:
Location Code | Equals | R1D1V1 |
And create point for this specific location, but there’s an option that aggregation on property level could be desired:
Aggregation | Location Code |
That would create payment points for each available location code. If we want to use the latter it’ll require additional development as it’s outside of scope for existing filters.
Example Picker for Payment Points:
Payment Points |
|
|
|
Name: PP1 | Filter Filed Location Code | Filter Type Equal | Value R1D1V1 |
| Filter Filed Age | Filter Type GTE | Value 10 |
Name: PP2 | Filter Filed Location Code | Filter Type Non Equal | Value R1D1V1 |
| Filter Filed Age | Filter Type LTE | Value 10 |
Would configure 2 payment points.
If we assume that payment points are stored on PaymentPlan level then Payment Points would be created and managed from PaymentPlan level.
When a new payment cycle is created then bills and invoices would be assigned to proper payment points by the calculation rule.
How payment points are created :
@startuml actor PaymentPointAdministrator as A boundary PaymentPlanView as PPV control PaymentPlanControler as PPC control PaymentPointControler as PP control BenefitPlanControler as BF entity InvoiceORM as I collections "Calculation Rules" as CL
A --> PPV: Create new payment point PPV --> BF: Fetch custom filters for benefit plan BF --> PPV: Send custom filter options PPV --> A: give custom filters loop Create payment points A --> PPV: Create new PaymentPoint A --> PPV: Set filters for Payment Point end A --> PPV: Save payment points PPV --> PPC: send payment points PPC --> PP: createPaymentPoints(payment points, paymentPlan) PP --> PPC: success PPC --> PPV: success PPV --> A: success @enduml
|
How payment points are utilized:
@startuml control PaymentCycleControler as PCC participant EventHub as E control PaymentPointControler as PP control BenefitPlanControler as BF entity InvoiceORM as I
PCC --> E: PaymentCycleCreatedEvent E --> PP: SendSignal(paymentCycle) loop for each invoice in paymentCyce PP --> I: Get invoice PP --> PP: Get invoice beneficiary alt Beneficiary matches PaymentPoint criteria PP--> PP: Add invoice to payment point else end
end @enduml
|
Requirements
Define Payment Points
User stories
As Payment Point Administrator I want to create rules that determine if given payment should be done in scope of specific payment point
As Payment Point Administrator I want to edit conditions.
Connect Payment Points with payments
Approval flow
Step 4. Payment managers
Summary
Allows for the creation of payment point managers
Allows searching of payment point managers
Allows to view details of payment point managers
Allows maker checker flow to approve payment point managers
Proposed solution
Add new system role.
Extend payment point picker:
Payment Points |
|
|
|
|
Name: PP1 | Filter Filed Location Code | Filter Type Equal | Value R1D1V1 | PPM: User A |
| Filter Filed Age | Filter Type GTE | Value 10 |
|
Name: PP2 | Filter Filed Location Code | Filter Type Non Equal | Value R1D1V1 | PPM: User A |
| Filter Filed Age | Filter Type LTE | Value 10 |
|
To be checked if PolicyHolder can take this role.
Questions:
What level of access should PPM have?
Does PPM have some specific attributes depending on the type of PaymentPlan (is PPM data different for Urban and Rural payments?)
Can we have > 1 PPM for payment points?
Requirements
Add new PPMs
Attach PPMs to the Payment Points
Step 4. Digital Payments Payroll
Summary
Payroll for digital payments allows creation of payroll that can be paid digitally.
As in the scope of a single benefit plan we can have beneficiaries that use either digital or on site payments we need to have a way to determine the payment method.
It should be possible to receive a summary of the invoices included in the payroll and summary of the total and number of unique invoices.
Invoices can be marked as duplicates.
Read-only csv of invoices can be downloaded.
Searching for invoices in payroll.
Rejection of payroll.
Maker-checker flow.
Feature to create PDF reports of invoices after approval.
Summary of total invoices and balance from digital account before payment can be started.
Allow integration with multiple Payment Service Providers
Report of invoices that were and were not paid successfully.
Proposed Solution
Payrolls in openIMIS are defined as all invoices created in scope of the single payment cycle for specific calculation rules.
In the Additional Attributes menu we will need to add a new section regarding the Payroll Method defined by a set of custom rules.
Nice to have: Define payment method indicators on benefit plan level and then choose it from dropdown in PaymentPlan instead of creating a new set of rules.
Payment Method Indicator | ||
Custom filter rule | Filter Type | Filter Value |
Custom filter rule | Filter Type | Filter Value |
As for the summary of the invoices it can be created in the PaymentCycle view. When we will have access to all invoices created in scope of single Payroll then by clicking on the icon we could display a summary (total number and unique invoices).
Marking duplicates should not be necessary. As we assume that one beneficiary will belong only to one PaymentPlan there should be no duplicates.
After payroll (by PaymentCycle batch) then all invoices should be in DRAFT state. We could add rejections of the invoices there.
Export and search will be done using search criteria.
As payrolls are created by the PaymentCycle batches, rejection of payrolls doesn’t seem to be necessary.
PDF Report can be done using reporting and ReporBro, we need to determine aggregations and fields.
As for making payments via API integration - flow needs to be determined. At which point we want to make the calls? We could use search criteria to determine “batch” which allows us to send particular invoices for payments.
Proposed Flow
@startuml actor Accountant as A boundary PaymentCyclesView as PCVs boundary PaymentCycleView as PCV control PaymentCycleController as PCC collections PaymentServiceProvider as PPS boundary PaymentLayerAdapter as PLA boundary ExternalPaymentSystem as EPS entity InvoiceORM as I
A --> PCVs: Select specific payment cycle PCVs --> PCV: Route to payment cycle PCV --> A: Return specific payment cycle A --> PCV: Select invoices for making payments A --> PCV: Send invoices for payment PCV --> PCC: Send invoices for payment
loop for each invoice PCC --> PPS: Send Payment for invoice PPS --> PLA: Send Payment PLA --> EPS: Send Payment Request EPS --> PLA: Acknowledge PLA --> I: Change invoice status to validated PLA --> PPS: Success note right To be checked how statuses should be affected end note PPS --> PCC: Success end PCC --> PCV: Success PCV --> A: Success
loop for each invoice alt Payment successful EPS --> PLA: Payment confirmation PLA --> I: Change status to PAID else Payment unsuccessful EPS --> PLA: Payment declination PLA --> I: Change status to CANCELLED end PLA --> PCC: Change invoice status to To Be Reconciled end
A --> PCV: Reconcile payment cycle PCV --> PCC: Reconcile payment cycle PCC --> PCV: Success PCV --> A: Success
@enduml
|
Questions:
What data should be displayed in the payroll summary?
What to do if a beneficiary is assigned by custom filters to more than one payment plan? Should we raise exceptions or assign them to the first available payment plan?
What are the repercussions for further payments if the invoice is rejected?
What data should be provided in the csv export?
What is the relationship between payroll and payment service provider? Is it 1 provider - 1 payment point or 1 provider - 1 payroll?
Step 5. Payroll Creation for Payment Point Managers
Summary
The main point of this functionality is to allow the creation of payroll which will be paid by payment point managers. It’s also crucial to introduce actions which can be done at payrolls
Did you encounter a problem or do you have a suggestion?
Please contact our Service Desk
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. https://creativecommons.org/licenses/by-sa/4.0/