diff --git a/database/migrations/2026_01_28_200000_create_documents_table.php b/database/migrations/2026_01_28_200000_create_documents_table.php new file mode 100644 index 0000000..5beae8b --- /dev/null +++ b/database/migrations/2026_01_28_200000_create_documents_table.php @@ -0,0 +1,115 @@ +id()->comment('ID'); + $table->foreignId('tenant_id')->constrained()->cascadeOnDelete()->comment('테넌트 ID'); + $table->foreignId('template_id')->constrained('document_templates')->comment('템플릿 ID'); + + // 문서 정보 + $table->string('document_no', 50)->comment('문서번호'); + $table->string('title', 255)->comment('문서 제목'); + $table->enum('status', ['DRAFT', 'PENDING', 'APPROVED', 'REJECTED', 'CANCELLED']) + ->default('DRAFT')->comment('상태 (DRAFT:임시저장, PENDING:결재중, APPROVED:승인, REJECTED:반려, CANCELLED:취소)'); + + // 연결 정보 (다형성) + $table->string('linkable_type', 100)->nullable()->comment('연결 모델 타입'); + $table->unsignedBigInteger('linkable_id')->nullable()->comment('연결 모델 ID'); + + // 결재 정보 + $table->timestamp('submitted_at')->nullable()->comment('결재 요청일'); + $table->timestamp('completed_at')->nullable()->comment('결재 완료일'); + + // 감사 컬럼 + $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); + $table->foreignId('updated_by')->nullable()->constrained('users')->comment('수정자 ID'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->comment('삭제자 ID'); + $table->timestamps(); + $table->softDeletes(); + + // 인덱스 + $table->index(['tenant_id', 'status']); + $table->index('document_no'); + $table->index(['linkable_type', 'linkable_id']); + }); + + // 문서 결재 + Schema::create('document_approvals', function (Blueprint $table) { + $table->id()->comment('ID'); + $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); + $table->foreignId('user_id')->constrained()->comment('결재자 ID'); + + $table->unsignedTinyInteger('step')->default(1)->comment('결재 순서'); + $table->string('role', 50)->comment('역할 (작성/검토/승인)'); + $table->enum('status', ['PENDING', 'APPROVED', 'REJECTED']) + ->default('PENDING')->comment('상태 (PENDING:대기, APPROVED:승인, REJECTED:반려)'); + + $table->text('comment')->nullable()->comment('결재 의견'); + $table->timestamp('acted_at')->nullable()->comment('결재 처리일'); + + // 감사 컬럼 + $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); + $table->foreignId('updated_by')->nullable()->constrained('users')->comment('수정자 ID'); + $table->timestamps(); + + // 인덱스 + $table->index(['document_id', 'step']); + $table->index(['user_id', 'status']); + }); + + // 문서 데이터 (EAV 패턴) + Schema::create('document_data', function (Blueprint $table) { + $table->id()->comment('ID'); + $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); + + $table->unsignedBigInteger('section_id')->nullable()->comment('섹션 ID (document_template_sections 참조)'); + $table->unsignedBigInteger('column_id')->nullable()->comment('컬럼 ID (document_template_columns 참조)'); + $table->unsignedSmallInteger('row_index')->default(0)->comment('행 인덱스 (테이블 데이터용)'); + + $table->string('field_key', 100)->comment('필드 키'); + $table->text('field_value')->nullable()->comment('필드 값'); + + $table->timestamps(); + + // 인덱스 + $table->index(['document_id', 'section_id']); + $table->index(['document_id', 'field_key']); + }); + + // 문서 첨부파일 + Schema::create('document_attachments', function (Blueprint $table) { + $table->id()->comment('ID'); + $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); + $table->foreignId('file_id')->constrained('files')->comment('파일 ID'); + + $table->string('attachment_type', 50)->default('general')->comment('첨부 유형 (general, signature, image 등)'); + $table->string('description', 255)->nullable()->comment('설명'); + + // 감사 컬럼 + $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('document_attachments'); + Schema::dropIfExists('document_data'); + Schema::dropIfExists('document_approvals'); + Schema::dropIfExists('documents'); + } +};