Training certificate flow

SFReg Salesforce-issued certificates (V0.99.18 / V0.99.19, DYNDOC DC3). State in the QA and UAT sandboxes on 14 Oct 2026, with Prod noted where it differs.

Trainer resultdoPut /v1/contactAssetRolesTraining Portal or Moodlesets status CRReal time Retry batchCertificateRequestBatchHourly at :00 NZ (Prod)CR + order invoicedNot scheduled in QA/UAT 8-week auto opt-outAutoOptOutSchedulerDaily 10:30 NZAttended, C or CR, 8+ wksNot scheduled anywhere Hold releasereleaseIfEntitledRegistration confirmedor NZQA opt-outGate on only (now off) Certificate requestSFReg_CertificateRequestService.requestBulkMode Skip or Mock stops; FIS is read as SalesforceAlready CS: stop (ALREADY_SENT) Entitlement rule (C1)SFReg_CertificateEntitlement.resolveNZQA Required add-on, matched on ExternalCode + AssetNo add-on, or add-on FALSE: STANDARDTRUE + registration complete + NSN + NSIVerified 'Yes': NZQATRUE, anything missing: NOT_READY Not readyNSN missing or unverifiedGate off: stays put, WARN logGate on: park at C, hold + Case NZQA certificateTemplate rule CERTIFICATE_NZQAStatus set to CR if not alreadyOne issue job queued Standard certificateTemplate rule CERTIFICATE_NON_NZQAStatus set to CR if not alreadyOne issue job queued Waits at CR (or C)Retry batch asks again8-week closure: standardQA/UAT: nothing retries yet Issue job (C3)SFReg_CertificateIssueQueueable, up to 5 bookings per hopRe-reads: still CR and still entitled, otherwise skippedQueues the next hop first Render and file the PDFDynamicCertificateService.issueCertificates (DYNDOC)Template from the product's rule; Certificate registry activeNZQA also needs rule CERTIFICATE_NZQA_CREDITSFile Cert_<First>_<Last>_<booking digits>_<yyyyMMdd> Email with the PDF attachedRegistrationEmailService.dispatchCertificateSend once: booking locked and still CREMAIL_POST_COURSE_CERT, or EMAIL_AUTO_OPT_OUT_CERT after closureOpted out or no email: filed, not sent, still moves to CS Render or email fails: stays CR, ERROR logged Booking status = CS (Certificate Sent)NZQA Credit add-on: TRUE for NZQA, FALSE for standardAttempt row stamped: request time and modeSFDocs trigger fires on the new file LMS certificates drawerHandler watches the label'Certificate Sent', not CS,so nothing is indexed Live in QA and UAT Off, waiting or broken
Booking = the attendee's Contact Asset Role (CAR). Status codes: C Competent, CR Certificate Requested, CS Certificate Sent.

When a certificate is triggered

TriggerClass and jobWhen it runsWhich bookingsReason passedQA / UAT
Trainer resultContactAssetRoleRestResource.doPut
(PUT /services/apexrest/v1/contactAssetRoles)
Real time, when Mule (integration user "My StJohn") calls itStatus header = CR: the CAR Ids sent by the Training Portal, or the registrations sent by Moodle (registration Id + attendee Id match)POST_COURSE_PASS_THROUGHLive
Retry batchCertificateRequestBatch (10 per chunk), run by CertificateRequestBatchSchedulable
Prod job: CertificateRequest_Hourly_Schedule
Prod: every hour on the hour, NZ time (cron 0 0 * * * ?)Status__c = 'CR' and the course's order has an invoice number (Asset → OrderItem → Order.InvoiceNumber__c not blank)RETRYNot scheduled
8-week auto opt-outAutoOptOutScheduler → SFReg_PostCourseCertClosureBatch (10 per chunk)
Job: SFReg Auto Opt-Out Closure Daily
Daily at autoOptOutDispatchTime__c (10:30 NZ), once AutoOptOutScheduler.scheduleDaily() is runRole Primary, Type Attendee, FATattendance__c = 'Attended', status C or CR, event at least 8 weeks ago (autoOptOutWeeksAfter__c) and within a 28-day catch-up window, no AutoOptOutClosedAt__c on its Attempts. NOT_READY ones get NZQA Required set FALSE first, so they become STANDARD.AUTO_OPT_OUT_CLOSURENot scheduled (waiting on AK/Ade)
Hold releaseSFReg_PostCourseCertGate.releaseIfEntitledWhen a held attendee's registration-confirmed email signal fires (EMAIL_REG_CONFIRMED / _NZQA), or they opt out of NZQABookings parked at C with a certificate holdHOLD_RELEASEDGate off, never runs

