From 597d24eb19621aa21402f1da6155185c478a5ab5 Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 15:03:57 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=EB=B0=94=EB=A1=9C=EB=B9=8C=20?= =?UTF-8?q?=EA=B3=BC=EA=B8=88=EA=B4=80=EB=A6=AC=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - barobill_subscriptions: 월정액 구독 관리 - barobill_billing_records: 과금 내역 기록 - barobill_monthly_summaries: 월별 집계 테이블 Co-Authored-By: Claude Opus 4.5 --- ...00_create_barobill_subscriptions_table.php | 46 ++++++++++++++++ ..._create_barobill_billing_records_table.php | 52 ++++++++++++++++++ ...reate_barobill_monthly_summaries_table.php | 53 +++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 database/migrations/2026_01_27_150000_create_barobill_subscriptions_table.php create mode 100644 database/migrations/2026_01_27_150100_create_barobill_billing_records_table.php create mode 100644 database/migrations/2026_01_27_150200_create_barobill_monthly_summaries_table.php diff --git a/database/migrations/2026_01_27_150000_create_barobill_subscriptions_table.php b/database/migrations/2026_01_27_150000_create_barobill_subscriptions_table.php new file mode 100644 index 0000000..ebcd126 --- /dev/null +++ b/database/migrations/2026_01_27_150000_create_barobill_subscriptions_table.php @@ -0,0 +1,46 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_27_150100_create_barobill_billing_records_table.php b/database/migrations/2026_01_27_150100_create_barobill_billing_records_table.php new file mode 100644 index 0000000..1023eec --- /dev/null +++ b/database/migrations/2026_01_27_150100_create_barobill_billing_records_table.php @@ -0,0 +1,52 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_27_150200_create_barobill_monthly_summaries_table.php b/database/migrations/2026_01_27_150200_create_barobill_monthly_summaries_table.php new file mode 100644 index 0000000..00b2b63 --- /dev/null +++ b/database/migrations/2026_01_27_150200_create_barobill_monthly_summaries_table.php @@ -0,0 +1,53 @@ +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'); + } +}; From 4106f59cd1459798623dab544a605c85ccb1c683 Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 15:17:34 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=EB=B0=94=EB=A1=9C=EB=B9=8C=20?= =?UTF-8?q?=EA=B3=BC=EA=B8=88=20=EC=A0=95=EC=B1=85=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - barobill_pricing_policies 테이블 생성 - 서비스 유형별 과금 정책 저장 (무료 제공량, 추가 과금 단위/금액) Co-Authored-By: Claude Opus 4.5 --- ...create_barobill_pricing_policies_table.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 database/migrations/2026_01_27_160000_create_barobill_pricing_policies_table.php diff --git a/database/migrations/2026_01_27_160000_create_barobill_pricing_policies_table.php b/database/migrations/2026_01_27_160000_create_barobill_pricing_policies_table.php new file mode 100644 index 0000000..e744058 --- /dev/null +++ b/database/migrations/2026_01_27_160000_create_barobill_pricing_policies_table.php @@ -0,0 +1,46 @@ +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'); + } +}; From 5de77e3b354d6580a8faf51b6925cd95e53437b6 Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 20:06:36 +0900 Subject: [PATCH 03/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=EC=9E=90=20User=20=ED=86=B5=ED=95=A9=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - users 테이블에 parent_id, approval_status, approved_by, approved_at, rejection_reason 컬럼 추가 - sales_manager_documents 테이블 생성 (멀티파일 업로드) Co-Authored-By: Claude Opus 4.5 --- ...dd_sales_manager_fields_to_users_table.php | 52 ++++++++++++++++++ ...0_create_sales_manager_documents_table.php | 53 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 database/migrations/2026_01_27_200000_add_sales_manager_fields_to_users_table.php create mode 100644 database/migrations/2026_01_27_200100_create_sales_manager_documents_table.php diff --git a/database/migrations/2026_01_27_200000_add_sales_manager_fields_to_users_table.php b/database/migrations/2026_01_27_200000_add_sales_manager_fields_to_users_table.php new file mode 100644 index 0000000..7a92744 --- /dev/null +++ b/database/migrations/2026_01_27_200000_add_sales_manager_fields_to_users_table.php @@ -0,0 +1,52 @@ +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', + ]); + }); + } +}; diff --git a/database/migrations/2026_01_27_200100_create_sales_manager_documents_table.php b/database/migrations/2026_01_27_200100_create_sales_manager_documents_table.php new file mode 100644 index 0000000..38426ed --- /dev/null +++ b/database/migrations/2026_01_27_200100_create_sales_manager_documents_table.php @@ -0,0 +1,53 @@ +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'); + } +}; From 9182cbc1b3666c43bda5b523ee7e008469ba1234 Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 22:39:30 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=EA=B6=8C(?= =?UTF-8?q?=EB=AA=85=ED=95=A8=EB=93=B1=EB=A1=9D)=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tenant_prospects 테이블 생성 - 영업권 2개월 유효, 1개월 쿨다운 정책 지원 - 테넌트 전환 추적 기능 포함 Co-Authored-By: Claude Opus 4.5 --- ...7_221000_create_tenant_prospects_table.php | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 database/migrations/2026_01_27_221000_create_tenant_prospects_table.php diff --git a/database/migrations/2026_01_27_221000_create_tenant_prospects_table.php b/database/migrations/2026_01_27_221000_create_tenant_prospects_table.php new file mode 100644 index 0000000..3b7a7de --- /dev/null +++ b/database/migrations/2026_01_27_221000_create_tenant_prospects_table.php @@ -0,0 +1,75 @@ +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'); + } +}; From 4839cfcad22b8604ed355d76ff3d1cae156dc46b Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 23:00:43 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:ai=5Fconfigs=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AI API 설정 테이블 (Gemini, Claude, OpenAI 지원) - provider별 활성화 상태 관리 - 명함 OCR 시스템을 위한 기반 구조 --- ...6_01_27_100000_create_ai_configs_table.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 database/migrations/2026_01_27_100000_create_ai_configs_table.php diff --git a/database/migrations/2026_01_27_100000_create_ai_configs_table.php b/database/migrations/2026_01_27_100000_create_ai_configs_table.php new file mode 100644 index 0000000..e4ddce6 --- /dev/null +++ b/database/migrations/2026_01_27_100000_create_ai_configs_table.php @@ -0,0 +1,39 @@ +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'); + } +}; From 3e8570ac3f8aa5766920abb9673ee41ca15f782d Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 23:36:32 +0900 Subject: [PATCH 06/19] =?UTF-8?q?feat:sales=5Fprospects=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20business=5Fcard=5Fimage=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 명함 이미지 저장을 위한 컬럼 추가 Co-Authored-By: Claude Opus 4.5 --- ...ss_card_image_to_sales_prospects_table.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 database/migrations/2026_01_27_234500_add_business_card_image_to_sales_prospects_table.php diff --git a/database/migrations/2026_01_27_234500_add_business_card_image_to_sales_prospects_table.php b/database/migrations/2026_01_27_234500_add_business_card_image_to_sales_prospects_table.php new file mode 100644 index 0000000..2007daf --- /dev/null +++ b/database/migrations/2026_01_27_234500_add_business_card_image_to_sales_prospects_table.php @@ -0,0 +1,29 @@ +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'); + }); + } +}; From a88f8a2021712adf32d41304a44194c9adf143eb Mon Sep 17 00:00:00 2001 From: pro Date: Tue, 27 Jan 2026 23:42:20 +0900 Subject: [PATCH 07/19] =?UTF-8?q?feat:sales=5Fprospects=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=8B=A0=EB=B6=84=EC=A6=9D/?= =?UTF-8?q?=ED=86=B5=EC=9E=A5=EC=82=AC=EB=B3=B8=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - id_card_image: 신분증 사본 이미지 경로 - bankbook_image: 통장 사본 이미지 경로 Co-Authored-By: Claude Opus 4.5 --- ...ankbook_image_to_sales_prospects_table.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 database/migrations/2026_01_27_235000_add_id_card_and_bankbook_image_to_sales_prospects_table.php diff --git a/database/migrations/2026_01_27_235000_add_id_card_and_bankbook_image_to_sales_prospects_table.php b/database/migrations/2026_01_27_235000_add_id_card_and_bankbook_image_to_sales_prospects_table.php new file mode 100644 index 0000000..87981ce --- /dev/null +++ b/database/migrations/2026_01_27_235000_add_id_card_and_bankbook_image_to_sales_prospects_table.php @@ -0,0 +1,31 @@ +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']); + }); + } +}; From 6e553ce3c990e861f76209ee3c695971c5e2fa76 Mon Sep 17 00:00:00 2001 From: pro Date: Wed, 28 Jan 2026 08:49:40 +0900 Subject: [PATCH 08/19] =?UTF-8?q?feat:tenant=5Fprospects=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=B2=A8=EB=B6=80=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - id_card_path: 신분증 사본 경로 - bankbook_path: 통장 사본 경로 Co-Authored-By: Claude Opus 4.5 --- LOGICAL_RELATIONSHIPS.md | 2 +- ..._attachments_to_tenant_prospects_table.php | 23 +++++++++++++++++++ fix_git_sync.sh | 9 ++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_01_28_090000_add_attachments_to_tenant_prospects_table.php create mode 100644 fix_git_sync.sh diff --git a/LOGICAL_RELATIONSHIPS.md b/LOGICAL_RELATIONSHIPS.md index aa05c5d..d6211e5 100644 --- a/LOGICAL_RELATIONSHIPS.md +++ b/LOGICAL_RELATIONSHIPS.md @@ -1,6 +1,6 @@ # 논리적 데이터베이스 관계 문서 -> **자동 생성**: 2026-01-26 16:11:41 +> **자동 생성**: 2026-01-28 08:49:03 > **소스**: Eloquent 모델 관계 분석 ## 📊 모델별 관계 현황 diff --git a/database/migrations/2026_01_28_090000_add_attachments_to_tenant_prospects_table.php b/database/migrations/2026_01_28_090000_add_attachments_to_tenant_prospects_table.php new file mode 100644 index 0000000..be7f9ec --- /dev/null +++ b/database/migrations/2026_01_28_090000_add_attachments_to_tenant_prospects_table.php @@ -0,0 +1,23 @@ +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']); + }); + } +}; diff --git a/fix_git_sync.sh b/fix_git_sync.sh new file mode 100644 index 0000000..65883ed --- /dev/null +++ b/fix_git_sync.sh @@ -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 From f163373fb074f902800a0602d4e6c44c5c187071 Mon Sep 17 00:00:00 2001 From: pro Date: Wed, 28 Jan 2026 16:49:31 +0900 Subject: [PATCH 09/19] =?UTF-8?q?feat:credit=5Finquiries=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20tenant=5Fid=20=EC=BB=AC=EB=9F=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 신용평가 조회회수 집계 기능을 위한 테넌트 구분 컬럼 - tenant_id, (tenant_id, inquired_at) 인덱스 추가 Co-Authored-By: Claude Opus 4.5 --- ...dd_tenant_id_to_credit_inquiries_table.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 database/migrations/2026_01_28_163000_add_tenant_id_to_credit_inquiries_table.php diff --git a/database/migrations/2026_01_28_163000_add_tenant_id_to_credit_inquiries_table.php b/database/migrations/2026_01_28_163000_add_tenant_id_to_credit_inquiries_table.php new file mode 100644 index 0000000..52ec02f --- /dev/null +++ b/database/migrations/2026_01_28_163000_add_tenant_id_to_credit_inquiries_table.php @@ -0,0 +1,26 @@ +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'); + }); + } +}; From 6208d90244018f1d768b358b61cb828a1085e869 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 06:42:25 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sales_partners: 영업 파트너 정보 - sales_tenant_managements: 테넌트별 영업 관리 (tenant_id FK) - sales_scenario_checklists: 시나리오 체크리스트 - sales_consultations: 상담 기록 (텍스트/음성/파일) Co-Authored-By: Claude Opus 4.5 --- ..._29_100000_create_sales_partners_table.php | 57 ++++++++++++++ ..._create_sales_tenant_managements_table.php | 75 +++++++++++++++++++ ...create_sales_scenario_checklists_table.php | 45 +++++++++++ ...00300_create_sales_consultations_table.php | 56 ++++++++++++++ ...mns_to_sales_scenario_checklists_table.php | 66 ++++++++++++++++ 5 files changed, 299 insertions(+) create mode 100644 database/migrations/2026_01_29_100000_create_sales_partners_table.php create mode 100644 database/migrations/2026_01_29_100100_create_sales_tenant_managements_table.php create mode 100644 database/migrations/2026_01_29_100200_create_sales_scenario_checklists_table.php create mode 100644 database/migrations/2026_01_29_100300_create_sales_consultations_table.php create mode 100644 database/migrations/2026_01_29_100400_add_columns_to_sales_scenario_checklists_table.php diff --git a/database/migrations/2026_01_29_100000_create_sales_partners_table.php b/database/migrations/2026_01_29_100000_create_sales_partners_table.php new file mode 100644 index 0000000..6cd3018 --- /dev/null +++ b/database/migrations/2026_01_29_100000_create_sales_partners_table.php @@ -0,0 +1,57 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_100100_create_sales_tenant_managements_table.php b/database/migrations/2026_01_29_100100_create_sales_tenant_managements_table.php new file mode 100644 index 0000000..320d387 --- /dev/null +++ b/database/migrations/2026_01_29_100100_create_sales_tenant_managements_table.php @@ -0,0 +1,75 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_100200_create_sales_scenario_checklists_table.php b/database/migrations/2026_01_29_100200_create_sales_scenario_checklists_table.php new file mode 100644 index 0000000..61d215e --- /dev/null +++ b/database/migrations/2026_01_29_100200_create_sales_scenario_checklists_table.php @@ -0,0 +1,45 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_100300_create_sales_consultations_table.php b/database/migrations/2026_01_29_100300_create_sales_consultations_table.php new file mode 100644 index 0000000..360f3fe --- /dev/null +++ b/database/migrations/2026_01_29_100300_create_sales_consultations_table.php @@ -0,0 +1,56 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_100400_add_columns_to_sales_scenario_checklists_table.php b/database/migrations/2026_01_29_100400_add_columns_to_sales_scenario_checklists_table.php new file mode 100644 index 0000000..5df33e1 --- /dev/null +++ b/database/migrations/2026_01_29_100400_add_columns_to_sales_scenario_checklists_table.php @@ -0,0 +1,66 @@ +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); + } + } + }); + } +}; From 4c7cf85ef90c5fa13aa2f6a27750a11f80212135 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 09:00:07 +0900 Subject: [PATCH 11/19] =?UTF-8?q?fix:=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4?= =?UTF-8?q?=20=EC=B2=B4=ED=81=AC=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9C=A0?= =?UTF-8?q?=EB=8B=88=ED=81=AC=20=ED=82=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 유니크 키 (tenant_id, user_id, step_id, checkpoint_index) 삭제 - 새 유니크 키 (tenant_id, scenario_type, step_id, checkpoint_id) 생성 - checkpoint_index를 nullable로 변경 Co-Authored-By: Claude Opus 4.5 --- LOGICAL_RELATIONSHIPS.md | 2 +- ...x_sales_scenario_checklists_unique_key.php | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_01_29_090000_fix_sales_scenario_checklists_unique_key.php diff --git a/LOGICAL_RELATIONSHIPS.md b/LOGICAL_RELATIONSHIPS.md index d6211e5..bafc746 100644 --- a/LOGICAL_RELATIONSHIPS.md +++ b/LOGICAL_RELATIONSHIPS.md @@ -1,6 +1,6 @@ # 논리적 데이터베이스 관계 문서 -> **자동 생성**: 2026-01-28 08:49:03 +> **자동 생성**: 2026-01-29 08:59:43 > **소스**: Eloquent 모델 관계 분석 ## 📊 모델별 관계 현황 diff --git a/database/migrations/2026_01_29_090000_fix_sales_scenario_checklists_unique_key.php b/database/migrations/2026_01_29_090000_fix_sales_scenario_checklists_unique_key.php new file mode 100644 index 0000000..fee45fd --- /dev/null +++ b/database/migrations/2026_01_29_090000_fix_sales_scenario_checklists_unique_key.php @@ -0,0 +1,48 @@ +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' + ); + }); + } +}; From 27a558dafb3219dad0e590ed6d17278caed4e53f Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 09:15:22 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:sales=5Fconsultations=EC=97=90=20gcs?= =?UTF-8?q?=5Furi=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 음성 녹음 파일의 Google Cloud Storage URI 저장용 Co-Authored-By: Claude Opus 4.5 --- LOGICAL_RELATIONSHIPS.md | 2 +- ...000_add_gcs_uri_to_sales_consultations.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_01_29_093000_add_gcs_uri_to_sales_consultations.php diff --git a/LOGICAL_RELATIONSHIPS.md b/LOGICAL_RELATIONSHIPS.md index bafc746..a75a39c 100644 --- a/LOGICAL_RELATIONSHIPS.md +++ b/LOGICAL_RELATIONSHIPS.md @@ -1,6 +1,6 @@ # 논리적 데이터베이스 관계 문서 -> **자동 생성**: 2026-01-29 08:59:43 +> **자동 생성**: 2026-01-29 09:14:06 > **소스**: Eloquent 모델 관계 분석 ## 📊 모델별 관계 현황 diff --git a/database/migrations/2026_01_29_093000_add_gcs_uri_to_sales_consultations.php b/database/migrations/2026_01_29_093000_add_gcs_uri_to_sales_consultations.php new file mode 100644 index 0000000..8ce6499 --- /dev/null +++ b/database/migrations/2026_01_29_093000_add_gcs_uri_to_sales_consultations.php @@ -0,0 +1,29 @@ +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'); + }); + } +}; From c7339ac7db62fb399c25ad863bd64bb8ba0607ef Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 11:39:20 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=EB=B3=B8=EC=82=AC=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=83=81=ED=83=9C=20=EB=B0=8F=20=EC=88=98=EB=8B=B9?= =?UTF-8?q?=20=EC=A7=80=EA=B8=89=20=EC=83=81=ED=83=9C=20=EC=BB=AC=EB=9F=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sales_tenant_managements 테이블: - hq_status: 본사 진행 상태 (pending, review, planning, coding, dev_test, dev_done, int_test, handover) - incentive_status: 수당 지급 상태 (pending, eligible, paid) Co-Authored-By: Claude Opus 4.5 --- ..._hq_status_to_sales_tenant_managements.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 database/migrations/2026_01_29_120000_add_hq_status_to_sales_tenant_managements.php diff --git a/database/migrations/2026_01_29_120000_add_hq_status_to_sales_tenant_managements.php b/database/migrations/2026_01_29_120000_add_hq_status_to_sales_tenant_managements.php new file mode 100644 index 0000000..cb76679 --- /dev/null +++ b/database/migrations/2026_01_29_120000_add_hq_status_to_sales_tenant_managements.php @@ -0,0 +1,34 @@ +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']); + }); + } +}; From e439bfffdac1951273b55785485a106b47622905 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 15:02:07 +0900 Subject: [PATCH 14/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=20=EC=83=81?= =?UTF-8?q?=ED=92=88=EA=B4=80=EB=A6=AC=20DB=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=20=EB=B0=8F=20=EC=8B=9C=EB=8D=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sales_product_categories: 상품 카테고리 테이블 - sales_products: 영업 상품 테이블 - sales_contract_products: 계약별 선택 상품 테이블 - SalesProductSeeder: 제조업체 8개, 공사업체 3개 상품 초기 데이터 Co-Authored-By: Claude Opus 4.5 --- ...29_150000_create_sales_products_tables.php | 74 +++++++ database/seeders/SalesProductSeeder.php | 201 ++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 database/migrations/2026_01_29_150000_create_sales_products_tables.php create mode 100644 database/seeders/SalesProductSeeder.php diff --git a/database/migrations/2026_01_29_150000_create_sales_products_tables.php b/database/migrations/2026_01_29_150000_create_sales_products_tables.php new file mode 100644 index 0000000..43061bc --- /dev/null +++ b/database/migrations/2026_01_29_150000_create_sales_products_tables.php @@ -0,0 +1,74 @@ +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'); + } +}; diff --git a/database/seeders/SalesProductSeeder.php b/database/seeders/SalesProductSeeder.php new file mode 100644 index 0000000..d043734 --- /dev/null +++ b/database/seeders/SalesProductSeeder.php @@ -0,0 +1,201 @@ +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' => '50GB', + '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' => 'SAM 기본 패키지', + 'description' => '생산관리, 재고관리, 주문관리 기본 기능 포함', + 'development_fee' => 3000000, + 'subscription_fee' => 150000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => true, + 'display_order' => 1, + ], + [ + 'code' => 'MFG_PRODUCTION', + 'name' => '생산관리 모듈', + 'description' => '작업지시, 공정관리, 생산실적 관리', + 'development_fee' => 1500000, + 'subscription_fee' => 80000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 2, + ], + [ + 'code' => 'MFG_INVENTORY', + 'name' => '재고관리 모듈', + 'description' => '입출고 관리, 재고실사, 로트 추적', + 'development_fee' => 1200000, + 'subscription_fee' => 60000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 3, + ], + [ + 'code' => 'MFG_QUALITY', + 'name' => '품질관리 모듈', + 'description' => '수입검사, 공정검사, 출하검사', + 'development_fee' => 1000000, + 'subscription_fee' => 50000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 4, + ], + [ + 'code' => 'MFG_ESTIMATE', + 'name' => '견적관리 모듈', + 'description' => '견적 수식 관리, 자동 계산, 견적서 생성', + 'development_fee' => 800000, + 'subscription_fee' => 40000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 5, + ], + [ + 'code' => 'MFG_ACCOUNTING', + 'name' => '회계관리 모듈', + 'description' => '매출/매입 관리, 세금계산서, 자금일보', + 'development_fee' => 1500000, + 'subscription_fee' => 100000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 6, + ], + [ + 'code' => 'MFG_MOBILE', + 'name' => '모바일 앱', + 'description' => '현장용 모바일 앱 (iOS/Android)', + 'development_fee' => 500000, + 'subscription_fee' => 30000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => false, + 'is_required' => false, + 'display_order' => 7, + ], + [ + 'code' => 'MFG_CUSTOM', + 'name' => '커스터마이징', + 'description' => '고객 맞춤 개발 (별도 협의)', + 'development_fee' => 0, + 'subscription_fee' => 0, + 'commission_rate' => 20, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 8, + ], + ]; + + foreach ($manufacturerProducts as $product) { + DB::table('sales_products')->insert([ + 'category_id' => $categoryIds['MANUFACTURER'], + ...$product, + 'is_active' => true, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + // 공사업체 상품 (3개) + $constructionProducts = [ + [ + 'code' => 'CON_BASIC', + 'name' => 'SAM 공사관리 패키지', + 'description' => '현장관리, 공사진행, 자재관리 기본 기능', + 'development_fee' => 2000000, + 'subscription_fee' => 100000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => true, + 'display_order' => 1, + ], + [ + 'code' => 'CON_FIELD', + 'name' => '현장관리 모듈', + 'description' => '현장 일지, 사진 관리, 공정 관리', + 'development_fee' => 1000000, + 'subscription_fee' => 50000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => true, + 'is_required' => false, + 'display_order' => 2, + ], + [ + 'code' => 'CON_MOBILE', + 'name' => '모바일 현장앱', + 'description' => '현장 기사용 모바일 앱', + 'development_fee' => 500000, + 'subscription_fee' => 30000, + 'commission_rate' => 25, + 'allow_flexible_pricing' => false, + 'is_required' => false, + 'display_order' => 3, + ], + ]; + + foreach ($constructionProducts as $product) { + DB::table('sales_products')->insert([ + 'category_id' => $categoryIds['CONSTRUCTION'], + ...$product, + 'is_active' => true, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + }); + + $this->command->info('✅ 영업 상품 초기 데이터 생성 완료 (2개 카테고리, 11개 상품)'); + } +} From e7054b663329482644b02f695bb4dc38bcaba403 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 16:18:22 +0900 Subject: [PATCH 15/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=ED=8C=8C=ED=8A=B8?= =?UTF-8?q?=EB=84=88/=EB=A7=A4=EB=8B=88=EC=A0=80=20=EC=88=98=EB=8B=B9?= =?UTF-8?q?=EC=9C=A8=20=EB=B6=84=EB=A6=AC=20(commission=5Frate=20=E2=86=92?= =?UTF-8?q?=20partner/manager)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ger_commission_to_sales_products_table.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 database/migrations/2026_01_29_161626_add_partner_manager_commission_to_sales_products_table.php diff --git a/database/migrations/2026_01_29_161626_add_partner_manager_commission_to_sales_products_table.php b/database/migrations/2026_01_29_161626_add_partner_manager_commission_to_sales_products_table.php new file mode 100644 index 0000000..2d4603c --- /dev/null +++ b/database/migrations/2026_01_29_161626_add_partner_manager_commission_to_sales_products_table.php @@ -0,0 +1,47 @@ +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']); + }); + } +}; From 8327568a77aa6b68c4fb7b53c25c303339981f91 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 16:25:30 +0900 Subject: [PATCH 16/19] =?UTF-8?q?fix:=EC=83=81=ED=92=88=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=98=84=EC=8B=A4?= =?UTF-8?q?=ED=99=94=20(=EA=B0=9C=EB=B0=9C=EB=B9=84=208=EC=B2=9C=EB=A7=8C?= =?UTF-8?q?=EC=9B=90,=20=EA=B5=AC=EB=8F=85=EB=A3=8C=2050=EB=A7=8C=EC=9B=90?= =?UTF-8?q?=20=EB=93=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/seeders/SalesProductSeeder.php | 150 +++++++++++++----------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/database/seeders/SalesProductSeeder.php b/database/seeders/SalesProductSeeder.php index d043734..7501ad2 100644 --- a/database/seeders/SalesProductSeeder.php +++ b/database/seeders/SalesProductSeeder.php @@ -7,6 +7,9 @@ /** * 영업 상품 초기 데이터 시더 + * + * 개발비: 원래 가격 (예: 80,000,000원) + * 가입비: 개발비의 25% (예: 20,000,000원) - 할인된 가격으로 표시 */ class SalesProductSeeder extends Seeder { @@ -22,16 +25,16 @@ public function run(): void $categories = [ [ 'code' => 'MANUFACTURER', - 'name' => '제조업체 솔루션', + 'name' => '제조 업체', 'description' => '제조업 전용 SAM 솔루션 상품군', 'base_storage' => '100GB', 'display_order' => 1, ], [ 'code' => 'CONSTRUCTION', - 'name' => '공사업체 솔루션', + 'name' => '공사 업체', 'description' => '공사/시공업 전용 SAM 솔루션 상품군', - 'base_storage' => '50GB', + 'base_storage' => '100GB', 'display_order' => 2, ], ]; @@ -50,88 +53,96 @@ public function run(): void $manufacturerProducts = [ [ 'code' => 'MFG_BASIC', - 'name' => 'SAM 기본 패키지', - 'description' => '생산관리, 재고관리, 주문관리 기본 기능 포함', - 'development_fee' => 3000000, - 'subscription_fee' => 150000, - 'commission_rate' => 25, + '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_PRODUCTION', - 'name' => '생산관리 모듈', - 'description' => '작업지시, 공정관리, 생산실적 관리', - 'development_fee' => 1500000, - 'subscription_fee' => 80000, - 'commission_rate' => 25, + '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_INVENTORY', - 'name' => '재고관리 모듈', - 'description' => '입출고 관리, 재고실사, 로트 추적', - 'development_fee' => 1200000, - 'subscription_fee' => 60000, - 'commission_rate' => 25, + '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_QUALITY', - 'name' => '품질관리 모듈', - 'description' => '수입검사, 공정검사, 출하검사', - 'development_fee' => 1000000, - 'subscription_fee' => 50000, - 'commission_rate' => 25, + '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_ESTIMATE', - 'name' => '견적관리 모듈', - 'description' => '견적 수식 관리, 자동 계산, 견적서 생성', - 'development_fee' => 800000, - 'subscription_fee' => 40000, - 'commission_rate' => 25, + '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_ACCOUNTING', - 'name' => '회계관리 모듈', - 'description' => '매출/매입 관리, 세금계산서, 자금일보', - 'development_fee' => 1500000, + 'code' => 'MFG_INVOICE_CARD', + 'name' => '계산서 + 카드 관리', + 'description' => '계산서 발행 (월 100건 기준 / 초과시 추가 5만원) + 법인카드 (접대비+복리후생비+가지급금 관리)', + 'development_fee' => 10000000, 'subscription_fee' => 100000, - 'commission_rate' => 25, + 'partner_commission_rate' => 20, + 'manager_commission_rate' => 5, 'allow_flexible_pricing' => true, 'is_required' => false, 'display_order' => 6, ], [ - 'code' => 'MFG_MOBILE', - 'name' => '모바일 앱', - 'description' => '현장용 모바일 앱 (iOS/Android)', - 'development_fee' => 500000, - 'subscription_fee' => 30000, - 'commission_rate' => 25, - 'allow_flexible_pricing' => false, + '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_CUSTOM', - 'name' => '커스터마이징', - 'description' => '고객 맞춤 개발 (별도 협의)', - 'development_fee' => 0, - 'subscription_fee' => 0, - 'commission_rate' => 20, + '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, @@ -152,34 +163,37 @@ public function run(): void $constructionProducts = [ [ 'code' => 'CON_BASIC', - 'name' => 'SAM 공사관리 패키지', - 'description' => '현장관리, 공사진행, 자재관리 기본 기능', - 'development_fee' => 2000000, - 'subscription_fee' => 100000, - 'commission_rate' => 25, + '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_FIELD', - 'name' => '현장관리 모듈', - 'description' => '현장 일지, 사진 관리, 공정 관리', - 'development_fee' => 1000000, - 'subscription_fee' => 50000, - 'commission_rate' => 25, + '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_MOBILE', - 'name' => '모바일 현장앱', - 'description' => '현장 기사용 모바일 앱', - 'development_fee' => 500000, - 'subscription_fee' => 30000, - 'commission_rate' => 25, - 'allow_flexible_pricing' => false, + '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, ], From ff829ad184084359b318af4c0bb30fad67338abe Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 16:31:50 +0900 Subject: [PATCH 17/19] =?UTF-8?q?feat:=EA=B0=80=EC=9E=85=EB=B9=84(registra?= =?UTF-8?q?tion=5Ffee)=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8B=9C=EB=8D=94=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...gistration_fee_to_sales_products_table.php | 32 +++++++++++++++++++ database/seeders/SalesProductSeeder.php | 2 ++ 2 files changed, 34 insertions(+) create mode 100644 database/migrations/2026_01_29_162847_add_registration_fee_to_sales_products_table.php diff --git a/database/migrations/2026_01_29_162847_add_registration_fee_to_sales_products_table.php b/database/migrations/2026_01_29_162847_add_registration_fee_to_sales_products_table.php new file mode 100644 index 0000000..4a6aa96 --- /dev/null +++ b/database/migrations/2026_01_29_162847_add_registration_fee_to_sales_products_table.php @@ -0,0 +1,32 @@ +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'); + }); + } +}; diff --git a/database/seeders/SalesProductSeeder.php b/database/seeders/SalesProductSeeder.php index 7501ad2..3e77387 100644 --- a/database/seeders/SalesProductSeeder.php +++ b/database/seeders/SalesProductSeeder.php @@ -153,6 +153,7 @@ public function run(): void 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(), @@ -203,6 +204,7 @@ public function run(): void 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(), From f8d37f0b5e5b6afb15c827f2e7e93b56a56cc1ef Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 16:38:34 +0900 Subject: [PATCH 18/19] =?UTF-8?q?refactor:sales=5Fcontract=5Fproducts=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20development=5Ffee=20=E2=86=92=20r?= =?UTF-8?q?egistration=5Ffee=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tration_fee_in_sales_contract_products.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 database/migrations/2026_01_29_163733_rename_development_fee_to_registration_fee_in_sales_contract_products.php diff --git a/database/migrations/2026_01_29_163733_rename_development_fee_to_registration_fee_in_sales_contract_products.php b/database/migrations/2026_01_29_163733_rename_development_fee_to_registration_fee_in_sales_contract_products.php new file mode 100644 index 0000000..d6914a8 --- /dev/null +++ b/database/migrations/2026_01_29_163733_rename_development_fee_to_registration_fee_in_sales_contract_products.php @@ -0,0 +1,28 @@ +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'); + }); + } +}; From 68ffbdfa08e289f5e109d9637679bc1ff1c6aded Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 29 Jan 2026 18:13:59 +0900 Subject: [PATCH 19/19] =?UTF-8?q?feat:=EC=98=81=EC=97=85=EC=88=98=EC=88=98?= =?UTF-8?q?=EB=A3=8C=20=EC=A0=95=EC=82=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sales_commissions 테이블 생성 (영업수수료 정산) - sales_commission_details 테이블 생성 (상품별 수당 내역) - sales_tenant_managements 테이블에 입금 정보 컬럼 추가 Co-Authored-By: Claude Opus 4.5 --- ..._170000_create_sales_commissions_table.php | 72 +++++++++++++++++++ ..._create_sales_commission_details_table.php | 47 ++++++++++++ ...nt_columns_to_sales_tenant_managements.php | 53 ++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 database/migrations/2026_01_29_170000_create_sales_commissions_table.php create mode 100644 database/migrations/2026_01_29_170100_create_sales_commission_details_table.php create mode 100644 database/migrations/2026_01_29_170200_add_payment_columns_to_sales_tenant_managements.php diff --git a/database/migrations/2026_01_29_170000_create_sales_commissions_table.php b/database/migrations/2026_01_29_170000_create_sales_commissions_table.php new file mode 100644 index 0000000..1fad60a --- /dev/null +++ b/database/migrations/2026_01_29_170000_create_sales_commissions_table.php @@ -0,0 +1,72 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_170100_create_sales_commission_details_table.php b/database/migrations/2026_01_29_170100_create_sales_commission_details_table.php new file mode 100644 index 0000000..9b60ba2 --- /dev/null +++ b/database/migrations/2026_01_29_170100_create_sales_commission_details_table.php @@ -0,0 +1,47 @@ +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'); + } +}; diff --git a/database/migrations/2026_01_29_170200_add_payment_columns_to_sales_tenant_managements.php b/database/migrations/2026_01_29_170200_add_payment_columns_to_sales_tenant_managements.php new file mode 100644 index 0000000..8a53699 --- /dev/null +++ b/database/migrations/2026_01_29_170200_add_payment_columns_to_sales_tenant_managements.php @@ -0,0 +1,53 @@ +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', + ]); + }); + } +};