Helpdesk Reference Public

Company Handbook

The AkbAI-powered Company Handbook: upload a PDF (or start from a DOLE template), let AI split it into draft sections, review, and publish an employees-only searchable handbook.

Guide version: r1 Module version: 0.1.0 Updated: 2026-07-22 Estimated time: 18 min 1 views

Company Handbook

The AkbAI compliance pillar of the Helpdesk module: HR uploads a born-digital PDF handbook (or starts from a built-in DOLE-aware template), AkbAI extracts and splits it into draft sections, HR reviews and publishes, and employees read a searchable, employees-only handbook.

HR Admin Employee AkbAI-powered module_helpdesk

What the Company Handbook is

The Company Handbook turns a finished PDF handbook into a living, searchable, employees-only reference inside ERPat. Instead of emailing a 40-page PDF that nobody reads, HR uploads the file once and AkbAI does the tedious work: it reads the PDF, finds the sections, rewrites each one into clean HTML, classifies it into a compliance category, and stages every section as a draft for HR to review. HR approves and publishes, and from that moment every authenticated employee with the handbook permit can browse the handbook by category, open an article, and search it.

Everything here is confidential company content. The reader (Handbook controller) extends App_Controller and is gated by module_helpdesk plus the handbook permission — it is never exposed to guests or the public web. The admin side (Handbook_admin controller) is where uploads, review and publishing happen.

Where it lives The whole pillar is a self-contained module at modules/Helpdesk/: controllers Handbook and Handbook_admin, three models (Handbook_import_model, Handbook_category_model, Handbook_article_model), four libraries (extractor, section detector, AI sectioner, HTML sanitizer), one cron job, and the handbook_helper taxonomy/template. Data is per-tenant across three tables: handbook_imports, handbook_categories, and handbook_articles.

Two ways to start a handbook

Upload a PDF

Upload your existing, born-digital handbook PDF. AkbAI extracts the text, detects sections, and drafts each one in the background. Source type upload.

The AI pipeline →

Start from DOLE template

Seed a starter handbook from the built-in, DOLE-aware 14-category template. No AI cost, no PDF — pre-written PH-compliance drafts land straight in review. Source type template.

The DOLE template →

The AkbAI processing pipeline

When you upload a PDF, the heavy work runs in the background via the cron job helpdesk_process_handbook_imports (ProcessHandbookImportJob) — the upload request returns immediately with "Uploaded. AkbAI is processing your handbook — check back shortly." Here is the full path from raw PDF to a published article.

UploadPDF only
ExtractPdf_handbook_extractor
DetectSection_detector
AI mapAi_sectioner
SanitizeHtml_sanitizer
Draftneeds_review
ReviewHR approves
PublishEmployees read

1. Upload & queue

HR uploads a PDF via the modal. A handbook_imports row is created first (so files land under a stable per-import path), the file is validated as PDF both client-side and server-side, and the import is set to status pending. The reader/admin UI shows it as Queued.

2. Extract text

Pdf_handbook_extractor (smalot/pdfparser, pure PHP) reads the PDF page by page. It records per-page character stats, collapses whitespace, strips recurring running headers/footers and page numbers, and flags the document as scanned if more than half the pages fall below SCANNED_CHARS_PER_PAGE (60 non-whitespace chars). Status becomes extracting.

3. Detect sections

Handbook_section_detector splits the plain text into sections using heading heuristics only (no font info): numbering depth (1., 1.1, I., A.), SECTION/ARTICLE/CHAPTER/PART markers, short ALL-CAPS lines, and isolated Title-Case lines. Each heading owns the body up to the next heading. Content before the first heading becomes an "Introduction" section. Status becomes processing.

4. AI map (per section)

For each detected section, Handbook_ai_sectioner makes one AkbAI call and gets back strict JSON {title, html, category, excerpt}. The section text is sent as data only (prompt-injection containment — handbook text is treated as untrusted), the model has no tools, the category is constrained to the fixed 14-item enum, and parsing has bounded retry/backoff. If AkbAI is not configured, this step is skipped and the section is drafted from the cleaned source text instead.

5. Sanitize

Handbook_html_sanitizer runs the returned HTML through HTMLPurifier with a strict allow-list (headings, paragraphs, lists, simple tables, emphasis, blockquotes, safe links). Scripts, styles, event handlers, classes and inline styles are stripped. This always runs on the way into storage, so the employees-only reader can echo the body safely.

