| Server IP : 72.60.21.38 / Your IP : 216.73.217.140 Web Server : LiteSpeed System : Linux uk-fast-web1372.main-hosting.eu 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64 User : u390967363 ( 390967363) PHP Version : 8.2.30 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/u390967363/domains/aibenproperties.com/public_html/app/ |
Upload File : |
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once 'includes/db.php';
require_once 'includes/functions.php';
require_once 'includes/installments.php';
$_role = $_SESSION['user_role'] ?? 'guest';
$__role_norm = strtolower(trim((string)$_role));
$__role_norm = str_replace([' ', '-'], '_', $__role_norm);
$__allow_roles = ['admin','super_admin','operations','operations_officer','estate_manager','finance_manager','finance_officer','finance','sales_manager','customer_rep','chairman_ceo','executive'];
$__is_fin = function_exists('isFinanceTier') ? isFinanceTier($_role) : in_array($__role_norm, ['finance','finance_officer','finance_manager'], true);
$__is_admin_tier = function_exists('isAdminTier') ? isAdminTier($_role) : in_array($__role_norm, ['admin','super_admin','estate_manager','sales_manager'], true);
$__is_manager_tier = function_exists('isManagerTier') ? isManagerTier($_role) : in_array($__role_norm, ['estate_manager','sales_manager'], true);
$__is_exec_tier = function_exists('isExecutive') ? isExecutive($_role) : in_array($__role_norm, ['chairman_ceo','chairman','ceo','executive','executives'], true);
$__is_readonly = function_exists('isReadOnlyRole') ? isReadOnlyRole($_role) : false;
if (!isset($_SESSION['user_id']) || (!$__is_fin && !in_array($__role_norm, $__allow_roles, true))) {
header("Location: dashboard.php");
exit;
}
$allocId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($allocId <= 0) {
header("Location: allocations.php");
exit;
}
$companyId = function_exists('getCurrentCompanyId') ? getCurrentCompanyId() : null;
$hasUsersTable = false;
$hasPropsTable = false;
$hasEstatesTable = false;
$hasDealsTable = false;
$hasPaymentsTable = false;
$hasAuditTable = false;
try { $hasUsersTable = $pdo->query("SHOW TABLES LIKE 'users'")->rowCount() > 0; } catch (Throwable $e) {}
try { $hasPropsTable = $pdo->query("SHOW TABLES LIKE 'properties'")->rowCount() > 0; } catch (Throwable $e) {}
try { $hasEstatesTable = $pdo->query("SHOW TABLES LIKE 'estates'")->rowCount() > 0; } catch (Throwable $e) {}
try { $hasDealsTable = $pdo->query("SHOW TABLES LIKE 'deals_submit'")->rowCount() > 0; } catch (Throwable $e) {}
try { $hasPaymentsTable = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0; } catch (Throwable $e) {}
try { $hasAuditTable = $pdo->query("SHOW TABLES LIKE 'audit_logs'")->rowCount() > 0; } catch (Throwable $e) {}
$propertyJoin = $hasPropsTable ? "LEFT JOIN properties p ON a.property_id = p.id" : "LEFT JOIN (SELECT NULL AS id, NULL AS title, NULL AS price, NULL AS address, NULL AS estate_id) p ON 1=0";
$hasAllocEstateId = $hasEstatesTable && function_exists('tableHasColumn') && tableHasColumn('allocations', 'estate_id');
$hasPropertyEstateId = $hasPropsTable && $hasEstatesTable && function_exists('tableHasColumn') && tableHasColumn('properties', 'estate_id');
$estateJoin = $hasEstatesTable ? ($hasAllocEstateId ? "LEFT JOIN estates e ON a.estate_id = e.id" : ($hasPropertyEstateId ? "LEFT JOIN estates e ON p.estate_id = e.id" : "LEFT JOIN estates e ON 1=0")) : "LEFT JOIN (SELECT NULL AS id, NULL AS name) e ON 1=0";
$dealJoin = $hasDealsTable ? "LEFT JOIN deals_submit d ON a.deal_id = d.id" : "LEFT JOIN (SELECT NULL AS id, NULL AS project_desc, NULL AS project_name, NULL AS amount_offered) d ON 1=0";
$clientNameExpr = $hasUsersTable && function_exists('tableHasColumn') && tableHasColumn('users', 'name')
? "c.name"
: ($hasUsersTable && function_exists('tableHasColumn') && tableHasColumn('users', 'full_name') ? "c.full_name" : "NULL");
$clientEmailExpr = $hasUsersTable && function_exists('tableHasColumn') && tableHasColumn('users', 'email') ? "c.email" : "NULL";
$clientPhoneExpr = "NULL";
if ($hasUsersTable && function_exists('tableHasColumn')) {
if (tableHasColumn('users', 'phone')) { $clientPhoneExpr = "c.phone"; }
elseif (tableHasColumn('users', 'mobile')) { $clientPhoneExpr = "c.mobile"; }
}
$plotNoExpr = "NULL";
if (function_exists('tableHasColumn')) {
if (tableHasColumn('allocations', 'plot_number')) { $plotNoExpr = "a.plot_number"; }
elseif ($hasPropsTable && tableHasColumn('properties', 'plot_number')) { $plotNoExpr = "p.plot_number"; }
elseif ($hasPropsTable && tableHasColumn('properties', 'unit_number')) { $plotNoExpr = "p.unit_number"; }
}
$plotSizeExpr = "NULL";
if (function_exists('tableHasColumn')) {
if (tableHasColumn('allocations', 'plot_size')) { $plotSizeExpr = "a.plot_size"; }
elseif ($hasPropsTable && tableHasColumn('properties', 'plot_size')) { $plotSizeExpr = "p.plot_size"; }
elseif ($hasPropsTable && tableHasColumn('properties', 'area_sqm')) { $plotSizeExpr = "p.area_sqm"; }
}
$allocPriceExpr = "0";
if (function_exists('tableHasColumn')) {
if (tableHasColumn('allocations','total_price')) { $allocPriceExpr = "a.total_price"; }
elseif (tableHasColumn('allocations','total_amount')) { $allocPriceExpr = "a.total_amount"; }
elseif (tableHasColumn('allocations','amount')) { $allocPriceExpr = "a.amount"; }
elseif (tableHasColumn('allocations','price')) { $allocPriceExpr = "a.price"; }
elseif (tableHasColumn('allocations','final_price')) { $allocPriceExpr = "a.final_price"; }
elseif (tableHasColumn('allocations','sale_price')) { $allocPriceExpr = "a.sale_price"; }
}
$titleExpr = "COALESCE(NULLIF(p.title,''), NULLIF(e.name,''), NULLIF(d.project_desc,''), NULLIF(d.project_name,''), CONCAT('Allocation #', a.id))";
$addressExpr = "COALESCE(NULLIF(p.address,''), NULLIF(e.name,''), NULLIF(d.project_name,''), NULLIF(d.project_desc,''), 'N/A')";
$paidExpr = "0";
if ($hasPaymentsTable && function_exists('tableHasColumn')) {
$statusApprovedSql = function_exists('kpiSqlList') ? kpiSqlList(kpiPaymentFinalizedStatuses()) : "('verified','approved','paid','completed','success')";
$paidMatchParts = [];
if (tableHasColumn('payments','allocation_id')) { $paidMatchParts[] = "allocation_id = a.id"; }
if (tableHasColumn('payments','deal_id') && tableHasColumn('allocations','deal_id')) { $paidMatchParts[] = "(a.deal_id IS NOT NULL AND deal_id = a.deal_id)"; }
$paidExpr = $paidMatchParts
? "(SELECT COALESCE(SUM(amount), 0) FROM payments WHERE status IN $statusApprovedSql AND (" . implode(' OR ', $paidMatchParts) . "))"
: "0";
}
$sql = "
SELECT
a.*,
{$titleExpr} AS property_title,
COALESCE(NULLIF({$allocPriceExpr},0), p.price, d.amount_offered, 0) AS total_price,
COALESCE(p.price, d.amount_offered, 0) AS property_price,
{$addressExpr} AS property_address,
e.name AS estate_name,
{$plotNoExpr} AS plot_number,
{$plotSizeExpr} AS plot_size,
{$paidExpr} AS total_paid
";
if ($clientNameExpr !== "NULL") { $sql .= ", {$clientNameExpr} AS client_name"; }
if ($clientEmailExpr !== "NULL") { $sql .= ", {$clientEmailExpr} AS client_email"; }
if ($clientPhoneExpr !== "NULL") { $sql .= ", {$clientPhoneExpr} AS client_phone"; }
$sql .= "
FROM allocations a
{$propertyJoin}
{$estateJoin}
{$dealJoin}
";
if ($hasUsersTable) { $sql .= " LEFT JOIN users c ON a.user_id = c.id"; }
$sql .= " WHERE a.id = ?";
$params = [$allocId];
if ($companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sql .= " AND (a.company_id = ? OR a.company_id IS NULL)";
$params[] = $companyId;
}
$sql .= " LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$alloc = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$alloc) {
header("Location: allocations.php?notice=" . urlencode('Allocation not found') . "&type=warning");
exit;
}
$total = (float)($alloc['total_price'] ?? 0);
$paid = (float)($alloc['total_paid'] ?? 0);
$balance = max(0.0, $total - $paid);
$percent = $total > 0 ? min(100, (int)round(($paid / $total) * 100)) : 0;
$barTone = $percent >= 100 ? 'success' : (($percent > 0) ? 'warning' : 'danger');
$statusRaw = strtolower(trim((string)($alloc['status'] ?? '')));
$statusKey = $statusRaw !== '' ? str_replace(' ', '_', $statusRaw) : 'unknown';
$instSummary = function_exists('getInstallmentStatusSummary') ? getInstallmentStatusSummary($pdo, $allocId) : ['label'=>'On Track','class'=>'badge bg-success'];
$instLabel = (string)($instSummary['label'] ?? 'On Track');
$instClass = (string)($instSummary['class'] ?? 'badge bg-success');
$canRecordPayment = !$__is_readonly && ($__is_fin || $__is_admin_tier);
$canApproveAlloc = !$__is_readonly && function_exists('hasApprovalRights') ? hasApprovalRights($_role, 'allocations') : $__is_admin_tier;
$canUpdateStatus = !$__is_readonly && ($__is_admin_tier || $__is_manager_tier || $__is_exec_tier);
$canAllocate = $canUpdateStatus && in_array($statusKey, ['approved','executive_approved','admin_approved'], true);
$userDisplayCol = null;
if ($hasUsersTable && function_exists('tableHasColumn')) {
if (tableHasColumn('users','name')) { $userDisplayCol = 'name'; }
elseif (tableHasColumn('users','full_name')) { $userDisplayCol = 'full_name'; }
}
$payments = [];
$paymentDateExpr = "created_at";
if ($hasPaymentsTable && function_exists('tableHasColumn')) {
if (tableHasColumn('payments','date')) { $paymentDateExpr = "date"; }
elseif (tableHasColumn('payments','payment_date')) { $paymentDateExpr = "payment_date"; }
elseif (tableHasColumn('payments','created_at')) { $paymentDateExpr = "created_at"; }
}
$recordedByCol = null;
if ($hasPaymentsTable && function_exists('tableHasColumn')) {
foreach (['recorded_by','created_by','uploaded_by','updated_by'] as $cand) {
if (tableHasColumn('payments', $cand)) { $recordedByCol = $cand; break; }
}
}
$matchWhere = [];
$matchParams = [];
if ($hasPaymentsTable && function_exists('tableHasColumn') && tableHasColumn('payments','allocation_id')) {
$matchWhere[] = "p.allocation_id = ?";
$matchParams[] = $allocId;
}
if ($hasPaymentsTable && function_exists('tableHasColumn') && tableHasColumn('payments','deal_id') && !empty($alloc['deal_id'])) {
$matchWhere[] = "p.deal_id = ?";
$matchParams[] = (int)$alloc['deal_id'];
}
if ($hasPaymentsTable && count($matchWhere) > 0) {
$sqlPay = "SELECT p.*";
if ($recordedByCol && $hasUsersTable && $userDisplayCol) { $sqlPay .= ", urec.{$userDisplayCol} AS recorded_by_name"; }
if ($hasUsersTable && $userDisplayCol && function_exists('tableHasColumn') && tableHasColumn('payments','approved_by')) { $sqlPay .= ", uapp.{$userDisplayCol} AS approved_by_name"; }
$sqlPay .= ", p.{$paymentDateExpr} AS payment_sort_date FROM payments p";
if ($recordedByCol && $hasUsersTable) { $sqlPay .= " LEFT JOIN users urec ON p.{$recordedByCol} = urec.id"; }
if ($hasUsersTable && function_exists('tableHasColumn') && tableHasColumn('payments','approved_by')) { $sqlPay .= " LEFT JOIN users uapp ON p.approved_by = uapp.id"; }
$sqlPay .= " WHERE (" . implode(' OR ', $matchWhere) . ")";
if ($companyId && function_exists('tableHasColumn') && tableHasColumn('payments','company_id')) {
$sqlPay .= " AND (p.company_id = ? OR p.company_id IS NULL)";
$matchParams[] = $companyId;
}
$sqlPay .= " ORDER BY p.{$paymentDateExpr} DESC, p.id DESC";
$stp = $pdo->prepare($sqlPay);
$stp->execute($matchParams);
$payments = $stp->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
$okPaidStatuses = function_exists('kpiPaymentFinalizedStatuses') ? array_map('strtolower', kpiPaymentFinalizedStatuses()) : ['verified','approved','paid','completed','success'];
$totalPaidFromHistory = 0.0;
foreach ($payments as $py) {
$st = strtolower((string)($py['status'] ?? ''));
if (in_array($st, $okPaidStatuses, true)) {
$totalPaidFromHistory += (float)($py['amount'] ?? 0);
}
}
$alerts = [];
if (in_array($statusKey, ['pending','pending_executive_approval','admin_pending'], true)) {
$alerts[] = ['type' => 'warning', 'title' => 'Unapproved allocation', 'msg' => 'This allocation is not yet approved. Some downstream actions should be restricted until approval.'];
}
if ($balance > 0 && (strpos(strtolower($instLabel), 'overdue') !== false || strpos(strtolower($instClass), 'danger') !== false)) {
$alerts[] = ['type' => 'danger', 'title' => 'Overdue payment', 'msg' => 'Installment schedule indicates overdue payments. Review payment history and follow up with the client.'];
}
$execFeedback = [
'decision' => '',
'decided_at' => '',
'comment' => '',
];
try {
if (function_exists('ensureAllocationLetterDataTable')) { ensureAllocationLetterDataTable($pdo); }
if (function_exists('allocationLetterDataGet')) {
$ld = allocationLetterDataGet($pdo, (int)$allocId);
if (is_array($ld)) {
$execFeedback['decision'] = (string)($ld['chairman_decision'] ?? '');
$execFeedback['decided_at'] = (string)($ld['chairman_decided_at'] ?? '');
$execFeedback['comment'] = (string)($ld['chairman_comment'] ?? '');
}
}
} catch (Throwable $e) {}
if (trim($execFeedback['comment']) === '') {
foreach (['exec_comment','review_comment','rejection_reason','reject_reason','reason','comment','note'] as $k) {
if (isset($alloc[$k]) && trim((string)$alloc[$k]) !== '') {
$execFeedback['comment'] = (string)$alloc[$k];
break;
}
}
}
if (trim($execFeedback['decision']) === '' && in_array($statusKey, ['rejected','revoked','declined'], true)) {
$execFeedback['decision'] = 'reject';
}
$letterDocs = [];
try {
$hasDocs = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0;
if ($hasDocs) {
$sqlDoc = "SELECT * FROM documents WHERE 1=1";
$docParams = [];
if (function_exists('tableHasColumn') && tableHasColumn('documents','type')) { $sqlDoc .= " AND type = ?"; $docParams[] = 'allocation_letter'; }
if (function_exists('tableHasColumn') && tableHasColumn('documents','allocation_id')) {
$sqlDoc .= " AND allocation_id = ?";
$docParams[] = $allocId;
} else {
$sqlDoc .= " AND file_path LIKE ?";
$docParams[] = '%Allocation_Letter_' . $allocId . '_%';
}
if ($companyId && function_exists('tableHasColumn') && tableHasColumn('documents','company_id')) {
$sqlDoc .= " AND (company_id = ? OR company_id IS NULL)";
$docParams[] = $companyId;
}
$sqlDoc .= " ORDER BY id DESC LIMIT 10";
$sd = $pdo->prepare($sqlDoc);
$sd->execute($docParams);
$letterDocs = $sd->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
} catch (Throwable $e) {}
$receiptPaths = [];
foreach ($payments as $py) {
foreach (['proof_file','receipt','receipt_path','file_path'] as $k) {
if (!empty($py[$k])) { $receiptPaths[] = (string)$py[$k]; }
}
}
$receiptPaths = array_values(array_unique(array_filter($receiptPaths)));
$transferRow = null;
$hasTransfersTable = false;
try { $hasTransfersTable = $pdo->query("SHOW TABLES LIKE 'ownership_transfers'")->rowCount() > 0; } catch (Throwable $e) { $hasTransfersTable = false; }
if ($hasTransfersTable) {
try {
$qt = "SELECT * FROM ownership_transfers WHERE allocation_id = ? ORDER BY id DESC LIMIT 1";
$tp = [$allocId];
if ($companyId && function_exists('tableHasColumn') && tableHasColumn('ownership_transfers','company_id')) {
$qt = "SELECT * FROM ownership_transfers WHERE allocation_id = ? AND (company_id = ? OR company_id IS NULL) ORDER BY id DESC LIMIT 1";
$tp[] = $companyId;
}
$stt = $pdo->prepare($qt);
$stt->execute($tp);
$transferRow = $stt->fetch(PDO::FETCH_ASSOC) ?: null;
} catch (Throwable $e) { $transferRow = null; }
}
$transferActionEnabled = in_array($statusKey, ['active','allocated','completed','finalized'], true);
$transferActionVisible = in_array($statusKey, ['approved','executive_approved','admin_approved','active','allocated','completed','finalized'], true);
$transferStatus = strtolower((string)($transferRow['status'] ?? ''));
$hasPendingTransfer = $transferRow && $transferStatus === 'pending';
$hasApprovedTransfer = $transferRow && $transferStatus === 'approved';
$reallocationNewAllocationId = 0;
$reallocationHasLink = false;
$reallocationHasDoc = false;
try {
if ($statusKey === 'revoked' && function_exists('tableHasColumn') && tableHasColumn('allocations','reallocated_from_allocation_id')) {
$st = $pdo->prepare("SELECT id FROM allocations WHERE reallocated_from_allocation_id = ? ORDER BY id DESC LIMIT 1");
$st->execute([$allocId]);
$reallocationNewAllocationId = (int)($st->fetchColumn() ?: 0);
$reallocationHasLink = $reallocationNewAllocationId > 0;
}
} catch (Throwable $e) { $reallocationNewAllocationId = 0; $reallocationHasLink = false; }
try {
$hasDocsTable = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0;
if ($hasDocsTable && $statusKey === 'revoked') {
$sql = "SELECT COUNT(*) FROM documents WHERE 1=1";
$params = [];
if (function_exists('tableHasColumn') && tableHasColumn('documents','type')) { $sql .= " AND type = ?"; $params[] = 'reallocation_letter'; }
if (function_exists('tableHasColumn') && tableHasColumn('documents','reference_id')) { $sql .= " AND reference_id = ?"; $params[] = $allocId; }
else { $sql .= " AND file_path LIKE ?"; $params[] = '%Reallocation_Letter_' . $allocId . '_%'; }
$st = $pdo->prepare($sql);
$st->execute($params);
$reallocationHasDoc = ((int)$st->fetchColumn()) > 0;
}
} catch (Throwable $e) { $reallocationHasDoc = false; }
$buyerOptions = [];
try {
if ($hasUsersTable) {
$qU = "SELECT id, name, email FROM users WHERE role = 'client' AND id <> ? ";
$pU = [(int)($alloc['user_id'] ?? 0)];
if ($companyId && function_exists('tableHasColumn') && tableHasColumn('users','company_id')) { $qU .= " AND (company_id = ? OR company_id IS NULL)"; $pU[] = $companyId; }
$qU .= " ORDER BY name ASC LIMIT 2000";
$stU = $pdo->prepare($qU);
$stU->execute($pU);
$buyerOptions = $stU->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
} catch (Throwable $e) { $buyerOptions = []; }
if (in_array($statusKey, ['executive_approved','approved','admin_approved','allocated','finalized','completed'], true) && count($letterDocs) === 0) {
$alerts[] = ['type' => 'warning', 'title' => 'Missing allocation letter document', 'msg' => 'No allocation letter file is linked to this allocation. Generate or upload the letter to complete the file.'];
}
if (count($payments) > 0) {
$missingProof = 0;
foreach ($payments as $py) {
$st = strtolower((string)($py['status'] ?? ''));
if (!in_array($st, $okPaidStatuses, true)) { continue; }
$hasAny = false;
foreach (['proof_file','receipt','receipt_path','file_path'] as $k) {
if (!empty($py[$k])) { $hasAny = true; break; }
}
if (!$hasAny) { $missingProof += 1; }
}
if ($missingProof > 0) {
$alerts[] = ['type' => 'warning', 'title' => 'Missing receipts', 'msg' => $missingProof . ' approved/paid payment(s) have no receipt/proof file attached.'];
}
}
$timeline = [];
$pushEvent = function($when, $title, $meta, $icon, $tone) use (&$timeline) {
$ts = null;
if ($when) {
try { $ts = strtotime((string)$when); } catch (Throwable $e) { $ts = null; }
}
$timeline[] = [
'ts' => $ts ?: 0,
'when' => $when,
'title' => $title,
'meta' => $meta,
'icon' => $icon,
'tone' => $tone
];
};
if (!empty($alloc['created_at'])) { $pushEvent($alloc['created_at'], 'Allocation created', 'System', 'fa-calendar-plus', 'primary'); }
if (!empty($alloc['approved_at'])) { $pushEvent($alloc['approved_at'], 'Allocation approved', 'Approval workflow', 'fa-circle-check', 'success'); }
if (!empty($alloc['updated_at'])) { $pushEvent($alloc['updated_at'], 'Allocation updated', 'Record updated', 'fa-pen-to-square', 'secondary'); }
foreach ($payments as $py) {
$dt = $py['payment_sort_date'] ?? ($py['date'] ?? ($py['payment_date'] ?? ($py['created_at'] ?? null)));
$amt = (float)($py['amount'] ?? 0);
$st = (string)($py['status'] ?? 'payment');
$pushEvent($dt, 'Payment recorded', '₦' . number_format($amt, 2) . ' • ' . $st, 'fa-money-bill-wave', 'info');
}
if ($hasAuditTable && function_exists('tableHasColumn') && tableHasColumn('audit_logs','entity_id') && tableHasColumn('audit_logs','entity_type')) {
try {
$fields = "a.*";
if (tableHasColumn('audit_logs','changed_by') && $hasUsersTable) { $fields .= ", u.name AS actor_name"; }
$qa = "SELECT {$fields} FROM audit_logs a";
if (tableHasColumn('audit_logs','changed_by') && $hasUsersTable) { $qa .= " LEFT JOIN users u ON a.changed_by = u.id"; }
$qa .= " WHERE a.entity_id = ?";
$ap = [$allocId];
if ($companyId && tableHasColumn('audit_logs','company_id')) {
$qa .= " AND (a.company_id = ? OR a.company_id IS NULL)";
$ap[] = $companyId;
}
$qa .= " ORDER BY a.created_at DESC LIMIT 200";
$sa = $pdo->prepare($qa);
$sa->execute($ap);
$auditRows = $sa->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($auditRows as $ar) {
$type = (string)($ar['entity_type'] ?? 'audit');
$oldV = isset($ar['old_value']) ? (string)$ar['old_value'] : '';
$newV = isset($ar['new_value']) ? (string)$ar['new_value'] : '';
$reason = isset($ar['reason']) ? (string)$ar['reason'] : '';
$who = isset($ar['actor_name']) && $ar['actor_name'] ? (string)$ar['actor_name'] : (isset($ar['changed_by']) ? ('User #' . (int)$ar['changed_by']) : 'System');
$meta = $who;
if ($oldV !== '' || $newV !== '') { $meta .= ' • ' . trim($oldV . ' → ' . $newV); }
if ($reason !== '') { $meta .= ' • ' . $reason; }
$pushEvent($ar['created_at'] ?? null, ucfirst(str_replace('_',' ', $type)), $meta, 'fa-shield-halved', 'secondary');
}
} catch (Throwable $e) {}
}
usort($timeline, function($a, $b) {
return ($b['ts'] <=> $a['ts']);
});
$fmtMoney = function($v) {
$n = (float)$v;
if (function_exists('formatCurrency')) {
try { return formatCurrency($n); } catch (Throwable $e) {}
}
return '₦' . number_format($n, 2);
};
require 'includes/header.php';
?>
<style>
.alloc-detail-wrap { max-width: 1400px; }
.alloc-hero { border: 1px solid rgba(226, 232, 240, 0.9); box-shadow: 0 18px 45px rgba(15, 23, 42, 0.06); border-radius: 18px; background: var(--bg-card); }
.alloc-hero .meta { color: #64748b; font-size: 0.85rem; }
.alloc-kpi { border: 1px solid rgba(226, 232, 240, 0.9); border-radius: 14px; background: #fff; }
[data-theme="dark"] .alloc-kpi { background: var(--bg-card); }
.alloc-section-card { border: 1px solid rgba(226, 232, 240, 0.9); border-radius: 18px; background: var(--bg-card); box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05); }
.alloc-section-title { font-size: 0.78rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em; color: #64748b; }
.alloc-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.alloc-actions .btn { border-radius: 12px; }
.alloc-timeline .timeline-item { padding-bottom: 1.1rem; }
.alloc-doc-pill { display: inline-flex; gap: 0.5rem; align-items: center; padding: 0.45rem 0.7rem; border-radius: 999px; border: 1px solid rgba(226, 232, 240, 0.9); background: #fff; }
[data-theme="dark"] .alloc-doc-pill { background: var(--bg-card); }
.alloc-doc-pill a { text-decoration: none; }
.transfer-modal .modal-content{ border-radius: 18px; overflow: hidden; }
.transfer-modal .modal-header{ background: linear-gradient(135deg, rgba(239, 246, 255, 0.95), rgba(240, 253, 244, 0.8)); border-bottom: 1px solid rgba(226,232,240,0.9); }
.transfer-modal .modal-title{ font-weight: 800; color: #0f172a; }
.transfer-modal .transfer-kpi{ border: 1px solid rgba(226,232,240,0.9); border-radius: 14px; background: #fff; padding: 12px; }
[data-theme="dark"] .transfer-modal .transfer-kpi{ background: var(--bg-card); }
.transfer-modal .transfer-kpi .lbl{ font-size: 12px; color: #64748b; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.transfer-modal .transfer-kpi .val{ font-weight: 800; color: #0f172a; }
.transfer-modal .upload-card{ border: 1px dashed rgba(15,23,42,0.22); border-radius: 14px; padding: 12px; background: rgba(248,250,252,0.8); }
.transfer-modal .upload-card .small{ color: #64748b; }
.transfer-modal .modal-dialog{ margin: 0.75rem auto; }
.transfer-modal .modal-content{ max-height: calc(100vh - 1.5rem); }
.transfer-modal form{ display:flex; flex-direction:column; flex:1 1 auto; min-height:0; }
.transfer-modal .modal-body{ overflow:auto; }
.transfer-modal .modal-footer{ margin-top:auto; background:#fff; }
[data-theme="dark"] .transfer-modal .modal-footer{ background: var(--bg-card); }
</style>
<div class="container-fluid px-4 alloc-detail-wrap">
<div class="d-flex justify-content-between align-items-start mt-4 mb-4">
<div>
<div class="d-flex align-items-center gap-3">
<h2 class="mb-0 fw-bold text-contrast">Allocation #<?= (int)$allocId ?></h2>
<span class="<?= htmlspecialchars($instClass) ?>"><?= htmlspecialchars($instLabel) ?></span>
</div>
<?php
$estateName = trim((string)($alloc['estate_name'] ?? '')) !== '' ? (string)$alloc['estate_name'] : 'N/A';
$plotBits = [];
if (!empty($alloc['plot_number'])) { $plotBits[] = 'Plot ' . (string)$alloc['plot_number']; }
if (!empty($alloc['plot_size'])) { $plotBits[] = (string)$alloc['plot_size']; }
$plotLine = $plotBits ? implode(' • ', $plotBits) : 'N/A';
$clientName = isset($alloc['client_name']) && $alloc['client_name'] !== '' ? (string)$alloc['client_name'] : ('Client #' . (int)($alloc['user_id'] ?? 0));
?>
<div class="meta mt-2"><?= htmlspecialchars($estateName) ?> • <?= htmlspecialchars($plotLine) ?> • <?= htmlspecialchars($clientName) ?></div>
</div>
<div class="text-end">
<a href="allocations.php" class="btn btn-outline-secondary"><i class="fa-solid fa-arrow-left me-2"></i>Back</a>
</div>
</div>
<?php if (!empty($alerts)): ?>
<div class="mb-4">
<?php foreach ($alerts as $al): ?>
<div class="alert alert-<?= htmlspecialchars($al['type']) ?> d-flex justify-content-between align-items-start">
<div>
<div class="fw-bold"><?= htmlspecialchars($al['title']) ?></div>
<div class="small"><?= htmlspecialchars($al['msg']) ?></div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php
$execDecisionRaw = strtolower(trim((string)($execFeedback['decision'] ?? '')));
$execDecisionLabel = $execDecisionRaw === 'approve' ? 'Approved' : ($execDecisionRaw === 'request_changes' ? 'Changes Requested' : ($execDecisionRaw === 'reject' ? 'Rejected' : ''));
$execDecisionAt = trim((string)($execFeedback['decided_at'] ?? ''));
$execComment = trim((string)($execFeedback['comment'] ?? ''));
$showExecFeedback = ($execDecisionLabel !== '' || $execComment !== '');
$execTone = $execDecisionRaw === 'approve' ? 'success' : (($execDecisionRaw === 'request_changes') ? 'warning' : (($execDecisionRaw === 'reject') ? 'danger' : 'secondary'));
?>
<?php if ($showExecFeedback): ?>
<div class="alloc-section-card p-4 mb-4">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="alloc-section-title mb-1">Executive Feedback</div>
<?php if ($execDecisionLabel !== ''): ?>
<div class="fw-bold"><?= htmlspecialchars($execDecisionLabel) ?></div>
<?php endif; ?>
<?php if ($execDecisionAt !== ''): ?>
<div class="small text-muted"><?= htmlspecialchars($execDecisionAt) ?></div>
<?php endif; ?>
</div>
<div class="badge bg-<?= htmlspecialchars($execTone) ?>"><?= htmlspecialchars($execDecisionLabel !== '' ? $execDecisionLabel : 'Feedback') ?></div>
</div>
<div class="mt-3 small" style="white-space:pre-wrap;"><?= $execComment !== '' ? htmlspecialchars($execComment) : '<span class="text-muted fst-italic">No note provided.</span>' ?></div>
</div>
<?php endif; ?>
<div class="alloc-hero p-4 mb-4">
<div class="row g-3 align-items-stretch">
<div class="col-lg-7">
<div class="d-flex justify-content-between align-items-start">
<div>
<?php
$statusLabel = $statusRaw !== '' ? ucwords(str_replace('_',' ', $statusKey)) : 'Unknown';
$badgeCls = 'status-badge-restricted';
if (in_array($statusKey, ['approved','executive_approved','admin_approved','allocated','finalized','completed'], true)) { $badgeCls = 'status-badge-approved'; }
elseif (in_array($statusKey, ['pending','pending_executive_approval','admin_pending'], true)) { $badgeCls = 'status-badge-pending'; }
elseif (in_array($statusKey, ['revoked','rejected'], true)) { $badgeCls = 'status-badge-rejected'; }
?>
<div class="d-flex align-items-center gap-2 mb-2">
<span class="status-badge <?= htmlspecialchars($badgeCls) ?>"><i class="fa-solid fa-clipboard-check"></i> <?= htmlspecialchars($statusLabel) ?></span>
</div>
<div class="fw-bold fs-5"><?= htmlspecialchars((string)($alloc['property_title'] ?? 'N/A')) ?></div>
<div class="text-muted small mt-1"><?= htmlspecialchars((string)($alloc['property_address'] ?? 'N/A')) ?></div>
<div class="small text-muted mt-2">
<span class="fw-semibold">Client:</span> <?= htmlspecialchars($clientName) ?>
<?php if (!empty($alloc['client_phone'])): ?> • <span class="fw-semibold">Phone:</span> <?= htmlspecialchars((string)$alloc['client_phone']) ?><?php endif; ?>
<?php if (!empty($alloc['client_email'])): ?> • <span class="fw-semibold">Email:</span> <?= htmlspecialchars((string)$alloc['client_email']) ?><?php endif; ?>
</div>
</div>
</div>
<div class="alloc-actions mt-4">
<?php if ($canRecordPayment): ?>
<a class="btn btn-primary" href="finance-payment-add.php?allocation_id=<?= (int)$allocId ?>" target="_blank" rel="noopener">
<i class="fa-solid fa-money-bill-wave me-2"></i>Record Payment
</a>
<?php endif; ?>
<?php if ($canApproveAlloc && in_array($statusKey, ['pending','pending_executive_approval','admin_pending'], true)): ?>
<button type="button" class="btn btn-success" onclick="changeAllocationStatus(<?= (int)$allocId ?>,'approved')">
<i class="fa-solid fa-circle-check me-2"></i>Approve Allocation
</button>
<?php endif; ?>
<?php if ($canAllocate): ?>
<button type="button" class="btn btn-warning" onclick="changeAllocationStatus(<?= (int)$allocId ?>,'allocated')">
<i class="fa-solid fa-check-double me-2"></i>Allocate Property
</button>
<?php endif; ?>
<?php if ($transferActionVisible && $canUpdateStatus): ?>
<?php if ($hasPendingTransfer || $hasApprovedTransfer): ?>
<a class="btn btn-outline-info" href="ownership-transfers.php?id=<?= (int)($transferRow['id'] ?? 0) ?>" target="_blank" rel="noopener">
<i class="fa-solid fa-right-left me-2"></i><?= $hasPendingTransfer ? 'Transfer Pending' : 'Transfer Approved' ?>
</a>
<?php else: ?>
<?php if ($transferActionEnabled): ?>
<button type="button" class="btn btn-outline-info" data-bs-toggle="modal" data-bs-target="#ownershipTransferModal">
<i class="fa-solid fa-right-left me-2"></i>Transfer Ownership
</button>
<?php else: ?>
<button type="button" class="btn btn-outline-info" disabled title="Transfer is allowed only when allocation status is Active/Allocated">
<i class="fa-solid fa-right-left me-2"></i>Transfer Ownership
</button>
<div class="small text-muted mt-1">Transfer is available after allocation becomes Active (Allocated). Use Allocate Property first.</div>
<?php endif; ?>
<?php endif; ?>
<?php endif; ?>
<a class="btn btn-outline-dark" href="allocation-letters.php?action=preview&allocation_id=<?= (int)$allocId ?>" target="_blank" rel="noopener">
<i class="fa-solid fa-print me-2"></i>Print Allocation Letter
</a>
<?php if ($statusKey === 'revoked'): ?>
<?php if ($reallocationHasLink || $reallocationHasDoc): ?>
<form action="generate_document.php" method="post" target="_blank" class="d-inline">
<input type="hidden" name="doc_type" value="reallocation_letter">
<input type="hidden" name="target_id" value="<?= (int)$allocId ?>">
<button type="submit" class="btn btn-outline-primary"><i class="fa-solid fa-file-lines me-2"></i>View Reallocation Letter</button>
</form>
<?php else: ?>
<button type="button" class="btn btn-outline-primary" disabled title="Reallocation letter is available after a replacement allocation is created."><i class="fa-solid fa-file-lines me-2"></i>View Reallocation Letter</button>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<div class="col-lg-5">
<div class="row g-3">
<div class="col-6">
<div class="alloc-kpi p-3 h-100">
<div class="alloc-section-title mb-1">Total Price</div>
<div class="fw-bold"><?= htmlspecialchars($fmtMoney($total)) ?></div>
</div>
</div>
<div class="col-6">
<div class="alloc-kpi p-3 h-100">
<div class="alloc-section-title mb-1">Amount Paid</div>
<div class="fw-bold text-success"><?= htmlspecialchars($fmtMoney($paid)) ?></div>
</div>
</div>
<div class="col-6">
<div class="alloc-kpi p-3 h-100">
<div class="alloc-section-title mb-1">Balance</div>
<div class="fw-bold text-danger"><?= htmlspecialchars($fmtMoney($balance)) ?></div>
</div>
</div>
<div class="col-6">
<div class="alloc-kpi p-3 h-100">
<div class="alloc-section-title mb-1">Progress</div>
<div class="fw-bold"><?= (int)$percent ?>%</div>
<div class="progress-premium mt-2">
<div class="progress-bar-premium progress-bar-<?= htmlspecialchars($barTone) ?>" role="progressbar" style="width: <?= (int)$percent ?>%"></div>
</div>
</div>
</div>
<div class="col-12">
<div class="alloc-kpi p-3">
<div class="small text-muted d-flex justify-content-between">
<span>Paid (from history)</span>
<span class="fw-semibold"><?= htmlspecialchars($fmtMoney($totalPaidFromHistory)) ?></span>
</div>
<div class="small text-muted d-flex justify-content-between mt-1">
<span>Remaining</span>
<span class="fw-semibold"><?= htmlspecialchars($fmtMoney(max(0.0, $total - $totalPaidFromHistory))) ?></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-4">
<div class="col-lg-7">
<div class="alloc-section-card p-4 mb-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="alloc-section-title">Payment History</div>
<div class="small text-muted">Total paid: <span class="fw-semibold"><?= htmlspecialchars($fmtMoney($totalPaidFromHistory)) ?></span></div>
</div>
<div class="table-responsive">
<table class="table table-sm align-middle mb-0">
<thead class="small text-muted">
<tr>
<th>Date</th>
<th class="text-end">Amount</th>
<th>Method</th>
<th>Recorded by</th>
</tr>
</thead>
<tbody>
<?php if (count($payments) === 0): ?>
<tr><td colspan="4" class="text-muted fst-italic">No payments recorded</td></tr>
<?php else: ?>
<?php foreach ($payments as $py): ?>
<?php
$dtRaw = $py['payment_sort_date'] ?? ($py['date'] ?? ($py['payment_date'] ?? ($py['created_at'] ?? '')));
$dt = $dtRaw ? date('Y-m-d', strtotime((string)$dtRaw)) : '';
$method = (string)($py['method'] ?? ($py['payment_method'] ?? '—'));
$who = $py['recorded_by_name'] ?? ($py['approved_by_name'] ?? '—');
?>
<tr>
<td class="text-muted"><?= htmlspecialchars($dt) ?></td>
<td class="text-end fw-bold"><?= htmlspecialchars($fmtMoney((float)($py['amount'] ?? 0))) ?></td>
<td><?= htmlspecialchars($method) ?></td>
<td><?= htmlspecialchars((string)$who) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="alloc-section-card p-4">
<div class="alloc-section-title mb-3">Timeline (Latest First)</div>
<div class="alloc-timeline">
<?php if (count($timeline) === 0): ?>
<div class="text-muted fst-italic">No timeline events available</div>
<?php else: ?>
<?php foreach ($timeline as $ev): ?>
<div class="timeline-item done">
<div class="timeline-dot"></div>
<div class="d-flex justify-content-between">
<div class="fw-bold small">
<i class="fa-solid <?= htmlspecialchars($ev['icon']) ?> text-<?= htmlspecialchars($ev['tone']) ?> me-1"></i>
<?= htmlspecialchars($ev['title']) ?>
</div>
<div class="text-muted small"><?= $ev['when'] ? htmlspecialchars(date('Y-m-d H:i', strtotime((string)$ev['when']))) : '' ?></div>
</div>
<div class="small text-muted"><?= htmlspecialchars($ev['meta']) ?></div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="alloc-section-card p-4 mb-4">
<div class="alloc-section-title mb-3">Property Details</div>
<div class="row g-2 small">
<div class="col-6 text-muted">Estate</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars($estateName) ?></div>
<?php if (!empty($alloc['building_number'])): ?>
<div class="col-6 text-muted">Building No.</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)$alloc['building_number']) ?></div>
<?php endif; ?>
<div class="col-6 text-muted">Plot No.</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)($alloc['plot_number'] ?? 'N/A')) ?></div>
<?php if (!empty($alloc['space_size'])): ?>
<div class="col-6 text-muted">Space Size</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)$alloc['space_size']) ?></div>
<?php endif; ?>
<div class="col-6 text-muted">Size</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)($alloc['plot_size'] ?? 'N/A')) ?></div>
<?php if (!empty($alloc['building_use'])): ?>
<div class="col-6 text-muted">Use</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)$alloc['building_use']) ?></div>
<?php endif; ?>
<?php if (!empty($alloc['house_type'])): ?>
<div class="col-6 text-muted">House Type</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars((string)$alloc['house_type']) ?></div>
<?php endif; ?>
<div class="col-6 text-muted">Price</div><div class="col-6 fw-semibold text-end"><?= htmlspecialchars($fmtMoney((float)($alloc['property_price'] ?? 0))) ?></div>
</div>
</div>
<div class="alloc-section-card p-4 mb-4">
<div class="alloc-section-title mb-3">Client Details</div>
<div class="row g-2 small">
<div class="col-4 text-muted">Name</div><div class="col-8 fw-semibold text-end"><?= htmlspecialchars($clientName) ?></div>
<div class="col-4 text-muted">Phone</div><div class="col-8 fw-semibold text-end"><?= htmlspecialchars((string)($alloc['client_phone'] ?? '—')) ?></div>
<div class="col-4 text-muted">Email</div><div class="col-8 fw-semibold text-end"><?= htmlspecialchars((string)($alloc['client_email'] ?? '—')) ?></div>
</div>
</div>
<div class="alloc-section-card p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="alloc-section-title">Documents</div>
<?php if (!$__is_readonly): ?>
<div class="d-flex gap-2">
<a class="btn btn-sm btn-outline-secondary" href="documents.php?open_upload=1&client_id=<?= (int)($alloc['user_id'] ?? 0) ?>&property_id=<?= (int)($alloc['property_id'] ?? 0) ?>&prefill_title=<?= urlencode('Allocation Letter') ?>&prefill_type=allocation_letter" target="_blank" rel="noopener">
<i class="fa-solid fa-cloud-arrow-up me-1"></i>Upload Letter
</a>
<a class="btn btn-sm btn-outline-secondary" href="documents.php?open_upload=1&client_id=<?= (int)($alloc['user_id'] ?? 0) ?>&property_id=<?= (int)($alloc['property_id'] ?? 0) ?>&prefill_title=<?= urlencode('Receipt') ?>&prefill_type=receipt" target="_blank" rel="noopener">
<i class="fa-solid fa-cloud-arrow-up me-1"></i>Upload Receipt
</a>
</div>
<?php endif; ?>
</div>
<div class="mb-3">
<div class="small text-muted fw-semibold mb-2">Allocation Letter</div>
<?php if (count($letterDocs) === 0): ?>
<div class="text-muted small fst-italic">No allocation letter file found</div>
<?php else: ?>
<div class="d-flex flex-wrap gap-2">
<?php foreach ($letterDocs as $d): ?>
<?php $path = (string)($d['file_path'] ?? ''); ?>
<?php if ($path !== ''): ?>
<span class="alloc-doc-pill">
<i class="fa-solid fa-file-lines text-primary"></i>
<a href="<?= htmlspecialchars($path) ?>" target="_blank" rel="noopener"><?= htmlspecialchars(basename($path)) ?></a>
</span>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div>
<div class="small text-muted fw-semibold mb-2">Receipts</div>
<?php if (count($receiptPaths) === 0): ?>
<div class="text-muted small fst-italic">No receipt files linked</div>
<?php else: ?>
<div class="d-flex flex-wrap gap-2">
<?php foreach ($receiptPaths as $rp): ?>
<span class="alloc-doc-pill">
<i class="fa-solid fa-receipt text-success"></i>
<a href="<?= htmlspecialchars($rp) ?>" target="_blank" rel="noopener"><?= htmlspecialchars(basename($rp)) ?></a>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?php if ($transferActionEnabled && $canUpdateStatus && !$hasPendingTransfer && !$hasApprovedTransfer): ?>
<div class="modal fade transfer-modal" id="ownershipTransferModal" tabindex="-1" aria-labelledby="ownershipTransferLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable modal-fullscreen-sm-down">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title d-flex align-items-center gap-2" id="ownershipTransferLabel">
<i class="fa-solid fa-right-left"></i>
Ownership Transfer
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" action="ownership-transfers.php" enctype="multipart/form-data">
<div class="modal-body">
<input type="hidden" name="action" value="create_transfer">
<input type="hidden" name="allocation_id" value="<?= (int)$allocId ?>">
<div class="alert alert-light border">
<div class="fw-semibold">Policy & Compliance</div>
<div class="small text-muted">Request remains pending until required documents are uploaded and transfer fees are paid and approved by Finance. Ownership will not change until Admin approves.</div>
</div>
<div class="row g-3">
<div class="col-lg-7">
<div class="mb-2 fw-semibold">Transfer Details</div>
<div class="mb-3">
<label class="form-label">Buyer (New Owner)</label>
<select name="buyer_id" class="form-select" required>
<option value="">Select buyer...</option>
<?php foreach ($buyerOptions as $b): ?>
<option value="<?= (int)$b['id'] ?>"><?= htmlspecialchars((string)($b['name'] ?? ('Client #' . (int)$b['id']))) ?><?= !empty($b['email']) ? (' • ' . htmlspecialchars((string)$b['email'])) : '' ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Sale Price (₦)</label>
<input type="number" id="transferSalePrice" name="sale_price" class="form-control" step="0.01" min="0" required>
<div class="form-text">Fee = tiered reallocation fee + 2.5% admin/legal fee (calculated from sale price).</div>
</div>
<div class="mb-2 fw-semibold">Required Documents</div>
<div class="row g-2">
<div class="col-md-12">
<div class="upload-card">
<div class="fw-semibold">Deed of Assignment</div>
<div class="small">PDF, JPG, PNG</div>
<input type="file" name="deed_of_assignment" class="form-control mt-2" accept=".pdf,.jpg,.jpeg,.png" required>
</div>
</div>
<div class="col-md-6">
<div class="upload-card">
<div class="fw-semibold">Seller ID</div>
<div class="small">PDF, JPG, PNG</div>
<input type="file" name="seller_id_doc" class="form-control mt-2" accept=".pdf,.jpg,.jpeg,.png" required>
</div>
</div>
<div class="col-md-6">
<div class="upload-card">
<div class="fw-semibold">Buyer ID</div>
<div class="small">PDF, JPG, PNG</div>
<input type="file" name="buyer_id_doc" class="form-control mt-2" accept=".pdf,.jpg,.jpeg,.png" required>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="mb-2 fw-semibold">Fee Breakdown</div>
<div class="transfer-kpi mb-2">
<div class="lbl">Reallocation Fee</div>
<div class="val" id="feeReallocation">₦0.00</div>
</div>
<div class="transfer-kpi mb-2">
<div class="lbl">Admin/Legal Fee (2.5%)</div>
<div class="val" id="feeAdmin">₦0.00</div>
</div>
<div class="transfer-kpi">
<div class="lbl">Total Transfer Fee</div>
<div class="val" id="feeTotal">₦0.00</div>
</div>
<div class="small text-muted mt-2">Payment will be created automatically after submission and must be approved before Admin can approve the transfer.</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-info text-white">Submit Transfer Request</button>
</div>
</form>
</div>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function(){
var priceEl = document.getElementById('transferSalePrice');
var outRealloc = document.getElementById('feeReallocation');
var outAdmin = document.getElementById('feeAdmin');
var outTotal = document.getElementById('feeTotal');
if (!priceEl || !outRealloc || !outAdmin || !outTotal) return;
function money(n){
var v = isFinite(n) ? Number(n) : 0;
return '₦' + v.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function calcReallocationFee(sale){
var sp = Math.max(0, Number(sale) || 0);
if (sp <= 5000000) return 300000;
if (sp <= 10000000) return 500000;
if (sp <= 20000000) return 750000;
return 1000000;
}
function update(){
var sp = Math.max(0, Number(priceEl.value) || 0);
var realloc = calcReallocationFee(sp);
var admin = Math.round(sp * 0.025 * 100) / 100;
var total = Math.round((realloc + admin) * 100) / 100;
outRealloc.textContent = money(realloc);
outAdmin.textContent = money(admin);
outTotal.textContent = money(total);
}
priceEl.addEventListener('input', update);
update();
});
</script>
<script>
async function changeAllocationStatus(id, status) {
const reason = prompt('Enter reason for this status change:');
if (reason === null) return;
if (!reason.trim()) return;
const btns = document.querySelectorAll('button, a.btn');
btns.forEach(b => b.disabled = true);
try {
const res = await fetch('ajax_update_allocation_status.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: Number(id), status: String(status), reason: String(reason) }),
credentials: 'same-origin'
});
const data = await res.json();
if (data && data.success) {
window.location.reload();
} else {
alert((data && data.message) ? data.message : 'Failed to update status');
}
} catch (e) {
alert('Network error while updating status');
} finally {
btns.forEach(b => b.disabled = false);
}
}
</script>
<?php include __DIR__ . '/includes/footer.php'; ?>