nhance_partner_be/app/Libraries/PartnerPayoutGridRetention.php
2026-04-08 15:52:25 +05:30

376 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;
/**
* 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<int, array<string, mixed>>
*/
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.retention_rate, vt.vehicle_type')
->join('vehicle_type vt', 'vt.id = prr.vehicle_type_id', 'left')
->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)));
}
/**
* @param array<int, array<string, mixed>> $prrRows
*
* @return array<string, array{partner_VT: string, partner_RR: float}>
*/
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']) : '';
$partnerVt = $vtRaw !== '' ? $vtRaw : 'N/A';
$entry = [
'partner_VT' => $partnerVt,
'partner_RR' => $partnerRr,
];
if ($vtRaw !== '') {
$meta[self::normalizeVehicleTypeLabel($vtRaw)] = $entry;
}
$vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0;
if ($vid > 0) {
$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<string, array{partner_VT: string, partner_RR: float}>
*/
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<string, int>
*/
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;
}
/**
* @param array<string, array{partner_VT: string, partner_RR: float}> $metaMap
* @param array<string, int>|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)
*/
public static function resolvePartnerMetaForGridRow(array $row, array $metaMap, ?array $masterNormLabelToVtId = null): ?array
{
$key = self::normalizeVehicleTypeLabel(isset($row['vehicle_type']) ? (string) $row['vehicle_type'] : '');
if ($key !== '' && isset($metaMap[$key])) {
return $metaMap[$key];
}
$vid = isset($row['vehicle_type_id']) ? (int) $row['vehicle_type_id'] : 0;
if ($vid > 0 && isset($metaMap['id:' . $vid])) {
return $metaMap['id:' . $vid];
}
// 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];
}
}
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 '-';
}
$v = self::toFloat($raw);
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 → '-'.
*/
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);
}
/**
* @param array<int, array<string, mixed>> $rows
*
* @return array<int, array<string, mixed>>
*/
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 formatGridNumericDisplay(float $v): string
{
if (abs($v - round($v)) < 0.00001) {
return (string) (int) round($v);
}
return rtrim(rtrim(number_format($v, 2, '.', ''), '0'), '.');
}
/**
* @param array<int, array<string, mixed>> $prrRows
*
* @return array<string, float> 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;
}
$rate = (float) $r['retention_rate'];
$name = isset($r['vehicle_type']) ? trim((string) $r['vehicle_type']) : '';
if ($name !== '') {
$map[self::normalizeVehicleTypeLabel($name)] = $rate;
}
$vid = isset($r['vehicle_type_id']) ? (int) $r['vehicle_type_id'] : 0;
if ($vid > 0) {
$map['id:' . $vid] = $rate;
}
}
return $map;
}
/**
* @return array<string, float> 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<int, array<string, mixed>> $rows
*
* @return array<int, array<string, mixed>>
*/
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);
$master = self::buildMasterNormLabelToVehicleTypeIdMap($db);
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;
$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'] = '-';
}
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;
}
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;
}
/**
* @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;
}
$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;
}
}