This document lists notable features, improvements, and changes to the APM (Approvals Management) system.
GET /budget-execution — dashboard UIGET /budget-execution/data — JSON API (Redis-cached via ApmPageCache, scope budget_execution, 5 min TTL; busted on SR/ARF/memo writes)division_name column from matrix eager load.nocache support on the UI.approved budget − committed spend (draft, pending, and approved documents).CACHE_STORE=redis (version-busted on budget writes).public/js/apm-working-balance.js.CACHE_STORE=redis in production .env.parent_service_request_id in DB) let creators request the remaining memo balance after a parent SR’s total requested funds are below the original memo budget.service_requests.parent_service_request_id.allow_child_service_requests (group service_requests, default on) in App Settings.php artisan migrate
php artisan db:seed --class=SystemSettingsSeeder
weekly-briefing:* commands, scheduler in bootstrap/app.php, global generate_pdf footer URL/QR).ApproverDashboardHelper::getAverageApprovalTimeAll() and ApprovalReceiptTimeCalculator).other_memos_approval_trails (submission / resubmit at order 0, then prior approved steps).approver_document_timing_records (migration 2026_05_01_000001_create_approver_document_timing_records_table.php).approval_trails action in approved / rejected (non-archived, with workflow + order), and per Other Memo approved row (sequence ≥ 1).staff_id and name snapshot, received_at, acted_at, hours_elapsed, document type label, title, document number, division (id + name snapshot), workflow name / role where applicable, and nullable links approval_trail_id / other_memo_approval_trail_id (unique, short index names for MySQL). Rows include Laravel created_at / updated_at for when the snapshot row was written or last touched.RecordApproverDocumentTimingJob: dispatched afterCommit from ApprovalService::processApproval when the action is approved or rejected.RecordOtherMemoApproverDocumentTimingJob: dispatched from Other Memo web approve and OtherMemoApiApprovalService::approve.queue:work / systemd worker). If the queue is not processed, rows are not written until jobs run.php artisan apm:backfill-approver-document-timings Options: --chunk=, --approval-only, --other-memo-only.
Idempotent: skips rows that already exist (unique source trail ids).
apm:backfill-approver-document-timings).GET /reports/average-time-per-document (and /export for CSV).user_session('role') === 10 (APM admin), or permission 87, or permission 88 — same convention as cross-division dashboard / admin UI elsewhere (approver_timing_report_can_view_all() in CustomHelper.php).staff_id may view only their own rows; staff_id query param must match session or be omitted (scoped automatically). Other combinations return 403.staff_id matches the current user.staff_id in the URL matches you.App\Models\ApproverDocumentTimingRecordApp\Services\ApprovalReceiptTimeCalculator, App\Services\ApproverDocumentTimingServiceApp\Http\Controllers\ApproverDocumentTimingReportControllerApprovalService::getNextApprover() after approval_level 7 no longer picks the next row as “first approval_order > 7” (which always selected 8 — Programs).status query param on matrices index).config/approval_states.php: added archived (and rejected) so matrix metadata badges do not error when overall_status is archived.2026_04_27_144500_add_previous_overall_status_to_memo_tables.php also adds previous_overall_status on matrices for unarchive restore (run migrations if not already applied)./apm/activities (draft, pending, approved, rejected, returned, archived, or all), included in AJAX tab reloads.activities/memo-pdf-simple.php: budget line items are grouped per fund code from budget_breakdown (same idea as the activity show page), instead of flattening into one table when multiple fund codes are used.Added: REST API for the APM module with JWT authentication and OpenAPI/Swagger documentation.
/api/apm/v1):- Auth: Login (email + password, Argon2i-compatible), refresh token, logout, me. Login and refresh return JWT plus user data (user_id, auth_staff_id, email, name, division_id, role, status).
- Pending approvals: GET pending-approvals and GET pending-approvals/summary — same data and rules as the web pending-approvals page; processed items excluded.
- Documents: GET documents/{type}/{id} — document by type (special_memo, matrix, activity, non_travel_memo, service_request, arf, change_request) with approval_trails (same structure as matrices/37 and special-memo/19).
- Actions: POST actions — approve, reject, return, or cancel (cancel = return when HOD for special memo). Body: type, id, action, optional comment and available_budget.
- Approved by me: GET approved-by-me and GET approved-by-me/average-time — list of documents approved/rejected by the current user and average approval time (same as dashboard).
- Matrices: GET matrices/{matrixId} (detail with activities and passed/pending metadata), POST matrices/{matrixId} (approve or return).
- Activities: GET matrices/{matrixId}/activities/{activityId} (activity or single-memo detail), POST (passed, returned, convert_to_single_memo).
- Memo list: GET memo-list/pending and GET memo-list/approved — memos for the authenticated user’s division only, with filters: year, quarter, memo_type (QM, SM, SPM, NT, CR, SR, ARF), title, document_number, per_page, page.
user table (same structure: user_id, auth_staff_id, password, name, role, status, etc.). Sync command php artisan users:sync runs hourly. API login uses email (from staff work_email) and password (Argon2i hashes supported). - OpenAPI 3.0 spec: documentation/APM_API_OPENAPI.yaml — full description of all endpoints, request/response schemas, and JWT security.
- Swagger UI: Visit /docs (e.g. http://localhost/staff/apm/docs) to explore and try the API with the interactive UI. Use Authorize to set the JWT after login.
/docs in the browser when the APM app is running.Added: Validate APM Document Signature Hashes feature.
- Validate uploaded document: Upload an APM PDF; the system extracts document number(s) and signature hashes, validates them against the database, and does not store the file. Supports multiple document numbers (e.g. ARF + parent memo).
- Look up document & signatory hashes: Enter document number and year to see the document and all signatories with their verification hashes.
- Verify a signature hash: Enter a hash and document number to see which signatory and action the hash corresponds to.
AU/CDC/SDI/IM/SM/011) are parsed so the correct base table (activities, special_memos, non_travel_memos, request_arfs, change_request, service_requests) is queried.approval_trails (morph) and activity_approval_trails, so hashes from the main signature section and budget section of PDFs can be validated.smalot/pdfparser for text extraction; install with composer require smalot/pdfparser if needed.Add new entries above this line, with a clear heading and date or version if applicable.