Module: Jobs / Employer (jobs / jobs+jobseeker)
Status: ๐ข Production (jobs + applications live) / ๐ก monetization off ยท Last validated: 2026-09-06 ยท Priority: P0
Job board + applicant tracking: employers post jobs and manage a hiring pipeline; job seekers build resumes and apply. Validated against classs/JobModule.php (+ JobEmployerOpsModule.php) + schema.
A. Purpose
Employers hire; seekers find work. Revenue = employer plans + pay-per-application โ but application fees default to โน0 (empty kv_commission_rules).
B. Actors
Job seeker (jobseeker); employer/recruiter (jobs) + staff; admin.
C. Entry points
Consumer/seeker: /jobs_hub.php (search), /job_detail.php (detail + apply form โ calls JobModule::getJob at job_detail.php:23), /my_resume.php. Employer: /merchant/jobs_dashboard.php + 14 routes (verified: merchant/page_sidebar/jobs_pagegroup.php โ 9 top-level pages + a 6-item "AI Hiring" submenu = 15 nav entries incl. the dashboard); careerlab_dashboard.php (CareerLab).
Correction (2026-09-06): the previous text namedapply-job.phpas the seeker-facing apply entry point. That file exists atapi/apply-job.php, but it is an unrelated legacy endpoint (requiresAPP_PATH . '/models/User.php', never callsJobModule) โ not part of this flow. The real apply path is the form onjob_detail.php, POSTed toforms/doaction_jobs.php:406($jobModule->applyToJob(...)).
D. Onboarding
Seeker: uploadResume (:564) / createDigitalResume (:634), setJobPreferences (:2140), setOpenToWork (:2093). Employer: enroll jobs, createJob (:721).
E. Feature registry (prefix JOB-)
| ID | Feature | Actor | Method | Tables | Status |
|---|---|---|---|---|---|
| JOB-001 | Upload / build resume | seeker | uploadResume :564, createDigitalResume :634 | user_resumes (17 rows, 2026-09-06) | ๐ข |
| JOB-002 | Profile strength / open-to-work | seeker | getProfileStrength :2012, setOpenToWork :2093 | customer_master.profile_data (JSON) | ๐ข |
| JOB-003 | Create / edit job | employer | createJob :721, updateJob :1438 | job_posts | ๐ข |
| JOB-004 | Search jobs | anon+seeker | searchJobs :1666 | job_posts | ๐ข |
| JOB-005 | Job detail | anon+seeker | getJob(publicOnly) :1939 | job_posts | ๐ข |
| JOB-006 | Apply to job | seeker | applyToJob :2216 | job_applications | ๐ข (fee=0, see gap) |
| JOB-007 | Employer pipeline | employer | listJobsForOwner :2509, ownerUpdateJobStatus :2541 | job_posts, job_pipeline_stages | ๐ข |
| JOB-008 | Report job | user | reportJob :1980 | job_reports | ๐ข |
F. Apply flow (JOB-006)
jobs_hub.php โ job_detail.php (getJob(publicOnly) :1939) โ forms/doaction_jobs.php:406
โ applyToJob(userId, data) :2216
INSERT job_applications (candidate โ job, resume ref)
pay-per-application fee resolved via kv_commission_rules โ 0 (empty) โ free today
notify employer; candidate enters job_pipeline_stages
Correction (2026-09-06): replaced the previousapply-job.phphop โ see the note under section C; the actual dispatcher isforms/doaction_jobs.php.
G. Data model (2026-09-06)
| Table | Rows | Role |
|---|---|---|
job_posts | 1,049 | job listings |
job_applications | 8 | applications |
job_pipeline_stages | 30 | ATS pipeline stages |
Correction (2026-09-06): the previous note said resume/seeker-profile tables existed "under different names than guessed" and left them unnamed. Re-derived from code: resumes live inuser_resumes(JobModule::createDigitalResume:643inserts here; 17 rows, 2026-09-06), and open-to-work/preferences flags live in theprofile_dataJSON column ofcustomer_master(JobModule::setOpenToWork:2100), not a dedicatedjob_seeker_profilestable (confirmed absent).resume_masterandjob_seeker_profilesremain not-live names.
H. Financial impact โ the gap (validated)
3 employer plans exist (kv_membership_plans module_key='jobs': free, silver, gold). Correction (2026-09-06): live kv_memberships shows 17 free/active + 1 free/paused + 1 gold/active (no silver subscribers) โ the prior "13 free + 1 gold active" is stale. Pay-per-application is blocked: the fee is resolved via kv_commission_rules which is empty, so applications are free (LeadUnlockService-style fee โ 0). See ../../40-financial/commission.md.
K. State machine
Job: draft โ published โ paused โ closed. Application (pipeline): applied โ screening โ shortlisted โ interview โ offer โ hired / rejected (job_pipeline_stages, ownerUpdateJobStatus).
L. Errors (prefix JOB-E)
JOB-E001application charged โน0 unexpectedly โkv_commission_rulesempty.JOB-E002cannot apply โ seeker profile/resume missing (getProfileStrengthlow) or job closed.
M / N. Dependencies & change-impact
Wallet (fees), membership (employer plans), CareerLab/YEP, notifications. Seeding kv_commission_rules turns on per-application monetization โ re-test apply + employer wallet debit.