mailed incentive: member gets higher amount (survey took longer) * - If actual LOI incentive < mailed incentive: member keeps original promise (can't reduce) * - Final reward = max(mailed_incentive, actual_loi_incentive) * * POINTS CONVERSION: * - 1 point = ₹0.50 (i.e. ₹1 = 2 points) * - All incentive calculations are in rupees first, then converted to points for crediting * * Place in: /public_html/close-project-rewards.php * Called by: clients/close-project.php when project status changes to "Closed" */ // Conversion rate: 1 point = ₹0.50, so ₹1 = 2 points define('POINTS_PER_RUPEE', 2); /** * Calculate incentive from LOI (same formula used in send-invitations.php) * Base ₹10 + ₹1 per minute beyond 5 minutes * Returns value in RUPEES */ function calculateIncentiveFromLOI($loiMinutes) { $base = 10; $extra = max(0, $loiMinutes - 5); return $base + $extra; } /** * Convert rupees to points */ function rupeesToPoints($rupees) { return round($rupees * POINTS_PER_RUPEE); } function processProjectCloseRewards($projectNumericId, $shopPdo = null, $panelPdo = null) { $summary = [ 'members_rewarded' => 0, 'total_member_points' => 0, 'total_member_rupees' => 0, 'affiliates_rewarded' => 0, 'total_affiliate_commission' => 0, 'incentive_note' => '', 'errors' => [] ]; try { // Get DB connections if ($shopPdo === null) { if (function_exists('getClientDBConnection')) { $shopPdo = getClientDBConnection(); } elseif (function_exists('getDBConnection')) { $shopPdo = getDBConnection(); } else { throw new Exception('No shop database connection available'); } } if ($panelPdo === null) { if (function_exists('getPanelDBConnection')) { $panelPdo = getPanelDBConnection(); } else { $summary['errors'][] = 'No panel DB connection - member points not credited'; $panelPdo = null; } } // Get project details $stmt = $shopPdo->prepare("SELECT * FROM projects WHERE id = ?"); $stmt->execute([$projectNumericId]); $project = $stmt->fetch(); if (!$project) { $summary['errors'][] = 'Project not found'; return $summary; } $projectCode = $project['project_id']; // e.g. RRIT130226001 $eloi = (int)($project['eloi'] ?? 0); // ===================================================== // CALCULATE ACTUAL AVG LOI FOR THE PROJECT // ===================================================== $actualAvgLoiMinutes = 0; try { $stmt = $shopPdo->prepare(" SELECT AVG(actual_loi_seconds) as avg_loi_seconds, SUM(CASE WHEN actual_loi_seconds IS NOT NULL THEN 1 ELSE 0 END) as with_loi FROM survey_urls WHERE project_id = ? AND status = 'complete' AND quality_flag = 'valid' "); $stmt->execute([$projectCode]); $loiStats = $stmt->fetch(); if ($loiStats && $loiStats['with_loi'] > 0 && $loiStats['avg_loi_seconds'] > 0) { $actualAvgLoiMinutes = round($loiStats['avg_loi_seconds'] / 60, 1); } else { // Fallback to ELOI if no actual LOI data $actualAvgLoiMinutes = $eloi; } } catch (Exception $e) { $actualAvgLoiMinutes = $eloi; } // Calculate actual-LOI-based incentive (in RUPEES) $actualLoiIncentive = calculateIncentiveFromLOI($actualAvgLoiMinutes); // Determine reward per complete from project level (in RUPEES) $rewardPerComplete = floatval($project['reward_per_complete'] ?? 0); // Get all selections for this project $stmt = $shopPdo->prepare("SELECT * FROM project_selections WHERE project_id = ?"); $stmt->execute([$projectNumericId]); $selections = $stmt->fetchAll(); if (empty($selections)) { $summary['errors'][] = 'No selections found'; return $summary; } foreach ($selections as $selection) { $selectionId = $selection['id']; // ===================================================== // DETERMINE MAILED (ORIGINAL) INCENTIVE (in RUPEES) // ===================================================== $mailedIncentive = $rewardPerComplete > 0 ? $rewardPerComplete : floatval($selection['reward_per_complete'] ?? 0); if ($mailedIncentive <= 0) { $mailedIncentive = floatval($selection['incentive_amount'] ?? 0); } // ===================================================== // APPLY INCENTIVE MODEL: max(mailed, actual_loi_based) // Both values are in RUPEES // ===================================================== $finalRewardRupees = max($mailedIncentive, $actualLoiIncentive); // Convert RUPEES to POINTS (₹1 = 2 points) $finalRewardPoints = rupeesToPoints($finalRewardRupees); // Determine the reason/note $rewardNote = ''; if ($actualLoiIncentive > $mailedIncentive && $mailedIncentive > 0) { $rewardNote = ' (Adjusted up: survey avg LOI ' . $actualAvgLoiMinutes . ' min exceeded estimate, incentive increased from ₹' . number_format($mailedIncentive, 0) . ' to ₹' . number_format($finalRewardRupees, 0) . ')'; $summary['incentive_note'] = 'Actual avg LOI (' . $actualAvgLoiMinutes . ' min) exceeded estimate (' . $eloi . ' min). Member incentive adjusted upward.'; } elseif ($actualLoiIncentive < $mailedIncentive && $mailedIncentive > 0) { $rewardNote = ' (Original promised incentive maintained despite lower avg LOI)'; if (empty($summary['incentive_note'])) { $summary['incentive_note'] = 'Actual avg LOI (' . $actualAvgLoiMinutes . ' min) was lower than estimate (' . $eloi . ' min). Original promised incentive of ₹' . number_format($mailedIncentive, 0) . ' maintained for members.'; } } if ($finalRewardRupees <= 0) { $summary['errors'][] = "Selection #{$selectionId}: no reward set - skipping member rewards"; } // Find all selection_members with sample_status = 'complete' $stmt = $shopPdo->prepare(" SELECT sm.* FROM selection_members sm WHERE sm.selection_id = ? AND sm.sample_status = 'complete' "); $stmt->execute([$selectionId]); $completeMembers = $stmt->fetchAll(); foreach ($completeMembers as $member) { $userId = $member['user_id']; try { // ======================================== // 1. CREDIT MEMBER SURVEY REWARD // ======================================== if ($finalRewardRupees > 0 && $panelPdo !== null) { // Check if already credited $alreadyCredited = false; try { $stmt = $shopPdo->prepare("SELECT id FROM survey_rewards_log WHERE user_id = ? AND project_id = ?"); $stmt->execute([$userId, $projectCode]); $alreadyCredited = (bool)$stmt->fetch(); } catch (Exception $e) { /* table might not exist */ } if (!$alreadyCredited) { // Log in shop DB (store POINTS) try { $stmt = $shopPdo->prepare(" INSERT INTO survey_rewards_log (user_id, project_id, selection_id, points_awarded, status) VALUES (?, ?, ?, ?, 'pending') "); $stmt->execute([$userId, $projectCode, $selectionId, $finalRewardPoints]); } catch (Exception $e) { error_log("survey_rewards_log insert: " . $e->getMessage()); } // Credit POINTS in panel DB $stmt = $panelPdo->prepare(" UPDATE user_points SET points = points + ?, total_earned = total_earned + ? WHERE user_id = ? "); $stmt->execute([$finalRewardPoints, $finalRewardPoints, $userId]); if ($stmt->rowCount() === 0) { $stmt = $panelPdo->prepare(" INSERT INTO user_points (user_id, points, total_earned) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE points = points + ?, total_earned = total_earned + ? "); $stmt->execute([$userId, $finalRewardPoints, $finalRewardPoints, $finalRewardPoints, $finalRewardPoints]); } // Add transaction record (POINTS) with LOI adjustment note $txnDesc = 'Survey reward: ' . $projectCode . ' - ' . $project['project_name'] . ' (₹' . number_format($finalRewardRupees, 2) . ' = ' . $finalRewardPoints . ' pts)' . $rewardNote; $stmt = $panelPdo->prepare(" INSERT INTO point_transactions (user_id, transaction_type, points, source, description, reference_id) VALUES (?, 'earned', ?, 'survey_reward', ?, ?) "); $stmt->execute([$userId, $finalRewardPoints, $txnDesc, $projectCode]); // Update survey_responses in panel DB // incentive_amount = RUPEES (what was promised), points_awarded = POINTS try { $stmt = $panelPdo->prepare(" INSERT INTO survey_responses (user_id, project_id, selection_id, status, incentive_amount, points_awarded, reward_status, completed_at) VALUES (?, ?, ?, 'complete', ?, ?, 'pending', NOW()) ON DUPLICATE KEY UPDATE points_awarded = ?, reward_status = 'pending' "); $stmt->execute([$userId, $projectCode, $selectionId, $finalRewardRupees, $finalRewardPoints, $finalRewardPoints]); } catch (Exception $e) { /* non-critical */ } // Update selection_members points_awarded (POINTS) try { $stmt = $shopPdo->prepare("UPDATE selection_members SET points_awarded = ? WHERE selection_id = ? AND user_id = ?"); $stmt->execute([$finalRewardPoints, $selectionId, $userId]); } catch (Exception $e) { /* non-critical */ } $summary['members_rewarded']++; $summary['total_member_points'] += $finalRewardPoints; $summary['total_member_rupees'] += $finalRewardRupees; } } // ========================================== // 2. CREDIT AFFILIATE REVENUE SHARE // ========================================== $stmt = $shopPdo->prepare(" SELECT a.id as affiliate_id, a.signup_reward FROM affiliate_signups asu JOIN affiliates a ON asu.affiliate_id = a.id WHERE asu.panel_user_id = ? AND asu.email_verified = 1 LIMIT 1 "); $stmt->execute([$userId]); $affiliateLink = $stmt->fetch(); if ($affiliateLink) { $affiliateId = $affiliateLink['affiliate_id']; $revenueShare = 0; // Try survey_reward column first try { $stmt = $shopPdo->prepare("SELECT survey_reward FROM affiliates WHERE id = ?"); $stmt->execute([$affiliateId]); $row = $stmt->fetch(); $revenueShare = floatval($row['survey_reward'] ?? 0); } catch (Exception $e) { /* column might not exist */ } // Fallback: revenue_share_per_survey if ($revenueShare <= 0) { try { $stmt = $shopPdo->prepare("SELECT revenue_share_per_survey FROM affiliates WHERE id = ?"); $stmt->execute([$affiliateId]); $row = $stmt->fetch(); $revenueShare = floatval($row['revenue_share_per_survey'] ?? 0); } catch (Exception $e) { /* column might not exist */ } } // Fallback: percentage of member reward (use RUPEE value) if ($revenueShare <= 0 && $finalRewardRupees > 0) { $commPct = floatval($project['affiliate_commission_pct'] ?? 10); $revenueShare = round($finalRewardRupees * ($commPct / 100), 2); } if ($revenueShare > 0) { $alreadyCredited = false; try { $stmt = $shopPdo->prepare(" SELECT id FROM partner_commission_log WHERE affiliate_id = ? AND type = 'survey_revenue_share' AND reference_id = ? AND panel_user_id = ? "); $stmt->execute([$affiliateId, $projectCode, $userId]); $alreadyCredited = (bool)$stmt->fetch(); } catch (Exception $e) { /* table might not exist */ } if (!$alreadyCredited) { try { $stmt = $shopPdo->prepare(" INSERT INTO partner_commission_log (affiliate_id, type, amount, reference_id, panel_user_id, description) VALUES (?, 'survey_revenue_share', ?, ?, ?, ?) "); $stmt->execute([$affiliateId, $revenueShare, $projectCode, $userId, 'Survey revenue share: ' . $projectCode . ' - ' . $project['project_name']]); } catch (Exception $e) { error_log("partner_commission_log insert: " . $e->getMessage()); } $stmt = $shopPdo->prepare(" UPDATE affiliates SET total_commission_earned = total_commission_earned + ?, commission_balance = commission_balance + ? WHERE id = ? "); $stmt->execute([$revenueShare, $revenueShare, $affiliateId]); $summary['affiliates_rewarded']++; $summary['total_affiliate_commission'] += $revenueShare; } } } } catch (Exception $e) { $summary['errors'][] = "User {$userId}: " . $e->getMessage(); } } } } catch (Exception $e) { $summary['errors'][] = $e->getMessage(); } return $summary; } ?>