-
Notifications
You must be signed in to change notification settings - Fork 32
Description
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
memberRateandcustomerRatefrom 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 ofdaysWorked - add field
paymentTotal- float, required, 0 by default
- add field
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
daysWorkedand nothing else. All other fields would be calculated by automation logic.
Work Period Payments
Work Period Payment Model
Add fields:
memberRate- optional floatcustomerRate- optional floatdaysPaid- 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 WPdays(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 fromRB.memberRate, if it'snullor0then return Error.customerRate- should be populated fromRB.customerRate(if not provided, thennull)billingAccountId- should be populated fromRB.billingAccountId, if it's not there then return Error.amount=WPP.memberRate * WPP.days / 5status=scheduledstatusDetails- would be only updated via SchedulerchallengeId- would be only updated via Scheduler
NOTE:
- This logic should be followed when we crate single payment using endpoint
POST /work-period-paymentsor multiple payments using the same endpoint (it supports arrays) - This logic should be also followed by the
POST /work-period-payments/queryendpoint. The only difference, we cannot passdaysfor payments, so in this casedaysshould 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
cancelledif the current status is NOTin-progress- any other can cancel - can be changed to
scheduledif the current status isfailed- so the scheduler would try to process payment one more time - cannot change status in any other case
- can be changed to
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 ofdaysWorkedare already paid, scheduled or in progress- it should get all
scheduled,in-progressandcompletedpayments of the WP and sumdaysof the payments, sodaysToPay = 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
schedulepreviousfailedpayment this value should be re-calculated again as we countscheduledpayments as paid
- it should get all
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-daysifdaysWorked === 0in-progressif any of the payments are inscheduledorin-progressstatuscompletedifdaysWorked - daysPaid === 0and there are noscheduledorin-progresspaymentspartially-completed(if we need to pay, but we have some payments completed and nothing is in progress) - i. e. ifdaysWorked - daysPaid > 0and there are some payments in statuscompletedand there are noscheduledorin-progresspaymentspending(if we need to pay, but there are no payments processed at all) - I. e. ifdaysWorked - daysPaid > 0and there are no ,completedpayments and noscheduledorin-progresspayments
paymentAmount- the total amount of payments that are NOTcancelledorfailed
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:
- remove all existent Work Periods and Work Period Payments
- regenerate Work Periods for all existent Resource Bookings using new logic, similar way as it was done before via https://github.com/topcoder-platform/taas-apis/blob/9ac2bdc6793d17d293cf3149ebd08bf6a6ae8fa0/migrations/2021-04-22-3-populate-work-periods-for-resource-bookings.js
- NOTE, that it has to be a new migration script (you can copy existent one) because the previous one was already run and we cannot run it again with the same name
General Requirements
- Follow Code Guideline in the README.
Verification Steps
- Create a RB with
memberRate=1000 - Get any WP with
daysWorked=5,WP.daysPaid=0,WP.paymentStatus="pending" - Update
WP.daysWorked=3
Result: updated:WP.daysWorked=3, same as before:WP.paymentStatus="pending",WP.daysPaid=0,WP.paymentTotal=0 - 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
- Payment scheduled:
- 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. - 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
- Payment completed:
- Update
WP.daysWorked = 2.
Result: Error - cannot updatedaysWorkedto the value less thandaysPiad - Try to create one payment
[POST { workPeriodId: WP.id } ].
Result: Cannot process one more payment because all 3 days Worked has been already paid. - Update
WP.daysWorked = 4
Result:WP.paymentStatus="partially-completed",WP.daysWorked=4, same as before:WP.daysPaid=3,WP.paymentTotal=600 - Update
RB.memberRate=2000 - 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
- 2nd payment created:
- 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
- Payment completed:
- Try to create one payment
[POST { workPeriodId: WP.id } ].
Result: Cannot process one more payment because all 4 days Worked has been already paid. - Set
WP.daysWorked = 5.
Result:WP.paymentStatus="partially-completed",WP.daysWorked=5, same as before:,WP.daysPaid=4, WP.paymentTotal=1000 - 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
- 3rd payment created:
- 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 indaysPaidand inpaymentTotal.
- Payment failed:
- 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, - 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