| @@ -1239,76 +1239,130 @@ | ||
| 1239 | 1239 | |
| 1240 | 1240 | return ['dates' => $dates, 'labels' => $labels]; |
| 1241 | 1241 | } |
| 1242 | 1242 | |
| 1243 | - public static function getPaymentsByType($startDate, $endDate, $type, $formId = 0) | |
| 1243 | + /** | |
| 1244 | + * All payment figures in ONE transaction scan, keyed 'onetime' / 'subscription' | |
| 1245 | + * / 'all' (disabled module → each empty []). 'all' counts every transaction type | |
| 1246 | + * with no filter, preserving getPaymentsByType()'s legacy no-type-filter contract. | |
| 1247 | + * Each block keeps getPaymentsByType()'s shape — blended per-status totals for the | |
| 1248 | + * admin consumer — plus a by_currency split for callers that must not blend | |
| 1249 | + * currencies (MCP). One scan so consumers needing both types don't pay for two. | |
| 1250 | + */ | |
| 1251 | + public static function getPaymentBreakdown($startDate, $endDate, $formId = 0) | |
| 1244 | 1252 | { |
| 1245 | 1253 | $paymentSettings = get_option('__fluentform_payment_module_settings'); |
| 1246 | 1254 | if (!$paymentSettings || !Arr::isTrue($paymentSettings, 'status')) { |
| 1247 | - return []; // Return empty if payment module is disabled | |
| 1255 | + return ['onetime' => [], 'subscription' => [], 'all' => []]; | |
| 1248 | 1256 | } |
| 1249 | 1257 | list($startDate, $endDate) = self::processDateRange($startDate, $endDate); |
| 1250 | - | |
| 1251 | - // Base query for transactions | |
| 1258 | + | |
| 1252 | 1259 | $query = \FluentForm\App\Models\Transaction::whereBetween('created_at', [$startDate, $endDate]); |
| 1253 | - | |
| 1254 | - // Filter by transaction type if specified | |
| 1255 | - if ($type === 'subscription') { | |
| 1256 | - $query->whereIn('transaction_type', ['subscription', 'subscription_signup_fee']); | |
| 1257 | - } elseif ($type === 'onetime') { | |
| 1258 | - $query->where('transaction_type', 'onetime'); | |
| 1259 | - } | |
| 1260 | - | |
| 1261 | 1260 | self::scopeQueryToAllowedForms($query, $formId); |
| 1262 | - | |
| 1263 | - // Get payments grouped by status | |
| 1264 | - $payments = $query->select('status') | |
| 1261 | + $payments = $query->select('transaction_type', 'status', 'currency') | |
| 1265 | 1262 | ->selectRaw('SUM(payment_total) as total_amount') |
| 1266 | 1263 | ->selectRaw('COUNT(*) as count') |
| 1267 | - ->groupBy('status') | |
| 1264 | + ->groupBy('transaction_type', 'status', 'currency') | |
| 1268 | 1265 | ->get(); |
| 1269 | - | |
| 1270 | - // Get the total payment amount | |
| 1271 | - $totalAmount = 0; | |
| 1266 | + | |
| 1267 | + $daysInRange = self::getDateDifference($startDate, $endDate); | |
| 1268 | + $weeksInRange = max(1, round($daysInRange / 7, 1)); | |
| 1269 | + $currencySign = Arr::get(PaymentHelper::getCurrencyConfig($formId), 'currency_sign', '$'); | |
| 1270 | + $defaultCurrency = strtoupper((string) Arr::get(PaymentHelper::getCurrencyConfig($formId), 'currency', 'USD')); | |
| 1271 | + | |
| 1272 | + $blended = ['onetime' => [], 'subscription' => [], 'all' => []]; | |
| 1273 | + $perCurrency = ['onetime' => [], 'subscription' => [], 'all' => []]; | |
| 1272 | 1274 | foreach ($payments as $payment) { |
| 1273 | - $totalAmount += $payment->total_amount; | |
| 1275 | + $status = strtolower((string) $payment->status); | |
| 1276 | + $cents = (int) $payment->total_amount; | |
| 1277 | + $count = (int) $payment->count; | |
| 1278 | + $currency = strtoupper(trim((string) $payment->currency)); | |
| 1279 | + if ('' === $currency) { | |
| 1280 | + $currency = $defaultCurrency; | |
| 1281 | + } | |
| 1282 | + | |
| 1283 | + self::addPaymentRow($blended['all'], $perCurrency['all'], $status, $currency, $cents, $count); | |
| 1284 | + | |
| 1285 | + if ('onetime' === $payment->transaction_type) { | |
| 1286 | + $type = 'onetime'; | |
| 1287 | + } elseif (in_array($payment->transaction_type, ['subscription', 'subscription_signup_fee'], true)) { | |
| 1288 | + $type = 'subscription'; | |
| 1289 | + } else { | |
| 1290 | + continue; | |
| 1291 | + } | |
| 1292 | + self::addPaymentRow($blended[$type], $perCurrency[$type], $status, $currency, $cents, $count); | |
| 1274 | 1293 | } |
| 1275 | - | |
| 1276 | - $formattedData = []; | |
| 1277 | - foreach ($payments as $payment) { | |
| 1278 | - $status = strtolower($payment->status); | |
| 1279 | - $amount = $payment->total_amount / 100; // Convert from cents to dollars | |
| 1280 | - $percentage = $totalAmount > 0 ? round(($payment->total_amount / $totalAmount) * 100, 2) : 0; | |
| 1281 | - | |
| 1282 | - $formattedData[$status] = [ | |
| 1283 | - 'amount' => $amount, | |
| 1284 | - 'percentage' => $percentage, | |
| 1285 | - 'count' => $payment->count | |
| 1286 | - ]; | |
| 1294 | + | |
| 1295 | + $out = []; | |
| 1296 | + foreach (['onetime', 'subscription', 'all'] as $type) { | |
| 1297 | + $byCurrency = []; | |
| 1298 | + foreach ($perCurrency[$type] as $currency => $statuses) { | |
| 1299 | + $symbol = html_entity_decode(PaymentHelper::getCurrencySymbol($currency), ENT_QUOTES); | |
| 1300 | + $byCurrency[$currency] = self::formatPaymentStatuses($statuses, $weeksInRange, $symbol); | |
| 1301 | + } | |
| 1302 | + $block = self::formatPaymentStatuses($blended[$type], $weeksInRange, $currencySign); | |
| 1303 | + $block['by_currency'] = $byCurrency; | |
| 1304 | + $out[$type] = $block; | |
| 1287 | 1305 | } |
| 1288 | - | |
| 1289 | - // Calculate weekly average paid amount | |
| 1290 | - $daysInRange = self::getDateDifference($startDate, $endDate); | |
| 1291 | - $weeksInRange = max(1, round($daysInRange / 7, 1)); | |
| 1292 | - | |
| 1306 | + | |
| 1307 | + return $out; | |
| 1308 | + } | |
| 1309 | + | |
| 1310 | + private static function addPaymentRow(array &$blended, array &$perCurrency, $status, $currency, $cents, $count) | |
| 1311 | + { | |
| 1312 | + if (!isset($blended[$status])) { | |
| 1313 | + $blended[$status] = ['amount' => 0, 'count' => 0]; | |
| 1314 | + } | |
| 1315 | + $blended[$status]['amount'] += $cents; | |
| 1316 | + $blended[$status]['count'] += $count; | |
| 1317 | + | |
| 1318 | + if (!isset($perCurrency[$currency][$status])) { | |
| 1319 | + $perCurrency[$currency][$status] = ['amount' => 0, 'count' => 0]; | |
| 1320 | + } | |
| 1321 | + $perCurrency[$currency][$status]['amount'] += $cents; | |
| 1322 | + $perCurrency[$currency][$status]['count'] += $count; | |
| 1323 | + } | |
| 1324 | + | |
| 1325 | + public static function getPaymentsByType($startDate, $endDate, $type, $formId = 0) | |
| 1326 | + { | |
| 1327 | + return Arr::get(self::getPaymentBreakdown($startDate, $endDate, $formId), $type, []); | |
| 1328 | + } | |
| 1329 | + | |
| 1330 | + /** | |
| 1331 | + * Shape a status => ['amount' => cents, 'count' => int] map into the payment | |
| 1332 | + * block used by both the admin Reports payment overview and MCP: whole-unit | |
| 1333 | + * amounts (cents ÷ 100), per-status share of the block total, integer counts, | |
| 1334 | + * and the paid weekly average. One formatter so the two callers can never drift. | |
| 1335 | + */ | |
| 1336 | + private static function formatPaymentStatuses(array $statuses, $weeksInRange, $currencySymbol) | |
| 1337 | + { | |
| 1338 | + $totalCents = 0; | |
| 1339 | + foreach ($statuses as $bucket) { | |
| 1340 | + $totalCents += $bucket['amount']; | |
| 1341 | + } | |
| 1342 | + | |
| 1343 | + $formatted = []; | |
| 1293 | 1344 | $paidAmount = 0; |
| 1294 | - foreach ($formattedData as $status => $data) { | |
| 1295 | - if ($status === 'paid') { | |
| 1296 | - $paidAmount = $data['amount']; | |
| 1297 | - break; | |
| 1345 | + foreach ($statuses as $status => $bucket) { | |
| 1346 | + $amount = $bucket['amount'] / 100; // Convert from cents to dollars | |
| 1347 | + $formatted[$status] = [ | |
| 1348 | + 'amount' => $amount, | |
| 1349 | + 'percentage' => $totalCents > 0 ? round(($bucket['amount'] / $totalCents) * 100, 2) : 0, | |
| 1350 | + 'count' => (int) $bucket['count'], | |
| 1351 | + ]; | |
| 1352 | + if ('paid' === $status) { | |
| 1353 | + $paidAmount = $amount; | |
| 1298 | 1354 | } |
| 1299 | 1355 | } |
| 1300 | - | |
| 1301 | - $weeklyAverage = $paidAmount / $weeksInRange; | |
| 1302 | - | |
| 1356 | + | |
| 1303 | 1357 | return [ |
| 1304 | - 'currency_symbol' => Arr::get(PaymentHelper::getCurrencyConfig($formId), 'currency_sign', '$'), | |
| 1305 | - 'payment_statuses' => $formattedData, | |
| 1306 | - 'total_amount' => $totalAmount / 100, // Convert from cents to dollars | |
| 1307 | - 'weekly_average' => round($weeklyAverage, 2) | |
| 1358 | + 'currency_symbol' => $currencySymbol, | |
| 1359 | + 'payment_statuses' => $formatted, | |
| 1360 | + 'total_amount' => $totalCents / 100, // Convert from cents to dollars | |
| 1361 | + 'weekly_average' => round($paidAmount / $weeksInRange, 2), | |
| 1308 | 1362 | ]; |
| 1309 | 1363 | } |
| 1310 | - | |
| 1364 | + | |
| 1311 | 1365 | /** |
| 1312 | 1366 | * Format payment method name for display |
| 1313 | 1367 | */ |
| 1314 | 1368 | protected static function formatPaymentMethodName($paymentMethod) |