6. Save as draft

Each sanitized section is written to handbook_articles with status draft, its category, an excerpt, a sort order, and a section_hash. When all sections are drafted, the import is set to needs_review and its section_count is recorded.

7. HR review

HR opens the review screen, reads each draft, edits titles/categories/excerpts/body as needed, and deletes any junk sections. Nothing is visible to employees yet.

8. Publish

HR clicks "Approve all & Publish". Every draft article of the import flips to published with a published_at timestamp, and the import is marked published. Employees can now read it.

It runs on its own schedule You don't trigger the pipeline manually. The cron job sweeps every 5 minutes (*/5 * * * *). After an upload, refresh the review screen — it polls the import status and shows drafts as soon as processing finishes.

Import status legend

Every upload (and template seed) is an handbook_imports row whose status drives the whole pipeline. These are the states you will see in the admin imports table and on the review screen (labels come from the handbook_status_* lang keys).

Status valueBadgeWhat it means
pending QueuedFreshly uploaded and waiting for the next cron sweep. Nothing has been processed yet.
extracting ExtractingThe cron picked it up and is pulling text out of the PDF (Pdf_handbook_extractor).
processing ProcessingText extracted; sections detected; AkbAI is mapping and drafting each section.
needs_review Needs reviewDrafts are staged and waiting for HR. This is the normal resting state after successful processing (and the state a DOLE template starts in).
published PublishedHR approved and published; the articles are live to employees.
failed FailedProcessing could not complete (unreadable PDF, missing file, or too many attempts). The error_message explains why. Re-upload or add sections manually.
Scanned handbooks land in review, not failed When the extractor detects a scanned/image-only PDF, the import goes to Needs review (not failed) with an explanatory message and a Scanned — manual entry flag on the row. A blank / near-blank PDF (no readable sections) also lands in needs_review with an explicit reason. In both cases HR adds the sections by hand.

Article status & idempotency

Inside an import, each section is an handbook_articles row with its own status:

draft published
Article statusMeaning
draftCreated by the pipeline or the template seed. Editable by HR, invisible to employees. Editing needs handbook_update.
publishedLive on the employees-only reader. Editing a published article needs the stronger handbook_publish permit (see RBAC).
section_hash makes re-runs safe Every drafted section carries a section_hash derived from stable content (import id + section path + prompt version + a whitespace-normalized title+body signature). Before creating a draft, the cron checks find_by_section_hash() — if the section was already drafted, it is skipped. So a re-picked or resumed import never produces duplicate drafts, and the hash is deliberately matched to the unique index across all rows (soft-deleted included) so it stays correct even after HR deletes a bad draft.

The background cron job

The pipeline is driven entirely by ProcessHandbookImportJob. It is registered as a discoverable module cron job and is per-tenant (handbook data is tenant-scoped), so it only runs for tenants where module_helpdesk is enabled.

Slug
helpdesk_process_handbook_imports
Schedule
*/5 * * * * (every 5 min)
Timeout
600s
Memory
512 MB
Scope
Per-tenant
Enabled by default
Yes

Retry bounding & stale recovery

The job is built to be safe against killed runs and pathological PDFs:

  • Attempt cap. Each pickup increments the attempts column. After MAX_ATTEMPTS (3) the import is set to failed with the "processing failed repeatedly" message — a PDF that keeps timing out or OOM-ing is given up on rather than looping forever.
  • Stale in-flight re-pick. get_pending() always picks up pending rows, but only re-picks an extracting/processing row once it is older than STALE_SECONDS (900s) — i.e. a previous run was killed mid-flight and never reached a terminal state. A legitimately live run is never re-picked (900s > the 600s timeout).
  • Per-section isolation. A throw on one section (sanitizer error, malformed UTF-8, oversized body) is logged and skipped; the rest of the import still drafts.

Running & inspecting the job

php erpat cron:list                                  # see all jobs incl. the handbook job
php erpat cron:run helpdesk_process_handbook_imports  # run it now for this tenant
php erpat cron:dry-run helpdesk_process_handbook_imports
php erpat cron:tick helpdesk_process_handbook_imports # run if due per its schedule

Requirements & edge cases

