7 Commits

Author SHA1 Message Date
gitea-actions[bot] 0bfca03942 chore(release): build 01.05.00 [skip ci] 2026-06-21 05:27:40 +00:00
jmiller aea5d95f09 Merge pull request 'feat: SafetyChecklistHelper' (#21) from dev into main 2026-06-21 05:25:38 +00:00
Jonathan Miller eeaef928b5 fix: safety checklist — verify pending+unchecked before marking, auto-complete status
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 18s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-21 00:25:13 -05:00
gitea-actions[bot] 4ce4814e50 chore(version): pre-release bump to 01.04.02-dev [skip ci] 2026-06-21 05:05:20 +00:00
gitea-actions[bot] bc0fb961b5 chore(version): auto-bump patch 01.04.01-dev [skip ci] 2026-06-21 05:05:09 +00:00
Jonathan Miller 9136156034 Add SafetyChecklistHelper — trade-specific pre-job safety checks, compliance gating
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
2026-06-21 00:04:55 -05:00
jmiller 8cee50d350 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-21 04:42:08 +00:00
3 changed files with 149 additions and 2 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 01.04.00
# VERSION: 01.05.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
@@ -0,0 +1,147 @@
<?php
namespace Moko\Plugin\System\MokoSuiteField\Helper;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseInterface;
/**
* Safety checklists — pre-job safety checks, compliance tracking, incident prevention.
*/
class SafetyChecklistHelper
{
/**
* Create a safety checklist for a work order.
*/
public static function createChecklist(int $woId, string $trade): int
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$now = Factory::getDate()->toSql();
$items = self::getDefaultItems($trade);
$checklist = (object) [
'wo_id' => $woId,
'trade' => $trade,
'status' => 'pending',
'created' => $now,
'created_by' => Factory::getApplication()->getIdentity()->id,
];
$db->insertObject('#__mokosuitefield_safety_checklists', $checklist, 'id');
$checklistId = (int) $checklist->id;
foreach ($items as $i => $item) {
$db->insertObject('#__mokosuitefield_safety_checklist_items', (object) [
'checklist_id' => $checklistId,
'item_text' => $item,
'checked' => 0,
'ordering' => $i + 1,
]);
}
return $checklistId;
}
/**
* Complete a checklist item.
*/
public static function checkItem(int $itemId, bool $passed, string $notes = ''): bool
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
// Verify item belongs to a pending checklist and is not already checked
$db->setQuery($db->getQuery(true)
->select('sci.id, sci.checked, sc.status AS checklist_status')
->from($db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci'))
->join('INNER', $db->quoteName('#__mokosuitefield_safety_checklists', 'sc') . ' ON sc.id = sci.checklist_id')
->where('sci.id = ' . (int) $itemId));
$existing = $db->loadObject();
if (!$existing || $existing->checklist_status !== 'pending' || (int) $existing->checked === 1) {
return false;
}
$update = (object) [
'id' => $itemId,
'checked' => 1,
'passed' => $passed ? 1 : 0,
'notes' => $notes,
'checked_at' => Factory::getDate()->toSql(),
'checked_by' => Factory::getApplication()->getIdentity()->id,
];
$db->updateObject('#__mokosuitefield_safety_checklist_items', $update, 'id');
// Auto-complete checklist if all items are checked
$db->setQuery($db->getQuery(true)
->select('sc.id, COUNT(sci2.id) AS total, SUM(CASE WHEN sci2.checked = 1 THEN 1 ELSE 0 END) AS done')
->from($db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci2'))
->join('INNER', $db->quoteName('#__mokosuitefield_safety_checklists', 'sc') . ' ON sc.id = sci2.checklist_id')
->where('sci2.id = ' . (int) $itemId)
->group('sc.id'));
$progress = $db->loadObject();
if ($progress && (int) $progress->done === (int) $progress->total) {
$db->setQuery($db->getQuery(true)
->update('#__mokosuitefield_safety_checklists')
->set($db->quoteName('status') . ' = ' . $db->quote('completed'))
->where('id = ' . (int) $progress->id));
$db->execute();
}
return true;
}
/**
* Get checklist completion status for a work order.
*/
public static function getStatus(int $woId): ?object
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery($db->getQuery(true)
->select('sc.id, sc.status')
->select('COUNT(sci.id) AS total_items')
->select('SUM(CASE WHEN sci.checked = 1 THEN 1 ELSE 0 END) AS checked_items')
->select('SUM(CASE WHEN sci.checked = 1 AND sci.passed = 0 THEN 1 ELSE 0 END) AS failed_items')
->from($db->quoteName('#__mokosuitefield_safety_checklists', 'sc'))
->join('LEFT', $db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci') . ' ON sci.checklist_id = sc.id')
->where('sc.wo_id = ' . (int) $woId)
->group('sc.id')
->order('sc.created DESC'));
$status = $db->loadObject();
if (!$status) return null;
$status->complete = (int) $status->checked_items === (int) $status->total_items;
$status->all_passed = (int) $status->failed_items === 0;
$status->safe_to_proceed = $status->complete && $status->all_passed;
return $status;
}
/**
* Get default safety items by trade.
*/
private static function getDefaultItems(string $trade): array
{
$common = [
'PPE worn (gloves, safety glasses, boots)',
'Work area inspected for hazards',
'Tools and equipment in good condition',
'Fire extinguisher accessible',
'Emergency exits identified',
];
$tradeSpecific = match ($trade) {
'electrical' => ['Lockout/tagout verified', 'Voltage tester functional', 'Grounding confirmed', 'Arc flash boundaries marked'],
'plumbing' => ['Water supply shut off', 'Gas lines identified and marked', 'Asbestos check for older buildings', 'Drain protection in place'],
'hvac' => ['Refrigerant handling certification verified', 'Electrical isolation confirmed', 'Ductwork supports inspected', 'Ladder/scaffold secured'],
'general' => ['Work permit obtained if required', 'Material Safety Data Sheets reviewed'],
default => [],
};
return array_merge($common, $tradeSpecific);
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade">
<name>Package - MokoSuite Field</name>
<packagename>mokosuitefield</packagename>
<version>01.04.00</version>
<version>01.05.00</version>
<creationDate>2026-06-12</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>