Skip to content

WP and WP Payments Updates #288

Closed
Closed
@maxceem

Description

@maxceem

Background

In the current logic we tracking amount inside payments. The same time we can process several payments for the same Work Period using different memberRate. Because of this we cannot understand if the payment is fully done for the week, underpaid or overpaid. To make sure we can correctly track the payment status we have to track days which has been paid in each payment. Also, we have to move memberRate and customerRate from Work Periods to payments for tracking purposes.

Work Periods

Work Period Model

  • Remove fields memberRate and customerRate from the model and endpoints. Because 1 Work Period could have multiple payments with different rates, so we would move these fields to the WorkPeriodPayment model
  • Add the next fields to the Work Periods model, but don't add them to the endpoints, they would be calculated automatically:
    • add field daysPaid - integer, required, 0 by default - this value would hold how many days we already paid for the Work Period out of daysWorked
    • add field paymentTotal - float, required, 0 by default

Work Period Endpoints (this section updated on June 11)

  • Remove endpoints to create WP and delete WP, but keep services. All WPs would be created and deleted automatically only. Update Swagger and Postman accordingly.
  • Endpoints to update WPs should ONLY allow us to update daysWorked and nothing else. All other fields would be calculated by automation logic.

Work Period Payments

Work Period Payment Model

Add fields:

  • memberRate - optional float
  • customerRate - optional float
  • daysPaid - required, integer, 0 by default

Work Period Payment Endpoints

Create (schedule) payment

POST /work-period-payments should accept payment body with the next fields only:

  • workPeriodId (required) - associate payment with WP
  • days (optional) - if provided we should validate that this value is integer more 1 or more, and not more than (WP.daysWorked - WP.daysPaid) or return error. If not provided then calculate automatically as (WP.daysWorked - WP.daysPaid), if the value is less than 1, then return an error, because there are no days to pay.

The next values should NOT be allowed in body during payment creation and should be calculated automatically:

  • memberRate - should be populated from RB.memberRate, if it's null or 0 then return Error.
  • customerRate - should be populated from RB.customerRate (if not provided, then null)
  • billingAccountId - should be populated from RB.billingAccountId, if it's not there then return Error.
  • amount=WPP.memberRate * WPP.days / 5
  • status=scheduled
  • statusDetails - would be only updated via Scheduler
  • challengeId - would be only updated via Scheduler

NOTE:

  • This logic should be followed when we crate single payment using endpoint POST /work-period-payments or multiple payments using the same endpoint (it supports arrays)
  • This logic should be also followed by the POST /work-period-payments/query endpoint. The only difference, we cannot pass days for payments, so in this case days should be always calculated automatically as shown above.

Update payment

PUT/PATCH /work-period-payments/:wppId should be updated to only allow changing status and nothing else

  • status
    • can be changed to cancelled if the current status is NOT in-progress - any other can cancel
    • can be changed to scheduled if the current status is failed - so the scheduler would try to process payment one more time
    • cannot change status in any other case

We should NOT be able to change other fields.

Automation logic

Each Work Period might have several payments, though we would like to be able to do sorting and filtering WPs by payment information.

When we create or update any payment inside WP or update dependant fields of the WP, we have to re-calculate fields automatically (inside event handlers). We should only update WP if any calculated value is really changed.

General Idea

If payment is in-progress, shceduled or completed - we count such payments as if we have them. If payment is cancelled or failed we don't count such payments.

Calcualtion Fields

  • daysPaid - how many of daysWorked are already paid, scheduled or in progress
    • it should get all scheduled, in-progress and completed payments of the WP and sum days of the payments, so daysToPay = sum(WP.payments, 'days')
    • note that if we cancel some payment, or some payment is failed, this value has to be re-calculated, as we don't count cancelled/failed payments as paid
    • the same thing if we schedule previous failed payment this value should be re-calculated again as we count scheduled payments as paid
  • paymentStatus - what is the current payments status
    Should be set as per next logic (the first status which matches the current situation is used):
    • no-days if daysWorked === 0
    • in-progress if any of the payments are in scheduled or in-progress status
    • completed if daysWorked - daysPaid === 0 and there are no scheduled or in-progress payments
    • partially-completed (if we need to pay, but we have some payments completed and nothing is in progress) - i. e. if daysWorked - daysPaid > 0 and there are some payments in status completed and there are no scheduled or in-progress payments
    • pending (if we need to pay, but there are no payments processed at all) - I. e. if daysWorked - daysPaid > 0 and there are no ,completed payments and no scheduled or in-progress payments
  • paymentAmount - the total amount of payments that are NOT cancelled or failed

