- 일일 일보 조회/엑셀 다운로드 API 추가 - 지출 예상 내역서 조회/엑셀 다운로드 API 추가 - ReportService: 전일/당일 잔액 계산, 월별 지출 예상 집계 - Laravel Excel을 이용한 엑셀 내보내기 구현 - Swagger 문서 작성 완료
223 lines
7.6 KiB
PHP
223 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Tenants\Deposit;
|
|
use App\Models\Tenants\Purchase;
|
|
use App\Models\Tenants\Withdrawal;
|
|
use Carbon\Carbon;
|
|
|
|
class ReportService extends Service
|
|
{
|
|
/**
|
|
* 일일 일보 조회
|
|
*
|
|
* @param array $params [date: 기준일]
|
|
*/
|
|
public function dailyReport(array $params): array
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$date = Carbon::parse($params['date'] ?? now()->toDateString());
|
|
$previousDate = $date->copy()->subDay();
|
|
|
|
// 전일 잔액 계산 (기준일 전일까지의 모든 입출금 합계)
|
|
$previousBalance = $this->calculateBalanceUntilDate($tenantId, $previousDate);
|
|
|
|
// 당일 입금 합계
|
|
$dailyDeposit = Deposit::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('deposit_date', $date)
|
|
->sum('amount');
|
|
|
|
// 당일 출금 합계
|
|
$dailyWithdrawal = Withdrawal::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('withdrawal_date', $date)
|
|
->sum('amount');
|
|
|
|
// 당일 잔액
|
|
$currentBalance = $previousBalance + $dailyDeposit - $dailyWithdrawal;
|
|
|
|
// 상세 내역 (입출금 통합)
|
|
$details = $this->getDailyDetails($tenantId, $date);
|
|
|
|
return [
|
|
'date' => $date->toDateString(),
|
|
'previous_balance' => (float) $previousBalance,
|
|
'daily_deposit' => (float) $dailyDeposit,
|
|
'daily_withdrawal' => (float) $dailyWithdrawal,
|
|
'current_balance' => (float) $currentBalance,
|
|
'details' => $details,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 지출 예상 내역서 조회
|
|
*
|
|
* @param array $params [year_month: YYYY-MM 형식]
|
|
*/
|
|
public function expenseEstimate(array $params): array
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$yearMonth = $params['year_month'] ?? now()->format('Y-m');
|
|
[$year, $month] = explode('-', $yearMonth);
|
|
|
|
$startDate = Carbon::createFromDate($year, $month, 1)->startOfMonth();
|
|
$endDate = $startDate->copy()->endOfMonth();
|
|
|
|
// 미결제 매입 내역 조회 (지출 예상)
|
|
$items = Purchase::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereIn('status', ['draft', 'confirmed'])
|
|
->whereNull('withdrawal_id')
|
|
->whereBetween('purchase_date', [$startDate, $endDate])
|
|
->with(['client:id,name'])
|
|
->orderBy('purchase_date')
|
|
->get()
|
|
->map(function ($purchase) {
|
|
return [
|
|
'id' => $purchase->id,
|
|
'expected_date' => $purchase->purchase_date->toDateString(),
|
|
'item_name' => $purchase->description ?? __('message.report.purchase'),
|
|
'amount' => (float) $purchase->total_amount,
|
|
'client_name' => $purchase->client?->name ?? '',
|
|
'account_name' => '',
|
|
];
|
|
});
|
|
|
|
// 예상 지출 합계
|
|
$totalEstimate = $items->sum('amount');
|
|
|
|
// 계좌 잔액 (대표 계좌 기준, 없으면 모든 계좌 합산)
|
|
$accountBalance = $this->getAccountBalance($tenantId);
|
|
|
|
// 예상 잔액
|
|
$expectedBalance = $accountBalance - $totalEstimate;
|
|
|
|
// 월별 합계
|
|
$monthlySummary = $this->getMonthlySummary($tenantId, $startDate);
|
|
|
|
return [
|
|
'year_month' => $yearMonth,
|
|
'total_estimate' => (float) $totalEstimate,
|
|
'account_balance' => (float) $accountBalance,
|
|
'expected_balance' => (float) $expectedBalance,
|
|
'items' => $items->values()->toArray(),
|
|
'monthly_summary' => $monthlySummary,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 특정 날짜까지의 잔액 계산
|
|
*/
|
|
private function calculateBalanceUntilDate(int $tenantId, Carbon $date): float
|
|
{
|
|
$totalDeposits = Deposit::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('deposit_date', '<=', $date)
|
|
->sum('amount');
|
|
|
|
$totalWithdrawals = Withdrawal::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('withdrawal_date', '<=', $date)
|
|
->sum('amount');
|
|
|
|
return (float) ($totalDeposits - $totalWithdrawals);
|
|
}
|
|
|
|
/**
|
|
* 일일 상세 내역 조회 (입출금 통합)
|
|
*/
|
|
private function getDailyDetails(int $tenantId, Carbon $date): array
|
|
{
|
|
// 입금 내역
|
|
$deposits = Deposit::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('deposit_date', $date)
|
|
->with(['client:id,name'])
|
|
->get()
|
|
->map(function ($deposit) {
|
|
return [
|
|
'type' => 'deposit',
|
|
'type_label' => __('message.report.deposit'),
|
|
'client_name' => $deposit->client?->name ?? $deposit->client_name ?? '',
|
|
'account_code' => $deposit->account_code ?? '',
|
|
'deposit_amount' => (float) $deposit->amount,
|
|
'withdrawal_amount' => 0,
|
|
'description' => $deposit->description ?? '',
|
|
'payment_method' => $deposit->payment_method,
|
|
];
|
|
});
|
|
|
|
// 출금 내역
|
|
$withdrawals = Withdrawal::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereDate('withdrawal_date', $date)
|
|
->with(['client:id,name'])
|
|
->get()
|
|
->map(function ($withdrawal) {
|
|
return [
|
|
'type' => 'withdrawal',
|
|
'type_label' => __('message.report.withdrawal'),
|
|
'client_name' => $withdrawal->client?->name ?? $withdrawal->client_name ?? '',
|
|
'account_code' => $withdrawal->account_code ?? '',
|
|
'deposit_amount' => 0,
|
|
'withdrawal_amount' => (float) $withdrawal->amount,
|
|
'description' => $withdrawal->description ?? '',
|
|
'payment_method' => $withdrawal->payment_method,
|
|
];
|
|
});
|
|
|
|
return $deposits->concat($withdrawals)->values()->toArray();
|
|
}
|
|
|
|
/**
|
|
* 계좌 잔액 조회
|
|
* 입출금 내역 기반으로 계산
|
|
*/
|
|
private function getAccountBalance(int $tenantId): float
|
|
{
|
|
return $this->calculateBalanceUntilDate($tenantId, Carbon::now());
|
|
}
|
|
|
|
/**
|
|
* 월별 지출 예상 합계
|
|
*/
|
|
private function getMonthlySummary(int $tenantId, Carbon $baseDate): array
|
|
{
|
|
$summary = [];
|
|
|
|
// 기준월부터 3개월간 집계
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$targetDate = $baseDate->copy()->addMonths($i);
|
|
$startOfMonth = $targetDate->copy()->startOfMonth();
|
|
$endOfMonth = $targetDate->copy()->endOfMonth();
|
|
|
|
$total = Purchase::query()
|
|
->where('tenant_id', $tenantId)
|
|
->whereIn('status', ['draft', 'confirmed'])
|
|
->whereNull('withdrawal_id')
|
|
->whereBetween('purchase_date', [$startOfMonth, $endOfMonth])
|
|
->sum('total_amount');
|
|
|
|
$summary[] = [
|
|
'month' => $targetDate->format('Y/m'),
|
|
'total' => (float) $total,
|
|
];
|
|
}
|
|
|
|
// 전체 합계
|
|
$grandTotal = collect($summary)->sum('total');
|
|
|
|
// 계좌 잔액
|
|
$accountBalance = $this->getAccountBalance($tenantId);
|
|
|
|
return [
|
|
'by_month' => $summary,
|
|
'total_expense' => $grandTotal,
|
|
'account_balance' => $accountBalance,
|
|
'final_difference' => $accountBalance - $grandTotal,
|
|
];
|
|
}
|
|
}
|