0, 'current_interest' => 0.0, 'settlement_interest' => 0.0, 'settlement_dates' => []]; } $cfg = getArchiveFinanceConfig($financeAll, $aid); $plans = (array)((getArchiveRepaymentPlan($planAll, $aid))['plans'] ?? []); $mode = (string)($cfg['interest_mode'] ?? 'monthly'); $nextSettle = $mode === 'bullet' ? ((string)($a['end_date'] ?? '') >= $todayQ ? (string)$a['end_date'] : null) : nextSettlementDate($todayQ, $mode, (int)($cfg['settlement_day'] ?? 21)); $instStats[$inst]['loan_count']++; $instStats[$inst]['current_interest'] += calcCurrentInterest($a, $cfg, $plans); if (!empty($nextSettle)) { $instStats[$inst]['settlement_interest'] += calcSettlementPeriodInterest($a, $cfg, $plans, $todayQ); $instStats[$inst]['settlement_dates'][(string)$nextSettle] = true; } } ksort($instStats); $instRows = []; foreach ($instStats as $inst => $s) { $instRows[] = [ 'loan_institution' => $inst, 'loan_count' => (int)$s['loan_count'], 'current_interest' => round((float)$s['current_interest'], 2), 'settlement_interest' => round((float)$s['settlement_interest'], 2), 'settlement_date' => implode(' / ', array_keys((array)$s['settlement_dates'])), ]; } $planRows = []; $planTotal = 0.0; foreach ($planAll as $aid => $cfg) { if (empty($cfg['enabled']) || empty($cfg['plans']) || !is_array($cfg['plans'])) { continue; } $aidInt = (int)$aid; $arc = $archiveMap[$aidInt] ?? null; if (!$arc) { continue; } foreach ((array)$cfg['plans'] as $p) { $status = (string)($p['status'] ?? 'pending'); $due = (string)($p['due_date'] ?? ''); if ($due === '' || $due < $monthStart || $due > $monthEnd || $status === 'paid') { continue; } $inst = (string)($arc['loan_institution'] ?? ''); if ($inst === '') { $inst = '未填写机构'; } $amt = (float)($p['amount'] ?? 0); $planRows[] = [ 'loan_institution' => $inst, 'archive_name' => (string)($arc['name'] ?? ''), 'borrower' => (string)($arc['borrower'] ?? ''), 'due_date' => $due, 'amount' => $amt, 'status' => $status, ]; $planTotal += $amt; } } usort($planRows, function ($a, $b) { $cmp = strcmp((string)$a['due_date'], (string)$b['due_date']); if ($cmp !== 0) { return $cmp; } return strcmp((string)$a['loan_institution'], (string)$b['loan_institution']); }); return ['instRows' => $instRows, 'planRows' => $planRows, 'planTotal' => round($planTotal, 2)]; } /** * @param string $scope all_bank|all_guar|bank_inst|guar_inst * @param string|null $inst 与档案「贷款机构/债权人」完全一致;后两项 scope 必填 * @return array{count:int,sum_credit:float,sum_remaining:float} */ function qqBuildTotalsQuery(array $rowsAll, array $financeAll, string $scope, ?string $inst): array { if (($scope === 'bank_inst' || $scope === 'guar_inst') && ($inst === null || trim($inst) === '')) { return ['count' => 0, 'sum_credit' => 0.0, 'sum_remaining' => 0.0]; } $instNorm = $inst !== null ? trim($inst) : ''; $filtered = array_filter($rowsAll, function ($a) use ($financeAll, $scope, $instNorm): bool { if (!archiveStatusIsOngoingAggregate((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id']))) { return false; } $kind = normalizeArchiveKind((string)($a['archive_kind'] ?? '')); if ($scope === 'all_bank') { return $kind === ARCHIVE_KIND_BANK_LOAN; } if ($scope === 'all_guar') { return $kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE; } $rowInst = (string)($a['loan_institution'] ?? ''); if ($scope === 'bank_inst') { return $kind === ARCHIVE_KIND_BANK_LOAN && $rowInst === $instNorm; } if ($scope === 'guar_inst') { return $kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE && $rowInst === $instNorm; } return false; }); $sumCredit = 0.0; $sumRemaining = 0.0; $count = 0; foreach ($filtered as $a) { $count++; if (isset($a['credit_limit']) && $a['credit_limit'] !== null && $a['credit_limit'] !== '') { $sumCredit += (float)$a['credit_limit']; } if (isset($a['remaining_limit']) && $a['remaining_limit'] !== null && $a['remaining_limit'] !== '') { $sumRemaining += (float)$a['remaining_limit']; } } return [ 'count' => $count, 'sum_credit' => round($sumCredit, 2), 'sum_remaining' => round($sumRemaining, 2), ]; } function qqSearchGuarantorArchives(array $rows, string $kw): array { return array_values(array_filter($rows, function ($r) use ($kw) { if ($kw === '') { return false; } return mb_stripos((string)($r['guarantor'] ?? ''), $kw) !== false; })); } /** * 历史还款:分期计划已还本金明细 + 已完成档案授信合计(按类型) * * @return array{plan_rows: list, plan_paid_total: float, completed_bank: array{count:int,sum_credit:float}, completed_guar: array{count:int,sum_credit:float}} */ function qqBuildRepaymentHistory(array $rowsAll, array $planAll, array $financeAll): array { $map = []; foreach ($rowsAll as $r) { $map[(int)($r['id'] ?? 0)] = $r; } $planRows = []; $planPaidTotal = 0.0; foreach ($planAll as $aidStr => $cfg) { $aid = (int)$aidStr; if (empty($cfg['enabled']) || empty($cfg['plans']) || !is_array($cfg['plans'])) { continue; } $arc = $map[$aid] ?? null; foreach ($cfg['plans'] as $p) { if ((string)($p['status'] ?? '') !== 'paid') { continue; } $amt = (float)($p['amount'] ?? 0); $planPaidTotal += $amt; $planRows[] = [ 'archive_id' => $aid, 'archive_name' => $arc ? (string)($arc['name'] ?? '') : '(档案不存在)', 'loan_institution' => $arc ? (string)($arc['loan_institution'] ?? '') : '', 'borrower' => $arc ? (string)($arc['borrower'] ?? '') : '', 'due_date' => (string)($p['due_date'] ?? ''), 'paid_at' => (string)($p['paid_at'] ?? ''), 'amount' => $amt, ]; } } usort($planRows, static function ($a, $b): int { $c = strcmp((string)($b['paid_at'] ?? ''), (string)($a['paid_at'] ?? '')); if ($c !== 0) { return $c; } return ((int)($b['archive_id'] ?? 0)) <=> ((int)($a['archive_id'] ?? 0)); }); $completedBank = ['count' => 0, 'sum_credit' => 0.0]; $completedGuar = ['count' => 0, 'sum_credit' => 0.0]; foreach ($rowsAll as $a) { $st = archiveStatusWithConfig((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id'])); if ($st !== '已完成') { continue; } $kind = normalizeArchiveKind((string)($a['archive_kind'] ?? '')); $credit = $a['credit_limit'] !== null && $a['credit_limit'] !== '' ? (float)$a['credit_limit'] : 0.0; if ($kind === ARCHIVE_KIND_BANK_LOAN) { $completedBank['count']++; $completedBank['sum_credit'] += $credit; } elseif ($kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE) { $completedGuar['count']++; $completedGuar['sum_credit'] += $credit; } } return [ 'plan_rows' => $planRows, 'plan_paid_total' => round($planPaidTotal, 2), 'completed_bank' => [ 'count' => (int)$completedBank['count'], 'sum_credit' => round((float)$completedBank['sum_credit'], 2), ], 'completed_guar' => [ 'count' => (int)$completedGuar['count'], 'sum_credit' => round((float)$completedGuar['sum_credit'], 2), ], ]; }