| Server IP : 72.60.21.38 / Your IP : 216.73.216.25 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
$GLOBALS['SKIP_GUARDS'] = true;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (isset($_POST['pm_action']) || isset($_POST['tx_action']))) {
$GLOBALS['JSON_MODE'] = true;
}
if (session_status() === PHP_SESSION_NONE) { session_start(); }
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/functions.php';
global $pdo;
if (function_exists('ensureFinancialControlsColumns')) { ensureFinancialControlsColumns(); }
$roleRaw = $_SESSION['user_role'] ?? 'guest';
$role = strtolower(str_replace([' ', '-'], '_', (string)$roleRaw));
$userId = (int)($_SESSION['user_id'] ?? 0);
$companyId = function_exists('getCurrentCompanyId') ? getCurrentCompanyId() : ($_SESSION['company_id'] ?? null);
$isAdminTier = in_array($role, ['admin','super_admin']);
$canVerify = in_array($role, ['finance','finance_officer','finance_manager','accountant','super_admin']);
$canApprovePayments = in_array($role, ['finance_manager','super_admin'], true);
$isAdminReadOnly = ($role === 'admin');
$applyCompanyFilter = $companyId && !$isAdminTier;
if ($isAdminReadOnly) {
if (!empty($GLOBALS['JSON_MODE'])) {
header('Content-Type: application/json; charset=UTF-8');
http_response_code(403);
echo json_encode(['ok' => false, 'message' => 'Access denied.'], JSON_UNESCAPED_SLASHES);
exit;
}
header('Location: approved-payments.php');
exit;
}
require_once __DIR__ . '/includes/header.php';
$queueStatuses = function_exists('kpiFinanceQueuePaymentStatuses')
? (array)kpiFinanceQueuePaymentStatuses()
: ['pending_verification','pending_confirmation','pending_gateway','payment_verification','awaiting_verification','pending','submitted'];
$queueStatusesSql = "('" . implode("','", $queueStatuses) . "')";
function colExists($table, $col) {
static $CACHE = [];
global $pdo;
$t = strtolower($table);
$c = strtolower($col);
if (isset($CACHE[$t])) {
return in_array($c, $CACHE[$t], true);
}
try {
$stmt = $pdo->query("SHOW COLUMNS FROM `$table`");
$cols = $stmt ? $stmt->fetchAll(PDO::FETCH_COLUMN) : [];
$cols = array_map('strtolower', $cols ?: []);
$CACHE[$t] = $cols;
return in_array($c, $cols, true);
} catch (Throwable $e) {
$CACHE[$t] = [];
return false;
}
}
$pdo->exec("
CREATE TABLE IF NOT EXISTS transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
property_id INT NULL,
company_id INT NULL,
transaction_type ENUM('form_fee','property_deposit','installment_payment','full_payment') NOT NULL,
reference VARCHAR(100),
amount DECIMAL(12,2),
payment_method VARCHAR(50) DEFAULT 'bank_transfer',
receipt_file VARCHAR(255),
status ENUM('pending_verification','approved','rejected') DEFAULT 'pending_verification',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
verified_at DATETIME NULL,
verified_by INT NULL
)
");
$repairCount = 0;
try {
$hasForms = $pdo->query("SHOW TABLES LIKE 'client_forms'")->rowCount() > 0;
$hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
if ($hasForms && $hasPayments) {
$formCols0 = ['id','client_id','company_id','amount_due','receipt_path'];
$tsCol0 = colExists('client_forms','updated_at') ? 'updated_at' : (colExists('client_forms','created_at') ? 'created_at' : null);
if ($tsCol0) { $formCols0[] = $tsCol0 . " AS form_ts"; }
$qf = "SELECT " . implode(',', $formCols0) . " FROM client_forms WHERE receipt_path IS NOT NULL AND TRIM(receipt_path) <> ''";
$sf = $pdo->query($qf);
$forms = $sf ? $sf->fetchAll(PDO::FETCH_ASSOC) : [];
foreach ($forms as $f) {
$rid = (int)($f['id'] ?? 0);
$cid = (int)($f['client_id'] ?? 0);
$cmp = $f['company_id'] ?? null;
$amt = (float)($f['amount_due'] ?? 0);
if ($amt <= 0) { $amt = 30000; }
$path = (string)($f['receipt_path'] ?? '');
if ($cid <= 0 || $path === '') { continue; }
$formTs = (string)($f['form_ts'] ?? '');
if ($formTs === '' || $formTs === '0000-00-00 00:00:00') { $formTs = date('Y-m-d H:i:s'); }
$receiptCol = null;
if (colExists('payments','receipt_file')) { $receiptCol = 'receipt_file'; }
elseif (colExists('payments','proof_file')) { $receiptCol = 'proof_file'; }
if ($receiptCol && $formTs !== '' && (colExists('payments','created_at') || colExists('payments','date')) && colExists('payments','reference')) {
$set = [];
$updParams = [];
if (colExists('payments','created_at')) { $set[] = "created_at = ?"; $updParams[] = $formTs; }
if (colExists('payments','date')) { $set[] = "date = ?"; $updParams[] = date('Y-m-d', strtotime($formTs)); }
if (!empty($set)) {
$updSql = "UPDATE payments SET " . implode(',', $set) . " WHERE user_id = ? AND {$receiptCol} = ? AND UPPER(reference) LIKE 'FORM_FEE%'";
$updParams[] = $cid;
$updParams[] = $path;
if (colExists('payments','created_at')) {
$updSql .= " AND (created_at IS NULL OR created_at = '' OR created_at > DATE_ADD(?, INTERVAL 6 HOUR) OR created_at < DATE_SUB(?, INTERVAL 6 HOUR))";
$updParams[] = $formTs;
$updParams[] = $formTs;
} elseif (colExists('payments','date')) {
$updSql .= " AND (date IS NULL OR date = '' OR date > DATE_ADD(DATE(?), INTERVAL 1 DAY) OR date < DATE_SUB(DATE(?), INTERVAL 1 DAY))";
$updParams[] = $formTs;
$updParams[] = $formTs;
}
try { $pdo->prepare($updSql)->execute($updParams); } catch (Throwable $eU0) {}
}
}
$existsSql = "SELECT COUNT(*) FROM payments WHERE user_id = ? AND status IN ('pending_verification','approved','rejected','duplicate')";
$ep = [$cid];
if ($receiptCol) { $existsSql .= " AND {$receiptCol} = ?"; $ep[] = $path; }
if (!$receiptCol && colExists('payments','reference')) { $existsSql .= " AND UPPER(reference) LIKE 'FORM_FEE%'"; }
$es = $pdo->prepare($existsSql); $es->execute($ep);
if ((int)$es->fetchColumn() === 0) {
$cols = ['user_id','amount','status'];
$vals = [$cid, $amt, 'pending_verification'];
$place = '?,?,?';
$refVal = 'FORM_FEE-' . $rid;
if (colExists('payments','payment_method')) { $cols[]='payment_method'; $vals[]='bank_transfer'; $place .= ',?'; }
elseif (colExists('payments','method')) { $cols[]='method'; $vals[]='bank_transfer'; $place .= ',?'; }
if (colExists('payments','reference')) { $cols[]='reference'; $vals[]=$refVal; $place .= ',?'; }
if (colExists('payments','receipt_file')) { $cols[]='receipt_file'; $vals[]=$path; $place .= ',?'; }
elseif (colExists('payments','proof_file')) { $cols[]='proof_file'; $vals[]=$path; $place .= ',?'; }
if (colExists('payments','company_id') && $cmp) { $cols[]='company_id'; $vals[]=$cmp; $place .= ',?'; }
if (colExists('payments','created_at')) { $cols[]='created_at'; $vals[]=$formTs; $place .= ',?'; }
if (colExists('payments','date')) { $cols[]='date'; $vals[]=date('Y-m-d', strtotime($formTs)); $place .= ',?'; }
$ins = $pdo->prepare("INSERT INTO payments (".implode(',', $cols).") VALUES ($place)");
$ins->execute($vals);
$repairCount++;
}
}
}
} catch (Throwable $e) {}
$syncCount = 0;
try {
$hasTx = $pdo->query("SHOW TABLES LIKE 'transactions'")->rowCount() > 0;
$hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
if ($hasTx && $hasPayments) {
$colReceipt = 'receipt_file';
if (!colExists('payments','receipt_file') && colExists('payments','proof_file')) { $colReceipt = 'proof_file'; }
$dateCol = colExists('payments','created_at') ? 'created_at' : (colExists('payments','date') ? 'date' : null);
$sql = "SELECT id, user_id, amount, reference, $colReceipt AS receipt_file, status" . ($dateCol ? ", $dateCol AS created_at" : "") . " FROM payments WHERE status = 'pending_verification'";
$st = $pdo->query($sql);
$rowsP = $st ? $st->fetchAll(PDO::FETCH_ASSOC) : [];
foreach ($rowsP as $p) {
$uid = (int)($p['user_id'] ?? 0);
if ($uid <= 0) { continue; }
$rt = strtolower((string)($p['reference'] ?? ''));
$type = (strpos($rt, 'form_fee') === 0) ? 'form_fee' : 'property_deposit';
$dup = $pdo->prepare("SELECT id FROM transactions WHERE user_id = ? AND transaction_type = ? AND status = 'pending_verification' LIMIT 1");
$dup->execute([$uid, $type]);
if ((int)($dup->fetchColumn() ?: 0) === 0) {
$ins = $pdo->prepare("INSERT INTO transactions (user_id, amount, transaction_type, receipt_file, status, created_at) VALUES (?, ?, ?, ?, 'pending_verification', NOW())");
$ins->execute([$uid, (float)($p['amount'] ?? 0), $type, (string)($p['receipt_file'] ?? '')]);
$syncCount++;
}
}
}
} catch (Throwable $e) {}
if (!empty($_GET['backfill_core'])) {
try {
$refCol = colExists('payments','reference') ? 'reference' : (colExists('payments','ref') ? 'ref' : null);
$rcpCol = colExists('payments','receipt_file') ? 'receipt_file' : (colExists('payments','proof_file') ? 'proof_file' : null);
$dateCol = colExists('payments','created_at') ? 'created_at' : (colExists('payments','date') ? 'date' : (colExists('payments','updated_at') ? 'updated_at' : null));
$sql = "SELECT id, user_id" . ($refCol ? ", {$refCol} AS reference" : ", NULL AS reference") . ($rcpCol ? ", {$rcpCol} AS receipt_file" : ", NULL AS receipt_file") . ", " . ($dateCol ? "{$dateCol} AS created_at" : "NULL AS created_at") . (colExists('payments','deal_id') ? ", deal_id" : ", NULL AS deal_id") . " FROM payments WHERE " . ($refCol ? "{$refCol}" : "''") . " LIKE 'deal-submission-%' ORDER BY " . ($dateCol ?: 'id') . " DESC LIMIT 300";
$st = $pdo->prepare($sql); $st->execute(); $prs = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
$updDeals = 0; $linkCnt = 0; $scan = 0;
foreach ($prs as $p) {
$scan++;
$pid = (int)($p['id'] ?? 0);
$dealId = (int)($p['deal_id'] ?? 0);
$created = (string)($p['created_at'] ?? '');
$receipt = (string)($p['receipt_file'] ?? '');
$uid = (int)($p['user_id'] ?? 0);
if ($dealId <= 0) {
$guess = 0;
if ($receipt !== '') {
if (colExists('deals','receipt_file')) {
$q = $pdo->prepare("SELECT id FROM deals WHERE receipt_file = ? ORDER BY id DESC LIMIT 1"); $q->execute([$receipt]); $guess = (int)($q->fetchColumn() ?: 0);
if ($guess <= 0) { $b = basename($receipt); if ($b !== '') { $q = $pdo->prepare("SELECT id FROM deals WHERE receipt_file LIKE ? ORDER BY id DESC LIMIT 1"); $q->execute(['%'.$b]); $guess = (int)($q->fetchColumn() ?: 0); } }
} elseif (colExists('deals','proof_file')) {
$q = $pdo->prepare("SELECT id FROM deals WHERE proof_file = ? ORDER BY id DESC LIMIT 1"); $q->execute([$receipt]); $guess = (int)($q->fetchColumn() ?: 0);
if ($guess <= 0) { $b = basename($receipt); if ($b !== '') { $q = $pdo->prepare("SELECT id FROM deals WHERE proof_file LIKE ? ORDER BY id DESC LIMIT 1"); $q->execute(['%'.$b]); $guess = (int)($q->fetchColumn() ?: 0); } }
}
}
if ($guess <= 0 && $created !== '' && colExists('deals','created_at')) {
if ($uid > 0 && colExists('deals','user_id')) {
$q = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1"); $q->execute([$uid, $created, $created]); $guess = (int)($q->fetchColumn() ?: 0);
} else {
$q = $pdo->prepare("SELECT id FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1"); $q->execute([$created, $created]); $guess = (int)($q->fetchColumn() ?: 0);
}
}
if ($guess > 0) {
$dealId = $guess;
$up = $pdo->prepare("UPDATE payments SET deal_id = ? WHERE id = ?"); $up->execute([$dealId, $pid]);
try { @file_put_contents(__DIR__ . '/debug_sql.txt', "PAYMENT_LINK:\nUPDATE payments SET deal_id = {$dealId} WHERE id = {$pid}\n", FILE_APPEND); } catch (Throwable $ePL) {}
$linkCnt++;
}
}
if ($dealId > 0) {
$dr = [];
try { $qd = $pdo->prepare("SELECT * FROM deals WHERE id = ? LIMIT 1"); $qd->execute([$dealId]); $dr = $qd->fetch(PDO::FETCH_ASSOC) ?: []; } catch (Throwable $eD) {}
$meta = [];
if (!empty($dr) && isset($dr['meta_json']) && is_string($dr['meta_json']) && $dr['meta_json'] !== '') {
$m = json_decode($dr['meta_json'], true); if (is_array($m)) { $meta = $m; }
} else {
if (colExists('payments','meta_json')) {
$qm = $pdo->prepare("SELECT meta_json FROM payments WHERE id = ? LIMIT 1"); $qm->execute([$pid]); $mj = (string)($qm->fetchColumn() ?: '');
if ($mj !== '') { $m = json_decode($mj, true); if (is_array($m)) { $meta = $m; } }
}
}
$get = function($arr, $keys) {
foreach ($keys as $k) { if (isset($arr[$k]) && $arr[$k] !== '' && $arr[$k] !== null) { return $arr[$k]; } }
return null;
};
$upd = [];
if (colExists('deals','project_name')) { $v = $get($meta, ['project_desc','project_name','property','estate','project']); if (!$v && isset($dr['project_desc'])) { $v = $dr['project_desc']; } if ($v !== null && trim((string)($dr['project_name'] ?? '')) === '') { $upd['project_name'] = (string)$v; } }
if (colExists('deals','project_desc')) { $v = $get($meta, ['project_desc','property','estate','project']); if ($v !== null && trim((string)($dr['project_desc'] ?? '')) === '') { $upd['project_desc'] = (string)$v; } }
if (colExists('deals','deal_source')) { $v = $get($meta, ['deal_source','source','deal_source_type','lead_source']); if ($v !== null && trim((string)($dr['deal_source'] ?? '')) === '') { $upd['deal_source'] = (string)$v; } }
if (colExists('deals','payment_plan')) { $v = $get($meta, ['plan_type','payment_plan','plan','plan_name']); if ($v !== null && trim((string)($dr['payment_plan'] ?? '')) === '') { $upd['payment_plan'] = (string)$v; } }
if (colExists('deals','marketer_name')) { $v = $get($meta, ['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','marketer','sales_agent','sales_rep','contact_rep','agent_name']); if ($v !== null && trim((string)($dr['marketer_name'] ?? '')) === '') { $upd['marketer_name'] = (string)$v; } }
if (colExists('deals','amount_offered')) { $v = $get($meta, ['amount_offered']); if ($v !== null && (float)($dr['amount_offered'] ?? 0) == 0.0) { $upd['amount_offered'] = (float)$v; } }
if (colExists('deals','amount_paid_so_far')) { $v = $get($meta, ['amount_paid_so_far']); if ($v !== null && (float)($dr['amount_paid_so_far'] ?? 0) == 0.0) { $upd['amount_paid_so_far'] = (float)$v; } }
if (colExists('deals','commission_percent')) { $v = $get($meta, ['commission_pct','commission_percent']); if ($v !== null && (float)($dr['commission_percent'] ?? 0) == 0.0) { $upd['commission_percent'] = (float)$v; } }
if (colExists('deals','discount_amount')) { $v = $get($meta, ['discount_amount']); if ($v !== null && (float)($dr['discount_amount'] ?? 0) == 0.0) { $upd['discount_amount'] = (float)$v; } }
if (colExists('deals','balance_remaining')) { $v = $get($meta, ['balance_remaining']); if ($v !== null && (float)($dr['balance_remaining'] ?? 0) == 0.0) { $upd['balance_remaining'] = (float)$v; } }
if (colExists('deals','notes')) { $v = $get($meta, ['client_payment_status','notes']); if ($v !== null && trim((string)($dr['notes'] ?? '')) === '') { $upd['notes'] = (string)$v; } }
if (!empty($upd)) {
$sets = []; $vals = [];
foreach ($upd as $k=>$v) { $sets[] = "{$k} = ?"; $vals[] = $v; }
$vals[] = $dealId;
$u = $pdo->prepare("UPDATE deals SET ".implode(',', $sets)." WHERE id = ?");
$u->execute($vals);
try { @file_put_contents(__DIR__ . '/debug_sql.txt', "REPAIR_UPDATE_DEAL:\nUPDATE deals SET ".implode(',', $sets)." WHERE id = {$dealId}\nPARAMS:\n".print_r($vals, true)."\n", FILE_APPEND); } catch (Throwable $eRU) {}
$updDeals++;
}
}
}
echo "<pre>backfill_core\nscanned: {$scan}\nlinked_payments: {$linkCnt}\nupdated_deals: {$updDeals}\n</pre>";
exit;
} catch (Throwable $e) {
echo "<pre>backfill_core_failed\n".(string)$e->getMessage()."</pre>";
exit;
}
}
// Ensure any client_forms receipts are present in transactions (form_fee)
try {
$hasForms = $pdo->query("SHOW TABLES LIKE 'client_forms'")->rowCount() > 0;
$hasTx = $pdo->query("SHOW TABLES LIKE 'transactions'")->rowCount() > 0;
$hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
if ($hasForms && ($hasTx || $hasPayments)) {
$formCols = ['client_id','receipt_path'];
if (colExists('client_forms','amount_due')) { $formCols[] = 'amount_due'; }
if (colExists('client_forms','company_id')) { $formCols[] = 'company_id'; }
$tsCol = colExists('client_forms','updated_at') ? 'updated_at' : (colExists('client_forms','created_at') ? 'created_at' : null);
if ($tsCol) { $formCols[] = $tsCol . ' AS form_ts'; }
$qf = "SELECT " . implode(',', $formCols) . " FROM client_forms WHERE receipt_path IS NOT NULL AND TRIM(receipt_path) <> '' AND status IN ('payment_verification','pending_verification')";
$stf = $pdo->query($qf);
$formsRows = $stf ? $stf->fetchAll(PDO::FETCH_ASSOC) : [];
foreach ($formsRows as $fr) {
$cid = (int)($fr['client_id'] ?? 0);
$rpath = (string)($fr['receipt_path'] ?? '');
if ($cid <= 0 || $rpath === '') continue;
$amtForm = (float)($fr['amount_due'] ?? 30000);
if ($hasTx) {
$dup = $pdo->prepare("SELECT id FROM transactions WHERE user_id = ? AND transaction_type = 'form_fee' AND receipt_file = ? AND status IN ('pending_verification','approved') LIMIT 1");
$dup->execute([$cid, $rpath]);
if ((int)($dup->fetchColumn() ?: 0) === 0) {
$ins = $pdo->prepare("INSERT INTO transactions (user_id, amount, transaction_type, receipt_file, status, created_at) VALUES (?, ?, 'form_fee', ?, 'pending_verification', NOW())");
$ins->execute([$cid, $amtForm, $rpath]);
}
}
if ($hasPayments) {
$payReceiptCol = colExists('payments','receipt_file') ? 'receipt_file' : (colExists('payments','proof_file') ? 'proof_file' : null);
$existsStatusesSql = "('" . implode("','", array_merge($queueStatuses, ['approved','rejected','duplicate'])) . "')";
$existsSql = "SELECT COUNT(*) FROM payments WHERE user_id = ?";
$existsParams = [$cid];
if ($payReceiptCol) { $existsSql .= " AND {$payReceiptCol} = ?"; $existsParams[] = $rpath; }
if (colExists('payments','reference')) { $existsSql .= " AND UPPER(reference) LIKE 'FORM_FEE%'"; }
$existsSql .= " AND status IN " . $existsStatusesSql;
$stx = $pdo->prepare($existsSql);
$stx->execute($existsParams);
if ((int)($stx->fetchColumn() ?: 0) === 0) {
$cols = ['user_id','amount','status'];
$vals = [$cid, $amtForm, 'pending_verification'];
if (colExists('payments','payment_method')) { $cols[]='payment_method'; $vals[]='bank_transfer'; }
elseif (colExists('payments','method')) { $cols[]='method'; $vals[]='bank_transfer'; }
if (colExists('payments','reference')) { $cols[]='reference'; $vals[]='FORM_FEE-' . $cid . '-' . (string)($fr['form_ts'] ?? date('YmdHis')); }
if ($payReceiptCol) { $cols[]=$payReceiptCol; $vals[]=$rpath; }
$cmp = $fr['company_id'] ?? null;
if (colExists('payments','company_id') && $cmp) { $cols[]='company_id'; $vals[]=$cmp; }
$ts = (string)($fr['form_ts'] ?? '');
if ($ts !== '' && colExists('payments','created_at')) { $cols[]='created_at'; $vals[]=$ts; }
if (colExists('payments','date')) {
$d = $ts !== '' ? date('Y-m-d', strtotime($ts)) : date('Y-m-d');
if (!in_array('date', $cols, true)) { $cols[]='date'; $vals[]=$d; }
}
$ins = $pdo->prepare("INSERT INTO payments (".implode(',', $cols).") VALUES (".implode(',', array_fill(0,count($cols),'?')).")");
$ins->execute($vals);
}
}
}
}
} catch (Throwable $e) {}
// Migrate pending transactions into payments (one-time backfill)
try {
$hasTx = $pdo->query("SHOW TABLES LIKE 'transactions'")->rowCount() > 0;
$hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
if ($hasTx && $hasPayments) {
$qtx = $pdo->query("SELECT id, user_id, amount, transaction_type, receipt_file, status, created_at, company_id FROM transactions WHERE status IN ('pending_verification','pending_confirmation') ORDER BY created_at DESC");
$rowsTx = $qtx ? $qtx->fetchAll(PDO::FETCH_ASSOC) : [];
foreach ($rowsTx as $t) {
$uid = (int)($t['user_id'] ?? 0);
$amt = (float)($t['amount'] ?? 0);
$rcp = (string)($t['receipt_file'] ?? '');
$stat = (string)($t['status'] ?? 'pending_verification');
$cmp = $t['company_id'] ?? null;
if ($uid <= 0) continue;
$tt = strtolower((string)($t['transaction_type'] ?? ''));
$txId = (int)($t['id'] ?? 0);
$ref = ($tt !== '' ? strtoupper($tt) : 'PAYMENT') . '-TX-' . (string)$txId;
if ($tt === 'form_fee') { $ref = 'FORM_FEE-TX-' . (string)$txId; }
$final = function_exists('kpiPaymentFinalizedStatuses') ? array_map('strtolower', (array)kpiPaymentFinalizedStatuses()) : ['verified','approved','paid','completed','success'];
$existsStatuses = array_values(array_unique(array_filter(array_merge($queueStatuses, $final, ['rejected','duplicate']))));
$existsSql = "SELECT COUNT(*) FROM payments WHERE user_id = ? AND LOWER(TRIM(status)) IN (" . implode(',', array_fill(0, count($existsStatuses), '?')) . ")";
$params = [$uid];
foreach ($existsStatuses as $s0) { $params[] = strtolower(trim((string)$s0)); }
if (colExists('payments','receipt_file') && $rcp !== '') { $existsSql .= " AND receipt_file = ?"; $params[] = $rcp; }
elseif (colExists('payments','proof_file') && $rcp !== '') { $existsSql .= " AND proof_file = ?"; $params[] = $rcp; }
$stx = $pdo->prepare($existsSql); $stx->execute($params);
if ((int)($stx->fetchColumn() ?: 0) === 0) {
$cols = ['user_id','amount','status'];
$vals = [$uid, $amt, ($stat === 'pending_confirmation' ? 'pending_confirmation' : 'pending_verification')];
if (colExists('payments','payment_method')) { $cols[]='payment_method'; $vals[]='bank_transfer'; }
elseif (colExists('payments','method')) { $cols[]='method'; $vals[]='bank_transfer'; }
if (colExists('payments','reference')) { $cols[]='reference'; $vals[]=$ref; }
if (colExists('payments','receipt_file') && $rcp !== '') { $cols[]='receipt_file'; $vals[]=$rcp; }
elseif (colExists('payments','proof_file') && $rcp !== '') { $cols[]='proof_file'; $vals[]=$rcp; }
if (colExists('payments','company_id') && $cmp) { $cols[]='company_id'; $vals[]=$cmp; }
$txTs = !empty($t['created_at']) ? (string)$t['created_at'] : '';
if (colExists('payments','created_at') && $txTs !== '') { $cols[]='created_at'; $vals[]=$txTs; }
if (colExists('payments','date')) {
$d = $txTs !== '' ? date('Y-m-d', strtotime($txTs)) : date('Y-m-d');
if (!in_array('date', $cols, true)) { $cols[]='date'; $vals[]=$d; }
}
$ins = $pdo->prepare("INSERT INTO payments (".implode(',', $cols).") VALUES (".implode(',', array_fill(0,count($cols),'?')).")");
$ins->execute($vals);
}
}
}
} catch (Throwable $e) {}
$action_msg = '';
if ($canVerify && $_SERVER['REQUEST_METHOD'] !== 'POST') {
try {
$stmt = $pdo->prepare("
SELECT
p.id AS payment_id,
p.user_id,
p.deal_id,
p.client_name,
p.client_email,
p.amount,
p.status,
p.reference,
p.created_at,
p.meta_json AS payment_meta,
d.id AS deal_id,
d.project_name,
d.project_desc,
d.deal_source,
d.payment_plan,
d.marketer_name,
d.amount_offered,
d.amount_paid_so_far,
d.discount_amount,
d.commission_percent,
d.balance_remaining,
d.notes,
d.meta_json AS deal_meta,
ds.submitted_by AS ds_submitted_by,
ds.marketer_name AS ds_marketer_name,
ds.notes AS ds_notes,
ds.marketer_bank AS ds_marketer_bank,
ds.agent_bank AS ds_agent_bank,
ds.meta_json AS ds_meta
FROM payments p
LEFT JOIN deals d ON p.deal_id = d.id
LEFT JOIN deals_submit ds ON " . (colExists('payments','deal_id') ? "p.deal_id = ds.id" : "p.user_id = ds.user_id") . "
ORDER BY p.id DESC
");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
try { @file_put_contents(__DIR__ . '/debug_finance.txt', "FINANCE_ROWS_JOIN:\n".print_r($rows, true)."\n", FILE_APPEND); } catch (Throwable $eDbgF3) {}
if (!empty($_GET['break_finance'])) { echo "<pre>"; print_r($rows); echo "</pre>"; exit; }
foreach ($rows as $row) {
if (!empty($_GET['debug_finance'])) {
echo "<pre style=\"white-space:pre-wrap; background:#fee; border:1px solid #f99; padding:8px;\">";
echo "payment_row:\n"; print_r($row);
echo "</pre>";
}
// Decode meta fields
$dealMeta = !empty($row['deal_meta']) ? (json_decode($row['deal_meta'], true) ?: []) : [];
$paymentMeta = !empty($row['payment_meta']) ? (json_decode($row['payment_meta'], true) ?: []) : [];
$dsMeta = !empty($row['ds_meta']) ? (json_decode($row['ds_meta'], true) ?: []) : [];
// Merge for downstream helpers (priority: payment > deal_submit > deal)
$meta = array_merge(is_array($dealMeta)?$dealMeta:[], is_array($dsMeta)?$dsMeta:[], is_array($paymentMeta)?$paymentMeta:[]);
// Inject explicit fields from deals_submit when present
if (!empty($row['ds_marketer_name']) && empty($meta['marketer_name'])) { $meta['marketer_name'] = (string)$row['ds_marketer_name']; }
if (!empty($row['ds_submitted_by']) && empty($meta['submitted_by_name'])) { $meta['submitted_by_name'] = (string)$row['ds_submitted_by']; }
if (!empty($row['ds_notes']) && empty($meta['notes'])) { $meta['notes'] = (string)$row['ds_notes']; }
// Inject bank details from deals_submit columns if not present in meta
try {
if (empty($meta['marketer_bank']) && !empty($row['ds_marketer_bank'])) {
$mb = $row['ds_marketer_bank'];
if (is_string($mb)) { $mb = json_decode($mb, true) ?: []; }
if (is_array($mb) && (!empty($mb['acc_no']) || !empty($mb['bank']) || !empty($mb['acc_name']))) {
$meta['marketer_bank'] = [
'acc_no' => trim((string)($mb['acc_no'] ?? '')),
'bank' => trim((string)($mb['bank'] ?? ($mb['bank_name'] ?? ($mb['bankName'] ?? '')))),
'acc_name' => trim((string)($mb['acc_name'] ?? ($mb['account_name'] ?? ($mb['accountName'] ?? ''))))
];
}
}
if (empty($meta['agent_bank']) && !empty($row['ds_agent_bank'])) {
$ab = $row['ds_agent_bank'];
if (is_string($ab)) { $ab = json_decode($ab, true) ?: []; }
if (is_array($ab) && (!empty($ab['acc_no']) || !empty($ab['bank']) || !empty($ab['acc_name']))) {
$meta['agent_bank'] = [
'acc_no' => trim((string)($ab['acc_no'] ?? '')),
'bank' => trim((string)($ab['bank'] ?? ($ab['bank_name'] ?? ($ab['bankName'] ?? '')))),
'acc_name' => trim((string)($ab['acc_name'] ?? ($ab['account_name'] ?? ($ab['accountName'] ?? ''))))
];
}
}
} catch (Throwable $eDSBanks) {}
if (empty($meta) || !is_array($meta)) {
try {
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals_submit','user_id') && colExists('deals_submit','meta_json')) {
$qs = $pdo->prepare("SELECT meta_json, submitted_by, marketer_name, notes FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qs->execute([$uid]);
$drs = $qs->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($drs)) {
$dmj = (string)($drs['meta_json'] ?? '');
if ($dmj !== '') {
$m = json_decode($dmj, true) ?: [];
if (is_array($m)) {
if (!empty($drs['submitted_by']) && empty($m['submitted_by_name'])) { $m['submitted_by_name'] = (string)$drs['submitted_by']; }
if (!empty($drs['marketer_name']) && empty($m['marketer_name'])) { $m['marketer_name'] = (string)$drs['marketer_name']; }
if (!empty($drs['notes']) && empty($m['notes'])) { $m['notes'] = (string)$drs['notes']; }
$meta = $m;
}
}
}
}
} catch (Throwable $eDs1) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$rcp = (string)($row['receipt_file'] ?? '');
if ($rcp !== '' && colExists('deals','meta_json')) {
if (colExists('deals','receipt_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = (string)($qs->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file LIKE ? LIMIT 1"); $qs->execute(['%'.$base]);
$dmj = (string)($qs->fetchColumn() ?: ''); if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
} elseif (colExists('deals','proof_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = (string)($qs->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file LIKE ? LIMIT 1"); $qs->execute(['%'.$base]);
$dmj = (string)($qs->fetchColumn() ?: ''); if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
}
}
} catch (Throwable $e2) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid]); $dmj = (string)($qd->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
} catch (Throwable $e3) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$ref = (string)($row['reference'] ?? '');
if ($ref !== '' && colExists('deals','created_at') && colExists('deals','meta_json')) {
$ts = null; if (preg_match('/(\\d{14})/', $ref, $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$win = $ts->format('Y-m-d H:i:s');
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id')) {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$uid, $win, $win]);
} else {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$win, $win]);
}
$dmj = (string)($qrf->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
} catch (Throwable $e4) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$ref = (string)($row['reference'] ?? '');
if ($ref !== '' && colExists('deals_submit','created_at') && colExists('deals_submit','meta_json')) {
$ts = null; if (preg_match('/(\\d{14})/', $ref, $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$win = $ts->format('Y-m-d H:i:s');
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals_submit','user_id')) {
$qrf = $pdo->prepare("SELECT meta_json, submitted_by, marketer_name, notes FROM deals_submit WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$uid, $win, $win]);
} else {
$qrf = $pdo->prepare("SELECT meta_json, submitted_by, marketer_name, notes FROM deals_submit WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$win, $win]);
}
$drs = $qrf->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($drs)) {
$dmj = (string)($drs['meta_json'] ?? '');
if ($dmj !== '') {
$m = json_decode($dmj, true) ?: [];
if (is_array($m)) {
if (!empty($drs['submitted_by']) && empty($m['submitted_by_name'])) { $m['submitted_by_name'] = (string)$drs['submitted_by']; }
if (!empty($drs['marketer_name']) && empty($m['marketer_name'])) { $m['marketer_name'] = (string)$drs['marketer_name']; }
if (!empty($drs['notes']) && empty($m['notes'])) { $m['notes'] = (string)$drs['notes']; }
$meta = $m;
}
}
}
}
}
} catch (Throwable $eDs2) {}
}
try {
$cfuid = (int)($row['user_id'] ?? 0);
$cfjson = '';
if ($cfuid > 0 && colExists('client_forms','form_data')) {
$qcf = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY created_at DESC LIMIT 1");
$qcf->execute([$cfuid]);
$cfjson = (string)($qcf->fetchColumn() ?: '');
}
if ($cfjson === '' && colExists('client_forms','form_data') && colExists('users','email')) {
$emailProbe = '';
try {
$emailProbe = isset($meta['client_email']) ? (string)$meta['client_email'] : '';
if ($emailProbe === '' && $cfuid > 0) {
$qe = $pdo->prepare("SELECT email FROM users WHERE id = ? LIMIT 1");
$qe->execute([$cfuid]);
$emailProbe = (string)($qe->fetchColumn() ?: '');
}
} catch (Throwable $eE0) {}
if ($emailProbe !== '') {
$qcf = $pdo->prepare("SELECT cf.form_data FROM client_forms cf LEFT JOIN users u ON cf.client_id = u.id WHERE u.email = ? ORDER BY cf.created_at DESC LIMIT 1");
$qcf->execute([$emailProbe]);
$cfjson = (string)($qcf->fetchColumn() ?: '');
}
}
if ($cfjson === '' && colExists('client_forms','form_data') && colExists('users','name')) {
$nameProbe = '';
$nameProbe = isset($meta['client_name']) ? (string)$meta['client_name'] : ((string)($row['client_name'] ?? ''));
if ($nameProbe !== '') {
$qcf = $pdo->prepare("SELECT TOP 1 cf.form_data FROM client_forms cf LEFT JOIN users u ON cf.client_id = u.id WHERE u.name LIKE ? ORDER BY cf.created_at DESC");
// Fallback for MySQL where TOP is invalid; prefer LIMIT if TOP fails in runtime. This is safe to parse-only here.
try {
$qcf->execute(['%'.$nameProbe.'%']);
$tmp = $qcf->fetchColumn();
if ($tmp !== false) { $cfjson = (string)$tmp; }
} catch (Throwable $eTop) {
$qcf = $pdo->prepare("SELECT cf.form_data FROM client_forms cf LEFT JOIN users u ON cf.client_id = u.id WHERE u.name LIKE ? ORDER BY cf.created_at DESC LIMIT 1");
$qcf->execute(['%'.$nameProbe.'%']);
$cfjson = (string)($qcf->fetchColumn() ?: '');
}
}
}
if ($cfjson !== '') {
if ($cfjson !== '') {
$cf = json_decode($cfjson, true) ?: [];
$cfProj = '';
foreach (['project_desc','Project / Property Description','Project / Property','Property / Estate','Property/Estate','Estate / Property','project_or_property','preferred_property','preferred property','estate_name','property_estate','estate_property','property','estate','project_property','project','property_title','property_name','project_title','Project Name'] as $k) {
$v = isset($cf[$k]) ? trim((string)$cf[$k]) : '';
if ($v !== '') { $cfProj = $v; break; }
}
$cfSource = '';
foreach (['deal_source','Deal Source','source','deal_source_type','lead_source','referral_source','referral source','referrer','referral','ref','submitted_by_role','Submitted By Role','department','team','channel','source_of_deal','source of deal','how did you hear about us','how you heard about us','where did you hear about us','where heard'] as $k) {
$v = isset($cf[$k]) ? trim((string)$cf[$k]) : '';
if ($v !== '') { $cfSource = $v; break; }
}
$cfPlan = '';
foreach (['plan_type','Payment Plan','plan','payment_plan','plan_name','installment_plan','installment','payment_mode','payment mode'] as $k) {
$v = isset($cf[$k]) ? trim((string)$cf[$k]) : '';
if ($v !== '') { $cfPlan = $v; break; }
}
$cfMarketer = '';
foreach (['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','Marketer’s Name','Marketer\'s Name','marketer','sales_agent','sales_rep','contact_rep','uploaded_by_name','submitted_by_name','uploaded_by','created_by_name','agent_name','marketer_full_name','marketer full name'] as $k) {
$v = isset($cf[$k]) ? trim((string)$cf[$k]) : '';
if ($v !== '') { $cfMarketer = $v; break; }
}
if ($cfMarketer === '') {
foreach ($cf as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false || strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false) { $cfMarketer = trim((string)$vv); break; }
}
}
if (!isset($meta['project_desc']) && $cfProj !== '') { $meta['project_desc'] = $cfProj; }
if (!isset($meta['property']) && isset($cf['property'])) { $meta['property'] = $cf['property']; }
if (!isset($meta['deal_source']) && $cfSource !== '') { $meta['deal_source'] = $cfSource; }
if (!isset($meta['plan_type']) && $cfPlan !== '') { $meta['plan_type'] = $cfPlan; }
if (!isset($meta['marketer_name']) && $cfMarketer !== '') { $meta['marketer_name'] = $cfMarketer; }
if (!isset($meta['client_payment_status'])) {
if (isset($cf['client_payment_status']) && trim((string)$cf['client_payment_status']) !== '') { $meta['client_payment_status'] = trim((string)$cf['client_payment_status']); }
elseif (isset($cf['Client Payment Status / Notes']) && trim((string)$cf['Client Payment Status / Notes']) !== '') { $meta['client_payment_status'] = trim((string)$cf['Client Payment Status / Notes']); }
elseif (isset($cf['Client Payment Status']) && trim((string)$cf['Client Payment Status']) !== '') { $meta['client_payment_status'] = trim((string)$cf['Client Payment Status']); }
elseif (isset($cf['notes']) && trim((string)$cf['notes']) !== '') { $meta['client_payment_status'] = trim((string)$cf['notes']); }
}
if (!isset($meta['client_email'])) {
if (isset($cf['client_email']) && trim((string)$cf['client_email']) !== '') { $meta['client_email'] = trim((string)$cf['client_email']); }
elseif (isset($cf['Client Email (for dashboard)']) && trim((string)$cf['Client Email (for dashboard)']) !== '') { $meta['client_email'] = trim((string)$cf['Client Email (for dashboard)']); }
elseif (isset($cf['email']) && trim((string)$cf['email']) !== '') { $meta['client_email'] = trim((string)$cf['email']); }
}
if (!isset($meta['client_name'])) {
if (isset($cf['Client’s Name (text)']) && trim((string)$cf['Client’s Name (text)']) !== '') { $meta['client_name'] = trim((string)$cf['Client’s Name (text)']); }
elseif (isset($cf["Client's Name (text)"]) && trim((string)$cf["Client's Name (text)"]) !== '') { $meta['client_name'] = trim((string)$cf["Client's Name (text)"]); }
elseif (isset($cf['client_name_text']) && trim((string)$cf['client_name_text']) !== '') { $meta['client_name'] = trim((string)$cf['client_name_text']); }
}
if (!isset($meta['amount_offered'])) {
if (isset($cf['Amount Offered (if installment)'])) { $meta['amount_offered'] = (float)$cf['Amount Offered (if installment)']; }
elseif (isset($cf['offered_amount'])) { $meta['amount_offered'] = (float)$cf['offered_amount']; }
elseif (isset($cf['offered amount'])) { $meta['amount_offered'] = (float)$cf['offered amount']; }
}
if (!isset($meta['amount_paid_so_far'])) {
if (isset($cf['Amount Paid So Far'])) { $meta['amount_paid_so_far'] = (float)$cf['Amount Paid So Far']; }
elseif (isset($cf['Paid So Far'])) { $meta['amount_paid_so_far'] = (float)$cf['Paid So Far']; }
}
if (!isset($meta['discount_approved_by'])) {
if (isset($cf['Discount Approved By']) && trim((string)$cf['Discount Approved By']) !== '') { $meta['discount_approved_by'] = trim((string)$cf['Discount Approved By']); }
}
if (!isset($meta['commission_pct'])) {
if (isset($cf['Commission %'])) { $meta['commission_pct'] = (float)$cf['Commission %']; }
}
if (!isset($meta['marketer_comm'])) {
if (isset($cf['Marketer Commission Amount'])) { $meta['marketer_comm'] = (float)$cf['Marketer Commission Amount']; }
elseif (isset($cf['Marketer Commission'])) { $meta['marketer_comm'] = (float)$cf['Marketer Commission']; }
}
if (!isset($meta['agent_comm'])) {
if (isset($cf['Agent Commission Amount'])) { $meta['agent_comm'] = (float)$cf['Agent Commission Amount']; }
elseif (isset($cf['Agent Commission'])) { $meta['agent_comm'] = (float)$cf['Agent Commission']; }
}
if (!isset($meta['amount_offered']) && isset($cf['amount_offered'])) { $meta['amount_offered'] = (float)$cf['amount_offered']; }
if (!isset($meta['amount_paid_so_far']) && isset($cf['amount_paid_so_far'])) { $meta['amount_paid_so_far'] = (float)$cf['amount_paid_so_far']; }
if (!isset($meta['discount_amount']) && isset($cf['discount_amount'])) { $meta['discount_amount'] = (float)$cf['discount_amount']; }
if (!isset($meta['discount_approved_by']) && isset($cf['discount_approved_by'])) { $meta['discount_approved_by'] = (string)$cf['discount_approved_by']; }
if (!isset($meta['balance_remaining']) && isset($cf['balance_remaining'])) { $meta['balance_remaining'] = (float)$cf['balance_remaining']; }
if (!isset($meta['marketer_bank'])) {
$mb = [
'acc_no' => trim((string)($cf['marketer_account_number'] ?? ($cf['marketer_acc_no'] ?? ''))),
'bank' => trim((string)($cf['marketer_bank_name'] ?? ($cf['marketer_bank'] ?? ''))),
'acc_name' => trim((string)($cf['marketer_account_name'] ?? ($cf['marketer_acc_name'] ?? '')))
];
if ($mb['acc_no'] !== '' || $mb['bank'] !== '' || $mb['acc_name'] !== '') { $meta['marketer_bank'] = $mb; }
}
if (!isset($meta['agent_bank'])) {
$ab = [
'acc_no' => trim((string)($cf['agent_account_number'] ?? ($cf['agent_acc_no'] ?? ''))),
'bank' => trim((string)($cf['agent_bank_name'] ?? ($cf['agent_bank'] ?? ''))),
'acc_name' => trim((string)($cf['agent_account_name'] ?? ($cf['agent_acc_name'] ?? '')))
];
if ($ab['acc_no'] !== '' || $ab['bank'] !== '' || $ab['acc_name'] !== '') { $meta['agent_bank'] = $ab; }
}
}
}
} catch (Throwable $eCFb) {}
// Force data mapping (deal first, fallback to meta)
$clientNameMeta = (string)($meta['client_name'] ?? '');
$clientEmailView = (string)($meta['client_email'] ?? '');
$proj = (string)($row['project_name'] ?: ($row['project_desc'] ?: ($dealMeta['project_desc'] ?? '')));
$planType = (string)($row['payment_plan'] ?: ($dealMeta['plan_type'] ?? ''));
$dealSource = (string)($row['deal_source'] ?: ($dealMeta['deal_source'] ?? ''));
$marketerName = (string)($row['marketer_name'] ?: ($dealMeta['marketer_name'] ?? ''));
$amountOffered = (float)($meta['amount_offered'] ?? ($row['amount_offered'] ?? 0));
$amountPaidSoFar = (float)($meta['amount_paid_so_far'] ?? ($row['amount_paid_so_far'] ?? 0));
$discountAmount = (float)($meta['discount_amount'] ?? ($row['discount_amount'] ?? 0));
$discountBy = (string)($meta['discount_approved_by'] ?? '');
$commissionPct = (float)($meta['commission_pct'] ?? ($row['commission_percent'] ?? 0));
$marketerComm = (float)($meta['marketer_comm'] ?? ($row['marketer_commission'] ?? 0));
$agentComm = (float)($meta['agent_comm'] ?? ($row['agent_commission'] ?? 0));
$balanceRem = (float)($meta['balance_remaining'] ?? ($row['balance_remaining'] ?? 0));
$statusNotes = (string)($row['notes'] ?: ($dealMeta['client_payment_status'] ?? ($meta['client_payment_status'] ?? ($meta['notes'] ?? ''))));
// STRICT: fetch bank details only from deals_submit JSON columns (not from meta_json)
$mkRaw = [];
if (!empty($row['ds_marketer_bank'])) {
$mkRaw = is_string($row['ds_marketer_bank']) ? (json_decode($row['ds_marketer_bank'], true) ?: []) : (is_array($row['ds_marketer_bank']) ? $row['ds_marketer_bank'] : []);
}
$agRaw = [];
if (!empty($row['ds_agent_bank'])) {
$agRaw = is_string($row['ds_agent_bank']) ? (json_decode($row['ds_agent_bank'], true) ?: []) : (is_array($row['ds_agent_bank']) ? $row['ds_agent_bank'] : []);
}
if ((empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) || (empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name'])))) {
try {
$uidProbe = (int)($row['user_id'] ?? 0);
if ($uidProbe > 0 && colExists('deals_submit','user_id')) {
$qs = $pdo->prepare("SELECT marketer_bank, agent_bank FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qs->execute([$uidProbe]);
$drs = $qs->fetch(PDO::FETCH_ASSOC) ?: [];
if ((empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) && !empty($drs['marketer_bank'])) {
$tmp = is_string($drs['marketer_bank']) ? (json_decode($drs['marketer_bank'], true) ?: []) : (is_array($drs['marketer_bank']) ? $drs['marketer_bank'] : []);
if (is_array($tmp)) { $mkRaw = $tmp; }
}
if ((empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name']))) && !empty($drs['agent_bank'])) {
$tmp = is_string($drs['agent_bank']) ? (json_decode($drs['agent_bank'], true) ?: []) : (is_array($drs['agent_bank']) ? $drs['agent_bank'] : []);
if (is_array($tmp)) { $agRaw = $tmp; }
}
}
$didProbe = (int)($row['deal_id'] ?? 0);
if (($didProbe > 0 || $uidProbe > 0) && colExists('deals_submit','id')) {
$fields = [];
foreach (['marketer_account_number','marketer_acc_no','marketer_bank_name','marketer_bank','marketer_account_name','marketer_acc_name','agent_account_number','agent_acc_no','agent_bank_name','agent_bank','agent_account_name','agent_acc_name'] as $f) {
if (colExists('deals_submit',$f)) { $fields[] = $f; }
}
if (!empty($fields)) {
$sel = implode(', ', array_map(function($f){ return $f; }, $fields));
$qds = null; $pr = [];
if ($didProbe > 0) { $qds = $pdo->prepare("SELECT {$sel} FROM deals_submit WHERE id = ? LIMIT 1"); $pr = [$didProbe]; }
elseif ($uidProbe > 0 && colExists('deals_submit','user_id')) { $qds = $pdo->prepare("SELECT {$sel} FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1"); $pr = [$uidProbe]; }
if ($qds) {
$qds->execute($pr);
$dsr = $qds->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) {
$accNo = trim((string)($dsr['marketer_account_number'] ?? ($dsr['marketer_acc_no'] ?? '')));
$bankN = trim((string)($dsr['marketer_bank_name'] ?? ($dsr['marketer_bank'] ?? '')));
$accNm = trim((string)($dsr['marketer_account_name'] ?? ($dsr['marketer_acc_name'] ?? '')));
if ($accNo !== '' || $bankN !== '' || $accNm !== '') { $mkRaw = ['acc_no'=>$accNo,'bank'=>$bankN,'acc_name'=>$accNm]; }
}
if (empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name']))) {
$accNo = trim((string)($dsr['agent_account_number'] ?? ($dsr['agent_acc_no'] ?? '')));
$bankN = trim((string)($dsr['agent_bank_name'] ?? ($dsr['agent_bank'] ?? '')));
$accNm = trim((string)($dsr['agent_account_name'] ?? ($dsr['agent_acc_name'] ?? '')));
if ($accNo !== '' || $bankN !== '' || $accNm !== '') { $agRaw = ['acc_no'=>$accNo,'bank'=>$bankN,'acc_name'=>$accNm]; }
}
}
}
}
} catch (Throwable $eDSFallback) {}
}
$mkBank = [
'acc_no' => trim((string)(
$mkRaw['acc_no'] ?? $mkRaw['account_number'] ?? $mkRaw['accountNumber'] ?? $mkRaw['accNo'] ?? ''
)),
'bank' => trim((string)(
$mkRaw['bank'] ?? $mkRaw['bank_name'] ?? $mkRaw['bankName'] ?? ''
)),
'acc_name' => trim((string)(
$mkRaw['acc_name'] ?? $mkRaw['account_name'] ?? $mkRaw['accountName'] ?? ''
))
];
$agBank = [
'acc_no' => trim((string)(
$agRaw['acc_no'] ?? $agRaw['account_number'] ?? $agRaw['accountNumber'] ?? $agRaw['accNo'] ?? ''
)),
'bank' => trim((string)(
$agRaw['bank'] ?? $agRaw['bank_name'] ?? $agRaw['bankName'] ?? ''
)),
'acc_name' => trim((string)(
$agRaw['acc_name'] ?? $agRaw['account_name'] ?? $agRaw['accountName'] ?? ''
))
];
$transactionsMeta = isset($meta['transactions']) && is_array($meta['transactions']) ? $meta['transactions'] : [];
// Leave client email lookup only if we truly lack email in meta
if ($clientEmailView === '' && isset($paymentMeta['client_email'])) { $clientEmailView = (string)$paymentMeta['client_email']; }
$dealIdRef = 0;
try {
if (isset($row['deal_id'])) { $dealIdRef = (int)$row['deal_id']; }
} catch (Throwable $eDid) {}
if ($dealIdRef > 0) {
try {
$qdrow = $pdo->prepare("SELECT * FROM deals WHERE id = ? LIMIT 1");
$qdrow->execute([$dealIdRef]);
$dr = $qdrow->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($dr)) {
if ($proj === '') {
$proj = (string)($dr['project_name'] ?? '');
if ($proj === '') { $proj = (string)($dr['project_desc'] ?? ($dr['property_estate'] ?? $proj)); }
}
if ($amountOffered <= 0 && isset($dr['amount_offered'])) { $amountOffered = (float)$dr['amount_offered']; }
if ($amountPaidSoFar <= 0 && isset($dr['amount_paid_so_far'])) { $amountPaidSoFar = (float)$dr['amount_paid_so_far']; }
if ($commissionPct <= 0) {
if (isset($dr['commission_percent'])) { $commissionPct = (float)$dr['commission_percent']; }
elseif (isset($dr['commission_pct'])) { $commissionPct = (float)$dr['commission_pct']; }
}
if ($balanceRem <= 0 && isset($dr['balance_remaining'])) { $balanceRem = (float)$dr['balance_remaining']; }
if (trim($dealSource) === '' && isset($dr['deal_source'])) { $dealSource = (string)$dr['deal_source']; }
if (trim($planType) === '' && isset($dr['payment_plan'])) { $planType = (string)$dr['payment_plan']; }
if (trim($marketerName) === '' && isset($dr['marketer_id']) && colExists('users','name')) {
$mid = (int)$dr['marketer_id']; if ($mid > 0) {
try { $qmname = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1"); $qmname->execute([$mid]); $marketerName = (string)($qmname->fetchColumn() ?: $marketerName); } catch (Throwable $eMN) {}
}
}
$sets = []; $vals = [];
if (isset($dr['project_name']) && trim((string)$dr['project_name']) === '' && $proj !== '') { $sets[]='project_name = ?'; $vals[]=$proj; }
if (isset($dr['project_desc']) && trim((string)$dr['project_desc']) === '' && $proj !== '') { $sets[]='project_desc = ?'; $vals[]=$proj; }
if (isset($dr['deal_source']) && trim((string)$dr['deal_source']) === '' && $dealSource !== '') { $sets[]='deal_source = ?'; $vals[]=$dealSource; }
if (isset($dr['payment_plan']) && trim((string)$dr['payment_plan']) === '' && $planType !== '') { $sets[]='payment_plan = ?'; $vals[]=$planType; }
if (isset($dr['marketer_name']) && trim((string)$dr['marketer_name']) === '' && $marketerName !== '') { $sets[]='marketer_name = ?'; $vals[]=$marketerName; }
if (isset($dr['amount_offered']) && (float)$dr['amount_offered'] == 0.0 && $amountOffered > 0) { $sets[]='amount_offered = ?'; $vals[]=$amountOffered; }
if (isset($dr['amount_paid_so_far']) && (float)$dr['amount_paid_so_far'] == 0.0 && $amountPaidSoFar > 0) { $sets[]='amount_paid_so_far = ?'; $vals[]=$amountPaidSoFar; }
if (isset($dr['commission_percent']) && (float)$dr['commission_percent'] == 0.0 && $commissionPct > 0) { $sets[]='commission_percent = ?'; $vals[]=$commissionPct; }
if (isset($dr['discount_amount']) && (float)$dr['discount_amount'] == 0.0 && $discountAmount > 0) { $sets[]='discount_amount = ?'; $vals[]=$discountAmount; }
if (isset($dr['balance_remaining']) && (float)$dr['balance_remaining'] == 0.0 && $balanceRem > 0) { $sets[]='balance_remaining = ?'; $vals[]=$balanceRem; }
if (isset($dr['notes']) && trim((string)$dr['notes']) === '' && $statusNotes !== '') { $sets[]='notes = ?'; $vals[]=$statusNotes; }
if (!empty($sets)) {
$vals[] = $dealIdRef;
$upD = $pdo->prepare("UPDATE deals SET ".implode(',', $sets)." WHERE id = ?");
$upD->execute($vals);
try { @file_put_contents(__DIR__ . '/debug_sql.txt', "REPAIR_UPDATE_DEAL:\nUPDATE deals SET ".implode(',', $sets)." WHERE id = {$dealIdRef}\nPARAMS:\n".print_r($vals, true)."\n", FILE_APPEND); } catch (Throwable $eRU0) {}
}
if (empty($mkBank['acc_no']) && (isset($dr['marketer_account_number']) || isset($dr['marketer_acc_no']))) {
$mkBank['acc_no'] = trim((string)($dr['marketer_account_number'] ?? ($dr['marketer_acc_no'] ?? '')));
}
if (empty($mkBank['bank']) && (isset($dr['marketer_bank_name']) || isset($dr['marketer_bank']))) {
$mkBank['bank'] = trim((string)($dr['marketer_bank_name'] ?? ($dr['marketer_bank'] ?? '')));
}
if (empty($mkBank['acc_name']) && (isset($dr['marketer_account_name']) || isset($dr['marketer_acc_name']))) {
$mkBank['acc_name'] = trim((string)($dr['marketer_account_name'] ?? ($dr['marketer_acc_name'] ?? '')));
}
if (empty($agBank['acc_no']) && (isset($dr['agent_account_number']) || isset($dr['agent_acc_no']))) {
$agBank['acc_no'] = trim((string)($dr['agent_account_number'] ?? ($dr['agent_acc_no'] ?? '')));
}
if (empty($agBank['bank']) && (isset($dr['agent_bank_name']) || isset($dr['agent_bank']))) {
$agBank['bank'] = trim((string)($dr['agent_bank_name'] ?? ($dr['agent_bank'] ?? '')));
}
if (empty($agBank['acc_name']) && (isset($dr['agent_account_name']) || isset($dr['agent_acc_name']))) {
$agBank['acc_name'] = trim((string)($dr['agent_account_name'] ?? ($dr['agent_acc_name'] ?? '')));
}
}
} catch (Throwable $eDL) {}
}
if (empty($transactionsMeta)) {
try {
$histLimit = 10;
$list = [];
$uidH = (int)($row['user_id'] ?? 0);
$amountColP = colExists('payments','amount') ? 'amount' : (colExists('payments','total') ? 'total' : null);
$dateColP = colExists('payments','created_at') ? 'created_at' : (colExists('payments','updated_at') ? 'updated_at' : (colExists('payments','date') ? 'date' : (colExists('payments','payment_date') ? 'payment_date' : null)));
$amountColT = colExists('transactions','amount') ? 'amount' : (colExists('transactions','total') ? 'total' : null);
$dateColT = colExists('transactions','created_at') ? 'created_at' : (colExists('transactions','updated_at') ? 'updated_at' : (colExists('transactions','date') ? 'date' : null));
if ($uidH > 0 && $amountColP && $dateColP && colExists('payments','user_id')) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE user_id = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([$uidH]);
$rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) {
$list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)];
}
} elseif (!empty($row['client_name']) && $amountColP && $dateColP && colExists('payments','client_name')) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE client_name = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([(string)$row['client_name']]);
$rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) { $list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)]; }
} elseif ($clientEmailView !== '' && $amountColP && $dateColP && colExists('payments','client_email')) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE client_email = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([$clientEmailView]);
$rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) { $list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)]; }
} elseif ($dealIdRef > 0 && $amountColP && $dateColP && colExists('payments','deal_id')) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE deal_id = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([$dealIdRef]);
$rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) { $list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)]; }
}
if ($amountColT && $dateColT) {
if ($uidH > 0 && colExists('transactions','user_id')) {
$qt = $pdo->prepare("SELECT {$dateColT} AS d, {$amountColT} AS a FROM transactions WHERE user_id = ? ORDER BY {$dateColT} DESC LIMIT ".$histLimit);
$qt->execute([$uidH]);
$txRows = $qt->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($txRows as $tx) { $list[] = ['date' => (string)$tx['d'], 'amount' => (float)($tx['a'] ?? 0)]; }
} elseif (!empty($row['client_name']) && colExists('transactions','client_name')) {
$qt = $pdo->prepare("SELECT {$dateColT} AS d, {$amountColT} AS a FROM transactions WHERE client_name = ? ORDER BY {$dateColT} DESC LIMIT ".$histLimit);
$qt->execute([(string)$row['client_name']]);
$txRows = $qt->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($txRows as $tx) { $list[] = ['date' => (string)$tx['d'], 'amount' => (float)($tx['a'] ?? 0)]; }
} elseif ($clientEmailView !== '' && colExists('transactions','client_email')) {
$qt = $pdo->prepare("SELECT {$dateColT} AS d, {$amountColT} AS a FROM transactions WHERE client_email = ? ORDER BY {$dateColT} DESC LIMIT ".$histLimit);
$qt->execute([$clientEmailView]);
$txRows = $qt->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($txRows as $tx) { $list[] = ['date' => (string)$tx['d'], 'amount' => (float)($tx['a'] ?? 0)]; }
}
}
// Fallback by reference window → discover email/name from deals near timestamp
if (empty($list)) {
$ref = (string)($row['reference'] ?? '');
if ($ref !== '' && preg_match('/(\\d{14})/', $ref, $m)) {
$ts = DateTime::createFromFormat('YmdHis', $m[1]);
if ($ts && colExists('deals','created_at')) {
$win = $ts->format('Y-m-d H:i:s');
try {
$qd = null;
if (colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qd->execute([$win,$win]);
$dm = (string)($qd->fetchColumn() ?: '');
if ($dm !== '') {
$mj = json_decode($dm, true) ?: [];
$em = (string)($mj['client_email'] ?? '');
$nm = (string)($mj['client_name'] ?? '');
if ($em !== '' && $amountColP && $dateColP && colExists('payments','client_email')) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE client_email = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([$em]); $rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) { $list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)]; }
}
if ($nm !== '' && $amountColP && $dateColP && colExists('payments','client_name') && empty($list)) {
$qh = $pdo->prepare("SELECT {$dateColP} AS d, {$amountColP} AS a FROM payments WHERE client_name = ? ORDER BY {$dateColP} DESC LIMIT ".$histLimit);
$qh->execute([$nm]); $rowsH = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsH as $r0) { $list[] = ['date' => (string)$r0['d'], 'amount' => (float)($r0['a'] ?? 0)]; }
}
}
}
} catch (Throwable $eRefWin) {}
}
}
}
if (!empty($list)) {
$transactionsMeta = [];
foreach ($list as $it) {
$d = (string)($it['date'] ?? '');
$amt = (float)($it['amount'] ?? 0);
$transactionsMeta[] = ['date' => ($d !== '' ? date('Y-m-d', strtotime($d)) : ''), 'amount' => $amt];
}
}
} catch (Throwable $eHist) {}
}
$paymentRefView = (string)($row['reference'] ?? '');
$methodCol = colExists('payments','payment_method') ? 'payment_method' : (colExists('payments','method') ? 'method' : null);
$paymentMethodView = '';
if ($methodCol) {
try { $qp = $pdo->prepare("SELECT {$methodCol} AS method FROM payments WHERE id = ? LIMIT 1"); $qp->execute([(int)$row['payment_id']]); $paymentMethodView = (string)($qp->fetchColumn() ?: ''); } catch (Throwable $eM) {}
}
$normalized = [
'client_name' => $clientNameMeta,
'client_email' => $clientEmailView,
'project_desc' => $proj,
'deal_source' => $dealSource,
'plan_type' => $planType,
'amount_offered' => $amountOffered,
'amount_paid_so_far' => $amountPaidSoFar,
'amount_paid_now' => (float)($row['amount'] ?? 0),
'discount_amount' => $discountAmount,
'discount_approved_by' => $discountBy,
'commission_pct' => $commissionPct,
'marketer_comm' => $marketerComm,
'agent_comm' => $agentComm,
'balance_remaining' => $balanceRem,
'client_payment_status' => $statusNotes,
'marketer_bank' => $mkBank,
'agent_bank' => $agBank,
'transactions' => $transactionsMeta,
'marketer_name' => $marketerName,
'payment_reference' => $paymentRefView,
'payment_method' => ($paymentMethodView !== '' ? $paymentMethodView : 'Bank Transfer')
];
$currentMeta = is_array($meta) ? $meta : [];
foreach ($normalized as $k=>$v) {
$hasKey = array_key_exists($k, $currentMeta);
$cur = $hasKey ? $currentMeta[$k] : null;
$shouldSet = !$hasKey;
if (!$shouldSet) {
if (is_string($cur)) { $t = trim($cur); $shouldSet = ($t === '' || $t === '-'); }
elseif (is_array($cur)) { $shouldSet = empty($cur); }
elseif ($cur === null) { $shouldSet = true; }
}
if ($shouldSet) { $currentMeta[$k] = $v; }
}
try {
if (colExists('payments','meta_json')) {
$newJson = json_encode($currentMeta);
if ($newJson) {
$up = $pdo->prepare("UPDATE payments SET meta_json = ? WHERE id = ?");
$up->execute([$newJson, (int)$row['payment_id']]);
}
}
} catch (Throwable $eW) {}
if ($methodCol) {
try {
$methodExisting = '';
$qm = $pdo->prepare("SELECT {$methodCol} AS m FROM payments WHERE id = ? LIMIT 1"); $qm->execute([(int)$row['payment_id']]);
$methodExisting = (string)($qm->fetchColumn() ?: '');
if (trim($methodExisting) === '') {
$upm = $pdo->prepare("UPDATE payments SET {$methodCol} = ? WHERE id = ?");
$upm->execute(['Bank Transfer', (int)$row['payment_id']]);
}
} catch (Throwable $eWM) {}
}
}
} catch (Throwable $e) {}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $canVerify) {
if (isset($_POST['backfill_meta'])) {
$updated = 0; $scanned = 0;
try {
$params = [];
$companyClause = '';
if ($applyCompanyFilter && colExists('payments','company_id')) { $companyClause = " AND (company_id = ? OR company_id IS NULL)"; $params[] = $companyId; }
$refCol = colExists('payments','reference') ? 'reference' : (colExists('payments','ref') ? 'ref' : "''");
$rcpCol = colExists('payments','receipt_file') ? 'receipt_file' : (colExists('payments','proof_file') ? 'proof_file' : "NULL");
$dateCol = colExists('payments','created_at') ? 'created_at' : (colExists('payments','date') ? 'date' : (colExists('payments','updated_at') ? 'updated_at' : null));
$dealIdCol = colExists('payments','deal_id') ? 'deal_id' : 'NULL';
$sql = "SELECT id, user_id, amount, {$refCol} AS reference, {$rcpCol} AS receipt_file, {$dealIdCol} AS deal_id"
. ($dateCol ? ", {$dateCol} AS created_at" : "")
. (colExists('payments','client_name') ? ", client_name" : ", NULL AS client_name")
. (colExists('payments','client_email') ? ", client_email" : ", NULL AS client_email")
. (colExists('payments','submitted_by_user') ? ", submitted_by_user" : ", NULL AS submitted_by_user")
. (colExists('payments','submitted_by') ? ", submitted_by" : ", NULL AS submitted_by")
. (colExists('payments','created_by') ? ", created_by" : ", NULL AS created_by")
. (colExists('payments','created_by_user') ? ", created_by_user" : ", NULL AS created_by_user")
. (colExists('payments','staff_id') ? ", staff_id" : ", NULL AS staff_id")
. (colExists('payments','marketer_id') ? ", marketer_id" : ", NULL AS marketer_id")
. (colExists('payments','agent_id') ? ", agent_id" : ", NULL AS agent_id")
. (colExists('payments','contact_rep_id') ? ", contact_rep_id" : ", NULL AS contact_rep_id")
. " FROM payments WHERE (".$refCol." LIKE 'deal-submission-%' OR ".$refCol." LIKE 'deal-quick-%' OR status IN ('pending_verification','approved')){$companyClause} ORDER BY ".($dateCol ?: 'id')." DESC LIMIT 500";
$st = $pdo->prepare($sql); $st->execute($params);
$rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rows as $row) {
$scanned++;
$meta = [];
try {
if (colExists('payments','meta_json')) {
$qm = $pdo->prepare("SELECT meta_json FROM payments WHERE id = ? LIMIT 1");
$qm->execute([(int)$row['id']]); $mj = (string)($qm->fetchColumn() ?: '');
if ($mj !== '') { $meta = json_decode($mj, true) ?: []; }
}
} catch (Throwable $e0) {}
// Pull from deals meta if missing
try {
if ((empty($meta) || !is_array($meta)) && (int)($row['deal_id'] ?? 0) > 0 && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE id = ? LIMIT 1");
$qd->execute([(int)$row['deal_id']]); $dmj = (string)($qd->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
} catch (Throwable $e1) {}
if (empty($meta) || !is_array($meta)) {
try {
$rcp = (string)($row['receipt_file'] ?? '');
if ($rcp !== '' && colExists('deals','meta_json')) {
if (colExists('deals','receipt_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = (string)($qs->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file LIKE ? LIMIT 1"); $qs->execute(['%'.$base]);
$dmj = (string)($qs->fetchColumn() ?: ''); if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
} elseif (colExists('deals','proof_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = (string)($qs->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file LIKE ? LIMIT 1"); $qs->execute(['%'.$base]);
$dmj = (string)($qs->fetchColumn() ?: ''); if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
}
}
} catch (Throwable $e2) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid]); $dmj = (string)($qd->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta) && $dateCol && colExists('deals','created_at')) {
$dt = (string)($row['created_at'] ?? '');
if ($dt !== '') {
$qdt = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qdt->execute([$uid, $dt, $dt]); $dmj = (string)($qdt->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
}
} catch (Throwable $e3) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$ref = (string)($row['reference'] ?? '');
if ($ref !== '' && colExists('deals','created_at') && colExists('deals','meta_json')) {
$ts = null; if (preg_match('/(\\d{14})/', $ref, $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$win = $ts->format('Y-m-d H:i:s');
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id')) {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$uid, $win, $win]);
} else {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$win, $win]);
}
$dmj = (string)($qrf->fetchColumn() ?: '');
if ($dmj !== '') { $meta = json_decode($dmj, true) ?: []; }
}
}
} catch (Throwable $e4) {}
}
try {
$cfuid = (int)($row['user_id'] ?? 0);
if ($cfuid > 0 && colExists('client_forms','form_data')) {
$qcf = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY created_at DESC LIMIT 1");
$qcf->execute([$cfuid]);
$cfjson = (string)($qcf->fetchColumn() ?: '');
if ($cfjson !== '') {
$cf = json_decode($cfjson, true) ?: [];
if (!isset($meta['project_desc']) && isset($cf['project_desc'])) { $meta['project_desc'] = $cf['project_desc']; }
if (!isset($meta['property']) && isset($cf['property'])) { $meta['property'] = $cf['property']; }
if (!isset($meta['deal_source']) && isset($cf['deal_source'])) { $meta['deal_source'] = $cf['deal_source']; }
if (!isset($meta['plan_type']) && isset($cf['plan_type'])) { $meta['plan_type'] = $cf['plan_type']; }
if (!isset($meta['marketer_name']) && isset($cf['marketer_name'])) { $meta['marketer_name'] = $cf['marketer_name']; }
if (!isset($meta['amount_offered']) && isset($cf['amount_offered'])) { $meta['amount_offered'] = (float)$cf['amount_offered']; }
if (!isset($meta['amount_paid_so_far']) && isset($cf['amount_paid_so_far'])) { $meta['amount_paid_so_far'] = (float)$cf['amount_paid_so_far']; }
if (!isset($meta['discount_amount']) && isset($cf['discount_amount'])) { $meta['discount_amount'] = (float)$cf['discount_amount']; }
if (!isset($meta['discount_approved_by']) && isset($cf['discount_approved_by'])) { $meta['discount_approved_by'] = (string)$cf['discount_approved_by']; }
if (!isset($meta['balance_remaining']) && isset($cf['balance_remaining'])) { $meta['balance_remaining'] = (float)$cf['balance_remaining']; }
if (!isset($meta['marketer_bank'])) {
$mb = [
'acc_no' => trim((string)($cf['marketer_account_number'] ?? ($cf['marketer_acc_no'] ?? ''))),
'bank' => trim((string)($cf['marketer_bank_name'] ?? ($cf['marketer_bank'] ?? ''))),
'acc_name' => trim((string)($cf['marketer_account_name'] ?? ($cf['marketer_acc_name'] ?? '')))
];
if ($mb['acc_no'] !== '' || $mb['bank'] !== '' || $mb['acc_name'] !== '') { $meta['marketer_bank'] = $mb; }
}
if (!isset($meta['agent_bank'])) {
$ab = [
'acc_no' => trim((string)($cf['agent_account_number'] ?? ($cf['agent_acc_no'] ?? ''))),
'bank' => trim((string)($cf['agent_bank_name'] ?? ($cf['agent_bank'] ?? ''))),
'acc_name' => trim((string)($cf['agent_account_name'] ?? ($cf['agent_acc_name'] ?? '')))
];
if ($ab['acc_no'] !== '' || $ab['bank'] !== '' || $ab['acc_name'] !== '') { $meta['agent_bank'] = $ab; }
}
}
}
} catch (Throwable $eCFb) {}
// Build normalized block using the same rules as the UI renderer
$clientNameMeta = (string)($meta['client_name'] ?? '');
$proj = (string)($meta['project_desc'] ?? ($meta['property'] ?? ''));
$planType = (string)($meta['plan_type'] ?? '');
$dealSource = (string)($meta['deal_source'] ?? '');
$marketerName = (string)($meta['marketer_name'] ?? '');
$amountOffered = (float)($meta['amount_offered'] ?? 0);
$amountPaidSoFar = (float)($meta['amount_paid_so_far'] ?? 0);
$discountAmount = (float)($meta['discount_amount'] ?? 0);
$discountBy = (string)($meta['discount_approved_by'] ?? '');
$commissionPct = (float)($meta['commission_pct'] ?? 0);
$marketerComm = (float)($meta['marketer_comm'] ?? 0);
$agentComm = (float)($meta['agent_comm'] ?? 0);
$balanceRem = (float)($meta['balance_remaining'] ?? 0);
$statusNotes = (string)($meta['client_payment_status'] ?? ($meta['notes'] ?? ''));
$mkRaw = isset($meta['marketer_bank']) && is_array($meta['marketer_bank']) ? $meta['marketer_bank'] : [];
$agRaw = isset($meta['agent_bank']) && is_array($meta['agent_bank']) ? $meta['agent_bank'] : [];
if ((empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) || (empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name'])))) {
try {
$uidProbe = (int)($row['user_id'] ?? 0);
$didProbe = (int)($row['deal_id'] ?? 0);
if ($didProbe > 0 || $uidProbe > 0) {
if (colExists('deals_submit','marketer_bank') || colExists('deals_submit','agent_bank')) {
$qs = null; $pr = [];
if ($didProbe > 0 && colExists('deals_submit','id')) {
$qs = $pdo->prepare("SELECT marketer_bank, agent_bank FROM deals_submit WHERE id = ? LIMIT 1"); $pr = [$didProbe];
} elseif ($uidProbe > 0 && colExists('deals_submit','user_id')) {
$qs = $pdo->prepare("SELECT marketer_bank, agent_bank FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1"); $pr = [$uidProbe];
}
if ($qs) {
$qs->execute($pr);
$drs = $qs->fetch(PDO::FETCH_ASSOC) ?: [];
if ((empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) && !empty($drs['marketer_bank'])) {
$tmp = is_string($drs['marketer_bank']) ? (json_decode($drs['marketer_bank'], true) ?: []) : (is_array($drs['marketer_bank']) ? $drs['marketer_bank'] : []);
if (is_array($tmp)) { $mkRaw = $tmp; }
}
if ((empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name']))) && !empty($drs['agent_bank'])) {
$tmp = is_string($drs['agent_bank']) ? (json_decode($drs['agent_bank'], true) ?: []) : (is_array($drs['agent_bank']) ? $drs['agent_bank'] : []);
if (is_array($tmp)) { $agRaw = $tmp; }
}
}
}
$fields = [];
foreach (['marketer_account_number','marketer_acc_no','marketer_bank_name','marketer_bank','marketer_account_name','marketer_acc_name','agent_account_number','agent_acc_no','agent_bank_name','agent_bank','agent_account_name','agent_acc_name'] as $f) {
if (colExists('deals_submit',$f)) { $fields[] = $f; }
}
if (!empty($fields)) {
$sel = implode(', ', $fields);
$qds = null; $pr2 = [];
if ($didProbe > 0 && colExists('deals_submit','id')) { $qds = $pdo->prepare("SELECT {$sel} FROM deals_submit WHERE id = ? LIMIT 1"); $pr2 = [$didProbe]; }
elseif ($uidProbe > 0 && colExists('deals_submit','user_id')) { $qds = $pdo->prepare("SELECT {$sel} FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1"); $pr2 = [$uidProbe]; }
if ($qds) {
$qds->execute($pr2);
$dsr = $qds->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($mkRaw) || (empty($mkRaw['acc_no']) && empty($mkRaw['bank']) && empty($mkRaw['acc_name']))) {
$accNo = trim((string)($dsr['marketer_account_number'] ?? ($dsr['marketer_acc_no'] ?? '')));
$bankN = trim((string)($dsr['marketer_bank_name'] ?? ($dsr['marketer_bank'] ?? '')));
$accNm = trim((string)($dsr['marketer_account_name'] ?? ($dsr['marketer_acc_name'] ?? '')));
if ($accNo !== '' || $bankN !== '' || $accNm !== '') { $mkRaw = ['acc_no'=>$accNo,'bank'=>$bankN,'acc_name'=>$accNm]; }
}
if (empty($agRaw) || (empty($agRaw['acc_no']) && empty($agRaw['bank']) && empty($agRaw['acc_name']))) {
$accNo = trim((string)($dsr['agent_account_number'] ?? ($dsr['agent_acc_no'] ?? '')));
$bankN = trim((string)($dsr['agent_bank_name'] ?? ($dsr['agent_bank'] ?? '')));
$accNm = trim((string)($dsr['agent_account_name'] ?? ($dsr['agent_acc_name'] ?? '')));
if ($accNo !== '' || $bankN !== '' || $accNm !== '') { $agRaw = ['acc_no'=>$accNo,'bank'=>$bankN,'acc_name'=>$accNm]; }
}
}
}
}
} catch (Throwable $eDSBanks) {}
}
$mkBank = [
'acc_no' => trim((string)($mkRaw['acc_no'] ?? ($mkRaw['account_number'] ?? ($mkRaw['accountNumber'] ?? ($mkRaw['accNo'] ?? ($meta['marketer_account_number'] ?? ($meta['marketer_acc_no'] ?? ($meta['marketer_acc_number'] ?? '')))))))),
'bank' => trim((string)($mkRaw['bank'] ?? ($mkRaw['bank_name'] ?? ($mkRaw['bankName'] ?? ($meta['marketer_bank_name'] ?? ($meta['marketer_bank'] ?? '')))))),
'acc_name' => trim((string)($mkRaw['acc_name'] ?? ($mkRaw['account_name'] ?? ($mkRaw['accountName'] ?? ($meta['marketer_account_name'] ?? ($meta['marketer_acc_name'] ?? ''))))))
];
$agBank = [
'acc_no' => trim((string)($agRaw['acc_no'] ?? ($agRaw['account_number'] ?? ($agRaw['accountNumber'] ?? ($agRaw['accNo'] ?? ($meta['agent_account_number'] ?? ($meta['agent_acc_no'] ?? ($meta['agent_acc_number'] ?? '')))))))),
'bank' => trim((string)($agRaw['bank'] ?? ($agRaw['bank_name'] ?? ($agRaw['bankName'] ?? ($meta['agent_bank_name'] ?? ($meta['agent_bank'] ?? '')))))),
'acc_name' => trim((string)($agRaw['acc_name'] ?? ($agRaw['account_name'] ?? ($agRaw['accountName'] ?? ($meta['agent_account_name'] ?? ($meta['agent_acc_name'] ?? ''))))))
];
$transactionsMeta = isset($meta['transactions']) && is_array($meta['transactions']) ? $meta['transactions'] : [];
$clientEmailView = (string)($meta['client_email'] ?? '');
if ($clientEmailView === '' && (int)($row['user_id'] ?? 0) > 0 && colExists('users','email')) {
try { $qe = $pdo->prepare("SELECT email FROM users WHERE id = ? LIMIT 1"); $qe->execute([(int)$row['user_id']]); $clientEmailView = (string)($qe->fetchColumn() ?: ''); } catch (Throwable $eE) {}
}
$paymentRefView = (string)($row['reference'] ?? '');
$methodCol = colExists('payments','payment_method') ? 'payment_method' : (colExists('payments','method') ? 'method' : null);
$paymentMethodView = '';
if ($methodCol) {
try { $qp = $pdo->prepare("SELECT {$methodCol} AS method FROM payments WHERE id = ? LIMIT 1"); $qp->execute([(int)$row['id']]); $paymentMethodView = (string)($qp->fetchColumn() ?: ''); } catch (Throwable $eM) {}
}
$normalized = [
'client_name' => $clientNameMeta,
'client_email' => $clientEmailView,
'project_desc' => $proj,
'deal_source' => $dealSource,
'plan_type' => $planType,
'amount_offered' => $amountOffered,
'amount_paid_so_far' => $amountPaidSoFar,
'amount_paid_now' => (float)($row['amount'] ?? 0),
'discount_amount' => $discountAmount,
'discount_approved_by' => $discountBy,
'commission_pct' => $commissionPct,
'marketer_comm' => $marketerComm,
'agent_comm' => $agentComm,
'balance_remaining' => $balanceRem,
'client_payment_status' => $statusNotes,
'marketer_bank' => $mkBank,
'agent_bank' => $agBank,
'transactions' => $transactionsMeta,
'marketer_name' => $marketerName,
'payment_reference' => $paymentRefView,
'payment_method' => ($paymentMethodView !== '' ? $paymentMethodView : 'Bank Transfer')
];
$currentMeta = is_array($meta) ? $meta : [];
foreach ($normalized as $k=>$v) {
$hasKey = array_key_exists($k, $currentMeta);
$cur = $hasKey ? $currentMeta[$k] : null;
$shouldSet = !$hasKey;
if (!$shouldSet) {
if (is_string($cur)) { $t = trim($cur); $shouldSet = ($t === '' || $t === '-'); }
elseif (is_array($cur)) { $shouldSet = empty($cur); }
elseif ($cur === null) { $shouldSet = true; }
}
if ($shouldSet) { $currentMeta[$k] = $v; }
}
try {
if (colExists('payments','meta_json')) {
$newJson = json_encode($currentMeta);
if ($newJson) {
$up = $pdo->prepare("UPDATE payments SET meta_json = ? WHERE id = ?");
$up->execute([$newJson, (int)$row['id']]);
$updated++;
}
}
} catch (Throwable $eW) {}
// Also ensure a visible method is saved when column exists but empty
if ($methodCol) {
try {
$methodExisting = '';
$qm = $pdo->prepare("SELECT {$methodCol} AS m FROM payments WHERE id = ? LIMIT 1"); $qm->execute([(int)$row['id']]);
$methodExisting = (string)($qm->fetchColumn() ?: '');
if (trim($methodExisting) === '') {
$upm = $pdo->prepare("UPDATE payments SET {$methodCol} = ? WHERE id = ?");
$upm->execute(['Bank Transfer', (int)$row['id']]);
}
} catch (Throwable $eWM) {}
}
}
$action_msg = "Backfill complete: {$updated} record(s) updated out of {$scanned} scanned.";
} catch (Throwable $e) {
$action_msg = "Backfill failed: " . (string)$e->getMessage();
}
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $canApprovePayments && !isset($_POST['backfill_meta'])) {
$pid = (int)($_POST['payment_id'] ?? 0);
if ($pid > 0) {
try {
if (isset($_POST['approve'])) {
$sql = "UPDATE payments SET status = 'approved'";
$vals = [];
if (colExists('payments','approved_by')) { $sql .= ", approved_by = ?"; $vals[] = $userId; }
if (colExists('payments','approval_date')) { $sql .= ", approval_date = NOW()"; }
elseif (colExists('payments','approved_at')) { $sql .= ", approved_at = NOW()"; }
if (colExists('payments','verified_by')) { $sql .= ", verified_by = ?"; $vals[] = $userId; }
if (colExists('payments','verified_at')) { $sql .= ", verified_at = NOW()"; }
$sql .= " WHERE id = ?";
$vals[] = $pid;
$st = $pdo->prepare($sql);
$st->execute($vals);
$action_msg = 'Payment approved.';
try {
$qu = $pdo->prepare("SELECT user_id, amount, reference, meta_json FROM payments WHERE id = ?");
$qu->execute([$pid]);
$pr = $qu->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$amt = (float)($pr['amount'] ?? 0);
$meta = [];
if (!empty($pr['meta_json'])) { $meta = json_decode($pr['meta_json'], true) ?: []; }
$submittedBy = (int)($meta['submitted_by_user'] ?? 0);
$clientEmail = ''; $clientName = '';
$submitEmail = ''; $submitName = '';
if ($uId > 0 && colExists('users','email')) {
$se = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$se->execute([$uId]); $rowE = $se->fetch(PDO::FETCH_ASSOC);
if ($rowE) { $clientEmail = (string)($rowE['email'] ?? ''); $clientName = (string)($rowE['display_name'] ?? 'Client'); }
}
if ($submittedBy > 0 && colExists('users','email')) {
$ss = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$ss->execute([$submittedBy]); $rowS = $ss->fetch(PDO::FETCH_ASSOC);
if ($rowS) { $submitEmail = (string)($rowS['email'] ?? ''); $submitName = (string)($rowS['display_name'] ?? ''); }
}
$companyName = function_exists('getSetting') ? getSetting('company_name', 'Aiben Properties') : 'Aiben Properties';
$noreplyEmail = function_exists('getSetting') ? getSetting('noreply_email', 'no-reply@aibenproperties.local') : 'no-reply@aibenproperties.local';
if ($clientEmail !== '') {
$subjectC = "Payment approved";
$bodyC = "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " has been approved.\n\nThank you,\n{$companyName}";
$headersC = "From: {$noreplyEmail}\r\n";
@mail($clientEmail, $subjectC, $bodyC, $headersC);
}
if ($submitEmail !== '') {
$subjectS = "Client payment approved";
$bodyS = "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " has been approved by Finance.\n\nRegards,\n{$companyName}";
$headersS = "From: {$noreplyEmail}\r\n";
@mail($submitEmail, $subjectS, $bodyS, $headersS);
}
} catch (Throwable $eN) {}
try {
$dealId = 0; $payAmt = 0.0; $clientIdForLedger = 0;
$qp = $pdo->prepare("SELECT deal_id, amount, user_id FROM payments WHERE id = ? LIMIT 1");
$qp->execute([$pid]);
$prow = $qp->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($prow)) {
$dealId = (int)($prow['deal_id'] ?? 0);
$payAmt = (float)($prow['amount'] ?? 0);
$clientIdForLedger = (int)($prow['user_id'] ?? 0);
}
try { createReceiptForApprovedPayment($pid); } catch (Throwable $e) {}
try {
if (file_exists(__DIR__ . '/includes/charges.php')) { require_once __DIR__ . '/includes/charges.php'; }
if (function_exists('applyPaymentToClientCharges')) { applyPaymentToClientCharges($pdo, (int)$pid, (int)$companyId); }
} catch (Throwable $e) {}
if ($dealId > 0 && $payAmt > 0) {
$curPaid = 0.0; $curBal = null; $total = null;
if (colExists('deals_submit','amount_paid_so_far')) {
$qp2 = $pdo->prepare("SELECT " . (colExists('deals_submit','amount_offered') ? "amount_offered" : "NULL") . " AS amount_offered, amount_paid_so_far" . (colExists('deals_submit','balance_remaining') ? ", balance_remaining" : "") . " FROM deals_submit WHERE id = ? LIMIT 1");
$qp2->execute([$dealId]);
$dr = $qp2->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($dr)) {
$curPaid = (float)($dr['amount_paid_so_far'] ?? 0);
if (isset($dr['balance_remaining'])) { $curBal = (float)$dr['balance_remaining']; }
if (isset($dr['amount_offered'])) { $total = (float)$dr['amount_offered']; }
}
$up1 = $pdo->prepare("UPDATE deals_submit SET amount_paid_so_far = COALESCE(amount_paid_so_far,0) + ? WHERE id = ?");
$up1->execute([$payAmt, $dealId]);
$newBal = $curBal;
if ($newBal !== null) { $newBal = max(0.0, $curBal - $payAmt); }
elseif ($total !== null) { $newBal = max(0.0, ($total - ($curPaid + $payAmt))); }
if ($newBal !== null && colExists('deals_submit','balance_remaining')) {
$up2 = $pdo->prepare("UPDATE deals_submit SET balance_remaining = ? WHERE id = ?");
$up2->execute([$newBal, $dealId]);
}
if (function_exists('updateInvoice')) { updateInvoice($dealId); }
if (function_exists('createInvoiceIfNotExists')) { createInvoiceIfNotExists($dealId); }
if (function_exists('ensureLedgerTable')) { ensureLedgerTable(); }
if ($clientIdForLedger > 0 && function_exists('tableHasColumn')) {
$balAfter = null;
if (colExists('deals_submit','balance_remaining')) {
$qba = $pdo->prepare("SELECT balance_remaining FROM deals_submit WHERE id = ? LIMIT 1");
$qba->execute([$dealId]);
$balAfter = $qba->fetchColumn();
if ($balAfter !== false) { $balAfter = (float)$balAfter; } else { $balAfter = null; }
}
$insLed = $pdo->prepare("INSERT INTO ledger (client_id, deal_id, type, amount, balance_after) VALUES (?, ?, 'payment', ?, ?)");
$insLed->execute([$clientIdForLedger, $dealId, $payAmt, $balAfter]);
}
}
}
} catch (Throwable $eRc) {}
// Update onboarding form status to PAYMENT_VERIFIED for this client
try {
$uId = 0;
if (colExists('payments','user_id')) {
$qu = $pdo->prepare("SELECT user_id FROM payments WHERE id = ?");
$qu->execute([$pid]);
$uId = (int)($qu->fetchColumn() ?: 0);
}
if ($uId > 0) {
$updF = $pdo->prepare("UPDATE client_forms SET status = 'payment_verified', updated_at = NOW() WHERE client_id = ?");
$updF->execute([$uId]);
}
} catch (Throwable $e) {}
try {
$uId = 0;
if (colExists('payments','user_id')) {
$qu = $pdo->prepare("SELECT user_id FROM payments WHERE id = ?");
$qu->execute([$pid]);
$uId = (int)($qu->fetchColumn() ?: 0);
}
if ($uId > 0 && colExists('deals','eligible_for_offer_letter')) {
$cnt = 0;
if (colExists('payments','status') && colExists('payments','user_id')) {
$qc = $pdo->prepare("SELECT COUNT(*) FROM payments WHERE user_id = ? AND status = 'approved'");
$qc->execute([$uId]);
$cnt = (int)($qc->fetchColumn() ?: 0);
}
if ($cnt > 0) {
$upd = $pdo->prepare("UPDATE deals SET eligible_for_offer_letter = 1 WHERE user_id = ?");
$upd->execute([$uId]);
}
}
} catch (Throwable $e) {}
} elseif (isset($_POST['reject'])) {
$reason = trim($_POST['rejection_reason'] ?? '');
$sql = "UPDATE payments SET status = 'rejected'";
$vals = [];
if (colExists('payments','rejection_reason')) { $sql .= ", rejection_reason = ?"; $vals[] = $reason; }
$sql .= " WHERE id = ?";
$vals[] = $pid;
$st = $pdo->prepare($sql);
$st->execute($vals);
$action_msg = 'Payment rejected.';
try {
$qu = $pdo->prepare("SELECT user_id, amount, meta_json FROM payments WHERE id = ?");
$qu->execute([$pid]);
$pr = $qu->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$amt = (float)($pr['amount'] ?? 0);
$meta = [];
if (!empty($pr['meta_json'])) { $meta = json_decode($pr['meta_json'], true) ?: []; }
$submittedBy = (int)($meta['submitted_by_user'] ?? 0);
$clientEmail = ''; $clientName = '';
$submitEmail = ''; $submitName = '';
if ($uId > 0 && colExists('users','email')) {
$se = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$se->execute([$uId]); $rowE = $se->fetch(PDO::FETCH_ASSOC);
if ($rowE) { $clientEmail = (string)($rowE['email'] ?? ''); $clientName = (string)($rowE['display_name'] ?? 'Client'); }
}
if ($submittedBy > 0 && colExists('users','email')) {
$ss = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$ss->execute([$submittedBy]); $rowS = $ss->fetch(PDO::FETCH_ASSOC);
if ($rowS) { $submitEmail = (string)($rowS['email'] ?? ''); $submitName = (string)($rowS['display_name'] ?? ''); }
}
$companyName = function_exists('getSetting') ? getSetting('company_name', 'Aiben Properties') : 'Aiben Properties';
$noreplyEmail = function_exists('getSetting') ? getSetting('noreply_email', 'no-reply@aibenproperties.local') : 'no-reply@aibenproperties.local';
if ($clientEmail !== '') {
$subjectC = "Payment rejected";
$bodyC = "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " was rejected. Please contact support for details.\n\nRegards,\n{$companyName}";
$headersC = "From: {$noreplyEmail}\r\n";
@mail($clientEmail, $subjectC, $bodyC, $headersC);
}
if ($submitEmail !== '') {
$subjectS = "Client payment rejected";
$bodyS = "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " was rejected by Finance.\n\nRegards,\n{$companyName}";
$headersS = "From: {$noreplyEmail}\r\n";
@mail($submitEmail, $subjectS, $bodyS, $headersS);
}
} catch (Throwable $eN2) {}
} elseif (isset($_POST['duplicate'])) {
$st = $pdo->prepare("UPDATE payments SET status = 'duplicate' WHERE id = ?");
$st->execute([$pid]);
$action_msg = 'Payment marked as duplicate.';
}
} catch (Throwable $e) {
$action_msg = 'Action failed.';
}
}
}
$tx_response_sent = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['tx_action'])) {
header('Content-Type: application/json');
if (!$canApprovePayments) { echo json_encode(['success' => false, 'message' => 'Not allowed']); exit; }
$tid = isset($_POST['tx_id']) ? (int)$_POST['tx_id'] : 0;
$act = $_POST['tx_action'] ?? '';
try {
if ($tid > 0 && in_array($act, ['approve','reject'], true)) {
if ($act === 'approve') {
$stmt = $pdo->prepare("UPDATE transactions SET status = 'approved' WHERE id = ?");
$stmt->execute([$tid]);
try {
if (colExists('transactions','verified_at')) {
$pdo->prepare("UPDATE transactions SET verified_at = NOW() WHERE id = ? AND verified_at IS NULL")->execute([$tid]);
}
if (colExists('transactions','verified_by')) {
$pdo->prepare("UPDATE transactions SET verified_by = ? WHERE id = ? AND (verified_by IS NULL OR verified_by = 0)")->execute([$userId, $tid]);
}
} catch (Throwable $eTMeta) {}
try {
$uStmt = $pdo->prepare("SELECT user_id, transaction_type FROM transactions WHERE id = ?");
$uStmt->execute([$tid]);
$rowU = $uStmt->fetch(PDO::FETCH_ASSOC);
$uId = (int)($rowU['user_id'] ?? 0);
$tType = (string)($rowU['transaction_type'] ?? '');
if ($uId > 0 && $tType === 'form_fee') {
$cf = $pdo->prepare("UPDATE client_forms SET status = 'payment_verified', updated_at = NOW() WHERE client_id = ?");
$cf->execute([$uId]);
}
} catch (Throwable $e) {}
try {
if ($pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0 && colExists('payments','reference') && colExists('payments','status')) {
$refLike = '%-TX-' . (string)$tid;
$sqlP = "UPDATE payments SET status = 'approved'";
$valsP = [];
if (colExists('payments','approved_by')) { $sqlP .= ", approved_by = ?"; $valsP[] = $userId; }
if (colExists('payments','approval_date')) { $sqlP .= ", approval_date = NOW()"; }
$sqlP .= " WHERE reference LIKE ? AND LOWER(TRIM(status)) IN " . $queueStatusesSql;
$valsP[] = $refLike;
$pdo->prepare($sqlP)->execute($valsP);
}
} catch (Throwable $eSyncPm) {}
} else {
$stmt = $pdo->prepare("UPDATE transactions SET status = 'rejected' WHERE id = ?");
$stmt->execute([$tid]);
}
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid request']);
}
} catch (Throwable $e) {
echo json_encode(['success' => false, 'message' => 'DB error']);
}
$tx_response_sent = true;
}
if ($tx_response_sent) { exit; }
$pm_response_sent = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['pm_action'])) {
header('Content-Type: application/json');
if (!$canApprovePayments) { echo json_encode(['success' => false, 'message' => 'Not allowed']); exit; }
$pid = isset($_POST['pm_id']) ? (int)$_POST['pm_id'] : 0;
$act = $_POST['pm_action'] ?? '';
try {
if ($pid > 0 && in_array($act, ['approve','reject','reverse'], true)) {
$oldRow = null;
$newRow = null;
$revId = 0;
$pdo->beginTransaction();
$lock = $pdo->prepare("SELECT * FROM payments WHERE id = ? FOR UPDATE");
$lock->execute([$pid]);
$oldRow = $lock->fetch(PDO::FETCH_ASSOC) ?: null;
if (!$oldRow) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Not found']);
$pm_response_sent = true;
} else {
$statusLower = strtolower(trim((string)($oldRow['status'] ?? '')));
$isReversed = (int)($oldRow['is_reversed'] ?? 0);
if ($isReversed === 1) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Already reversed']);
$pm_response_sent = true;
} else {
if ($act === 'approve') {
if (in_array($statusLower, ['approved','paid','reversed','duplicate'], true)) {
$pdo->rollBack();
echo json_encode(['success' => true, 'message' => 'Already approved']);
$pm_response_sent = true;
} else {
try {
$receiptColDup = null;
if (colExists('payments','receipt_file')) { $receiptColDup = 'receipt_file'; }
elseif (colExists('payments','proof_file')) { $receiptColDup = 'proof_file'; }
$rcpDup = ($receiptColDup && isset($oldRow[$receiptColDup])) ? trim((string)$oldRow[$receiptColDup]) : '';
if ($receiptColDup && $rcpDup !== '') {
$final = function_exists('kpiPaymentFinalizedStatuses') ? array_map('strtolower', (array)kpiPaymentFinalizedStatuses()) : ['verified','approved','paid','completed','success'];
$ph = implode(',', array_fill(0, count($final), '?'));
$qDup = "SELECT id FROM payments WHERE id <> ? AND user_id = ? AND {$receiptColDup} = ? AND LOWER(TRIM(status)) IN ($ph) ORDER BY id DESC LIMIT 1";
$pDup = [$pid, (int)($oldRow['user_id'] ?? 0), $rcpDup];
foreach ($final as $s) { $pDup[] = strtolower(trim((string)$s)); }
$stDup = $pdo->prepare($qDup);
$stDup->execute($pDup);
$dupId = (int)($stDup->fetchColumn() ?: 0);
if ($dupId > 0) {
$pdo->prepare("UPDATE payments SET status = 'duplicate' WHERE id = ?")->execute([$pid]);
try {
if ($pdo->query("SHOW TABLES LIKE 'transactions'")->rowCount() > 0 && colExists('payments','reference') && colExists('transactions','status')) {
$ref0 = (string)($oldRow['reference'] ?? '');
$txId = 0;
if ($ref0 !== '' && preg_match('/-TX-(\\d+)/i', $ref0, $mTx)) { $txId = (int)($mTx[1] ?? 0); }
if ($txId > 0) {
$pdo->prepare("UPDATE transactions SET status = 'approved' WHERE id = ?")->execute([$txId]);
if (colExists('transactions','verified_at')) { $pdo->prepare("UPDATE transactions SET verified_at = NOW() WHERE id = ? AND verified_at IS NULL")->execute([$txId]); }
if (colExists('transactions','verified_by')) { $pdo->prepare("UPDATE transactions SET verified_by = ? WHERE id = ? AND (verified_by IS NULL OR verified_by = 0)")->execute([$userId, $txId]); }
}
}
} catch (Throwable $eTxDup) {}
$selNew = $pdo->prepare("SELECT * FROM payments WHERE id = ? LIMIT 1");
$selNew->execute([$pid]);
$newRow = $selNew->fetch(PDO::FETCH_ASSOC) ?: null;
$pdo->commit();
$pm_response_sent = true;
echo json_encode(['success' => true, 'message' => 'This receipt was already approved as Payment #' . $dupId . '. Marked this entry as duplicate so it will not count twice.']);
exit;
}
}
} catch (Throwable $eDup) {}
try {
$dealId = 0; $uid = 0; $rcp = ''; $dt = '';
$receiptCol = 'receipt_file';
if (!colExists('payments','receipt_file') && colExists('payments','proof_file')) { $receiptCol = 'proof_file'; }
$dateCol = colExists('payments','created_at') ? 'created_at' : (colExists('payments','date') ? 'date' : null);
$sel = "deal_id, user_id, {$receiptCol} AS receipt";
if ($dateCol) { $sel .= ", {$dateCol} AS created_at"; }
if (colExists('payments','reference')) { $sel .= ", reference"; }
if (colExists('payments','allocation_id')) { $sel .= ", allocation_id"; }
if (colExists('payments','property_id')) { $sel .= ", property_id"; }
$qp = $pdo->prepare("SELECT {$sel} FROM payments WHERE id = ? LIMIT 1");
$qp->execute([$pid]);
$rowP = $qp->fetch(PDO::FETCH_ASSOC) ?: [];
$dealId = (int)($rowP['deal_id'] ?? 0);
$uid = (int)($rowP['user_id'] ?? 0);
$rcp = (string)($rowP['receipt'] ?? '');
$dt = (string)($rowP['created_at'] ?? '');
$allocId = (int)($rowP['allocation_id'] ?? 0);
$propId = (int)($rowP['property_id'] ?? 0);
if ($dealId <= 0) {
$found = 0;
if ($found <= 0 && $allocId > 0 && colExists('deals','allocation_id')) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE allocation_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$allocId]); $found = (int)($qd->fetchColumn() ?: 0);
}
if ($found <= 0 && $propId > 0 && colExists('deals','property_id')) {
if ($uid > 0 && colExists('deals','user_id')) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND property_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid, $propId]); $found = (int)($qd->fetchColumn() ?: 0);
} else {
$qd = $pdo->prepare("SELECT id FROM deals WHERE property_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$propId]); $found = (int)($qd->fetchColumn() ?: 0);
}
}
if ($rcp !== '') {
if (colExists('deals','receipt_file')) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE receipt_file = ? LIMIT 1");
$qd->execute([$rcp]); $found = (int)($qd->fetchColumn() ?: 0);
if ($found <= 0) {
$base = basename($rcp);
if ($base !== '') {
$qd = $pdo->prepare("SELECT id FROM deals WHERE receipt_file LIKE ? LIMIT 1");
$qd->execute(['%'.$base]); $found = (int)($qd->fetchColumn() ?: 0);
}
}
} elseif (colExists('deals','proof_file')) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE proof_file = ? LIMIT 1");
$qd->execute([$rcp]); $found = (int)($qd->fetchColumn() ?: 0);
if ($found <= 0) {
$base = basename($rcp);
if ($base !== '') {
$qd = $pdo->prepare("SELECT id FROM deals WHERE proof_file LIKE ? LIMIT 1");
$qd->execute(['%'.$base]); $found = (int)($qd->fetchColumn() ?: 0);
}
}
}
}
if ($found <= 0 && $uid > 0 && colExists('deals','user_id')) {
if ($dt !== '' && colExists('deals','created_at')) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qd->execute([$uid, $dt, $dt]); $found = (int)($qd->fetchColumn() ?: 0);
}
if ($found <= 0) {
$qd = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid]); $found = (int)($qd->fetchColumn() ?: 0);
}
}
if ($found <= 0 && !empty($rowP['reference']) && colExists('deals','created_at')) {
$ref = (string)$rowP['reference'];
$ts = null;
if (preg_match('/(\d{14})/', $ref, $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$w = $ts->format('Y-m-d H:i:s');
if ($uid > 0 && colExists('deals','user_id')) {
$qrf = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$uid, $w, $w]); $found = (int)($qrf->fetchColumn() ?: 0);
} else {
$qrf = $pdo->prepare("SELECT id FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$w, $w]); $found = (int)($qrf->fetchColumn() ?: 0);
}
}
}
if ($found > 0 && colExists('payments','deal_id')) {
$up = $pdo->prepare("UPDATE payments SET deal_id = ? WHERE id = ?");
$up->execute([$found, $pid]);
}
}
} catch (Throwable $eDealLink) {}
$sql = "UPDATE payments SET status = 'approved'";
$vals = [];
if (colExists('payments','approved_by')) { $sql .= ", approved_by = ?"; $vals[] = $userId; }
if (colExists('payments','approval_date')) { $sql .= ", approval_date = NOW()"; }
elseif (colExists('payments','approved_at')) { $sql .= ", approved_at = NOW()"; }
if (colExists('payments','verified_by')) { $sql .= ", verified_by = ?"; $vals[] = $userId; }
if (colExists('payments','verified_at')) { $sql .= ", verified_at = NOW()"; }
$sql .= " WHERE id = ?";
$vals[] = $pid;
$st = $pdo->prepare($sql);
$st->execute($vals);
$selNew = $pdo->prepare("SELECT * FROM payments WHERE id = ? LIMIT 1");
$selNew->execute([$pid]);
$newRow = $selNew->fetch(PDO::FETCH_ASSOC) ?: null;
$newStatusLower = strtolower(trim((string)($newRow['status'] ?? '')));
if ($newStatusLower !== 'approved') {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Approval failed: payment is still pending. Please refresh and try again.']);
$pm_response_sent = true;
} else {
try {
if ($pdo->query("SHOW TABLES LIKE 'transactions'")->rowCount() > 0 && colExists('payments','reference') && colExists('transactions','status')) {
$ref0 = (string)($newRow['reference'] ?? ($oldRow['reference'] ?? ''));
$txId = 0;
if ($ref0 !== '' && preg_match('/-TX-(\\d+)/i', $ref0, $mTx)) { $txId = (int)($mTx[1] ?? 0); }
if ($txId > 0) {
$pdo->prepare("UPDATE transactions SET status = 'approved' WHERE id = ?")->execute([$txId]);
if (colExists('transactions','verified_at')) { $pdo->prepare("UPDATE transactions SET verified_at = NOW() WHERE id = ? AND verified_at IS NULL")->execute([$txId]); }
if (colExists('transactions','verified_by')) { $pdo->prepare("UPDATE transactions SET verified_by = ? WHERE id = ? AND (verified_by IS NULL OR verified_by = 0)")->execute([$userId, $txId]); }
}
}
} catch (Throwable $eTxSync) {}
$pdo->commit();
if (function_exists('log_audit')) { log_audit('approve', 'payments', $pid, $oldRow, $newRow); }
try {
$qu = $pdo->prepare("SELECT user_id, reference FROM payments WHERE id = ?");
$qu->execute([$pid]);
$pr = $qu->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$ref = strtolower((string)($pr['reference'] ?? ''));
if ($uId > 0 && strpos($ref, 'form_fee') === 0) {
$updF = $pdo->prepare("UPDATE client_forms SET status = 'payment_verified', updated_at = NOW() WHERE client_id = ?");
$updF->execute([$uId]);
}
} catch (Throwable $e) {}
try {
$qu = $pdo->prepare("SELECT user_id, amount, meta_json FROM payments WHERE id = ?");
$qu->execute([$pid]);
$pr = $qu->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$amt = (float)($pr['amount'] ?? 0);
$meta = [];
if (!empty($pr['meta_json'])) { $meta = json_decode($pr['meta_json'], true) ?: []; }
$submittedBy = (int)($meta['submitted_by_user'] ?? 0);
$clientEmail = ''; $clientName = '';
$submitEmail = ''; $submitName = '';
if ($uId > 0 && colExists('users','email')) {
$se = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$se->execute([$uId]); $rowE = $se->fetch(PDO::FETCH_ASSOC);
if ($rowE) { $clientEmail = (string)($rowE['email'] ?? ''); $clientName = (string)($rowE['display_name'] ?? 'Client'); }
}
if ($submittedBy > 0 && colExists('users','email')) {
$ss = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$ss->execute([$submittedBy]); $rowS = $ss->fetch(PDO::FETCH_ASSOC);
if ($rowS) { $submitEmail = (string)($rowS['email'] ?? ''); $submitName = (string)($rowS['display_name'] ?? ''); }
}
$companyName = function_exists('getSetting') ? getSetting('company_name', 'Aiben Properties') : 'Aiben Properties';
$noreplyEmail = function_exists('getSetting') ? getSetting('noreply_email', 'no-reply@aibenproperties.local') : 'no-reply@aibenproperties.local';
if ($clientEmail !== '') {
$subjectC = "Payment approved";
$bodyC = "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " has been approved.\n\nThank you,\n{$companyName}";
$headersC = "From: {$noreplyEmail}\r\n";
@mail($clientEmail, $subjectC, $bodyC, $headersC);
}
if ($submitEmail !== '') {
$subjectS = "Client payment approved";
$bodyS = "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " has been approved by Finance.\n\nRegards,\n{$companyName}";
$headersS = "From: {$noreplyEmail}\r\n";
@mail($submitEmail, $subjectS, $bodyS, $headersS);
}
} catch (Throwable $eN) {}
$pm_response_sent = true;
echo json_encode(['success' => true]);
}
}
} else {
if ($act === 'reject') {
if (in_array($statusLower, ['approved','paid','reversed'], true)) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Status locked']);
$pm_response_sent = true;
} else {
$st = $pdo->prepare("UPDATE payments SET status = 'rejected' WHERE id = ?");
$st->execute([$pid]);
$selNew = $pdo->prepare("SELECT * FROM payments WHERE id = ? LIMIT 1");
$selNew->execute([$pid]);
$newRow = $selNew->fetch(PDO::FETCH_ASSOC) ?: null;
$pdo->commit();
if (function_exists('log_audit')) { log_audit('reject', 'payments', $pid, $oldRow, $newRow); }
try {
$qu = $pdo->prepare("SELECT user_id, amount, meta_json FROM payments WHERE id = ?");
$qu->execute([$pid]);
$pr = $qu->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$amt = (float)($pr['amount'] ?? 0);
$meta = [];
if (!empty($pr['meta_json'])) { $meta = json_decode($pr['meta_json'], true) ?: []; }
$submittedBy = (int)($meta['submitted_by_user'] ?? 0);
$clientEmail = ''; $clientName = '';
$submitEmail = ''; $submitName = '';
if ($uId > 0 && colExists('users','email')) {
$se = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$se->execute([$uId]); $rowE = $se->fetch(PDO::FETCH_ASSOC);
if ($rowE) { $clientEmail = (string)($rowE['email'] ?? ''); $clientName = (string)($rowE['display_name'] ?? 'Client'); }
}
if ($submittedBy > 0 && colExists('users','email')) {
$ss = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$ss->execute([$submittedBy]); $rowS = $ss->fetch(PDO::FETCH_ASSOC);
if ($rowS) { $submitEmail = (string)($rowS['email'] ?? ''); $submitName = (string)($rowS['display_name'] ?? ''); }
}
$companyName = function_exists('getSetting') ? getSetting('company_name', 'Aiben Properties') : 'Aiben Properties';
$noreplyEmail = function_exists('getSetting') ? getSetting('noreply_email', 'no-reply@aibenproperties.local') : 'no-reply@aibenproperties.local';
if ($clientEmail !== '') {
$subjectC = "Payment rejected";
$bodyC = "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " was rejected. Please contact support for details.\n\nRegards,\n{$companyName}";
$headersC = "From: {$noreplyEmail}\r\n";
@mail($clientEmail, $subjectC, $bodyC, $headersC);
}
if ($submitEmail !== '') {
$subjectS = "Client payment rejected";
$bodyS = "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " was rejected by Finance.\n\nRegards,\n{$companyName}";
$headersS = "From: {$noreplyEmail}\r\n";
@mail($submitEmail, $subjectS, $bodyS, $headersS);
}
} catch (Throwable $eN2) {}
$pm_response_sent = true;
echo json_encode(['success' => true]);
}
} else {
$amt0 = (float)($oldRow['amount'] ?? 0);
$now = date('Y-m-d H:i:s');
$cols = [];
$vals = [];
$add = function($col, $val) use (&$cols, &$vals) { $cols[] = $col; $vals[] = $val; };
if (colExists('payments','user_id')) { $add('user_id', (int)($oldRow['user_id'] ?? 0)); }
if (colExists('payments','company_id') && array_key_exists('company_id', $oldRow)) { $add('company_id', $oldRow['company_id']); }
if (colExists('payments','deal_id') && array_key_exists('deal_id', $oldRow)) { $add('deal_id', $oldRow['deal_id']); }
if (colExists('payments','allocation_id') && array_key_exists('allocation_id', $oldRow)) { $add('allocation_id', $oldRow['allocation_id']); }
if (colExists('payments','property_id') && array_key_exists('property_id', $oldRow)) { $add('property_id', $oldRow['property_id']); }
if (colExists('payments','invoice_id') && array_key_exists('invoice_id', $oldRow)) { $add('invoice_id', $oldRow['invoice_id']); }
if (colExists('payments','amount')) { $add('amount', 0 - abs($amt0)); }
if (colExists('payments','status')) { $add('status', 'reversed'); }
if (colExists('payments','reference')) { $add('reference', 'reversal_of_' . $pid); }
if (colExists('payments','payment_method')) { $add('payment_method', 'reversal'); }
elseif (colExists('payments','method')) { $add('method', 'reversal'); }
if (colExists('payments','meta_json')) {
$meta = [];
if (!empty($oldRow['meta_json'])) {
try { $meta = json_decode((string)$oldRow['meta_json'], true) ?: []; } catch (Throwable $eM) { $meta = []; }
}
$meta['reversal_of_id'] = $pid;
$meta['reversed_by'] = $userId;
$meta['reversed_at'] = $now;
$add('meta_json', json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
if (colExists('payments','created_at')) { $add('created_at', $now); }
if (colExists('payments','updated_at')) { $add('updated_at', $now); }
if (count($cols) < 2) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Cannot reverse']);
$pm_response_sent = true;
} else {
$ins = "INSERT INTO payments (" . implode(',', $cols) . ") VALUES (" . implode(',', array_fill(0, count($cols), '?')) . ")";
$pdo->prepare($ins)->execute($vals);
$revId = (int)$pdo->lastInsertId();
$sets = [];
$p = [];
if (colExists('payments','is_reversed')) { $sets[] = "is_reversed = 1"; }
if (colExists('payments','reversed_by')) { $sets[] = "reversed_by = ?"; $p[] = $userId; }
if (colExists('payments','reversed_at')) { $sets[] = "reversed_at = NOW()"; }
if (colExists('payments','reversal_record_id')) { $sets[] = "reversal_record_id = ?"; $p[] = $revId; }
if (colExists('payments','status')) { $sets[] = "status = 'reversed'"; }
$p[] = $pid;
$pdo->prepare("UPDATE payments SET " . implode(', ', $sets) . " WHERE id = ?")->execute($p);
$selNew = $pdo->prepare("SELECT * FROM payments WHERE id = ? LIMIT 1");
$selNew->execute([$pid]);
$newRow = $selNew->fetch(PDO::FETCH_ASSOC) ?: null;
$pdo->commit();
if (function_exists('log_audit')) {
log_audit('reverse', 'payments', $pid, $oldRow, $newRow);
if ($revId > 0) { log_audit('create', 'payments', $revId, null, ['reversal_of_id' => $pid]); }
}
$pm_response_sent = true;
echo json_encode(['success' => true]);
}
}
}
}
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid request']);
}
} catch (Throwable $e) {
try { if ($pdo && $pdo->inTransaction()) { $pdo->rollBack(); } } catch (Throwable $e2) {}
echo json_encode(['success' => false, 'message' => 'DB error']);
}
$pm_response_sent = true;
}
if ($pm_response_sent) { exit; }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['modal_payload'])) {
header('Content-Type: application/json');
$pid = isset($_POST['payment_id']) ? (int)$_POST['payment_id'] : 0;
$res = ['success' => false];
try {
if ($pid > 0) {
$row = [];
if (colExists('payments','deal_id')) {
$st = $pdo->prepare("SELECT p.*,
d.meta_json AS d_meta_json,
d.marketer_name AS d_marketer_name,
d.deal_source AS d_deal_source,
d.payment_plan AS d_payment_plan,
d.project_desc AS d_project_desc,
d.property_estate AS d_property_estate,
" . (colExists('deals','marketer_bank') ? "d.marketer_bank" : "NULL") . " AS d_marketer_bank,
" . (colExists('deals','agent_bank') ? "d.agent_bank" : "NULL") . " AS d_agent_bank
FROM payments p
LEFT JOIN deals d ON p.deal_id = d.id
WHERE p.id = ?
LIMIT 1");
$st->execute([$pid]); $row = $st->fetch(PDO::FETCH_ASSOC) ?: [];
} else {
$st = $pdo->prepare("SELECT * FROM payments WHERE id = ? LIMIT 1");
$st->execute([$pid]); $row = $st->fetch(PDO::FETCH_ASSOC) ?: [];
}
if (!empty($row)) {
$meta = [];
$dealMeta = [];
if (isset($row['meta_json']) && is_string($row['meta_json']) && $row['meta_json'] !== '') {
$m = json_decode($row['meta_json'], true); if (is_array($m)) { $meta = $m; }
}
if (empty($dealMeta) && isset($row['d_meta_json']) && is_string($row['d_meta_json']) && $row['d_meta_json'] !== '') {
$m2 = json_decode($row['d_meta_json'], true); if (is_array($m2)) { $dealMeta = $m2; }
}
$metaEff = is_array($dealMeta) ? $dealMeta : [];
if (is_array($meta) && !empty($meta)) { foreach ($meta as $k => $v) { $metaEff[$k] = $v; } }
$clientId = (int)($row['user_id'] ?? 0);
if ($clientId <= 0 && colExists('payments','client_id')) { $clientId = (int)($row['client_id'] ?? 0); }
if ($clientId <= 0 && isset($metaEff['client_id'])) { $clientId = (int)($metaEff['client_id'] ?? 0); }
if ($clientId <= 0 && isset($metaEff['user_id'])) { $clientId = (int)($metaEff['user_id'] ?? 0); }
$clientEmailView = (string)($row['client_email'] ?? ($metaEff['client_email'] ?? ''));
$clientNameMeta = (string)($row['client_name'] ?? ($metaEff['client_name'] ?? ''));
if (($clientNameMeta === '' || $clientEmailView === '') && $clientId > 0 && colExists('users','id')) {
try {
$selName = colExists('users','name') ? "name" : (colExists('users','full_name') ? "full_name" : (colExists('users','first_name') && colExists('users','last_name') ? "CONCAT(first_name,' ',last_name)" : "''"));
$selEmail = colExists('users','email') ? "email" : "''";
$qu = $pdo->prepare("SELECT {$selName} AS nm, {$selEmail} AS em FROM users WHERE id = ? LIMIT 1");
$qu->execute([$clientId]);
$urow = $qu->fetch(PDO::FETCH_ASSOC) ?: [];
if ($clientNameMeta === '' && !empty($urow['nm'])) { $clientNameMeta = (string)$urow['nm']; }
if ($clientEmailView === '' && !empty($urow['em'])) { $clientEmailView = (string)$urow['em']; }
} catch (Throwable $e0) {}
}
if ($clientNameMeta === '' && $clientEmailView !== '' && colExists('users','email')) {
try { $qu = $pdo->prepare("SELECT " . (colExists('users','name') ? "name" : (colExists('users','full_name') ? "full_name" : (colExists('users','first_name') && colExists('users','last_name') ? "CONCAT(first_name,' ',last_name)" : "''"))) . " AS nm FROM users WHERE email = ? LIMIT 1"); $qu->execute([$clientEmailView]); $nm = (string)($qu->fetchColumn() ?: ''); if ($nm !== '') { $clientNameMeta = $nm; } } catch (Throwable $e0) {}
}
$cfjson = '';
if ($clientId > 0 && colExists('client_forms','client_id') && colExists('client_forms','form_data')) {
try {
$qf = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY updated_at DESC, created_at DESC LIMIT 1");
$qf->execute([$clientId]);
$cfjson = (string)($qf->fetchColumn() ?: '');
} catch (Throwable $e) {}
}
if ($cfjson === '' && $clientEmailView !== '' && colExists('client_forms','form_data')) {
try { $qf = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1"); $qf->execute(['%"email":"'.$clientEmailView.'"%']); $cfjson = (string)($qf->fetchColumn() ?: ''); } catch (Throwable $e) {}
if ($cfjson === '') { try { $qf = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1"); $qf->execute(['%"Client Email (for dashboard)":"'.$clientEmailView.'"%']); $cfjson = (string)($qf->fetchColumn() ?: ''); } catch (Throwable $e) {} }
}
$cf = $cfjson ? (json_decode($cfjson, true) ?: []) : [];
$proj = (string)($meta['project_desc'] ?? ($dealMeta['project_desc'] ?? ($meta['property'] ?? ($meta['property_estate'] ?? ($dealMeta['property_estate'] ?? '')))));
if ($proj === '' && !empty($row['d_project_desc'])) { $proj = (string)$row['d_project_desc']; }
if ($proj === '' && !empty($row['project_desc'])) { $proj = (string)$row['project_desc']; }
if ($proj === '' && isset($row['d_property_estate']) && (string)$row['d_property_estate'] !== '') { $proj = (string)$row['d_property_estate']; }
if ($proj === '' && isset($row['property_estate']) && (string)$row['property_estate'] !== '') { $proj = (string)$row['property_estate']; }
if ($proj === '' && isset($row['property']) && (string)$row['property'] !== '') { $proj = (string)$row['property']; }
if ($proj === '' && $cf) {
$keys = ['project_desc','Project / Property Description','Project / Property','Property / Estate','Property/Estate','Estate / Property','project_or_property','preferred_property','preferred property','estate_name','property_estate','estate_property','property','estate','project_property','project','property_title','property_name','project_title','Project Name'];
foreach ($keys as $k){ if (isset($cf[$k]) && trim((string)$cf[$k])!==''){ $proj = trim((string)$cf[$k]); break; } }
}
$dealSource = (string)($meta['deal_source'] ?? ($dealMeta['deal_source'] ?? ($row['deal_source'] ?? ($row['d_deal_source'] ?? ''))));
if ($dealSource === '' && $cf) {
$keys = ['deal_source','Deal Source','source','deal_source_type','lead_source','referral_source','referral source','referrer','referral','ref','submitted_by_role','Submitted By Role','department','team','channel','source_of_deal','source of deal','how did you hear about us','how you heard about us','where did you hear about us','where heard'];
foreach ($keys as $k){ if (isset($cf[$k]) && trim((string)$cf[$k])!==''){ $dealSource = trim((string)$cf[$k]); break; } }
}
if ($dealSource === '' || $dealSource === '-') { $dealSource = 'Marketing'; }
$planType = (string)($meta['plan_type'] ?? ($dealMeta['plan_type'] ?? ($row['payment_plan'] ?? ($row['d_payment_plan'] ?? ''))));
if ($planType === '' && $cf) {
$keys = ['plan_type','Payment Plan','plan','payment_plan','plan_name','installment_plan','installment','payment_mode','payment mode'];
foreach ($keys as $k){ if (isset($cf[$k]) && trim((string)$cf[$k])!==''){ $planType = trim((string)$cf[$k]); break; } }
}
$marketerName = (string)($row['marketer_name'] ?? ($meta['marketer_name'] ?? ($dealMeta['marketer_name'] ?? ($row['d_marketer_name'] ?? ''))));
if ($marketerName === '' && $cf) {
$keys = ['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','Marketer’s Name','Marketer\'s Name','marketer','sales_agent','sales_rep','contact_rep','uploaded_by_name','submitted_by_name','uploaded_by','created_by_name','agent_name','marketer_full_name','marketer full name'];
foreach ($keys as $k){ if (isset($cf[$k]) && trim((string)$cf[$k])!==''){ $marketerName = trim((string)$cf[$k]); break; } }
if ($marketerName === '') {
foreach ($cf as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false || strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false) { $marketerName = trim((string)$vv); break; }
}
}
}
$statusNotes = (string)($meta['client_payment_status'] ?? ($meta['notes'] ?? ($row['notes'] ?? '')));
if ($statusNotes === '' && $cf) {
if (isset($cf['client_payment_status']) && trim((string)$cf['client_payment_status']) !== '') { $statusNotes = trim((string)$cf['client_payment_status']); }
elseif (isset($cf['Client Payment Status / Notes']) && trim((string)$cf['Client Payment Status / Notes']) !== '') { $statusNotes = trim((string)$cf['Client Payment Status / Notes']); }
elseif (isset($cf['Client Payment Status']) && trim((string)$cf['Client Payment Status']) !== '') { $statusNotes = trim((string)$cf['Client Payment Status']); }
elseif (isset($cf['notes']) && trim((string)$cf['notes']) !== '') { $statusNotes = trim((string)$cf['notes']); }
}
$mkRaw = isset($metaEff['marketer_bank']) && is_array($metaEff['marketer_bank']) ? $metaEff['marketer_bank'] : (isset($meta['marketer_bank']) && is_array($meta['marketer_bank']) ? $meta['marketer_bank'] : []);
$agRaw = isset($metaEff['agent_bank']) && is_array($metaEff['agent_bank']) ? $metaEff['agent_bank'] : (isset($meta['agent_bank']) && is_array($meta['agent_bank']) ? $meta['agent_bank'] : []);
if (empty($mkRaw) && isset($row['d_marketer_bank']) && is_string($row['d_marketer_bank']) && trim((string)$row['d_marketer_bank']) !== '') {
try { $tmp = json_decode((string)$row['d_marketer_bank'], true); if (is_array($tmp)) { $mkRaw = $tmp; } } catch (Throwable $e) {}
}
if (empty($agRaw) && isset($row['d_agent_bank']) && is_string($row['d_agent_bank']) && trim((string)$row['d_agent_bank']) !== '') {
try { $tmp = json_decode((string)$row['d_agent_bank'], true); if (is_array($tmp)) { $agRaw = $tmp; } } catch (Throwable $e) {}
}
$mkAccNo = (string)($mkRaw['acc_no'] ?? ($mkRaw['account_number'] ?? ($mkRaw['accountNumber'] ?? ($mkRaw['accNo'] ?? ($metaEff['marketer_account_number'] ?? ($metaEff['marketer_acc_no'] ?? ($metaEff['marketer_acc_number'] ?? ($meta['marketer_account_number'] ?? ($meta['marketer_acc_no'] ?? ($meta['marketer_acc_number'] ?? ''))))))))));
$mkBankName = (string)($mkRaw['bank'] ?? ($mkRaw['bank_name'] ?? ($mkRaw['bankName'] ?? ($metaEff['marketer_bank_name'] ?? ($metaEff['marketer_bank'] ?? ($meta['marketer_bank_name'] ?? ($meta['marketer_bank'] ?? '')))))));
$mkAccName = (string)($mkRaw['acc_name'] ?? ($mkRaw['account_name'] ?? ($mkRaw['accountName'] ?? ($metaEff['marketer_account_name'] ?? ($metaEff['marketer_acc_name'] ?? ($meta['marketer_account_name'] ?? ($meta['marketer_acc_name'] ?? '')))))));
$agAccNo = (string)($agRaw['acc_no'] ?? ($agRaw['account_number'] ?? ($agRaw['accountNumber'] ?? ($agRaw['accNo'] ?? ($metaEff['agent_account_number'] ?? ($metaEff['agent_acc_no'] ?? ($metaEff['agent_acc_number'] ?? ($meta['agent_account_number'] ?? ($meta['agent_acc_no'] ?? ($meta['agent_acc_number'] ?? ''))))))))));
$agBankName = (string)($agRaw['bank'] ?? ($agRaw['bank_name'] ?? ($agRaw['bankName'] ?? ($metaEff['agent_bank_name'] ?? ($metaEff['agent_bank'] ?? ($meta['agent_bank_name'] ?? ($meta['agent_bank'] ?? '')))))));
$agAccName = (string)($agRaw['acc_name'] ?? ($agRaw['account_name'] ?? ($agRaw['accountName'] ?? ($metaEff['agent_account_name'] ?? ($metaEff['agent_acc_name'] ?? ($meta['agent_account_name'] ?? ($meta['agent_acc_name'] ?? '')))))));
if (((trim($mkAccNo) === '' && trim($mkBankName) === '' && trim($mkAccName) === '') || (trim($agAccNo) === '' && trim($agBankName) === '' && trim($agAccName) === '')) && (colExists('deals_submit','marketer_bank') || colExists('deals_submit','agent_bank'))) {
try {
$dsIdLocal = 0;
if (isset($metaEff['deal_submit_id']) && (int)$metaEff['deal_submit_id'] > 0) { $dsIdLocal = (int)$metaEff['deal_submit_id']; }
if ($dsIdLocal <= 0) {
$refTry = (string)($row['reference'] ?? ($row['reference_number'] ?? ($row['ref'] ?? '')));
if ($refTry !== '' && preg_match('/-(\d+)\s*$/', $refTry, $mm)) { $dsIdLocal = (int)($mm[1] ?? 0); }
}
$dsBank = [];
if ($dsIdLocal > 0 && colExists('deals_submit','id')) {
$qs = $pdo->prepare("SELECT " . (colExists('deals_submit','marketer_bank') ? "marketer_bank" : "NULL") . " AS marketer_bank, " . (colExists('deals_submit','agent_bank') ? "agent_bank" : "NULL") . " AS agent_bank FROM deals_submit WHERE id = ? LIMIT 1");
$qs->execute([$dsIdLocal]);
$dsBank = $qs->fetch(PDO::FETCH_ASSOC) ?: [];
} elseif ($clientId > 0 && colExists('deals_submit','user_id')) {
$qs = $pdo->prepare("SELECT " . (colExists('deals_submit','marketer_bank') ? "marketer_bank" : "NULL") . " AS marketer_bank, " . (colExists('deals_submit','agent_bank') ? "agent_bank" : "NULL") . " AS agent_bank FROM deals_submit WHERE user_id = ? ORDER BY created_at DESC, id DESC LIMIT 1");
$qs->execute([$clientId]);
$dsBank = $qs->fetch(PDO::FETCH_ASSOC) ?: [];
}
if (!empty($dsBank)) {
if ((trim($mkAccNo) === '' && trim($mkBankName) === '' && trim($mkAccName) === '') && !empty($dsBank['marketer_bank'])) {
$tmp = is_string($dsBank['marketer_bank']) ? (json_decode((string)$dsBank['marketer_bank'], true) ?: []) : (is_array($dsBank['marketer_bank']) ? $dsBank['marketer_bank'] : []);
if (is_array($tmp)) {
$mkAccNo = (string)($tmp['acc_no'] ?? ($tmp['account_number'] ?? ($tmp['accountNumber'] ?? ($tmp['accNo'] ?? $mkAccNo))));
$mkBankName = (string)($tmp['bank'] ?? ($tmp['bank_name'] ?? ($tmp['bankName'] ?? $mkBankName)));
$mkAccName = (string)($tmp['acc_name'] ?? ($tmp['account_name'] ?? ($tmp['accountName'] ?? $mkAccName)));
}
}
if ((trim($agAccNo) === '' && trim($agBankName) === '' && trim($agAccName) === '') && !empty($dsBank['agent_bank'])) {
$tmp = is_string($dsBank['agent_bank']) ? (json_decode((string)$dsBank['agent_bank'], true) ?: []) : (is_array($dsBank['agent_bank']) ? $dsBank['agent_bank'] : []);
if (is_array($tmp)) {
$agAccNo = (string)($tmp['acc_no'] ?? ($tmp['account_number'] ?? ($tmp['accountNumber'] ?? ($tmp['accNo'] ?? $agAccNo))));
$agBankName = (string)($tmp['bank'] ?? ($tmp['bank_name'] ?? ($tmp['bankName'] ?? $agBankName)));
$agAccName = (string)($tmp['acc_name'] ?? ($tmp['account_name'] ?? ($tmp['accountName'] ?? $agAccName)));
}
}
}
} catch (Throwable $eDSB) {}
}
$mkBank = ['acc_no' => trim($mkAccNo), 'bank' => trim($mkBankName), 'acc_name' => trim($mkAccName)];
$agBank = ['acc_no' => trim($agAccNo), 'bank' => trim($agBankName), 'acc_name' => trim($agAccName)];
$cfReceipt = '';
if (colExists('client_forms','receipt_path')) {
try {
if ($clientId > 0 && colExists('client_forms','client_id')) {
$qr = $pdo->prepare("SELECT receipt_path FROM client_forms WHERE client_id = ? ORDER BY updated_at DESC, created_at DESC LIMIT 1");
$qr->execute([$clientId]); $cfReceipt = (string)($qr->fetchColumn() ?: '');
}
} catch (Throwable $e) {}
if ($cfReceipt === '' && isset($row['receipt_file'])) { $cfReceipt = (string)$row['receipt_file']; }
if ($cfReceipt === '' && isset($row['proof_file'])) { $cfReceipt = (string)$row['proof_file']; }
}
$instStart = (string)($metaEff['installment_start_date'] ?? '');
if ($instStart === '' && isset($metaEff['installmentStartDate'])) { $instStart = (string)$metaEff['installmentStartDate']; }
if ($instStart === '' && isset($metaEff['start_date'])) { $instStart = (string)$metaEff['start_date']; }
if ($instStart === '' && isset($metaEff['startDate'])) { $instStart = (string)$metaEff['startDate']; }
if ($instStart === '' && isset($metaEff['deal_submit_id']) && (int)$metaEff['deal_submit_id'] > 0 && colExists('deals_submit','installment_start_date')) {
try {
$stS = $pdo->prepare("SELECT installment_start_date FROM deals_submit WHERE id = ? LIMIT 1");
$stS->execute([(int)$metaEff['deal_submit_id']]);
$instStart = (string)($stS->fetchColumn() ?: '');
} catch (Throwable $e) {}
}
if ($instStart === '' && $clientId > 0 && colExists('deals_submit','installment_start_date') && colExists('deals_submit','user_id')) {
try {
$stS = $pdo->prepare("SELECT installment_start_date FROM deals_submit WHERE user_id = ? ORDER BY created_at DESC, id DESC LIMIT 1");
$stS->execute([$clientId]);
$instStart = (string)($stS->fetchColumn() ?: '');
} catch (Throwable $e) {}
}
$sqmVal = null;
if (isset($metaEff['sqm']) && is_numeric($metaEff['sqm'])) { $sqmVal = (float)$metaEff['sqm']; }
elseif (isset($metaEff['sqm_size']) && is_numeric($metaEff['sqm_size'])) { $sqmVal = (float)$metaEff['sqm_size']; }
elseif (isset($metaEff['plot_size']) && is_numeric($metaEff['plot_size'])) { $sqmVal = (float)$metaEff['plot_size']; }
elseif (isset($metaEff['size_sqm']) && is_numeric($metaEff['size_sqm'])) { $sqmVal = (float)$metaEff['size_sqm']; }
elseif (isset($row['sqm']) && is_numeric($row['sqm'])) { $sqmVal = (float)$row['sqm']; }
$dsId = 0;
if (isset($metaEff['deal_submit_id']) && (int)$metaEff['deal_submit_id'] > 0) { $dsId = (int)$metaEff['deal_submit_id']; }
if ($dsId <= 0) {
$refTry = (string)($row['reference'] ?? ($row['reference_number'] ?? ($row['ref'] ?? '')));
if ($refTry !== '') {
if (preg_match('/-(\d+)\s*$/', $refTry, $mm)) { $dsId = (int)($mm[1] ?? 0); }
}
}
if ($dsId > 0 && (colExists('deals_submit','id'))) {
$startCol = colExists('deals_submit','installment_start_date') ? 'installment_start_date' : (colExists('deals_submit','start_date') ? 'start_date' : null);
$sqmCol = colExists('deals_submit','sqm') ? 'sqm' : (colExists('deals_submit','area_sqm') ? 'area_sqm' : (colExists('deals_submit','total_sqm') ? 'total_sqm' : null));
if ($startCol || $sqmCol) {
try {
$sel = [];
if ($startCol) { $sel[] = "{$startCol} AS installment_start_date"; }
if ($sqmCol) { $sel[] = "{$sqmCol} AS sqm"; }
$qds = $pdo->prepare("SELECT " . implode(', ', $sel) . " FROM deals_submit WHERE id = ? LIMIT 1");
$qds->execute([$dsId]);
$dsRow = $qds->fetch(PDO::FETCH_ASSOC) ?: [];
if ($instStart === '' && isset($dsRow['installment_start_date']) && is_string($dsRow['installment_start_date'])) { $instStart = (string)$dsRow['installment_start_date']; }
if (($sqmVal === null || $sqmVal <= 0) && isset($dsRow['sqm']) && is_numeric($dsRow['sqm'])) { $sqmVal = (float)$dsRow['sqm']; }
} catch (Throwable $e) {}
}
if (($instStart === '' || $sqmVal === null || $sqmVal <= 0) && colExists('deals_submit','meta_json')) {
try {
$qdm = $pdo->prepare("SELECT meta_json FROM deals_submit WHERE id = ? LIMIT 1");
$qdm->execute([$dsId]);
$mjRaw = (string)($qdm->fetchColumn() ?: '');
if ($mjRaw !== '') {
$mj = json_decode($mjRaw, true);
if (is_array($mj)) {
if ($instStart === '' && isset($mj['installment_start_date'])) { $instStart = (string)$mj['installment_start_date']; }
if ($instStart === '' && isset($mj['installmentStartDate'])) { $instStart = (string)$mj['installmentStartDate']; }
if ($instStart === '' && isset($mj['start_date'])) { $instStart = (string)$mj['start_date']; }
if ($instStart === '' && isset($mj['startDate'])) { $instStart = (string)$mj['startDate']; }
if (($sqmVal === null || $sqmVal <= 0) && isset($mj['sqm']) && is_numeric($mj['sqm'])) { $sqmVal = (float)$mj['sqm']; }
if (($sqmVal === null || $sqmVal <= 0) && isset($mj['sqm_size']) && is_numeric($mj['sqm_size'])) { $sqmVal = (float)$mj['sqm_size']; }
if (($sqmVal === null || $sqmVal <= 0) && isset($mj['plot_size']) && is_numeric($mj['plot_size'])) { $sqmVal = (float)$mj['plot_size']; }
}
}
} catch (Throwable $e) {}
}
}
if (($sqmVal === null || $sqmVal <= 0) && isset($metaEff['property_id']) && (int)$metaEff['property_id'] > 0 && colExists('properties','id')) {
$pidProp = (int)$metaEff['property_id'];
$sqmCol = colExists('properties','area_sqm') ? 'area_sqm' : (colExists('properties','total_sqm') ? 'total_sqm' : null);
if ($sqmCol) {
try {
$sp = $pdo->prepare("SELECT {$sqmCol} FROM properties WHERE id = ? LIMIT 1");
$sp->execute([$pidProp]);
$v = $sp->fetchColumn();
if (is_numeric($v)) { $sqmVal = (float)$v; }
} catch (Throwable $e) {}
}
}
$paymentDate = '';
try {
$cands = [];
foreach (['created_at','date','payment_date','approved_at','approval_date','verified_at','updated_at'] as $c) {
if (isset($row[$c]) && is_string($row[$c]) && trim($row[$c]) !== '') { $cands[] = trim((string)$row[$c]); }
}
if (!empty($cands)) { $paymentDate = (string)$cands[0]; }
} catch (Throwable $e) { $paymentDate = ''; }
$pmSubmission = [];
try {
$allow = [
'allocation_id','property_id','client_charge_id',
'payment_reference','reference','ref','reference_number',
'payment_method','method','payment_type',
'plan_type','payment_plan','custom_months','installment_start_date','sqm',
'marketer_bank','agent_bank','marketer_account_number','marketer_bank_name','marketer_account_name','agent_account_number','agent_bank_name','agent_account_name',
'submitted_by_user','submitted_by_role','submitted_by_name','marketer_name',
'deal_id','deal_submit_id','transactions'
];
foreach ($allow as $k) {
if (array_key_exists($k, $metaEff)) { $pmSubmission[$k] = $metaEff[$k]; }
}
if (!isset($pmSubmission['marketer_bank']) || !is_array($pmSubmission['marketer_bank'])) { $pmSubmission['marketer_bank'] = $mkBank; }
if (!isset($pmSubmission['agent_bank']) || !is_array($pmSubmission['agent_bank'])) { $pmSubmission['agent_bank'] = $agBank; }
if (!isset($pmSubmission['client_id']) && $clientId > 0) { $pmSubmission['client_id'] = $clientId; }
$pmSubmission['payment_id'] = $pid;
$pmSubmission['payment_amount'] = (float)($row['amount'] ?? 0);
$pmSubmission['payment_status'] = (string)($row['status'] ?? '');
$pmSubmission['payment_date'] = $paymentDate;
if (!isset($pmSubmission['payment_reference']) || trim((string)$pmSubmission['payment_reference']) === '') {
$pmSubmission['payment_reference'] = (string)($row['reference'] ?? ($row['reference_number'] ?? ($row['ref'] ?? ($meta['reference'] ?? ($meta['ref'] ?? '')))));
}
if (!isset($pmSubmission['payment_method']) || trim((string)$pmSubmission['payment_method']) === '') {
$pmSubmission['payment_method'] = (string)($row['payment_method'] ?? ($row['method'] ?? ($meta['payment_method'] ?? '')));
}
if (!isset($pmSubmission['plan_type']) || trim((string)$pmSubmission['plan_type']) === '') {
$pmSubmission['plan_type'] = (string)($metaEff['plan_type'] ?? ($row['plan_type'] ?? ($row['payment_plan'] ?? '')));
}
if (!isset($pmSubmission['installment_start_date']) || trim((string)$pmSubmission['installment_start_date']) === '') {
$pmSubmission['installment_start_date'] = $instStart;
}
if (!isset($pmSubmission['sqm']) || (is_numeric($pmSubmission['sqm']) && (float)$pmSubmission['sqm'] <= 0)) {
if ($sqmVal !== null) { $pmSubmission['sqm'] = $sqmVal; }
}
if ((!isset($pmSubmission['submitted_by_name']) || trim((string)$pmSubmission['submitted_by_name']) === '') && isset($row['submitted_by_name']) && is_string($row['submitted_by_name'])) {
$pmSubmission['submitted_by_name'] = (string)$row['submitted_by_name'];
}
if ((!isset($pmSubmission['submitted_by_role']) || trim((string)$pmSubmission['submitted_by_role']) === '') && isset($row['submitted_by_role']) && is_string($row['submitted_by_role'])) {
$pmSubmission['submitted_by_role'] = (string)$row['submitted_by_role'];
}
if ((!isset($pmSubmission['submitted_by_user']) || (int)$pmSubmission['submitted_by_user'] <= 0) && isset($row['submitted_by_user']) && is_numeric($row['submitted_by_user'])) {
$pmSubmission['submitted_by_user'] = (int)$row['submitted_by_user'];
}
if (!isset($pmSubmission['transactions']) || !is_array($pmSubmission['transactions'])) {
$pmSubmission['transactions'] = [[
'date' => $paymentDate,
'amount' => (float)($row['amount'] ?? 0),
'type' => (string)($row['payment_method'] ?? ($row['method'] ?? 'property_payment')),
'status' => (string)($row['status'] ?? '')
]];
}
} catch (Throwable $e) { $pmSubmission = []; }
$history = [];
$histLimit = 20;
$allocId = (int)($row['allocation_id'] ?? ($meta['allocation_id'] ?? 0));
$propId = (int)($row['property_id'] ?? ($meta['property_id'] ?? 0));
$dealIdForHistory = isset($row['deal_id']) ? (int)$row['deal_id'] : 0;
$dateExpr = (colExists('payments','created_at') ? "created_at" : (colExists('payments','date') ? "date" : (colExists('payments','updated_at') ? "updated_at" : "NOW()")));
$typeExpr = (colExists('payments','payment_type') ? "payment_type" : (colExists('payments','type') ? "type" : (colExists('payments','payment_method') ? "payment_method" : "'Payment'")));
$statusExpr = (colExists('payments','status') ? "status" : (colExists('payments','payment_status') ? "payment_status" : (colExists('payments','client_payment_status') ? "client_payment_status" : "NULL")));
$amountExpr = (colExists('payments','amount') ? "amount" : (colExists('payments','paid_amount') ? "paid_amount" : (colExists('payments','amount_paid') ? "amount_paid" : "0")));
$companyClause = '';
$companyParams = [];
if ($applyCompanyFilter && colExists('payments','company_id')) { $companyClause = " AND (company_id = ? OR company_id IS NULL OR company_id = 0)"; $companyParams[] = $companyId; }
$clientFilterSql = '';
$clientFilterParams = [];
if ($clientId > 0) {
$parts = [];
if (colExists('payments','user_id')) { $parts[] = "user_id = ?"; $clientFilterParams[] = $clientId; }
if (colExists('payments','client_id')) { $parts[] = "client_id = ?"; $clientFilterParams[] = $clientId; }
if (!empty($parts)) { $clientFilterSql = " AND (" . implode(' OR ', $parts) . ")"; }
}
if ($allocId > 0 && colExists('payments','allocation_id')) {
try {
$qh = $pdo->prepare("SELECT id, {$amountExpr} AS amount, {$typeExpr} AS payment_type, {$statusExpr} AS status, {$dateExpr} AS created_at FROM payments WHERE allocation_id = ?{$clientFilterSql}{$companyClause} ORDER BY created_at DESC LIMIT {$histLimit}");
$qh->execute(array_merge([$allocId], $clientFilterParams, $companyParams));
$history = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {}
} elseif ($dealIdForHistory > 0 && colExists('payments','deal_id')) {
try {
$qh = $pdo->prepare("SELECT id, {$amountExpr} AS amount, {$typeExpr} AS payment_type, {$statusExpr} AS status, {$dateExpr} AS created_at FROM payments WHERE deal_id = ?{$clientFilterSql}{$companyClause} ORDER BY created_at DESC LIMIT {$histLimit}");
$qh->execute(array_merge([$dealIdForHistory], $clientFilterParams, $companyParams));
$history = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {}
} elseif ($propId > 0 && colExists('payments','property_id') && $clientId > 0) {
try {
$ucl = [];
$prm = [$propId];
if (colExists('payments','user_id')) { $ucl[] = "user_id = ?"; $prm[] = $clientId; }
if (colExists('payments','client_id')) { $ucl[] = "client_id = ?"; $prm[] = $clientId; }
if (!empty($ucl)) {
$qh = $pdo->prepare("SELECT id, {$amountExpr} AS amount, {$typeExpr} AS payment_type, {$statusExpr} AS status, {$dateExpr} AS created_at FROM payments WHERE property_id = ? AND (" . implode(' OR ', $ucl) . "){$companyClause} ORDER BY created_at DESC LIMIT {$histLimit}");
$qh->execute(array_merge($prm, $companyParams));
$history = $qh->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
} catch (Throwable $e) {}
}
if (empty($history)) {
$history = [[
'id' => $pid,
'amount' => (float)($row['amount'] ?? 0),
'payment_type' => (string)($row['payment_type'] ?? ($row['type'] ?? ($row['payment_method'] ?? ($row['method'] ?? 'Payment')))),
'status' => (string)($row['status'] ?? ''),
'created_at' => $paymentDate
]];
}
if (!empty($history)) {
$seenH = [];
$uniqH = [];
foreach ($history as $h) {
$kid = (string)($h['id'] ?? '');
$kdt = (string)($h['created_at'] ?? '');
$kam = (string)($h['amount'] ?? '');
$k = $kid . '|' . $kdt . '|' . $kam;
if (isset($seenH[$k])) { continue; }
$seenH[$k] = true;
$uniqH[] = $h;
}
$history = $uniqH;
}
$pm = [
'payment_id' => $pid,
'payment_amount' => (float)($row['amount'] ?? 0),
'payment_status' => (string)($row['status'] ?? ''),
'payment_reference' => (string)($meta['payment_reference'] ?? ($meta['reference'] ?? ($row['reference'] ?? ($row['reference_number'] ?? ($row['ref'] ?? ''))))),
'payment_method' => (string)($meta['payment_method'] ?? ($row['payment_method'] ?? ($row['method'] ?? ''))),
'payment_date' => $paymentDate,
'allocation_id' => $allocId,
'property_id' => $propId,
'sqm' => $sqmVal,
'installment_start_date' => $instStart,
'project_desc' => $proj,
'project_name' => (string)($row['project_name'] ?? ($meta['project_name'] ?? '')),
'property_estate' => (string)($row['property_estate'] ?? ($meta['property_estate'] ?? '')),
'deal_source' => $dealSource,
'plan_type' => $planType,
'payment_plan' => (string)($row['payment_plan'] ?? ($meta['payment_plan'] ?? '')),
'marketer_name' => $marketerName,
'client_payment_status' => $statusNotes,
'notes' => (string)($row['notes'] ?? ($meta['notes'] ?? '')),
'amount_offered' => (float)($meta['amount_offered'] ?? ($row['amount_offered'] ?? 0)),
'amount_paid_so_far' => (float)($meta['amount_paid_so_far'] ?? ($row['amount_paid_so_far'] ?? 0)),
'amount_paid_now' => (float)($row['amount'] ?? ($meta['amount_now'] ?? 0)),
'discount_amount' => (float)($meta['discount_amount'] ?? ($row['discount_amount'] ?? 0)),
'discount_approved_by' => (string)($meta['discount_approved_by'] ?? ($row['discount_approved_by'] ?? '')),
'commission_pct' => (float)($meta['commission_pct'] ?? ($row['commission_percent'] ?? 0)),
'marketer_comm' => (float)($meta['marketer_comm'] ?? ($row['marketer_commission'] ?? 0)),
'agent_comm' => (float)($meta['agent_comm'] ?? ($row['agent_commission'] ?? 0)),
'balance_remaining' => (float)($meta['balance_remaining'] ?? ($row['balance_remaining'] ?? 0)),
'submitted_by_role' => (string)($meta['submitted_by_role'] ?? ($row['submitted_by_role'] ?? '')),
'marketer_bank' => $mkBank,
'agent_bank' => $agBank,
'transactions' => isset($meta['transactions']) && is_array($meta['transactions']) ? $meta['transactions'] : []
];
$inv = null; $rcAuto = null;
if (isset($row['deal_id']) && (int)$row['deal_id'] > 0) {
$dealIdX = (int)$row['deal_id'];
if (function_exists('updateInvoice')) { @updateInvoice($dealIdX); }
if (function_exists('createInvoiceIfNotExists')) { @createInvoiceIfNotExists($dealIdX); }
try {
$qi = $pdo->prepare("SELECT invoice_number, total_amount, amount_paid, balance, status, due_date, created_at FROM invoices WHERE deal_id = ? LIMIT 1");
$qi->execute([$dealIdX]); $invRow = $qi->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($invRow)) { $inv = $invRow; }
} catch (Throwable $e) {}
try {
$qrn = $pdo->prepare("SELECT receipt_number FROM receipts WHERE deal_id = ? ORDER BY id DESC LIMIT 1");
$qrn->execute([$dealIdX]); $rcAuto = (string)($qrn->fetchColumn() ?: '');
} catch (Throwable $e) {}
}
$res = [
'success' => true,
'deal_id' => isset($row['deal_id']) ? (int)$row['deal_id'] : 0,
'pm' => $pm,
'pm_submission' => $pmSubmission,
'data' => $cf,
'history' => $history,
'receipt' => $cfReceipt,
'invoice' => $inv,
'auto_receipt_number' => $rcAuto,
'status' => (string)($meta['form_status'] ?? ''),
'submitter' => (string)($meta['submitted_by_name'] ?? ''),
'client' => $clientNameMeta,
'email' => $clientEmailView
];
}
}
} catch (Throwable $e) {
$res = ['success' => false];
}
echo json_encode($res);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['history_action'])) {
header('Content-Type: application/json');
$action = (string)($_POST['history_action'] ?? '');
if ($action === 'approved_history') {
try {
$uid = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
$pmid = isset($_POST['payment_id']) ? (int)$_POST['payment_id'] : 0;
$cname = isset($_POST['client_name']) ? (string)$_POST['client_name'] : '';
$cemail = isset($_POST['client_email']) ? (string)$_POST['client_email'] : '';
$items = [];
$limit = 20;
$dateExprP0 = (colExists('payments','created_at') ? "created_at" : (colExists('payments','date') ? "date" : (colExists('payments','updated_at') ? "updated_at" : (colExists('payments','verified_at') ? "verified_at" : "NOW()"))));
$statusExprP0 = (colExists('payments','status') ? "status" : (colExists('payments','payment_status') ? "payment_status" : (colExists('payments','client_payment_status') ? "client_payment_status" : "NULL")));
$typeExprP0 = (colExists('payments','payment_type') ? "payment_type" : (colExists('payments','type') ? "type" : (colExists('payments','payment_method') ? "payment_method" : "'Payment'")));
$amountExprP0 = (colExists('payments','amount') ? "amount" : (colExists('payments','paid_amount') ? "paid_amount" : (colExists('payments','amount_paid') ? "amount_paid" : "0")));
$companyClause = '';
$companyParams = [];
if ($applyCompanyFilter && colExists('payments','company_id')) { $companyClause = " AND (company_id = ? OR company_id IS NULL OR company_id = 0)"; $companyParams[] = $companyId; }
if ($pmid <= 0) {
$params = [];
$where = [];
if ($uid > 0 && colExists('payments','user_id')) { $where[] = "user_id = ?"; $params[] = $uid; }
if ($uid > 0 && colExists('payments','client_id')) { $where[] = "client_id = ?"; $params[] = $uid; }
if ($cname !== '' && colExists('payments','client_name')) { $where[] = "client_name = ?"; $params[] = $cname; }
if ($cemail !== '' && colExists('payments','client_email')) { $where[] = "client_email = ?"; $params[] = $cemail; }
if (!empty($where)) {
$sql = "SELECT id, ".$amountExprP0." AS amount, ".$typeExprP0." AS payment_type, ".$statusExprP0." AS status, ".$dateExprP0." AS created_at FROM payments WHERE (" . implode(' OR ', $where) . ")" . $companyClause . " ORDER BY created_at DESC LIMIT ".$limit;
$st = $pdo->prepare($sql); $st->execute(array_merge($params, $companyParams));
$rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rows as $r) { $items[] = $r; }
}
}
if ($pmid > 0 && colExists('payments','id')) {
$qp = $pdo->prepare("SELECT id, " . (colExists('payments','user_id') ? "user_id" : "NULL AS user_id") . ", " . (colExists('payments','client_id') ? "client_id" : "NULL AS client_id") . ", " . (colExists('payments','client_name') ? "client_name" : "NULL AS client_name") . ", " . (colExists('payments','client_email') ? "client_email" : "NULL AS client_email") . ", " . (colExists('payments','deal_id') ? "deal_id" : "NULL AS deal_id") . ", " . (colExists('payments','allocation_id') ? "allocation_id" : "NULL AS allocation_id") . ", " . (colExists('payments','property_id') ? "property_id" : "NULL AS property_id") . ", " . (colExists('payments','receipt_file') ? "receipt_file" : (colExists('payments','proof_file') ? "proof_file AS receipt_file" : "NULL AS receipt_file")) . " FROM payments WHERE id = ? LIMIT 1");
$qp->execute([$pmid]); $base = $qp->fetch(PDO::FETCH_ASSOC) ?: [];
$uid2 = (int)($base['user_id'] ?? 0);
if ($uid2 <= 0) { $uid2 = (int)($base['client_id'] ?? 0); }
$cname2 = (string)($base['client_name'] ?? '');
$cemail2 = (string)($base['client_email'] ?? '');
$deal2 = (int)($base['deal_id'] ?? 0);
$alloc2 = (int)($base['allocation_id'] ?? 0);
$prop2 = (int)($base['property_id'] ?? 0);
$rcp2 = (string)($base['receipt_file'] ?? '');
$dateExpr2 = (colExists('payments','created_at') ? "created_at" : (colExists('payments','date') ? "date" : (colExists('payments','updated_at') ? "updated_at" : (colExists('payments','verified_at') ? "verified_at" : "NOW()"))));
$statusExpr2 = (colExists('payments','status') ? "status" : (colExists('payments','payment_status') ? "payment_status" : (colExists('payments','client_payment_status') ? "client_payment_status" : "NULL")));
$typeExpr2 = (colExists('payments','payment_type') ? "payment_type" : (colExists('payments','type') ? "type" : (colExists('payments','payment_method') ? "payment_method" : "'Payment'")));
$amountExpr2 = (colExists('payments','amount') ? "amount" : (colExists('payments','paid_amount') ? "paid_amount" : (colExists('payments','amount_paid') ? "amount_paid" : "0")));
$prefix = "SELECT id, ".$amountExpr2." AS amount, ".$typeExpr2." AS payment_type, ".$statusExpr2." AS status, ".$dateExpr2." AS created_at FROM payments WHERE 1=1 ";
$sqlU = '';
$pU = [];
$clientFilterSql = '';
$clientFilterParams = [];
if ($uid2 > 0) {
$parts = [];
if (colExists('payments','user_id')) { $parts[] = "user_id = ?"; $clientFilterParams[] = $uid2; }
if (colExists('payments','client_id')) { $parts[] = "client_id = ?"; $clientFilterParams[] = $uid2; }
if (!empty($parts)) { $clientFilterSql = " AND (" . implode(' OR ', $parts) . ")"; }
}
if ($alloc2 > 0 && colExists('payments','allocation_id')) {
$sqlU = $prefix . "AND allocation_id = ?" . $clientFilterSql . $companyClause . " ORDER BY created_at DESC LIMIT ".$limit;
$pU = array_merge([$alloc2], $clientFilterParams, $companyParams);
} elseif ($deal2 > 0 && colExists('payments','deal_id')) {
$sqlU = $prefix . "AND deal_id = ?" . $clientFilterSql . $companyClause . " ORDER BY created_at DESC LIMIT ".$limit;
$pU = array_merge([$deal2], $clientFilterParams, $companyParams);
} elseif ($prop2 > 0 && colExists('payments','property_id') && $uid2 > 0) {
$uParts = [];
$pU = [$prop2];
if (colExists('payments','user_id')) { $uParts[] = "user_id = ?"; $pU[] = $uid2; }
if (colExists('payments','client_id')) { $uParts[] = "client_id = ?"; $pU[] = $uid2; }
if (!empty($uParts)) {
$sqlU = $prefix . "AND property_id = ? AND (" . implode(' OR ', $uParts) . ")" . $companyClause . " ORDER BY created_at DESC LIMIT ".$limit;
$pU = array_merge($pU, $companyParams);
}
} elseif ($uid2 > 0) {
$uParts = [];
$pU = [];
if (colExists('payments','user_id')) { $uParts[] = "user_id = ?"; $pU[] = $uid2; }
if (colExists('payments','client_id')) { $uParts[] = "client_id = ?"; $pU[] = $uid2; }
if (!empty($uParts)) {
$sqlU = "SELECT id, ".$amountExpr2." AS amount, ".$typeExpr2." AS payment_type, ".$statusExpr2." AS status, ".$dateExpr2." AS created_at FROM payments WHERE (" . implode(' OR ', $uParts) . ")" . $companyClause . " ORDER BY created_at DESC LIMIT ".$limit;
$pU = array_merge($pU, $companyParams);
}
}
if ($sqlU !== '') {
try { $stU = $pdo->prepare($sqlU); $stU->execute($pU); $rowsU = $stU->fetchAll(PDO::FETCH_ASSOC) ?: []; foreach ($rowsU as $r) { $items[] = $r; } } catch (Throwable $e) {}
}
if (empty($items) && $pmid > 0) {
$items[] = [
'id' => $pmid,
'amount' => null,
'payment_type' => null,
'status' => null,
'created_at' => null
];
}
}
$seen = []; $uniq = [];
foreach ($items as $it) {
$key = ((string)($it['id'] ?? '')) . '|' . ((string)($it['created_at'] ?? ''));
if (!isset($seen[$key])) { $seen[$key] = true; $uniq[] = $it; }
}
usort($uniq, function($a,$b){ return strcmp((string)($b['created_at'] ?? ''),(string)($a['created_at'] ?? '')); });
if (count($uniq) > $limit) { $uniq = array_slice($uniq, 0, $limit); }
echo json_encode(['success' => true, 'items' => $uniq]);
} catch (Throwable $e) {
echo json_encode(['success' => false]);
}
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['verify_action'])) {
header('Content-Type: application/json');
$id = isset($_POST['verify_id']) ? (int)$_POST['verify_id'] : 0;
$action = $_POST['verify_action'] ?? '';
try {
if ($id > 0 && in_array($action, ['approve','reject'], true)) {
if ($action === 'approve') {
$sql = "UPDATE payments SET status = 'approved'";
$vals = [];
if (colExists('payments','approved_by')) { $sql .= ", approved_by = ?"; $vals[] = $userId; }
if (colExists('payments','approval_date')) { $sql .= ", approval_date = NOW()"; }
elseif (colExists('payments','approved_at')) { $sql .= ", approved_at = NOW()"; }
if (colExists('payments','verified_by')) { $sql .= ", verified_by = ?"; $vals[] = $userId; }
if (colExists('payments','verified_at')) { $sql .= ", verified_at = NOW()"; }
$sql .= " WHERE id = ?";
$vals[] = $id;
$sp = $pdo->prepare($sql);
$sp->execute($vals);
} else {
$sp = $pdo->prepare("UPDATE payments SET status = 'rejected' WHERE id = ?");
$sp->execute([$id]);
}
if ($sp->rowCount() > 0) {
if ($action === 'approve') { try { createReceiptForApprovedPayment($id); } catch (Throwable $e) {} }
if ($action === 'approve') {
try {
if (file_exists(__DIR__ . '/includes/charges.php')) { require_once __DIR__ . '/includes/charges.php'; }
if (function_exists('applyPaymentToClientCharges')) { applyPaymentToClientCharges($pdo, (int)$id, (int)$companyId); }
} catch (Throwable $e) {}
}
try {
$uStmt = $pdo->prepare("SELECT user_id, reference FROM payments WHERE id = ?");
$uStmt->execute([$id]);
$rowP = $uStmt->fetch(PDO::FETCH_ASSOC);
$uId = (int)($rowP['user_id'] ?? 0);
if ($uId > 0 && $action === 'approve') {
$cf = $pdo->prepare("UPDATE client_forms SET status = 'payment_verified', updated_at = NOW() WHERE client_id = ?");
$cf->execute([$uId]);
}
} catch (Throwable $e) {}
try {
$qr = $pdo->prepare("SELECT user_id, amount, meta_json FROM payments WHERE id = ?");
$qr->execute([$id]);
$pr = $qr->fetch(PDO::FETCH_ASSOC);
$uId = (int)($pr['user_id'] ?? 0);
$amt = (float)($pr['amount'] ?? 0);
$meta = [];
if (!empty($pr['meta_json'])) { $meta = json_decode($pr['meta_json'], true) ?: []; }
$submittedBy = (int)($meta['submitted_by_user'] ?? 0);
$clientEmail = ''; $clientName = '';
$submitEmail = ''; $submitName = '';
if ($uId > 0 && colExists('users','email')) {
$se = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$se->execute([$uId]); $rowE = $se->fetch(PDO::FETCH_ASSOC);
if ($rowE) { $clientEmail = (string)($rowE['email'] ?? ''); $clientName = (string)($rowE['display_name'] ?? 'Client'); }
}
if ($submittedBy > 0 && colExists('users','email')) {
$ss = $pdo->prepare("SELECT email, " . (colExists('users','name') ? "name" : "email") . " AS display_name FROM users WHERE id = ? LIMIT 1");
$ss->execute([$submittedBy]); $rowS = $ss->fetch(PDO::FETCH_ASSOC);
if ($rowS) { $submitEmail = (string)($rowS['email'] ?? ''); $submitName = (string)($rowS['display_name'] ?? ''); }
}
$companyName = function_exists('getSetting') ? getSetting('company_name', 'Aiben Properties') : 'Aiben Properties';
$noreplyEmail = function_exists('getSetting') ? getSetting('noreply_email', 'no-reply@aibenproperties.local') : 'no-reply@aibenproperties.local';
if ($clientEmail !== '') {
$subjectC = $action === 'approve' ? "Payment approved" : "Payment rejected";
$bodyC = $action === 'approve'
? "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " has been approved.\n\nThank you,\n{$companyName}"
: "Dear {$clientName},\n\nYour payment of ₦" . number_format($amt,2) . " was rejected. Please contact support for details.\n\nRegards,\n{$companyName}";
$headersC = "From: {$noreplyEmail}\r\n";
@mail($clientEmail, $subjectC, $bodyC, $headersC);
}
if ($submitEmail !== '') {
$subjectS = $action === 'approve' ? "Client payment approved" : "Client payment rejected";
$bodyS = $action === 'approve'
? "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " has been approved by Finance.\n\nRegards,\n{$companyName}"
: "Hello {$submitName},\n\nThe client payment of ₦" . number_format($amt,2) . " was rejected by Finance.\n\nRegards,\n{$companyName}";
$headersS = "From: {$noreplyEmail}\r\n";
@mail($submitEmail, $subjectS, $bodyS, $headersS);
}
} catch (Throwable $eN) {}
echo json_encode(['success'=>true]);
} else {
echo json_encode(['success'=>false, 'message'=>'Not found']);
}
} else {
echo json_encode(['success'=>false, 'message'=>'Invalid request']);
}
} catch (Throwable $e) {
echo json_encode(['success'=>false, 'message'=>'DB error']);
}
exit;
}
if (!isset($queueStatuses)) { $queueStatuses = ['pending_verification','pending_confirmation','pending_gateway','payment_verification','awaiting_verification','pending','submitted']; }
if (!isset($queueStatusesSql)) { $queueStatusesSql = "('" . implode("','", $queueStatuses) . "')"; }
$metrics = ['pending'=>0,'approved_today'=>0,'rejected'=>0,'volume'=>0.0];
try {
$hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
if ($hasPayments) {
$params = [];
$companyClause = '';
if ($applyCompanyFilter && colExists('payments','company_id')) {
$companyClause = " AND ((company_id = ? OR company_id IS NULL)" . (colExists('payments','reference') ? " OR UPPER(COALESCE(reference,'')) LIKE 'FORM_FEE%'" : "") . ")";
$params[] = $companyId;
}
$pendingSet = $queueStatusesSql;
$st = $pdo->prepare("SELECT COUNT(*) FROM payments WHERE status IN $pendingSet" . $companyClause);
$st->execute($params); $metrics['pending'] = (int)($st->fetchColumn() ?: 0);
$st = $pdo->prepare("SELECT COUNT(*) FROM payments WHERE status IN ('rejected','failed')" . $companyClause);
$st->execute($params); $metrics['rejected'] = (int)($st->fetchColumn() ?: 0);
$dateCol = colExists('payments','approval_date') ? 'approval_date' : (colExists('payments','updated_at') ? 'updated_at' : (colExists('payments','created_at') ? 'created_at' : 'date'));
$st = $pdo->prepare("SELECT COUNT(*) FROM payments WHERE status = 'approved' AND DATE($dateCol) = CURDATE()" . $companyClause);
$st->execute($params); $metrics['approved_today'] = (int)($st->fetchColumn() ?: 0);
$st = $pdo->prepare("SELECT COALESCE(SUM(amount),0) FROM payments WHERE status = 'approved'" . $companyClause);
$st->execute($params); $metrics['volume'] = (float)($st->fetchColumn() ?: 0.0);
} else {
if ($applyCompanyFilter) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM transactions WHERE status = 'pending_verification' AND (company_id = ? OR company_id IS NULL)");
$stmt->execute([$companyId]); $metrics['pending'] = (int)($stmt->fetchColumn() ?: 0);
$stmt = $pdo->prepare("SELECT COUNT(*) FROM transactions WHERE status = 'rejected' AND (company_id = ? OR company_id IS NULL)");
$stmt->execute([$companyId]); $metrics['rejected'] = (int)($stmt->fetchColumn() ?: 0);
$stmt = $pdo->prepare("SELECT COUNT(*) FROM transactions WHERE status = 'approved' AND DATE(verified_at) = CURDATE() AND (company_id = ? OR company_id IS NULL)");
$stmt->execute([$companyId]); $metrics['approved_today'] = (int)($stmt->fetchColumn() ?: 0);
$stmt = $pdo->prepare("SELECT COALESCE(SUM(amount),0) FROM transactions WHERE status = 'approved' AND (company_id = ? OR company_id IS NULL)");
$stmt->execute([$companyId]); $metrics['volume'] = (float)($stmt->fetchColumn() ?: 0.0);
} else {
$metrics['pending'] = (int)$pdo->query("SELECT COUNT(*) FROM transactions WHERE status = 'pending_verification'")->fetchColumn();
$metrics['rejected'] = (int)$pdo->query("SELECT COUNT(*) FROM transactions WHERE status = 'rejected'")->fetchColumn();
$metrics['approved_today'] = (int)$pdo->query("SELECT COUNT(*) FROM transactions WHERE status = 'approved' AND DATE(verified_at) = CURDATE()")->fetchColumn();
$metrics['volume'] = (float)$pdo->query("SELECT COALESCE(SUM(amount),0) FROM transactions WHERE status = 'approved'")->fetchColumn();
}
}
} catch (Throwable $e) {}
$filterProjectId = isset($_GET['project_id']) ? (int)$_GET['project_id'] : 0;
$filterProperty = isset($_GET['property']) ? trim($_GET['property']) : '';
$filterPaidFor = isset($_GET['paid_for']) ? strtolower(trim($_GET['paid_for'])) : 'all';
$filterUploadedBy = isset($_GET['uploaded_by']) ? strtolower(trim($_GET['uploaded_by'])) : 'all';
$projectOptions = [];
$propertyOptions = [];
try {
if ($pdo->query("SHOW TABLES LIKE 'projects'")->rowCount() > 0) {
$paramsProj = [];
$companyClauseProj = '';
if ($applyCompanyFilter && colExists('projects','company_id')) { $companyClauseProj = " WHERE (company_id = ? OR company_id IS NULL)"; $paramsProj[] = $companyId; }
$spj = $pdo->prepare("SELECT id, name, type FROM projects{$companyClauseProj} ORDER BY name ASC LIMIT 500");
$spj->execute($paramsProj); $projectOptions = $spj->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
if ($pdo->query("SHOW TABLES LIKE 'properties'")->rowCount() > 0) {
$titleCol = colExists('properties','title') ? 'title' : (colExists('properties','name') ? 'name' : null);
if ($titleCol) {
$paramsProps = [];
$companyClauseProps = '';
if ($applyCompanyFilter && colExists('properties','company_id')) { $companyClauseProps = " WHERE (company_id = ? OR company_id IS NULL)"; $paramsProps[] = $companyId; }
$sp = $pdo->prepare("SELECT id, {$titleCol} AS title FROM properties{$companyClauseProps} ORDER BY {$titleCol} ASC LIMIT 200");
$sp->execute($paramsProps); $propertyOptions = $sp->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
}
} catch (Throwable $e) {}
$transactions = [];
try {
$hasUsersTbl = $pdo->query("SHOW TABLES LIKE 'users'")->rowCount() > 0;
$nameExprU = ($hasUsersTbl && colExists('users','name')) ? 'u.name' : 'NULL';
$joinUsers = $hasUsersTbl ? " LEFT JOIN users u ON p.user_id = u.id " : " ";
$companyClause = '';
$params = [];
if ($applyCompanyFilter && colExists('payments','company_id')) {
$companyClause = " AND ((p.company_id = ? OR p.company_id IS NULL OR p.company_id = 0)" . (colExists('payments','reference') ? " OR UPPER(COALESCE(p.reference,'')) LIKE 'FORM_FEE%'" : "") . ")";
$params[] = $companyId;
}
$receiptExpr = colExists('payments','receipt_file') ? 'p.receipt_file' : (colExists('payments','proof_file') ? 'p.proof_file' : "NULL");
$dateExpr = colExists('payments','created_at') ? 'p.created_at' : (colExists('payments','date') ? 'p.date' : (colExists('payments','updated_at') ? 'p.updated_at' : "NOW()"));
$orderSortExpr = "CASE WHEN {$dateExpr} > DATE_ADD(NOW(), INTERVAL 1 DAY) THEN 0 ELSE UNIX_TIMESTAMP({$dateExpr}) END";
$paymentTypeExpr = colExists('payments','reference')
? "CASE WHEN UPPER(COALESCE(p.reference,'')) LIKE 'FORM_FEE%' THEN 'Form Fee' ELSE 'Property Payment' END AS payment_type"
: "'Property Payment' AS payment_type";
$paymentTypeRawExpr = colExists('payments','payment_type') ? "p.payment_type AS payment_type_raw" : "NULL AS payment_type_raw";
$submittedByNameExpr = colExists('payments','submitted_by_name') ? "p.submitted_by_name" : "NULL";
$submittedByRoleExpr = colExists('payments','submitted_by_role') ? "p.submitted_by_role" : "NULL";
$sql = "
SELECT
p.id,
p.user_id,
$nameExprU AS client_name,
" . (colExists('payments','deal_id') ? "p.deal_id" : "NULL AS deal_id") . ",
" . (colExists('payments','allocation_id') ? "p.allocation_id" : "NULL AS allocation_id") . ",
" . (colExists('payments','project_id') ? "p.project_id" : "NULL AS project_id") . ",
$paymentTypeExpr,
$paymentTypeRawExpr,
p.amount,
$receiptExpr AS receipt_file,
$dateExpr AS created_at,
p.status,
{$submittedByNameExpr} AS submitted_by_name,
{$submittedByRoleExpr} AS submitted_by_role
" . (colExists('payments','meta_json') ? ", p.meta_json AS meta_json" : ", NULL AS meta_json") . "
FROM payments p
".$joinUsers."
WHERE p.status IN $queueStatusesSql".$companyClause."
ORDER BY p.id DESC
";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$transactions = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
if (!empty($transactions) && ($filterProjectId > 0 || $filterProperty !== '' || $filterPaidFor !== 'all' || $filterUploadedBy !== 'all')) {
$allocProjectMap = [];
if ($filterProjectId > 0 && colExists('payments','allocation_id') && colExists('payments','project_id')) {
try {
if ($pdo->query("SHOW TABLES LIKE 'allocations'")->rowCount() > 0 && $pdo->query("SHOW TABLES LIKE 'properties'")->rowCount() > 0 && $pdo->query("SHOW TABLES LIKE 'projects'")->rowCount() > 0) {
$allocIds = [];
foreach ($transactions as $r0) {
$pid0 = (int)($r0['project_id'] ?? 0);
$aid0 = (int)($r0['allocation_id'] ?? 0);
if ($pid0 <= 0 && $aid0 > 0) { $allocIds[$aid0] = true; }
}
$allocIds = array_keys($allocIds);
if (!empty($allocIds)) {
$ph = implode(',', array_fill(0, count($allocIds), '?'));
$cmpJoin = '';
$cmpParams = [];
if ($applyCompanyFilter && colExists('projects','company_id') && colExists('payments','company_id')) { $cmpJoin = " AND (prj.company_id = ? OR prj.company_id IS NULL)"; $cmpParams[] = $companyId; }
$sqlMap = "
SELECT a.id AS allocation_id, prj.id AS project_id
FROM allocations a
JOIN properties pr ON pr.id = a.property_id
JOIN projects prj ON prj.ref_table = 'properties' AND prj.ref_id = pr.id$cmpJoin
WHERE a.id IN ($ph)
";
$stM = $pdo->prepare($sqlMap);
$stM->execute(array_merge($allocIds, $cmpParams));
$rowsM = $stM->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsM as $rm) {
$aid = (int)($rm['allocation_id'] ?? 0);
$pid = (int)($rm['project_id'] ?? 0);
if ($aid > 0 && $pid > 0) { $allocProjectMap[$aid] = $pid; }
}
}
}
} catch (Throwable $e) { $allocProjectMap = []; }
}
$filtered = [];
foreach ($transactions as $r) {
if ($filterProjectId > 0) {
$effPid = (int)($r['project_id'] ?? 0);
if ($effPid <= 0) {
$aid = (int)($r['allocation_id'] ?? 0);
if ($aid > 0 && isset($allocProjectMap[$aid])) { $effPid = (int)$allocProjectMap[$aid]; }
}
if ($effPid !== (int)$filterProjectId) { continue; }
}
$propText = '';
$subRole = '';
$subUid = 0;
try {
if (colExists('payments','meta_json')) {
$qpm = $pdo->prepare("SELECT meta_json FROM payments WHERE id = ? LIMIT 1");
$qpm->execute([(int)$r['id']]);
$mjRaw = (string)($qpm->fetchColumn() ?: '');
if ($mjRaw !== '') {
$m = json_decode($mjRaw, true) ?: [];
$propText = (string)($m['project_desc'] ?? ($m['property'] ?? ''));
$subRole = strtolower((string)($m['submitted_by_role'] ?? ''));
$subUid = (int)($m['submitted_by_user'] ?? 0);
if (isset($m['submitted_by_name']) && trim((string)$m['submitted_by_name']) !== '') { $r['submitted_by_name'] = (string)$m['submitted_by_name']; }
if (isset($m['marketer_name']) && trim((string)$m['marketer_name']) !== '') { $r['marketer_name'] = (string)$m['marketer_name']; }
} elseif (colExists('payments','deal_id') && colExists('deals','meta_json') && isset($r['deal_id']) && (int)$r['deal_id'] > 0) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE id = ? LIMIT 1");
$qd->execute([(int)$r['deal_id']]);
$dm = (string)($qd->fetchColumn() ?: '');
if ($dm !== '') {
$m = json_decode($dm, true) ?: [];
$propText = (string)($m['project_desc'] ?? ($m['property'] ?? ($m['property_estate'] ?? '')));
$subRole = strtolower((string)($m['submitted_by_role'] ?? ''));
$subUid = (int)($m['submitted_by_user'] ?? 0);
if (isset($m['submitted_by_name']) && trim((string)$m['submitted_by_name']) !== '') { $r['submitted_by_name'] = (string)$m['submitted_by_name']; }
if (isset($m['marketer_name']) && trim((string)$m['marketer_name']) !== '') { $r['marketer_name'] = (string)$m['marketer_name']; }
}
}
}
} catch (Throwable $eM) {}
$uploaderGroup = '';
if (in_array($subRole, ['marketing','sales','sales_agent','agent'], true)) { $uploaderGroup = 'marketing'; }
elseif (in_array($subRole, ['contact_rep','customer_rep','contact_centre','contact_center','customer_care'], true)) { $uploaderGroup = 'contact'; }
elseif ($subUid === 0) { $uploaderGroup = 'client'; }
else { $uploaderGroup = 'client'; }
if ($filterProperty !== '' && ($propText === '' || stripos($propText, $filterProperty) === false)) { continue; }
if ($filterPaidFor === 'form_fee' && strtolower((string)$r['payment_type']) !== 'form fee') { continue; }
if ($filterPaidFor === 'property' && strtolower((string)$r['payment_type']) !== 'property payment') { continue; }
if ($filterUploadedBy !== 'all' && $uploaderGroup !== $filterUploadedBy) { continue; }
$r['property_text'] = $propText;
$r['submitted_group'] = $uploaderGroup;
$filtered[] = $r;
}
$transactions = $filtered;
}
if (!empty($transactions)) {
$hasClientsTbl = $pdo->query("SHOW TABLES LIKE 'clients'")->rowCount() > 0;
foreach ($transactions as &$r) {
if (empty($r['client_name'])) {
$uid = (int)($r['user_id'] ?? 0);
$nm = '';
if ($hasUsersTbl && $uid > 0) {
if (colExists('users','name')) {
$qs = $pdo->prepare("SELECT name FROM users WHERE id = ?");
$qs->execute([$uid]); $nm = (string)($qs->fetchColumn() ?: '');
}
if ($nm === '' && colExists('users','first_name') && colExists('users','last_name')) {
$qs = $pdo->prepare("SELECT CONCAT(first_name,' ',last_name) FROM users WHERE id = ?");
$qs->execute([$uid]); $nm = (string)($qs->fetchColumn() ?: '');
}
if ($nm === '' && colExists('users','full_name')) {
$qs = $pdo->prepare("SELECT full_name FROM users WHERE id = ?");
$qs->execute([$uid]); $nm = (string)($qs->fetchColumn() ?: '');
}
}
if ($nm === '' && $uid > 0) { $nm = 'Client #'.$uid; }
$r['client_name'] = $nm !== '' ? $nm : ($r['client_name'] ?? '');
}
}
unset($r);
}
} catch (Throwable $e) {}
// Ensure metric pending count matches the visible queue exactly
$metrics['pending'] = is_array($transactions) ? count($transactions) : 0;
$perPage = isset($_GET['per_page']) ? (int)$_GET['per_page'] : 50;
$perPage = max(10, min(200, $perPage));
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$page = max(1, $page);
$totalRows = is_array($transactions) ? count($transactions) : 0;
$totalPages = $perPage > 0 ? (int)ceil($totalRows / $perPage) : 1;
if ($totalPages <= 0) { $totalPages = 1; }
if ($page > $totalPages) { $page = $totalPages; }
$offset = ($page - 1) * $perPage;
$transactionsPage = is_array($transactions) ? array_slice($transactions, $offset, $perPage) : [];
?>
<div class="container-fluid px-4">
<div class="d-flex justify-content-between align-items-center mt-4 mb-3">
<h1 class="h3 mb-0"><i class="fa-solid fa-list-check me-2"></i>Finance Payment Approvals</h1>
<div class="d-flex align-items-center gap-2">
<?php if ($canVerify): ?>
<span class="badge bg-primary">Role: <?= htmlspecialchars($role) ?></span>
<?php else: ?>
<span class="badge bg-secondary">Read-only</span>
<?php endif; ?>
</div>
</div>
<?php if ($canVerify && isset($_GET['debug']) && $_GET['debug'] === '1'): ?>
<div class="card mt-3">
<div class="card-header">Debug</div>
<div class="card-body">
<?php
$pid = isset($_GET['pid']) ? (int)$_GET['pid'] : 0;
if ($pid > 0) {
try {
$refExprDbg = colExists('payments','reference') ? 'reference' : (colExists('payments','ref') ? 'ref' : "''");
$pmExprDbg = colExists('payments','payment_method') ? 'payment_method' : (colExists('payments','method') ? 'method' : "''");
$mjExprDbg = colExists('payments','meta_json') ? 'meta_json' : 'NULL';
$sqlDbg = "SELECT id, user_id, amount, {$refExprDbg} AS reference, {$pmExprDbg} AS pm, {$mjExprDbg} AS meta_json FROM payments WHERE id = ? LIMIT 1";
$stp = $pdo->prepare($sqlDbg);
$stp->execute([$pid]);
$prow = $stp->fetch(PDO::FETCH_ASSOC) ?: null;
if ($prow) {
$meta = [];
if (!empty($prow['meta_json'])) { $t = json_decode((string)$prow['meta_json'], true); if (is_array($t)) { $meta = $t; } }
$clientEmail = (string)($meta['client_email'] ?? '');
if ($clientEmail === '' && (int)$prow['user_id'] > 0 && colExists('users','email')) { try { $qe = $pdo->prepare("SELECT email FROM users WHERE id = ? LIMIT 1"); $qe->execute([(int)$prow['user_id']]); $clientEmail = (string)($qe->fetchColumn() ?: ''); } catch (Throwable $e) {} }
$dealSource = (string)($meta['deal_source'] ?? '');
$marketerName = (string)($meta['marketer_name'] ?? '');
$clientPhone = '';
if ((int)$prow['user_id'] > 0 && colExists('users','phone')) {
try { $qp = $pdo->prepare("SELECT phone FROM users WHERE id = ? LIMIT 1"); $qp->execute([(int)$prow['user_id']]); $clientPhone = (string)($qp->fetchColumn() ?: ''); } catch (Throwable $e) {}
}
if ($clientPhone === '') {
$phoneKeys = ['phone','Phone','mobile','Mobile','company_contact','Company Contact','contact','Contact'];
foreach ($phoneKeys as $pk) { if (!empty($meta[$pk]) && is_string($meta[$pk]) && trim($meta[$pk]) !== '') { $clientPhone = trim((string)$meta[$pk]); break; } }
}
$mkAccNo = is_array($meta['marketer_bank'] ?? null) ? (string)($meta['marketer_bank']['acc_no'] ?? '') : '';
$mkBank = is_array($meta['marketer_bank'] ?? null) ? (string)($meta['marketer_bank']['bank'] ?? '') : '';
$mkAccName = is_array($meta['marketer_bank'] ?? null) ? (string)($meta['marketer_bank']['acc_name'] ?? '') : '';
$agAccNo = is_array($meta['agent_bank'] ?? null) ? (string)($meta['agent_bank']['acc_no'] ?? '') : '';
$agBank = is_array($meta['agent_bank'] ?? null) ? (string)($meta['agent_bank']['bank'] ?? '') : '';
$agAccName = is_array($meta['agent_bank'] ?? null) ? (string)($meta['agent_bank']['acc_name'] ?? '') : '';
$notes = is_string($meta['client_payment_status'] ?? null) ? (string)$meta['client_payment_status'] : '';
if ($notes === '' && colExists('payments','deal_id') && colExists('deals','notes') && !empty($prow['deal_id'])) {
try { $qn = $pdo->prepare("SELECT notes FROM deals WHERE id = ? LIMIT 1"); $qn->execute([(int)$prow['deal_id']]); $notes = (string)($qn->fetchColumn() ?: ''); } catch (Throwable $e) {}
}
$submittedBy = '';
$sbCandidates = [
$meta['marketer_name'] ?? null,
$meta['contact_rep'] ?? null,
$meta['contact_centre_name'] ?? null,
$meta['submitted_by_name'] ?? null,
$meta['Submitted By'] ?? null,
$meta['submitted_by'] ?? null,
$meta['SUBMITTED BY'] ?? null,
$meta['created_by_name'] ?? null,
$meta['sales_agent'] ?? null,
$meta['sales_rep'] ?? null,
$meta['uploaded_by_name'] ?? null,
$meta['agent_name'] ?? null,
];
foreach ($sbCandidates as $cand) {
$val = is_string($cand) ? trim($cand) : '';
if ($val !== '') { $submittedBy = $val; break; }
}
if ($submittedBy === '' && isset($meta['submitted_by_user'])) {
$subUidFull = (int)$meta['submitted_by_user'];
if ($subUidFull > 0 && colExists('users','name')) {
try { $qn = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1"); $qn->execute([$subUidFull]); $n = (string)($qn->fetchColumn() ?: ''); if ($n !== '') { $submittedBy = $n; } } catch (Throwable $e) {}
}
}
echo '<div class="table-responsive"><table class="table table-sm table-bordered w-auto">';
echo '<tr><th>Payment ID</th><td>'.(int)$prow['id'].'</td></tr>';
echo '<tr><th>User ID</th><td>'.(int)$prow['user_id'].'</td></tr>';
echo '<tr><th>Reference</th><td>'.htmlspecialchars((string)$prow['reference']).'</td></tr>';
echo '<tr><th>Payment Method</th><td>'.htmlspecialchars((string)$prow['pm']).'</td></tr>';
echo '<tr><th>Client Email</th><td>'.htmlspecialchars($clientEmail).'</td></tr>';
echo '<tr><th>Client Phone</th><td>'.htmlspecialchars($clientPhone).'</td></tr>';
echo '<tr><th>Marketer or Contact Centre Name</th><td>'.htmlspecialchars($marketerName).'</td></tr>';
// Deal Source intentionally hidden
echo '<tr><th>Submitted By</th><td>'.htmlspecialchars($submittedBy).'</td></tr>';
if ($mkAccNo !== '' || $mkBank !== '' || $mkAccName !== '') {
echo '<tr><th>Marketer Account</th><td>'.htmlspecialchars(trim(($mkAccName?:'').' — '.($mkBank?:'').' — '.($mkAccNo?:''))).'</td></tr>';
}
if ($agAccNo !== '' || $agBank !== '' || $agAccName !== '') {
echo '<tr><th>Agent Account</th><td>'.htmlspecialchars(trim(($agAccName?:'').' — '.($agBank?:'').' — '.($agAccNo?:''))).'</td></tr>';
}
if ($notes !== '') {
echo '<tr><th>Notes</th><td>'.htmlspecialchars($notes).'</td></tr>';
}
echo '</table></div>';
} else {
echo '<div class="alert alert-warning">Payment not found.</div>';
}
} catch (Throwable $e) {
echo '<div class="alert alert-danger">Error fetching payment.</div>';
}
}
$lookupMatches = [];
$pendingPreview = [];
$q = isset($_GET['q']) ? trim($_GET['q']) : '';
$uidLookup = isset($_GET['uid']) ? (int)$_GET['uid'] : 0;
try {
// Build pending preview (limited union)
$hasUsersTbl = $pdo->query("SHOW TABLES LIKE 'users'")->rowCount() > 0;
$nameExprU = ($hasUsersTbl && colExists('users','name')) ? 'u.name' : 'NULL';
$joinUsersTx = $hasUsersTbl ? " LEFT JOIN users u ON t.user_id = u.id " : " ";
$joinUsersPm = $hasUsersTbl ? " LEFT JOIN users u ON p.user_id = u.id " : " ";
$txCompanyClause = '';
$pmCompanyClause = '';
$paramsPreview = [];
if ($applyCompanyFilter && colExists('transactions','company_id')) { $txCompanyClause = " AND (t.company_id = ? OR t.company_id IS NULL)"; $paramsPreview[] = $companyId; }
if ($applyCompanyFilter && colExists('payments','company_id')) { $pmCompanyClause = " AND (p.company_id = ? OR p.company_id IS NULL)"; $paramsPreview[] = $companyId; }
$receiptExpr = colExists('payments','receipt_file') ? 'p.receipt_file' : (colExists('payments','proof_file') ? 'p.proof_file' : "NULL");
$dateExpr = colExists('payments','created_at') ? 'p.created_at' : (colExists('payments','date') ? 'p.date' : (colExists('payments','updated_at') ? 'p.updated_at' : "NOW()"));
$uidFilterTx = $uidLookup > 0 ? " AND t.user_id = ?" : "";
$uidFilterPm = $uidLookup > 0 ? " AND p.user_id = ?" : "";
if ($uidLookup > 0) { $paramsPreview[] = $uidLookup; $paramsPreview[] = $uidLookup; }
$sqlPreview = "
SELECT t.id, t.user_id, $nameExprU AS client_name, t.amount, t.receipt_file AS receipt_file, t.created_at, t.status, 'transactions' AS source
FROM transactions t
$joinUsersTx
WHERE t.status IN ('pending_verification','pending_confirmation')$txCompanyClause$uidFilterTx
UNION
SELECT p.id, p.user_id, $nameExprU AS client_name, p.amount, $receiptExpr AS receipt_file, $dateExpr AS created_at, p.status, 'payments' AS source
FROM payments p
$joinUsersPm
WHERE p.status IN $queueStatusesSql$pmCompanyClause$uidFilterPm
ORDER BY created_at DESC
LIMIT 50
";
$stPr = $pdo->prepare($sqlPreview);
$stPr->execute($paramsPreview);
$pendingPreview = $stPr->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {}
try {
// User/client lookup by name
if ($q !== '') {
$like = '%' . $q . '%';
// users table
if ($pdo->query("SHOW TABLES LIKE 'users'")->rowCount() > 0) {
$sqlU = "SELECT id,
" . (colExists('users','name') ? "name" : (colExists('users','full_name') ? "full_name" : (colExists('users','first_name') && colExists('users','last_name') ? "CONCAT(first_name,' ',last_name)" : "''"))) . " AS display_name,
" . (colExists('users','email') ? "email" : "''") . " AS email,
" . (colExists('users','role') ? "role" : "''") . " AS role
FROM users
WHERE " . (colExists('users','name') ? "name LIKE ?" : (colExists('users','full_name') ? "full_name LIKE ?" : (colExists('users','first_name') && colExists('users','last_name') ? "CONCAT(first_name,' ',last_name) LIKE ?" : "1=0"))) . "
LIMIT 20";
$su = $pdo->prepare($sqlU); $su->execute([$like]);
$lookupMatches = array_merge($lookupMatches, $su->fetchAll(PDO::FETCH_ASSOC) ?: []);
}
// clients table
if ($pdo->query("SHOW TABLES LIKE 'clients'")->rowCount() > 0) {
$sqlC = "SELECT id,
" . (colExists('clients','name') ? "name" : (colExists('clients','full_name') ? "full_name" : (colExists('clients','first_name') && colExists('clients','last_name') ? "CONCAT(first_name,' ',last_name)" : "''"))) . " AS display_name
FROM clients
WHERE " . (colExists('clients','name') ? "name LIKE ?" : (colExists('clients','full_name') ? "full_name LIKE ?" : (colExists('clients','first_name') && colExists('clients','last_name') ? "CONCAT(first_name,' ',last_name) LIKE ?" : "1=0"))) . "
LIMIT 20";
$sc = $pdo->prepare($sqlC); $sc->execute([$like]);
$rowsC = $sc->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rowsC as $rc) { $lookupMatches[] = ['id'=>$rc['id'],'display_name'=>$rc['display_name'],'email'=>'','role'=>'client']; }
}
}
} catch (Throwable $e) {}
$txCounts = [];
$pmCounts = [];
try {
$sqlD = "SELECT status, COUNT(*) AS cnt FROM transactions GROUP BY status";
$stD = $pdo->query($sqlD);
$txCounts = $stD ? $stD->fetchAll(PDO::FETCH_ASSOC) : [];
} catch (Throwable $e) {}
try {
if ($pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0) {
$sqlP = "SELECT status, COUNT(*) AS cnt FROM payments GROUP BY status";
$stP = $pdo->query($sqlP);
$pmCounts = $stP ? $stP->fetchAll(PDO::FETCH_ASSOC) : [];
}
} catch (Throwable $e) {}
?>
<form method="get" class="mb-3 d-flex gap-2">
<input type="hidden" name="debug" value="1" />
<input type="text" name="q" class="form-control" placeholder="Search client name (e.g., Andrew Ezekiel)" value="<?= htmlspecialchars($q) ?>" />
<input type="number" name="uid" class="form-control" placeholder="User ID (optional)" value="<?= (int)$uidLookup ?>" />
<button class="btn btn-outline-dark" type="submit">Lookup</button>
</form>
<div class="row">
<div class="col-md-6">
<div class="small text-muted mb-1">Transactions by status</div>
<ul class="list-group list-group-flush">
<?php foreach ($txCounts as $c): ?>
<li class="list-group-item d-flex justify-content-between"><span><?= htmlspecialchars($c['status'] ?? '-') ?></span><strong><?= (int)($c['cnt'] ?? 0) ?></strong></li>
<?php endforeach; ?>
<?php if (empty($txCounts)): ?><li class="list-group-item text-muted">No data</li><?php endif; ?>
</ul>
</div>
<div class="col-md-6">
<div class="small text-muted mb-1">Payments by status</div>
<ul class="list-group list-group-flush">
<?php foreach ($pmCounts as $c): ?>
<li class="list-group-item d-flex justify-content-between"><span><?= htmlspecialchars($c['status'] ?? '-') ?></span><strong><?= (int)($c['cnt'] ?? 0) ?></strong></li>
<?php endforeach; ?>
<?php if (empty($pmCounts)): ?><li class="list-group-item text-muted">No data</li><?php endif; ?>
</ul>
</div>
</div>
<hr class="my-3" />
<div class="small text-muted mb-1">Pending preview (latest 50)</div>
<div class="table-responsive mb-3">
<table class="table table-sm">
<thead>
<tr>
<th>Source</th>
<th>ID</th>
<th>User ID</th>
<th>Client</th>
<th>Amount</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php foreach ($pendingPreview as $pp): ?>
<tr>
<td class="text-muted"><?= htmlspecialchars($pp['source'] ?? '-') ?></td>
<td>#<?= (int)$pp['id'] ?></td>
<td><?= (int)$pp['user_id'] ?></td>
<td><?= htmlspecialchars($pp['client_name'] ?? '') ?></td>
<td>₦<?= number_format((float)($pp['amount'] ?? 0)) ?></td>
<td><?= htmlspecialchars($pp['status'] ?? '') ?></td>
<td><?= htmlspecialchars($pp['created_at'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($pendingPreview)): ?>
<tr><td colspan="7" class="text-muted text-center">No pending entries found.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="small text-muted mb-1">User lookup</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>User ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<?php foreach ($lookupMatches as $m): ?>
<tr>
<td><?= (int)($m['id'] ?? 0) ?></td>
<td><?= htmlspecialchars($m['display_name'] ?? '') ?></td>
<td><?= htmlspecialchars($m['email'] ?? '') ?></td>
<td><span class="badge bg-light text-dark"><?= htmlspecialchars($m['role'] ?? '') ?></span></td>
</tr>
<?php endforeach; ?>
<?php if (empty($lookupMatches)): ?>
<tr><td colspan="4" class="text-muted text-center">No matches</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
<?php if (!empty($action_msg)): ?><div class="alert alert-info"><?= htmlspecialchars($action_msg) ?></div><?php endif; ?>
<div class="row g-3">
<div class="col-md-3"><div class="card p-3"><div class="text-muted small">Pending Payments</div><div class="fw-bold fs-5"><?= (int)$metrics['pending'] ?></div></div></div>
<div class="col-md-3"><div class="card p-3"><div class="text-muted small">Approved Today</div><div class="fw-bold fs-5"><?= (int)$metrics['approved_today'] ?></div></div></div>
<div class="col-md-3"><div class="card p-3"><div class="text-muted small">Rejected Payments</div><div class="fw-bold fs-5"><?= (int)$metrics['rejected'] ?></div></div></div>
<div class="col-md-3"><div class="card p-3"><div class="text-muted small">Total Payment Volume</div><div class="fw-bold fs-5">₦<?= number_format((float)$metrics['volume']) ?></div></div></div>
</div>
<div class="card mt-4">
<div class="card-header">
<div class="fw-semibold fs-5">Verification Queue</div>
<div class="text-muted small">Review and approve incoming payment submissions.</div>
</div>
<?php
$countPending = 0;
$countApproved = 0;
$countRejected = 0;
foreach (($transactions ?? []) as $__r) {
$stx = strtolower((string)($__r['status'] ?? ''));
$isPendingX = in_array($stx, $queueStatuses, true);
if ($isPendingX) { $countPending++; }
if ($stx === 'approved') { $countApproved++; }
if ($stx === 'rejected') { $countRejected++; }
}
?>
<div class="p-3 border-bottom bg-white">
<style>
.fin-filter-card { width: 100%; background: #ffffff; border: 1px solid #e9ecef; border-radius: 16px; padding: 10px 12px; box-shadow: 0 10px 22px rgba(0,0,0,.04); }
.fin-filter-tabs .nav-link { border-radius: 999px; padding: .4rem .75rem; }
.fin-filter-tabs .nav-link .badge { margin-left: .4rem; }
.fin-bulk-bar { border: 1px solid #e9ecef; background: #ffffff; border-radius: 999px; padding: 6px 10px; }
.verification-table-wrap { display: block; width: 100%; max-width: 100%; min-width: 0; overflow-x: auto; overflow-y: visible; -webkit-overflow-scrolling: touch; }
.verification-table-wrap .table-responsive { display: block; width: 100%; max-width: 100%; min-width: 0; overflow: visible; }
.fin-verify-table { min-width: 1180px; }
.fin-verify-table thead th { position: sticky; top: 0; z-index: 3; background: #ffffff; }
.fin-verify-table thead th { box-shadow: inset 0 -1px 0 rgba(0,0,0,.08); }
.fin-verify-table th, .fin-verify-table td { padding: .85rem .75rem; }
.fin-verify-table tbody tr.verif-row { transition: transform .12s ease, box-shadow .12s ease; }
.fin-verify-table tbody tr.verif-row:hover { transform: translateY(-1px); box-shadow: 0 10px 22px rgba(0,0,0,.06); }
.fin-verify-table tbody tr.verif-row:hover td { box-shadow: inset 0 0 0 1px rgba(13,110,253,.12); }
.fin-status-badge { font-weight: 700; letter-spacing: .2px; }
.badge-status-pending { background: #fd7e14; color: #1f2d3d; }
.badge-status-approved { background: #198754; color: #ffffff; }
.badge-status-rejected { background: #dc3545; color: #ffffff; }
.fin-verify-table th:nth-child(9), .fin-verify-table td:nth-child(9) { width: 140px; }
.fin-verify-table th:nth-child(10), .fin-verify-table td:nth-child(10) { width: 120px; }
.fin-verify-table td:nth-child(9), .fin-verify-table td:nth-child(10) { white-space: nowrap; }
#verificationTable th:last-child, #verificationTable td:last-child { min-width: 220px; white-space: nowrap; }
.fin-action-buttons { display: flex; align-items: center; justify-content: flex-end; gap: .5rem; flex-wrap: nowrap; white-space: nowrap; }
.fin-action-buttons .btn { min-width: 92px; padding: .25rem .5rem; }
.fin-verify-table .reject-btn { font-weight: 800; }
@media (max-width: 1199.98px) {
.verification-table-wrap { overflow-x: visible; }
.verification-table-wrap table { min-width: 0 !important; width: 100%; }
.fin-verify-table thead { display: none; }
.fin-verify-table, .fin-verify-table tbody, .fin-verify-table tr, .fin-verify-table td { display: block; width: 100%; }
.fin-verify-table tbody tr.verif-row { background: #ffffff; border: 1px solid #e9ecef; border-radius: 16px; overflow: hidden; margin-bottom: 12px; box-shadow: 0 10px 22px rgba(0,0,0,.04); }
.fin-verify-table tbody tr.verif-row:hover { transform: none; box-shadow: 0 10px 22px rgba(0,0,0,.04); }
.fin-verify-table tbody tr.verif-row:hover td { box-shadow: none; }
.fin-verify-table tbody tr.verif-row td { padding: .6rem .85rem; border-top: 1px solid #eef0f2; display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.fin-verify-table tbody tr.verif-row td:first-child { border-top: 0; }
.fin-verify-table tbody tr.verif-row td::before { content: attr(data-label); font-weight: 800; color: #64748b; flex: 0 0 42%; }
.fin-verify-table tbody tr.verif-row td[data-label="Select"] { justify-content: flex-end; }
.fin-verify-table tbody tr.verif-row td[data-label="Select"]::before { display: none; }
.fin-action-buttons { width: 100%; justify-content: flex-end; flex-wrap: wrap; }
.fin-action-buttons .btn { width: auto; }
.fin-verify-table tr.verif-collapse-row { margin: -8px 0 12px; }
.fin-verify-table tr.verif-collapse-row td { padding: 0; border: 0; display: block; }
.fin-verify-table tr.verif-collapse-row td::before { display: none; }
}
</style>
<div class="fin-filter-card mb-3">
<div class="small fw-semibold mb-2">Filter Verification Queue</div>
<form method="get" class="d-flex align-items-center gap-2 flex-wrap">
<select name="project_id" class="form-select form-select-sm" style="min-width: 220px;">
<option value="0">All Projects</option>
<?php foreach ($projectOptions as $pj): ?>
<?php $pid = (int)($pj['id'] ?? 0); if ($pid <= 0) continue; ?>
<?php $label = (string)($pj['name'] ?? ''); $t = trim((string)($pj['type'] ?? '')); if ($t !== '') { $label .= " (" . $t . ")"; } ?>
<option value="<?= $pid ?>" <?= $filterProjectId === $pid ? 'selected' : '' ?>><?= htmlspecialchars($label) ?></option>
<?php endforeach; ?>
</select>
<input list="properties-list" name="property" class="form-control form-control-sm" placeholder="Property" value="<?= htmlspecialchars($filterProperty) ?>" style="min-width: 200px;" />
<datalist id="properties-list">
<?php foreach ($propertyOptions as $po): ?>
<option value="<?= htmlspecialchars($po['title'] ?? '') ?>"></option>
<?php endforeach; ?>
</datalist>
<select name="paid_for" class="form-select form-select-sm">
<option value="all" <?= $filterPaidFor==='all'?'selected':'' ?>>All Types</option>
<option value="form_fee" <?= $filterPaidFor==='form_fee'?'selected':'' ?>>Form Fee</option>
<option value="property" <?= $filterPaidFor==='property'?'selected':'' ?>>Property Payment</option>
</select>
<select name="uploaded_by" class="form-select form-select-sm">
<option value="all" <?= $filterUploadedBy==='all'?'selected':'' ?>>Any Uploader</option>
<option value="marketing" <?= $filterUploadedBy==='marketing'?'selected':'' ?>>Marketing/Sales</option>
<option value="contact" <?= $filterUploadedBy==='contact'?'selected':'' ?>>Contact Centre</option>
<option value="client" <?= $filterUploadedBy==='client'?'selected':'' ?>>Client</option>
</select>
<button class="btn btn-sm btn-primary" type="submit">Filter</button>
<a class="btn btn-sm btn-outline-secondary" href="finance-payments.php">Reset</a>
</form>
</div>
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
<div class="nav nav-pills fin-filter-tabs" id="verifyTabs">
<button type="button" class="nav-link active" data-filter="all">All <span class="badge bg-light text-dark border"><?= number_format((int)count($transactions ?? [])) ?></span></button>
<button type="button" class="nav-link" data-filter="pending">Pending <span class="badge bg-light text-dark border"><?= number_format($countPending) ?></span></button>
<button type="button" class="nav-link" data-filter="approved">Approved <span class="badge bg-light text-dark border"><?= number_format($countApproved) ?></span></button>
<button type="button" class="nav-link" data-filter="rejected">Rejected <span class="badge bg-light text-dark border"><?= number_format($countRejected) ?></span></button>
</div>
<div class="fin-bulk-bar d-none" id="bulkBar">
<div class="d-flex align-items-center gap-2">
<span class="text-muted small"><span id="bulkSelectedCount">0</span> selected</span>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-success" id="bulkApproveBtn" disabled>Approve selected</button>
<button type="button" class="btn btn-outline-danger" id="bulkRejectBtn" disabled>Reject selected</button>
</div>
</div>
</div>
</div>
</div>
<div class="verification-table-wrap table-wrapper">
<div class="table-responsive">
<table class="table table-hover align-middle fin-verify-table" id="verificationTable">
<thead>
<tr>
<th class="px-2"><input type="checkbox" class="form-check-input" id="selectAllRows" /></th>
<th>Payment ID</th>
<th>Client Name</th>
<th>Submitted By</th>
<th>Payment Type</th>
<th>Plan</th>
<th>Amount</th>
<th>Receipt</th>
<th>Upload Date</th>
<th>Status</th>
<th>View Details</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($transactionsPage as $row): ?>
<?php
$meta = [];
$modalMetaJson = '';
$dealIdRef = (int)($row['deal_id'] ?? 0);
$payTypeDb = (string)($row['payment_type_raw'] ?? '');
$subNameDb = (string)($row['submitted_by_name'] ?? '');
$subRoleDb = (string)($row['submitted_by_role'] ?? '');
if (colExists('payments','meta_json') && isset($row['meta_json']) && is_string($row['meta_json']) && trim($row['meta_json']) !== '') {
try {
$m0 = json_decode((string)$row['meta_json'], true);
if (is_array($m0) && !empty($m0)) {
$meta = $m0;
$modalMetaJson = (string)$row['meta_json'];
}
} catch (Throwable $ePm0) {}
}
if ((empty($meta) || !is_array($meta)) && $dealIdRef > 0 && colExists('deals','meta_json')) {
try {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE id = ? LIMIT 1");
$qd->execute([$dealIdRef]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
} catch (Throwable $eDeal) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$rcp = (string)($row['receipt_file'] ?? '');
if ($rcp !== '') {
if (colExists('deals','receipt_file') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file = ? LIMIT 1");
$qd->execute([$rcp]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file LIKE ? LIMIT 1");
$qd->execute(['%'.$base]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
}
}
} elseif (colExists('deals','proof_file') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file = ? LIMIT 1");
$qd->execute([$rcp]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$base = basename($rcp);
if ($base !== '') {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file LIKE ? LIMIT 1");
$qd->execute(['%'.$base]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
}
}
}
}
} catch (Throwable $eRcp) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid]);
$dmj = $qd->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
if (empty($meta)) {
$dt = (string)($row['created_at'] ?? '');
if ($dt !== '' && colExists('deals','created_at')) {
$qdt = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qdt->execute([$uid, $dt, $dt]);
$dmj = $qdt->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
}
}
}
} catch (Throwable $eUid) {}
}
if (empty($meta) || !is_array($meta)) {
try {
$ref = (string)($row['reference'] ?? '');
if ($ref !== '' && colExists('deals','created_at') && colExists('deals','meta_json')) {
$ts = null;
if (preg_match('/(\d{14})/', $ref, $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$windowStart = $ts->format('Y-m-d H:i:s');
$windowEnd = $ts->format('Y-m-d H:i:s');
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id')) {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$uid, $windowStart, $windowEnd]);
} else {
$qrf = $pdo->prepare("SELECT meta_json FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qrf->execute([$windowStart, $windowEnd]);
}
$dmj = $qrf->fetchColumn();
if ($dmj) { $meta = json_decode($dmj, true) ?: []; }
}
}
} catch (Throwable $eRef) {}
}
/* removed non-onboarding extras */
$collapseId = 'meta'.(int)$row['id'];
$proj = $meta['project_desc'] ?? '-';
$planType = isset($meta['plan_type']) ? (string)$meta['plan_type'] : '';
$dealSource = isset($meta['deal_source']) ? (string)$meta['deal_source'] : '';
$clientNameMeta = isset($meta['client_name']) ? (string)$meta['client_name'] : '';
$marketerName = isset($meta['marketer_name']) ? (string)$meta['marketer_name'] : '';
if (is_string($proj) && trim($proj) === '-') { $proj = ''; }
if (is_string($planType) && trim($planType) === '-') { $planType = ''; }
if (is_string($dealSource) && trim($dealSource) === '-') { $dealSource = ''; }
if (is_string($marketerName) && trim($marketerName) === '-') { $marketerName = ''; }
$amountOffered = isset($meta['amount_offered']) ? (float)$meta['amount_offered'] : 0;
$amountPaidSoFar = isset($meta['amount_paid_so_far']) ? (float)$meta['amount_paid_so_far'] : 0;
$discountAmount = isset($meta['discount_amount']) ? (float)$meta['discount_amount'] : 0;
$discountBy = isset($meta['discount_approved_by']) ? (string)$meta['discount_approved_by'] : '';
if (is_string($discountBy) && trim($discountBy) === '-') { $discountBy = ''; }
$commissionPct = isset($meta['commission_pct']) ? (float)$meta['commission_pct'] : 0;
$marketerComm = isset($meta['marketer_comm']) ? (float)$meta['marketer_comm'] : 0;
$agentComm = isset($meta['agent_comm']) ? (float)$meta['agent_comm'] : 0;
$balanceRem = isset($meta['balance_remaining']) ? (float)$meta['balance_remaining'] : 0;
$statusNotes = isset($meta['client_payment_status']) ? (string)$meta['client_payment_status'] : '';
$paidNow = (float)($row['amount'] ?? 0);
if ($amountPaidSoFar <= 0 && $paidNow > 0) { $amountPaidSoFar = $paidNow; }
if ($balanceRem <= 0 && $amountOffered > 0) { $balanceRem = max(0.0, $amountOffered - $amountPaidSoFar); }
if ($commissionPct <= 0 && $marketerComm > 0) {
$baseAmt = $paidNow > 0 ? $paidNow : ($amountPaidSoFar > 0 ? $amountPaidSoFar : 0);
if ($baseAmt > 0) { $commissionPct = round(($marketerComm / $baseAmt) * 100, 2); }
}
if ((!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') && isset($subRoleFull) && $subRoleFull !== '') { $dealSource = ucfirst(strtolower($subRoleFull)); }
$mkRaw = isset($meta['marketer_bank']) && is_array($meta['marketer_bank']) ? $meta['marketer_bank'] : [];
$agRaw = isset($meta['agent_bank']) && is_array($meta['agent_bank']) ? $meta['agent_bank'] : [];
$mkAccNo = (string)($mkRaw['acc_no'] ?? ($mkRaw['account_number'] ?? ($mkRaw['accountNumber'] ?? ($mkRaw['accNo'] ?? ($meta['marketer_account_number'] ?? ($meta['marketer_acc_no'] ?? ($meta['marketer_acc_number'] ?? '')))))));
$mkBankName = (string)($mkRaw['bank'] ?? ($mkRaw['bank_name'] ?? ($mkRaw['bankName'] ?? ($meta['marketer_bank_name'] ?? ($meta['marketer_bank'] ?? '')))));
$mkAccName = (string)($mkRaw['acc_name'] ?? ($mkRaw['account_name'] ?? ($mkRaw['accountName'] ?? ($meta['marketer_account_name'] ?? ($meta['marketer_acc_name'] ?? '')))));
$agAccNo = (string)($agRaw['acc_no'] ?? ($agRaw['account_number'] ?? ($agRaw['accountNumber'] ?? ($agRaw['accNo'] ?? ($meta['agent_account_number'] ?? ($meta['agent_acc_no'] ?? ($meta['agent_acc_number'] ?? '')))))));
$agBankName = (string)($agRaw['bank'] ?? ($agRaw['bank_name'] ?? ($agRaw['bankName'] ?? ($meta['agent_bank_name'] ?? ($meta['agent_bank'] ?? '')))));
$agAccName = (string)($agRaw['acc_name'] ?? ($agRaw['account_name'] ?? ($agRaw['accountName'] ?? ($meta['agent_account_name'] ?? ($meta['agent_acc_name'] ?? '')))));
$mkBank = ['acc_no' => trim($mkAccNo), 'bank' => trim($mkBankName), 'acc_name' => trim($mkAccName)];
$agBank = ['acc_no' => trim($agAccNo), 'bank' => trim($agBankName), 'acc_name' => trim($agAccName)];
$transactionsMeta = isset($meta['transactions']) && is_array($meta['transactions']) ? $meta['transactions'] : [];
$quickProperty = isset($meta['property']) ? (string)$meta['property'] : '';
if ($quickProperty === '' && isset($meta['property_estate'])) { $quickProperty = (string)$meta['property_estate']; }
$quickNotes = isset($meta['notes']) ? (string)$meta['notes'] : '';
$submittedBy = '';
$subName = trim($subNameDb !== '' ? $subNameDb : (string)($meta['submitted_by_name'] ?? ''));
if ($subName === '') { $subName = trim((string)($meta['marketer_name'] ?? '')); }
$subRole = trim($subRoleDb !== '' ? $subRoleDb : (string)($meta['submitted_by_role'] ?? ''));
if ($subRole === '') { $subRole = trim((string)($meta['deal_source'] ?? '')); }
if ($subName !== '') { $submittedBy = $subName . ($subRole !== '' ? ' / ' . strtoupper($subRole) : ''); }
elseif (!empty($meta['submitted_by_user'])) {
$subUidInit = (int)$meta['submitted_by_user'];
$subRoleInit = (string)($meta['submitted_by_role'] ?? '');
$subNameInit = '';
try {
if ($subUidInit > 0 && colExists('users','name')) {
$qs = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
$qs->execute([$subUidInit]); $subNameInit = (string)($qs->fetchColumn() ?: '');
}
} catch (Throwable $eN0) {}
$submittedBy = '#'.$subUidInit . ($subNameInit !== '' ? ' '.$subNameInit : '') . ($subRoleInit !== '' ? ' / '.strtoupper($subRoleInit) : '');
}
$displayPaymentType = '';
$pt = strtolower(trim($payTypeDb !== '' ? $payTypeDb : (string)($meta['payment_type'] ?? '')));
if ($pt === '' && isset($meta['Payment Type'])) { $pt = strtolower(trim((string)$meta['Payment Type'])); }
if (is_string($row['payment_type'] ?? null) && strtolower(trim((string)$row['payment_type'])) === 'form fee') {
$displayPaymentType = 'Form Fee';
} elseif ($pt === 'infrastructure' || $pt === 'infra') {
$displayPaymentType = 'Infrastructure Fee';
} elseif ($pt === 'excavation' || $pt === 'excav') {
$displayPaymentType = 'Excavation Fee';
} elseif ($pt === 'land') {
$displayPaymentType = 'Land Payment';
} elseif ($pt !== '') {
$displayPaymentType = ucfirst($pt);
} else {
$displayPaymentType = 'Property Payment';
}
if ($dealIdRef > 0) {
try {
$sel = [];
if (colExists('deals','property_estate')) { $sel[] = 'property_estate'; }
if (colExists('deals','project_desc')) { $sel[] = 'project_desc'; }
if (colExists('deals','amount_offered')) { $sel[] = 'amount_offered'; }
if (colExists('deals','amount_paid_now')) { $sel[] = 'amount_paid_now'; }
if (colExists('deals','discount_amount')) { $sel[] = 'discount_amount'; }
if (colExists('deals','discount_approved_by')) { $sel[] = 'discount_approved_by'; }
if (colExists('deals','balance_remaining')) { $sel[] = 'balance_remaining'; }
if (colExists('deals','plan_type')) { $sel[] = 'plan_type'; }
if (colExists('deals','deal_source')) { $sel[] = 'deal_source'; }
if (colExists('deals','marketer_name')) { $sel[] = 'marketer_name'; }
if (colExists('deals','submitted_by_user')) { $sel[] = 'submitted_by_user'; }
if (colExists('deals','created_by')) { $sel[] = 'created_by'; }
if (colExists('deals','created_by_user')) { $sel[] = 'created_by_user'; }
if (colExists('deals','marketer_id')) { $sel[] = 'marketer_id'; }
if (colExists('deals','agent_id')) { $sel[] = 'agent_id'; }
if (colExists('deals','contact_rep_id')) { $sel[] = 'contact_rep_id'; }
if (!empty($sel)) {
$qd2 = $pdo->prepare("SELECT ".implode(',', $sel)." FROM deals WHERE id = ? LIMIT 1");
$qd2->execute([$dealIdRef]);
$dr = $qd2->fetch(PDO::FETCH_ASSOC) ?: [];
$pEst = (string)($dr['property_estate'] ?? ($dr['property'] ?? ($dr['estate'] ?? '')));
$pDesc = (string)($dr['project_desc'] ?? ($dr['project'] ?? ($dr['property_title'] ?? ($dr['property_name'] ?? ''))));
$pName = isset($dr['project_name']) ? (string)$dr['project_name'] : '';
$proj = ((is_string($proj) && trim($proj) !== '' && $proj !== '-') ? $proj : ($pName ?: ($pDesc ?: ($pEst ?: $proj))));
if (isset($dr['amount_offered'])) { $amountOffered = (float)$dr['amount_offered']; }
if (isset($dr['amount_paid_now']) && $amountPaidSoFar <= 0) { $amountPaidSoFar = (float)$dr['amount_paid_now']; }
if (isset($dr['discount_amount'])) { $discountAmount = (float)$dr['discount_amount']; }
if (isset($dr['discount_approved_by'])) { $discountBy = (string)$dr['discount_approved_by']; }
if (isset($dr['balance_remaining'])) { $balanceRem = (float)$dr['balance_remaining']; }
if (isset($dr['plan_type']) && !$planType) { $planType = (string)$dr['plan_type']; }
if (isset($dr['deal_source']) && !$dealSource) { $dealSource = (string)$dr['deal_source']; }
if (isset($dr['marketer_name']) && (!is_string($marketerName) || trim($marketerName)==='')) { $marketerName = (string)$dr['marketer_name']; }
if ((!is_string($marketerName) || trim($marketerName)==='')) {
$sid = 0;
if (isset($dr['submitted_by_user']) && (int)$dr['submitted_by_user'] > 0) { $sid = (int)$dr['submitted_by_user']; }
elseif (isset($dr['created_by_user']) && (int)$dr['created_by_user'] > 0) { $sid = (int)$dr['created_by_user']; }
elseif (isset($dr['created_by']) && (int)$dr['created_by'] > 0) { $sid = (int)$dr['created_by']; }
elseif (isset($dr['marketer_id']) && (int)$dr['marketer_id'] > 0) { $sid = (int)$dr['marketer_id']; }
elseif (isset($dr['agent_id']) && (int)$dr['agent_id'] > 0) { $sid = (int)$dr['agent_id']; }
elseif (isset($dr['contact_rep_id']) && (int)$dr['contact_rep_id'] > 0) { $sid = (int)$dr['contact_rep_id']; }
if ($sid > 0) {
try {
$n = '';
if (colExists('users','name')) { $qs = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1"); $qs->execute([$sid]); $n = (string)($qs->fetchColumn() ?: ''); }
if ($n === '' && colExists('users','full_name')) { $qs = $pdo->prepare("SELECT full_name FROM users WHERE id = ? LIMIT 1"); $qs->execute([$sid]); $n = (string)($qs->fetchColumn() ?: ''); }
if ($n === '' && colExists('users','first_name') && colExists('users','last_name')) { $qs = $pdo->prepare("SELECT CONCAT(first_name,' ',last_name) FROM users WHERE id = ? LIMIT 1"); $qs->execute([$sid]); $n = (string)($qs->fetchColumn() ?: ''); }
if ($n !== '') { $marketerName = $n; }
} catch (Throwable $eS) {}
}
}
$quickProperty = $quickProperty ?: ($pEst ?: $pDesc);
}
} catch (Throwable $eX) {}
} else {
try {
$dealGuess = 0;
$rcpG = (string)($row['receipt_file'] ?? '');
$uidG = (int)($row['user_id'] ?? 0);
$dtG = (string)($row['created_at'] ?? '');
if ($dealGuess <= 0 && $rcpG !== '') {
if (colExists('deals','receipt_file')) {
$qg = $pdo->prepare("SELECT id FROM deals WHERE receipt_file = ? LIMIT 1"); $qg->execute([$rcpG]);
$dealGuess = (int)($qg->fetchColumn() ?: 0);
if ($dealGuess <= 0) {
$base = basename($rcpG);
if ($base !== '') { $qg = $pdo->prepare("SELECT id FROM deals WHERE receipt_file LIKE ? LIMIT 1"); $qg->execute(['%'.$base]); $dealGuess = (int)($qg->fetchColumn() ?: 0); }
}
} elseif (colExists('deals','proof_file')) {
$qg = $pdo->prepare("SELECT id FROM deals WHERE proof_file = ? LIMIT 1"); $qg->execute([$rcpG]);
$dealGuess = (int)($qg->fetchColumn() ?: 0);
if ($dealGuess <= 0) {
$base = basename($rcpG);
if ($base !== '') { $qg = $pdo->prepare("SELECT id FROM deals WHERE proof_file LIKE ? LIMIT 1"); $qg->execute(['%'.$base]); $dealGuess = (int)($qg->fetchColumn() ?: 0); }
}
}
}
if ($dealGuess <= 0 && $uidG > 0 && $dtG !== '' && colExists('deals','user_id') && colExists('deals','created_at')) {
$qg = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qg->execute([$uidG, $dtG, $dtG]); $dealGuess = (int)($qg->fetchColumn() ?: 0);
if ($dealGuess <= 0) {
$qg = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1"); $qg->execute([$uidG]); $dealGuess = (int)($qg->fetchColumn() ?: 0);
}
}
if ($dealGuess <= 0 && !empty($row['reference']) && colExists('deals','created_at')) {
$ts = null; if (preg_match('/(\\d{14})/', (string)$row['reference'], $m)) { $ts = DateTime::createFromFormat('YmdHis', $m[1]); }
if ($ts) {
$w = $ts->format('Y-m-d H:i:s');
if ($uidG > 0 && colExists('deals','user_id')) {
$qg = $pdo->prepare("SELECT id FROM deals WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qg->execute([$uidG, $w, $w]); $dealGuess = (int)($qg->fetchColumn() ?: 0);
} else {
$qg = $pdo->prepare("SELECT id FROM deals WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 1 DAY) AND DATE_ADD(?, INTERVAL 1 DAY) ORDER BY created_at DESC LIMIT 1");
$qg->execute([$w, $w]); $dealGuess = (int)($qg->fetchColumn() ?: 0);
}
}
}
if ($dealGuess > 0 && colExists('payments','deal_id')) {
try {
$up = $pdo->prepare("UPDATE payments SET deal_id = ? WHERE id = ?");
$up->execute([$dealGuess, (int)$row['id']]);
try { @file_put_contents(__DIR__ . '/debug_sql.txt', "PAYMENT_LINK:\nUPDATE payments SET deal_id = {$dealGuess} WHERE id = ".(int)$row['id']."\n", FILE_APPEND); } catch (Throwable $ePL0) {}
} catch (Throwable $eUpd) {}
}
if ($dealGuess > 0) {
$sel = [];
if (colExists('deals','property_estate')) { $sel[] = 'property_estate'; }
if (colExists('deals','project_desc')) { $sel[] = 'project_desc'; }
if (colExists('deals','amount_offered')) { $sel[] = 'amount_offered'; }
if (colExists('deals','amount_paid_now')) { $sel[] = 'amount_paid_now'; }
if (colExists('deals','discount_amount')) { $sel[] = 'discount_amount'; }
if (colExists('deals','discount_approved_by')) { $sel[] = 'discount_approved_by'; }
if (colExists('deals','balance_remaining')) { $sel[] = 'balance_remaining'; }
if (colExists('deals','plan_type')) { $sel[] = 'plan_type'; }
if (colExists('deals','deal_source')) { $sel[] = 'deal_source'; }
if (colExists('deals','marketer_name')) { $sel[] = 'marketer_name'; }
if (!empty($sel)) {
$qd2 = $pdo->prepare("SELECT ".implode(',', $sel)." FROM deals WHERE id = ? LIMIT 1");
$qd2->execute([$dealGuess]);
$dr = $qd2->fetch(PDO::FETCH_ASSOC) ?: [];
$pEst = (string)($dr['property_estate'] ?? ($dr['property'] ?? ($dr['estate'] ?? '')));
$pDesc = (string)($dr['project_desc'] ?? ($dr['project'] ?? ($dr['property_title'] ?? ($dr['property_name'] ?? ''))));
$pName = isset($dr['project_name']) ? (string)$dr['project_name'] : '';
$proj = ((is_string($proj) && trim($proj) !== '' && $proj !== '-') ? $proj : ($pName ?: ($pDesc ?: ($pEst ?: $proj))));
if (isset($dr['amount_offered'])) { $amountOffered = (float)$dr['amount_offered']; }
if (isset($dr['amount_paid_now']) && $amountPaidSoFar <= 0) { $amountPaidSoFar = (float)$dr['amount_paid_now']; }
if (isset($dr['discount_amount'])) { $discountAmount = (float)$dr['discount_amount']; }
if (isset($dr['discount_approved_by'])) { $discountBy = (string)$dr['discount_approved_by']; }
if (isset($dr['balance_remaining'])) { $balanceRem = (float)$dr['balance_remaining']; }
if (isset($dr['plan_type']) && !$planType) { $planType = (string)$dr['plan_type']; }
if (isset($dr['deal_source']) && !$dealSource) { $dealSource = (string)$dr['deal_source']; }
if (isset($dr['marketer_name']) && (!is_string($marketerName) || trim($marketerName)==='')) { $marketerName = (string)$dr['marketer_name']; }
$quickProperty = $quickProperty ?: ($pEst ?: $pDesc);
$sets = []; $vals = [];
if (isset($dr['project_name']) && trim((string)$dr['project_name']) === '' && $proj !== '') { $sets[]='project_name = ?'; $vals[]=$proj; }
if (isset($dr['project_desc']) && trim((string)$dr['project_desc']) === '' && $proj !== '') { $sets[]='project_desc = ?'; $vals[]=$proj; }
if (isset($dr['deal_source']) && trim((string)$dr['deal_source']) === '' && $dealSource !== '') { $sets[]='deal_source = ?'; $vals[]=$dealSource; }
if (isset($dr['payment_plan']) && trim((string)$dr['payment_plan']) === '' && $planType !== '') { $sets[]='payment_plan = ?'; $vals[]=$planType; }
if (isset($dr['marketer_name']) && trim((string)$dr['marketer_name']) === '' && $marketerName !== '') { $sets[]='marketer_name = ?'; $vals[]=$marketerName; }
if (isset($dr['amount_offered']) && (float)$dr['amount_offered'] == 0.0 && $amountOffered > 0) { $sets[]='amount_offered = ?'; $vals[]=$amountOffered; }
if (isset($dr['amount_paid_now']) && (float)$dr['amount_paid_now'] == 0.0 && $amountPaidSoFar > 0) { $sets[]='amount_paid_now = ?'; $vals[]=$amountPaidSoFar; }
if (isset($dr['discount_amount']) && (float)$dr['discount_amount'] == 0.0 && $discountAmount > 0) { $sets[]='discount_amount = ?'; $vals[]=$discountAmount; }
if (isset($dr['balance_remaining']) && (float)$dr['balance_remaining'] == 0.0 && $balanceRem > 0) { $sets[]='balance_remaining = ?'; $vals[]=$balanceRem; }
if (isset($dr['plan_type']) && trim((string)$dr['plan_type']) === '' && $planType !== '') { $sets[]='plan_type = ?'; $vals[]=$planType; }
if (!empty($sets)) {
$vals[] = $dealGuess;
$upD = $pdo->prepare("UPDATE deals SET ".implode(',', $sets)." WHERE id = ?");
$upD->execute($vals);
try { @file_put_contents(__DIR__ . '/debug_sql.txt', "REPAIR_UPDATE_DEAL:\nUPDATE deals SET ".implode(',', $sets)." WHERE id = {$dealGuess}\nPARAMS:\n".print_r($vals, true)."\n", FILE_APPEND); } catch (Throwable $eRU1) {}
}
}
}
} catch (Throwable $eX2) {}
}
try {
$uidCF0 = (int)($row['user_id'] ?? 0);
if ($uidCF0 > 0 && colExists('client_forms','form_data')) {
$qcf0 = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY created_at DESC LIMIT 1");
$qcf0->execute([$uidCF0]);
$cfj0 = (string)($qcf0->fetchColumn() ?: '');
if ($cfj0 !== '') {
$cfm0 = json_decode($cfj0, true) ?: [];
$cfProj = '';
foreach (['project_desc','Project / Property Description','Project / Property','Property / Estate','Property/Estate','Estate / Property','project_or_property','preferred_property','preferred property','estate_name','property_estate','estate_property','property','estate','project_property','project','property_title','property_name','project_title','Project Name'] as $k) {
$v = isset($cfm0[$k]) ? trim((string)$cfm0[$k]) : '';
if ($v !== '') { $cfProj = $v; break; }
}
$cfSource = '';
foreach (['deal_source','Deal Source','source','deal_source_type','lead_source','referral_source','referral source','submitted_by_role','Submitted By Role','department','team','channel','source_of_deal','source of deal','how did you hear about us','how you heard about us','where did you hear about us','where heard'] as $k) {
$v = isset($cfm0[$k]) ? trim((string)$cfm0[$k]) : '';
if ($v !== '') { $cfSource = $v; break; }
}
$cfPlan = '';
foreach (['plan_type','Payment Plan','plan','payment_plan','plan_name','installment_plan','payment_mode','payment mode'] as $k) {
$v = isset($cfm0[$k]) ? trim((string)$cfm0[$k]) : '';
if ($v !== '') { $cfPlan = $v; break; }
}
$cfMarketer = '';
foreach (['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','Marketer’s Name','Marketer\'s Name','marketer','sales_agent','sales_rep','contact_rep','uploaded_by_name','submitted_by_name','agent_name','marketer_full_name','marketer full name'] as $k) {
$v = isset($cfm0[$k]) ? trim((string)$cfm0[$k]) : '';
if ($v !== '') { $cfMarketer = $v; break; }
}
if ($cfMarketer === '') {
foreach ($cfm0 as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false || strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false) { $cfMarketer = trim((string)$vv); break; }
}
}
if (!isset($meta['client_payment_status'])) {
if (isset($cfm0['client_payment_status']) && trim((string)$cfm0['client_payment_status']) !== '') { $meta['client_payment_status'] = trim((string)$cfm0['client_payment_status']); }
elseif (isset($cfm0['Client Payment Status / Notes']) && trim((string)$cfm0['Client Payment Status / Notes']) !== '') { $meta['client_payment_status'] = trim((string)$cfm0['Client Payment Status / Notes']); }
elseif (isset($cfm0['Client Payment Status']) && trim((string)$cfm0['Client Payment Status']) !== '') { $meta['client_payment_status'] = trim((string)$cfm0['Client Payment Status']); }
elseif (isset($cfm0['notes']) && trim((string)$cfm0['notes']) !== '') { $meta['client_payment_status'] = trim((string)$cfm0['notes']); }
}
if (!isset($meta['client_email'])) {
if (isset($cfm0['client_email']) && trim((string)$cfm0['client_email']) !== '') { $meta['client_email'] = trim((string)$cfm0['client_email']); }
elseif (isset($cfm0['Client Email (for dashboard)']) && trim((string)$cfm0['Client Email (for dashboard)']) !== '') { $meta['client_email'] = trim((string)$cfm0['Client Email (for dashboard)']); }
elseif (isset($cfm0['email']) && trim((string)$cfm0['email']) !== '') { $meta['client_email'] = trim((string)$cfm0['email']); }
}
if (!isset($meta['client_name'])) {
if (isset($cfm0['Client’s Name (text)']) && trim((string)$cfm0['Client’s Name (text)']) !== '') { $meta['client_name'] = trim((string)$cfm0['Client’s Name (text)']); }
elseif (isset($cfm0["Client's Name (text)"]) && trim((string)$cfm0["Client's Name (text)"]) !== '') { $meta['client_name'] = trim((string)$cfm0["Client's Name (text)"]); }
elseif (isset($cfm0['client_name_text']) && trim((string)$cfm0['client_name_text']) !== '') { $meta['client_name'] = trim((string)$cfm0['client_name_text']); }
}
if (!isset($meta['amount_offered'])) {
if (isset($cfm0['Amount Offered (if installment)'])) { $meta['amount_offered'] = (float)$cfm0['Amount Offered (if installment)']; }
}
if (!isset($meta['discount_approved_by'])) {
if (isset($cfm0['Discount Approved By']) && trim((string)$cfm0['Discount Approved By']) !== '') { $meta['discount_approved_by'] = trim((string)$cfm0['Discount Approved By']); }
}
if (!isset($meta['commission_pct'])) {
if (isset($cfm0['Commission %'])) { $meta['commission_pct'] = (float)$cfm0['Commission %']; }
}
if (!isset($meta['marketer_comm'])) {
if (isset($cfm0['Marketer Commission Amount'])) { $meta['marketer_comm'] = (float)$cfm0['Marketer Commission Amount']; }
elseif (isset($cfm0['Marketer Commission'])) { $meta['marketer_comm'] = (float)$cfm0['Marketer Commission']; }
}
if (!isset($meta['agent_comm'])) {
if (isset($cfm0['Agent Commission Amount'])) { $meta['agent_comm'] = (float)$cfm0['Agent Commission Amount']; }
elseif (isset($cfm0['Agent Commission'])) { $meta['agent_comm'] = (float)$cfm0['Agent Commission']; }
}
if ($proj === '-' || $proj === '') { $proj = $cfProj !== '' ? $cfProj : $proj; }
if (!$dealSource && $cfSource !== '') { $dealSource = $cfSource; }
if (!$planType && $cfPlan !== '') { $planType = $cfPlan; }
if (!$marketerName && $cfMarketer !== '') { $marketerName = $cfMarketer; }
if (empty($mkBank) || (empty($mkBank['acc_no']) && empty($mkBank['bank']) && empty($mkBank['acc_name']))) {
if (isset($cfm0['marketer_bank']) && is_array($cfm0['marketer_bank'])) {
$mkBank = [
'acc_no' => trim((string)($cfm0['marketer_bank']['acc_no'] ?? $cfm0['marketer_bank']['account_number'] ?? $cfm0['marketer_bank']['accNo'] ?? '')),
'bank' => trim((string)($cfm0['marketer_bank']['bank'] ?? $cfm0['marketer_bank']['bank_name'] ?? $cfm0['marketer_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cfm0['marketer_bank']['acc_name'] ?? $cfm0['marketer_bank']['account_name'] ?? $cfm0['marketer_bank']['accountName'] ?? ''))
];
}
if (empty($mkBank) || (empty($mkBank['acc_no']) && empty($mkBank['bank']) && empty($mkBank['acc_name']))) {
$mkBank = [
'acc_no' => trim((string)($cfm0['mk_acc_no'] ?? ($cfm0['marketer_account_number'] ?? ($cfm0['Marketer Account Number'] ?? ($cfm0['marketer_acc_no'] ?? ''))))),
'bank' => trim((string)($cfm0['mk_bank'] ?? ($cfm0['marketer_bank_name'] ?? ($cfm0['Marketer Bank Name'] ?? ($cfm0['marketer_bank'] ?? ''))))),
'acc_name' => trim((string)($cfm0['mk_acc_name'] ?? ($cfm0['marketer_account_name'] ?? ($cfm0['Marketer Account Name'] ?? ($cfm0['marketer_acc_name'] ?? '')))))
];
}
}
if (empty($agBank) || (empty($agBank['acc_no']) && empty($agBank['bank']) && empty($agBank['acc_name']))) {
if (isset($cfm0['agent_bank']) && is_array($cfm0['agent_bank'])) {
$agBank = [
'acc_no' => trim((string)($cfm0['agent_bank']['acc_no'] ?? $cfm0['agent_bank']['account_number'] ?? $cfm0['agent_bank']['accNo'] ?? '')),
'bank' => trim((string)($cfm0['agent_bank']['bank'] ?? $cfm0['agent_bank']['bank_name'] ?? $cfm0['agent_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cfm0['agent_bank']['acc_name'] ?? $cfm0['agent_bank']['account_name'] ?? $cfm0['agent_bank']['accountName'] ?? ''))
];
}
if (empty($agBank) || (empty($agBank['acc_no']) && empty($agBank['bank']) && empty($agBank['acc_name']))) {
$agBank = [
'acc_no' => trim((string)($cfm0['ag_acc_no'] ?? ($cfm0['agent_account_number'] ?? ($cfm0['Agent Account Number'] ?? ($cfm0['agent_acc_no'] ?? ''))))),
'bank' => trim((string)($cfm0['ag_bank'] ?? ($cfm0['agent_bank_name'] ?? ($cfm0['Agent Bank Name'] ?? ($cfm0['agent_bank'] ?? ''))))),
'acc_name' => trim((string)($cfm0['ag_acc_name'] ?? ($cfm0['agent_account_name'] ?? ($cfm0['Agent Account Name'] ?? ($cfm0['agent_acc_name'] ?? '')))))
];
}
}
if ($amountOffered <= 0) {
if (isset($cfm0['amount_offered'])) { $amountOffered = (float)$cfm0['amount_offered']; }
elseif (isset($cfm0['offered_amount'])) { $amountOffered = (float)$cfm0['offered_amount']; }
elseif (isset($cfm0['offered amount'])) { $amountOffered = (float)$cfm0['offered amount']; }
}
if ($discountAmount <= 0 && isset($cfm0['discount_amount'])) { $discountAmount = (float)$cfm0['discount_amount']; }
if ($discountBy === '' && isset($cfm0['discount_approved_by'])) { $discountBy = (string)$cfm0['discount_approved_by']; }
if ($balanceRem <= 0 && isset($cfm0['balance_remaining'])) { $balanceRem = (float)$cfm0['balance_remaining']; }
if ($amountPaidSoFar <= 0 && isset($cfm0['amount_paid_so_far'])) { $amountPaidSoFar = (float)$cfm0['amount_paid_so_far']; }
if ($commissionPct <= 0) {
if (isset($cfm0['commission_pct'])) { $commissionPct = (float)$cfm0['commission_pct']; }
elseif (isset($cfm0['commission_percent'])) { $commissionPct = (float)$cfm0['commission_percent']; }
elseif (isset($cfm0['commission %'])) { $commissionPct = (float)$cfm0['commission %']; }
}
if ($marketerComm <= 0) {
if (isset($cfm0['marketer_comm'])) { $marketerComm = (float)$cfm0['marketer_comm']; }
elseif (isset($cfm0['marketer_commission'])) { $marketerComm = (float)$cfm0['marketer_commission']; }
elseif (isset($cfm0['marketer commission'])) { $marketerComm = (float)$cfm0['marketer commission']; }
}
if ($agentComm <= 0) {
if (isset($cfm0['agent_comm'])) { $agentComm = (float)$cfm0['agent_comm']; }
elseif (isset($cfm0['agent_commission'])) { $agentComm = (float)$cfm0['agent_commission']; }
elseif (isset($cfm0['agent commission'])) { $agentComm = (float)$cfm0['agent commission']; }
}
if ($statusNotes === '') {
if (isset($cfm0['client_payment_status'])) { $statusNotes = (string)$cfm0['client_payment_status']; }
elseif (isset($cfm0['payment_status'])) { $statusNotes = (string)$cfm0['payment_status']; }
elseif (isset($cfm0['payment status'])) { $statusNotes = (string)$cfm0['payment status']; }
elseif (isset($cfm0['notes'])) { $statusNotes = (string)$cfm0['notes']; }
}
}
}
} catch (Throwable $eCF0) {}
try {
$needFromEmail = false;
if (!is_string($proj) || trim($proj) === '' || $proj === '-') { $needFromEmail = true; }
if (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') { $needFromEmail = true; }
if (!is_string($planType) || trim($planType) === '' || $planType === '-') { $needFromEmail = true; }
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') { $needFromEmail = true; }
if ($needFromEmail && colExists('client_forms','form_data')) {
$em = '';
if (isset($clientEmailView) && is_string($clientEmailView) && trim($clientEmailView) !== '') { $em = trim($clientEmailView); }
elseif (isset($row['client_email']) && is_string($row['client_email'])) { $em = trim($row['client_email']); }
elseif (isset($meta['client_email']) && is_string($meta['client_email'])) { $em = trim($meta['client_email']); }
if ($em !== '') {
$cfjE = '';
// 1) Exact JSON key match for "email"
try {
$qfe = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1");
$qfe->execute(['%"email":"'.$em.'"%']);
$cfjE = (string)($qfe->fetchColumn() ?: '');
} catch (Throwable $eE1) {}
// 2) Known alternate label
if ($cfjE === '') {
try {
$qfe2 = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1");
$qfe2->execute(['%"Client Email (for dashboard)":"'.$em.'"%']);
$cfjE = (string)($qfe2->fetchColumn() ?: '');
} catch (Throwable $eE2) {}
}
// 3) Fallback: any occurrence of the email substring
if ($cfjE === '') {
try {
$qfe3 = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1");
$qfe3->execute(['%'.$em.'%']);
$cfjE = (string)($qfe3->fetchColumn() ?: '');
} catch (Throwable $eE3) {}
}
// 4) If still empty, try matching by client name labels
if ($cfjE === '') {
try {
$clientNameGuess = '';
if (!empty($row['client_name'])) { $clientNameGuess = (string)$row['client_name']; }
elseif (!empty($meta['client_name'])) { $clientNameGuess = (string)$meta['client_name']; }
if ($clientNameGuess !== '') {
$patterns = [
'%"Client’s Name (text)":"'.$clientNameGuess.'"%',
'%"Client\'s Name (text)":"'.$clientNameGuess.'"%',
'%"client_name_text":"'.$clientNameGuess.'"%',
'%'.$clientNameGuess.'%'
];
foreach ($patterns as $pt) {
$qfcn = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 1");
$qfcn->execute([$pt]);
$tmp = (string)($qfcn->fetchColumn() ?: '');
if ($tmp !== '') { $cfjE = $tmp; break; }
}
}
} catch (Throwable $eE4) {}
}
if ($cfjE !== '') {
$cfmE = json_decode($cfjE, true) ?: [];
$cfProj = '';
foreach (['project_desc','Project / Property Description','Project / Property','Property / Estate','Property/Estate','Estate / Property','project_or_property','preferred_property','preferred property','estate_name','property_estate','estate_property','property','estate','project_property','project','property_title','property_name','project_title','Project Name'] as $k) {
$v = isset($cfmE[$k]) ? trim((string)$cfmE[$k]) : '';
if ($v !== '') { $cfProj = $v; break; }
}
$cfSource = '';
foreach (['deal_source','Deal Source','source','deal_source_type','lead_source','referral_source','referral source','submitted_by_role','Submitted By Role','department','team','channel','source_of_deal','source of deal','how did you hear about us','how you heard about us','where did you hear about us','where heard'] as $k) {
$v = isset($cfmE[$k]) ? trim((string)$cfmE[$k]) : '';
if ($v !== '') { $cfSource = $v; break; }
}
$cfPlan = '';
foreach (['plan_type','Payment Plan','plan','payment_plan','plan_name','installment_plan','payment_mode','payment mode'] as $k) {
$v = isset($cfmE[$k]) ? trim((string)$cfmE[$k]) : '';
if ($v !== '') { $cfPlan = $v; break; }
}
$cfMarketer = '';
foreach (['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','Marketer’s Name','Marketer\'s Name','marketer','sales_agent','sales_rep','contact_rep','uploaded_by_name','submitted_by_name','agent_name','marketer_full_name','marketer full name'] as $k) {
$v = isset($cfmE[$k]) ? trim((string)$cfmE[$k]) : '';
if ($v !== '') { $cfMarketer = $v; break; }
}
if ($cfMarketer === '') {
foreach ($cfmE as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false || strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false) { $cfMarketer = trim((string)$vv); break; }
}
}
if (!isset($meta['client_payment_status'])) {
if (isset($cfmE['client_payment_status']) && trim((string)$cfmE['client_payment_status']) !== '') { $meta['client_payment_status'] = trim((string)$cfmE['client_payment_status']); }
elseif (isset($cfmE['Client Payment Status / Notes']) && trim((string)$cfmE['Client Payment Status / Notes']) !== '') { $meta['client_payment_status'] = trim((string)$cfmE['Client Payment Status / Notes']); }
elseif (isset($cfmE['Client Payment Status']) && trim((string)$cfmE['Client Payment Status']) !== '') { $meta['client_payment_status'] = trim((string)$cfmE['Client Payment Status']); }
elseif (isset($cfmE['notes']) && trim((string)$cfmE['notes']) !== '') { $meta['client_payment_status'] = trim((string)$cfmE['notes']); }
}
if (!isset($meta['client_email'])) {
if (isset($cfmE['client_email']) && trim((string)$cfmE['client_email']) !== '') { $meta['client_email'] = trim((string)$cfmE['client_email']); }
elseif (isset($cfmE['Client Email (for dashboard)']) && trim((string)$cfmE['Client Email (for dashboard)']) !== '') { $meta['client_email'] = trim((string)$cfmE['Client Email (for dashboard)']); }
elseif (isset($cfmE['email']) && trim((string)$cfmE['email']) !== '') { $meta['client_email'] = trim((string)$cfmE['email']); }
}
if (!isset($meta['client_name'])) {
if (isset($cfmE['Client’s Name (text)']) && trim((string)$cfmE['Client’s Name (text)']) !== '') { $meta['client_name'] = trim((string)$cfmE['Client’s Name (text)']); }
elseif (isset($cfmE["Client's Name (text)"]) && trim((string)$cfmE["Client's Name (text)"]) !== '') { $meta['client_name'] = trim((string)$cfmE["Client's Name (text)"]); }
elseif (isset($cfmE['client_name_text']) && trim((string)$cfmE['client_name_text']) !== '') { $meta['client_name'] = trim((string)$cfmE['client_name_text']); }
}
if (!isset($meta['amount_offered'])) {
if (isset($cfmE['Amount Offered (if installment)'])) { $meta['amount_offered'] = (float)$cfmE['Amount Offered (if installment)']; }
elseif (isset($cfmE['offered_amount'])) { $meta['amount_offered'] = (float)$cfmE['offered_amount']; }
elseif (isset($cfmE['offered amount'])) { $meta['amount_offered'] = (float)$cfmE['offered amount']; }
}
if (!isset($meta['amount_paid_so_far'])) {
if (isset($cfmE['Amount Paid So Far'])) { $meta['amount_paid_so_far'] = (float)$cfmE['Amount Paid So Far']; }
elseif (isset($cfmE['Paid So Far'])) { $meta['amount_paid_so_far'] = (float)$cfmE['Paid So Far']; }
}
if (!isset($meta['discount_approved_by'])) {
if (isset($cfmE['Discount Approved By']) && trim((string)$cfmE['Discount Approved By']) !== '') { $meta['discount_approved_by'] = trim((string)$cfmE['Discount Approved By']); }
}
if (!isset($meta['commission_pct'])) {
if (isset($cfmE['Commission %'])) { $meta['commission_pct'] = (float)$cfmE['Commission %']; }
}
if (!isset($meta['marketer_comm'])) {
if (isset($cfmE['Marketer Commission Amount'])) { $meta['marketer_comm'] = (float)$cfmE['Marketer Commission Amount']; }
elseif (isset($cfmE['Marketer Commission'])) { $meta['marketer_comm'] = (float)$cfmE['Marketer Commission']; }
}
if (!isset($meta['agent_comm'])) {
if (isset($cfmE['Agent Commission Amount'])) { $meta['agent_comm'] = (float)$cfmE['Agent Commission Amount']; }
elseif (isset($cfmE['Agent Commission'])) { $meta['agent_comm'] = (float)$cfmE['Agent Commission']; }
}
if ($proj === '-' || $proj === '') { $proj = $cfProj !== '' ? $cfProj : $proj; }
if (!$dealSource && $cfSource !== '') { $dealSource = $cfSource; }
if (!$planType && $cfPlan !== '') { $planType = $cfPlan; }
if (!$marketerName && $cfMarketer !== '') { $marketerName = $cfMarketer; }
if (empty($mkBank) || (empty($mkBank['acc_no']) && empty($mkBank['bank']) && empty($mkBank['acc_name']))) {
if (isset($cfmE['marketer_bank']) && is_array($cfmE['marketer_bank'])) {
$mkBank = [
'acc_no' => trim((string)($cfmE['marketer_bank']['acc_no'] ?? $cfmE['marketer_bank']['account_number'] ?? $cfmE['marketer_bank']['accNo'] ?? '')),
'bank' => trim((string)($cfmE['marketer_bank']['bank'] ?? $cfmE['marketer_bank']['bank_name'] ?? $cfmE['marketer_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cfmE['marketer_bank']['acc_name'] ?? $cfmE['marketer_bank']['account_name'] ?? $cfmE['marketer_bank']['accountName'] ?? ''))
];
}
if (empty($mkBank) || (empty($mkBank['acc_no']) && empty($mkBank['bank']) && empty($mkBank['acc_name']))) {
$mkBank = [
'acc_no' => trim((string)($cfmE['mk_acc_no'] ?? ($cfmE['marketer_account_number'] ?? ($cfmE['Marketer Account Number'] ?? ($cfmE['marketer_acc_no'] ?? ''))))),
'bank' => trim((string)($cfmE['mk_bank'] ?? ($cfmE['marketer_bank_name'] ?? ($cfmE['Marketer Bank Name'] ?? ($cfmE['marketer_bank'] ?? ''))))),
'acc_name' => trim((string)($cfmE['mk_acc_name'] ?? ($cfmE['marketer_account_name'] ?? ($cfmE['Marketer Account Name'] ?? ($cfmE['marketer_acc_name'] ?? '')))))
];
}
}
if (empty($agBank) || (empty($agBank['acc_no']) && empty($agBank['bank']) && empty($agBank['acc_name']))) {
if (isset($cfmE['agent_bank']) && is_array($cfmE['agent_bank'])) {
$agBank = [
'acc_no' => trim((string)($cfmE['agent_bank']['acc_no'] ?? $cfmE['agent_bank']['account_number'] ?? $cfmE['agent_bank']['accNo'] ?? '')),
'bank' => trim((string)($cfmE['agent_bank']['bank'] ?? $cfmE['agent_bank']['bank_name'] ?? $cfmE['agent_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cfmE['agent_bank']['acc_name'] ?? $cfmE['agent_bank']['account_name'] ?? $cfmE['agent_bank']['accountName'] ?? ''))
];
}
if (empty($agBank) || (empty($agBank['acc_no']) && empty($agBank['bank']) && empty($agBank['acc_name']))) {
$agBank = [
'acc_no' => trim((string)($cfmE['ag_acc_no'] ?? ($cfmE['agent_account_number'] ?? ($cfmE['Agent Account Number'] ?? ($cfmE['agent_acc_no'] ?? ''))))),
'bank' => trim((string)($cfmE['ag_bank'] ?? ($cfmE['agent_bank_name'] ?? ($cfmE['Agent Bank Name'] ?? ($cfmE['agent_bank'] ?? ''))))),
'acc_name' => trim((string)($cfmE['ag_acc_name'] ?? ($cfmE['agent_account_name'] ?? ($cfmE['Agent Account Name'] ?? ($cfmE['agent_acc_name'] ?? '')))))
];
}
}
if ($amountOffered <= 0) {
if (isset($cfmE['amount_offered'])) { $amountOffered = (float)$cfmE['amount_offered']; }
elseif (isset($cfmE['offered_amount'])) { $amountOffered = (float)$cfmE['offered_amount']; }
elseif (isset($cfmE['offered amount'])) { $amountOffered = (float)$cfmE['offered amount']; }
}
if ($discountAmount <= 0 && isset($cfmE['discount_amount'])) { $discountAmount = (float)$cfmE['discount_amount']; }
if ($discountBy === '' && isset($cfmE['discount_approved_by'])) { $discountBy = (string)$cfmE['discount_approved_by']; }
if ($balanceRem <= 0 && isset($cfmE['balance_remaining'])) { $balanceRem = (float)$cfmE['balance_remaining']; }
if ($amountPaidSoFar <= 0 && isset($cfmE['amount_paid_so_far'])) { $amountPaidSoFar = (float)$cfmE['amount_paid_so_far']; }
if ($commissionPct <= 0) {
if (isset($cfmE['commission_pct'])) { $commissionPct = (float)$cfmE['commission_pct']; }
elseif (isset($cfmE['commission_percent'])) { $commissionPct = (float)$cfmE['commission_percent']; }
elseif (isset($cfmE['commission %'])) { $commissionPct = (float)$cfmE['commission %']; }
}
if ($marketerComm <= 0) {
if (isset($cfmE['marketer_comm'])) { $marketerComm = (float)$cfmE['marketer_comm']; }
elseif (isset($cfmE['marketer_commission'])) { $marketerComm = (float)$cfmE['marketer_commission']; }
elseif (isset($cfmE['marketer commission'])) { $marketerComm = (float)$cfmE['marketer commission']; }
}
if ($agentComm <= 0) {
if (isset($cfmE['agent_comm'])) { $agentComm = (float)$cfmE['agent_comm']; }
elseif (isset($cfmE['agent_commission'])) { $agentComm = (float)$cfmE['agent_commission']; }
elseif (isset($cfmE['agent commission'])) { $agentComm = (float)$cfmE['agent commission']; }
}
if ($statusNotes === '') {
if (isset($cfmE['client_payment_status'])) { $statusNotes = (string)$cfmE['client_payment_status']; }
elseif (isset($cfmE['payment_status'])) { $statusNotes = (string)$cfmE['payment_status']; }
elseif (isset($cfmE['payment status'])) { $statusNotes = (string)$cfmE['payment status']; }
elseif (isset($cfmE['notes'])) { $statusNotes = (string)$cfmE['notes']; }
}
}
}
}
} catch (Throwable $eCFE) {}
try {
$needMore = false;
if (!is_string($proj) || trim($proj) === '' || $proj === '-') { $needMore = true; }
if (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') { $needMore = true; }
if (!is_string($planType) || trim($planType) === '' || $planType === '-') { $needMore = true; }
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') { $needMore = true; }
if ($needMore && colExists('client_forms','form_data')) {
$em2 = '';
if (isset($clientEmailView) && is_string($clientEmailView) && trim($clientEmailView) !== '') { $em2 = trim($clientEmailView); }
elseif (isset($row['client_email']) && is_string($row['client_email'])) { $em2 = trim($row['client_email']); }
elseif (isset($meta['client_email']) && is_string($meta['client_email'])) { $em2 = trim($meta['client_email']); }
if ($em2 !== '') {
$qfm = $pdo->prepare("SELECT form_data FROM client_forms WHERE form_data LIKE ? ORDER BY created_at DESC LIMIT 10");
$qfm->execute(['%"email":"'.$em2.'"%']);
$forms = $qfm->fetchAll(PDO::FETCH_COLUMN) ?: [];
foreach ($forms as $cfj) {
$cf = json_decode((string)$cfj, true) ?: [];
if ((!is_string($proj) || trim($proj) === '' || $proj === '-')) {
foreach (['project_desc','property','estate','project_property','project','property_title','property_name','estate_name','project_or_property','preferred_property','preferred property'] as $k) {
if (isset($cf[$k]) && trim((string)$cf[$k]) !== '') { $proj = trim((string)$cf[$k]); break; }
}
}
if ((!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-')) {
foreach (['deal_source','Deal Source','source','deal_source_type','lead_source','referral_source','referral source'] as $k) {
if (isset($cf[$k]) && trim((string)$cf[$k]) !== '') { $dealSource = trim((string)$cf[$k]); break; }
}
}
if ((!is_string($planType) || trim($planType) === '' || $planType === '-')) {
foreach (['plan_type','Payment Plan','plan','payment_plan','plan_name','installment_plan','payment_mode','payment mode'] as $k) {
if (isset($cf[$k]) && trim((string)$cf[$k]) !== '') { $planType = trim((string)$cf[$k]); break; }
}
}
if ((!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-')) {
foreach (['marketer_name','Marketer Name','Marketer or Contact Centre Name','Marketer or Contact Center Name','Contact Centre Name','Contact Center Name','marketer','sales_agent','sales_rep','contact_rep','uploaded_by_name','submitted_by_name','agent_name'] as $k) {
if (isset($cf[$k]) && trim((string)$cf[$k]) !== '') { $marketerName = trim((string)$cf[$k]); break; }
}
}
if ((empty($mkBank) || (empty($mkBank['acc_no']) && empty($mkBank['bank']) && empty($mkBank['acc_name'])))) {
if (isset($cf['marketer_bank']) && is_array($cf['marketer_bank'])) {
$mkBank = [
'acc_no' => trim((string)($cf['marketer_bank']['acc_no'] ?? $cf['marketer_bank']['account_number'] ?? $cf['marketer_bank']['accNo'] ?? '')),
'bank' => trim((string)($cf['marketer_bank']['bank'] ?? $cf['marketer_bank']['bank_name'] ?? $cf['marketer_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cf['marketer_bank']['acc_name'] ?? $cf['marketer_bank']['account_name'] ?? $cf['marketer_bank']['accountName'] ?? ''))
];
} else {
$mkBank = [
'acc_no' => trim((string)($cf['mk_acc_no'] ?? ($cf['marketer_account_number'] ?? ($cf['Marketer Account Number'] ?? ($cf['marketer_acc_no'] ?? ''))))),
'bank' => trim((string)($cf['mk_bank'] ?? ($cf['marketer_bank_name'] ?? ($cf['Marketer Bank Name'] ?? ($cf['marketer_bank'] ?? ''))))),
'acc_name' => trim((string)($cf['mk_acc_name'] ?? ($cf['marketer_account_name'] ?? ($cf['Marketer Account Name'] ?? ($cf['marketer_acc_name'] ?? '')))))
];
}
}
if ((empty($agBank) || (empty($agBank['acc_no']) && empty($agBank['bank']) && empty($agBank['acc_name'])))) {
if (isset($cf['agent_bank']) && is_array($cf['agent_bank'])) {
$agBank = [
'acc_no' => trim((string)($cf['agent_bank']['acc_no'] ?? $cf['agent_bank']['account_number'] ?? $cf['agent_bank']['accNo'] ?? '')),
'bank' => trim((string)($cf['agent_bank']['bank'] ?? $cf['agent_bank']['bank_name'] ?? $cf['agent_bank']['bankName'] ?? '')),
'acc_name' => trim((string)($cf['agent_bank']['acc_name'] ?? $cf['agent_bank']['account_name'] ?? $cf['agent_bank']['accountName'] ?? ''))
];
} else {
$agBank = [
'acc_no' => trim((string)($cf['ag_acc_no'] ?? ($cf['agent_account_number'] ?? ($cf['Agent Account Number'] ?? ($cf['agent_acc_no'] ?? ''))))),
'bank' => trim((string)($cf['ag_bank'] ?? ($cf['agent_bank_name'] ?? ($cf['Agent Bank Name'] ?? ($cf['agent_bank'] ?? ''))))),
'acc_name' => trim((string)($cf['ag_acc_name'] ?? ($cf['agent_account_name'] ?? ($cf['Agent Account Name'] ?? ($cf['agent_acc_name'] ?? '')))))
];
}
}
if ($amountOffered <= 0) {
if (isset($cf['amount_offered'])) { $amountOffered = (float)$cf['amount_offered']; }
elseif (isset($cf['offered_amount'])) { $amountOffered = (float)$cf['offered_amount']; }
elseif (isset($cf['offered amount'])) { $amountOffered = (float)$cf['offered amount']; }
}
if ($discountAmount <= 0 && isset($cf['discount_amount'])) { $discountAmount = (float)$cf['discount_amount']; }
if ($discountBy === '' && isset($cf['discount_approved_by'])) { $discountBy = (string)$cf['discount_approved_by']; }
if ($balanceRem <= 0 && isset($cf['balance_remaining'])) { $balanceRem = (float)$cf['balance_remaining']; }
if ($amountPaidSoFar <= 0 && isset($cf['amount_paid_so_far'])) { $amountPaidSoFar = (float)$cf['amount_paid_so_far']; }
if ($commissionPct <= 0) {
if (isset($cf['commission_pct'])) { $commissionPct = (float)$cf['commission_pct']; }
elseif (isset($cf['commission_percent'])) { $commissionPct = (float)$cf['commission_percent']; }
elseif (isset($cf['commission %'])) { $commissionPct = (float)$cf['commission %']; }
}
if ($marketerComm <= 0) {
if (isset($cf['marketer_comm'])) { $marketerComm = (float)$cf['marketer_comm']; }
elseif (isset($cf['marketer_commission'])) { $marketerComm = (float)$cf['marketer_commission']; }
elseif (isset($cf['marketer commission'])) { $marketerComm = (float)$cf['marketer commission']; }
}
if ($agentComm <= 0) {
if (isset($cf['agent_comm'])) { $agentComm = (float)$cf['agent_comm']; }
elseif (isset($cf['agent_commission'])) { $agentComm = (float)$cf['agent_commission']; }
elseif (isset($cf['agent commission'])) { $agentComm = (float)$cf['agent commission']; }
}
if ($statusNotes === '') {
if (isset($cf['client_payment_status'])) { $statusNotes = (string)$cf['client_payment_status']; }
elseif (isset($cf['payment_status'])) { $statusNotes = (string)$cf['payment_status']; }
elseif (isset($cf['payment status'])) { $statusNotes = (string)$cf['payment status']; }
elseif (isset($cf['notes'])) { $statusNotes = (string)$cf['notes']; }
}
$done = true;
if ((is_string($proj) && trim($proj) !== '' && $proj !== '-')
&& (is_string($dealSource) && trim($dealSource) !== '' && $dealSource !== '-')
&& (is_string($planType) && trim($planType) !== '' && $planType !== '-')
&& (is_string($marketerName) && trim($marketerName) !== '' && $marketerName !== '-')) { break; }
}
}
}
} catch (Throwable $eCFMany) {}
try {
if ((!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-')) {
$sid = 0;
if (colExists('payments','submitted_by_user') && isset($row['submitted_by_user'])) { $sid = (int)$row['submitted_by_user']; }
if ($sid <= 0 && isset($meta['submitted_by_user'])) { $sid = (int)$meta['submitted_by_user']; }
if ($sid > 0) {
$name = '';
try {
$qu = $pdo->prepare("SELECT name,full_name,fullname,first_name,last_name FROM users WHERE id = ? LIMIT 1");
$qu->execute([$sid]);
$ur = $qu->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($ur)) {
foreach (['name','full_name','fullname'] as $n) {
if (isset($ur[$n]) && trim((string)$ur[$n]) !== '') { $name = trim((string)$ur[$n]); break; }
}
if ($name === '') {
$fn = isset($ur['first_name']) ? trim((string)$ur['first_name']) : '';
$ln = isset($ur['last_name']) ? trim((string)$ur['last_name']) : '';
$nm = trim($fn.' '.$ln);
if ($nm !== '') { $name = $nm; }
}
}
} catch (Throwable $eU) {}
if ($name !== '') { $marketerName = $name; }
}
}
} catch (Throwable $eStaff) {}
if ($amountPaidSoFar <= 0 && isset($row['amount'])) { $amountPaidSoFar = (float)$row['amount']; }
$subUidFull = 0; $subRoleFull = ''; $subNameFull = '';
if (!$submittedBy) {
try {
$subUid = 0; $subRole = '';
if ($dealIdRef > 0 && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE id = ? LIMIT 1");
$qd->execute([$dealIdRef]);
$dmj = $qd->fetchColumn();
if ($dmj) {
$m = json_decode($dmj, true) ?: [];
$subUid = (int)($m['submitted_by_user'] ?? 0);
$subRole = (string)($m['submitted_by_role'] ?? '');
if ($subNameFull === '' && !empty($m['submitted_by_name'])) { $subNameFull = (string)$m['submitted_by_name']; }
}
}
if ($subUid <= 0) {
$rcp = (string)($row['receipt_file'] ?? '');
if ($rcp !== '' && colExists('deals','meta_json')) {
if (colExists('deals','receipt_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE receipt_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = $qs->fetchColumn();
if ($dmj) { $m = json_decode($dmj, true) ?: []; $subUid = (int)($m['submitted_by_user'] ?? 0); $subRole = (string)($m['submitted_by_role'] ?? ''); if ($subNameFull === '' && !empty($m['submitted_by_name'])) { $subNameFull = (string)$m['submitted_by_name']; } }
} elseif (colExists('deals','proof_file')) {
$qs = $pdo->prepare("SELECT meta_json FROM deals WHERE proof_file = ? LIMIT 1"); $qs->execute([$rcp]);
$dmj = $qs->fetchColumn();
if ($dmj) { $m = json_decode($dmj, true) ?: []; $subUid = (int)($m['submitted_by_user'] ?? 0); $subRole = (string)($m['submitted_by_role'] ?? ''); if ($subNameFull === '' && !empty($m['submitted_by_name'])) { $subNameFull = (string)$m['submitted_by_name']; } }
}
}
}
if ($subUid <= 0) {
$uid = (int)($row['user_id'] ?? 0);
if ($uid > 0 && colExists('deals','user_id') && colExists('deals','meta_json')) {
$qd = $pdo->prepare("SELECT meta_json FROM deals WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$uid]);
$dmj = $qd->fetchColumn();
if ($dmj) { $m = json_decode($dmj, true) ?: []; $subUid = (int)($m['submitted_by_user'] ?? 0); $subRole = (string)($m['submitted_by_role'] ?? ''); if ($subNameFull === '' && !empty($m['submitted_by_name'])) { $subNameFull = (string)$m['submitted_by_name']; } }
}
}
if ($subUid > 0) {
$display = '#'.$subUid;
if (colExists('users','name')) {
$qn = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
$qn->execute([$subUid]); $n = (string)($qn->fetchColumn() ?: '');
if ($n !== '') { $display .= ' '.$n; if ($subNameFull === '') { $subNameFull = $n; } }
}
if ($subRole !== '') { $display .= ' / '.strtoupper($subRole); }
$subUidFull = $subUid; $subRoleFull = $subRole;
$submittedBy = $display;
}
if (!$submittedBy && $marketerName !== '' && colExists('users','name')) {
try {
$qnm = $pdo->prepare("SELECT id FROM users WHERE name = ? LIMIT 1");
$qnm->execute([$marketerName]);
$sid = (int)($qnm->fetchColumn() ?: 0);
if ($sid <= 0) {
$qnm = $pdo->prepare("SELECT id FROM users WHERE name LIKE ? ORDER BY id DESC LIMIT 1");
$qnm->execute(['%'.$marketerName.'%']); $sid = (int)($qnm->fetchColumn() ?: 0);
}
if ($sid > 0) {
$subUidFull = $sid;
$subRoleFull = ($dealSource !== '' ? strtolower($dealSource) : 'marketing');
$disp = '#'.$sid;
if (colExists('users','name')) {
$qn = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
$qn->execute([$sid]); $nn = (string)($qn->fetchColumn() ?: '');
if ($nn !== '') { $disp .= ' '.$nn; if ($subNameFull === '') { $subNameFull = $nn; } }
}
if ($subRoleFull !== '') { $disp .= ' / '.strtoupper($subRoleFull); }
$submittedBy = $disp;
}
} catch (Throwable $eNameMap) {}
}
if (!$submittedBy && $marketerName !== '') {
$subRoleFull = $subRoleFull !== '' ? $subRoleFull : ($dealSource !== '' ? strtolower($dealSource) : 'marketing');
$submittedBy = $marketerName . ' / ' . strtoupper($subRoleFull);
if ($subNameFull === '') { $subNameFull = $marketerName; }
}
if (!$submittedBy) {
try {
$uidP = 0;
// Prefer explicit staff/submitter identifiers on payments
$payStaffCols = ['submitted_by_user','submitted_by','created_by','created_by_user','staff_id','marketer_id','agent_id','contact_rep_id'];
foreach ($payStaffCols as $pc) {
if ($uidP > 0) break;
if (isset($row[$pc]) && (int)$row[$pc] > 0) { $uidP = (int)$row[$pc]; break; }
}
// Fallback to meta-provided submitter identifiers
if ($uidP <= 0) {
$metaStaffKeys = ['submitted_by_user','submitted_by','created_by','created_by_user','staff_id','marketer_id','agent_id','contact_rep_id'];
foreach ($metaStaffKeys as $mk) {
if ($uidP > 0) break;
if (isset($meta[$mk]) && (int)$meta[$mk] > 0) { $uidP = (int)$meta[$mk]; break; }
}
}
if ($uidP > 0) {
$nmP = '';
if (colExists('users','name')) {
$qn = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
$qn->execute([$uidP]); $nmP = (string)($qn->fetchColumn() ?: '');
}
$roleP = $subRoleFull !== '' ? $subRoleFull : ((string)($meta['submitted_by_role'] ?? '') !== '' ? (string)$meta['submitted_by_role'] : ($dealSource !== '' ? strtolower($dealSource) : 'marketing'));
$dispP = '#'.$uidP . ($nmP !== '' ? ' '.$nmP : '') . ($roleP !== '' ? ' / '.strtoupper($roleP) : '');
$submittedBy = $dispP;
if ($subUidFull <= 0) { $subUidFull = $uidP; }
if ($subRoleFull === '') { $subRoleFull = $roleP; }
if ($subNameFull === '' && $nmP !== '') { $subNameFull = $nmP; }
}
} catch (Throwable $eSubP) {}
}
} catch (Throwable $eSB) {}
if (!$submittedBy) {
$nameAlt = '';
if ($subNameFull !== '') { $nameAlt = $subNameFull; }
elseif (!empty($meta['submitted_by_name'])) { $nameAlt = (string)$meta['submitted_by_name']; }
elseif (!empty($meta['uploaded_by_name'])) { $nameAlt = (string)$meta['uploaded_by_name']; }
elseif (!empty($meta['created_by_name'])) { $nameAlt = (string)$meta['created_by_name']; }
elseif (!empty($meta['creator_name'])) { $nameAlt = (string)$meta['creator_name']; }
elseif (!empty($meta['contact_centre_name'])) { $nameAlt = (string)$meta['contact_centre_name']; }
elseif (!empty($meta['contact_center_name'])) { $nameAlt = (string)$meta['contact_center_name']; }
elseif (!empty($meta['Submitted By'])) { $nameAlt = (string)$meta['Submitted By']; }
elseif (!empty($meta['submitted_by'])) { $nameAlt = (string)$meta['submitted_by']; }
elseif (!empty($meta['SUBMITTED BY'])) { $nameAlt = (string)$meta['SUBMITTED BY']; }
elseif (!empty($meta['sales_agent'])) { $nameAlt = (string)$meta['sales_agent']; }
elseif (!empty($meta['sales_rep'])) { $nameAlt = (string)$meta['sales_rep']; }
elseif (!empty($meta['agent_name'])) { $nameAlt = (string)$meta['agent_name']; }
elseif (isset($row['submitted_by_name']) && is_string($row['submitted_by_name']) && trim($row['submitted_by_name'])!=='') { $nameAlt = (string)$row['submitted_by_name']; }
elseif (isset($row['uploaded_by_name']) && is_string($row['uploaded_by_name']) && trim($row['uploaded_by_name'])!=='') { $nameAlt = (string)$row['uploaded_by_name']; }
elseif (isset($row['created_by_name']) && is_string($row['created_by_name']) && trim($row['created_by_name'])!=='') { $nameAlt = (string)$row['created_by_name']; }
elseif (isset($row['created_by_full_name']) && is_string($row['created_by_full_name']) && trim($row['created_by_full_name'])!=='') { $nameAlt = (string)$row['created_by_full_name']; }
elseif ($marketerName !== '') { $nameAlt = $marketerName; }
$roleAlt = $subRoleFull !== '' ? $subRoleFull : ((string)($meta['submitted_by_role'] ?? '') !== '' ? (string)$meta['submitted_by_role'] : ($dealSource !== '' ? strtolower($dealSource) : 'marketing'));
$roleDisp = strtoupper($roleAlt ?: 'marketing');
if ($nameAlt !== '') { $subNameFull = $nameAlt; $submittedBy = $nameAlt . ' / ' . $roleDisp; }
}
}
$clientEmailView = isset($meta['client_email']) ? (string)$meta['client_email'] : '';
$paymentRefView = '';
$paymentMethodView = '';
try {
$uid = (int)($row['user_id'] ?? 0);
if ($clientEmailView === '' && $uid > 0 && colExists('users','email')) {
$qe = $pdo->prepare("SELECT email FROM users WHERE id = ? LIMIT 1");
$qe->execute([$uid]);
$clientEmailView = (string)($qe->fetchColumn() ?: '');
}
$refCol = colExists('payments','reference') ? "reference" : (colExists('payments','ref') ? "ref" : "NULL");
$methodCol = colExists('payments','payment_method') ? "payment_method" : (colExists('payments','method') ? "method" : "NULL");
$qp = $pdo->prepare("SELECT {$refCol} AS ref, {$methodCol} AS method FROM payments WHERE id = ? LIMIT 1");
$qp->execute([(int)$row['id']]);
$prm = $qp->fetch(PDO::FETCH_ASSOC) ?: [];
$paymentRefView = (string)($prm['ref'] ?? '');
$paymentMethodView = (string)($prm['method'] ?? '');
} catch (Throwable $eInfo) {}
try {
$needCore = false;
if (!is_string($proj) || trim($proj) === '' || $proj === '-') { $needCore = true; }
if (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') { $needCore = true; }
if (!is_string($planType) || trim($planType) === '' || $planType === '-') { $needCore = true; }
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') { $needCore = true; }
if ($needCore) {
$rcpG = (string)($row['receipt_file'] ?? '');
if ($rcpG !== '') {
$selCols = [];
$selCols[] = 'id';
if (colExists('deals','project_name')) { $selCols[] = 'project_name'; }
if (colExists('deals','project_desc')) { $selCols[] = 'project_desc'; }
if (colExists('deals','deal_source')) { $selCols[] = 'deal_source'; }
if (colExists('deals','plan_type')) { $selCols[] = 'plan_type'; }
if (colExists('deals','payment_plan')) { $selCols[] = 'payment_plan'; }
if (colExists('deals','marketer_name')) { $selCols[] = 'marketer_name'; }
if (colExists('deals','amount_offered')) { $selCols[] = 'amount_offered'; }
if (colExists('deals','amount_paid_so_far')) { $selCols[] = 'amount_paid_so_far'; }
if (colExists('deals','discount_amount')) { $selCols[] = 'discount_amount'; }
if (colExists('deals','discount_approved_by')) { $selCols[] = 'discount_approved_by'; }
if (colExists('deals','commission_percent')) { $selCols[] = 'commission_percent'; }
if (colExists('deals','balance_remaining')) { $selCols[] = 'balance_remaining'; }
if (colExists('deals','notes')) { $selCols[] = 'notes'; }
$sel = implode(',', $selCols);
$drX = [];
if (colExists('deals','receipt_file')) {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE receipt_file = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$rcpG]); $drX = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($drX)) {
$base = basename($rcpG);
if ($base !== '') { $qd = $pdo->prepare("SELECT $sel FROM deals WHERE receipt_file LIKE ? ORDER BY id DESC LIMIT 1"); $qd->execute(['%'.$base]); $drX = $qd->fetch(PDO::FETCH_ASSOC) ?: []; }
}
} elseif (colExists('deals','proof_file')) {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE proof_file = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$rcpG]); $drX = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($drX)) {
$base = basename($rcpG);
if ($base !== '') { $qd = $pdo->prepare("SELECT $sel FROM deals WHERE proof_file LIKE ? ORDER BY id DESC LIMIT 1"); $qd->execute(['%'.$base]); $drX = $qd->fetch(PDO::FETCH_ASSOC) ?: []; }
}
}
if (!empty($drX)) {
if ((!is_string($proj) || trim($proj) === '' || $proj === '-')) {
$pName = isset($drX['project_name']) ? (string)$drX['project_name'] : '';
$pDesc = isset($drX['project_desc']) ? (string)$drX['project_desc'] : '';
$proj = $pName !== '' ? $pName : ($pDesc !== '' ? $pDesc : $proj);
}
if ((!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') && isset($drX['deal_source'])) { $dealSource = (string)$drX['deal_source']; }
if ((!is_string($planType) || trim($planType) === '' || $planType === '-')) {
$planType = isset($drX['plan_type']) ? (string)$drX['plan_type'] : (isset($drX['payment_plan']) ? (string)$drX['payment_plan'] : $planType);
}
if ((!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') && isset($drX['marketer_name'])) { $marketerName = (string)$drX['marketer_name']; }
if ($amountOffered <= 0 && isset($drX['amount_offered'])) { $amountOffered = (float)$drX['amount_offered']; }
if ($amountPaidSoFar <= 0 && isset($drX['amount_paid_so_far'])) { $amountPaidSoFar = (float)$drX['amount_paid_so_far']; }
if ($discountAmount <= 0 && isset($drX['discount_amount'])) { $discountAmount = (float)$drX['discount_amount']; }
if ($commissionPct <= 0 && isset($drX['commission_percent'])) { $commissionPct = (float)$drX['commission_percent']; }
if ($balanceRem <= 0 && isset($drX['balance_remaining'])) { $balanceRem = (float)$drX['balance_remaining']; }
if ($statusNotes === '' && isset($drX['notes'])) { $statusNotes = (string)$drX['notes']; }
}
}
// Time-window fallback by created_at and user_id if still missing
$stillMissing = (!is_string($proj) || trim($proj) === '' || $proj === '-')
|| (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-')
|| (!is_string($planType) || trim($planType) === '' || $planType === '-')
|| (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-');
if ($stillMissing) {
$createdAt = (string)($row['created_at'] ?? '');
$uidH = (int)($row['user_id'] ?? 0);
if ($createdAt !== '') {
$selCols2 = [];
$selCols2[] = 'id';
if (colExists('deals','project_name')) { $selCols2[] = 'project_name'; }
if (colExists('deals','project_desc')) { $selCols2[] = 'project_desc'; }
if (colExists('deals','deal_source')) { $selCols2[] = 'deal_source'; }
if (colExists('deals','plan_type')) { $selCols2[] = 'plan_type'; }
if (colExists('deals','payment_plan')) { $selCols2[] = 'payment_plan'; }
if (colExists('deals','marketer_name')) { $selCols2[] = 'marketer_name'; }
if (colExists('deals','amount_offered')) { $selCols2[] = 'amount_offered'; }
if (colExists('deals','amount_paid_so_far')) { $selCols2[] = 'amount_paid_so_far'; }
if (colExists('deals','discount_amount')) { $selCols2[] = 'discount_amount'; }
if (colExists('deals','discount_approved_by')) { $selCols2[] = 'discount_approved_by'; }
if (colExists('deals','commission_percent')) { $selCols2[] = 'commission_percent'; }
if (colExists('deals','balance_remaining')) { $selCols2[] = 'balance_remaining'; }
if (colExists('deals','notes')) { $selCols2[] = 'notes'; }
$sel2 = implode(',', $selCols2);
$clauses = ["created_at BETWEEN DATE_SUB(?, INTERVAL 2 HOUR) AND DATE_ADD(?, INTERVAL 2 HOUR)"];
$params = [$createdAt, $createdAt];
if ($uidH > 0 && (colExists('deals','user_id') || colExists('deals','submitted_by'))) {
$cl = [];
if (colExists('deals','user_id')) { $cl[] = "user_id = ?"; $params[] = $uidH; }
if (colExists('deals','submitted_by')) { $cl[] = "submitted_by = ?"; $params[] = $uidH; }
if (!empty($cl)) { $clauses[] = '('.implode(' OR ', $cl).')'; }
}
$sqlF = "SELECT $sel2 FROM deals WHERE ".implode(' AND ', $clauses)." ORDER BY id DESC LIMIT 1";
try {
$qf = $pdo->prepare($sqlF);
$qf->execute($params);
$drW = $qf->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($drW)) {
if ((!is_string($proj) || trim($proj) === '' || $proj === '-')) {
$pName = isset($drW['project_name']) ? (string)$drW['project_name'] : '';
$pDesc = isset($drW['project_desc']) ? (string)$drW['project_desc'] : '';
$proj = $pName !== '' ? $pName : ($pDesc !== '' ? $pDesc : $proj);
}
if ((!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') && isset($drW['deal_source'])) { $dealSource = (string)$drW['deal_source']; }
if ((!is_string($planType) || trim($planType) === '' || $planType === '-')) {
$planType = isset($drW['plan_type']) ? (string)$drW['plan_type'] : (isset($drW['payment_plan']) ? (string)$drW['payment_plan'] : $planType);
}
if ((!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') && isset($drW['marketer_name'])) { $marketerName = (string)$drW['marketer_name']; }
if ($amountOffered <= 0 && isset($drW['amount_offered'])) { $amountOffered = (float)$drW['amount_offered']; }
if ($amountPaidSoFar <= 0 && isset($drW['amount_paid_so_far'])) { $amountPaidSoFar = (float)$drW['amount_paid_so_far']; }
if ($discountAmount <= 0 && isset($drW['discount_amount'])) { $discountAmount = (float)$drW['discount_amount']; }
if ($commissionPct <= 0 && isset($drW['commission_percent'])) { $commissionPct = (float)$drW['commission_percent']; }
if ($balanceRem <= 0 && isset($drW['balance_remaining'])) { $balanceRem = (float)$drW['balance_remaining']; }
if ($statusNotes === '' && isset($drW['notes'])) { $statusNotes = (string)$drW['notes']; }
}
} catch (Throwable $eWin) {}
}
}
}
} catch (Throwable $ePreNorm) {}
try {
if (colExists('payments','meta_json')) {
$currentMeta = [];
try {
$qcm = $pdo->prepare("SELECT meta_json FROM payments WHERE id = ? LIMIT 1");
$qcm->execute([(int)$row['id']]); $cmj = (string)($qcm->fetchColumn() ?: '');
if ($cmj !== '') { $currentMeta = json_decode($cmj, true) ?: []; }
} catch (Throwable $eCM) {}
// Hard-resolution for missing core fields before building normalized payload
try {
$projMissing = (!is_string($proj) || trim($proj) === '' || $proj === '-');
$srcMissing = (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-');
$planMissing = (!is_string($planType) || trim($planType) === '' || $planType === '-');
$markMissing = (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-');
$rcpG = (string)($row['receipt_file'] ?? '');
if (($projMissing || $srcMissing || $planMissing || $markMissing) && $rcpG !== '') {
$dealCols = [];
$dealCols[] = 'id';
if (colExists('deals','project_name')) { $dealCols[] = 'project_name'; }
if (colExists('deals','project_desc')) { $dealCols[] = 'project_desc'; }
if (colExists('deals','deal_source')) { $dealCols[] = 'deal_source'; }
if (colExists('deals','plan_type')) { $dealCols[] = 'plan_type'; }
if (colExists('deals','payment_plan')) { $dealCols[] = 'payment_plan'; }
if (colExists('deals','marketer_name')) { $dealCols[] = 'marketer_name'; }
if (colExists('deals','amount_offered')) { $dealCols[] = 'amount_offered'; }
if (colExists('deals','amount_paid_so_far')) { $dealCols[] = 'amount_paid_so_far'; }
if (colExists('deals','discount_amount')) { $dealCols[] = 'discount_amount'; }
if (colExists('deals','discount_approved_by')) { $dealCols[] = 'discount_approved_by'; }
if (colExists('deals','commission_percent')) { $dealCols[] = 'commission_percent'; }
if (colExists('deals','balance_remaining')) { $dealCols[] = 'balance_remaining'; }
if (colExists('deals','notes')) { $dealCols[] = 'notes'; }
if (colExists('deals','meta_json')) { $dealCols[] = 'meta_json'; }
$sel = implode(',', $dealCols);
$qd = null; $dr = [];
if (colExists('deals','receipt_file')) {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE receipt_file = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$rcpG]);
$dr = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($dr)) {
$base = basename($rcpG);
if ($base !== '') {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE receipt_file LIKE ? ORDER BY id DESC LIMIT 1");
$qd->execute(['%'.$base]);
$dr = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
}
}
} elseif (colExists('deals','proof_file')) {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE proof_file = ? ORDER BY id DESC LIMIT 1");
$qd->execute([$rcpG]);
$dr = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
if (empty($dr)) {
$base = basename($rcpG);
if ($base !== '') {
$qd = $pdo->prepare("SELECT $sel FROM deals WHERE proof_file LIKE ? ORDER BY id DESC LIMIT 1");
$qd->execute(['%'.$base]);
$dr = $qd->fetch(PDO::FETCH_ASSOC) ?: [];
}
}
}
if (!empty($dr)) {
if ($projMissing) {
$pName = (string)($dr['project_name'] ?? '');
$pDesc = (string)($dr['project_desc'] ?? '');
$proj = $pName !== '' ? $pName : ($pDesc !== '' ? $pDesc : $proj);
}
if ($srcMissing && isset($dr['deal_source'])) { $dealSource = (string)$dr['deal_source']; }
if ($planMissing) {
$planType = isset($dr['plan_type']) ? (string)$dr['plan_type'] : (isset($dr['payment_plan']) ? (string)$dr['payment_plan'] : $planType);
}
if ($markMissing && isset($dr['marketer_name'])) { $marketerName = (string)$dr['marketer_name']; }
if ($amountOffered <= 0 && isset($dr['amount_offered'])) { $amountOffered = (float)$dr['amount_offered']; }
if ($amountPaidSoFar <= 0 && isset($dr['amount_paid_so_far'])) { $amountPaidSoFar = (float)$dr['amount_paid_so_far']; }
if ($discountAmount <= 0 && isset($dr['discount_amount'])) { $discountAmount = (float)$dr['discount_amount']; }
if ($commissionPct <= 0 && isset($dr['commission_percent'])) { $commissionPct = (float)$dr['commission_percent']; }
if ($balanceRem <= 0 && isset($dr['balance_remaining'])) { $balanceRem = (float)$dr['balance_remaining']; }
if ($statusNotes === '' && isset($dr['notes'])) { $statusNotes = (string)$dr['notes']; }
if (colExists('payments','deal_id') && isset($dr['id'])) {
try {
$pdo->prepare("UPDATE payments SET deal_id = ? WHERE id = ?")->execute([(int)$dr['id'], (int)$row['id']]);
@file_put_contents(__DIR__ . '/debug_sql.txt', "PAYMENT_LINK:\nUPDATE payments SET deal_id = ".(int)$dr['id']." WHERE id = ".(int)$row['id']."\n", FILE_APPEND);
} catch (Throwable $eLink) {}
}
if (empty($currentMeta) && isset($dr['meta_json']) && is_string($dr['meta_json']) && $dr['meta_json'] !== '') {
$currentMeta = json_decode($dr['meta_json'], true) ?: [];
}
}
}
} catch (Throwable $eHard) {}
$paymentStatusMeta = (string)($row['status'] ?? ($row['payment_status'] ?? ($row['paymentStatus'] ?? '')));
$paymentDateMeta = (string)($row['created_at'] ?? ($row['date'] ?? ($row['payment_date'] ?? ($row['approved_at'] ?? ($row['verified_at'] ?? ($row['updated_at'] ?? ''))))));
$instStartMeta = (string)($meta['installment_start_date'] ?? ($meta['installmentStartDate'] ?? ($meta['start_date'] ?? ($meta['startDate'] ?? ($meta['Installment Start Date'] ?? ($meta['Start Date'] ?? ''))))));
$sqmMeta = null;
foreach (['sqm','SQM','sqm_size','SQM Size','plot_size','Plot Size','size_sqm','Size (SQM)'] as $k0) {
if (isset($meta[$k0]) && is_numeric($meta[$k0])) { $sqmMeta = (float)$meta[$k0]; break; }
}
$normalized = [
'payment_id' => (int)($row['id'] ?? 0),
'payment_status' => $paymentStatusMeta,
'payment_date' => $paymentDateMeta,
'client_name' => $clientNameMeta ?: ($row['client_name'] ?? ''),
'client_email' => $clientEmailView,
'project_desc' => $proj !== '-' ? $proj : ($quickProperty ?: ''),
'deal_source' => $dealSource,
'plan_type' => $planType,
'installment_start_date' => $instStartMeta,
'sqm' => $sqmMeta,
'amount_offered' => $amountOffered,
'amount_paid_so_far' => $amountPaidSoFar,
'amount_paid_now' => (float)($row['amount'] ?? 0),
'discount_amount' => $discountAmount,
'discount_approved_by' => $discountBy,
'commission_pct' => $commissionPct,
'marketer_comm' => $marketerComm,
'agent_comm' => $agentComm,
'balance_remaining' => $balanceRem,
'client_payment_status' => $statusNotes ?: $quickNotes,
'marketer_bank' => $mkBank,
'agent_bank' => $agBank,
'transactions' => $transactionsMeta,
'submitted_by_user' => $subUidFull > 0 ? $subUidFull : (int)($meta['submitted_by_user'] ?? 0),
'submitted_by_role' => $subRoleFull !== '' ? $subRoleFull : (string)($meta['submitted_by_role'] ?? ''),
'submitted_by_name' => ($subNameFull !== '' ? $subNameFull : $marketerName),
'marketer_name' => $marketerName,
'payment_reference' => $paymentRefView,
'payment_method' => $paymentMethodView
];
$merged = is_array($currentMeta) ? $currentMeta : [];
$allBlankArr = function($arr) {
if (!is_array($arr)) return false;
if (empty($arr)) return true;
foreach ($arr as $vv) {
if (is_array($vv)) {
if (!empty($vv)) return false;
continue;
}
$s = trim((string)$vv);
if ($s !== '' && $s !== '-') return false;
}
return true;
};
foreach ($normalized as $k=>$v) {
$hasKey = array_key_exists($k, $merged);
$cur = $hasKey ? $merged[$k] : null;
$shouldSet = !$hasKey;
if (!$shouldSet) {
if (is_string($cur)) { $t = trim($cur); $shouldSet = ($t === '' || $t === '-'); }
elseif (is_array($cur)) { $shouldSet = $allBlankArr($cur); }
elseif ($cur === null) { $shouldSet = true; }
}
if ($shouldSet) { $merged[$k] = $v; }
}
$newJson = json_encode($merged);
$oldJson = isset($cmj) ? $cmj : '';
if ($newJson && $newJson !== $oldJson) {
$up = $pdo->prepare("UPDATE payments SET meta_json = ? WHERE id = ?");
$up->execute([$newJson, (int)$row['id']]);
}
$modalMetaJson = $newJson ?: '{}';
}
} catch (Throwable $eWrite) {}
// Ensure modal meta exists even if payments.meta_json column is absent
if (!isset($modalMetaJson) || !is_string($modalMetaJson) || trim($modalMetaJson) === '') {
$fallbackMeta = [
'payment_id' => (int)($row['id'] ?? 0),
'payment_status' => (string)($row['status'] ?? ($row['payment_status'] ?? ($row['paymentStatus'] ?? ''))),
'payment_date' => (string)($row['created_at'] ?? ($row['date'] ?? ($row['payment_date'] ?? ($row['approved_at'] ?? ($row['verified_at'] ?? ($row['updated_at'] ?? '')))))),
'client_name' => $clientNameMeta ?: ($row['client_name'] ?? ''),
'client_email' => $clientEmailView,
'project_desc' => $proj !== '-' ? $proj : ($quickProperty ?: ''),
'deal_source' => $dealSource,
'plan_type' => $planType,
'installment_start_date' => (string)($meta['installment_start_date'] ?? ($meta['installmentStartDate'] ?? ($meta['start_date'] ?? ($meta['startDate'] ?? ($meta['Installment Start Date'] ?? ($meta['Start Date'] ?? '')))))),
'sqm' => (function() use ($meta) {
foreach (['sqm','SQM','sqm_size','SQM Size','plot_size','Plot Size','size_sqm','Size (SQM)'] as $k0) {
if (isset($meta[$k0]) && is_numeric($meta[$k0])) { return (float)$meta[$k0]; }
}
return null;
})(),
'amount_offered' => $amountOffered,
'amount_paid_so_far' => $amountPaidSoFar,
'amount_paid_now' => (float)($row['amount'] ?? 0),
'discount_amount' => $discountAmount,
'discount_approved_by' => $discountBy,
'commission_pct' => $commissionPct,
'marketer_comm' => $marketerComm,
'agent_comm' => $agentComm,
'balance_remaining' => $balanceRem,
'client_payment_status' => $statusNotes ?: $quickNotes,
'marketer_bank' => $mkBank,
'agent_bank' => $agBank,
'transactions' => $transactionsMeta,
'submitted_by_user' => $subUidFull > 0 ? $subUidFull : (int)($meta['submitted_by_user'] ?? 0),
'submitted_by_role' => $subRoleFull !== '' ? $subRoleFull : (string)($meta['submitted_by_role'] ?? ''),
'submitted_by_name' => ($subNameFull !== '' ? $subNameFull : $marketerName),
'marketer_name' => $marketerName,
'payment_reference' => $paymentRefView,
'payment_method' => $paymentMethodView
];
$modalMetaJson = json_encode($fallbackMeta);
}
// Apply final fallback from modal meta for server-rendered summary row
if (isset($modalMetaJson) && is_string($modalMetaJson) && trim($modalMetaJson) !== '') {
$mm = json_decode($modalMetaJson, true);
if (is_array($mm)) {
if (($proj === '' || $proj === '-') && !empty($mm['project_desc'])) { $proj = (string)$mm['project_desc']; }
if ($dealSource === '' && !empty($mm['deal_source'])) { $dealSource = (string)$mm['deal_source']; }
if ($planType === '' && !empty($mm['plan_type'])) { $planType = (string)$mm['plan_type']; }
if ($marketerName === '' && !empty($mm['marketer_name'])) { $marketerName = (string)$mm['marketer_name']; }
if ($marketerName === '' && !empty($mm['submitted_by_name'])) { $marketerName = (string)$mm['submitted_by_name']; }
if (($dealSource === '' || $dealSource === '-') && !empty($mm['submitted_by_role'])) { $dealSource = ucfirst(strtolower((string)$mm['submitted_by_role'])); }
if ($amountOffered <= 0 && isset($mm['amount_offered'])) { $amountOffered = (float)$mm['amount_offered']; }
if ($amountPaidSoFar <= 0 && isset($mm['amount_paid_so_far'])) { $amountPaidSoFar = (float)$mm['amount_paid_so_far']; }
if ($discountAmount <= 0 && isset($mm['discount_amount'])) { $discountAmount = (float)$mm['discount_amount']; }
if ($commissionPct <= 0 && isset($mm['commission_pct'])) { $commissionPct = (float)$mm['commission_pct']; }
if ($balanceRem <= 0 && isset($mm['balance_remaining'])) { $balanceRem = (float)$mm['balance_remaining']; }
if ($statusNotes === '' && !empty($mm['client_payment_status'])) { $statusNotes = (string)$mm['client_payment_status']; }
if (empty($mkBank)) { $mkBank = is_array($mm['marketer_bank'] ?? null) ? $mm['marketer_bank'] : []; }
if (empty($agBank)) { $agBank = is_array($mm['agent_bank'] ?? null) ? $mm['agent_bank'] : []; }
if (empty($transactionsMeta)) { $transactionsMeta = is_array($mm['transactions'] ?? null) ? $mm['transactions'] : []; }
}
}
if (!is_string($dealSource) || trim($dealSource) === '' || $dealSource === '-') { $dealSource = 'Marketing'; }
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') {
if (isset($meta['submitted_by_name']) && trim((string)$meta['submitted_by_name']) !== '') { $marketerName = (string)$meta['submitted_by_name']; }
elseif (isset($meta['Submitted By']) && trim((string)$meta['Submitted By']) !== '') { $marketerName = (string)$meta['Submitted By']; }
elseif (isset($meta['submitted_by']) && trim((string)$meta['submitted_by']) !== '') { $marketerName = (string)$meta['submitted_by']; }
elseif (isset($meta['SUBMITTED BY']) && trim((string)$meta['SUBMITTED BY']) !== '') { $marketerName = (string)$meta['SUBMITTED BY']; }
elseif (isset($meta['created_by_name']) && trim((string)$meta['created_by_name']) !== '') { $marketerName = (string)$meta['created_by_name']; }
elseif (isset($row['submitted_by_name']) && is_string($row['submitted_by_name']) && trim($row['submitted_by_name']) !== '') { $marketerName = (string)$row['submitted_by_name']; }
elseif (isset($row['uploaded_by_name']) && is_string($row['uploaded_by_name']) && trim($row['uploaded_by_name']) !== '') { $marketerName = (string)$row['uploaded_by_name']; }
}
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') {
if (is_array($meta) && !empty($meta)) {
foreach ($meta as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false || strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false) { $marketerName = trim((string)$vv); break; }
}
}
}
if (!is_string($marketerName) || trim($marketerName) === '' || $marketerName === '-') {
try {
if (colExists('deals_submit','marketer_name')) {
$did = isset($row['deal_id']) ? (int)$row['deal_id'] : 0;
if ($did > 0) {
$qdss = $pdo->prepare("SELECT marketer_name, submitted_by FROM deals_submit WHERE id = ? LIMIT 1");
$qdss->execute([$did]);
$dsv = $qdss->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($dsv)) {
if ($marketerName === '' && !empty($dsv['marketer_name'])) { $marketerName = (string)$dsv['marketer_name']; }
if ($subNameFull === '' && !empty($dsv['submitted_by'])) { $subNameFull = (string)$dsv['submitted_by']; }
}
}
if ((!is_string($marketerName) || trim($marketerName) === '') && isset($row['user_id']) && (int)$row['user_id'] > 0 && colExists('deals_submit','user_id')) {
$qdss = $pdo->prepare("SELECT marketer_name, submitted_by FROM deals_submit WHERE user_id = ? ORDER BY id DESC LIMIT 1");
$qdss->execute([(int)$row['user_id']]);
$dsv = $qdss->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($dsv)) {
if ($marketerName === '' && !empty($dsv['marketer_name'])) { $marketerName = (string)$dsv['marketer_name']; }
if ($subNameFull === '' && !empty($dsv['submitted_by'])) { $subNameFull = (string)$dsv['submitted_by']; }
}
}
if ((!is_string($marketerName) || trim($marketerName) === '') && isset($row['created_at']) && is_string($row['created_at']) && trim($row['created_at']) !== '' && colExists('deals_submit','created_at')) {
$cat = (string)$row['created_at'];
if ($cat !== '') {
if (isset($row['user_id']) && (int)$row['user_id'] > 0 && colExists('deals_submit','user_id')) {
$qdss = $pdo->prepare("SELECT marketer_name, submitted_by FROM deals_submit WHERE user_id = ? AND created_at BETWEEN DATE_SUB(?, INTERVAL 2 HOUR) AND DATE_ADD(?, INTERVAL 2 HOUR) ORDER BY id DESC LIMIT 1");
$qdss->execute([(int)$row['user_id'], $cat, $cat]);
} else {
$qdss = $pdo->prepare("SELECT marketer_name, submitted_by FROM deals_submit WHERE created_at BETWEEN DATE_SUB(?, INTERVAL 2 HOUR) AND DATE_ADD(?, INTERVAL 2 HOUR) ORDER BY id DESC LIMIT 1");
$qdss->execute([$cat, $cat]);
}
$dsv = $qdss->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($dsv)) {
if ($marketerName === '' && !empty($dsv['marketer_name'])) { $marketerName = (string)$dsv['marketer_name']; }
if ($subNameFull === '' && !empty($dsv['submitted_by'])) { $subNameFull = (string)$dsv['submitted_by']; }
}
}
}
}
} catch (Throwable $eDSN) {}
}
// Finalize Submitted By just before render to avoid blanks
$sbNameFinal = '';
if (!empty($subNameFull)) { $sbNameFinal = (string)$subNameFull; }
elseif (!empty($meta['submitted_by_name'])) { $sbNameFinal = (string)$meta['submitted_by_name']; }
elseif (!empty($meta['Submitted By'])) { $sbNameFinal = (string)$meta['Submitted By']; }
elseif (!empty($meta['submitted_by'])) { $sbNameFinal = (string)$meta['submitted_by']; }
elseif (!empty($meta['SUBMITTED BY'])) { $sbNameFinal = (string)$meta['SUBMITTED BY']; }
elseif (!empty($meta['uploaded_by_name'])) { $sbNameFinal = (string)$meta['uploaded_by_name']; }
elseif (!empty($meta['created_by_name'])) { $sbNameFinal = (string)$meta['created_by_name']; }
elseif (isset($row['submitted_by_name']) && is_string($row['submitted_by_name']) && trim($row['submitted_by_name'])!=='') { $sbNameFinal = (string)$row['submitted_by_name']; }
elseif (isset($row['uploaded_by_name']) && is_string($row['uploaded_by_name']) && trim($row['uploaded_by_name'])!=='') { $sbNameFinal = (string)$row['uploaded_by_name']; }
elseif ($marketerName !== '') { $sbNameFinal = $marketerName; }
if ($sbNameFinal === '' && is_array($meta) && !empty($meta)) {
foreach ($meta as $kk=>$vv) {
if (!is_string($vv) || trim($vv)==='') continue;
$lk = strtolower(preg_replace('/[^a-z]/','', (string)$kk));
if (
strpos($lk,'submittedby')!==false || strpos($lk,'marketername')!==false || strpos($lk,'marketer')!==false
|| strpos($lk,'contactcentre')!==false || strpos($lk,'contactcenter')!==false
|| strpos($lk,'salesagent')!==false || strpos($lk,'salesrep')!==false
|| ($lk==='agent' || strpos($lk,'agentname')!==false) || strpos($lk,'staff')!==false
|| strpos($lk,'uploadedby')!==false || strpos($lk,'createdby')!==false
) { $sbNameFinal = trim((string)$vv); break; }
}
}
if ($sbNameFinal === '') {
$uidTry = (int)($row['user_id'] ?? 0);
if ($uidTry > 0) {
try {
$dealName = '';
if (colExists('deals','client_id') && colExists('deals','marketer_name')) {
$qdn = $pdo->prepare("SELECT marketer_name FROM deals WHERE client_id = ? AND marketer_name IS NOT NULL AND TRIM(marketer_name) <> '' ORDER BY id DESC LIMIT 1");
$qdn->execute([$uidTry]);
$dealName = (string)($qdn->fetchColumn() ?: '');
}
if ($dealName === '' && colExists('deals','user_id') && colExists('deals','marketer_name')) {
$qdn = $pdo->prepare("SELECT marketer_name FROM deals WHERE user_id = ? AND marketer_name IS NOT NULL AND TRIM(marketer_name) <> '' ORDER BY id DESC LIMIT 1");
$qdn->execute([$uidTry]);
$dealName = (string)($qdn->fetchColumn() ?: '');
}
if ($dealName !== '') { $sbNameFinal = $dealName; }
} catch (Throwable $eDN) {}
}
}
$sbRoleFinal = '';
if (!empty($subRoleFull)) { $sbRoleFinal = (string)$subRoleFull; }
elseif (!empty($meta['submitted_by_role'])) { $sbRoleFinal = (string)$meta['submitted_by_role']; }
elseif (!empty($dealSource)) { $sbRoleFinal = strtolower((string)$dealSource); }
if ($sbNameFinal !== '') { $submittedBy = $sbNameFinal; }
?>
<?php
$rowAmt = (float)($row['amount'] ?? 0);
$rowStatusRaw = strtolower((string)($row['status'] ?? ''));
$rowIsPending = in_array($rowStatusRaw, $queueStatuses, true);
$rowStatusUi = $rowIsPending ? 'pending' : ($rowStatusRaw !== '' ? $rowStatusRaw : 'unknown');
$rowPrioClass = '';
$rowSelectable = $canVerify && $rowIsPending;
$rowUploadTs = 0;
try { $rowUploadTs = (int)strtotime((string)($row['created_at'] ?? '')); } catch (Throwable $eT) { $rowUploadTs = 0; }
if ($rowUploadTs > (time() + 86400)) { $rowUploadTs = 0; }
?>
<tr class="verif-row <?= htmlspecialchars($rowPrioClass) ?>" data-status="<?= htmlspecialchars($rowStatusUi) ?>" data-amount="<?= htmlspecialchars((string)$rowAmt) ?>" data-upload-ts="<?= (int)$rowUploadTs ?>" data-id="<?= (int)$row['id'] ?>">
<td class="px-2" data-label="Select"><input type="checkbox" class="form-check-input row-check" data-id="<?= (int)$row['id'] ?>" <?= $rowSelectable ? '' : 'disabled' ?>></td>
<td data-label="Payment ID"><?= (int)$row['id'] ?></td>
<td data-label="Client Name">
<?php
$displayClient = $clientNameMeta ?: ($row['client_name'] ?? 'Unknown');
$linkUserId = (int)($row['user_id'] ?? 0);
if ($linkUserId <= 0) {
$metaClientId = isset($meta['client_id']) ? (int)$meta['client_id'] : 0;
if ($metaClientId > 0) { $linkUserId = $metaClientId; }
}
if ($linkUserId <= 0 && !empty($meta['client_email']) && colExists('users','email')) {
try {
$qe = $pdo->prepare("SELECT id FROM users WHERE email = ? LIMIT 1");
$qe->execute([(string)$meta['client_email']]);
$linkUserId = (int)($qe->fetchColumn() ?: 0);
} catch (Throwable $eE) {}
}
if ($linkUserId <= 0 && $displayClient !== 'Unknown' && colExists('users','name')) {
try {
$qn = $pdo->prepare("SELECT id FROM users WHERE name = ? ORDER BY id DESC LIMIT 1");
$qn->execute([$displayClient]);
$linkUserId = (int)($qn->fetchColumn() ?: 0);
} catch (Throwable $eN) {}
}
if (($displayClient === '' || $displayClient === 'Unknown') && $linkUserId > 0 && colExists('users','name')) {
try { $qnm = $pdo->prepare("SELECT name FROM users WHERE id = ? LIMIT 1"); $qnm->execute([$linkUserId]); $nm = (string)($qnm->fetchColumn() ?: ''); if ($nm !== '') { $displayClient = $nm; } } catch (Throwable $eU) {}
}
if (($displayClient === '' || $displayClient === 'Unknown') && !empty($meta['client_name'])) { $displayClient = (string)$meta['client_name']; }
?>
<?= htmlspecialchars($displayClient) ?>
<?php if ($linkUserId > 0): ?>
<a href="client-profile.php?client_id=<?= (int)$linkUserId ?>" target="_blank" class="ms-2 small text-decoration-none" title="Open client profile"><i class="fa-solid fa-user"></i></a>
<?php endif; ?>
</td>
<td data-label="Submitted By"><?= htmlspecialchars($submittedBy ?: '-') ?></td>
<td data-label="Payment Type"><?= htmlspecialchars($displayPaymentType ?: ($row['payment_type'] ?? '-')) ?></td>
<td data-label="Plan">
<?php
$planRaw = $planType;
if ($planRaw === '' && isset($row['plan_type'])) { $planRaw = (string)$row['plan_type']; }
if ($planRaw === '' && isset($row['payment_plan'])) { $planRaw = (string)$row['payment_plan']; }
if ($planRaw === '' && isset($meta['payment_plan'])) { $planRaw = (string)$meta['payment_plan']; }
$planNorm = strtolower(trim((string)$planRaw));
$planNorm = str_replace([' ', '-'], '_', $planNorm);
$planNorm = preg_replace('/_+/', '_', $planNorm);
$planLabel = '-';
if ($planNorm !== '') {
if (in_array($planNorm, ['full', 'full_payment', 'outright', 'one_time', 'one_off', 'single'], true)) {
$planLabel = 'Full Payment';
} elseif (preg_match('/(\d+)_?months?/', $planNorm, $m)) {
$planLabel = (int)$m[1] . ' Months';
} else {
$planLabel = ucwords(str_replace('_', ' ', $planNorm));
}
}
$startRaw = '';
if (!empty($meta['installment_start_date'])) { $startRaw = (string)$meta['installment_start_date']; }
$startDisp = '';
if ($startRaw !== '' && preg_match('/^\d{4}-\d{2}-\d{2}/', $startRaw)) {
try { $startDisp = date('M d, Y', strtotime(substr($startRaw, 0, 10))); } catch (Throwable $e) { $startDisp = ''; }
}
?>
<div class="fw-semibold small"><?= htmlspecialchars($planLabel) ?></div>
<?php if ($startDisp !== '' && $planLabel !== 'Full Payment'): ?>
<div class="text-muted small">Start: <?= htmlspecialchars($startDisp) ?></div>
<?php endif; ?>
</td>
<td data-label="Amount">
₦<?= number_format((float)($row['amount'] ?? 0)) ?>
</td>
<td data-label="Receipt">
<?php
$rcp = (string)($row['receipt_file'] ?? '');
if ($rcp !== '') {
$tmp = trim($rcp);
$href = $tmp;
if ($tmp !== '' && !preg_match('#^https?://#i', $tmp)) {
$norm = str_replace('\\', '/', $tmp);
$pos = stripos($norm, '/uploads/');
if ($pos === false) { $pos = stripos($norm, 'uploads/'); }
if ($pos !== false) {
$norm = substr($norm, $pos);
if ($norm !== '' && $norm[0] !== '/') { $norm = '/' . $norm; }
}
$baseAbs = function_exists('buildAbsBaseUrl') ? (string)buildAbsBaseUrl() : (isset($absBaseUrl) ? (string)$absBaseUrl : '/');
$href = function_exists('normalizeUrlPath') ? normalizeUrlPath($norm, $baseAbs) : $norm;
}
?>
<a href="<?= htmlspecialchars($href) ?>" target="_blank">View Receipt</a>
<?php
} else {
?>
-
<?php
}
?>
</td>
<td data-label="Upload Date"><?= date('M d, Y', strtotime($row['created_at'] ?? date('Y-m-d'))) ?></td>
<?php $st = strtolower((string)($row['status'] ?? '')); $badgeClass = ($st === 'approved' ? 'badge-status-approved' : ($st === 'rejected' ? 'badge-status-rejected' : 'badge-status-pending')); $stLabel = (in_array($st, $queueStatuses, true) ? 'Pending' : ucfirst($st)); ?>
<td data-label="Status"><span class="badge fin-status-badge <?= $badgeClass ?>"><?= htmlspecialchars($stLabel) ?></span></td>
<td data-label="View Details">
<?php
$cfData = '{}'; $cfReceipt = ''; $cfStatus = '';
try {
$uidCF = (int)($row['user_id'] ?? 0);
if ($uidCF > 0) {
$qcf = $pdo->prepare("SELECT form_data, receipt_path, status FROM client_forms WHERE client_id = ? ORDER BY created_at DESC LIMIT 1");
$qcf->execute([$uidCF]);
$cfRow = $qcf->fetch(PDO::FETCH_ASSOC) ?: [];
$cfData = isset($cfRow['form_data']) && is_string($cfRow['form_data']) ? $cfRow['form_data'] : '{}';
$cfReceipt = (string)($cfRow['receipt_path'] ?? '');
$cfStatus = (string)($cfRow['status'] ?? '');
}
if ((trim($cfData) === '' || trim($cfData) === '{}' || trim($cfData) === 'null') && colExists('client_forms','receipt_path')) {
$rcpTry = (string)($row['receipt_file'] ?? '');
if ($rcpTry === '' && isset($row['proof_file'])) { $rcpTry = (string)$row['proof_file']; }
if ($rcpTry !== '') {
$qcf2 = $pdo->prepare("SELECT form_data, receipt_path, status FROM client_forms WHERE receipt_path = ? ORDER BY created_at DESC LIMIT 1");
$qcf2->execute([$rcpTry]);
$cfRow2 = $qcf2->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($cfRow2)) {
$cfData = isset($cfRow2['form_data']) && is_string($cfRow2['form_data']) ? $cfRow2['form_data'] : $cfData;
$cfReceipt = (string)($cfRow2['receipt_path'] ?? $cfReceipt);
$cfStatus = (string)($cfRow2['status'] ?? $cfStatus);
} else {
$base = basename($rcpTry);
if ($base !== '') {
$qcf3 = $pdo->prepare("SELECT form_data, receipt_path, status FROM client_forms WHERE receipt_path LIKE ? ORDER BY created_at DESC LIMIT 1");
$qcf3->execute(['%'.$base]);
$cfRow3 = $qcf3->fetch(PDO::FETCH_ASSOC) ?: [];
if (!empty($cfRow3)) {
$cfData = isset($cfRow3['form_data']) && is_string($cfRow3['form_data']) ? $cfRow3['form_data'] : $cfData;
$cfReceipt = (string)($cfRow3['receipt_path'] ?? $cfReceipt);
$cfStatus = (string)($cfRow3['status'] ?? $cfStatus);
}
}
}
}
}
} catch (Throwable $eCF) {}
$cfB64 = base64_encode($cfData);
?>
<?php
$basePath = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/\\');
$pmB64 = base64_encode(isset($modalMetaJson) ? $modalMetaJson : '{}');
// Prefer receipt from client_forms; if missing, fall back to payments receipt_file or proof_file
$receiptForBtn = $cfReceipt;
if ($receiptForBtn === '') {
$receiptForBtn = (string)($row['receipt_file'] ?? '');
if ($receiptForBtn === '' && isset($row['proof_file'])) { $receiptForBtn = (string)$row['proof_file']; }
}
$history = [[
'id' => (int)($row['id'] ?? 0),
'amount' => $row['amount'] ?? null,
'payment_type' => (string)($row['payment_type_raw'] ?? ($row['payment_method'] ?? ($row['type'] ?? 'Payment'))),
'status' => (string)($row['status'] ?? ''),
'created_at' => (string)($row['created_at'] ?? '')
]];
$histB64 = base64_encode(json_encode($history));
?>
<button type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#financeClientInfoModal"
data-pm-id="<?= (int)($row['id'] ?? 0) ?>"
data-pay-status="<?= htmlspecialchars((string)($row['status'] ?? '')) ?>"
data-pay-date="<?= htmlspecialchars((string)($row['created_at'] ?? ($row['date'] ?? ''))) ?>"
data-client="<?= htmlspecialchars($row['client_name'] ?? '') ?>"
data-email="<?= htmlspecialchars($clientEmailView ?? '') ?>"
data-amount="<?= htmlspecialchars((string)($row['amount'] ?? '0')) ?>"
data-receipt="<?= htmlspecialchars($receiptForBtn) ?>"
data-form="<?= htmlspecialchars($cfB64) ?>"
data-status="<?= htmlspecialchars($cfStatus) ?>"
data-base="<?= htmlspecialchars($basePath) ?>"
data-payment="<?= htmlspecialchars($pmB64) ?>"
data-submitter="<?= htmlspecialchars($submittedBy ?: '-') ?>"
data-history="<?= htmlspecialchars($histB64) ?>"
data-user-id="<?= (int)($row['user_id'] ?? 0) ?>"
data-client-email="<?= htmlspecialchars($clientEmailView ?? '') ?>">
<i class="fa-solid fa-circle-info me-1"></i>View Details
</button>
</td>
<td data-label="Actions">
<?php $stL = strtolower((string)($row['status'] ?? '')); $isRev = (int)($row['is_reversed'] ?? 0); ?>
<?php if ($canApprovePayments && in_array($stL, $queueStatuses, true)): ?>
<div class="fin-action-buttons">
<button type="button" class="btn btn-sm btn-success approve-btn" data-id="<?= (int)$row['id'] ?>"><i class="fa-solid fa-check me-1"></i>Approve</button>
<button type="button" class="btn btn-sm btn-danger reject-btn" data-id="<?= (int)$row['id'] ?>"><i class="fa-solid fa-xmark me-1"></i>Reject</button>
</div>
<?php elseif ($canApprovePayments && $stL === 'approved' && $isRev === 0): ?>
<div class="fin-action-buttons">
<button type="button" class="btn btn-sm btn-outline-warning reverse-btn" data-id="<?= (int)$row['id'] ?>"><i class="fa-solid fa-rotate-left me-1"></i>Reverse</button>
</div>
<?php else: ?>
-
<?php endif; ?>
</td>
</tr>
<tr class="collapse verif-collapse-row" id="<?= $collapseId ?>">
<td colspan="12">
<div class="border rounded p-3 bg-light">
<div class="row g-3">
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Client</div>
<div class="row g-3">
<div class="col-md-6"><div class="small text-muted">Client</div><div class="fw-semibold"><?= htmlspecialchars($clientNameMeta ?: ($row['client_name'] ?? 'Unknown')) ?></div></div>
<div class="col-md-6"><div class="small text-muted">Client Email</div><div class="fw-semibold"><?= htmlspecialchars($clientEmailView ?: '-') ?></div></div>
<div class="col-md-6"><div class="small text-muted">Submitted By</div><div class="fw-semibold"><?= htmlspecialchars($submittedBy ?: ($marketerName ?: '-')) ?></div></div>
</div>
</div>
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Deal</div>
<div class="row g-3">
<div class="col-md-6"><div class="small text-muted">Project / Property</div><div class="fw-semibold"><?= htmlspecialchars($proj !== '-' ? $proj : ($quickProperty ?: '-')) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Plan</div><div class="fw-semibold"><?= htmlspecialchars($planType ?: '-') ?></div></div>
<div class="col-md-6"><div class="small text-muted">Marketer or Contact Centre Name</div><div class="fw-semibold"><?= htmlspecialchars($marketerName ?: '-') ?></div></div>
</div>
</div>
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Payment</div>
<div class="row g-3">
<div class="col-md-3"><div class="small text-muted">Amount Paid Now</div><div class="fw-semibold"><?= formatCurrency((float)($row['amount'] ?? 0)) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Payment Reference</div><div class="fw-semibold"><?= htmlspecialchars($paymentRefView ?: '-') ?></div></div>
<div class="col-md-3"><div class="small text-muted">Payment Method</div><div class="fw-semibold"><?= htmlspecialchars($paymentMethodView ?: '-') ?></div></div>
<div class="col-md-12"><div class="small text-muted">Payment Status / Notes</div><div class="fw-semibold"><?= htmlspecialchars($statusNotes ?: ($quickNotes ?: '-')) ?></div></div>
</div>
</div>
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Financials</div>
<div class="row g-3">
<div class="col-md-3"><div class="small text-muted">Amount Offered</div><div class="fw-semibold"><?= formatCurrency($amountOffered) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Paid So Far</div><div class="fw-semibold"><?= formatCurrency($amountPaidSoFar) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Discount</div><div class="fw-semibold"><?= formatCurrency($discountAmount) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Discount Approved By</div><div class="fw-semibold"><?= htmlspecialchars($discountBy ?: '-') ?></div></div>
<div class="col-md-3"><div class="small text-muted">Commission %</div><div class="fw-semibold"><?= htmlspecialchars(number_format($commissionPct, 2)) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Marketer Commission</div><div class="fw-semibold"><?= formatCurrency($marketerComm) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Agent Commission</div><div class="fw-semibold"><?= formatCurrency($agentComm) ?></div></div>
<div class="col-md-3"><div class="small text-muted">Balance Remaining</div><div class="fw-semibold"><?= formatCurrency($balanceRem) ?></div></div>
</div>
</div>
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Bank Details</div>
<div class="row g-3">
<div class="col-md-6">
<div class="border rounded p-2 h-100">
<div class="small text-muted">Marketer Bank</div>
<div class="fw-semibold mb-2"><?= htmlspecialchars(($mkBank['bank'] ?? '').' / '.($mkBank['acc_name'] ?? '').' / '.($mkBank['acc_no'] ?? '')) ?></div>
<div class="row g-2">
<div class="col-md-4"><div class="small text-muted">Account Number</div><div class="fw-semibold"><?= htmlspecialchars($mkBank['acc_no'] ?? '') ?></div></div>
<div class="col-md-4"><div class="small text-muted">Bank Name</div><div class="fw-semibold"><?= htmlspecialchars($mkBank['bank'] ?? '') ?></div></div>
<div class="col-md-4"><div class="small text-muted">Account Name</div><div class="fw-semibold"><?= htmlspecialchars($mkBank['acc_name'] ?? '') ?></div></div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="border rounded p-2 h-100">
<div class="small text-muted">Agent Bank</div>
<div class="fw-semibold mb-2"><?= htmlspecialchars(($agBank['bank'] ?? '').' / '.($agBank['acc_name'] ?? '').' / '.($agBank['acc_no'] ?? '')) ?></div>
<div class="row g-2">
<div class="col-md-4"><div class="small text-muted">Account Number</div><div class="fw-semibold"><?= htmlspecialchars($agBank['acc_no'] ?? '') ?></div></div>
<div class="col-md-4"><div class="small text-muted">Bank Name</div><div class="fw-semibold"><?= htmlspecialchars($agBank['bank'] ?? '') ?></div></div>
<div class="col-md-4"><div class="small text-muted">Account Name</div><div class="fw-semibold"><?= htmlspecialchars($agBank['acc_name'] ?? '') ?></div></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="small text-uppercase text-muted fw-semibold mb-2">Transaction History</div>
<?php if (empty($transactionsMeta)): ?>
<div class="text-muted">-</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-sm">
<thead><tr><th>Date</th><th class="text-end">Amount</th></tr></thead>
<tbody>
<?php foreach ($transactionsMeta as $t): ?>
<tr><td><?= htmlspecialchars($t['date']) ?></td><td class="text-end"><?= formatCurrency($t['amount'] ?? 0) ?></td></tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php $hasRows = count($transactionsPage) > 0; ?>
<tr id="verifyEmptyState" class="<?= $hasRows ? 'd-none' : '' ?>">
<td colspan="12" class="text-muted text-center py-4">No pending approvals at the moment</td>
</tr>
</tbody>
</table>
</div>
</div>
<?php if ($totalPages > 1): ?>
<?php
$start = $totalRows > 0 ? ($offset + 1) : 0;
$end = min($totalRows, $offset + count($transactionsPage));
$qsBase = $_GET;
unset($qsBase['page']);
$makeUrl = function(int $p) use ($qsBase) {
$q = $qsBase;
$q['page'] = $p;
return 'finance-payments.php?' . http_build_query($q);
};
$window = 2;
$from = max(1, $page - $window);
$to = min($totalPages, $page + $window);
if ($to - $from < ($window * 2)) {
$from = max(1, $to - ($window * 2));
$to = min($totalPages, $from + ($window * 2));
}
?>
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 p-3 border-top bg-white">
<div class="small text-muted">Showing <?= (int)$start ?>–<?= (int)$end ?> of <?= (int)$totalRows ?></div>
<nav aria-label="Verification pagination">
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars($makeUrl(max(1, $page - 1))) ?>">Prev</a>
</li>
<?php for ($p = $from; $p <= $to; $p++): ?>
<li class="page-item <?= $p === $page ? 'active' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars($makeUrl($p)) ?>"><?= (int)$p ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars($makeUrl(min($totalPages, $page + 1))) ?>">Next</a>
</li>
</ul>
</nav>
</div>
<?php endif; ?>
<div class="modal fade" id="financeClientInfoModal" tabindex="-1" aria-labelledby="financeClientInfoLabel" aria-hidden="true" data-bs-backdrop="false" data-bs-keyboard="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content modal-premium">
<div class="modal-header">
<div class="d-flex align-items-center gap-3">
<img id="finAvatarImg" src="" alt="" style="width:56px;height:56px;border-radius:50%;object-fit:cover;display:none;">
<div>
<h5 class="modal-title" id="financeClientInfoLabel">Client Information</h5>
<div class="small text-muted" id="finEmailText"></div>
</div>
<span class="badge bg-light text-dark border ms-3" id="finStatusBadge"></span>
<span class="badge bg-light text-dark border ms-2" id="finPlanBadge" style="display:none;"></span>
<button type="button" class="btn btn-sm btn-outline-light ms-auto" id="finTransBtn">Transactions</button>
</div>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex align-items-center justify-content-between">
<strong>This Payment (Clicked)</strong>
<span class="small text-muted" id="finPaymentMeta"></span>
</div>
<div class="card-body" id="finPaymentSummary"><div class="text-muted">Loading…</div></div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header bg-white"><strong>Profile & Contact</strong></div>
<div class="card-body" id="finDetailsProfile"><div class="text-muted">Loading…</div></div>
</div>
</div>
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header bg-white"><strong>Attachments</strong></div>
<div class="card-body" id="finDetailsAttachments"><div class="text-muted">Loading…</div></div>
</div>
</div>
</div>
<div class="mt-3">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex align-items-center justify-content-between">
<strong>Payment History</strong>
<span class="small text-muted" id="finTransCount"></span>
</div>
<div class="card-body" id="finTransHistory"><div class="text-muted">Loading…</div></div>
</div>
</div>
<div class="mt-3">
<div class="card shadow-sm">
<div class="card-header bg-white"><strong>Application Data</strong></div>
<div class="card-body" id="finDetailsData"><div class="text-muted">Loading…</div></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
<style>
.modal { z-index: 1065 !important; }
.modal-backdrop { z-index: 1040 !important; }
.modal-content.modal-premium { border-radius: 24px; background: #ffffff; border: 6px solid transparent; background-image: linear-gradient(#ffffff, #ffffff), linear-gradient(135deg, #001F3F, #2ECC40); background-origin: border-box; background-clip: padding-box, border-box; box-shadow: 0 40px 80px rgba(0,31,63,.28); overflow: hidden; }
.modal-content.modal-premium .modal-header { background: linear-gradient(135deg, rgba(0,31,63,.95), rgba(46,204,64,.85)); color: #fff; border-bottom: 0; position: sticky; top: 0; z-index: 1060; }
.modal-content.modal-premium .modal-title { font-weight: 700; letter-spacing: .3px; }
.modal-content.modal-premium .modal-header .modal-title { color: #ffffff !important; }
.modal-content.modal-premium .btn { border-radius: 10px; }
.modal-content.modal-premium .btn-close { filter: invert(1); opacity: .85; }
#financeClientInfoModal { pointer-events: none; }
#financeClientInfoModal .modal-dialog { pointer-events: auto; max-width: min(1000px, 94vw); margin: 0; position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); }
#financeClientInfoModal .modal-dialog.draggable { transform: none; }
#financeClientInfoModal .modal-content { display: flex; flex-direction: column; max-height: 70vh; overflow: visible !important; }
#financeClientInfoModal .modal-body { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.modal-backdrop, .modal-backdrop.fade.show { display: none !important; }
#financeClientInfoModal .modal-footer { position: static; background: #fff; border-top: 1px solid #e9ecef; flex-shrink: 0; }
#financeClientInfoModal .card-body img { max-width: 100%; height: auto; }
.kv-table th, .kv-table td { vertical-align: top; word-break: break-word; white-space: normal; }
.kv-table td { overflow-wrap: anywhere; }
@media (max-width: 576px) { #financeClientInfoModal .modal-dialog { margin-top: 0; } #financeClientInfoModal .modal-content { max-height: 80vh; } }
</style>
<script>
document.getElementById('financeClientInfoModal').addEventListener('shown.bs.modal', function (ev) {
var mb = document.querySelector('#financeClientInfoModal .modal-body'); if (mb) { mb.scrollTop = 0; }
try {
var dlg0 = document.querySelector('#financeClientInfoModal .modal-dialog');
if (dlg0) { dlg0.classList.remove('draggable'); dlg0.style.left=''; dlg0.style.top=''; }
document.body.style.overflow = 'auto';
document.body.classList.remove('modal-open');
document.body.style.paddingRight = '';
} catch(e){}
var btn = ev.relatedTarget;
var client = btn.getAttribute('data-client') || '';
var email = btn.getAttribute('data-email') || '';
var pmid = btn.getAttribute('data-pm-id') || '';
var uid = btn.getAttribute('data-user-id') || '';
var cemail = btn.getAttribute('data-client-email') || '';
var amount = btn.getAttribute('data-amount') || '';
var receipt = btn.getAttribute('data-receipt') || '';
var formB64 = btn.getAttribute('data-form') || '';
var status = btn.getAttribute('data-status') || '';
var payStatus0 = btn.getAttribute('data-pay-status') || '';
var payDate0 = btn.getAttribute('data-pay-date') || '';
var base = btn.getAttribute('data-base') || '';
var pmB64 = btn.getAttribute('data-payment') || '';
var submitter = btn.getAttribute('data-submitter') || '';
var histB64 = btn.getAttribute('data-history') || '';
var data = {};
var pm = {};
var pmSubmission = {};
var hist = [];
try {
var smallForm = formB64 && formB64.length < 150000;
var smallPm = pmB64 && pmB64.length < 150000;
var smallHist = histB64 && histB64.length < 120000;
if (smallForm) { try { data = JSON.parse(atob(formB64)); } catch(e){} }
if (smallPm) { try { pm = JSON.parse(atob(pmB64)); } catch(e){} }
if (smallHist) { try { hist = JSON.parse(atob(histB64)); } catch(e){} }
} catch(eParse) { data = {}; pm = {}; hist = []; }
var initialRendered = false;
function renderAll(){
function valFromKeys(obj, keys){ for (var i=0;i<keys.length;i++){ var k = keys[i]; var v = (obj && obj[k]!=null) ? String(obj[k]) : ''; if (v.trim()!=='') return v; } return ''; }
var avatar = valFromKeys(data, ['passport_photo_path','passport_photo','passportPhotoPath','passportPhoto','passport','passport_url']);
if (avatar && !/^https?:\/\//i.test(avatar)) { avatar = base + '/' + avatar.replace(/^\/+/, ''); }
var avatarImg = document.getElementById('finAvatarImg');
if (avatar && avatarImg) { avatarImg.src = avatar; avatarImg.style.display = 'block'; } else { if (avatarImg) { avatarImg.style.display = 'none'; } }
var emEl = document.getElementById('finEmailText'); if (emEl) { emEl.textContent = email || ''; }
var stEl = document.getElementById('finStatusBadge'); if (stEl) { stEl.textContent = status || ''; }
function pick(){ for (var i=0;i<arguments.length;i++){ var v = arguments[i]; if (v!==undefined && v!==null && String(v).trim()!=='') return v; } return ''; }
function naira(x){ var n = Number(x||0); try { return '₦'+n.toLocaleString('en-NG'); } catch(e){ return '₦'+n; } }
var srcPm = (pmSubmission && Object.keys(pmSubmission).length) ? pmSubmission : pm;
var project = pick(pm.project_desc, pm.project_name, pm.property_estate, data.project_desc, data.project_or_property, data.preferred_property, data['preferred property'], data.estate_name, data.property_estate, data['Property / Estate'], data['Estate / Property'], data.property, data.project, data.property_title, data.property_name, data.project_title, data['Project Name']);
var dealSource = pick(pm.deal_source, data.deal_source, data['Deal Source'], data.source, data.deal_source_type, data.lead_source);
var planType = pick(pm.plan_type, pm.payment_plan, data.plan_type, data['Payment Plan'], data.payment_plan, data.plan, data.plan_name, data.installment_plan);
var planNorm = String(planType || '').toLowerCase().trim().replace(/[\s-]+/g, '_').replace(/_+/g,'_');
var planLabel = '';
if (planNorm) {
if (['full','full_payment','outright','one_time','one_off','single'].indexOf(planNorm) !== -1) { planLabel = 'Full Payment'; }
else {
var m = planNorm.match(/(\d+)_?months?/);
if (m && m[1]) { planLabel = String(parseInt(m[1],10)) + ' Months'; }
else { planLabel = planNorm.replace(/_/g,' ').replace(/\b\w/g, function(ch){ return ch.toUpperCase(); }); }
}
}
var pb = document.getElementById('finPlanBadge');
if (pb) {
if (planLabel) { pb.textContent = planLabel; pb.style.display = ''; }
else { pb.textContent = ''; pb.style.display = 'none'; }
}
var phoneVal = pick(data.phone, data.phone_number, data.telephone, data.mobile, data['Phone Number'], data['phone'], pm.client_phone, pm.phone, pm.client_mobile);
var marketer = pick(pm.marketer_name, pm.submitted_by_name, data.marketer_name, data.sales_agent, data.sales_rep, data.contact_rep, data.uploaded_by_name, data.submitted_by_name, data.agent_name);
var notes = pick(pm.client_payment_status, pm.notes, data.client_payment_status, data['Client Payment Status / Notes'], data.notes);
var amountOffered = Number(pm.amount_offered || 0);
var paidSoFar = Number(pm.amount_paid_so_far || 0);
var discountAmt = Number(pm.discount_amount || 0);
var commissionPct = (pm.commission_pct!=null)? pm.commission_pct : '';
var marketerComm = Number(pm.marketer_comm || 0);
var agentComm = Number(pm.agent_comm || 0);
var balanceRem = Number(pm.balance_remaining || 0);
var clickedSrc = (function(){
try {
if (pm && Object.keys(pm).length) {
var has = pick(pm.payment_id, pm.payment_status, pm.payment_date, pm.sqm, pm.installment_start_date, pm.start_date, pm.SQM);
if (has) return pm;
}
} catch(e){}
return srcPm;
})();
var clickedId = pick(clickedSrc.payment_id, pmid);
var clickedAmt = pick(clickedSrc.payment_amount, clickedSrc.amount_paid_now, amount);
var clickedStatus = pick(clickedSrc.payment_status, clickedSrc.paymentStatus, clickedSrc.status, payStatus0, status);
var clickedRef = pick(clickedSrc.payment_reference, clickedSrc.reference, clickedSrc.ref, clickedSrc.reference_number);
var clickedMethod = pick(clickedSrc.payment_method, clickedSrc.method, clickedSrc.payment_mode);
var clickedDate = pick(clickedSrc.payment_date, clickedSrc.paymentDate, clickedSrc.created_at, clickedSrc.date, payDate0);
var clickedAlloc = pick(clickedSrc.allocation_id, clickedSrc.inv_allocation_id);
var clickedProp = pick(clickedSrc.property_id, clickedSrc.property);
var clickedSqm = pick(
clickedSrc.sqm, clickedSrc.SQM, clickedSrc.sqm_size, clickedSrc.plot_size, clickedSrc.size_sqm,
clickedSrc['SQM'], clickedSrc['Size (SQM)'], clickedSrc['Size(SQM)'],
data.sqm, data.SQM, data.sqm_size, data.plot_size, data.size_sqm, data['SQM'], data['Size (SQM)'], data['Size(SQM)']
);
var clickedStart = pick(
clickedSrc.installment_start_date, clickedSrc.installmentStartDate, clickedSrc.start_date, clickedSrc.startDate,
clickedSrc['Installment Start Date'], clickedSrc['Start Date'],
data.installment_start_date, data.installmentStartDate, data.start_date, data.startDate,
data['Installment Start Date'], data['Start Date']
);
var startDisp = '';
if (clickedStart && /^\d{4}-\d{2}-\d{2}/.test(String(clickedStart))) {
try { startDisp = new Date(String(clickedStart).slice(0,10)).toLocaleDateString('en-US', {year:'numeric', month:'short', day:'2-digit'}); } catch(e){ startDisp = String(clickedStart).slice(0,10); }
}
var dateDisp = '';
if (clickedDate) {
var sdt = String(clickedDate);
if (/^\d{4}-\d{2}-\d{2}/.test(sdt)) {
try { dateDisp = new Date(sdt.replace(' ', 'T')).toLocaleDateString('en-US', {year:'numeric', month:'short', day:'2-digit'}); } catch(e){ dateDisp = sdt.slice(0,10); }
} else { dateDisp = sdt; }
}
var receiptLink = '';
if (receipt) {
var rcp3 = receipt;
if (!/^https?:\/\//i.test(rcp3)) { rcp3 = base + '/' + rcp3.replace(/^\/+/, ''); }
receiptLink = '<a target="_blank" href="'+rcp3+'">Open Receipt</a>';
}
var metaLine = [];
if (clickedAlloc) metaLine.push('Alloc #' + clickedAlloc);
if (clickedProp) metaLine.push('Prop #' + clickedProp);
var pmMetaEl = document.getElementById('finPaymentMeta');
if (pmMetaEl) { pmMetaEl.textContent = metaLine.join(' • '); }
var payHtml = '<div class="row g-2">'
+ '<div class="col-md-3"><div class="small text-muted">Payment ID</div><div class="fw-semibold">#'+ (clickedId || '-') +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Amount</div><div class="fw-semibold">'+ naira(clickedAmt || 0) +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Status</div><div class="fw-semibold">'+ (clickedStatus || '-') +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Date</div><div class="fw-semibold">'+ (dateDisp || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Reference</div><div class="fw-semibold">'+ (clickedRef || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Method</div><div class="fw-semibold">'+ (clickedMethod || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Receipt</div><div class="fw-semibold">'+ (receiptLink || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Plan</div><div class="fw-semibold">'+ (planLabel || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Start Date</div><div class="fw-semibold">'+ (startDisp || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">SQM</div><div class="fw-semibold">'+ (clickedSqm || '-') +'</div></div>'
+ '</div>';
var payEl = document.getElementById('finPaymentSummary');
if (payEl) { payEl.innerHTML = payHtml; }
document.getElementById('finDetailsProfile').innerHTML =
'<div class="row g-2">'
+ '<div class="col-12"><div class="fw-bold">'+client+'</div><div class="text-muted small">'+email+'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Phone</span><div>'+ (phoneVal || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Address</span><div>'+ (data.address || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Purpose</span><div>'+ (data.purpose || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Applicant Status</span><div>'+ (data.applicant_status || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">DOB</span><div>'+ (data.dob || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Nationality</span><div>'+ (data.nationality || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Form Status</span><div>'+ (status || '-') +'</div></div>'
+ (function(){ var sbName = pick(submitter, pm.submitted_by_name, pm['Submitted By'], pm.submitted_by, pm.created_by_name, pm.uploaded_by_name, data.submitted_by_name, data['Submitted By'], data['submitted_by'], data['SUBMITTED BY'], data.contact_rep, data.contact_centre_name, data.contact_center_name, data.marketer_name, data.sales_agent, data.sales_rep, data.agent_name); var sbDisp = sbName || '-'; return '<div class="col-6"><span class="text-muted small">Submitted By</span><div>'+ sbDisp +'</div></div>'; })()
+ '<div class="col-6"><span class="text-muted small">Project / Property</span><div>'+ (project || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Payment Plan</span><div>'+ (planType || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Marketer or Contact Centre Name</span><div>'+ (marketer || '-') +'</div></div>'
+ '</div>';
var attHtml = '<div class="d-flex flex-wrap gap-2 align-items-start">';
if (receipt) { var rcp = receipt; if (!/^https?:\/\//i.test(rcp)) { rcp = base + '/' + rcp.replace(/^\/+/, ''); } attHtml += '<a class="btn btn-sm btn-outline-secondary" target="_blank" href="'+rcp+'">Receipt</a>'; }
var passPath = valFromKeys(data, ['passport_photo_path','passport_photo','passportPhotoPath','passportPhoto','passport','passport_url']);
if (passPath) { var pp = passPath; if (!/^https?:\/\//i.test(pp)) { pp = base + '/' + pp.replace(/^\/+/, ''); } attHtml += '<a class="btn btn-sm btn-outline-primary" target="_blank" href="'+pp+'">Passport Photo</a>'; attHtml += '<div><img src="'+pp+'" style="width:120px;height:120px;border-radius:10px;object-fit:cover;border:1px solid #e5e7eb;"></div>'; }
var idDocPath = valFromKeys(data, ['id_document_path','id_document','id_card','idCard','national_id','nin_card']);
if (idDocPath) { var idp = idDocPath; if (!/^https?:\/\//i.test(idp)) { idp = base + '/' + idp.replace(/^\/+/, ''); } attHtml += '<a class="btn btn-sm btn-outline-dark" target="_blank" href="'+idp+'">ID Document</a>'; }
attHtml += '</div>';
document.getElementById('finDetailsAttachments').innerHTML = attHtml;
var keys = ['employer','occupation','office_address','nhf_number','other_info','nok_name','nok_relationship','nok_address','nok_phone','company_name','company_address','company_reg_no','business_nature','company_contact','preferred_property','offered_amount','payment_mode','rent_per_annum','rent_payer','referral_source','referral_other'];
var table = '<div class=\"table-responsive\"><table class=\"table table-sm\"><tbody>';
keys.forEach(function(k){ var label = k.replace(/_/g,' ').replace(/\\b\\w/g,function(m){return m.toUpperCase();}); var val = (data[k] || ''); table += '<tr><th class=\"w-25\">'+ label +'</th><td>'+ (val || '-') +'</td></tr>'; });
table += '</tbody></table></div>';
var blocks = [];
function bval(o, a, b, c){ if(!o) return ''; return o[a]||o[b]||o[c]||''; }
var marketerRole = pick(pm.submitted_by_role, data.submitted_by_role, dealSource ? String(dealSource).toLowerCase() : '');
var marketerNameField = pick(pm.marketer_name, pm.submitted_by_name, pm['Marketer Name'], data.marketer_name, data['Marketer Name'], data.sales_agent, data.sales_rep, data.contact_rep, data.uploaded_by_name, data.submitted_by_name, data.agent_name);
var amountNow = Number(pm.amount_paid_now || amount || 0);
var mkAccNo = bval(pm.marketer_bank, 'acc_no', 'account_number', 'accNo');
var mkBankName = bval(pm.marketer_bank, 'bank', 'bank_name', 'bankName');
var mkAccName = bval(pm.marketer_bank, 'acc_name', 'account_name', 'accountName');
var agAccNo = bval(pm.agent_bank, 'acc_no', 'account_number', 'accNo');
var agBankName = bval(pm.agent_bank, 'bank', 'bank_name', 'bankName');
var agAccName = bval(pm.agent_bank, 'acc_name', 'account_name', 'accountName');
var rcpLink = ''; if (receipt) { var rcp2 = receipt; if (!/^https?:\/\//i.test(rcp2)) { rcp2 = base + '/' + rcp2.replace(/^\/+/, ''); } rcpLink = '<a target=\"_blank\" href=\"'+rcp2+'\">View Receipt</a>'; }
function txSummary() { try { if (Array.isArray(pm.transactions) && pm.transactions.length) { return pm.transactions.map(function(t){ var d = t.date || t.d || t.created_at || ''; var a = t.amount || t.a || ''; return (d ? d : '') + (a!=='' ? ' — '+naira(a) : ''); }).join('; '); } } catch(e){} try { if (Array.isArray(hist) && hist.length) { return hist.map(function(t){ var d = t.created_at || t.d || ''; var a = t.amount || t.a || ''; return (d ? d : '') + (a!=='' ? ' — '+a : ''); }).join('; '); } } catch(e){} return ''; }
var onb = '<div class=\"row g-3\">'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Client’s Name (text)</div><div class=\"fw-semibold\">'+ (client || '-') +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Client Email (for dashboard)</div><div class=\"fw-semibold\">'+ (email || '-') +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Marketer</div><div class=\"fw-semibold\">'+ (marketerRole ? marketerRole.toUpperCase() : '-') +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Marketer or Contact Centre Name</div><div class=\"fw-semibold\">'+ (marketerNameField || '-') +'</div></div>'
+ '<div class=\"col-md-12\"><div class=\"small text-muted\">Project / Property Description</div><div class=\"fw-semibold\">'+ (project || '-') +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Payment Plan</div><div class=\"fw-semibold\">'+ (planType || '-') +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Amount Offered (if installment)</div><div class=\"fw-semibold\">'+ naira(amountOffered) +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Amount Paid So Far</div><div class=\"fw-semibold\">'+ naira(paidSoFar) +'</div></div>'
+ '<div class=\"col-md-6\"><div class=\"small text-muted\">Amount Paid Now</div><div class=\"fw-semibold\">'+ naira(amountNow) +'</div></div>'
+ '<div class=\"col-md-12\"><div class=\"small text-muted\">Client Payment Status / Notes</div><div class=\"fw-semibold\">'+ (notes || '-') +'</div></div>'
+ '<div class=\"col-md-12\"><div class=\"small text-muted\">Transaction History</div><div class=\"fw-semibold\">'+ (txSummary() || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Discount Amount</div><div class=\"fw-semibold\">'+ naira(discountAmt) +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Discount Approved By</div><div class=\"fw-semibold\">'+ (pm.discount_approved_by || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Balance Remaining</div><div class=\"fw-semibold\">'+ naira(balanceRem) +'</div></div>'
+ '<div class=\"col-md-3\"><div class=\"small text-muted\">Commission %</div><div class=\"fw-semibold\">'+ (commissionPct!=='' ? commissionPct : '-') +'</div></div>'
+ '<div class=\"col-md-3\"><div class=\"small text-muted\">Marketer Commission Amount</div><div class=\"fw-semibold\">'+ naira(marketerComm) +'</div></div>'
+ '<div class=\"col-md-3\"><div class=\"small text-muted\">Agent Commission Amount</div><div class=\"fw-semibold\">'+ naira(agentComm) +'</div></div>'
+ '<div class=\"col-md-3\"><div class=\"small text-muted\">Upload Payment Receipt (Image/PDF)</div><div class=\"fw-semibold\">'+ (rcpLink || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Marketer Account Number</div><div class=\"fw-semibold\">'+ (mkAccNo || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Marketer Bank Name</div><div class=\"fw-semibold\">'+ (mkBankName || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Marketer Account Name</div><div class=\"fw-semibold\">'+ (mkAccName || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Agent Account Number</div><div class=\"fw-semibold\">'+ (agAccNo || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Agent Bank Name</div><div class=\"fw-semibold\">'+ (agBankName || '-') +'</div></div>'
+ '<div class=\"col-md-4\"><div class=\"small text-muted\">Agent Account Name</div><div class=\"fw-semibold\">'+ (agAccName || '-') +'</div></div>'
+ '</div>';
var blocks = [];
function buildKVTable(obj) {
function escHtml(s){
return String(s)
.replace(/&/g,'&')
.replace(/</g,'<')
.replace(/>/g,'>')
.replace(/"/g,'"')
.replace(/'/g,''');
}
function bankLine(o){
if (!o || typeof o !== 'object') return '';
var accNo = o.acc_no || o.account_number || o.accNo || o.acc || '';
var bank = o.bank || o.bank_name || o.bankName || '';
var accName = o.acc_name || o.account_name || o.accountName || '';
if (!accNo && !bank && !accName) return '';
var parts = [];
if (accName) parts.push(accName);
if (bank) parts.push(bank);
if (accNo) parts.push(accNo);
return parts.join(' — ');
}
function txLine(t){
if (!t || typeof t !== 'object') return '';
var d = t.date || t.created_at || t.d || '';
var a = (t.amount!=null) ? t.amount : ((t.a!=null) ? t.a : '');
var st = t.status || t.s || '';
var parts = [];
if (d) parts.push(d);
if (a!=='' && a!==null && a!==undefined) parts.push(naira(a));
if (st) parts.push(st);
return parts.join(' — ');
}
function formatVal(v){
if (v === null || v === undefined) return '';
if (typeof v === 'object') {
if (Array.isArray(v)) {
if (!v.length) return '';
var lines = [];
var max = Math.min(v.length, 5);
for (var i=0;i<max;i++){
var it = v[i];
if (it && typeof it === 'object') {
var ln = txLine(it);
if (!ln) { try { ln = JSON.stringify(it); } catch(e){ ln = String(it); } }
lines.push(ln);
} else {
lines.push(String(it));
}
}
var extra = v.length - lines.length;
return '<div class="small">'
+ lines.map(function(x){ return '<div>'+escHtml(x)+'</div>'; }).join('')
+ (extra > 0 ? '<div class="text-muted">+'+extra+' more</div>' : '')
+ '</div>';
}
var bk = bankLine(v);
if (bk) return '<span>'+escHtml(bk)+'</span>';
var str = '';
try { str = JSON.stringify(v, null, 2); } catch(e){ str = String(v); }
if (str.length > 800) str = str.slice(0,800) + '…';
return '<pre class="mb-0 small">'+escHtml(str)+'</pre>';
}
var s = String(v);
if (s.length > 800) s = s.slice(0,800) + '…';
return escHtml(s);
}
var keys = Object.keys(obj || {});
var html = '<div class="table-responsive"><table class="table table-sm kv-table"><tbody>';
var cap = 80;
for (var i=0;i<keys.length && i<cap;i++){
var k = keys[i];
var v = obj[k];
var valHtml = formatVal(v);
var label = k.replace(/_/g,' ').replace(/\\b\\w/g,function(m){return m.toUpperCase();});
html += '<tr><th class="w-25">'+escHtml(label)+'</th><td>'+(valHtml || '-')+'</td></tr>';
}
if (keys.length > cap) {
html += '<tr><td colspan="2" class="text-muted small">'+escHtml(String(keys.length-cap))+' more fields not shown</td></tr>';
}
html += '</tbody></table></div>';
return html;
}
var usePm = (pmSubmission && Object.keys(pmSubmission).length) ? pmSubmission : pm;
var pmAll = buildKVTable(usePm);
var dataAll = buildKVTable(data);
var cards = '<div class=\"card shadow-sm mb-3\"><div class=\"card-header bg-white\"><strong>Uploaded Payment Data (this payment)</strong></div><div class=\"card-body\">'+ pmAll +'</div></div>'
+ '<div class=\"card shadow-sm mb-3\"><div class=\"card-header bg-white\"><strong>Client Form Data (full)</strong></div><div class=\"card-body\">'+ dataAll +'</div></div>';
document.getElementById('finDetailsData').innerHTML = cards + table;
function renderHist(list){ if (!Array.isArray(list) || list.length === 0) { document.getElementById('finTransHistory').innerHTML = '<div class=\"text-muted\">No transactions found for this client.</div>'; document.getElementById('finTransCount').textContent = ''; return; } var h = '<div class=\"table-responsive\"><table class=\"table table-sm\"><thead><tr><th>ID</th><th>Date</th><th>Type</th><th class=\"text-end\">Amount</th><th>Status</th></tr></thead><tbody>'; list.forEach(function(it){ var dt = it.created_at || ''; var amt = (it.amount!=null)? it.amount : 0; var type = it.payment_type || '-'; var st = it.status || '-'; h += '<tr><td>#'+ (it.id || '-') +'</td><td>'+ dt +'</td><td>'+ type +'</td><td class=\"text-end\">'+ amt +'</td><td>'+ st +'</td></tr>'; }); h += '</tbody></table></div>'; document.getElementById('finTransHistory').innerHTML = h; document.getElementById('finTransCount').textContent = list.length + ' records'; }
document.getElementById('finTransCount').textContent = (Array.isArray(hist) ? hist.length : 0) + ' records';
initialRendered = true;
}
setTimeout(renderAll, 0);
function valFromKeys(obj, keys){ for (var i=0;i<keys.length;i++){ var k = keys[i]; var v = (obj && obj[k]!=null) ? String(obj[k]) : ''; if (v.trim()!=='') return v; } return ''; }
var avatar = valFromKeys(data, ['passport_photo_path','passport_photo','passportPhotoPath','passportPhoto','passport','passport_url']);
if (avatar && !/^https?:\/\//i.test(avatar)) { avatar = base + '/' + avatar.replace(/^\/+/, ''); }
var avatarImg = document.getElementById('finAvatarImg');
if (avatar && avatarImg) { avatarImg.src = avatar; avatarImg.style.display = 'block'; } else { if (avatarImg) { avatarImg.style.display = 'none'; } }
var emEl = document.getElementById('finEmailText'); if (emEl) { emEl.textContent = email || ''; }
var stEl = document.getElementById('finStatusBadge'); if (stEl) { stEl.textContent = status || ''; }
function pick(){
for (var i=0;i<arguments.length;i++){
var v = arguments[i];
if (v!==undefined && v!==null && String(v).trim()!=='') return v;
}
return '';
}
function naira(x){
var n = Number(x||0); try { return '₦'+n.toLocaleString('en-NG'); } catch(e){ return '₦'+n; }
}
var project = pick(pm.project_desc, pm.project_name, pm.property_estate, data.project_desc, data.project_or_property, data.preferred_property, data['preferred property'], data.estate_name, data.property_estate, data['Property / Estate'], data['Estate / Property'], data.property, data.project, data.property_title, data.property_name, data.project_title, data['Project Name']);
var dealSource = pick(pm.deal_source, data.deal_source, data['Deal Source'], data.source, data.deal_source_type, data.lead_source);
var planType = pick(pm.plan_type, pm.payment_plan, data.plan_type, data['Payment Plan'], data.payment_plan, data.plan, data.plan_name, data.installment_plan);
var planNorm = String(planType || '').toLowerCase().trim().replace(/[\s-]+/g, '_').replace(/_+/g,'_');
var planLabel = '';
if (planNorm) {
if (['full','full_payment','outright','one_time','one_off','single'].indexOf(planNorm) !== -1) { planLabel = 'Full Payment'; }
else {
var m = planNorm.match(/(\d+)_?months?/);
if (m && m[1]) { planLabel = String(parseInt(m[1],10)) + ' Months'; }
else { planLabel = planNorm.replace(/_/g,' ').replace(/\b\w/g, function(ch){ return ch.toUpperCase(); }); }
}
}
var pb = document.getElementById('finPlanBadge');
if (pb) {
if (planLabel) { pb.textContent = planLabel; pb.style.display = ''; }
else { pb.textContent = ''; pb.style.display = 'none'; }
}
var phoneVal = pick(data.phone, data.phone_number, data.telephone, data.mobile, data['Phone Number'], data['phone'], pm.client_phone, pm.phone, pm.client_mobile);
var marketer = pick(pm.marketer_name, pm.submitted_by_name, data.marketer_name, data.sales_agent, data.sales_rep, data.contact_rep, data.uploaded_by_name, data.submitted_by_name, data.agent_name);
var notes = pick(pm.client_payment_status, pm.notes, data.client_payment_status, data['Client Payment Status / Notes'], data.notes);
var amountOffered = Number(pm.amount_offered || 0);
var paidSoFar = Number(pm.amount_paid_so_far || 0);
var discountAmt = Number(pm.discount_amount || 0);
var commissionPct = (pm.commission_pct!=null)? pm.commission_pct : '';
var marketerComm = Number(pm.marketer_comm || 0);
var agentComm = Number(pm.agent_comm || 0);
var balanceRem = Number(pm.balance_remaining || 0);
document.getElementById('finDetailsProfile').innerHTML =
'<div class="row g-2">'
+ '<div class="col-12"><div class="fw-bold">'+client+'</div><div class="text-muted small">'+email+'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Phone</span><div>'+ (phoneVal || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Address</span><div>'+ (data.address || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Purpose</span><div>'+ (data.purpose || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Applicant Status</span><div>'+ (data.applicant_status || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">DOB</span><div>'+ (data.dob || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Nationality</span><div>'+ (data.nationality || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Form Status</span><div>'+ (status || '-') +'</div></div>'
+ (function(){ var sbName = pick(submitter, pm.submitted_by_name, pm['Submitted By'], pm.submitted_by, pm.created_by_name, pm.uploaded_by_name, data.submitted_by_name, data['Submitted By'], data['submitted_by'], data['SUBMITTED BY'], data.contact_rep, data.contact_centre_name, data.contact_center_name, data.marketer_name, data.sales_agent, data.sales_rep, data.agent_name); var sbDisp = sbName || '-'; return '<div class="col-6"><span class="text-muted small">Submitted By</span><div>'+ sbDisp +'</div></div>'; })()
+ '<div class="col-6"><span class="text-muted small">Project / Property</span><div>'+ (project || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Payment Plan</span><div>'+ (planType || '-') +'</div></div>'
+ '<div class="col-6"><span class="text-muted small">Marketer or Contact Centre Name</span><div>'+ (marketer || '-') +'</div></div>'
+ '</div>';
var attHtml = '<div class="d-flex flex-wrap gap-2 align-items-start">';
if (receipt) {
var rcp = receipt; if (!/^https?:\/\//i.test(rcp)) { rcp = base + '/' + rcp.replace(/^\/+/, ''); }
attHtml += '<a class="btn btn-sm btn-outline-secondary" target="_blank" href="'+rcp+'">Receipt</a>';
}
if (data.passport_photo_path) {
var pp = data.passport_photo_path; if (!/^https?:\/\//i.test(pp)) { pp = base + '/' + pp.replace(/^\/+/, ''); }
attHtml += '<a class="btn btn-sm btn-outline-primary" target="_blank" href="'+pp+'">Passport Photo</a>';
attHtml += '<div><img src="'+pp+'" style="width:120px;height:120px;border-radius:10px;object-fit:cover;border:1px solid #e5e7eb;"></div>';
}
if (data.id_document_path) {
var idp = data.id_document_path; if (!/^https?:\/\//i.test(idp)) { idp = base + '/' + idp.replace(/^\/+/, ''); }
attHtml += '<a class="btn btn-sm btn-outline-dark" target="_blank" href="'+idp+'">ID Document</a>';
}
attHtml += '</div>';
document.getElementById('finDetailsAttachments').innerHTML = attHtml;
var keys = ['employer','occupation','office_address','nhf_number','other_info','nok_name','nok_relationship','nok_address','nok_phone','company_name','company_address','company_reg_no','business_nature','company_contact','preferred_property','offered_amount','payment_mode','rent_per_annum','rent_payer','referral_source','referral_other'];
var table = '<div class="table-responsive"><table class="table table-sm"><tbody>';
keys.forEach(function(k){
var label = k.replace(/_/g,' ').replace(/\b\\w/g,function(m){return m.toUpperCase();});
var val = (data[k] || '');
table += '<tr><th class="w-25">'+ label +'</th><td>'+ (val || '-') +'</td></tr>';
});
table += '</tbody></table></div>';
var blocks = [];
blocks.push('<div class="alert alert-info mb-3">Amount Paid Now: <strong>'+ (amount ? naira(amount) : '₦0') +'</strong></div>');
function bval(o, a, b, c){ if(!o) return ''; return o[a]||o[b]||o[c]||''; }
var marketerRole = pick(pm.submitted_by_role, data.submitted_by_role, dealSource ? String(dealSource).toLowerCase() : '');
var marketerNameField = pick(pm.marketer_name, pm.submitted_by_name, pm['Marketer Name'], data.marketer_name, data['Marketer Name'], data.sales_agent, data.sales_rep, data.contact_rep, data.uploaded_by_name, data.submitted_by_name, data.agent_name);
var amountNow = Number(pm.amount_paid_now || amount || 0);
var mkAccNo = bval(pm.marketer_bank, 'acc_no', 'account_number', 'accNo');
var mkBankName = bval(pm.marketer_bank, 'bank', 'bank_name', 'bankName');
var mkAccName = bval(pm.marketer_bank, 'acc_name', 'account_name', 'accountName');
var agAccNo = bval(pm.agent_bank, 'acc_no', 'account_number', 'accNo');
var agBankName = bval(pm.agent_bank, 'bank', 'bank_name', 'bankName');
var agAccName = bval(pm.agent_bank, 'acc_name', 'account_name', 'accountName');
var rcpLink = '';
if (receipt) { var rcp = receipt; if (!/^https?:\/\//i.test(rcp)) { rcp = base + '/' + rcp.replace(/^\/+/, ''); } rcpLink = '<a target="_blank" href="'+rcp+'">View Receipt</a>'; }
// Build a compact transaction summary line from pm.transactions or hist list
function txSummary() {
try {
if (Array.isArray(pm.transactions) && pm.transactions.length) {
return pm.transactions.map(function(t){
var d = t.date || t.d || t.created_at || '';
var a = t.amount || t.a || '';
return (d ? d : '') + (a!=='' ? ' — '+naira(a) : '');
}).join('; ');
}
} catch(e){}
try {
if (Array.isArray(hist) && hist.length) {
return hist.map(function(t){
var d = t.created_at || t.d || '';
var a = t.amount || t.a || '';
return (d ? d : '') + (a!=='' ? ' — '+a : '');
}).join('; ');
}
} catch(e){}
return '';
}
var onb = '<div class="row g-3">'
+ '<div class="col-md-6"><div class="small text-muted">Client’s Name (text)</div><div class="fw-semibold">'+ (client || '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Client Email (for dashboard)</div><div class="fw-semibold">'+ (email || '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Deal Source</div><div class="fw-semibold">'+ (dealSource || '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Marketer</div><div class="fw-semibold">'+ (marketerRole ? marketerRole.toUpperCase() : '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Marketer or Contact Centre Name</div><div class="fw-semibold">'+ (marketerNameField || '-') +'</div></div>'
+ '<div class="col-md-12"><div class="small text-muted">Project / Property Description</div><div class="fw-semibold">'+ (project || '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Payment Plan</div><div class="fw-semibold">'+ (planType || '-') +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Amount Offered (if installment)</div><div class="fw-semibold">'+ naira(amountOffered) +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Amount Paid So Far</div><div class="fw-semibold">'+ naira(paidSoFar) +'</div></div>'
+ '<div class="col-md-6"><div class="small text-muted">Amount Paid Now</div><div class="fw-semibold">'+ naira(amountNow) +'</div></div>'
+ '<div class="col-md-12"><div class="small text-muted">Client Payment Status / Notes</div><div class="fw-semibold">'+ (notes || '-') +'</div></div>'
+ '<div class="col-md-12"><div class="small text-muted">Transaction History</div><div class="fw-semibold">'+ (txSummary() || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Discount Amount</div><div class="fw-semibold">'+ naira(discountAmt) +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Discount Approved By</div><div class="fw-semibold">'+ (pm.discount_approved_by || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Balance Remaining</div><div class="fw-semibold">'+ naira(balanceRem) +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Commission %</div><div class="fw-semibold">'+ (commissionPct!=='' ? commissionPct : '-') +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Marketer Commission Amount</div><div class="fw-semibold">'+ naira(marketerComm) +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Agent Commission Amount</div><div class="fw-semibold">'+ naira(agentComm) +'</div></div>'
+ '<div class="col-md-3"><div class="small text-muted">Upload Payment Receipt (Image/PDF)</div><div class="fw-semibold">'+ (rcpLink || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Marketer Account Number</div><div class="fw-semibold">'+ (mkAccNo || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Marketer Bank Name</div><div class="fw-semibold">'+ (mkBankName || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Marketer Account Name</div><div class="fw-semibold">'+ (mkAccName || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Agent Account Number</div><div class="fw-semibold">'+ (agAccNo || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Agent Bank Name</div><div class="fw-semibold">'+ (agBankName || '-') +'</div></div>'
+ '<div class="col-md-4"><div class="small text-muted">Agent Account Name</div><div class="fw-semibold">'+ (agAccName || '-') +'</div></div>'
+ '</div>';
var blocks = [];
function buildKVTable(obj) {
function escHtml(s){
return String(s)
.replace(/&/g,'&')
.replace(/</g,'<')
.replace(/>/g,'>')
.replace(/"/g,'"')
.replace(/'/g,''');
}
function bankLine(o){
if (!o || typeof o !== 'object') return '';
var accNo = o.acc_no || o.account_number || o.accNo || o.acc || '';
var bank = o.bank || o.bank_name || o.bankName || '';
var accName = o.acc_name || o.account_name || o.accountName || '';
if (!accNo && !bank && !accName) return '';
var parts = [];
if (accName) parts.push(accName);
if (bank) parts.push(bank);
if (accNo) parts.push(accNo);
return parts.join(' — ');
}
function txLine(t){
if (!t || typeof t !== 'object') return '';
var d = t.date || t.created_at || t.d || '';
var a = (t.amount!=null) ? t.amount : ((t.a!=null) ? t.a : '');
var st = t.status || t.s || '';
var parts = [];
if (d) parts.push(d);
if (a!=='' && a!==null && a!==undefined) parts.push(naira(a));
if (st) parts.push(st);
return parts.join(' — ');
}
function formatVal(v){
if (v === null || v === undefined) return '';
if (typeof v === 'object') {
if (Array.isArray(v)) {
if (!v.length) return '';
var lines = [];
var max = Math.min(v.length, 5);
for (var i=0;i<max;i++){
var it = v[i];
if (it && typeof it === 'object') {
var ln = txLine(it);
if (!ln) { try { ln = JSON.stringify(it); } catch(e){ ln = String(it); } }
lines.push(ln);
} else {
lines.push(String(it));
}
}
var extra = v.length - lines.length;
return '<div class="small">'
+ lines.map(function(x){ return '<div>'+escHtml(x)+'</div>'; }).join('')
+ (extra > 0 ? '<div class="text-muted">+'+extra+' more</div>' : '')
+ '</div>';
}
var bk = bankLine(v);
if (bk) return '<span>'+escHtml(bk)+'</span>';
var str = '';
try { str = JSON.stringify(v, null, 2); } catch(e){ str = String(v); }
if (str.length > 800) str = str.slice(0,800) + '…';
return '<pre class="mb-0 small">'+escHtml(str)+'</pre>';
}
var s = String(v);
if (s.length > 800) s = s.slice(0,800) + '…';
return escHtml(s);
}
var keys = Object.keys(obj || {});
var html = '<div class="table-responsive"><table class="table table-sm kv-table"><tbody>';
var cap = 80;
for (var i=0;i<keys.length && i<cap;i++){
var k = keys[i];
var v = obj[k];
var valHtml = formatVal(v);
var label = k.replace(/_/g,' ').replace(/\\b\\w/g,function(m){return m.toUpperCase();});
html += '<tr><th class="w-25">'+escHtml(label)+'</th><td>'+(valHtml || '-')+'</td></tr>';
}
if (keys.length > cap) {
html += '<tr><td colspan="2" class="text-muted small">'+escHtml(String(keys.length-cap))+' more fields not shown</td></tr>';
}
html += '</tbody></table></div>';
return html;
}
var usePm2 = (pmSubmission && Object.keys(pmSubmission).length) ? pmSubmission : pm;
var pmAll = buildKVTable(usePm2);
var dataAll = buildKVTable(data);
var cards = '<div class="card shadow-sm mb-3"><div class="card-header bg-white"><strong>Uploaded Payment Data (this payment)</strong></div><div class="card-body">'+ pmAll +'</div></div>'
+ '<div class="card shadow-sm mb-3"><div class="card-header bg-white"><strong>Client Form Data (full)</strong></div><div class="card-body">'+ dataAll +'</div></div>';
document.getElementById('finDetailsData').innerHTML = cards + table;
function renderHist(list){
if (!Array.isArray(list) || list.length === 0) {
document.getElementById('finTransHistory').innerHTML = '<div class="text-muted">No transactions found for this client.</div>';
document.getElementById('finTransCount').textContent = '';
return;
}
var pidStr = String(pmid || '');
var norm = [];
list.forEach(function(it){ norm.push(it); });
var idx = -1;
for (var i=0;i<norm.length;i++){
if (String(norm[i].id || '') === pidStr) { idx = i; break; }
}
if (idx > 0) {
var hit = norm.splice(idx, 1)[0];
norm.unshift(hit);
}
var h = '<div class="table-responsive"><table class="table table-sm"><thead><tr><th>ID</th><th>Date</th><th>Type</th><th class="text-end">Amount</th><th>Status</th></tr></thead><tbody>';
norm.forEach(function(it){
var dt = it.created_at || '';
var amt = (it.amount!=null)? it.amount : 0;
var type = it.payment_type || '-';
var st = it.status || '-';
var isCur = (String(it.id || '') === pidStr);
h += '<tr'+(isCur?' class="table-success"':'')+'><td>'+ (String(it.id||'').indexOf('tx_')===0 ? String(it.id) : '#'+(it.id || '-')) + (isCur ? ' <span class="badge bg-success ms-2">Current</span>' : '') +'</td><td>'+ dt +'</td><td>'+ type +'</td><td class="text-end">'+ naira(amt) +'</td><td>'+ st +'</td></tr>';
});
h += '</tbody></table></div>';
document.getElementById('finTransHistory').innerHTML = h;
document.getElementById('finTransCount').textContent = norm.length + ' records';
}
renderHist(hist);
document.getElementById('finTransCount').textContent = (Array.isArray(hist) ? hist.length : 0) + ' records';
var btnTrans = document.getElementById('finTransBtn');
var transactionsRendered = true;
if (btnTrans) {
btnTrans.onclick = function(){
var el = document.getElementById('finTransHistory');
if (!el) return;
if (el.style.display === 'none') { el.style.display = ''; } else { el.style.display = 'none'; }
};
}
var fd = new FormData();
fd.append('history_action','approved_history');
fd.append('user_id', uid);
fd.append('payment_id', pmid);
fd.append('client_name', client);
fd.append('client_email', cemail);
fetch('finance-payments.php', { method: 'POST', body: fd })
.then(function(r){ return r.json(); })
.then(function(j){
if (j && j.success && Array.isArray(j.items)) {
hist = j.items;
document.getElementById('finTransCount').textContent = hist.length + ' records';
if (transactionsRendered) { renderHist(hist); }
}
})
.catch(function(){});
var fd2 = new FormData();
fd2.append('modal_payload','get');
fd2.append('payment_id', pmid);
fetch('finance-payments.php', { method: 'POST', body: fd2 })
.then(function(r){ return r.json(); })
.then(function(j){
if (j && j.success) {
if (j.pm) pm = j.pm;
if (j.pm_submission) pmSubmission = j.pm_submission;
if (j.data) data = j.data;
if (Array.isArray(j.history)) {
hist = j.history;
document.getElementById('finTransCount').textContent = hist.length + ' records';
if (transactionsRendered) { renderHist(hist); }
}
if (j.receipt) receipt = j.receipt;
if (j.status) status = j.status;
if (j.submitter) submitter = j.submitter;
if (j.client) client = j.client;
if (j.email) email = j.email;
try { if (window.requestAnimationFrame) { requestAnimationFrame(renderAll); } else { setTimeout(renderAll, 0); } } catch(e) { setTimeout(renderAll, 0); }
}
})
.catch(function(){});
try {
var dlg = document.querySelector('#financeClientInfoModal .modal-dialog');
var header = document.querySelector('#financeClientInfoModal .modal-header');
if (dlg && header) {
header.style.cursor = 'grab';
if (!window.__finDragInstalled) {
(function(){
var down = false, sx = 0, sy = 0, ol = 0, ot = 0;
function clamp(n, min, max){ return Math.max(min, Math.min(max, n)); }
header.addEventListener('mousedown', function(e){
try {
if (e && e.button !== 0) return;
var t = e && e.target ? e.target : null;
if (t && t.closest && t.closest('[data-bs-dismiss="modal"], .btn-close, button, a, input, select, textarea, label')) return;
} catch(eSkip){}
try {
var r0 = dlg.getBoundingClientRect();
dlg.classList.add('draggable');
dlg.style.left = r0.left + 'px';
dlg.style.top = r0.top + 'px';
} catch(e0){}
down = true; sx = e.clientX; sy = e.clientY;
var r = dlg.getBoundingClientRect(); ol = r.left; ot = r.top;
document.body.style.userSelect = 'none';
header.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', function(e){
if (!down) return;
var nl = ol + (e.clientX - sx);
var nt = ot + (e.clientY - sy);
var maxL = window.innerWidth - dlg.offsetWidth;
var maxT = window.innerHeight - 40;
dlg.style.left = clamp(nl, 0, maxL) + 'px';
dlg.style.top = clamp(nt, 0, maxT) + 'px';
});
document.addEventListener('mouseup', function(){
if (down) { down = false; document.body.style.userSelect = ''; header.style.cursor = 'grab'; }
});
header.addEventListener('touchstart', function(e){
try {
var t0 = e && e.target ? e.target : null;
if (t0 && t0.closest && t0.closest('[data-bs-dismiss="modal"], .btn-close, button, a, input, select, textarea, label')) return;
} catch(eSkip){}
try {
var r0 = dlg.getBoundingClientRect();
dlg.classList.add('draggable');
dlg.style.left = r0.left + 'px';
dlg.style.top = r0.top + 'px';
} catch(e0){}
var t = e.touches[0]; down = true; sx = t.clientX; sy = t.clientY;
var r = dlg.getBoundingClientRect(); ol = r.left; ot = r.top;
}, { passive: true });
document.addEventListener('touchmove', function(e){
if (!down) return;
var t = e.touches[0];
var nl = ol + (t.clientX - sx);
var nt = ot + (t.clientY - sy);
var maxL = window.innerWidth - dlg.offsetWidth;
var maxT = window.innerHeight - 40;
dlg.style.left = clamp(nl, 0, maxL) + 'px';
dlg.style.top = clamp(nt, 0, maxT) + 'px';
}, { passive: true });
document.addEventListener('touchend', function(){ down = false; header.style.cursor = 'grab'; });
window.__finDragInstalled = true;
})();
}
}
} catch(e){}
});
document.getElementById('financeClientInfoModal').addEventListener('hide.bs.modal', function(){
try {
var dlg = document.querySelector('#financeClientInfoModal .modal-dialog');
if (dlg) { dlg.classList.remove('draggable'); dlg.style.left=''; dlg.style.top=''; }
document.body.style.userSelect = '';
document.body.style.overflow = '';
} catch(e){}
});
</script>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function(){
function send(action, id, btn){
var fd = new FormData();
fd.append('pm_action', action);
fd.append('pm_id', id);
fetch('finance-payments.php', {
method: 'POST',
body: fd
}).then(function(r){ return r.json(); }).then(function(j){
if (j && j.success) {
if (j && j.message) { alert(j.message); }
location.reload();
}
else { if (btn) btn.disabled = false; alert('Action failed'); }
}).catch(function(){ if (btn) btn.disabled = false; alert('Network error'); });
}
document.querySelectorAll('.approve-btn').forEach(function(btn){
btn.addEventListener('click', function(e){
if (e && e.preventDefault) e.preventDefault();
if (e && e.stopPropagation) e.stopPropagation();
btn.disabled = true;
var id = btn.getAttribute('data-id');
var api = window.showConfirm ? window.showConfirm({ title: 'Approve Payment', message: 'Are you sure you want to approve this payment?', confirmText: 'Yes, approve', variant: 'success' }) : Promise.resolve(window.confirm('Are you sure?'));
api.then(function(ok){
if (!ok) { btn.disabled = false; return; }
send('approve', id, btn);
});
});
});
document.querySelectorAll('.reject-btn').forEach(function(btn){
btn.addEventListener('click', function(e){
if (e && e.preventDefault) e.preventDefault();
if (e && e.stopPropagation) e.stopPropagation();
btn.disabled = true;
var id = btn.getAttribute('data-id');
var api = window.showConfirm ? window.showConfirm({ title: 'Reject Payment', message: 'Are you sure you want to reject this payment?', confirmText: 'Yes, reject', variant: 'danger' }) : Promise.resolve(window.confirm('Are you sure?'));
api.then(function(ok){
if (!ok) { btn.disabled = false; return; }
send('reject', id, btn);
});
});
});
document.querySelectorAll('.reverse-btn').forEach(function(btn){
btn.addEventListener('click', function(e){
if (e && e.preventDefault) e.preventDefault();
if (e && e.stopPropagation) e.stopPropagation();
btn.disabled = true;
var id = btn.getAttribute('data-id');
var api = window.showConfirm ? window.showConfirm({ title: 'Reverse Payment', message: 'Reverse this payment? This creates a reversal record and locks the original.', confirmText: 'Yes, reverse', variant: 'warning' }) : Promise.resolve(window.confirm('Reverse this payment?'));
api.then(function(ok){
if (!ok) { btn.disabled = false; return; }
send('reverse', id, btn);
});
});
});
var table = document.getElementById('verificationTable');
var tabs = document.querySelectorAll('#verifyTabs .nav-link');
var selectAll = document.getElementById('selectAllRows');
var bulkBar = document.getElementById('bulkBar');
var bulkCount = document.getElementById('bulkSelectedCount');
var bulkApprove = document.getElementById('bulkApproveBtn');
var bulkReject = document.getElementById('bulkRejectBtn');
var emptyState = document.getElementById('verifyEmptyState');
var currentFilter = 'all';
function rowIsVisible(row){
return row && row.style.display !== 'none';
}
function setCollapseVisibility(row, visible){
try {
var nxt = row && row.nextElementSibling ? row.nextElementSibling : null;
if (nxt && nxt.classList && nxt.classList.contains('verif-collapse-row')) {
nxt.style.display = visible ? '' : 'none';
if (!visible) { nxt.classList.remove('show'); }
}
} catch(e){}
}
function updateEmptyState(){
if (!emptyState) return;
var rows = document.querySelectorAll('#verificationTable tbody tr.verif-row');
var vis = 0;
rows.forEach(function(r){ if (rowIsVisible(r)) vis++; });
emptyState.classList.toggle('d-none', vis > 0);
}
function updateBulkBar(){
var checked = document.querySelectorAll('.row-check:checked').length;
if (bulkCount) bulkCount.textContent = String(checked);
if (bulkApprove) bulkApprove.disabled = checked === 0;
if (bulkReject) bulkReject.disabled = checked === 0;
if (bulkBar) {
if (checked > 0) bulkBar.classList.remove('d-none');
else bulkBar.classList.add('d-none');
}
if (selectAll) {
var cbs = document.querySelectorAll('#verificationTable tbody tr.verif-row');
var enabledVisible = 0;
var checkedVisible = 0;
cbs.forEach(function(r){
if (!rowIsVisible(r)) return;
var cb = r.querySelector('.row-check');
if (!cb || cb.disabled) return;
enabledVisible++;
if (cb.checked) checkedVisible++;
});
selectAll.indeterminate = enabledVisible > 0 && checkedVisible > 0 && checkedVisible < enabledVisible;
selectAll.checked = enabledVisible > 0 && checkedVisible === enabledVisible;
selectAll.disabled = enabledVisible === 0;
}
}
function sortByNewest(){
var tbody = document.querySelector('#verificationTable tbody');
if (!tbody) return;
var empty = document.getElementById('verifyEmptyState');
var rows = Array.from(tbody.querySelectorAll('tr.verif-row'));
rows.sort(function(a, b){
var ta = parseFloat(a.getAttribute('data-upload-ts') || '0') || 0;
var tb = parseFloat(b.getAttribute('data-upload-ts') || '0') || 0;
if (tb !== ta) return tb - ta;
var ia = parseFloat(a.getAttribute('data-id') || '0') || 0;
var ib = parseFloat(b.getAttribute('data-id') || '0') || 0;
return ib - ia;
});
var frag = document.createDocumentFragment();
rows.forEach(function(r){
var nxt = r.nextElementSibling;
frag.appendChild(r);
if (nxt && nxt.classList && nxt.classList.contains('verif-collapse-row')) frag.appendChild(nxt);
});
tbody.appendChild(frag);
if (empty) tbody.appendChild(empty);
}
function applyFilter(){
var rows = document.querySelectorAll('#verificationTable tbody tr.verif-row');
rows.forEach(function(r){
var st = r.getAttribute('data-status') || 'unknown';
var show = true;
if (currentFilter === 'pending') show = (st === 'pending');
else if (currentFilter === 'approved') show = (st === 'approved');
else if (currentFilter === 'rejected') show = (st === 'rejected');
r.style.display = show ? '' : 'none';
if (!show) {
var cb = r.querySelector('.row-check');
if (cb) cb.checked = false;
}
setCollapseVisibility(r, show);
});
sortByNewest();
updateEmptyState();
updateBulkBar();
}
if (tabs && tabs.length) {
tabs.forEach(function(t){
t.addEventListener('click', function(){
tabs.forEach(function(x){ x.classList.remove('active'); });
t.classList.add('active');
currentFilter = t.getAttribute('data-filter') || 'all';
applyFilter();
});
});
}
if (selectAll) {
selectAll.addEventListener('change', function(){
var rows = document.querySelectorAll('#verificationTable tbody tr.verif-row');
rows.forEach(function(r){
if (!rowIsVisible(r)) return;
var cb = r.querySelector('.row-check');
if (!cb || cb.disabled) return;
cb.checked = !!selectAll.checked;
});
updateBulkBar();
});
}
document.querySelectorAll('.row-check').forEach(function(cb){
cb.addEventListener('change', updateBulkBar);
});
function postAction(action, id){
var fd = new FormData();
fd.append('pm_action', action);
fd.append('pm_id', id);
return fetch('finance-payments.php', { method: 'POST', body: fd })
.then(function(r){ return r.json(); })
.then(function(j){ return { ok: !!(j && j.success), id: id }; })
.catch(function(){ return { ok: false, id: id }; });
}
function bulkRun(action){
var ids = [];
document.querySelectorAll('.row-check:checked').forEach(function(cb){
var id = cb.getAttribute('data-id');
if (id) ids.push(id);
});
if (!ids.length) return;
var title = action === 'approve' ? 'Approve Selected' : 'Reject Selected';
var message = action === 'approve' ? ('Approve ' + ids.length + ' selected payments?') : ('Reject ' + ids.length + ' selected payments?');
var variant = action === 'approve' ? 'success' : 'danger';
var confirmText = action === 'approve' ? 'Yes, approve' : 'Yes, reject';
var api = window.showConfirm ? window.showConfirm({ title: title, message: message, confirmText: confirmText, variant: variant }) : Promise.resolve(window.confirm(message));
api.then(function(ok){
if (!ok) return;
if (bulkApprove) bulkApprove.disabled = true;
if (bulkReject) bulkReject.disabled = true;
if (selectAll) selectAll.disabled = true;
var failures = [];
var i = 0;
function next(){
if (i >= ids.length) {
if (failures.length) { alert('Some actions failed (' + failures.length + '). Reloading…'); }
location.reload();
return;
}
var id = ids[i];
i++;
postAction(action, id).then(function(res){
if (!res.ok) failures.push(id);
next();
});
}
next();
});
}
if (bulkApprove) bulkApprove.addEventListener('click', function(){ bulkRun('approve'); });
if (bulkReject) bulkReject.addEventListener('click', function(){ bulkRun('reject'); });
applyFilter();
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>