Calculate Driver Advance Payment ver2
Version: Draft— Changes pending production deployment.
Overview
AThis is a cron job that runsexecuting every 5 minutes tothat calculatecomputes and create actualgenerates advance payment records perfor driver,drivers groupedbased byon their completeddelivered jobsorders onwithin a specificspecified date.Malaysia Usesdate range, leveraging JobCostSetup to determine payment amountsrates per route. Scoped to company ID 14 only.
| Task Code | Calculate Driver Advance Payment |
| Schedule | Every 5 minutes |
| Company Scope | ID = 14 (hardcoded) |
| Output Tables | driver_advance_payment, driver_advance_payment_job |
| Source File | src/_cron/cron.service.ts ~line |
Changelog
| Date | ||
|---|---|---|
| 2026-03-02 | sf | Scoped to company 14; switched order filter from job.jobStatus/supplierDoDatetime to order.order_status/unloaded_datetime; added MYT→UTC date range derivation; added latest-job-per-order subquery; fixed effectiveFrom comparison to use durationTo; added placeholder driver_advance_payment_job rows for no-cost/no-route-match cases; jobUnloadedDatetime now uses order.unloadedDatetime |
| 2026-02-25 | sf | Changed job jobStatus = COMPLETED to jobStatus = Delivered |
Previous versions are preserved in Page Revisions (top-right menu → Revisions).
Trigger Mechanism
A MicroserviceTask record must exist with code = Calculate Driver Advance Payment and status = Pending must exist in the database.. The cron picksselects the oldest pending task during each run.execution cycle.
Task Input
The task value field requires JSON with a target date (JSON)Malaysia date):
{
"targetDate": "2025-01-31"
}
Date format: yyyy-MM-dd. The value represents a Malaysia (MYT, UTC+8) calendar date.
Date Range Derivation (MYT → UTC)
targetDate is a Malaysia calendar date. Since order.unloaded_datetime is stored in UTC, the code converts the full Malaysia day to a UTC range before querying:
| Malaysia Time | UTC Stored |
|---|---|
2025-01-31 00:00:00 MYT |
2025-01-30 16:00:00 UTC → durationFrom |
2025-01-31 23:59:59 MYT |
2025-01-31 15:59:59 UTC → durationTo |
Both durationFrom and durationTo are used in the BETWEEN filter and for effectiveFrom comparison.
Processing Flow
PickSelect oldest Pending taskand mark RunningFetchParseall existing orderId valuestargetDatefromdriver_advance_payment_jobtask value; derivedurationFrom/durationTo(dedupMYTlist)day → UTC)QueryRetrieveJobs wherejobStatus = Delivered, DATE(order.supplierDoDatetime) = targetDate, and order.id NOT INall existing order IDs fromdriver_advance_payment_jobwhereorder.company_id = 14(deduplication scoped to company 14)- Query Orders (via latest Job per order) matching:
order.company_id = 14order.order_status = 'Delivered'order.unloaded_datetime BETWEEN durationFrom AND durationTo- Latest job per order (MAX job.id subquery)
- Joins: Driver (via job.driver_id), Company (via order.company_id)
- Exclude orders already in
driver_advance_payment_job
- Group resulting jobs by
driverIddriver ID (skip jobs with no driver) For eachPer driver:CollectIdentify unique routes (fromCountryIdoriginto→toCountryId)destination country)QueryFetch matchingJobCostSetupperrecords:route where effectiveFromeffective_from <=,targetDate,durationTocostFrequencyfrequencyBY_JOBorNULL, recipientDRIVER,is_deleted =, ordered byBY_JOB or NULL, payTo = DRIVER, isDeleted = false,falseeffectiveFromeffective_from DESC(latest rate wins)SumCalculate total amountacrossbyjobssummingxjobmatching× matched coststo get totalAmountINSERTCreatedriver_advance_paymentrecord withstatusPENDINGPENDINGstatus,paymentDate = targetDateINSERTPer job, createdriver_advance_payment_job:per- Cost
xmatchedmatching→ real cost fields,jobUnloadedDatetime = order.unloadedDatetime - No cost setup → placeholder with
null/0cost fields,jobUnloadedDatetime = order.unloadedDatetime - Cost exists but route not matched → same placeholder,
jobUnloadedDatetime = order.unloadedDatetime
job- Cost
- On error: log and continue to next driver
Finally updateUpdate taskstatustoSuccessorFail
Job Type DeterminationClassification
Determined by determineJobType(fromCountryId, toCountryId): assigns:
Output: driver_advance_payment
OneSingle record per driver per run.execution:
| Field | Value |
|---|---|
| driverId | |
| paymentDate | targetDate (Malaysia date string) |
| totalAmount | sum of |
| totalJobs | count of jobs |
| status | PENDING |
| isAutoGenerated | true |
| createdBy | System_Cron |
Output: driver_advance_payment_job
One recordor more records per job x(always matchingat costleast setup.one, even with no cost):
| Field | Value |
|---|---|
| driverAdvancePaymentId | parent |
| driverId | |
| jobId | |
| orderId | |
| jobCostSetupId | |
| jobCostName | |
| jobCostAmount | |
| jobPayTo | DRIVER or null |
| jobUnloadedDatetime | order. |
| jobType | LOCAL / EXPORT / IMPORT |
| isDeducted | false |
| createdBy | System_Cron |
ImportantCritical NotesConsiderations
DedupCompany-Scoped Deduplication — Existing order ID exclusion isglobalfiltered tocompany_id = 14; orders from other companies are unaffected- Always Tracked
-— Every delivered order always produces at least onedriver_advance_payment_jobrow (placeholder if no cost), preventing duplicate processing on subsequent cron runs - Zero-Amount Payments — Records generate even without matching cost setups (
totalAmount = 0), with placeholder job rows - Malaysia Date Handling —
targetDateis MYT; queries use UTC-converted range to correctly capture allexistingrecordsorderwithinIDsthat calendar day - effectiveFrom Comparison — Uses
durationTo(end of MYT day indriver_advance_payment_jobUTC),are excluded regardless of date. An order processed in any previous run will never be reprocessed, even ifnot thetaskrawistargetDaterequeuedstring,forto avoid missing costs set up within the samedate.Malaysia day Zero-amountLatestrecordJobstillpercreatedOrder-—ifOnlynothematchinghighestJobCostSetupjob.idper order isfoundprocessedfor(oneaadvancedriver,paymentaperrecord is still inserted with totalAmount = 0.order)Per-driverDriver-LevelerrorErrorisolationIsolation-—if oneIndividual driverfails,failuresthedon'tloophaltcontinuesprocessing;toerrorsthe next driver; the error isare loggedbutandnotprocessingrethrown.continues- No
transactionTransactionwrappingProtection-—eachEachsave()save isindependent. Aindependent; mid-loopfailurefailurescanmayleavecreate partial recordsfor a driver. Uses supplierDoDatetime- filtering uses supplier_do_datetime, unlike ver1 which uses unloadedDatetime.