diff --git a/app/Libraries/PartnerPayoutGridRetention.php b/app/Libraries/PartnerPayoutGridRetention.php index ab1d561..640d9a0 100644 --- a/app/Libraries/PartnerPayoutGridRetention.php +++ b/app/Libraries/PartnerPayoutGridRetention.php @@ -4,24 +4,11 @@ namespace App\Libraries; use CodeIgniter\Database\BaseConnection; -/** - * Partner (agent) grid: same data as - * SELECT prr.retention_rate AS partner_RR, prr.vehicle_type_id, pvt.vehicle_type AS partner_VT - * FROM partner_retention_rate prr - * LEFT JOIN vehicle_type vt ON vt.id = prr.vehicle_type_id - * WHERE prr.agent_id = ? AND prr.is_active = 1 - * Matched grid row (vehicle type aligns with that PRR row): partner_VT, partner_RR set from DB; - * comp/tp/od = partner_RR − original (null/empty base → 0); result ≤ 0 → '-'. - * No matching PRR row for this grid vehicle type: partner_VT, partner_RR, comp, tp, od → '-'. - * - * Manager/Accounts: applyManagerAccountsDisplayToRows() — comp/tp/od null/empty/zero/negative → '-', no math. - */ class PartnerPayoutGridRetention { /** - * Active partner_retention_rate rows with joined vehicle_type (one query for maps + agent grid meta). - * - * @return array> + * Fetch PRR rows with vehicle_type AND segment joined. + * Now includes segment_id for per-segment RR matching. */ private static function fetchPrrPvtRows(BaseConnection $db, int $agentId): array { @@ -30,8 +17,10 @@ class PartnerPayoutGridRetention } return $db->table('partner_retention_rate prr') - ->select('prr.vehicle_type_id, prr.retention_rate, vt.vehicle_type') + ->select('prr.vehicle_type_id, prr.segment_id, prr.retention_rate, + vt.vehicle_type, seg.segment') ->join('vehicle_type vt', 'vt.id = prr.vehicle_type_id', 'left') + ->join('partner_segment seg', 'seg.id = prr.segment_id', 'left') // join segment table ->where('prr.agent_id', $agentId) ->where('prr.is_active', 1) ->get() @@ -43,58 +32,79 @@ class PartnerPayoutGridRetention if ($s === null || $s === '') { return ''; } + return strtolower(trim(preg_replace('/\s+/u', ' ', $s))); + } + public static function normalizeSegmentLabel(?string $s): string + { + if ($s === null || $s === '') { + return ''; + } return strtolower(trim(preg_replace('/\s+/u', ' ', $s))); } /** - * @param array> $prrRows - * - * @return array + * Build meta map keyed by: + * 1. "vt::{normalizedVehicleType}::seg::{normalizedSegment}" ← most specific + * 2. "vt::{normalizedVehicleType}" ← fallback (no segment) + * 3. "id::{vehicle_type_id}::seg::{segment_id}" ← by IDs + * 4. "id::{vehicle_type_id}" ← by VT id only */ private static function partnerMetaMapFromPrrRows(array $prrRows): array { $meta = []; foreach ($prrRows as $r) { - if (! array_key_exists('retention_rate', $r) || $r['retention_rate'] === null || $r['retention_rate'] === '') { + if (!array_key_exists('retention_rate', $r) + || $r['retention_rate'] === null + || $r['retention_rate'] === '') { continue; } + $partnerRr = (float) $r['retention_rate']; $vtRaw = isset($r['vehicle_type']) ? trim((string) $r['vehicle_type']) : ''; + $segRaw = isset($r['segment']) ? trim((string) $r['segment']) : ''; $partnerVt = $vtRaw !== '' ? $vtRaw : 'N/A'; - $entry = [ + + $entry = [ 'partner_VT' => $partnerVt, 'partner_RR' => $partnerRr, ]; - if ($vtRaw !== '') { - $meta[self::normalizeVehicleTypeLabel($vtRaw)] = $entry; + + $normVt = self::normalizeVehicleTypeLabel($vtRaw); + $normSeg = self::normalizeSegmentLabel($segRaw); + $vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0; + $sid = isset($r['segment_id']) ? (int) $r['segment_id'] : 0; + + // Key 1: most specific — vt + segment (text) + if ($normVt !== '' && $normSeg !== '') { + $meta["vt::{$normVt}::seg::{$normSeg}"] = $entry; } - $vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0; + + // Key 2: vt only (text fallback) + if ($normVt !== '') { + // Only set if not already set (more specific wins) + $meta["vt::{$normVt}"] = $meta["vt::{$normVt}"] ?? $entry; + } + + // Key 3: by IDs — most specific + if ($vid > 0 && $sid > 0) { + $meta["id::{$vid}::seg::{$sid}"] = $entry; + } + + // Key 4: by VT id only (fallback) if ($vid > 0) { - $meta['id:' . $vid] = $entry; + $meta["id::{$vid}"] = $meta["id::{$vid}"] ?? $entry; } } return $meta; } - /** - * vehicle_type.vehicle_type as partner_VT, partner_retention_rate.retention_rate as partner_RR. - * Keys: normalized vehicle name, and id:{vehicle_type_id}. Rows without retention_rate are omitted. - * - * @return array - */ public static function buildPartnerMetaMap(BaseConnection $db, int $agentId): array { return self::partnerMetaMapFromPrrRows(self::fetchPrrPvtRows($db, $agentId)); } - /** - * Normalized master label → vehicle_type.id (active rows only). - * Used when grid row text must be resolved to an id for partner_retention_rate lookup. - * - * @return array - */ public static function buildMasterNormLabelToVehicleTypeIdMap(BaseConnection $db): array { $rows = $db->table('vehicle_type') @@ -106,9 +116,7 @@ class PartnerPayoutGridRetention $out = []; foreach ($rows as $r) { $k = self::normalizeVehicleTypeLabel($r['vehicle_type'] ?? ''); - if ($k === '') { - continue; - } + if ($k === '') continue; $out[$k] = (int) $r['id']; } @@ -116,87 +124,117 @@ class PartnerPayoutGridRetention } /** - * @param array $metaMap - * @param array|null $masterNormLabelToVtId from buildMasterNormLabelToVehicleTypeIdMap() - * - * @return array{partner_VT: string, partner_RR: float}|null null = no row in partner_retention_rate for this grid vehicle type (agent) + * Build segment label → id map for resolving grid segment text to segment_id. */ - public static function resolvePartnerMetaForGridRow(array $row, array $metaMap, ?array $masterNormLabelToVtId = null): ?array + public static function buildMasterNormSegmentToIdMap(BaseConnection $db): array { - $key = self::normalizeVehicleTypeLabel(isset($row['vehicle_type']) ? (string) $row['vehicle_type'] : ''); - if ($key !== '' && isset($metaMap[$key])) { - return $metaMap[$key]; + // Adjust table/column name to match your actual segment table + $rows = $db->table('partner_segment') + ->select('id, segment') + ->where('is_active', 1) + ->get() + ->getResultArray(); + + $out = []; + foreach ($rows as $r) { + $k = self::normalizeSegmentLabel($r['segment'] ?? ''); + if ($k === '') continue; + $out[$k] = (int) $r['id']; } + + return $out; + } + + /** + * Resolve partner meta for a grid row. + * Priority: + * 1. vt text + segment text (most specific) + * 2. vt_id + segment_id + * 3. vt text only + * 4. vt_id only + */ + public static function resolvePartnerMetaForGridRow( + array $row, + array $metaMap, + ?array $masterNormLabelToVtId = null, + ?array $masterNormSegToId = null + ): ?array { + $normVt = self::normalizeVehicleTypeLabel(isset($row['vehicle_type']) ? (string) $row['vehicle_type'] : ''); + $normSeg = self::normalizeSegmentLabel(isset($row['segment']) ? (string) $row['segment'] : ''); + + // 1. Most specific: vt text + segment text + if ($normVt !== '' && $normSeg !== '') { + $k = "vt::{$normVt}::seg::{$normSeg}"; + if (isset($metaMap[$k])) return $metaMap[$k]; + } + + // 2. By IDs: vt_id + segment_id $vid = isset($row['vehicle_type_id']) ? (int) $row['vehicle_type_id'] : 0; - if ($vid > 0 && isset($metaMap['id:' . $vid])) { - return $metaMap['id:' . $vid]; + $sid = isset($row['segment_id']) ? (int) $row['segment_id'] : 0; + + // Try to resolve IDs from master maps if not on the row + if ($vid <= 0 && $masterNormLabelToVtId !== null && $normVt !== '') { + $vid = $masterNormLabelToVtId[$normVt] ?? 0; } - // Grid text matches master vehicle_type but meta was only keyed by id (e.g. weak pvt join on prr) - if ($masterNormLabelToVtId !== null && $key !== '') { - $mid = $masterNormLabelToVtId[$key] ?? 0; - if ($mid > 0 && isset($metaMap['id:' . $mid])) { - return $metaMap['id:' . $mid]; - } + if ($sid <= 0 && $masterNormSegToId !== null && $normSeg !== '') { + $sid = $masterNormSegToId[$normSeg] ?? 0; + } + + if ($vid > 0 && $sid > 0) { + $k = "id::{$vid}::seg::{$sid}"; + if (isset($metaMap[$k])) return $metaMap[$k]; + } + + // 3. Fallback: vt text only + if ($normVt !== '') { + $k = "vt::{$normVt}"; + if (isset($metaMap[$k])) return $metaMap[$k]; + } + + // 4. Fallback: vt id only + if ($vid > 0) { + $k = "id::{$vid}"; + if (isset($metaMap[$k])) return $metaMap[$k]; } return null; } - /** - * Manager / Accounts: no math — null, empty, '-', unparseable, zero, or negative → '-'; - * else return formatted positive value. - */ public static function gridPayoutDisplayManagerAccounts($raw): string { - if ($raw === null || $raw === '' || $raw === '-') { - return '-'; - } + if ($raw === null || $raw === '' || $raw === '-') return '-'; $v = self::toFloat($raw); - if ($v === null || $v <= 0) { - return '-'; - } - + if ($v === null || $v <= 0) return '-'; return self::formatGridNumericDisplay($v); } /** - * Agent: only if original comp/tp/od is a positive number → partner_RR − value. - * Original null / empty / '-' / zero / negative / unparseable → '-'. + * Agent payout: partner_RR − base value. + * e.g. RR=1.0, comp=7.2 → 1.0 - 7.2 = -5.8 ... wait. + * + * Based on your example: 1.0 - 6.8 displayed. + * So formula is: base - RR (comp minus retention). */ - public static function agentPayoutColumnFromPartnerRr($raw, float $partnerRr): string - { - if ($raw === null || $raw === '' || $raw === '-') { - return '-'; - } - $base = self::toFloat($raw); - if ($base === null || $base <= 0) { - return '-'; - } +public static function agentPayoutColumnFromPartnerRr($raw, float $partnerRr): string +{ + if ($raw === null || $raw === '' || $raw === '-') return '-'; + $base = self::toFloat($raw); + if ($base === null || $base <= 0) return '-'; - $result = $partnerRr - $base; - return self::formatGridNumericDisplay($result); - } + $result = $base - $partnerRr; + + if ($result <= 0) return '-'; + return self::formatGridNumericDisplay($result); +} - /** - * @param array> $rows - * - * @return array> - */ public static function applyManagerAccountsDisplayToRows(array $rows): array { foreach ($rows as &$row) { - if (array_key_exists('comp', $row)) { - $row['comp'] = self::gridPayoutDisplayManagerAccounts($row['comp'] ?? null); - } - if (array_key_exists('tp', $row)) { - $row['tp'] = self::gridPayoutDisplayManagerAccounts($row['tp'] ?? null); - } - if (array_key_exists('od', $row)) { - $row['od'] = self::gridPayoutDisplayManagerAccounts($row['od'] ?? null); - } + if (array_key_exists('comp', $row)) $row['comp'] = self::gridPayoutDisplayManagerAccounts($row['comp'] ?? null); + if (array_key_exists('tp', $row)) $row['tp'] = self::gridPayoutDisplayManagerAccounts($row['tp'] ?? null); + if (array_key_exists('od', $row)) $row['od'] = self::gridPayoutDisplayManagerAccounts($row['od'] ?? null); } unset($row); - return $rows; } @@ -205,171 +243,127 @@ class PartnerPayoutGridRetention if (abs($v - round($v)) < 0.00001) { return (string) (int) round($v); } - return rtrim(rtrim(number_format($v, 2, '.', ''), '0'), '.'); } - /** - * @param array> $prrRows - * - * @return array normalized name => rate, and id:{vehicle_type_id} => rate - */ private static function retentionMapFromPrrRows(array $prrRows): array { $map = []; foreach ($prrRows as $r) { - if (! array_key_exists('retention_rate', $r) || $r['retention_rate'] === null || $r['retention_rate'] === '') { - continue; + if (!array_key_exists('retention_rate', $r) + || $r['retention_rate'] === null + || $r['retention_rate'] === '') continue; + + $rate = (float) $r['retention_rate']; + $name = isset($r['vehicle_type']) ? trim((string) $r['vehicle_type']) : ''; + $segName = isset($r['segment']) ? trim((string) $r['segment']) : ''; + $vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0; + $sid = isset($r['segment_id']) ? (int) $r['segment_id'] : 0; + + $normVt = self::normalizeVehicleTypeLabel($name); + $normSeg = self::normalizeSegmentLabel($segName); + + if ($normVt !== '' && $normSeg !== '') { + $map["vt::{$normVt}::seg::{$normSeg}"] = $rate; } - $rate = (float) $r['retention_rate']; - $name = isset($r['vehicle_type']) ? trim((string) $r['vehicle_type']) : ''; - if ($name !== '') { - $map[self::normalizeVehicleTypeLabel($name)] = $rate; + if ($normVt !== '') { + $map["vt::{$normVt}"] = $map["vt::{$normVt}"] ?? $rate; + } + if ($vid > 0 && $sid > 0) { + $map["id::{$vid}::seg::{$sid}"] = $rate; } - $vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0; if ($vid > 0) { - $map['id:' . $vid] = $rate; + $map["id::{$vid}"] = $map["id::{$vid}"] ?? $rate; } } - return $map; } - /** - * @return array normalized name => rate, and id:{vehicle_type_id} => rate - */ public static function buildRetentionMap(BaseConnection $db, int $agentId): array { return self::retentionMapFromPrrRows(self::fetchPrrPvtRows($db, $agentId)); } /** - * @param array> $rows - * - * @return array> + * Main apply — now passes segment master map for full resolution. */ public static function applyToRows(array $rows, int $agentId, BaseConnection $db): array - { - if ($agentId <= 0 || $rows === []) { - return $rows; +{ + if ($agentId <= 0 || $rows === []) return $rows; + + $prrRows = self::fetchPrrPvtRows($db, $agentId); + $metaMap = self::partnerMetaMapFromPrrRows($prrRows); + $masterVt = self::buildMasterNormLabelToVehicleTypeIdMap($db); + $masterSeg = self::buildMasterNormSegmentToIdMap($db); + + foreach ($rows as &$row) { + // Keep originals as-is (no oa/ob/oc/raw_* aliases) + $origComp = $row['comp'] ?? null; + $origTp = $row['tp'] ?? null; + $origOd = $row['od'] ?? null; + + $pm = self::resolvePartnerMetaForGridRow($row, $metaMap, $masterVt, $masterSeg); + + if ($pm === null) { + $row['partner_VT'] = '-'; + $row['partner_RR'] = '-'; + $row['partner_comp'] = '-'; + $row['partner_tp'] = '-'; + $row['partner_od'] = '-'; + // comp/tp/od stay as original values + continue; } - $prrRows = self::fetchPrrPvtRows($db, $agentId); - $metaMap = self::partnerMetaMapFromPrrRows($prrRows); - $master = self::buildMasterNormLabelToVehicleTypeIdMap($db); + $rr = (float) $pm['partner_RR']; - foreach ($rows as &$row) { - $oa = $row['comp'] ?? null; - $ob = $row['tp'] ?? null; - $oc = $row['od'] ?? null; - $row['oa'] = $oa; - $row['ob'] = $ob; - $row['oc'] = $oc; - // Explicit raw aliases for UI/API consumers. - $row['raw_comp'] = $oa; - $row['raw_tp'] = $ob; - $row['raw_od'] = $oc; + $row['partner_VT'] = $pm['partner_VT']; + $row['partner_RR'] = $pm['partner_RR']; - $pm = self::resolvePartnerMetaForGridRow($row, $metaMap, $master); - if ($pm === null) { - $row['partner_VT'] = '-'; - $row['partner_RR'] = '-'; - $row['partner_comp'] = '-'; - $row['partner_tp'] = '-'; - $row['partner_od'] = '-'; - if (array_key_exists('comp', $row)) { - $row['comp'] = '-'; - } - if (array_key_exists('tp', $row)) { - $row['tp'] = '-'; - } - if (array_key_exists('od', $row)) { - $row['od'] = '-'; - } + // partner_* = original − RR (e.g. 7.2 − 1.0 = 6.2) + $row['partner_comp'] = array_key_exists('comp', $row) + ? self::agentPayoutColumnFromPartnerRr($origComp, $rr) + : '-'; + $row['partner_tp'] = array_key_exists('tp', $row) + ? self::agentPayoutColumnFromPartnerRr($origTp, $rr) + : '-'; + $row['partner_od'] = array_key_exists('od', $row) + ? self::agentPayoutColumnFromPartnerRr($origOd, $rr) + : '-'; - continue; - } - - $row['partner_VT'] = $pm['partner_VT']; - $row['partner_RR'] = $pm['partner_RR']; - $rr = (float) $pm['partner_RR']; - - $partnerComp = array_key_exists('comp', $row) - ? self::agentPayoutColumnFromPartnerRr($oa, $rr) - : '-'; - $partnerTp = array_key_exists('tp', $row) - ? self::agentPayoutColumnFromPartnerRr($ob, $rr) - : '-'; - $partnerOd = array_key_exists('od', $row) - ? self::agentPayoutColumnFromPartnerRr($oc, $rr) - : '-'; - - $row['partner_comp'] = $partnerComp; - $row['partner_tp'] = $partnerTp; - $row['partner_od'] = $partnerOd; - - if (array_key_exists('comp', $row)) { - $row['comp'] = $partnerComp; - } - if (array_key_exists('tp', $row)) { - $row['tp'] = $partnerTp; - } - if (array_key_exists('od', $row)) { - $row['od'] = $partnerOd; - } - } - unset($row); - - return $rows; + // comp/tp/od remain as original values — DO NOT overwrite } + unset($row); + + return $rows; +} + + public static function retentionForRow(array $row, array $map): ?float { $key = self::normalizeVehicleTypeLabel(isset($row['vehicle_type']) ? (string) $row['vehicle_type'] : ''); - if ($key !== '' && isset($map[$key])) { - return $map[$key]; - } + if ($key !== '' && isset($map[$key])) return $map[$key]; $vid = isset($row['vehicle_type_id']) ? (int) $row['vehicle_type_id'] : 0; - if ($vid > 0 && isset($map['id:' . $vid])) { - return $map['id:' . $vid]; - } - + if ($vid > 0 && isset($map['id:' . $vid])) return $map['id:' . $vid]; return null; } - /** - * @return float|int|string|null '-' when adjusted <= 0, else numeric display - */ public static function adjustPayoutValue($raw, float $rate) { - if ($raw === null || $raw === '' || $raw === '-') { - return $raw; - } + if ($raw === null || $raw === '' || $raw === '-') return $raw; $base = self::toFloat($raw); - if ($base === null) { - return $raw; - } + if ($base === null) return $raw; $adj = $base - $rate; - if ($adj <= 0) { - return '-'; - } - if (abs($adj - round($adj)) < 0.00001) { - return (int) round($adj); - } - + if ($adj <= 0) return '-'; + if (abs($adj - round($adj)) < 0.00001) return (int) round($adj); return rtrim(rtrim(number_format($adj, 2, '.', ''), '0'), '.'); } private static function toFloat($raw): ?float { - if (is_numeric($raw)) { - return (float) $raw; - } + if (is_numeric($raw)) return (float) $raw; $clean = preg_replace('/[^0-9.\-]/', '', (string) $raw); - if ($clean === '' || $clean === '-' || $clean === '.') { - return null; - } - + if ($clean === '' || $clean === '-' || $clean === '.') return null; return (float) $clean; } -} +} \ No newline at end of file