This logic would be triggered by multiple events like Payment creation, Payment update, and WP update. So make sure that all these updates happen if any of the dependant values changed.

Migration

As we don't use Work Periods and Payments on production yet we can keep it simple and don't think how to migrate existent data.
Create a migration script in the migrations folder which would do the next thing:

General Requirements

Verification Steps

  1. Create a RB with memberRate=1000
  2. Get any WP with daysWorked=5, WP.daysPaid=0, WP.paymentStatus="pending"
  3. Update WP.daysWorked=3
    Result: updated: WP.daysWorked=3, same as before: WP.paymentStatus="pending", WP.daysPaid=0, WP.paymentTotal=0
  4. Schedule payment 1 [POST { workPeriodId: WP.id }]
    Result:
    • Payment scheduled: days = WP.daysWorked - WP.daysPaid (3), amount = WPP.memberRate * WPP.days / 5 ($600), memberRate=RB.memberRate ($1000)
    • WP automatically updated: WP.paymentStatus="in-progress", WP.daysPaid=3, WP.paymentTotal=600, same as before: WP.daysWorked=3
  5. Try to schedule one more payment immediately after that again.
    Result: Cannot process one more payment because all 3 days Worked has been already scheduled to be paid by the previous payment.
  6. After some time scheduler processed the payment.
    Result:
    • Payment completed: WPP.status="completed"
    • WP automatically updated: WP.paymentStatus="completed", same as before: WP.daysWorked=3, WP.daysPaid=3, WP.paymentTotal=600
  7. Update WP.daysWorked = 2.
    Result: Error - cannot update daysWorked to the value less than daysPiad
  8. Try to create one payment [POST { workPeriodId: WP.id } ].
    Result: Cannot process one more payment because all 3 days Worked has been already paid.
  9. Update WP.daysWorked = 4
    Result: WP.paymentStatus="partially-completed", WP.daysWorked=4, same as before: WP.daysPaid=3, WP.paymentTotal=600
  10. Update RB.memberRate=2000
  11. Process payment 2 [POST { workPeriodId: WP.id } ].
    Result:
    • 2nd payment created: days = WP.daysWorked - WP.daysPaid (1), amount = RB.memberRate / 5 * days ($400), memberRate=RB.memberRate ($2000)
    • WP automatically updated: WP.paymentStatus="in-progress", WP.daysPaid=4, WP.paymentTotal=1000, same as before: WP.daysWorked=4
  12. After some time scheduler processed the payment.
    Result:
    • Payment completed: WPP.status="completed"
    • WP automatically updated: WP.paymentStatus="completed", same as before: WP.daysWorked=4, WP.daysPaid=4, WP.paymentTotal=1000
  13. Try to create one payment [POST { workPeriodId: WP.id } ].
    Result: Cannot process one more payment because all 4 days Worked has been already paid.
  14. Set WP.daysWorked = 5.
    Result: WP.paymentStatus="partially-completed", WP.daysWorked=5, same as before:, WP.daysPaid=4, WP.paymentTotal=1000
  15. Process payment 3 [POST { workPeriodId: WP.id } ].
    Result:
    • 3rd payment created: days = WP.daysWorked - WP.daysPaid (1), amount = RB.memberRate / 5 * days ($400), memberRate=RB.memberRate ($2000)
    • WP automatically updated: WP.paymentStatus="in-progress", WP.daysPaid=5, WP.paymentTotal=1400, same as before: WP.daysWorked=5
  16. Imagine that after some time the scheduler tried to process the payment, but it failed.
    Result:
    • Payment failed: WPP.status="failed"
    • WP automatically updated: WP.paymentStatus="partially-completed", WP.daysPaid=4, WP.paymentTotal=1000, same as before: WP.daysWorked=5,
      NOTE, that failed payment is not counted in daysPaid and in paymentTotal.
  17. Cancel payment 1 (days = 3).
    Result: WP.paymentStatus="partially-completed", WP.daysPaid=1, WP.paymentTotal=400 (because we still have the 2nd payment complete), WP.daysWorked=5,
  18. Cancel payment 2 (days = 1).
    Result: WP.paymentStatus="pending", WP.daysPaid=0, WP.paymentTotal=0 (because all the payments are cancelled or failed and not counted), WP.daysWorked=5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions