nhance_partner_be/app/Libraries/PartnerPayoutGridRetention.php
2026-04-08 16:56:41 +05:30

370 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Libraries;
use CodeIgniter\Database\BaseConnection;
class PartnerPayoutGridRetention
{
/**
* 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
{
if ($agentId <= 0) {
return [];
}
return $db->table('partner_retention_rate prr')
->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()
->getResultArray();
}
public static function normalizeVehicleTypeLabel(?string $s): string
{
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)));
}
/**
* 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'] === '') {
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 = [
'partner_VT' => $partnerVt,
'partner_RR' => $partnerRr,
];
$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;
}
// 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}"] = $meta["id::{$vid}"] ?? $entry;
}
}
return $meta;
}
public static function buildPartnerMetaMap(BaseConnection $db, int $agentId): array
{
return self::partnerMetaMapFromPrrRows(self::fetchPrrPvtRows($db, $agentId));
}
public static function buildMasterNormLabelToVehicleTypeIdMap(BaseConnection $db): array
{
$rows = $db->table('vehicle_type')
->select('id, vehicle_type')
->where('is_active', 1)
->get()
->getResultArray();
$out = [];
foreach ($rows as $r) {
$k = self::normalizeVehicleTypeLabel($r['vehicle_type'] ?? '');
if ($k === '') continue;
$out[$k] = (int) $r['id'];
}
return $out;
}
/**
* Build segment label → id map for resolving grid segment text to segment_id.
*/
public static function buildMasterNormSegmentToIdMap(BaseConnection $db): array
{
// 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;
$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;
}
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;
}
public static function gridPayoutDisplayManagerAccounts($raw): string
{
if ($raw === null || $raw === '' || $raw === '-') return '-';
$v = self::toFloat($raw);
if ($v === null || $v <= 0) return '-';
return self::formatGridNumericDisplay($v);
}
/**
* 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 '-';
// Formula: RR base (e.g. RR=1.0, comp=7.2 → 1.0 - 7.2 = -6.2)
$result = $partnerRr - $base;
return self::formatGridNumericDisplay($result);
}
private static function formatGridNumericDisplay(float $v): string
{
if (abs($v - round($v)) < 0.00001) {
return (string) (int) round($v);
}
return rtrim(rtrim(number_format($v, 2, '.', ''), '0'), '.');
}
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);
}
unset($row);
return $rows;
}
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;
$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;
}
if ($normVt !== '') {
$map["vt::{$normVt}"] = $map["vt::{$normVt}"] ?? $rate;
}
if ($vid > 0 && $sid > 0) {
$map["id::{$vid}::seg::{$sid}"] = $rate;
}
if ($vid > 0) {
$map["id::{$vid}"] = $map["id::{$vid}"] ?? $rate;
}
}
return $map;
}
public static function buildRetentionMap(BaseConnection $db, int $agentId): array
{
return self::retentionMapFromPrrRows(self::fetchPrrPvtRows($db, $agentId));
}
/**
* 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;
$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;
}
$rr = (float) $pm['partner_RR'];
$row['partner_VT'] = $pm['partner_VT'];
$row['partner_RR'] = $pm['partner_RR'];
// 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)
: '-';
// 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];
$vid = isset($row['vehicle_type_id']) ? (int) $row['vehicle_type_id'] : 0;
if ($vid > 0 && isset($map['id:' . $vid])) return $map['id:' . $vid];
return null;
}
public static function adjustPayoutValue($raw, float $rate)
{
if ($raw === null || $raw === '' || $raw === '-') return $raw;
$base = self::toFloat($raw);
if ($base === null) return $raw;
$adj = $base - $rate;
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;
$clean = preg_replace('/[^0-9.\-]/', '', (string) $raw);
if ($clean === '' || $clean === '-' || $clean === '.') return null;
return (float) $clean;
}
}