235 lines
9.2 KiB
PHP
235 lines
9.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
function qqBuildPayableInterestData(array $rowsAll, array $financeAll, array $planAll): array
|
|
{
|
|
$todayQ = date('Y-m-d');
|
|
$monthStart = date('Y-m-01');
|
|
$monthEnd = date('Y-m-t');
|
|
$archiveMap = [];
|
|
foreach ($rowsAll as $row) {
|
|
$archiveMap[(int)($row['id'] ?? 0)] = $row;
|
|
}
|
|
|
|
$bankActive = array_values(array_filter($rowsAll, function ($a) use ($financeAll) {
|
|
$kind = normalizeArchiveKind((string)($a['archive_kind'] ?? ''));
|
|
if ($kind !== ARCHIVE_KIND_BANK_LOAN) {
|
|
return false;
|
|
}
|
|
return archiveStatusIsOngoingAggregate((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id']));
|
|
}));
|
|
|
|
$instStats = [];
|
|
foreach ($bankActive as $a) {
|
|
$aid = (int)$a['id'];
|
|
$inst = (string)($a['loan_institution'] ?? '');
|
|
if ($inst === '') {
|
|
$inst = '未填写机构';
|
|
}
|
|
if (!isset($instStats[$inst])) {
|
|
$instStats[$inst] = ['loan_count' => 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<array>, 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),
|
|
],
|
|
];
|
|
}
|