feat:영업담당자 User 모듈 통합 및 승인 시스템 구현
- SalesManagerController: User 시스템 기반으로 재구현 - SalesManagerService: 영업담당자 CRUD, 승인/반려 로직 - SalesManagerDocument: 멀티파일 업로드 모델 - User 모델에 parent, approval 관계 및 메서드 추가 - SalesRoleSeeder: 영업 역할 시더 (sales_operator, sales_admin, sales_manager) - 뷰 파일 전면 수정 (역할 체크박스, 멀티파일 업로드, 승인/반려 UI) - 라우트 추가 (approve, reject, documents) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
146
app/Models/Sales/SalesManagerDocument.php
Normal file
146
app/Models/Sales/SalesManagerDocument.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Sales;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SalesManagerDocument extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'sales_manager_documents';
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'user_id',
|
||||
'file_path',
|
||||
'original_name',
|
||||
'stored_name',
|
||||
'mime_type',
|
||||
'file_size',
|
||||
'document_type',
|
||||
'description',
|
||||
'uploaded_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'file_size' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 문서 타입 목록
|
||||
*/
|
||||
public const DOCUMENT_TYPES = [
|
||||
'id_card' => '신분증',
|
||||
'business_license' => '사업자등록증',
|
||||
'contract' => '계약서',
|
||||
'bank_account' => '통장사본',
|
||||
'other' => '기타',
|
||||
];
|
||||
|
||||
/**
|
||||
* 사용자 (영업담당자)
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 업로더
|
||||
*/
|
||||
public function uploader(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'uploaded_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 타입 라벨
|
||||
*/
|
||||
public function getDocumentTypeLabelAttribute(): string
|
||||
{
|
||||
return self::DOCUMENT_TYPES[$this->document_type] ?? $this->document_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 크기 포맷팅
|
||||
*/
|
||||
public function getFormattedSizeAttribute(): string
|
||||
{
|
||||
$bytes = $this->file_size;
|
||||
|
||||
if ($bytes >= 1073741824) {
|
||||
return number_format($bytes / 1073741824, 2) . ' GB';
|
||||
} elseif ($bytes >= 1048576) {
|
||||
return number_format($bytes / 1048576, 2) . ' MB';
|
||||
} elseif ($bytes >= 1024) {
|
||||
return number_format($bytes / 1024, 2) . ' KB';
|
||||
}
|
||||
|
||||
return $bytes . ' bytes';
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 확장자
|
||||
*/
|
||||
public function getExtensionAttribute(): string
|
||||
{
|
||||
return pathinfo($this->original_name, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 여부
|
||||
*/
|
||||
public function isImage(): bool
|
||||
{
|
||||
return str_starts_with($this->mime_type ?? '', 'image/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 스토리지 전체 경로
|
||||
*/
|
||||
public function getStoragePath(): string
|
||||
{
|
||||
return Storage::disk('tenant')->path($this->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 존재 여부
|
||||
*/
|
||||
public function existsInStorage(): bool
|
||||
{
|
||||
return Storage::disk('tenant')->exists($this->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 URL
|
||||
*/
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
if (!$this->existsInStorage()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Storage::disk('tenant')->url($this->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 다운로드 응답
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
if (!$this->existsInStorage()) {
|
||||
abort(404, '파일을 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
return Storage::disk('tenant')->download($this->file_path, $this->original_name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user