AkbAI is optional but recommended AI sectioning needs AkbAI configured (AKBAI_HOST / AKBAI_KEY). Without it, uploads still work: the PDF is extracted, sections are detected, and each section is drafted from the cleaned source text (wrapped in safe paragraphs) for HR to edit manually. With AkbAI on, each section is rewritten into clean, semantic HTML and auto-classified into a category. The review screen and the cron log both indicate whether AI was on or off.
Born-digital PDFs only — no OCR in v1 The extractor reads text embedded in the PDF; it does not OCR images. A scanned/image-only handbook is detected (page char stats) and routed to needs_review with the message "This looks like a scanned/image-only PDF. OCR is not available in this version — please add the sections manually." Upload a text PDF, or add the sections by hand.
PDF-only, enforced twice The handbook pillar accepts only .pdf files. The dropzone validates each file client-side via validate_file, and save_upload re-checks the moved file server-side (client checks are bypassable) — a non-PDF deletes the import and returns "Only PDF files are accepted for the handbook."
S3-backed tenants smalot/pdfparser needs a real local path, so for S3 storage the object is materialized to a temp file (tempnam) before extraction and unlinked immediately after. For local storage the path is built under the fixed app root and hardened against traversal (basename + realpath containment). This is all handled by the job — no operator action needed.
Handbook content is treated as hostile HTML Text inside an uploaded PDF (or AI output derived from it) can carry injected markup, so Handbook_html_sanitizer purifies every body on the way into storage — through all write paths (cron, template seed, manual edit), enforced centrally in Handbook_article_model::save(). If HTMLPurifier is unavailable, a conservative strip_tags allow-list fallback runs and an error is logged so operators notice the degraded (link-stripped) path.

The DOLE-aware starter template

Don't have a handbook yet? Click Start from DOLE Template to seed a ready-to-review starter. It creates a template import at status needs_review and populates deterministic, pre-written Philippine-compliance draft articles — no AI, no PDF, no token cost. Every article carries a "Review & customize this section for your company before publishing" note, and the set is anchored by a legal disclaimer.

A starting point, not legal advice The template is grounded in PH labor law and DOLE issuances, but statutory rates and brackets change yearly, so figures are written as "verify the current issuance," not hardcoded guarantees. The built-in Legal Disclaimer article states plainly that the handbook must be reviewed by qualified Philippine labor counsel before adoption. Always customize before publishing.

The 14-category taxonomy

The same 14 categories serve as (a) the seeded per-tenant taxonomy and (b) the fixed enum AkbAI classifies uploaded sections into. They are seeded on first use (handbook_seed_categories()) and are compliance-aligned to the PH context.

#CategoryCovers
1About & WelcomeCompany overview, mission, values, handbook purpose, and the legal disclaimer.
2Employment & ClassificationEmployment status, probation, regularization, and security of tenure.
3Recruitment & OnboardingHiring, pre-employment requirements, and orientation.
4Work Hours & AttendanceSchedules, overtime, night differential, holidays, and timekeeping.
5Leaves & Time OffStatutory and company leaves and how to file them.
6Compensation & PayrollPay periods, payslips, deductions, and 13th-month pay.
7Government-Mandated BenefitsSSS, PhilHealth, Pag-IBIG, and BIR withholding.
8Performance ManagementGoal setting, appraisals, and promotions.
9Code of Conduct & DisciplineStandards of conduct, offenses, and the twin-notice due-process rule.
10Health, Safety & Mental HealthOSH, drug-free workplace, and mental-health programs.
11Data Privacy & ConfidentialityHandling of personal data and confidential information.
12Anti-Harassment & Equal OpportunitySafe Spaces, anti-sexual-harassment, and non-discrimination.
13IT & Acceptable UseUse of company devices, accounts, and systems.
14Separation & OffboardingResignation, final pay, clearance, and Certificate of Employment.

Admin SOPs (HR)

SOP — Upload & process a handbook PDF
  1. Open the Handbook reader (/handbook) and click the Edit button (visible with handbook_create or handbook_update) to reach the admin screen at /admin/handbook.
  2. Click Upload Handbook PDF. In the modal, enter a handbook title (e.g. "2026 Employee Handbook") and choose your PDF (born-digital, not a scan).
  3. Click Upload & Process. You'll see "Uploaded. AkbAI is processing your handbook — check back shortly." The import appears in the table as Queued.
  4. Wait for the background cron (runs every 5 minutes). The status will move through extractingprocessing Needs review.
  5. Click Review on the row to open the drafts. Proceed to the review SOP below.
