Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ai_configs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 50)->comment('설정 이름 (Gemini, Claude 등)');
|
||||
$table->string('provider', 30)->comment('제공자 (gemini, claude, openai)');
|
||||
$table->string('api_key', 255)->comment('API 키');
|
||||
$table->string('model', 100)->comment('모델명 (gemini-2.0-flash 등)');
|
||||
$table->string('base_url', 255)->nullable()->comment('API Base URL');
|
||||
$table->text('description')->nullable()->comment('설명');
|
||||
$table->boolean('is_active')->default(false)->comment('활성화 여부 (provider당 1개만 활성화)');
|
||||
$table->json('options')->nullable()->comment('추가 옵션 (JSON)');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->index('provider');
|
||||
$table->index(['provider', 'is_active']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ai_configs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* 바로빌 월정액 구독 테이블
|
||||
*
|
||||
* 회원사별 월정액 서비스 구독 현황 관리
|
||||
* - 계좌조회, 카드내역, 홈텍스 매입/매출
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('barobill_subscriptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('member_id')->comment('바로빌 회원사 ID');
|
||||
$table->enum('service_type', ['bank_account', 'card', 'hometax'])
|
||||
->comment('서비스 유형: bank_account=계좌조회, card=카드내역, hometax=홈텍스');
|
||||
$table->unsignedInteger('monthly_fee')->default(0)->comment('월정액 금액 (원)');
|
||||
$table->date('started_at')->comment('구독 시작일');
|
||||
$table->date('ended_at')->nullable()->comment('구독 종료일 (null=진행중)');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 상태');
|
||||
$table->text('memo')->nullable()->comment('메모');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['member_id', 'service_type']);
|
||||
$table->index(['is_active', 'service_type']);
|
||||
|
||||
// 외래키
|
||||
$table->foreign('member_id')
|
||||
->references('id')
|
||||
->on('barobill_members')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('barobill_subscriptions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* 바로빌 과금 기록 테이블
|
||||
*
|
||||
* 월별 과금 내역 (월정액 + 건별 사용량)
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('barobill_billing_records', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('member_id')->comment('바로빌 회원사 ID');
|
||||
$table->string('billing_month', 7)->comment('과금 월 (YYYY-MM)');
|
||||
$table->enum('service_type', ['tax_invoice', 'bank_account', 'card', 'hometax'])
|
||||
->comment('서비스 유형');
|
||||
$table->enum('billing_type', ['subscription', 'usage'])
|
||||
->comment('과금 유형: subscription=월정액, usage=건별');
|
||||
$table->unsignedInteger('quantity')->default(1)->comment('수량 (월정액=1, 건별=사용건수)');
|
||||
$table->unsignedInteger('unit_price')->default(0)->comment('단가 (원)');
|
||||
$table->unsignedInteger('total_amount')->default(0)->comment('총액 (원)');
|
||||
$table->date('billed_at')->comment('과금일');
|
||||
$table->text('description')->nullable()->comment('설명');
|
||||
$table->timestamps();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['member_id', 'billing_month']);
|
||||
$table->index(['billing_month', 'service_type']);
|
||||
$table->index(['billing_month', 'billing_type']);
|
||||
$table->index('billed_at');
|
||||
|
||||
// 중복 방지 (같은 월, 같은 서비스, 같은 과금유형)
|
||||
$table->unique(['member_id', 'billing_month', 'service_type', 'billing_type'], 'billing_unique');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('member_id')
|
||||
->references('id')
|
||||
->on('barobill_members')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('barobill_billing_records');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* 바로빌 월별 집계 테이블
|
||||
*
|
||||
* 조회 성능 최적화를 위한 월별 집계 데이터
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('barobill_monthly_summaries', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('member_id')->comment('바로빌 회원사 ID');
|
||||
$table->string('billing_month', 7)->comment('과금 월 (YYYY-MM)');
|
||||
|
||||
// 월정액 항목별
|
||||
$table->unsignedInteger('bank_account_fee')->default(0)->comment('계좌조회 월정액');
|
||||
$table->unsignedInteger('card_fee')->default(0)->comment('카드내역 월정액');
|
||||
$table->unsignedInteger('hometax_fee')->default(0)->comment('홈텍스 월정액');
|
||||
$table->unsignedInteger('subscription_total')->default(0)->comment('월정액 합계');
|
||||
|
||||
// 건별 사용량
|
||||
$table->unsignedInteger('tax_invoice_count')->default(0)->comment('세금계산서 발행 건수');
|
||||
$table->unsignedInteger('tax_invoice_amount')->default(0)->comment('세금계산서 과금액');
|
||||
$table->unsignedInteger('usage_total')->default(0)->comment('건별 사용 합계');
|
||||
|
||||
// 총합계
|
||||
$table->unsignedInteger('grand_total')->default(0)->comment('총합계');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
// 인덱스
|
||||
$table->unique(['member_id', 'billing_month']);
|
||||
$table->index('billing_month');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('member_id')
|
||||
->references('id')
|
||||
->on('barobill_members')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('barobill_monthly_summaries');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('barobill_pricing_policies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('service_type', 50)->comment('서비스 유형: card, tax_invoice, bank_account');
|
||||
$table->string('name', 100)->comment('정책명');
|
||||
$table->string('description')->nullable()->comment('설명');
|
||||
|
||||
// 기본 무료 제공량
|
||||
$table->integer('free_quota')->default(0)->comment('무료 기본 제공량');
|
||||
$table->string('free_quota_unit', 20)->default('개')->comment('무료 제공 단위 (장, 건, 개 등)');
|
||||
|
||||
// 추가 과금 설정
|
||||
$table->integer('additional_unit')->default(1)->comment('추가 과금 단위 (1, 50 등)');
|
||||
$table->string('additional_unit_label', 20)->default('개')->comment('추가 단위 라벨');
|
||||
$table->integer('additional_price')->default(0)->comment('추가 과금 금액 (원)');
|
||||
|
||||
$table->boolean('is_active')->default(true)->comment('활성화 여부');
|
||||
$table->integer('sort_order')->default(0)->comment('정렬 순서');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique('service_type');
|
||||
$table->index('is_active');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('barobill_pricing_policies');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
// 영업담당자 계층 구조 (상위 관리자)
|
||||
$table->unsignedBigInteger('parent_id')->nullable()->after('is_super_admin');
|
||||
$table->foreign('parent_id')->references('id')->on('users')->onDelete('set null');
|
||||
|
||||
// 승인 상태: pending(대기), approved(승인), rejected(반려)
|
||||
$table->string('approval_status', 20)->default('approved')->after('parent_id');
|
||||
|
||||
// 승인 관련 정보
|
||||
$table->unsignedBigInteger('approved_by')->nullable()->after('approval_status');
|
||||
$table->timestamp('approved_at')->nullable()->after('approved_by');
|
||||
$table->text('rejection_reason')->nullable()->after('approved_at');
|
||||
|
||||
// 인덱스
|
||||
$table->index('parent_id');
|
||||
$table->index('approval_status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropForeign(['parent_id']);
|
||||
$table->dropIndex(['parent_id']);
|
||||
$table->dropIndex(['approval_status']);
|
||||
|
||||
$table->dropColumn([
|
||||
'parent_id',
|
||||
'approval_status',
|
||||
'approved_by',
|
||||
'approved_at',
|
||||
'rejection_reason',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_manager_documents', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id');
|
||||
$table->unsignedBigInteger('user_id');
|
||||
|
||||
// 파일 정보
|
||||
$table->string('file_path', 500);
|
||||
$table->string('original_name', 255);
|
||||
$table->string('stored_name', 255);
|
||||
$table->string('mime_type', 100)->nullable();
|
||||
$table->unsignedBigInteger('file_size')->default(0);
|
||||
|
||||
// 문서 타입: id_card(신분증), business_license(사업자등록증), contract(계약서), other(기타)
|
||||
$table->string('document_type', 50)->default('other');
|
||||
$table->string('description', 500)->nullable();
|
||||
|
||||
// 메타 정보
|
||||
$table->unsignedBigInteger('uploaded_by')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
$table->unsignedBigInteger('deleted_by')->nullable();
|
||||
|
||||
// 외래키 및 인덱스
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('uploaded_by')->references('id')->on('users')->onDelete('set null');
|
||||
|
||||
$table->index(['tenant_id', 'user_id']);
|
||||
$table->index('document_type');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_manager_documents');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* 영업파트너 영업권(명함등록) 테이블
|
||||
*
|
||||
* 영업 프로세스:
|
||||
* 1. 영업파트너가 명함 등록 (status = 'active')
|
||||
* 2. 2개월간 영업권 유효 (expires_at)
|
||||
* 3. 계약 성사 시 테넌트 전환 (status = 'converted', tenant_id 연결)
|
||||
* 4. 미성사/만료 시 (status = 'expired')
|
||||
* 5. 만료 후 1개월 쿨다운 (cooldown_ends_at) 이후 재등록 가능
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tenant_prospects', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
// 회사 정보
|
||||
$table->string('business_number', 20)->index()->comment('사업자번호 (중복체크 키)');
|
||||
$table->string('company_name', 100)->comment('회사명');
|
||||
$table->string('ceo_name', 50)->nullable()->comment('대표자명');
|
||||
$table->string('contact_phone', 20)->nullable()->comment('연락처');
|
||||
$table->string('contact_email', 100)->nullable()->comment('이메일');
|
||||
$table->string('address', 500)->nullable()->comment('주소');
|
||||
|
||||
// 영업파트너 정보
|
||||
$table->foreignId('registered_by')
|
||||
->constrained('users')
|
||||
->cascadeOnDelete()
|
||||
->comment('등록한 영업파트너 ID');
|
||||
|
||||
// 명함 이미지
|
||||
$table->string('business_card_path', 500)->nullable()->comment('명함 이미지 경로');
|
||||
|
||||
// 영업권 상태
|
||||
$table->string('status', 20)->default('active')->index()->comment('active, expired, converted');
|
||||
$table->timestamp('registered_at')->useCurrent()->comment('등록일');
|
||||
$table->timestamp('expires_at')->comment('만료일 (등록일 + 2개월)');
|
||||
$table->timestamp('cooldown_ends_at')->comment('쿨다운 종료일 (만료일 + 1개월)');
|
||||
|
||||
// 테넌트 전환 정보
|
||||
$table->foreignId('tenant_id')
|
||||
->nullable()
|
||||
->constrained('tenants')
|
||||
->nullOnDelete()
|
||||
->comment('전환된 테넌트 ID');
|
||||
$table->timestamp('converted_at')->nullable()->comment('테넌트 전환일');
|
||||
$table->foreignId('converted_by')
|
||||
->nullable()
|
||||
->constrained('users')
|
||||
->nullOnDelete()
|
||||
->comment('전환 처리자 ID');
|
||||
|
||||
// 메모
|
||||
$table->text('memo')->nullable()->comment('메모');
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 복합 인덱스: 사업자번호 + 상태 (중복 체크용)
|
||||
$table->index(['business_number', 'status']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tenant_prospects');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_prospects', function (Blueprint $table) {
|
||||
$table->string('business_card_image', 500)->nullable()->after('address')
|
||||
->comment('명함 이미지 파일 경로');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_prospects', function (Blueprint $table) {
|
||||
$table->dropColumn('business_card_image');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_prospects', function (Blueprint $table) {
|
||||
$table->string('id_card_image', 500)->nullable()->after('business_card_image')
|
||||
->comment('신분증 사본 이미지 파일 경로');
|
||||
$table->string('bankbook_image', 500)->nullable()->after('id_card_image')
|
||||
->comment('통장 사본 이미지 파일 경로');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_prospects', function (Blueprint $table) {
|
||||
$table->dropColumn(['id_card_image', 'bankbook_image']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tenant_prospects', function (Blueprint $table) {
|
||||
$table->string('id_card_path')->nullable()->after('business_card_path')->comment('신분증 사본 경로');
|
||||
$table->string('bankbook_path')->nullable()->after('id_card_path')->comment('통장 사본 경로');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tenant_prospects', function (Blueprint $table) {
|
||||
$table->dropColumn(['id_card_path', 'bankbook_path']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('credit_inquiries', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('tenant_id')->nullable()->after('id');
|
||||
$table->index('tenant_id');
|
||||
$table->index(['tenant_id', 'inquired_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('credit_inquiries', function (Blueprint $table) {
|
||||
$table->dropIndex(['tenant_id', 'inquired_at']);
|
||||
$table->dropIndex(['tenant_id']);
|
||||
$table->dropColumn('tenant_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_scenario_checklists', function (Blueprint $table) {
|
||||
// 기존 유니크 키 삭제
|
||||
$table->dropUnique('sales_scenario_unique');
|
||||
|
||||
// checkpoint_index를 nullable로 변경
|
||||
$table->unsignedTinyInteger('checkpoint_index')->nullable()->change();
|
||||
|
||||
// 새 유니크 키 생성 (checkpoint_id 기반)
|
||||
$table->unique(
|
||||
['tenant_id', 'scenario_type', 'step_id', 'checkpoint_id'],
|
||||
'sales_scenario_checkpoint_unique'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_scenario_checklists', function (Blueprint $table) {
|
||||
// 새 유니크 키 삭제
|
||||
$table->dropUnique('sales_scenario_checkpoint_unique');
|
||||
|
||||
// checkpoint_index를 NOT NULL로 복원
|
||||
$table->unsignedTinyInteger('checkpoint_index')->nullable(false)->change();
|
||||
|
||||
// 기존 유니크 키 복원
|
||||
$table->unique(
|
||||
['tenant_id', 'user_id', 'step_id', 'checkpoint_index'],
|
||||
'sales_scenario_unique'
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_consultations', function (Blueprint $table) {
|
||||
$table->string('gcs_uri', 500)->nullable()->after('duration')
|
||||
->comment('Google Cloud Storage URI (본사 연구용 백업)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_consultations', function (Blueprint $table) {
|
||||
$table->dropColumn('gcs_uri');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* 영업 파트너(영업 담당자) 테이블
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_partners', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id')->comment('연결된 사용자 ID');
|
||||
$table->string('partner_code', 20)->unique()->comment('파트너 고유 코드');
|
||||
$table->enum('partner_type', ['individual', 'corporate'])->default('individual')->comment('파트너 유형');
|
||||
|
||||
// 수수료 정보
|
||||
$table->decimal('commission_rate', 5, 2)->default(20.00)->comment('기본 수수료율 (%)');
|
||||
$table->decimal('manager_commission_rate', 5, 2)->default(5.00)->comment('관리자 수수료율 (%)');
|
||||
|
||||
// 계좌 정보
|
||||
$table->string('bank_name', 50)->nullable()->comment('은행명');
|
||||
$table->string('account_number', 50)->nullable()->comment('계좌번호');
|
||||
$table->string('account_holder', 50)->nullable()->comment('예금주');
|
||||
|
||||
// 상태 관리
|
||||
$table->enum('status', ['pending', 'active', 'inactive', 'suspended'])->default('pending')->comment('상태');
|
||||
$table->timestamp('approved_at')->nullable()->comment('승인 일시');
|
||||
$table->unsignedBigInteger('approved_by')->nullable()->comment('승인자 ID');
|
||||
|
||||
// 실적 통계 (캐시용)
|
||||
$table->unsignedInteger('total_contracts')->default(0)->comment('총 계약 건수');
|
||||
$table->decimal('total_commission', 15, 2)->default(0)->comment('총 누적 수당');
|
||||
|
||||
$table->text('notes')->nullable()->comment('메모');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index('user_id');
|
||||
$table->index('status');
|
||||
$table->index('partner_type');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_partners');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* 테넌트별 영업 관리 테이블 (tenants 외래키 연결)
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_tenant_managements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->unique()->comment('테넌트 ID (1:1 관계)');
|
||||
$table->unsignedBigInteger('sales_partner_id')->nullable()->comment('영업 담당자 ID');
|
||||
$table->unsignedBigInteger('manager_user_id')->nullable()->comment('관리 매니저 사용자 ID');
|
||||
|
||||
// 시나리오 진행 상태
|
||||
$table->unsignedTinyInteger('sales_scenario_step')->default(1)->comment('영업 시나리오 현재 단계 (1-6)');
|
||||
$table->unsignedTinyInteger('manager_scenario_step')->default(1)->comment('매니저 시나리오 현재 단계 (1-6)');
|
||||
|
||||
// 영업 상태
|
||||
$table->enum('status', [
|
||||
'prospect', // 잠재 고객
|
||||
'approach', // 접근 중
|
||||
'negotiation', // 협상 중
|
||||
'contracted', // 계약 완료
|
||||
'onboarding', // 온보딩 중
|
||||
'active', // 활성 고객
|
||||
'churned', // 이탈
|
||||
])->default('prospect')->comment('영업 상태');
|
||||
|
||||
// 계약 정보
|
||||
$table->timestamp('first_contact_at')->nullable()->comment('최초 접촉일');
|
||||
$table->timestamp('contracted_at')->nullable()->comment('계약 체결일');
|
||||
$table->timestamp('onboarding_completed_at')->nullable()->comment('온보딩 완료일');
|
||||
|
||||
// 가입비 정보
|
||||
$table->decimal('membership_fee', 12, 2)->nullable()->comment('가입비');
|
||||
$table->timestamp('membership_paid_at')->nullable()->comment('가입비 입금일');
|
||||
$table->enum('membership_status', ['pending', 'partial', 'paid', 'refunded'])->nullable()->comment('가입비 상태');
|
||||
|
||||
// 수당 정보
|
||||
$table->decimal('sales_commission', 12, 2)->nullable()->comment('영업 수당');
|
||||
$table->decimal('manager_commission', 12, 2)->nullable()->comment('관리 수당');
|
||||
$table->timestamp('commission_paid_at')->nullable()->comment('수당 지급일');
|
||||
$table->enum('commission_status', ['pending', 'approved', 'paid'])->nullable()->comment('수당 상태');
|
||||
|
||||
// 진행률 캐시
|
||||
$table->unsignedTinyInteger('sales_progress')->default(0)->comment('영업 시나리오 진행률 (%)');
|
||||
$table->unsignedTinyInteger('manager_progress')->default(0)->comment('매니저 시나리오 진행률 (%)');
|
||||
|
||||
$table->text('notes')->nullable()->comment('메모');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index('sales_partner_id');
|
||||
$table->index('manager_user_id');
|
||||
$table->index('status');
|
||||
$table->index('contracted_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_tenant_managements');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* 영업 시나리오 체크리스트 진행 상태 테이블
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_scenario_checklists', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->enum('scenario_type', ['sales', 'manager'])->comment('시나리오 유형');
|
||||
$table->unsignedTinyInteger('step_id')->comment('단계 ID (1-6)');
|
||||
$table->string('checkpoint_id', 50)->comment('체크포인트 ID');
|
||||
|
||||
$table->boolean('is_checked')->default(false)->comment('체크 여부');
|
||||
$table->timestamp('checked_at')->nullable()->comment('체크 일시');
|
||||
$table->unsignedBigInteger('checked_by')->nullable()->comment('체크한 사용자 ID');
|
||||
|
||||
$table->text('memo')->nullable()->comment('메모');
|
||||
$table->timestamps();
|
||||
|
||||
// 복합 유니크 키 (테넌트 + 시나리오타입 + 단계 + 체크포인트)
|
||||
$table->unique(['tenant_id', 'scenario_type', 'step_id', 'checkpoint_id'], 'unique_checklist_item');
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'scenario_type']);
|
||||
$table->index('checked_by');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_scenario_checklists');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* 영업 상담 기록 테이블 (텍스트, 음성, 첨부파일)
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_consultations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->enum('scenario_type', ['sales', 'manager'])->comment('시나리오 유형');
|
||||
$table->unsignedTinyInteger('step_id')->nullable()->comment('관련 단계 ID (1-6)');
|
||||
|
||||
// 상담 유형 및 내용
|
||||
$table->enum('consultation_type', ['text', 'audio', 'file'])->comment('상담 유형');
|
||||
$table->text('content')->nullable()->comment('텍스트 내용');
|
||||
|
||||
// 파일 정보 (음성/첨부파일)
|
||||
$table->string('file_path', 500)->nullable()->comment('파일 경로');
|
||||
$table->string('file_name', 255)->nullable()->comment('원본 파일명');
|
||||
$table->unsignedInteger('file_size')->nullable()->comment('파일 크기 (bytes)');
|
||||
$table->string('file_type', 100)->nullable()->comment('MIME 타입');
|
||||
|
||||
// 음성 관련
|
||||
$table->text('transcript')->nullable()->comment('음성 변환 텍스트 (STT)');
|
||||
$table->unsignedInteger('duration')->nullable()->comment('녹음 길이 (초)');
|
||||
|
||||
// 작성자
|
||||
$table->unsignedBigInteger('created_by')->comment('작성자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'scenario_type']);
|
||||
$table->index('consultation_type');
|
||||
$table->index('step_id');
|
||||
$table->index('created_by');
|
||||
$table->index('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_consultations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* sales_scenario_checklists 테이블에 필요한 컬럼 추가
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_scenario_checklists', function (Blueprint $table) {
|
||||
// scenario_type 컬럼 추가 (sales/manager 구분)
|
||||
if (!Schema::hasColumn('sales_scenario_checklists', 'scenario_type')) {
|
||||
$table->enum('scenario_type', ['sales', 'manager'])->default('sales')->after('tenant_id')->comment('시나리오 유형');
|
||||
}
|
||||
|
||||
// checkpoint_id 컬럼 추가 (문자열 ID)
|
||||
if (!Schema::hasColumn('sales_scenario_checklists', 'checkpoint_id')) {
|
||||
$table->string('checkpoint_id', 50)->nullable()->after('step_id')->comment('체크포인트 ID');
|
||||
}
|
||||
|
||||
// checked_at 컬럼 추가
|
||||
if (!Schema::hasColumn('sales_scenario_checklists', 'checked_at')) {
|
||||
$table->timestamp('checked_at')->nullable()->after('is_checked')->comment('체크 일시');
|
||||
}
|
||||
|
||||
// checked_by 컬럼 추가 (user_id를 대체)
|
||||
if (!Schema::hasColumn('sales_scenario_checklists', 'checked_by')) {
|
||||
$table->unsignedBigInteger('checked_by')->nullable()->after('checked_at')->comment('체크한 사용자 ID');
|
||||
}
|
||||
|
||||
// memo 컬럼 추가
|
||||
if (!Schema::hasColumn('sales_scenario_checklists', 'memo')) {
|
||||
$table->text('memo')->nullable()->after('checked_by')->comment('메모');
|
||||
}
|
||||
});
|
||||
|
||||
// 인덱스 추가 (존재 여부 확인)
|
||||
$indexExists = DB::select("SHOW INDEX FROM sales_scenario_checklists WHERE Key_name = 'sales_scenario_checklists_tenant_id_scenario_type_index'");
|
||||
if (empty($indexExists)) {
|
||||
Schema::table('sales_scenario_checklists', function (Blueprint $table) {
|
||||
$table->index(['tenant_id', 'scenario_type']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_scenario_checklists', function (Blueprint $table) {
|
||||
$columns = ['scenario_type', 'checkpoint_id', 'checked_at', 'checked_by', 'memo'];
|
||||
foreach ($columns as $column) {
|
||||
if (Schema::hasColumn('sales_scenario_checklists', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_tenant_managements', function (Blueprint $table) {
|
||||
// 본사 진행 상태
|
||||
$table->string('hq_status', 20)->default('pending')->after('manager_progress')
|
||||
->comment('본사 진행 상태: pending, review, planning, coding, dev_test, dev_done, int_test, handover');
|
||||
|
||||
// 수당 지급 상태
|
||||
$table->string('incentive_status', 20)->default('pending')->after('hq_status')
|
||||
->comment('수당 지급 상태: pending, eligible, paid');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_tenant_managements', function (Blueprint $table) {
|
||||
$table->dropColumn(['hq_status', 'incentive_status']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// 상품 카테고리 테이블
|
||||
Schema::create('sales_product_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('code', 50)->unique()->comment('카테고리 코드 (manufacturing, construction)');
|
||||
$table->string('name', 100)->comment('카테고리명 (제조 업체, 공사 업체)');
|
||||
$table->text('description')->nullable()->comment('설명');
|
||||
$table->string('base_storage', 20)->default('100GB')->comment('기본 제공 용량');
|
||||
$table->integer('display_order')->default(0)->comment('표시 순서');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
// 상품 테이블
|
||||
Schema::create('sales_products', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained('sales_product_categories')->comment('카테고리 FK');
|
||||
$table->string('code', 50)->comment('상품 코드');
|
||||
$table->string('name', 100)->comment('상품명');
|
||||
$table->text('description')->nullable()->comment('프로그램 타입/설명');
|
||||
$table->decimal('development_fee', 15, 2)->default(0)->comment('개발비 (가입비)');
|
||||
$table->decimal('subscription_fee', 15, 2)->default(0)->comment('월 구독료');
|
||||
$table->decimal('commission_rate', 5, 2)->default(25.00)->comment('수당 비율 (%)');
|
||||
$table->boolean('allow_flexible_pricing')->default(true)->comment('재량권 허용');
|
||||
$table->boolean('is_required')->default(false)->comment('필수 선택 여부');
|
||||
$table->integer('display_order')->default(0)->comment('표시 순서');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unique(['category_id', 'code'], 'uk_category_code');
|
||||
});
|
||||
|
||||
// 계약별 선택 상품 테이블
|
||||
Schema::create('sales_contract_products', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('tenant_id')->constrained('tenants')->comment('테넌트 FK');
|
||||
$table->foreignId('management_id')->constrained('sales_tenant_managements')->comment('영업관리 FK');
|
||||
$table->foreignId('category_id')->constrained('sales_product_categories')->comment('선택한 카테고리');
|
||||
$table->foreignId('product_id')->constrained('sales_products')->comment('선택한 상품');
|
||||
$table->decimal('development_fee', 15, 2)->nullable()->comment('적용된 개발비 (협상 가능)');
|
||||
$table->decimal('subscription_fee', 15, 2)->nullable()->comment('적용된 구독료');
|
||||
$table->decimal('discount_rate', 5, 2)->default(0)->comment('할인율 (%)');
|
||||
$table->text('notes')->nullable()->comment('비고');
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->comment('등록자');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['tenant_id', 'category_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_contract_products');
|
||||
Schema::dropIfExists('sales_products');
|
||||
Schema::dropIfExists('sales_product_categories');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->decimal('partner_commission_rate', 5, 2)->default(20.00)->after('subscription_fee')->comment('영업파트너 수당율(%)');
|
||||
$table->decimal('manager_commission_rate', 5, 2)->default(5.00)->after('partner_commission_rate')->comment('매니저 수당율(%)');
|
||||
});
|
||||
|
||||
// 기존 데이터 업데이트: commission_rate 25% → partner 20%, manager 5%
|
||||
DB::table('sales_products')->update([
|
||||
'partner_commission_rate' => 20.00,
|
||||
'manager_commission_rate' => 5.00,
|
||||
]);
|
||||
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->dropColumn('commission_rate');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->decimal('commission_rate', 5, 2)->default(25.00)->after('subscription_fee')->comment('수당율(%)');
|
||||
});
|
||||
|
||||
// 롤백 시 partner + manager 합산으로 복원
|
||||
DB::statement('UPDATE sales_products SET commission_rate = partner_commission_rate + manager_commission_rate');
|
||||
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->dropColumn(['partner_commission_rate', 'manager_commission_rate']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->decimal('registration_fee', 15, 2)->default(0)->after('development_fee')->comment('가입비 (할인된 가격)');
|
||||
});
|
||||
|
||||
// 기존 데이터: 가입비 = 개발비 × 25%
|
||||
DB::statement('UPDATE sales_products SET registration_fee = development_fee * 0.25');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_products', function (Blueprint $table) {
|
||||
$table->dropColumn('registration_fee');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_contract_products', function (Blueprint $table) {
|
||||
$table->renameColumn('development_fee', 'registration_fee');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_contract_products', function (Blueprint $table) {
|
||||
$table->renameColumn('registration_fee', 'development_fee');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 영업수수료 정산 테이블
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_commissions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('management_id')->comment('영업 관리 ID (sales_tenant_managements)');
|
||||
|
||||
// 입금 정보
|
||||
$table->enum('payment_type', ['deposit', 'balance'])->comment('입금 구분 (deposit:계약금, balance:잔금)');
|
||||
$table->decimal('payment_amount', 14, 2)->comment('입금액');
|
||||
$table->date('payment_date')->comment('입금일');
|
||||
|
||||
// 수당 계산 기준
|
||||
$table->decimal('base_amount', 14, 2)->comment('수당 계산 기준액 (가입비의 50%)');
|
||||
$table->decimal('partner_rate', 5, 2)->default(20.00)->comment('영업파트너 수당률 (%)');
|
||||
$table->decimal('manager_rate', 5, 2)->default(5.00)->comment('매니저 수당률 (%)');
|
||||
$table->decimal('partner_commission', 14, 2)->comment('영업파트너 수당액');
|
||||
$table->decimal('manager_commission', 14, 2)->comment('매니저 수당액');
|
||||
|
||||
// 지급 정보
|
||||
$table->date('scheduled_payment_date')->comment('지급예정일 (익월 10일)');
|
||||
$table->enum('status', ['pending', 'approved', 'paid', 'cancelled'])
|
||||
->default('pending')
|
||||
->comment('상태 (pending:대기, approved:승인, paid:지급완료, cancelled:취소)');
|
||||
$table->date('actual_payment_date')->nullable()->comment('실제 지급일');
|
||||
|
||||
// 대상자 정보
|
||||
$table->unsignedBigInteger('partner_id')->comment('영업파트너 ID (sales_partners)');
|
||||
$table->unsignedBigInteger('manager_user_id')->nullable()->comment('매니저 사용자 ID');
|
||||
|
||||
// 부가 정보
|
||||
$table->text('notes')->nullable()->comment('메모');
|
||||
$table->string('bank_reference', 100)->nullable()->comment('이체 참조번호');
|
||||
|
||||
// 승인 정보
|
||||
$table->unsignedBigInteger('approved_by')->nullable()->comment('승인자 ID');
|
||||
$table->timestamp('approved_at')->nullable()->comment('승인일시');
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id');
|
||||
$table->index('management_id');
|
||||
$table->index('partner_id');
|
||||
$table->index('manager_user_id');
|
||||
$table->index('payment_type');
|
||||
$table->index('payment_date');
|
||||
$table->index('scheduled_payment_date');
|
||||
$table->index('status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_commissions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 영업수수료 상세 테이블 (상품별 수당 내역)
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sales_commission_details', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('commission_id')->comment('수수료 정산 ID');
|
||||
$table->unsignedBigInteger('contract_product_id')->comment('계약 상품 ID (sales_contract_products)');
|
||||
|
||||
// 상품별 수당 계산
|
||||
$table->decimal('registration_fee', 14, 2)->comment('상품 가입비');
|
||||
$table->decimal('base_amount', 14, 2)->comment('수당 계산 기준액 (가입비의 50%)');
|
||||
$table->decimal('partner_rate', 5, 2)->comment('영업파트너 수당률 (%)');
|
||||
$table->decimal('manager_rate', 5, 2)->comment('매니저 수당률 (%)');
|
||||
$table->decimal('partner_commission', 14, 2)->comment('영업파트너 수당액');
|
||||
$table->decimal('manager_commission', 14, 2)->comment('매니저 수당액');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
// 인덱스 및 외래키
|
||||
$table->index('commission_id');
|
||||
$table->index('contract_product_id');
|
||||
|
||||
$table->foreign('commission_id')
|
||||
->references('id')
|
||||
->on('sales_commissions')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sales_commission_details');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 영업관리 테이블에 입금 정보 컬럼 추가
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sales_tenant_managements', function (Blueprint $table) {
|
||||
// 계약금 정보
|
||||
$table->decimal('deposit_amount', 14, 2)->nullable()->after('membership_status')->comment('계약금');
|
||||
$table->date('deposit_paid_date')->nullable()->after('deposit_amount')->comment('계약금 입금일');
|
||||
$table->enum('deposit_status', ['pending', 'paid'])
|
||||
->default('pending')
|
||||
->after('deposit_paid_date')
|
||||
->comment('계약금 상태 (pending:대기, paid:입금완료)');
|
||||
|
||||
// 잔금 정보
|
||||
$table->decimal('balance_amount', 14, 2)->nullable()->after('deposit_status')->comment('잔금');
|
||||
$table->date('balance_paid_date')->nullable()->after('balance_amount')->comment('잔금 입금일');
|
||||
$table->enum('balance_status', ['pending', 'paid'])
|
||||
->default('pending')
|
||||
->after('balance_paid_date')
|
||||
->comment('잔금 상태 (pending:대기, paid:입금완료)');
|
||||
|
||||
// 총 가입비 (계약 상품 합계, 캐시용)
|
||||
$table->decimal('total_registration_fee', 14, 2)->nullable()->after('balance_status')->comment('총 가입비');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sales_tenant_managements', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'deposit_amount',
|
||||
'deposit_paid_date',
|
||||
'deposit_status',
|
||||
'balance_amount',
|
||||
'balance_paid_date',
|
||||
'balance_status',
|
||||
'total_registration_fee',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
217
database/seeders/SalesProductSeeder.php
Normal file
217
database/seeders/SalesProductSeeder.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 영업 상품 초기 데이터 시더
|
||||
*
|
||||
* 개발비: 원래 가격 (예: 80,000,000원)
|
||||
* 가입비: 개발비의 25% (예: 20,000,000원) - 할인된 가격으로 표시
|
||||
*/
|
||||
class SalesProductSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
DB::transaction(function () {
|
||||
// 기존 데이터 삭제
|
||||
DB::table('sales_contract_products')->delete();
|
||||
DB::table('sales_products')->delete();
|
||||
DB::table('sales_product_categories')->delete();
|
||||
|
||||
// 카테고리 생성
|
||||
$categories = [
|
||||
[
|
||||
'code' => 'MANUFACTURER',
|
||||
'name' => '제조 업체',
|
||||
'description' => '제조업 전용 SAM 솔루션 상품군',
|
||||
'base_storage' => '100GB',
|
||||
'display_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'CONSTRUCTION',
|
||||
'name' => '공사 업체',
|
||||
'description' => '공사/시공업 전용 SAM 솔루션 상품군',
|
||||
'base_storage' => '100GB',
|
||||
'display_order' => 2,
|
||||
],
|
||||
];
|
||||
|
||||
$categoryIds = [];
|
||||
foreach ($categories as $category) {
|
||||
$categoryIds[$category['code']] = DB::table('sales_product_categories')->insertGetId([
|
||||
...$category,
|
||||
'is_active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
// 제조업체 상품 (8개)
|
||||
$manufacturerProducts = [
|
||||
[
|
||||
'code' => 'MFG_BASIC',
|
||||
'name' => '기본 / PC + 모바일 사용 겸용',
|
||||
'description' => '일정관리+근태+재고+견적+발주+생산공정 1개+출고+회계+신용조회+대표자 화면 (보고서+전자결제 음성알림)',
|
||||
'development_fee' => 80000000,
|
||||
'subscription_fee' => 500000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => true,
|
||||
'display_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_QUALITY',
|
||||
'name' => '품질관리',
|
||||
'description' => '로트관리 + 사진등록기능 + 설비 관리(QR)',
|
||||
'development_fee' => 80000000,
|
||||
'subscription_fee' => 500000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 2,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_PROCESS_ADD',
|
||||
'name' => '생산 공정 1개 추가시',
|
||||
'description' => '별도의 작업지시서',
|
||||
'development_fee' => 20000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 3,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_AI',
|
||||
'name' => 'SAM 봇 / AI기능',
|
||||
'description' => '음성녹음-->텍스트 변환 (회의록+업무일지) + 프로그램 내 검색 기능',
|
||||
'development_fee' => 20000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 4,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_PHOTO',
|
||||
'name' => '사진등록기능',
|
||||
'description' => '현장 또는 원하는 포인트에 촬영 --> 프로그램에 바로 등록 (맞춤형)',
|
||||
'development_fee' => 10000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 5,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_INVOICE_CARD',
|
||||
'name' => '계산서 + 카드 관리',
|
||||
'description' => '계산서 발행 (월 100건 기준 / 초과시 추가 5만원) + 법인카드 (접대비+복리후생비+가지급금 관리)',
|
||||
'development_fee' => 10000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 6,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_EQUIPMENT',
|
||||
'name' => '설비 관리 (QR)',
|
||||
'description' => '관리 프로그램',
|
||||
'development_fee' => 10000000,
|
||||
'subscription_fee' => 50000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 7,
|
||||
],
|
||||
[
|
||||
'code' => 'MFG_RND',
|
||||
'name' => '기업부설연구소',
|
||||
'description' => '관리 프로그램',
|
||||
'development_fee' => 10000000,
|
||||
'subscription_fee' => 50000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 8,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($manufacturerProducts as $product) {
|
||||
DB::table('sales_products')->insert([
|
||||
'category_id' => $categoryIds['MANUFACTURER'],
|
||||
...$product,
|
||||
'registration_fee' => $product['development_fee'] * 0.25,
|
||||
'is_active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
// 공사업체 상품 (3개)
|
||||
$constructionProducts = [
|
||||
[
|
||||
'code' => 'CON_BASIC',
|
||||
'name' => '기본 / PC + 모바일 사용 겸용',
|
||||
'description' => '일정관리+근태+견적+발주+공사관리+기성+회계+신용조회+대표자 화면 (보고서+전자결제 음성알림)',
|
||||
'development_fee' => 80000000,
|
||||
'subscription_fee' => 500000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => true,
|
||||
'display_order' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'CON_AI',
|
||||
'name' => 'SAM 봇 / AI기능',
|
||||
'description' => '음성녹음-->텍스트 변환 (회의록+업무일지) + 프로그램 내 검색 기능',
|
||||
'development_fee' => 20000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 2,
|
||||
],
|
||||
[
|
||||
'code' => 'CON_PHOTO',
|
||||
'name' => '사진등록기능',
|
||||
'description' => '현장 또는 원하는 포인트에 촬영 --> 프로그램에 바로 등록 (맞춤형)',
|
||||
'development_fee' => 10000000,
|
||||
'subscription_fee' => 100000,
|
||||
'partner_commission_rate' => 20,
|
||||
'manager_commission_rate' => 5,
|
||||
'allow_flexible_pricing' => true,
|
||||
'is_required' => false,
|
||||
'display_order' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($constructionProducts as $product) {
|
||||
DB::table('sales_products')->insert([
|
||||
'category_id' => $categoryIds['CONSTRUCTION'],
|
||||
...$product,
|
||||
'registration_fee' => $product['development_fee'] * 0.25,
|
||||
'is_active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
$this->command->info('✅ 영업 상품 초기 데이터 생성 완료 (2개 카테고리, 11개 상품)');
|
||||
}
|
||||
}
|
||||
9
fix_git_sync.sh
Normal file
9
fix_git_sync.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
echo "Stopping file mode tracking..."
|
||||
git config core.filemode false
|
||||
echo "Resetting unintentional changes..."
|
||||
git checkout .
|
||||
echo "Pulling remote changes with rebase..."
|
||||
git pull --rebase origin develop
|
||||
echo "Pushing synchronized changes..."
|
||||
git push
|
||||
Reference in New Issue
Block a user