feat: complete jobcard client management foundation
This commit is contained in:
+68
-2
@@ -72,6 +72,37 @@ CREATE TABLE IF NOT EXISTS jobcard_sequences (
|
||||
next_sequence INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS technical_information (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
client_id BIGINT UNSIGNED NOT NULL,
|
||||
category VARCHAR(80) NOT NULL,
|
||||
data_json JSON NOT NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
UNIQUE KEY technical_client_category (client_id, category)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS credentials (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
client_id BIGINT UNSIGNED NOT NULL,
|
||||
category VARCHAR(80) NOT NULL,
|
||||
label VARCHAR(120) NOT NULL,
|
||||
username VARCHAR(190) NULL,
|
||||
secret_ciphertext TEXT NOT NULL,
|
||||
notes TEXT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX credentials_client_idx (client_id),
|
||||
INDEX credentials_category_idx (category)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sla_agreements (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
client_id BIGINT UNSIGNED NOT NULL,
|
||||
@@ -153,6 +184,35 @@ CREATE TABLE IF NOT EXISTS time_entries (
|
||||
INDEX time_date_idx (work_date)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS attachments (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
jobcard_id BIGINT UNSIGNED NOT NULL,
|
||||
original_name VARCHAR(255) NOT NULL,
|
||||
stored_name VARCHAR(255) NOT NULL UNIQUE,
|
||||
mime_type VARCHAR(120) NOT NULL,
|
||||
file_size BIGINT UNSIGNED NOT NULL,
|
||||
client_visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
uploaded_by BIGINT UNSIGNED NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (jobcard_id) REFERENCES jobcards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX attachments_jobcard_idx (jobcard_id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
type VARCHAR(80) NOT NULL,
|
||||
title VARCHAR(190) NOT NULL,
|
||||
body TEXT NULL,
|
||||
deduplication_key VARCHAR(190) NULL,
|
||||
read_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY notification_dedup_idx (user_id, deduplication_key),
|
||||
INDEX notification_unread_idx (user_id, read_at, created_at)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_events (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NULL,
|
||||
@@ -181,14 +241,20 @@ INSERT IGNORE INTO permissions (name, description) VALUES
|
||||
('jobcards.assign', 'Assign technicians to jobcards'),
|
||||
('jobcards.internal_notes', 'View and edit internal jobcard notes'),
|
||||
('time_entries.record', 'Record technician time entries'),
|
||||
('technical.view', 'View client technical information'),
|
||||
('technical.manage', 'Manage client technical information'),
|
||||
('credentials.view', 'View protected credentials'),
|
||||
('credentials.manage', 'Manage protected credentials'),
|
||||
('attachments.view', 'View jobcard attachments'),
|
||||
('attachments.manage', 'Manage jobcard attachments'),
|
||||
('notifications.view', 'View notifications'),
|
||||
('sla.view', 'View client SLA agreements and usage'),
|
||||
('sla.manage', 'Configure client SLA agreements'),
|
||||
('reports.view', 'View reports'),
|
||||
('reports.export', 'Export reports'),
|
||||
('users.manage', 'Manage users'),
|
||||
('roles.manage', 'Manage roles and permissions'),
|
||||
('audit.view', 'View audit events'),
|
||||
('credentials.view', 'View protected credentials');
|
||||
('audit.view', 'View audit events');
|
||||
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM roles r CROSS JOIN permissions p WHERE r.name = 'Administrator';
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
-- JOBcard additive upgrade for installations created before the current schema.
|
||||
-- Take a database backup first. Run with the target database selected:
|
||||
-- mysql --default-character-set=utf8mb4 -u USER -p DATABASE < database/upgrade.sql
|
||||
-- Resolve duplicate SLA rows before adding the unique client constraint.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS technical_information (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
client_id BIGINT UNSIGNED NOT NULL,
|
||||
category VARCHAR(80) NOT NULL,
|
||||
data_json JSON NOT NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
UNIQUE KEY technical_client_category (client_id, category)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS credentials (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
client_id BIGINT UNSIGNED NOT NULL,
|
||||
category VARCHAR(80) NOT NULL,
|
||||
label VARCHAR(120) NOT NULL,
|
||||
username VARCHAR(190) NULL,
|
||||
secret_ciphertext TEXT NOT NULL,
|
||||
notes TEXT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX credentials_client_idx (client_id),
|
||||
INDEX credentials_category_idx (category)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS jobcard_sequences (
|
||||
sequence_year SMALLINT UNSIGNED PRIMARY KEY,
|
||||
next_sequence INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS jobcard_status_history (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
jobcard_id BIGINT UNSIGNED NOT NULL,
|
||||
from_status VARCHAR(60) NOT NULL,
|
||||
to_status VARCHAR(60) NOT NULL,
|
||||
changed_by BIGINT UNSIGNED NULL,
|
||||
changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (jobcard_id) REFERENCES jobcards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (changed_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX status_history_jobcard_idx (jobcard_id, changed_at)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS attachments (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
jobcard_id BIGINT UNSIGNED NOT NULL,
|
||||
original_name VARCHAR(255) NOT NULL,
|
||||
stored_name VARCHAR(255) NOT NULL UNIQUE,
|
||||
mime_type VARCHAR(120) NOT NULL,
|
||||
file_size BIGINT UNSIGNED NOT NULL,
|
||||
client_visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
uploaded_by BIGINT UNSIGNED NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (jobcard_id) REFERENCES jobcards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
INDEX attachments_jobcard_idx (jobcard_id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
type VARCHAR(80) NOT NULL,
|
||||
title VARCHAR(190) NOT NULL,
|
||||
body TEXT NULL,
|
||||
deduplication_key VARCHAR(190) NULL,
|
||||
read_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY notification_dedup_idx (user_id, deduplication_key),
|
||||
INDEX notification_unread_idx (user_id, read_at, created_at)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT IGNORE INTO permissions (name, description) VALUES
|
||||
('technical.view', 'View client technical information'),
|
||||
('technical.manage', 'Manage client technical information'),
|
||||
('credentials.view', 'View protected credentials'),
|
||||
('credentials.manage', 'Manage protected credentials'),
|
||||
('attachments.view', 'View jobcard attachments'),
|
||||
('attachments.manage', 'Manage jobcard attachments'),
|
||||
('notifications.view', 'View notifications');
|
||||
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM roles r CROSS JOIN permissions p WHERE r.name = 'Administrator';
|
||||
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM roles r JOIN permissions p ON p.name IN ('technical.view') WHERE r.name = 'Technician';
|
||||
|
||||
-- Enforce one SLA agreement per client after duplicate remediation.
|
||||
SET @sla_unique_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE() AND table_name = 'sla_agreements' AND index_name = 'sla_client_unique'
|
||||
);
|
||||
SET @sla_duplicate_count := (
|
||||
SELECT COUNT(*) FROM (SELECT client_id FROM sla_agreements GROUP BY client_id HAVING COUNT(*) > 1) duplicates_check
|
||||
);
|
||||
SET @sla_add_unique_sql := IF(@sla_duplicate_count = 0 AND @sla_unique_exists = 0,
|
||||
'ALTER TABLE sla_agreements ADD UNIQUE KEY sla_client_unique (client_id)',
|
||||
IF(@sla_unique_exists > 0, 'SELECT 1', 'SELECT 1 FROM jobcard_upgrade_duplicate_sla_rows'));
|
||||
PREPARE sla_add_unique_stmt FROM @sla_add_unique_sql;
|
||||
EXECUTE sla_add_unique_stmt;
|
||||
DEALLOCATE PREPARE sla_add_unique_stmt;
|
||||
Reference in New Issue
Block a user