feat:법인카드 요약카드 개선 (결제일/사용금액/선불결제)
- 요약카드 4개→6개 확장 (등록카드, 총한도, 매월결제일, 사용금액, 선불결제, 잔여한도) - 매월결제일: 휴일/주말 시 다음 영업일로 자동 조정 표시 - 사용금액: barobill_card_transactions 기반 청구기간 실거래 합산 - 선불결제: 수정 모달로 테넌트 단위 월별 금액 관리 - 잔여한도: (총한도 - 사용금액 + 선불결제) 계산 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,12 @@
|
||||
namespace App\Http\Controllers\Finance;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Barobill\CardTransaction;
|
||||
use App\Models\Barobill\CardTransactionHide;
|
||||
use App\Models\Finance\CorporateCard;
|
||||
use App\Models\Finance\CorporateCardPrepayment;
|
||||
use App\Models\System\Holiday;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -179,4 +184,185 @@ public function destroy(int $id): JsonResponse
|
||||
'message' => '카드가 영구 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 요약 데이터 (결제일, 사용금액, 선불결제)
|
||||
*/
|
||||
public function summary(): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$now = Carbon::now();
|
||||
|
||||
// 활성 카드 조회
|
||||
$activeCards = CorporateCard::forTenant($tenantId)->active()->get();
|
||||
|
||||
if ($activeCards->isEmpty()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'paymentDate' => null,
|
||||
'paymentDay' => 15,
|
||||
'originalDate' => null,
|
||||
'isAdjusted' => false,
|
||||
'billingPeriod' => null,
|
||||
'billingUsage' => 0,
|
||||
'prepaidAmount' => 0,
|
||||
'prepaidMemo' => '',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// 대표 결제일 (첫 번째 활성 신용카드 기준)
|
||||
$creditCard = $activeCards->firstWhere('card_type', 'credit');
|
||||
$paymentDay = $creditCard ? $creditCard->payment_day : 15;
|
||||
|
||||
// 휴일 조정 결제일 계산
|
||||
$originalDate = $this->createPaymentDate($now->year, $now->month, $paymentDay);
|
||||
$adjustedDate = $this->getAdjustedPaymentDate($tenantId, $now->year, $now->month, $paymentDay);
|
||||
$isAdjusted = !$originalDate->isSameDay($adjustedDate);
|
||||
|
||||
// 청구기간: 전월 1일 ~ 당월 결제일(조정후)
|
||||
$billingStart = $now->copy()->subMonth()->startOfMonth();
|
||||
$billingEnd = $adjustedDate->copy();
|
||||
|
||||
// 카드번호 목록
|
||||
$cardNumbers = $activeCards->pluck('card_number')->toArray();
|
||||
|
||||
// 사용금액 계산
|
||||
$billingUsage = $this->calculateBillingUsage(
|
||||
$tenantId,
|
||||
$billingStart->format('Y-m-d'),
|
||||
$billingEnd->format('Y-m-d'),
|
||||
$cardNumbers
|
||||
);
|
||||
|
||||
// 선불결제 금액
|
||||
$yearMonth = $now->format('Y-m');
|
||||
$prepayment = CorporateCardPrepayment::getOrCreate($tenantId, $yearMonth);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'paymentDate' => $adjustedDate->format('Y-m-d'),
|
||||
'paymentDay' => $paymentDay,
|
||||
'originalDate' => $originalDate->format('Y-m-d'),
|
||||
'isAdjusted' => $isAdjusted,
|
||||
'billingPeriod' => [
|
||||
'start' => $billingStart->format('Y-m-d'),
|
||||
'end' => $billingEnd->format('Y-m-d'),
|
||||
],
|
||||
'billingUsage' => $billingUsage,
|
||||
'prepaidAmount' => (int) $prepayment->amount,
|
||||
'prepaidMemo' => $prepayment->memo ?? '',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 선불결제 금액 수정
|
||||
*/
|
||||
public function updatePrepayment(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'amount' => 'required|integer|min:0',
|
||||
'memo' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$yearMonth = Carbon::now()->format('Y-m');
|
||||
|
||||
$prepayment = CorporateCardPrepayment::updateOrCreate(
|
||||
['tenant_id' => $tenantId, 'year_month' => $yearMonth],
|
||||
['amount' => $request->input('amount'), 'memo' => $request->input('memo')]
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '선불결제 금액이 저장되었습니다.',
|
||||
'data' => [
|
||||
'amount' => (int) $prepayment->amount,
|
||||
'memo' => $prepayment->memo ?? '',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 결제일 날짜 생성 (월 말일 초과 방지)
|
||||
*/
|
||||
private function createPaymentDate(int $year, int $month, int $day): Carbon
|
||||
{
|
||||
$maxDay = Carbon::create($year, $month)->daysInMonth;
|
||||
return Carbon::create($year, $month, min($day, $maxDay));
|
||||
}
|
||||
|
||||
/**
|
||||
* 휴일/주말 조정된 결제일 계산
|
||||
*/
|
||||
private function getAdjustedPaymentDate(int $tenantId, int $year, int $month, int $paymentDay): Carbon
|
||||
{
|
||||
$date = $this->createPaymentDate($year, $month, $paymentDay);
|
||||
|
||||
// 해당 기간의 휴일 조회
|
||||
$holidays = Holiday::forTenant($tenantId)
|
||||
->where('start_date', '<=', $date->copy()->addDays(10)->format('Y-m-d'))
|
||||
->where('end_date', '>=', $date->format('Y-m-d'))
|
||||
->get();
|
||||
|
||||
$holidayDates = [];
|
||||
foreach ($holidays as $h) {
|
||||
$current = $h->start_date->copy();
|
||||
while ($current <= $h->end_date) {
|
||||
$holidayDates[] = $current->format('Y-m-d');
|
||||
$current->addDay();
|
||||
}
|
||||
}
|
||||
|
||||
// 토/일/공휴일이면 다음 영업일로 이동
|
||||
while ($date->isWeekend() || in_array($date->format('Y-m-d'), $holidayDates)) {
|
||||
$date->addDay();
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 청구기간 사용금액 계산 (바로빌 카드거래 합산)
|
||||
*/
|
||||
private function calculateBillingUsage(int $tenantId, string $startDate, string $endDate, array $cardNumbers): int
|
||||
{
|
||||
// 카드번호 정규화 (하이픈 제거)
|
||||
$normalizedNums = array_map(fn($num) => str_replace('-', '', $num), $cardNumbers);
|
||||
|
||||
if (empty($normalizedNums)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// use_date는 YYYYMMDD 형식
|
||||
$startYmd = str_replace('-', '', $startDate);
|
||||
$endYmd = str_replace('-', '', $endDate);
|
||||
|
||||
// 숨긴 거래 키 조회
|
||||
$hiddenKeys = CardTransactionHide::getHiddenKeys($tenantId, $startYmd, $endYmd);
|
||||
|
||||
// 바로빌 카드거래 조회
|
||||
$transactions = CardTransaction::where('tenant_id', $tenantId)
|
||||
->whereIn('card_num', $normalizedNums)
|
||||
->whereBetween('use_date', [$startYmd, $endYmd])
|
||||
->get();
|
||||
|
||||
$total = 0;
|
||||
foreach ($transactions as $tx) {
|
||||
if ($hiddenKeys->contains($tx->unique_key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tx->approval_type === '1') {
|
||||
$total += (int) $tx->approval_amount; // 승인
|
||||
} elseif ($tx->approval_type === '2') {
|
||||
$total -= (int) $tx->approval_amount; // 취소
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
}
|
||||
|
||||
25
app/Models/Finance/CorporateCardPrepayment.php
Normal file
25
app/Models/Finance/CorporateCardPrepayment.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Finance;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CorporateCardPrepayment extends Model
|
||||
{
|
||||
protected $table = 'corporate_card_prepayments';
|
||||
|
||||
protected $fillable = ['tenant_id', 'year_month', 'amount', 'memo'];
|
||||
|
||||
public function scopeForTenant($query, int $tenantId)
|
||||
{
|
||||
return $query->where('tenant_id', $tenantId);
|
||||
}
|
||||
|
||||
public static function getOrCreate(int $tenantId, string $yearMonth): self
|
||||
{
|
||||
return static::firstOrCreate(
|
||||
['tenant_id' => $tenantId, 'year_month' => $yearMonth],
|
||||
['amount' => 0, 'memo' => null]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,11 @@ function CorporateCardsManagement() {
|
||||
const [modalMode, setModalMode] = useState('add'); // 'add' or 'edit'
|
||||
const [editingCard, setEditingCard] = useState(null);
|
||||
|
||||
// 요약 데이터
|
||||
const [summaryData, setSummaryData] = useState(null);
|
||||
const [showPrepaymentModal, setShowPrepaymentModal] = useState(false);
|
||||
const [prepaymentForm, setPrepaymentForm] = useState({ amount: '', memo: '' });
|
||||
|
||||
// CSRF 토큰
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||
|
||||
@@ -90,18 +95,73 @@ function CorporateCardsManagement() {
|
||||
const fetchCards = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch('/finance/corporate-cards/list');
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
setCards(result.data);
|
||||
const [cardsRes, summaryRes] = await Promise.all([
|
||||
fetch('/finance/corporate-cards/list'),
|
||||
fetch('/finance/corporate-cards/summary'),
|
||||
]);
|
||||
const cardsResult = await cardsRes.json();
|
||||
const summaryResult = await summaryRes.json();
|
||||
if (cardsResult.success) {
|
||||
setCards(cardsResult.data);
|
||||
}
|
||||
if (summaryResult.success) {
|
||||
setSummaryData(summaryResult.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('카드 목록 로드 실패:', error);
|
||||
console.error('데이터 로드 실패:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 선불결제 저장
|
||||
const handleSavePrepayment = async () => {
|
||||
try {
|
||||
const response = await fetch('/finance/corporate-cards/prepayment', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
amount: parseInt(parseInputCurrency(prepaymentForm.amount)) || 0,
|
||||
memo: prepaymentForm.memo,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
setSummaryData(prev => ({
|
||||
...prev,
|
||||
prepaidAmount: result.data.amount,
|
||||
prepaidMemo: result.data.memo,
|
||||
}));
|
||||
setShowPrepaymentModal(false);
|
||||
} else {
|
||||
alert(result.message || '저장에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('선불결제 저장 실패:', error);
|
||||
alert('저장 중 오류가 발생했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
// 선불결제 수정 모달 열기
|
||||
const openPrepaymentModal = () => {
|
||||
setPrepaymentForm({
|
||||
amount: summaryData?.prepaidAmount ? String(summaryData.prepaidAmount) : '',
|
||||
memo: summaryData?.prepaidMemo || '',
|
||||
});
|
||||
setShowPrepaymentModal(true);
|
||||
};
|
||||
|
||||
// 날짜 포맷 (M/D(요일))
|
||||
const formatPaymentDate = (dateStr) => {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
const days = ['일', '월', '화', '수', '목', '금', '토'];
|
||||
return `${date.getMonth() + 1}/${date.getDate()}(${days[date.getDay()]})`;
|
||||
};
|
||||
|
||||
// 테스트용 임시 데이터 생성
|
||||
const generateTestData = async () => {
|
||||
const companies = ['삼성카드', '현대카드', '국민카드', '신한카드', '롯데카드'];
|
||||
@@ -377,38 +437,83 @@ className="p-1.5 bg-amber-500 hover:bg-amber-600 text-white rounded transition-c
|
||||
</header>
|
||||
|
||||
{/* 요약 카드 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-6 gap-4 mb-6">
|
||||
{/* 1. 등록 카드 */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-gray-500">등록 카드</span>
|
||||
<CreditCard className="w-5 h-5 text-gray-400" />
|
||||
<span className="text-xs text-gray-500">등록 카드</span>
|
||||
<CreditCard className="w-4 h-4 text-gray-400" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{cards.length}장</p>
|
||||
<p className="text-xl font-bold text-gray-900">{cards.length}장</p>
|
||||
<p className="text-xs text-gray-400 mt-1">활성 {cards.filter(c => c.status === 'active').length}장</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
{/* 2. 총 한도 */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-gray-500">총 한도</span>
|
||||
<DollarSign className="w-5 h-5 text-gray-400" />
|
||||
<span className="text-xs text-gray-500">총 한도</span>
|
||||
<DollarSign className="w-4 h-4 text-gray-400" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{formatCurrency(totalLimit)}원</p>
|
||||
<p className="text-xl font-bold text-gray-900">{formatCurrency(totalLimit)}원</p>
|
||||
<p className="text-xs text-gray-400 mt-1">신용카드 기준</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-violet-200 p-6 bg-violet-50/30">
|
||||
{/* 3. 매월결제일 */}
|
||||
<div className="bg-white rounded-xl border border-blue-200 p-4 bg-blue-50/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-violet-700">이번 달 사용</span>
|
||||
<CreditCard className="w-5 h-5 text-violet-500" />
|
||||
<span className="text-xs text-blue-700">매월결제일</span>
|
||||
<Calendar className="w-4 h-4 text-blue-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-violet-600">{formatCurrency(totalUsage)}원</p>
|
||||
<p className="text-xs text-violet-500 mt-1">{totalLimit > 0 ? getUsagePercent(totalUsage, totalLimit) : 0}% 사용</p>
|
||||
<p className="text-xl font-bold text-blue-600">
|
||||
{summaryData?.paymentDate ? formatPaymentDate(summaryData.paymentDate) : '-'}
|
||||
</p>
|
||||
<p className="text-xs text-blue-400 mt-1">
|
||||
{summaryData?.isAdjusted
|
||||
? `${summaryData.paymentDay}일→휴일조정`
|
||||
: summaryData?.paymentDay ? `매월 ${summaryData.paymentDay}일` : '-'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
{/* 4. 사용금액 (청구기간) */}
|
||||
<div className="bg-white rounded-xl border border-violet-200 p-4 bg-violet-50/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-gray-500">잔여 한도</span>
|
||||
<DollarSign className="w-5 h-5 text-emerald-500" />
|
||||
<span className="text-xs text-violet-700">사용금액</span>
|
||||
<CreditCard className="w-4 h-4 text-violet-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-emerald-600">{formatCurrency(totalLimit - totalUsage)}원</p>
|
||||
<p className="text-xs text-gray-400 mt-1">사용 가능</p>
|
||||
<p className="text-xl font-bold text-violet-600">
|
||||
{summaryData ? formatCurrency(summaryData.billingUsage) : '0'}원
|
||||
</p>
|
||||
<p className="text-xs text-violet-400 mt-1">
|
||||
{summaryData?.billingPeriod
|
||||
? `${summaryData.billingPeriod.start.slice(5).replace('-','/')}~${summaryData.billingPeriod.end.slice(5).replace('-','/')} 기준`
|
||||
: '-'}
|
||||
</p>
|
||||
</div>
|
||||
{/* 5. 선불결제 */}
|
||||
<div className="bg-white rounded-xl border border-amber-200 p-4 bg-amber-50/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs text-amber-700">선불결제</span>
|
||||
<button
|
||||
onClick={openPrepaymentModal}
|
||||
className="text-xs px-1.5 py-0.5 bg-amber-100 hover:bg-amber-200 text-amber-700 rounded transition-colors"
|
||||
>
|
||||
수정
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xl font-bold text-amber-600">
|
||||
{summaryData ? formatCurrency(summaryData.prepaidAmount) : '0'}원
|
||||
</p>
|
||||
<p className="text-xs text-amber-400 mt-1 truncate" title={summaryData?.prepaidMemo || ''}>
|
||||
{summaryData?.prepaidMemo || '미입력'}
|
||||
</p>
|
||||
</div>
|
||||
{/* 6. 잔여 한도 */}
|
||||
<div className="bg-white rounded-xl border border-emerald-200 p-4 bg-emerald-50/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs text-emerald-700">잔여 한도</span>
|
||||
<DollarSign className="w-4 h-4 text-emerald-500" />
|
||||
</div>
|
||||
<p className="text-xl font-bold text-emerald-600">
|
||||
{formatCurrency(totalLimit - (summaryData?.billingUsage || 0) + (summaryData?.prepaidAmount || 0))}원
|
||||
</p>
|
||||
<p className="text-xs text-emerald-400 mt-1">한도-사용+선결제</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -749,6 +854,59 @@ className="flex-1 px-4 py-2 bg-violet-600 hover:bg-violet-700 text-white rounded
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 선불결제 수정 모달 */}
|
||||
{showPrepaymentModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-xl p-6 w-full max-w-sm mx-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-bold text-gray-900">선불결제 금액 수정</h3>
|
||||
<button
|
||||
onClick={() => setShowPrepaymentModal(false)}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg"
|
||||
>
|
||||
<X className="w-5 h-5 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">선불결제 금액</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formatInputCurrency(prepaymentForm.amount)}
|
||||
onChange={(e) => setPrepaymentForm(prev => ({ ...prev, amount: parseInputCurrency(e.target.value) }))}
|
||||
placeholder="0"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-amber-500 text-right text-lg font-bold"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">메모 (선택)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={prepaymentForm.memo}
|
||||
onChange={(e) => setPrepaymentForm(prev => ({ ...prev, memo: e.target.value }))}
|
||||
placeholder="예: 2월 선결제"
|
||||
maxLength={200}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-amber-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-3 mt-6">
|
||||
<button
|
||||
onClick={() => setShowPrepaymentModal(false)}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSavePrepayment}
|
||||
className="flex-1 px-4 py-2 bg-amber-500 hover:bg-amber-600 text-white rounded-lg"
|
||||
>
|
||||
저장
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -886,6 +886,8 @@
|
||||
// 법인카드 API
|
||||
Route::prefix('corporate-cards')->name('corporate-cards.')->group(function () {
|
||||
Route::get('/list', [\App\Http\Controllers\Finance\CorporateCardController::class, 'index'])->name('list');
|
||||
Route::get('/summary', [\App\Http\Controllers\Finance\CorporateCardController::class, 'summary'])->name('summary');
|
||||
Route::post('/prepayment', [\App\Http\Controllers\Finance\CorporateCardController::class, 'updatePrepayment'])->name('prepayment');
|
||||
Route::post('/store', [\App\Http\Controllers\Finance\CorporateCardController::class, 'store'])->name('store');
|
||||
Route::put('/{id}', [\App\Http\Controllers\Finance\CorporateCardController::class, 'update'])->name('update');
|
||||
Route::post('/{id}/deactivate', [\App\Http\Controllers\Finance\CorporateCardController::class, 'deactivate'])->name('deactivate');
|
||||
|
||||
Reference in New Issue
Block a user