Files
breadcrumb-the-shire/modules/helpdesk/lib/Module/Helpdesk/Repository/HandoverRepository.php
fs e7c60468c9 feat(helpdesk): add domain linking, bulk actions, and revision polish to handovers
Adds domain selection (cascaded from debitor) to handover creation and edit,
bulk delete with confirmation dialog, and various UI improvements to the
handover wizard and list page. Includes migration for domain columns,
domain-select AJAX endpoint, and updated tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:21:12 +02:00

245 lines
7.3 KiB
PHP

<?php
namespace MintyPHP\Module\Helpdesk\Repository;
use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery;
class HandoverRepository
{
/**
* @param array<string, mixed> $data
* @return int|null Inserted ID or null on failure
*/
public function insert(array $data): ?int
{
$result = DB::insert(
'INSERT INTO helpdesk_handovers '
. '(tenant_id, debitor_no, debitor_name, product_code, domain_no, domain_url, status, schema_snapshot, field_values, created_by) '
. 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
(string) ($data['tenant_id'] ?? 0),
(string) ($data['debitor_no'] ?? ''),
(string) ($data['debitor_name'] ?? ''),
(string) ($data['product_code'] ?? ''),
(string) ($data['domain_no'] ?? ''),
(string) ($data['domain_url'] ?? ''),
(string) ($data['status'] ?? 'draft'),
$data['schema_snapshot'] ?? null,
$data['field_values'] ?? null,
(string) ($data['created_by'] ?? 0)
);
if ($result === false) {
return null;
}
return (int) $result;
}
public function findById(int $tenantId, int $id): ?array
{
if ($id <= 0) {
return null;
}
$row = DB::selectOne(
'SELECT h.*,'
. ' uc.display_name AS created_by_label, uc.uuid AS created_by_uuid,'
. ' uu.display_name AS updated_by_label, uu.uuid AS updated_by_uuid'
. ' FROM helpdesk_handovers h'
. ' LEFT JOIN users uc ON uc.id = h.created_by'
. ' LEFT JOIN users uu ON uu.id = h.updated_by'
. ' WHERE h.id = ? AND h.tenant_id = ? LIMIT 1',
(string) $id,
(string) $tenantId
);
return $this->normalizeRow($row);
}
/**
* @param array<string, mixed> $filters
* @return array{total: int, rows: list<array<string, mixed>>}
*/
public function listPaged(int $tenantId, array $filters): array
{
$search = trim((string) ($filters['search'] ?? ''));
$status = trim((string) ($filters['status'] ?? ''));
$productCode = trim((string) ($filters['product_code'] ?? ''));
[$limit, $offset] = RepoQuery::sanitizeLimitOffset($filters, 20, 1, 100, 0);
[$order, $dir] = RepoQuery::sanitizeOrder(
$filters,
['id', 'domain_url', 'debitor_name', 'product_code', 'status', 'created_at', 'created_by'],
'created_at',
'desc'
);
$where = ['h.tenant_id = ?'];
$params = [(string) $tenantId];
RepoQuery::addLikeFilter($where, $params, ['h.debitor_name', 'h.debitor_no', 'h.domain_url', 'h.product_code'], $search);
if ($status !== '' && $status !== 'all') {
$where[] = 'h.status = ?';
$params[] = $status;
}
if ($productCode !== '') {
$where[] = 'h.product_code = ?';
$params[] = $productCode;
}
$whereSql = ' WHERE ' . implode(' AND ', $where);
$total = (int) (DB::selectValue('SELECT COUNT(*) FROM helpdesk_handovers h' . $whereSql, ...$params) ?? 0);
$rows = DB::select(
'SELECT h.*, u.display_name AS created_by_name,'
. ' sp.name AS product_name, sp.bc_description AS product_bc_description'
. ' FROM helpdesk_handovers h'
. ' LEFT JOIN users u ON u.id = h.created_by'
. ' LEFT JOIN helpdesk_software_products sp ON sp.code = h.product_code'
. $whereSql
. sprintf(' ORDER BY h.`%s` %s LIMIT ? OFFSET ?', $order, $dir),
...array_merge($params, [(string) $limit, (string) $offset])
);
$normalized = [];
if (is_array($rows)) {
foreach ($rows as $row) {
$item = $this->normalizeRow($row);
if ($item !== null) {
$normalized[] = $item;
}
}
}
return [
'total' => $total,
'rows' => $normalized,
];
}
public function updateFieldValues(int $tenantId, int $id, string $fieldValuesJson, int $updatedBy): bool
{
$result = DB::update(
'UPDATE helpdesk_handovers SET field_values = ?, updated_by = ? WHERE id = ? AND tenant_id = ?',
$fieldValuesJson,
(string) $updatedBy,
(string) $id,
(string) $tenantId
);
return $result !== false;
}
public function updateStatus(int $tenantId, int $id, string $status, int $updatedBy): bool
{
$result = DB::update(
'UPDATE helpdesk_handovers SET status = ?, updated_by = ? WHERE id = ? AND tenant_id = ?',
$status,
(string) $updatedBy,
(string) $id,
(string) $tenantId
);
return $result !== false;
}
/**
* @param list<int> $ids
*/
public function deleteByIds(int $tenantId, array $ids): int
{
if ($ids === []) {
return 0;
}
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$params = array_map('strval', $ids);
$params[] = (string) $tenantId;
$result = DB::update(
'DELETE FROM helpdesk_handovers WHERE id IN (' . $placeholders . ') AND tenant_id = ?',
...$params
);
return is_int($result) ? $result : 0;
}
/**
* Find all handovers linked to a specific domain.
*
* @api Called from DomainDetailService
* @return list<array<string, mixed>>
*/
public function findByDomainNo(int $tenantId, string $domainNo): array
{
if ($domainNo === '') {
return [];
}
$rows = DB::select(
'SELECT h.*, u.display_name AS created_by_name,'
. ' sp.name AS product_name, sp.bc_description AS product_bc_description'
. ' FROM helpdesk_handovers h'
. ' LEFT JOIN users u ON u.id = h.created_by'
. ' LEFT JOIN helpdesk_software_products sp ON sp.code = h.product_code'
. ' WHERE h.tenant_id = ? AND h.domain_no = ?'
. ' ORDER BY h.created_at DESC',
(string) $tenantId,
$domainNo
);
$normalized = [];
if (is_array($rows)) {
foreach ($rows as $row) {
$item = $this->normalizeRow($row);
if ($item !== null) {
$normalized[] = $item;
}
}
}
return $normalized;
}
public function beginTransaction(): void
{
DB::handle()->begin_transaction();
}
public function commitTransaction(): void
{
DB::handle()->commit();
}
public function rollbackTransaction(): void
{
DB::handle()->rollback();
}
private function normalizeRow(mixed $row): ?array
{
if (!is_array($row)) {
return null;
}
// JOIN queries return nested arrays keyed by table name — merge all sub-arrays
$item = [];
foreach ($row as $key => $value) {
if (is_array($value)) {
$item = array_merge($item, $value);
} else {
$item[$key] = $value;
}
}
if (!isset($item['id'])) {
return null;
}
return $item;
}
}