SOP — Seed & use the DOLE template
  1. On the admin screen (/admin/handbook), click Start from DOLE Template and confirm the prompt.
  2. A "DOLE Template Handbook" import is created at needs_review with pre-written draft articles, and you're taken straight to its review screen.
  3. Edit each draft to match your company (the template articles contain bracketed placeholders like "[Add your company mission…]" and "verify the current issuance" notes).
  4. Have the content reviewed by qualified Philippine labor counsel before publishing (per the built-in Legal Disclaimer).
  5. Publish when ready (see below).
SOP — Review drafts
  1. On the review screen (/admin/handbook/review/{id}), each section shows its title, category label, status badge, and excerpt.
  2. Click Edit on a draft (needs handbook_update) to open the article modal. Adjust the title, pick the correct category, refine the excerpt, and edit the body in the rich-text editor.
  3. Save. The body is re-sanitized on save; the page reloads so you see the change.
  4. Delete any junk sections the detector picked up (needs handbook_delete). Add missing sections manually where extraction fell short (e.g. scanned pages).
  5. If the banner shows a warning (scanned PDF, no sections found, processing error), read the message and act accordingly — add sections by hand or re-upload.
SOP — Approve all & publish
  1. On the review screen, confirm every draft reads correctly and is in the right category.
  2. Click Approve all & Publish (visible only with handbook_publish, and only while the import isn't already published) and confirm.
  3. Every draft article of that import flips to published with a timestamp; the import is marked Published. You'll see "Published N article(s) to employees."
  4. Employees with the handbook permit can now read the handbook at /handbook.
Editing a live article needs the publish gate handbook_update alone governs drafts. Editing an already-published, employee-visible article requires handbook_publish — if you lack it, the save is refused. This prevents an update-only role from silently changing live policy.

The employees-only reader

Once published, employees read the handbook through the Handbook controller. It's a clean, card-and-sidebar reader with category browsing, article view, and search — all gated by module_helpdesk + the handbook permission.

  • Landing (/handbook). A hero, a category sidebar with per-category published-article counts, and a card grid of categories. Empty until the handbook is published.
  • Category (/handbook/category/{id}). Lists the published articles in one active category, with excerpts.
  • Article (/handbook/view/{id}). Renders a single published article's sanitized body; the view counter (total_views) increments on each read. Only published articles are reachable — an unpublished or bad id returns 404.
  • Search (/handbook/search). A typeahead POST endpoint that matches published article titles and excerpts and returns id/label pairs.
  • The reader shows an Edit button in the hero for users with handbook_create or handbook_update, linking to /admin/handbook.
  • The admin screen lists all imports (any status) in a DataTable with title, source, section count, status, date, and a Review action.
  • The admin controller is reached via that Edit button — the handbook is intentionally not a sidebar menu item.

Routes reference

All routes are module-owned (declared in modules/Helpdesk/config/routes.php). The reader lives under /handbook; the HR admin under /admin/handbook.

RouteController / methodPurposeAudience
handbookHandbook/indexReader landing — categories & cards.Employee
handbook/category/(:num)Handbook/category/$1Published articles in one category.Employee
handbook/view/(:num)Handbook/view/$1Read one published article (increments views).Employee
handbook/searchHandbook/searchTypeahead search (published title/excerpt).Employee
admin/handbookHandbook_admin/indexAdmin — list of imports.HR
admin/handbook/imports-list-dataHandbook_admin/imports_list_dataDataTable JSON for the imports table.HR
admin/handbook/upload_formHandbook_admin/upload_formUpload modal (needs handbook_create).HR
admin/handbook/upload_fileHandbook_admin/upload_fileDropzone temp upload endpoint.HR
admin/handbook/validate_fileHandbook_admin/validate_filePer-file PDF validation.HR
admin/handbook/save_uploadHandbook_admin/save_uploadCreate the import & queue processing.HR
admin/handbook/start_templateHandbook_admin/start_templateSeed the DOLE template handbook.HR
admin/handbook/review/(:num)Handbook_admin/review/$1Review & publish drafts of an import.HR
admin/handbook/article_form/(:num)Handbook_admin/article_form/$1Edit-article modal (needs handbook_update).HR
admin/handbook/save_articleHandbook_admin/save_articleSave an edited article.HR
admin/handbook/delete_articleHandbook_admin/delete_articleSoft-delete an article (needs handbook_delete).HR
admin/handbook/publish/(:num)Handbook_admin/publish/$1Approve all & publish (needs handbook_publish).HR
admin/handbook/status/(:num)Handbook_admin/status/$1Poll a single import's status (progress UI).HR

Permissions & RBAC

Access to the whole pillar is module toggle enabled AND the user's permission grants the pillar permit: the master module_helpdesk setting must be on, and the user needs the relevant handbook* permit. These permission keys are module-owned (declared in modules/Helpdesk/config/permissions.php) and appear in the Roles UI under the Help Center: Handbook group when module_helpdesk is enabled.

Permission keyLabelGrants
handbookHandbook (read)Read the published, employees-only handbook (reader landing, category, article, search). Gate on the Handbook controller.
handbook_createUpload / Import HandbookUpload a PDF and seed the DOLE template. Required for the upload modal and save_upload/start_template.
handbook_updateEdit Handbook DraftsOpen the review screen's edit modal and save draft article changes. Governs drafts only.
handbook_deleteDelete Handbook ArticlesSoft-delete an article from an import.
handbook_publishPublish Handbook to EmployeesApprove all & publish an import's drafts, and edit an already-published (live) article.
Admin entry needs any one of three Reaching /admin/handbook requires module_helpdesk plus any of handbook, handbook_create, or handbook_update. Individual admin actions then re-check their specific permit (upload → handbook_create, edit → handbook_update, delete → handbook_delete, publish → handbook_publish). See the Permissions Reference for the full Helpdesk permission surface.

Who uses what

PersonaTypical permitsDoes
HR Adminhandbook, handbook_create, handbook_update, handbook_delete, handbook_publishUploads PDFs, seeds templates, reviews and edits drafts, publishes to employees, and maintains live articles.
EmployeehandbookReads the published handbook, browses by category, opens articles, and searches. No editing.

Terminology

Import handbook_imports

One ingestion run — an uploaded PDF or a seeded template. Its status drives the pipeline; it owns a set of articles.

Article handbook_articles

One handbook section — a draft or published unit of content with a title, body, category, excerpt, and sort order.

Category handbook_categories

One of the 14 compliance-aligned buckets; doubles as the AI classification enum and the reader's navigation.

Section detection

Heuristic splitting of extracted text into sections by heading patterns — no font info, dependency-free.

AI map

The per-section AkbAI call that returns strict JSON {title, html, category, excerpt}; the section text is data-only.

section_hash

A content-derived idempotency key so re-runs skip already-drafted sections and never duplicate them.

Born-digital PDF

A PDF with real embedded text (exported, not scanned). The only kind the extractor can read — no OCR in v1.

Troubleshooting & FAQ

Processing runs on the cron job, which sweeps every 5 minutes. Give it a few minutes and refresh. If it never moves, confirm the cron runtime is ticking and that module_helpdesk is enabled for this tenant (the job returns early otherwise). You can force a run with php erpat cron:run helpdesk_process_handbook_imports.

Open the review screen; the warning banner shows the error_message. Common cases: "We couldn't read this PDF" (corrupt or password-protected — try another file), "The uploaded PDF could not be located" (re-upload), or "Processing failed repeatedly and was stopped" (hit the 3-attempt cap — re-upload or add sections manually).

The PDF is likely scanned/image-only or blank. There is no OCR in v1 — the import lands in needs_review with an explanatory message. Add the sections manually, or re-export the source document as a born-digital (text) PDF and re-upload.

AkbAI is probably not configured (AKBAI_HOST / AKBAI_KEY). The pipeline still extracts, detects, and drafts each section from the cleaned source text so you can edit it manually — it just doesn't auto-rewrite or auto-classify. Configure AkbAI and re-upload for the full AI experience.

Editing a published article requires handbook_publish, not just handbook_update. Ask an admin to grant the publish permit, or have someone with it make the change.

Confirm the import status shows Published (publishing flips the import too), that the employees have the handbook permit, and that module_helpdesk is enabled. Only published articles appear in the reader; drafts never do.

Delete the junk draft on the review screen (needs handbook_delete). Because section_hash is matched against all rows including soft-deleted ones, deleting a bad draft won't cause a re-run to re-create it as a duplicate.

Related reading This is one of the Helpdesk support pillars. See Knowledge Base and Internal Wiki for the other content pillars, Permissions Reference for the full RBAC surface, and Glossary & Legends for all status badges.
Was this guide helpful?

Report a content problem