| Server IP : 72.60.21.38 / Your IP : 216.73.217.154 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
session_start();
require_once 'includes/db.php';
require_once 'includes/functions.php';
require_once 'includes/installments.php';
header('Content-Type: application/json');
if (!isset($_GET['id'])) {
echo json_encode(['error' => 'No ID provided']);
exit;
}
$alloc_id = $_GET['id'];
try {
// 1. Fetch Allocation Details
$hasProps = false;
$hasDeals = false;
try { $hasProps = $pdo->query("SHOW TABLES LIKE 'properties'")->rowCount() > 0; } catch (Throwable $e) { $hasProps = false; }
try { $hasDeals = $pdo->query("SHOW TABLES LIKE 'deals_submit'")->rowCount() > 0; } catch (Throwable $e) { $hasDeals = false; }
$areaCol = ($hasProps && tableHasColumn('properties', 'area_sqm')) ? "p.area_sqm" : "NULL";
$phoneCol = tableHasColumn('users', 'phone') ? "c.phone" : (tableHasColumn('users', 'mobile') ? "c.mobile" : (tableHasColumn('users', 'phone_number') ? "c.phone_number" : "NULL"));
$unitCol = ($hasProps && tableHasColumn('properties', 'unit_number')) ? "p.unit_number" : "NULL";
$plotCol = ($hasProps && tableHasColumn('properties', 'plot_number')) ? "p.plot_number" : "NULL";
$allocHouseTypeCol = tableHasColumn('allocations', 'house_type') ? "a.house_type" : "NULL";
$allocBuildingUseCol = tableHasColumn('allocations', 'building_use') ? "a.building_use" : "NULL";
$allocBuildingNumberCol = tableHasColumn('allocations', 'building_number') ? "a.building_number" : "NULL";
$pTitleCol = ($hasProps && tableHasColumn('properties', 'title')) ? "p.title" : "NULL";
$pAddrCol = "NULL";
if ($hasProps) {
if (tableHasColumn('properties', 'location')) { $pAddrCol = "p.location"; }
elseif (tableHasColumn('properties', 'address')) { $pAddrCol = "p.address"; }
elseif (tableHasColumn('properties', 'property_address')) { $pAddrCol = "p.property_address"; }
}
$pPriceCol = "0";
if ($hasProps) {
if (tableHasColumn('properties', 'price')) { $pPriceCol = "p.price"; }
elseif (tableHasColumn('properties', 'amount')) { $pPriceCol = "p.amount"; }
elseif (tableHasColumn('properties', 'total_price')) { $pPriceCol = "p.total_price"; }
}
$aPriceCol = "0";
if (tableHasColumn('allocations', 'total_price')) { $aPriceCol = "a.total_price"; }
elseif (tableHasColumn('allocations', 'price')) { $aPriceCol = "a.price"; }
elseif (tableHasColumn('allocations', 'amount')) { $aPriceCol = "a.amount"; }
elseif (tableHasColumn('allocations', 'amount_offered')) { $aPriceCol = "a.amount_offered"; }
elseif (tableHasColumn('allocations', 'offer_amount')) { $aPriceCol = "a.offer_amount"; }
$dTitleCol = $hasDeals && tableHasColumn('deals_submit', 'project_desc') ? "d.project_desc" : "NULL";
$dNameCol = $hasDeals && tableHasColumn('deals_submit', 'project_name') ? "d.project_name" : "NULL";
$dAmountCol = "0";
if ($hasDeals) {
if (tableHasColumn('deals_submit', 'amount_offered')) { $dAmountCol = "d.amount_offered"; }
elseif (tableHasColumn('deals_submit', 'offer_amount')) { $dAmountCol = "d.offer_amount"; }
elseif (tableHasColumn('deals_submit', 'price')) { $dAmountCol = "d.price"; }
elseif (tableHasColumn('deals_submit', 'amount')) { $dAmountCol = "d.amount"; }
}
$propJoin = $hasProps ? "LEFT JOIN properties p ON a.property_id = p.id" : "LEFT JOIN (SELECT NULL AS id) p ON 1=0";
$dealJoin = $hasDeals ? "LEFT JOIN deals_submit d ON a.deal_id = d.id" : "LEFT JOIN (SELECT NULL AS id) d ON 1=0";
$titleExpr = "COALESCE(NULLIF({$pTitleCol},''), NULLIF({$dTitleCol},''), NULLIF({$dNameCol},''), CONCAT('Allocation #', a.id))";
$addrExpr = "COALESCE(NULLIF({$pAddrCol},''), NULLIF({$dNameCol},''), NULLIF({$dTitleCol},''), '')";
$priceExpr = "COALESCE(NULLIF({$aPriceCol},0), NULLIF({$pPriceCol},0), NULLIF({$dAmountCol},0), 0)";
$sql = "
SELECT a.*,
{$titleExpr} as property_title,
{$addrExpr} as property_address,
{$priceExpr} as property_price,
" . (($hasProps && tableHasColumn('properties','type')) ? "p.type" : "NULL") . " as property_type,
$areaCol as area_sqm,
$unitCol as unit_number, $plotCol as plot_number,
$allocHouseTypeCol as alloc_house_type, $allocBuildingUseCol as alloc_building_use, $allocBuildingNumberCol as alloc_building_number,
c.name as client_name, c.email as client_email, $phoneCol as client_phone,
ag.name as agent_name
FROM allocations a
{$propJoin}
{$dealJoin}
LEFT JOIN users c ON a.user_id = c.id
LEFT JOIN users ag ON a.agent_id = ag.id
WHERE a.id = ?
";
$stmt = $pdo->prepare($sql);
$stmt->execute([$alloc_id]);
$allocation = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$allocation) {
echo json_encode(['error' => 'Allocation not found']);
exit;
}
$approvedAt = $allocation['approved_at'] ?? null;
$updatedAt = $allocation['updated_at'] ?? null;
if (!empty($approvedAt) && !empty($updatedAt)) {
$wasApproved = strtolower((string)($allocation['status'] ?? '')) === 'approved';
if ($wasApproved && strtotime($updatedAt) > strtotime($approvedAt)) {
try {
$st = $pdo->prepare("UPDATE allocations SET status = 'pending', signed = 0 WHERE id = ?");
$st->execute([$alloc_id]);
$allocation['status'] = 'pending';
$allocation['signed'] = 0;
try {
$pdo->query("DESCRIBE audit_logs");
$ins = $pdo->prepare("INSERT INTO audit_logs (action, details, ip_address, user_id, created_at) VALUES (?, ?, ?, ?, NOW())");
$ins->execute([
'allocation_reset_pending',
'Allocation edited after approval; reset to Pending. Allocation #'.$alloc_id,
$_SERVER['REMOTE_ADDR'] ?? '',
$_SESSION['user_id'] ?? null
]);
} catch (\Throwable $e) {}
} catch (\Throwable $e) {}
}
}
// Try to attach client photo from documents (passport/profile)
try {
$photoStmt = $pdo->prepare("SELECT file_path FROM documents WHERE user_id = ? AND type IN ('passport','photo','profile','profile_photo','id_photo') ORDER BY created_at DESC LIMIT 1");
$photoStmt->execute([$allocation['user_id']]);
$allocation['client_photo'] = $photoStmt->fetchColumn() ?: null;
$allocation['client_photo_b64'] = null;
if (!$allocation['client_photo']) {
$alt = getClientAvatarUrl($pdo, (int)$allocation['user_id']);
if ($alt) { $allocation['client_photo'] = $alt; }
}
if ($allocation['client_photo']) {
$p = $allocation['client_photo'];
if (!preg_match('#^https?://#i', $p)) {
$abs = __DIR__ . '/' . ltrim($p, '/');
if (is_file($abs) && filesize($abs) <= 5*1024*1024) {
$mime = 'image/jpeg';
if (function_exists('finfo_open')) {
$f = finfo_open(FILEINFO_MIME_TYPE);
$m = finfo_file($f, $abs);
finfo_close($f);
if ($m) $mime = $m;
}
$allocation['client_photo_b64'] = 'data:' . $mime . ';base64,' . base64_encode(file_get_contents($abs));
}
}
}
} catch (Exception $e) {
$allocation['client_photo'] = null;
$allocation['client_photo_b64'] = null;
}
// 2. Fetch Payments
$payOrder = "id DESC";
if (tableHasColumn('payments','date')) { $payOrder = "date DESC"; }
elseif (tableHasColumn('payments','created_at')) { $payOrder = "created_at DESC"; }
$stmt = $pdo->prepare("SELECT * FROM payments WHERE allocation_id = ? ORDER BY {$payOrder}");
$stmt->execute([$alloc_id]);
$payments = $stmt->fetchAll(PDO::FETCH_ASSOC);
$total_paid = 0;
foreach ($payments as $pay) {
$st = strtolower(trim((string)($pay['status'] ?? '')));
if (in_array($st, ['verified','approved','paid','completed','success'], true)) {
$amtRaw = $pay['amount'] ?? 0;
$amtStr = is_string($amtRaw) ? $amtRaw : (string)$amtRaw;
$amtStr = preg_replace('/[^\d.\-]/', '', $amtStr);
$total_paid += (float)($amtStr !== '' ? $amtStr : 0);
}
}
$allocationPriceRaw = $allocation['property_price'] ?? 0;
$allocationPriceStr = is_string($allocationPriceRaw) ? $allocationPriceRaw : (string)$allocationPriceRaw;
$allocationPriceStr = preg_replace('/[^\d.\-]/', '', $allocationPriceStr);
$allocationPrice = (float)($allocationPriceStr !== '' ? $allocationPriceStr : 0);
$allocation['property_price'] = $allocationPrice;
$outstanding = $allocationPrice - $total_paid;
$payment_status = ($outstanding <= 0) ? 'Paid' : (($total_paid > 0) ? 'Partially Paid' : 'Unpaid');
// Derived fields
$allocation['building_number'] = $allocation['alloc_building_number'] ?? $allocation['plot_number'] ?? $allocation['unit_number'] ?? null;
$allocation['house_type'] = $allocation['alloc_house_type'] ?? $allocation['property_type'] ?? null;
$allocation['space_size'] = $allocation['area_sqm'] ?? null;
$allocation['building_use'] = $allocation['alloc_building_use'] ?? null;
$allocation['location'] = $allocation['property_address'] ?? null;
$companyId = function_exists('getCurrentCompanyId') ? getCurrentCompanyId() : ($_SESSION['company_id'] ?? null);
if (function_exists('ensureAllocationBillingTable')) { ensureAllocationBillingTable($pdo); }
$defaultInfraPercent = (float)parseMoneyValue(getSetting('infra_lease_percent', '20'));
$defaultVatPercent = (float)parseMoneyValue(getSetting('vat_percent', '7.5'));
$defaultApplicationFee = (float)parseMoneyValue(getSetting('application_fee', '0'));
$billingRow = null;
try {
$stB = $pdo->prepare("SELECT * FROM allocation_billing WHERE allocation_id = ? LIMIT 1");
$stB->execute([(int)$alloc_id]);
$billingRow = $stB->fetch(PDO::FETCH_ASSOC) ?: null;
} catch (Throwable $e) { $billingRow = null; }
$billingSettings = [
'allocation_id' => (int)$alloc_id,
'land_cost' => $allocationPrice,
'infra_mode' => 'percent',
'infra_percent' => $defaultInfraPercent,
'infra_amount' => 0,
'excavation_fee' => 0,
'construction_supervision' => 0,
'approval_fee' => 0,
'application_form_fee' => $defaultApplicationFee,
'fence_gate_cost' => 0,
'carcass_cost' => 0,
'exterior_finishing_cost' => 0,
'dpc_cost' => 0,
'include_fence' => 0,
'include_carcass' => 0,
'include_exterior_finishing' => 0,
'include_dpc' => 0,
'vat_percent' => $defaultVatPercent,
'payment_plan_months' => 3,
];
if (is_array($billingRow)) {
foreach ($billingSettings as $k => $v) {
if (array_key_exists($k, $billingRow)) { $billingSettings[$k] = $billingRow[$k]; }
}
if (array_key_exists('infra_mode', $billingRow)) { $billingSettings['infra_mode'] = $billingRow['infra_mode']; }
if (array_key_exists('include_exterior_finishing', $billingRow)) { $billingSettings['include_exterior_finishing'] = $billingRow['include_exterior_finishing']; }
}
$computedBilling = function_exists('computeAllocationBilling') ? computeAllocationBilling($billingSettings) : [];
$grandTotal = (float)($computedBilling['grand_total'] ?? $allocationPrice);
$outstanding = $grandTotal - $total_paid;
$payment_status = ($outstanding <= 0) ? 'Paid' : (($total_paid > 0) ? 'Partially Paid' : 'Unpaid');
// 3. Fetch Documents (if any linked to user/property)
// Assuming simple fetch for now based on property or user context
$stmt = $pdo->prepare("SELECT * FROM documents WHERE property_id = ? OR (user_id = ? AND type = 'allocation_letter') ORDER BY created_at DESC");
$stmt->execute([$allocation['property_id'], $allocation['user_id']]);
$documents = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3b. Fetch Edit History from audit logs
$edit_history = [];
try {
$qh = $pdo->prepare("
SELECT a.entity_id, a.changed_by, a.created_at, a.reason, a.old_value, a.new_value, u.name AS editor_name
FROM audit_logs a
LEFT JOIN users u ON a.changed_by = u.id
WHERE a.entity_type = 'allocation_edit' AND a.entity_id = ?
ORDER BY a.created_at DESC
");
$qh->execute([$alloc_id]);
$rowsH = $qh->fetchAll(PDO::FETCH_ASSOC);
foreach ($rowsH as $h) {
$note = isset($h['reason']) ? (string)$h['reason'] : '';
$parsed = null;
if ($note !== '') {
try { $tmp = json_decode($note, true); if (is_array($tmp)) $parsed = $tmp; } catch (\Throwable $e) {}
}
$summary = $note;
$oldV = null; $newV = null;
if (!empty($h['old_value'])) { try { $oldV = json_decode($h['old_value'], true); } catch (\Throwable $e) {} }
if (!empty($h['new_value'])) { try { $newV = json_decode($h['new_value'], true); } catch (\Throwable $e) {} }
if (is_array($oldV) && is_array($newV)) {
$parts = [];
foreach (['plot_size','plot_number'] as $k) {
if (array_key_exists($k, $oldV) || array_key_exists($k, $newV)) {
$parts[] = $k . ': ' . (isset($oldV[$k]) && $oldV[$k] !== null && $oldV[$k] !== '' ? $oldV[$k] : '—') . ' → ' . (isset($newV[$k]) && $newV[$k] !== null && $newV[$k] !== '' ? $newV[$k] : '—');
}
}
if (count($parts) > 0) {
$summary = implode('; ', $parts);
if (is_array($parsed) && isset($parsed['note']) && $parsed['note'] !== '') { $summary .= ' — ' . $parsed['note']; }
}
} elseif (is_array($parsed)) {
$parts = [];
if (!empty($parsed['plot_size'])) $parts[] = 'plot_size: ' . $parsed['plot_size'];
if (!empty($parsed['plot_number'])) $parts[] = 'plot_number: ' . $parsed['plot_number'];
if (!empty($parsed['note'])) $parts[] = $parsed['note'];
if (count($parts) > 0) $summary = implode('; ', $parts);
}
$edit_history[] = [
'allocation_id' => (int)$h['entity_id'],
'edited_by' => (int)($h['changed_by'] ?? 0),
'edited_by_name' => $h['editor_name'] ?? null,
'edited_at' => $h['created_at'] ?? null,
'edit_note' => (is_array($parsed) && isset($parsed['note'])) ? $parsed['note'] : ($note !== '' ? $note : null),
'summary' => $summary
];
}
} catch (Exception $e) {
$edit_history = [];
}
$letterData = [];
try {
if (function_exists('ensureAllocationLetterDataTable')) { ensureAllocationLetterDataTable($pdo); }
if (function_exists('allocationLetterDataGet')) {
$letterData = allocationLetterDataGet($pdo, (int)$alloc_id);
}
} catch (Throwable $e) { $letterData = []; }
// 4. Construct Timeline (Allocation + Letter workflow)
$timeline = [];
$timeline[] = [
'step' => 'Reservation Created',
'date' => $allocation['created_at'],
'status' => 'done',
'role' => 'System'
];
if ($total_paid > 0) {
$timeline[] = [
'step' => 'Initial Payment Received',
'date' => $payments[count($payments)-1]['date'] ?? $allocation['created_at'], // First payment date
'status' => 'done',
'role' => 'Finance'
];
} else {
$timeline[] = [
'step' => 'Initial Payment',
'date' => null,
'status' => 'pending',
'role' => 'Client'
];
}
if ($allocation['status'] === 'approved' || $allocation['status'] === 'completed') {
$timeline[] = [
'step' => 'Manager Approval',
'date' => $allocation['updated_at'], // Approximation
'status' => 'done',
'role' => 'Manager'
];
} else {
$timeline[] = [
'step' => 'Manager Approval',
'date' => null,
'status' => ($allocation['status'] == 'rejected') ? 'rejected' : 'pending',
'role' => 'Manager'
];
}
if ($allocation['status'] === 'completed') {
$timeline[] = [
'step' => 'Allocation Finalized',
'date' => $allocation['allocated_at'] ?? $allocation['updated_at'],
'status' => 'done',
'role' => 'Admin'
];
}
if (!empty($letterData)) {
$letterStatus = strtolower((string)($letterData['status'] ?? ''));
$ldUpdated = (string)($letterData['updated_at'] ?? '');
if ($ldUpdated !== '') {
$timeline[] = [
'step' => 'Letter Draft Saved',
'date' => $ldUpdated,
'status' => in_array($letterStatus, ['draft','rejected','changes_requested','pending_chairman_approval','chairman_approved','sent_to_client','accepted_by_client'], true) ? 'done' : 'pending',
'role' => 'Admin'
];
}
if (!empty($letterData['sent_to_chairman_at'])) {
$timeline[] = [
'step' => 'Sent to Chairman Approval',
'date' => $letterData['sent_to_chairman_at'],
'status' => 'done',
'role' => 'Admin'
];
} elseif (in_array($letterStatus, ['pending_chairman_approval'], true)) {
$timeline[] = [
'step' => 'Sent to Chairman Approval',
'date' => null,
'status' => 'pending',
'role' => 'Admin'
];
}
if (!empty($letterData['chairman_decided_at'])) {
$decision = strtolower((string)($letterData['chairman_decision'] ?? ''));
$label = $decision === 'approve' ? 'Chairman Approved' : ($decision === 'request_changes' ? 'Chairman Requested Changes' : 'Chairman Rejected');
$tone = $decision === 'approve' ? 'done' : ($decision === 'request_changes' ? 'pending' : 'rejected');
$timeline[] = [
'step' => $label,
'date' => $letterData['chairman_decided_at'],
'status' => $tone,
'role' => 'Chairman'
];
}
if (!empty($letterData['sent_to_client_at'])) {
$timeline[] = [
'step' => 'Sent to Client Dashboard',
'date' => $letterData['sent_to_client_at'],
'status' => 'done',
'role' => 'Admin'
];
} elseif (in_array($letterStatus, ['sent_to_client'], true)) {
$timeline[] = [
'step' => 'Sent to Client Dashboard',
'date' => null,
'status' => 'pending',
'role' => 'Admin'
];
}
if (!empty($letterData['client_viewed_at'])) {
$timeline[] = [
'step' => 'Viewed by Client',
'date' => $letterData['client_viewed_at'],
'status' => 'done',
'role' => 'Client'
];
}
if (!empty($letterData['client_accepted_at']) || (int)($letterData['client_accepted'] ?? 0) === 1) {
$timeline[] = [
'step' => 'Accepted by Client',
'date' => $letterData['client_accepted_at'] ?? null,
'status' => 'done',
'role' => 'Client'
];
} else {
$timeline[] = [
'step' => 'Accepted by Client',
'date' => null,
'status' => 'pending',
'role' => 'Client'
];
}
}
$instSummary = getInstallmentStatusSummary($pdo, (int)$alloc_id);
if ($outstanding <= 0.00001 && $total_paid > 0) {
$instSummary = ['label' => 'Paid', 'class' => 'badge bg-success'];
}
echo json_encode([
'success' => true,
'allocation' => $allocation,
'payments' => $payments,
'installment_summary' => $instSummary,
'payment_summary' => [
'total_price' => (float)$grandTotal,
'total_paid' => (float)$total_paid,
'outstanding' => (float)$outstanding,
'status' => $payment_status
],
'billing_settings' => $billingSettings,
'cost_breakdown' => $computedBilling,
'letter_data' => $letterData,
'documents' => $documents,
'timeline' => $timeline,
'edit_history' => $edit_history
]);
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}