feat: 더미 데이터 시더 추가 및 회계 관련 마이그레이션
- DummyDataSeeder 및 개별 시더 추가 (Client, BadDebt, Deposit 등) - payments.paid_at nullable 마이그레이션 - subscriptions 취소 컬럼 추가 - clients 테이블 bad_debt 컬럼 제거 - PlanController, ClientService 수정 - 불필요한 claudedocs, flow-test 파일 정리
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* 결제 대기 상태(pending)에서는 paid_at이 null이어야 하므로 nullable로 변경
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
$table->datetime('paid_at')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
$table->datetime('paid_at')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* Subscription 취소 시 필요한 컬럼 추가
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('subscriptions', function (Blueprint $table) {
|
||||
$table->timestamp('cancelled_at')->nullable()->after('ended_at')->comment('취소 일시');
|
||||
$table->string('cancel_reason', 500)->nullable()->after('cancelled_at')->comment('취소 사유');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('subscriptions', function (Blueprint $table) {
|
||||
$table->dropColumn(['cancelled_at', 'cancel_reason']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* clients 테이블에서 bad_debt 관련 컬럼 제거
|
||||
*
|
||||
* 악성채권 정보는 이제 bad_debts 테이블에서 관리됨
|
||||
* clients.bad_debts() 관계를 통해 조회
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('clients', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'bad_debt',
|
||||
'bad_debt_amount',
|
||||
'bad_debt_receive_date',
|
||||
'bad_debt_end_date',
|
||||
'bad_debt_progress',
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('clients', function (Blueprint $table) {
|
||||
$table->boolean('bad_debt')->default(false)->after('tax_end_date')->comment('악성채권 여부');
|
||||
$table->decimal('bad_debt_amount', 15, 2)->nullable()->after('bad_debt')->comment('악성채권 금액');
|
||||
$table->date('bad_debt_receive_date')->nullable()->after('bad_debt_amount')->comment('악성채권 발생일');
|
||||
$table->date('bad_debt_end_date')->nullable()->after('bad_debt_receive_date')->comment('악성채권 종료일');
|
||||
$table->string('bad_debt_progress', 20)->nullable()->after('bad_debt_end_date')->comment('악성채권 진행상황');
|
||||
});
|
||||
}
|
||||
};
|
||||
100
database/seeders/Dummy/DummyBadDebtSeeder.php
Normal file
100
database/seeders/Dummy/DummyBadDebtSeeder.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\BadDebts\BadDebt;
|
||||
use App\Models\Orders\Client;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyBadDebtSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// SALES 또는 BOTH 타입의 거래처 조회
|
||||
$clients = Client::where('tenant_id', $tenantId)
|
||||
->whereIn('client_type', ['SALES', 'BOTH'])
|
||||
->get();
|
||||
|
||||
if ($clients->isEmpty()) {
|
||||
$this->command->warn(' ⚠ clients 데이터가 없습니다. DummyClientSeeder를 먼저 실행하세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 상태별 분포
|
||||
$statuses = [
|
||||
'collecting' => 6, // 추심중
|
||||
'legal_action' => 4, // 법적조치
|
||||
'recovered' => 5, // 회수완료
|
||||
'bad_debt' => 3, // 대손처리
|
||||
];
|
||||
|
||||
// 금액 범위 (채권 금액)
|
||||
$amounts = [
|
||||
'small' => [500000, 3000000], // 50만~300만
|
||||
'medium' => [3000000, 15000000], // 300만~1500만
|
||||
'large' => [15000000, 50000000], // 1500만~5000만
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
$year = 2025;
|
||||
$clientIndex = 0;
|
||||
|
||||
foreach ($statuses as $status => $qty) {
|
||||
for ($i = 0; $i < $qty; $i++) {
|
||||
$client = $clients[$clientIndex % $clients->count()];
|
||||
$clientIndex++;
|
||||
|
||||
// 발생월 (1~10월 사이 랜덤)
|
||||
$month = rand(1, 10);
|
||||
$day = rand(1, 28);
|
||||
$occurredAt = sprintf('%04d-%02d-%02d', $year, $month, $day);
|
||||
|
||||
// 연체일수 계산 (발생일로부터 현재까지)
|
||||
$occurredDate = new \DateTime($occurredAt);
|
||||
$now = new \DateTime('2025-12-23');
|
||||
$overdueDays = $occurredDate->diff($now)->days;
|
||||
|
||||
// 금액 결정 (분포에 따라)
|
||||
$rand = rand(1, 100);
|
||||
if ($rand <= 40) {
|
||||
$amount = rand($amounts['small'][0], $amounts['small'][1]);
|
||||
} elseif ($rand <= 80) {
|
||||
$amount = rand($amounts['medium'][0], $amounts['medium'][1]);
|
||||
} else {
|
||||
$amount = rand($amounts['large'][0], $amounts['large'][1]);
|
||||
}
|
||||
|
||||
// 종료일 (회수완료/대손처리인 경우만)
|
||||
$closedAt = null;
|
||||
if (in_array($status, ['recovered', 'bad_debt'])) {
|
||||
$closedMonth = rand($month + 1, 12);
|
||||
$closedDay = rand(1, 28);
|
||||
$closedAt = sprintf('%04d-%02d-%02d', $year, $closedMonth, $closedDay);
|
||||
}
|
||||
|
||||
BadDebt::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'client_id' => $client->id,
|
||||
'debt_amount' => $amount,
|
||||
'status' => $status,
|
||||
'overdue_days' => $overdueDays,
|
||||
'assigned_user_id' => $userId,
|
||||
'occurred_at' => $occurredAt,
|
||||
'closed_at' => $closedAt,
|
||||
'is_active' => true,
|
||||
'options' => null,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ bad_debts: ' . $count . '건 생성');
|
||||
$this->command->info(' - collecting: 6건, legal_action: 4건, recovered: 5건, bad_debt: 3건');
|
||||
}
|
||||
}
|
||||
40
database/seeders/Dummy/DummyBankAccountSeeder.php
Normal file
40
database/seeders/Dummy/DummyBankAccountSeeder.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Tenants\BankAccount;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyBankAccountSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
$accounts = [
|
||||
['bank_code' => '004', 'bank_name' => 'KB국민은행', 'account_number' => '123-45-6789012', 'account_holder' => '프론트테스트', 'account_name' => '운영계좌', 'is_primary' => true],
|
||||
['bank_code' => '088', 'bank_name' => '신한은행', 'account_number' => '110-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '급여계좌', 'is_primary' => false],
|
||||
['bank_code' => '020', 'bank_name' => '우리은행', 'account_number' => '1002-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '예비계좌', 'is_primary' => false],
|
||||
['bank_code' => '081', 'bank_name' => '하나은행', 'account_number' => '123-456789-12345','account_holder' => '프론트테스트', 'account_name' => '법인카드', 'is_primary' => false],
|
||||
['bank_code' => '011', 'bank_name' => 'NH농협은행', 'account_number' => '351-1234-5678-12','account_holder' => '프론트테스트', 'account_name' => '비상금', 'is_primary' => false],
|
||||
];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
BankAccount::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'bank_code' => $account['bank_code'],
|
||||
'bank_name' => $account['bank_name'],
|
||||
'account_number' => $account['account_number'],
|
||||
'account_holder' => $account['account_holder'],
|
||||
'account_name' => $account['account_name'],
|
||||
'status' => 'active',
|
||||
'is_primary' => $account['is_primary'],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ bank_accounts: ' . count($accounts) . '건 생성');
|
||||
}
|
||||
}
|
||||
37
database/seeders/Dummy/DummyClientGroupSeeder.php
Normal file
37
database/seeders/Dummy/DummyClientGroupSeeder.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\ClientGroup;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyClientGroupSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
$groups = [
|
||||
['group_code' => 'VIP', 'group_name' => 'VIP 고객', 'price_rate' => 0.95],
|
||||
['group_code' => 'GOLD', 'group_name' => '골드 고객', 'price_rate' => 0.97],
|
||||
['group_code' => 'SILVER', 'group_name' => '실버 고객', 'price_rate' => 0.98],
|
||||
['group_code' => 'NORMAL', 'group_name' => '일반 고객', 'price_rate' => 1.00],
|
||||
['group_code' => 'NEW', 'group_name' => '신규 고객', 'price_rate' => 1.00],
|
||||
];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
ClientGroup::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'group_code' => $group['group_code'],
|
||||
'group_name' => $group['group_name'],
|
||||
'price_rate' => $group['price_rate'],
|
||||
'is_active' => true,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ client_groups: ' . count($groups) . '건 생성');
|
||||
}
|
||||
}
|
||||
94
database/seeders/Dummy/DummyClientSeeder.php
Normal file
94
database/seeders/Dummy/DummyClientSeeder.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Orders\ClientGroup;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyClientSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 그룹 ID 조회
|
||||
$groups = ClientGroup::where('tenant_id', $tenantId)
|
||||
->pluck('id', 'group_code')
|
||||
->toArray();
|
||||
|
||||
// 매출처 (SALES) - 10개
|
||||
$salesClients = [
|
||||
['code' => 'S001', 'name' => '삼성전자', 'business_no' => '124-81-00998', 'group' => 'VIP', 'contact' => '김철수', 'phone' => '02-1234-5678', 'email' => 'kim@samsung.com'],
|
||||
['code' => 'S002', 'name' => 'LG전자', 'business_no' => '107-86-14075', 'group' => 'VIP', 'contact' => '이영희', 'phone' => '02-2345-6789', 'email' => 'lee@lg.com'],
|
||||
['code' => 'S003', 'name' => 'SK하이닉스', 'business_no' => '204-81-17169', 'group' => 'GOLD', 'contact' => '박민수', 'phone' => '031-123-4567', 'email' => 'park@skhynix.com'],
|
||||
['code' => 'S004', 'name' => '현대자동차', 'business_no' => '101-81-05765', 'group' => 'GOLD', 'contact' => '정은지', 'phone' => '02-3456-7890', 'email' => 'jung@hyundai.com'],
|
||||
['code' => 'S005', 'name' => '네이버', 'business_no' => '220-81-62517', 'group' => 'GOLD', 'contact' => '최준호', 'phone' => '031-234-5678', 'email' => 'choi@naver.com'],
|
||||
['code' => 'S006', 'name' => '카카오', 'business_no' => '120-87-65763', 'group' => 'SILVER', 'contact' => '강미래', 'phone' => '02-4567-8901', 'email' => 'kang@kakao.com'],
|
||||
['code' => 'S007', 'name' => '쿠팡', 'business_no' => '120-88-00767', 'group' => 'SILVER', 'contact' => '임도현', 'phone' => '02-5678-9012', 'email' => 'lim@coupang.com'],
|
||||
['code' => 'S008', 'name' => '토스', 'business_no' => '120-87-83139', 'group' => 'NORMAL', 'contact' => '윤서연', 'phone' => '02-6789-0123', 'email' => 'yoon@toss.im'],
|
||||
['code' => 'S009', 'name' => '배달의민족', 'business_no' => '220-87-93847', 'group' => 'NORMAL', 'contact' => '한지민', 'phone' => '02-7890-1234', 'email' => 'han@woowahan.com'],
|
||||
['code' => 'S010', 'name' => '당근마켓', 'business_no' => '815-87-01234', 'group' => 'NEW', 'contact' => '오태양', 'phone' => '02-8901-2345', 'email' => 'oh@daangn.com'],
|
||||
];
|
||||
|
||||
// 매입처 (PURCHASE) - 7개
|
||||
$purchaseClients = [
|
||||
['code' => 'P001', 'name' => '한화솔루션', 'business_no' => '138-81-00610', 'group' => null, 'contact' => '김재원', 'phone' => '02-1111-2222', 'email' => 'kim@hanwha.com'],
|
||||
['code' => 'P002', 'name' => '포스코', 'business_no' => '506-81-08754', 'group' => null, 'contact' => '이현석', 'phone' => '054-111-2222', 'email' => 'lee@posco.com'],
|
||||
['code' => 'P003', 'name' => '롯데케미칼', 'business_no' => '301-81-07123', 'group' => null, 'contact' => '박서준', 'phone' => '02-2222-3333', 'email' => 'park@lottechem.com'],
|
||||
['code' => 'P004', 'name' => 'GS칼텍스', 'business_no' => '104-81-23858', 'group' => null, 'contact' => '정해인', 'phone' => '02-3333-4444', 'email' => 'jung@gscaltex.com'],
|
||||
['code' => 'P005', 'name' => '대한항공', 'business_no' => '110-81-14794', 'group' => null, 'contact' => '송민호', 'phone' => '02-4444-5555', 'email' => 'song@koreanair.com'],
|
||||
['code' => 'P006', 'name' => '현대제철', 'business_no' => '130-81-12345', 'group' => null, 'contact' => '강동원', 'phone' => '032-555-6666', 'email' => 'kang@hyundaisteel.com'],
|
||||
['code' => 'P007', 'name' => 'SK이노베이션', 'business_no' => '110-81-67890', 'group' => null, 'contact' => '유재석', 'phone' => '02-6666-7777', 'email' => 'yoo@skinnovation.com'],
|
||||
];
|
||||
|
||||
// 매출매입처 (BOTH) - 3개
|
||||
$bothClients = [
|
||||
['code' => 'B001', 'name' => '두산에너빌리티', 'business_no' => '124-81-08628', 'group' => 'GOLD', 'contact' => '조인성', 'phone' => '02-7777-8888', 'email' => 'cho@doosan.com'],
|
||||
['code' => 'B002', 'name' => 'CJ대한통운', 'business_no' => '104-81-39849', 'group' => 'SILVER', 'contact' => '공유', 'phone' => '02-8888-9999', 'email' => 'gong@cjlogistics.com'],
|
||||
['code' => 'B003', 'name' => '삼성SDS', 'business_no' => '124-81-34567', 'group' => 'VIP', 'contact' => '이정재', 'phone' => '02-9999-0000', 'email' => 'lee@samsungsds.com'],
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
|
||||
// 매출처 생성
|
||||
foreach ($salesClients as $client) {
|
||||
$this->createClient($client, 'SALES', $tenantId, $userId, $groups);
|
||||
$count++;
|
||||
}
|
||||
|
||||
// 매입처 생성
|
||||
foreach ($purchaseClients as $client) {
|
||||
$this->createClient($client, 'PURCHASE', $tenantId, $userId, $groups);
|
||||
$count++;
|
||||
}
|
||||
|
||||
// 매출매입처 생성
|
||||
foreach ($bothClients as $client) {
|
||||
$this->createClient($client, 'BOTH', $tenantId, $userId, $groups);
|
||||
$count++;
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ clients: ' . $count . '건 생성');
|
||||
}
|
||||
|
||||
private function createClient(array $data, string $type, int $tenantId, int $userId, array $groups): void
|
||||
{
|
||||
Client::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'client_group_id' => $data['group'] ? ($groups[$data['group']] ?? null) : null,
|
||||
'client_code' => $data['code'],
|
||||
'name' => $data['name'],
|
||||
'client_type' => $type,
|
||||
'contact_person' => $data['contact'],
|
||||
'phone' => $data['phone'],
|
||||
'email' => $data['email'],
|
||||
'business_no' => $data['business_no'],
|
||||
'business_type' => '제조업',
|
||||
'business_item' => '전자제품',
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
94
database/seeders/Dummy/DummyDepositSeeder.php
Normal file
94
database/seeders/Dummy/DummyDepositSeeder.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Tenants\BankAccount;
|
||||
use App\Models\Tenants\Deposit;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyDepositSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 거래처 매핑 (SALES, BOTH만)
|
||||
$clients = Client::where('tenant_id', $tenantId)
|
||||
->whereIn('client_type', ['SALES', 'BOTH'])
|
||||
->get()
|
||||
->keyBy('name');
|
||||
|
||||
// 은행계좌 (대표계좌 우선)
|
||||
$bankAccounts = BankAccount::where('tenant_id', $tenantId)
|
||||
->where('status', 'active')
|
||||
->orderByDesc('is_primary')
|
||||
->get();
|
||||
|
||||
$primaryBankId = $bankAccounts->first()?->id;
|
||||
|
||||
// 결제수단 분포: transfer(70%), card(15%), cash(10%), check(5%)
|
||||
$methods = array_merge(
|
||||
array_fill(0, 14, 'transfer'),
|
||||
array_fill(0, 3, 'card'),
|
||||
array_fill(0, 2, 'cash'),
|
||||
array_fill(0, 1, 'check')
|
||||
);
|
||||
|
||||
// 거래처 순환 목록
|
||||
$clientNames = [
|
||||
'삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버',
|
||||
'카카오', '쿠팡', '토스', '배달의민족', '당근마켓',
|
||||
'두산에너빌리티', 'CJ대한통운', '삼성SDS',
|
||||
];
|
||||
|
||||
// 금액 범위
|
||||
$amounts = [
|
||||
'small' => [1000000, 5000000], // 30%
|
||||
'medium' => [5000000, 30000000], // 50%
|
||||
'large' => [30000000, 100000000], // 20%
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
$year = 2025;
|
||||
|
||||
// 월별 5건씩, 총 60건
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$day = rand(1, $daysInMonth);
|
||||
$clientName = $clientNames[($month * 5 + $i) % count($clientNames)];
|
||||
$client = $clients->get($clientName);
|
||||
|
||||
// 금액 결정 (분포에 따라)
|
||||
$rand = rand(1, 100);
|
||||
if ($rand <= 30) {
|
||||
$amount = rand($amounts['small'][0], $amounts['small'][1]);
|
||||
} elseif ($rand <= 80) {
|
||||
$amount = rand($amounts['medium'][0], $amounts['medium'][1]);
|
||||
} else {
|
||||
$amount = rand($amounts['large'][0], $amounts['large'][1]);
|
||||
}
|
||||
|
||||
Deposit::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'deposit_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
|
||||
'client_id' => $client?->id,
|
||||
'client_name' => $client ? null : $clientName,
|
||||
'bank_account_id' => $primaryBankId,
|
||||
'amount' => $amount,
|
||||
'payment_method' => $methods[array_rand($methods)],
|
||||
'description' => $clientName . ' 입금',
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ deposits: ' . $count . '건 생성');
|
||||
}
|
||||
}
|
||||
219
database/seeders/Dummy/DummyPaymentSeeder.php
Normal file
219
database/seeders/Dummy/DummyPaymentSeeder.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Tenants\Payment;
|
||||
use App\Models\Tenants\Plan;
|
||||
use App\Models\Tenants\Subscription;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyPaymentSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 1. 요금제 생성 (없으면)
|
||||
$plans = $this->createPlans($userId);
|
||||
|
||||
// 2. 구독 생성
|
||||
$subscription = $this->createSubscription($tenantId, $plans['standard']->id, $userId);
|
||||
|
||||
// 3. 결제 내역 생성
|
||||
$paymentCount = $this->createPayments($subscription, $userId);
|
||||
|
||||
$this->command->info(' ✓ plans: '.count($plans).'건 생성');
|
||||
$this->command->info(' ✓ subscriptions: 1건 생성');
|
||||
$this->command->info(' ✓ payments: '.$paymentCount.'건 생성');
|
||||
}
|
||||
|
||||
/**
|
||||
* 요금제 생성
|
||||
*/
|
||||
private function createPlans(int $userId): array
|
||||
{
|
||||
$plansData = [
|
||||
'free' => [
|
||||
'name' => '무료 체험',
|
||||
'code' => 'FREE',
|
||||
'description' => '14일 무료 체험',
|
||||
'price' => 0,
|
||||
'billing_cycle' => Plan::BILLING_MONTHLY,
|
||||
'features' => ['basic_features', 'limited_users'],
|
||||
'is_active' => true,
|
||||
],
|
||||
'starter' => [
|
||||
'name' => '스타터',
|
||||
'code' => 'STARTER',
|
||||
'description' => '소규모 팀을 위한 기본 플랜',
|
||||
'price' => 29000,
|
||||
'billing_cycle' => Plan::BILLING_MONTHLY,
|
||||
'features' => ['basic_features', 'email_support', 'up_to_5_users'],
|
||||
'is_active' => true,
|
||||
],
|
||||
'standard' => [
|
||||
'name' => '스탠다드',
|
||||
'code' => 'STANDARD',
|
||||
'description' => '성장하는 팀을 위한 표준 플랜',
|
||||
'price' => 79000,
|
||||
'billing_cycle' => Plan::BILLING_MONTHLY,
|
||||
'features' => ['all_features', 'priority_support', 'up_to_20_users', 'api_access'],
|
||||
'is_active' => true,
|
||||
],
|
||||
'professional' => [
|
||||
'name' => '프로페셔널',
|
||||
'code' => 'PRO',
|
||||
'description' => '전문가 팀을 위한 고급 플랜',
|
||||
'price' => 149000,
|
||||
'billing_cycle' => Plan::BILLING_MONTHLY,
|
||||
'features' => ['all_features', 'dedicated_support', 'unlimited_users', 'api_access', 'custom_integrations'],
|
||||
'is_active' => true,
|
||||
],
|
||||
'enterprise' => [
|
||||
'name' => '엔터프라이즈',
|
||||
'code' => 'ENTERPRISE',
|
||||
'description' => '대기업 맞춤형 플랜',
|
||||
'price' => 499000,
|
||||
'billing_cycle' => Plan::BILLING_MONTHLY,
|
||||
'features' => ['all_features', 'dedicated_support', 'unlimited_users', 'api_access', 'custom_integrations', 'sla', 'on_premise'],
|
||||
'is_active' => true,
|
||||
],
|
||||
'yearly_standard' => [
|
||||
'name' => '스탠다드 (연간)',
|
||||
'code' => 'STANDARD_YEARLY',
|
||||
'description' => '연간 결제 시 20% 할인',
|
||||
'price' => 758400, // 79000 * 12 * 0.8
|
||||
'billing_cycle' => Plan::BILLING_YEARLY,
|
||||
'features' => ['all_features', 'priority_support', 'up_to_20_users', 'api_access'],
|
||||
'is_active' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$plans = [];
|
||||
foreach ($plansData as $key => $data) {
|
||||
$plans[$key] = Plan::firstOrCreate(
|
||||
['code' => $data['code']],
|
||||
array_merge($data, ['created_by' => $userId])
|
||||
);
|
||||
}
|
||||
|
||||
return $plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* 구독 생성
|
||||
*/
|
||||
private function createSubscription(int $tenantId, int $planId, int $userId): Subscription
|
||||
{
|
||||
// 기존 활성 구독이 있으면 반환
|
||||
$existing = Subscription::where('tenant_id', $tenantId)
|
||||
->where('status', Subscription::STATUS_ACTIVE)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
// 새 구독 생성 (12개월 전부터 시작)
|
||||
return Subscription::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'plan_id' => $planId,
|
||||
'started_at' => now()->subMonths(12)->startOfMonth(),
|
||||
'ended_at' => now()->addMonths(1)->endOfMonth(),
|
||||
'status' => Subscription::STATUS_ACTIVE,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 결제 내역 생성
|
||||
*/
|
||||
private function createPayments(Subscription $subscription, int $userId): int
|
||||
{
|
||||
// 이미 결제 내역이 있으면 스킵
|
||||
if ($subscription->payments()->count() > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$plan = $subscription->plan;
|
||||
$paymentMethods = [
|
||||
Payment::METHOD_CARD,
|
||||
Payment::METHOD_CARD,
|
||||
Payment::METHOD_CARD, // 카드 60%
|
||||
Payment::METHOD_BANK,
|
||||
Payment::METHOD_BANK, // 계좌이체 30%
|
||||
Payment::METHOD_VIRTUAL, // 가상계좌 10%
|
||||
];
|
||||
|
||||
$statuses = [
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED,
|
||||
Payment::STATUS_COMPLETED, // 완료 80%
|
||||
Payment::STATUS_CANCELLED, // 취소 10%
|
||||
Payment::STATUS_REFUNDED, // 환불 10%
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
|
||||
// 12개월치 결제 내역 생성
|
||||
for ($i = 12; $i >= 0; $i--) {
|
||||
$paymentDate = now()->subMonths($i)->startOfMonth();
|
||||
|
||||
// 이번 달은 대기 상태로
|
||||
$status = $i === 0
|
||||
? Payment::STATUS_PENDING
|
||||
: $statuses[array_rand($statuses)];
|
||||
|
||||
// 금액 변동 (할인, 프로모션 등)
|
||||
$baseAmount = $plan->price;
|
||||
$amount = match (true) {
|
||||
$i >= 10 => $baseAmount * 0.5, // 첫 3개월 50% 할인
|
||||
$i >= 6 => $baseAmount * 0.8, // 다음 4개월 20% 할인
|
||||
default => $baseAmount, // 이후 정가
|
||||
};
|
||||
|
||||
// 취소/환불은 금액 0
|
||||
if (in_array($status, [Payment::STATUS_CANCELLED, Payment::STATUS_REFUNDED])) {
|
||||
$amount = $baseAmount;
|
||||
}
|
||||
|
||||
$paidAt = $status === Payment::STATUS_COMPLETED
|
||||
? $paymentDate->copy()->addDays(rand(1, 5))
|
||||
: null;
|
||||
|
||||
$transactionId = $status === Payment::STATUS_COMPLETED
|
||||
? 'TXN'.$paymentDate->format('Ymd').str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT)
|
||||
: null;
|
||||
|
||||
$memo = match ($status) {
|
||||
Payment::STATUS_CANCELLED => '고객 요청에 의한 취소',
|
||||
Payment::STATUS_REFUNDED => '서비스 불만족으로 인한 환불',
|
||||
Payment::STATUS_PENDING => '결제 대기 중',
|
||||
default => null,
|
||||
};
|
||||
|
||||
Payment::create([
|
||||
'subscription_id' => $subscription->id,
|
||||
'amount' => $amount,
|
||||
'payment_method' => $paymentMethods[array_rand($paymentMethods)],
|
||||
'transaction_id' => $transactionId,
|
||||
'paid_at' => $paidAt,
|
||||
'status' => $status,
|
||||
'memo' => $memo,
|
||||
'created_by' => $userId,
|
||||
'created_at' => $paymentDate,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
107
database/seeders/Dummy/DummyPopupSeeder.php
Normal file
107
database/seeders/Dummy/DummyPopupSeeder.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Popups\Popup;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyPopupSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
$popups = [
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '시스템 점검 안내',
|
||||
'content' => '<p>안녕하세요.</p><p>2025년 1월 15일(수) 02:00 ~ 06:00 동안 시스템 점검이 예정되어 있습니다.</p><p>점검 시간 동안 서비스 이용이 제한될 수 있으니 양해 부탁드립니다.</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now()->subDays(5),
|
||||
'ended_at' => now()->addDays(10),
|
||||
],
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '신규 기능 업데이트 안내',
|
||||
'content' => '<p>새로운 기능이 추가되었습니다!</p><ul><li>대시보드 개선</li><li>보고서 내보내기 기능</li><li>알림 설정 강화</li></ul><p>자세한 내용은 도움말을 확인해 주세요.</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now()->subDays(3),
|
||||
'ended_at' => now()->addDays(30),
|
||||
],
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '연말 휴무 안내',
|
||||
'content' => '<p>2024년 연말 휴무 일정을 안내드립니다.</p><p><strong>휴무 기간</strong>: 12월 30일(월) ~ 1월 1일(수)</p><p>새해 복 많이 받으세요!</p>',
|
||||
'status' => 'inactive',
|
||||
'started_at' => now()->subMonth(),
|
||||
'ended_at' => now()->subDays(20),
|
||||
],
|
||||
[
|
||||
'target_type' => 'department',
|
||||
'target_id' => 1,
|
||||
'title' => '부서 회의 안내',
|
||||
'content' => '<p>이번 주 금요일 오후 2시에 정기 회의가 있습니다.</p><p><strong>장소</strong>: 3층 회의실</p><p><strong>안건</strong>: 1분기 실적 검토</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now(),
|
||||
'ended_at' => now()->addDays(7),
|
||||
],
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '보안 업데이트 필수 안내',
|
||||
'content' => '<p><strong>중요!</strong></p><p>보안 강화를 위해 비밀번호 변경이 필요합니다.</p><p>최근 3개월 이내 비밀번호를 변경하지 않으신 분은 마이페이지에서 변경해 주세요.</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now()->subDays(1),
|
||||
'ended_at' => now()->addDays(14),
|
||||
],
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '서비스 이용약관 변경 안내',
|
||||
'content' => '<p>서비스 이용약관이 2025년 2월 1일부터 변경됩니다.</p><p>주요 변경 사항:</p><ul><li>개인정보 처리방침 개정</li><li>서비스 이용 조건 명확화</li></ul><p>변경된 약관은 공지사항에서 확인하실 수 있습니다.</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now(),
|
||||
'ended_at' => now()->addDays(45),
|
||||
],
|
||||
[
|
||||
'target_type' => 'department',
|
||||
'target_id' => 2,
|
||||
'title' => '영업팀 워크샵 안내',
|
||||
'content' => '<p>영업팀 상반기 워크샵이 예정되어 있습니다.</p><p><strong>일시</strong>: 2025년 2월 15일(토)</p><p><strong>장소</strong>: 추후 공지</p><p>많은 참여 바랍니다!</p>',
|
||||
'status' => 'active',
|
||||
'started_at' => now()->addDays(5),
|
||||
'ended_at' => now()->addDays(50),
|
||||
],
|
||||
[
|
||||
'target_type' => 'all',
|
||||
'target_id' => null,
|
||||
'title' => '모바일 앱 출시 안내',
|
||||
'content' => '<p>SAM 모바일 앱이 출시되었습니다!</p><p>앱스토어와 구글플레이에서 "SAM"을 검색해 주세요.</p><p>모바일에서도 편리하게 업무를 처리하세요.</p>',
|
||||
'status' => 'inactive',
|
||||
'started_at' => now()->subMonths(2),
|
||||
'ended_at' => now()->subMonth(),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($popups as $popup) {
|
||||
Popup::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'target_type' => $popup['target_type'],
|
||||
'target_id' => $popup['target_id'],
|
||||
'title' => $popup['title'],
|
||||
'content' => $popup['content'],
|
||||
'status' => $popup['status'],
|
||||
'started_at' => $popup['started_at'],
|
||||
'ended_at' => $popup['ended_at'],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ popups: ' . count($popups) . '건 생성');
|
||||
}
|
||||
}
|
||||
91
database/seeders/Dummy/DummyPurchaseSeeder.php
Normal file
91
database/seeders/Dummy/DummyPurchaseSeeder.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Tenants\Purchase;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyPurchaseSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 거래처 매핑 (PURCHASE, BOTH만)
|
||||
$clients = Client::where('tenant_id', $tenantId)
|
||||
->whereIn('client_type', ['PURCHASE', 'BOTH'])
|
||||
->get()
|
||||
->keyBy('name');
|
||||
|
||||
$clientNames = [
|
||||
'한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공',
|
||||
'현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티',
|
||||
];
|
||||
|
||||
$amounts = [
|
||||
'small' => [1000000, 5000000],
|
||||
'medium' => [5000000, 30000000],
|
||||
'large' => [30000000, 80000000],
|
||||
];
|
||||
|
||||
// 월별 건수: 1~10월 5~6건, 11월 7건, 12월 6건 = 70건
|
||||
$monthlyCount = [5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 7, 6];
|
||||
|
||||
$count = 0;
|
||||
$year = 2025;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
$purchaseCount = $monthlyCount[$month - 1];
|
||||
|
||||
for ($i = 0; $i < $purchaseCount; $i++) {
|
||||
$day = (int) (($i + 1) * $daysInMonth / ($purchaseCount + 1));
|
||||
$day = max(1, min($day, $daysInMonth));
|
||||
|
||||
$clientName = $clientNames[($count) % count($clientNames)];
|
||||
$client = $clients->get($clientName);
|
||||
|
||||
$rand = rand(1, 100);
|
||||
if ($rand <= 30) {
|
||||
$supply = rand($amounts['small'][0], $amounts['small'][1]);
|
||||
} elseif ($rand <= 80) {
|
||||
$supply = rand($amounts['medium'][0], $amounts['medium'][1]);
|
||||
} else {
|
||||
$supply = rand($amounts['large'][0], $amounts['large'][1]);
|
||||
}
|
||||
|
||||
// 상태 결정
|
||||
if ($month <= 10) {
|
||||
$status = 'confirmed';
|
||||
} elseif ($month == 11) {
|
||||
$status = $i < 4 ? 'confirmed' : 'draft';
|
||||
} else {
|
||||
$status = $i < 1 ? 'confirmed' : 'draft';
|
||||
}
|
||||
|
||||
$tax = round($supply * 0.1);
|
||||
$total = $supply + $tax;
|
||||
|
||||
Purchase::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'purchase_number' => sprintf('PUR-%04d%02d-%04d', $year, $month, $i + 1),
|
||||
'purchase_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
|
||||
'client_id' => $client->id,
|
||||
'supply_amount' => $supply,
|
||||
'tax_amount' => $tax,
|
||||
'total_amount' => $total,
|
||||
'description' => $clientName . ' 매입',
|
||||
'status' => $status,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ purchases: ' . $count . '건 생성');
|
||||
}
|
||||
}
|
||||
93
database/seeders/Dummy/DummySaleSeeder.php
Normal file
93
database/seeders/Dummy/DummySaleSeeder.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Tenants\Sale;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummySaleSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 거래처 매핑 (SALES, BOTH만)
|
||||
$clients = Client::where('tenant_id', $tenantId)
|
||||
->whereIn('client_type', ['SALES', 'BOTH'])
|
||||
->get()
|
||||
->keyBy('name');
|
||||
|
||||
$clientNames = [
|
||||
'삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버',
|
||||
'카카오', '쿠팡', '토스', '배달의민족', '당근마켓',
|
||||
'두산에너빌리티', 'CJ대한통운', '삼성SDS',
|
||||
];
|
||||
|
||||
$amounts = [
|
||||
'small' => [1000000, 5000000],
|
||||
'medium' => [5000000, 30000000],
|
||||
'large' => [30000000, 100000000],
|
||||
];
|
||||
|
||||
// 월별 건수: 1~10월 6~7건, 11월 8건, 12월 7건 = 80건
|
||||
$monthlyCount = [6, 6, 7, 6, 7, 6, 7, 6, 7, 7, 8, 7];
|
||||
|
||||
$count = 0;
|
||||
$year = 2025;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
$salesCount = $monthlyCount[$month - 1];
|
||||
|
||||
for ($i = 0; $i < $salesCount; $i++) {
|
||||
$day = (int) (($i + 1) * $daysInMonth / ($salesCount + 1));
|
||||
$day = max(1, min($day, $daysInMonth));
|
||||
|
||||
$clientName = $clientNames[($count) % count($clientNames)];
|
||||
$client = $clients->get($clientName);
|
||||
|
||||
// 금액 결정
|
||||
$rand = rand(1, 100);
|
||||
if ($rand <= 30) {
|
||||
$supply = rand($amounts['small'][0], $amounts['small'][1]);
|
||||
} elseif ($rand <= 80) {
|
||||
$supply = rand($amounts['medium'][0], $amounts['medium'][1]);
|
||||
} else {
|
||||
$supply = rand($amounts['large'][0], $amounts['large'][1]);
|
||||
}
|
||||
|
||||
// 상태 결정: 1~10월 invoiced/confirmed, 11~12월 draft 포함
|
||||
if ($month <= 10) {
|
||||
$status = rand(0, 1) ? 'invoiced' : 'confirmed';
|
||||
} elseif ($month == 11) {
|
||||
$status = $i < 5 ? (rand(0, 1) ? 'invoiced' : 'confirmed') : 'draft';
|
||||
} else {
|
||||
$status = $i < 1 ? 'confirmed' : 'draft';
|
||||
}
|
||||
|
||||
$tax = round($supply * 0.1);
|
||||
$total = $supply + $tax;
|
||||
|
||||
Sale::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'sale_number' => sprintf('SAL-%04d%02d-%04d', $year, $month, $i + 1),
|
||||
'sale_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
|
||||
'client_id' => $client->id,
|
||||
'supply_amount' => $supply,
|
||||
'tax_amount' => $tax,
|
||||
'total_amount' => $total,
|
||||
'description' => $clientName . ' 매출',
|
||||
'status' => $status,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ sales: ' . $count . '건 생성');
|
||||
}
|
||||
}
|
||||
87
database/seeders/Dummy/DummyWithdrawalSeeder.php
Normal file
87
database/seeders/Dummy/DummyWithdrawalSeeder.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Tenants\BankAccount;
|
||||
use App\Models\Tenants\Withdrawal;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyWithdrawalSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 거래처 매핑 (PURCHASE, BOTH만)
|
||||
$clients = Client::where('tenant_id', $tenantId)
|
||||
->whereIn('client_type', ['PURCHASE', 'BOTH'])
|
||||
->get()
|
||||
->keyBy('name');
|
||||
|
||||
// 은행계좌
|
||||
$primaryBankId = BankAccount::where('tenant_id', $tenantId)
|
||||
->where('is_primary', true)
|
||||
->value('id');
|
||||
|
||||
// 결제수단 분포
|
||||
$methods = array_merge(
|
||||
array_fill(0, 14, 'transfer'),
|
||||
array_fill(0, 3, 'card'),
|
||||
array_fill(0, 2, 'cash'),
|
||||
array_fill(0, 1, 'check')
|
||||
);
|
||||
|
||||
// 매입처 순환 목록
|
||||
$clientNames = [
|
||||
'한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공',
|
||||
'현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티',
|
||||
];
|
||||
|
||||
$amounts = [
|
||||
'small' => [1000000, 5000000],
|
||||
'medium' => [5000000, 30000000],
|
||||
'large' => [30000000, 80000000],
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
$year = 2025;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$day = rand(1, $daysInMonth);
|
||||
$clientName = $clientNames[($month * 5 + $i) % count($clientNames)];
|
||||
$client = $clients->get($clientName);
|
||||
|
||||
$rand = rand(1, 100);
|
||||
if ($rand <= 30) {
|
||||
$amount = rand($amounts['small'][0], $amounts['small'][1]);
|
||||
} elseif ($rand <= 80) {
|
||||
$amount = rand($amounts['medium'][0], $amounts['medium'][1]);
|
||||
} else {
|
||||
$amount = rand($amounts['large'][0], $amounts['large'][1]);
|
||||
}
|
||||
|
||||
Withdrawal::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'withdrawal_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
|
||||
'client_id' => $client?->id,
|
||||
'client_name' => $client ? null : $clientName,
|
||||
'bank_account_id' => $primaryBankId,
|
||||
'amount' => $amount,
|
||||
'payment_method' => $methods[array_rand($methods)],
|
||||
'description' => $clientName . ' 지급',
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ withdrawals: ' . $count . '건 생성');
|
||||
}
|
||||
}
|
||||
54
database/seeders/DummyDataSeeder.php
Normal file
54
database/seeders/DummyDataSeeder.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
// database/seeders/DummyDataSeeder.php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyDataSeeder extends Seeder
|
||||
{
|
||||
// 대상 테넌트 ID
|
||||
public const TENANT_ID = 287;
|
||||
|
||||
// 생성자 사용자 ID
|
||||
public const USER_ID = 1;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$this->command->info('🌱 더미 데이터 시딩 시작...');
|
||||
$this->command->info(' 대상 테넌트: ID '.self::TENANT_ID);
|
||||
|
||||
$this->call([
|
||||
Dummy\DummyClientGroupSeeder::class,
|
||||
Dummy\DummyBankAccountSeeder::class,
|
||||
Dummy\DummyClientSeeder::class,
|
||||
Dummy\DummyDepositSeeder::class,
|
||||
Dummy\DummyWithdrawalSeeder::class,
|
||||
Dummy\DummySaleSeeder::class,
|
||||
Dummy\DummyPurchaseSeeder::class,
|
||||
Dummy\DummyPopupSeeder::class,
|
||||
Dummy\DummyPaymentSeeder::class,
|
||||
]);
|
||||
|
||||
$this->command->info('');
|
||||
$this->command->info('✅ 더미 데이터 시딩 완료!');
|
||||
$this->command->table(
|
||||
['테이블', '생성 수량'],
|
||||
[
|
||||
['client_groups', '5'],
|
||||
['bank_accounts', '5'],
|
||||
['clients', '20'],
|
||||
['deposits', '60'],
|
||||
['withdrawals', '60'],
|
||||
['sales', '80'],
|
||||
['purchases', '70'],
|
||||
['popups', '8'],
|
||||
['plans', '6'],
|
||||
['subscriptions', '1'],
|
||||
['payments', '13'],
|
||||
['총계', '~328'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user