- Introduce pause icon for individual jobs in the manage grid.
- Add `Jobs::postPauseJob` controller method to handle pausing of single jobs.
- Update tabular helper to include pause link for pending jobs.
- Extend language files with translation for "pause job."
- Implement client-side logic for pausing jobs with AJAX in manage_grid.js.
- Update CSV import logic to correctly handle escaped headers.
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
- Add import_batch_contexts table plus ImportBatchContext model so
per-batch data (e.g. Items CSV attribute definitions) is stored
once and referenced by context_id, instead of duplicated into
every queued row's job payload.
- Extend queue_jobs, queue_jobs_failed, and new queue_paused_jobs
tables with batch_id/context_id columns and FKs (ON DELETE
RESTRICT) back to import_batch_contexts, so context rows cannot
be purged while any job still references them.
- Introduce ImportBatchService.pushRow() to centralize queuing a
batch row with batch/context stamping and priority; update
Customers and Items controllers to use it instead of calling
importBatch->create() and queue->push() separately.
- Add queue_paused_jobs table and Jobs::postPauseJobs()/pauseQueues()
to move pending jobs out of queue_jobs into a paused table per
queue (all core queues or a selected subset), leaving in-flight
jobs untouched (issue #3833 pause/resume design).
- Change Jobs::getEditJob()/postSaveJob() routes to accept separate
source/id path segments instead of a combined uid string.
- Add test coverage for ImportBatchContext, ImportBatchService, and
ImportBatch (including purge-retains-while-referenced guards).
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
- Rename Jobs::getView to getEditJob and Jobs::postSave to
postSaveJob for clearer, action-specific route naming
- Update tabular_helper edit link and job_edit form action to
use new jobs/editJob and jobs/saveJob routes
- Add confirm_delete, confirm_restore, and no_jobs_to_display
strings to Jobs language file for upcoming bulk actions UI
- Fix alignment/formatting of Jobs language array entries
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Extend Jobs controller to support full lifecycle management of
queued/failed jobs from the Manage tab UI.
- Add getView/postSave to edit a pending/reserved job's payload,
priority, and available_at via modal (job_edit view)
- Add getSearch to back the AJAX-driven jobs table, using new
JobQueueManage model for search/sort/pagination and payload
decoding
- Add postDelete to bulk-delete pending/failed jobs, blocking
deletion of reserved (in-progress) jobs
- Add postProcessJob to manually run a single job immediately,
requeuing failed jobs back into queue_jobs first via new
requeueFailedJob helper
- Add splitUid helper to parse "source:id" row identifiers shared
across the new endpoints
- Extend queue_jobs_failed migration with attempts column since
codeigniter4/queue's DatabaseHandler::failed() doesn't carry it
over; BoundedQueueWorker now records it on failure so Manage
grid can show real error counts
BREAKING CHANGE: queue_jobs_failed table gains required attempts
column; run new migration before deploying.
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Adds the queue_jobs, queue_jobs_failed, and job_throttles tables and wires
up BoundedQueueWorker (deadline-bounded queue draining, shared by the
JobRunner filter for web/auto mode and the manual "Process Jobs" UI) gated
by JobThrottleGate. Adds retry limit, retention, and auto-purge settings
to the Job Queue settings tab, and a manual per-queue processing UI on the
Utilities tab. Also adds a second-granularity throttle period and
BaseOsposJob as the shared base for core queue jobs.
This is issue #3833 Phase 2 — no real job types are queued yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Introduce a soft, log-only per-task time budget (jobs_task_max_seconds)
separate from the existing web request budget (jobs_web_max_seconds),
since PHP cannot preempt a running task mid-execution.
- Config: add taskMaxSeconds default (30s) to app/Config/Jobs.php
- Controller: validate and persist task_max_seconds in
Jobs::postSaveSettings
- BoundedTaskRunner: accept taskMaxSeconds, measure task runtime via
microtime, log a warning when a task exceeds the threshold
- JobRunner: read jobs_task_max_seconds from config and pass it to
BoundedTaskRunner
- Migration: add AddJobsTaskMaxSecondsConfigKey to seed default
app_config value
- Language: add en strings for label, validation message, and
enabled/disabled tooltips
- View: add task_max_seconds form field to settings_config.php,
wired to same web-mode enable/disable toggle as web_max_seconds
- Tests: update JobsControllerTest to cover new field in save/
validation scenarios
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Bug: saveThrottles validated and saved throttles in same loop.
Invalid entry mid-loop stopped processing but left already-saved
throttles committed, and omitted throttles could still get deleted
via notToDelete tracking — partial/inconsistent state on failure.
- app/Controllers/Jobs.php: split into two passes — validate all
throttleData entries first; on any invalid entry, roll back
transaction and return failure immediately before touching DB.
Only save/delete once full payload validated. Drop now-redundant
$success flag folded into invalid-entry early return.
- tests/Controllers/JobsControllerTest.php: add test asserting
existing throttles stay unchanged in DB when payload contains
invalid throttle data.
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Throttle POST field parsing used preg_replace with an unanchored
lazy pattern to pull numeric ids out of field names, which could
mis-extract or silently fall through on unexpected key formats.
- Replace preg_replace('/.*?_(\d+)$/', ...) with preg_match against
an anchored pattern ('/^throttle_count_(\d+)$/' and
'/^throttle_period_(\d+)$/') in app/Controllers/Jobs.php
- Only process the key when the anchored pattern actually matches,
avoiding bogus $throttleId values feeding $arraySave/$notToDelete
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
- JobThrottle::saveValue now returns int throttle_id instead of bool:
new inserts return the DB-generated insertID, updates return the
existing throttleId.
- Jobs controller collects saved ids into $notToDelete using the
returned value, so newly inserted throttles are correctly tracked.
Fixes bug where newly inserted throttles had no id captured,
causing them to be wrongly deleted in the post-save cleanup step.
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Introduce a lightweight job scheduling system built on CI4 Tasks,
allowing due tasks to run via normal web traffic instead of relying
solely on external cron/Task Scheduler processes.
- Add JobRunner filter: runs TaskRunner after response send via
shutdown function, throttled per-minute via cache, respects
jobs_mode config ('auto'/'web'/'manual') and max-seconds budget
- Register jobrunner filter globally (app/Config/Filters.php),
disabled during testing to avoid heartbeat log spam
- Add Config/Tasks.php with jobs_heartbeat scheduled task (debug
log every minute) as scaffolding for future job commands
- Add Config/Jobs.php for mode/webMaxSeconds defaults
- Add Jobs controller + views (manage, settings_config,
utilities_config, partial/job_throttles) for configuring mode,
web max seconds, and per-period throttles; process-all/selected
actions stubbed for a later phase
- Add gulp task to copy jobs.svg menubar icon
- Fix tooltip clipping: use container: 'body' for bootstrap
tooltips and add max-width/text-align rules for tooltip-inner /
ui-tooltip in ospos.css
- Fix missing closing div in partial/footer.php layout
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>