A manual status edit does not trigger a certificate: only these four paths call the request service. To re-send one booking by hand, run SFReg_CertificateRequestService.requestBulk(new Set<Id>{carId}, SFReg_CertificateRequestService.RequestReason.RETRY) as anonymous Apex.

What each step checks

StepClassPasses whenOtherwise
1. ModeSFReg_CertificateRequestService.resolveModecertificateGenerationMode__c is Salesforce (or FIS / blank, both read as Salesforce)Skip: nothing happens. Mock: logged only.
2. Already sentrequestViaSalesforceStatus is not CSALREADY_SENT, nothing done
3. Entitlement (C1)SFReg_CertificateEntitlement.resolveNZQA Required add-on (Role AddOn, Type NZQA Required, matched on ExternalCode__c + Asset__c) is missing or FALSE → STANDARD. TRUE and all three Attribute__c rows present: attendeeRegistrationComplete, attendeeNSN not blank, attendeeNSIVerified = 'Yes' → NZQA.NOT_READY (REGISTRATION_NOT_FINISHED, NSN_MISSING or NSN_NOT_VERIFIED). Gate off: left as is with a WARN log. Gate on: parked at C with a hold and a Case.
4. QueuequeueIssueStatus moved to CR if needed; one SFReg_CertificateIssueQueueable queuedNo queueable capacity: stays CR for the retry batch
5. Re-checkSFReg_CertificateIssueQueueableStill CR and still entitled (up to 5 per hop)Skipped (moved on, or the answer changed)
6. Render and fileDynamicCertificateService.issueCertificatesThe product's chain (Family → Offer → Variant → Option, Definition_Attribute__c Rule rows) resolves CERTIFICATE_NZQA or CERTIFICATE_NON_NZQA to an active template; for NZQA, CERTIFICATE_NZQA_CREDITS also resolves; Certificate registry record activeMISSING_DATA or render error: stays CR, one ERROR row in SFREG_Log__c
7. EmailRegistrationEmailService.dispatchCertificateBooking locked and still CR. Template rule EMAIL_POST_COURSE_CERT (PostCourseCompletionCert_Dyn), or EMAIL_AUTO_OPT_OUT_CERT (AutoOptOutNZQACertSend_Dyn) when the latest Attempt has AutoOptOutClosedAt__c. PDF attached.Opted out or no address: not sent, still moves to CS. Send failure: stays CR.
8. FinishSFReg_CertificateIssueQueueableNZQA Credit add-on set TRUE (NZQA) or FALSE (standard); status CR → CS—

Settings that control it

Setting (AttendeeRegistrationConfig__mdt Default)QAUATEffect
certificateGenerationMode__cSalesforceFIS (read as Salesforce, WARN each request)Skip is the emergency stop without a deploy
postCourseCertGateEnabled__cfalsefalseOff: not-ready bookings just wait; on: they are held at C
autoOptOutWeeksAfter__c88Weeks after the event before the auto opt-out closes a booking
autoOptOutDispatchTime__c10:3010:30Run time used by scheduleDaily()

Known gaps and risks

Prod today

Prod still prints through FIS: CR publishes PRINT_CERTIFICATE to Mule and FIS prints and posts the certificate; the hourly batch re-publishes anything left at CR. Promoting V0.99.18 retires that path, so it needs business and FIS-team sign-off first.

Verified end to end in UAT on 14 Oct 2026 with a test booking: NZQA certificate filed, emailed (redirected to the sandbox test mailboxes) and the booking moved to CS.

Live in QA and UATOff, waiting or broken