From d164bb4c4af62c1709fa4b1fa948bd9ac18ff4f1 Mon Sep 17 00:00:00 2001 From: hskwon Date: Thu, 4 Dec 2025 21:13:58 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[client]=20=EA=B1=B0=EB=9E=98=EC=B2=98?= =?UTF-8?q?=20API=202=EC=B0=A8=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B2=AC=EC=A0=81=20=EA=B3=84=ED=9A=8D=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 거래처 유형(client_type), 연락처(mobile, fax), 담당자 정보 필드 추가 - 발주처 설정(account_id/password, payment_day) 필드 추가 - 약정 세금(tax_agreement, tax_amount, tax_start/end_date) 필드 추가 - 악성채권(bad_debt 관련 5개 필드) 정보 필드 추가 - Model, Service, FormRequest, Swagger 문서 업데이트 - 견적 API 계획에 문서 발송 API(email/fax/kakao) 요구사항 추가 --- .../Requests/Client/ClientStoreRequest.php | 27 ++++++ .../Requests/Client/ClientUpdateRequest.php | 30 ++++++- app/Models/Orders/Client.php | 32 ++++++++ app/Services/ClientService.php | 40 +++++++++ app/Swagger/v1/ClientApi.php | 63 +++++++++++++- ...-2025-12-04] quote-api-development-plan.md | 55 +++++++++++-- ...3_add_extended_fields_to_clients_table.php | 82 +++++++++++++++++++ 7 files changed, 318 insertions(+), 11 deletions(-) create mode 100644 database/migrations/2025_12_04_205603_add_extended_fields_to_clients_table.php diff --git a/app/Http/Requests/Client/ClientStoreRequest.php b/app/Http/Requests/Client/ClientStoreRequest.php index 9430654..9b0dde2 100644 --- a/app/Http/Requests/Client/ClientStoreRequest.php +++ b/app/Http/Requests/Client/ClientStoreRequest.php @@ -17,13 +17,40 @@ public function rules(): array 'client_group_id' => 'nullable|integer', 'client_code' => 'required|string|max:50', 'name' => 'required|string|max:100', + 'client_type' => 'nullable|in:매입,매출,매입매출', + // 연락처 정보 'contact_person' => 'nullable|string|max:100', 'phone' => 'nullable|string|max:20', + 'mobile' => 'nullable|string|max:20', + 'fax' => 'nullable|string|max:20', 'email' => 'nullable|email|max:100', 'address' => 'nullable|string|max:255', + // 담당자 정보 + 'manager_name' => 'nullable|string|max:50', + 'manager_tel' => 'nullable|string|max:20', + 'system_manager' => 'nullable|string|max:50', + // 발주처 설정 + 'account_id' => 'nullable|string|max:50', + 'account_password' => 'nullable|string|max:255', + 'purchase_payment_day' => 'nullable|string|max:20', + 'sales_payment_day' => 'nullable|string|max:20', + // 사업자 정보 'business_no' => 'nullable|string|max:20', 'business_type' => 'nullable|string|max:50', 'business_item' => 'nullable|string|max:100', + // 약정 세금 + 'tax_agreement' => 'nullable|boolean', + 'tax_amount' => 'nullable|numeric|min:0', + 'tax_start_date' => 'nullable|date', + 'tax_end_date' => 'nullable|date|after_or_equal:tax_start_date', + // 악성채권 정보 + 'bad_debt' => 'nullable|boolean', + 'bad_debt_amount' => 'nullable|numeric|min:0', + 'bad_debt_receive_date' => 'nullable|date', + 'bad_debt_end_date' => 'nullable|date|after_or_equal:bad_debt_receive_date', + 'bad_debt_progress' => 'nullable|in:협의중,소송중,회수완료,대손처리', + // 기타 + 'memo' => 'nullable|string', 'is_active' => 'nullable|in:Y,N', ]; } diff --git a/app/Http/Requests/Client/ClientUpdateRequest.php b/app/Http/Requests/Client/ClientUpdateRequest.php index c99b46a..5030bfa 100644 --- a/app/Http/Requests/Client/ClientUpdateRequest.php +++ b/app/Http/Requests/Client/ClientUpdateRequest.php @@ -17,12 +17,40 @@ public function rules(): array 'client_group_id' => 'nullable|integer', 'client_code' => 'sometimes|string|max:50', 'name' => 'sometimes|string|max:100', + 'client_type' => 'nullable|in:매입,매출,매입매출', + // 연락처 정보 'contact_person' => 'nullable|string|max:100', 'phone' => 'nullable|string|max:20', + 'mobile' => 'nullable|string|max:20', + 'fax' => 'nullable|string|max:20', 'email' => 'nullable|email|max:100', - 'address' => 'nullable|string|max:255', 'business_no' => 'nullable|string|max:20', + 'address' => 'nullable|string|max:255', + // 담당자 정보 + 'manager_name' => 'nullable|string|max:50', + 'manager_tel' => 'nullable|string|max:20', + 'system_manager' => 'nullable|string|max:50', + // 발주처 설정 + 'account_id' => 'nullable|string|max:50', + 'account_password' => 'nullable|string|max:255', + 'purchase_payment_day' => 'nullable|string|max:20', + 'sales_payment_day' => 'nullable|string|max:20', + // 사업자 정보 + 'business_no' => 'nullable|string|max:20', 'business_type' => 'nullable|string|max:50', 'business_item' => 'nullable|string|max:100', + // 약정 세금 + 'tax_agreement' => 'nullable|boolean', + 'tax_amount' => 'nullable|numeric|min:0', + 'tax_start_date' => 'nullable|date', + 'tax_end_date' => 'nullable|date|after_or_equal:tax_start_date', + // 악성채권 정보 + 'bad_debt' => 'nullable|boolean', + 'bad_debt_amount' => 'nullable|numeric|min:0', + 'bad_debt_receive_date' => 'nullable|date', + 'bad_debt_end_date' => 'nullable|date|after_or_equal:bad_debt_receive_date', + 'bad_debt_progress' => 'nullable|in:협의중,소송중,회수완료,대손처리', + // 기타 + 'memo' => 'nullable|string', 'is_active' => 'nullable|in:Y,N', ]; } diff --git a/app/Models/Orders/Client.php b/app/Models/Orders/Client.php index d343c14..aae063b 100644 --- a/app/Models/Orders/Client.php +++ b/app/Models/Orders/Client.php @@ -17,16 +17,48 @@ class Client extends Model 'name', 'contact_person', 'phone', + 'mobile', + 'fax', 'email', 'address', + 'account_id', + 'account_password', + 'purchase_payment_day', + 'sales_payment_day', 'business_no', 'business_type', 'business_item', + 'tax_agreement', + 'tax_amount', + 'tax_start_date', + 'tax_end_date', + 'bad_debt', + 'bad_debt_amount', + 'bad_debt_receive_date', + 'bad_debt_end_date', + 'bad_debt_progress', + 'memo', 'is_active', + 'client_type', + 'manager_name', + 'manager_tel', + 'system_manager', ]; protected $casts = [ 'is_active' => 'boolean', + 'tax_agreement' => 'boolean', + 'tax_amount' => 'decimal:2', + 'tax_start_date' => 'date', + 'tax_end_date' => 'date', + 'bad_debt' => 'boolean', + 'bad_debt_amount' => 'decimal:2', + 'bad_debt_receive_date' => 'date', + 'bad_debt_end_date' => 'date', + ]; + + protected $hidden = [ + 'account_password', ]; // ClientGroup 관계 diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index 978e7c5..36c41e3 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -59,13 +59,33 @@ public function store(array $params) $v = Validator::make($params, [ 'client_code' => 'required|string|max:50', 'name' => 'required|string|max:100', + 'client_type' => 'nullable|in:매입,매출,매입매출', 'contact_person' => 'nullable|string|max:50', 'phone' => 'nullable|string|max:30', + 'mobile' => 'nullable|string|max:20', + 'fax' => 'nullable|string|max:20', 'email' => 'nullable|email|max:80', 'address' => 'nullable|string|max:255', + 'manager_name' => 'nullable|string|max:50', + 'manager_tel' => 'nullable|string|max:20', + 'system_manager' => 'nullable|string|max:50', + 'account_id' => 'nullable|string|max:50', + 'account_password' => 'nullable|string|max:255', + 'purchase_payment_day' => 'nullable|string|max:20', + 'sales_payment_day' => 'nullable|string|max:20', 'business_no' => 'nullable|string|max:20', 'business_type' => 'nullable|string|max:50', 'business_item' => 'nullable|string|max:100', + 'tax_agreement' => 'nullable|boolean', + 'tax_amount' => 'nullable|numeric|min:0', + 'tax_start_date' => 'nullable|date', + 'tax_end_date' => 'nullable|date', + 'bad_debt' => 'nullable|boolean', + 'bad_debt_amount' => 'nullable|numeric|min:0', + 'bad_debt_receive_date' => 'nullable|date', + 'bad_debt_end_date' => 'nullable|date', + 'bad_debt_progress' => 'nullable|in:협의중,소송중,회수완료,대손처리', + 'memo' => 'nullable|string', 'is_active' => 'nullable|in:Y,N', ]); @@ -103,13 +123,33 @@ public function update(int $id, array $params) $v = Validator::make($params, [ 'client_code' => 'sometimes|required|string|max:50', 'name' => 'sometimes|required|string|max:100', + 'client_type' => 'nullable|in:매입,매출,매입매출', 'contact_person' => 'nullable|string|max:50', 'phone' => 'nullable|string|max:30', + 'mobile' => 'nullable|string|max:20', + 'fax' => 'nullable|string|max:20', 'email' => 'nullable|email|max:80', 'address' => 'nullable|string|max:255', + 'manager_name' => 'nullable|string|max:50', + 'manager_tel' => 'nullable|string|max:20', + 'system_manager' => 'nullable|string|max:50', + 'account_id' => 'nullable|string|max:50', + 'account_password' => 'nullable|string|max:255', + 'purchase_payment_day' => 'nullable|string|max:20', + 'sales_payment_day' => 'nullable|string|max:20', 'business_no' => 'nullable|string|max:20', 'business_type' => 'nullable|string|max:50', 'business_item' => 'nullable|string|max:100', + 'tax_agreement' => 'nullable|boolean', + 'tax_amount' => 'nullable|numeric|min:0', + 'tax_start_date' => 'nullable|date', + 'tax_end_date' => 'nullable|date', + 'bad_debt' => 'nullable|boolean', + 'bad_debt_amount' => 'nullable|numeric|min:0', + 'bad_debt_receive_date' => 'nullable|date', + 'bad_debt_end_date' => 'nullable|date', + 'bad_debt_progress' => 'nullable|in:협의중,소송중,회수완료,대손처리', + 'memo' => 'nullable|string', 'is_active' => 'nullable|in:Y,N', ]); diff --git a/app/Swagger/v1/ClientApi.php b/app/Swagger/v1/ClientApi.php index 1e85991..290a5c8 100644 --- a/app/Swagger/v1/ClientApi.php +++ b/app/Swagger/v1/ClientApi.php @@ -15,13 +15,32 @@ * @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="고객 그룹 ID"), * @OA\Property(property="client_code", type="string", example="CLIENT_001"), * @OA\Property(property="name", type="string", example="거래처명"), + * @OA\Property(property="client_type", type="string", enum={"매입", "매출", "매입매출"}, example="매입", description="거래처 유형"), * @OA\Property(property="contact_person", type="string", nullable=true, example="홍길동"), - * @OA\Property(property="phone", type="string", nullable=true, example="010-1234-5678"), + * @OA\Property(property="phone", type="string", nullable=true, example="02-1234-5678"), + * @OA\Property(property="mobile", type="string", nullable=true, example="010-1234-5678", description="모바일 번호"), + * @OA\Property(property="fax", type="string", nullable=true, example="02-1234-5679", description="팩스 번호"), * @OA\Property(property="email", type="string", nullable=true, example="client@example.com"), * @OA\Property(property="address", type="string", nullable=true, example="서울시 강남구"), + * @OA\Property(property="manager_name", type="string", nullable=true, example="김담당", description="담당자명"), + * @OA\Property(property="manager_tel", type="string", nullable=true, example="010-9876-5432", description="담당자 전화"), + * @OA\Property(property="system_manager", type="string", nullable=true, example="박시스템", description="시스템 관리자"), + * @OA\Property(property="account_id", type="string", nullable=true, example="user123", description="계정 ID"), + * @OA\Property(property="purchase_payment_day", type="string", nullable=true, example="매월 25일", description="매입 결제일"), + * @OA\Property(property="sales_payment_day", type="string", nullable=true, example="매월 말일", description="매출 결제일"), * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, example="123-45-67890", description="사업자등록번호"), * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, example="제조업", description="업태"), * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, example="전자부품", description="업종"), + * @OA\Property(property="tax_agreement", type="boolean", example=false, description="세금 약정 여부"), + * @OA\Property(property="tax_amount", type="number", format="float", nullable=true, example=1000000, description="약정 금액"), + * @OA\Property(property="tax_start_date", type="string", format="date", nullable=true, example="2025-01-01", description="약정 시작일"), + * @OA\Property(property="tax_end_date", type="string", format="date", nullable=true, example="2025-12-31", description="약정 종료일"), + * @OA\Property(property="bad_debt", type="boolean", example=false, description="악성채권 여부"), + * @OA\Property(property="bad_debt_amount", type="number", format="float", nullable=true, example=500000, description="악성채권 금액"), + * @OA\Property(property="bad_debt_receive_date", type="string", format="date", nullable=true, example="2024-06-01", description="채권 발생일"), + * @OA\Property(property="bad_debt_end_date", type="string", format="date", nullable=true, example="2025-06-01", description="채권 만료일"), + * @OA\Property(property="bad_debt_progress", type="string", enum={"협의중", "소송중", "회수완료", "대손처리"}, nullable=true, description="진행 상태"), + * @OA\Property(property="memo", type="string", nullable=true, example="특이사항 메모", description="메모"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}, example="Y"), * @OA\Property(property="created_at", type="string", example="2025-10-01 12:00:00"), * @OA\Property(property="updated_at", type="string", example="2025-10-01 12:00:00") @@ -70,13 +89,33 @@ * @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="고객 그룹 ID"), * @OA\Property(property="client_code", type="string", maxLength=50, example="CLIENT_001"), * @OA\Property(property="name", type="string", maxLength=100, example="거래처명"), + * @OA\Property(property="client_type", type="string", enum={"매입", "매출", "매입매출"}, nullable=true, example="매입", description="거래처 유형"), * @OA\Property(property="contact_person", type="string", nullable=true, maxLength=100, example="홍길동"), - * @OA\Property(property="phone", type="string", nullable=true, maxLength=20, example="010-1234-5678"), + * @OA\Property(property="phone", type="string", nullable=true, maxLength=20, example="02-1234-5678"), + * @OA\Property(property="mobile", type="string", nullable=true, maxLength=20, example="010-1234-5678", description="모바일 번호"), + * @OA\Property(property="fax", type="string", nullable=true, maxLength=20, example="02-1234-5679", description="팩스 번호"), * @OA\Property(property="email", type="string", nullable=true, maxLength=100, example="client@example.com"), * @OA\Property(property="address", type="string", nullable=true, maxLength=255, example="서울시 강남구"), + * @OA\Property(property="manager_name", type="string", nullable=true, maxLength=50, example="김담당", description="담당자명"), + * @OA\Property(property="manager_tel", type="string", nullable=true, maxLength=20, example="010-9876-5432", description="담당자 전화"), + * @OA\Property(property="system_manager", type="string", nullable=true, maxLength=50, example="박시스템", description="시스템 관리자"), + * @OA\Property(property="account_id", type="string", nullable=true, maxLength=50, example="user123", description="계정 ID"), + * @OA\Property(property="account_password", type="string", nullable=true, maxLength=255, example="password123", description="계정 비밀번호"), + * @OA\Property(property="purchase_payment_day", type="string", nullable=true, maxLength=20, example="매월 25일", description="매입 결제일"), + * @OA\Property(property="sales_payment_day", type="string", nullable=true, maxLength=20, example="매월 말일", description="매출 결제일"), * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, example="123-45-67890", description="사업자등록번호"), * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, example="제조업", description="업태"), * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, example="전자부품", description="업종"), + * @OA\Property(property="tax_agreement", type="boolean", nullable=true, example=false, description="세금 약정 여부"), + * @OA\Property(property="tax_amount", type="number", format="float", nullable=true, example=1000000, description="약정 금액"), + * @OA\Property(property="tax_start_date", type="string", format="date", nullable=true, example="2025-01-01", description="약정 시작일"), + * @OA\Property(property="tax_end_date", type="string", format="date", nullable=true, example="2025-12-31", description="약정 종료일"), + * @OA\Property(property="bad_debt", type="boolean", nullable=true, example=false, description="악성채권 여부"), + * @OA\Property(property="bad_debt_amount", type="number", format="float", nullable=true, example=500000, description="악성채권 금액"), + * @OA\Property(property="bad_debt_receive_date", type="string", format="date", nullable=true, example="2024-06-01", description="채권 발생일"), + * @OA\Property(property="bad_debt_end_date", type="string", format="date", nullable=true, example="2025-06-01", description="채권 만료일"), + * @OA\Property(property="bad_debt_progress", type="string", enum={"협의중", "소송중", "회수완료", "대손처리"}, nullable=true, description="진행 상태"), + * @OA\Property(property="memo", type="string", nullable=true, example="특이사항 메모", description="메모"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}, example="Y") * ) * @@ -87,13 +126,33 @@ * @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="고객 그룹 ID"), * @OA\Property(property="client_code", type="string", maxLength=50), * @OA\Property(property="name", type="string", maxLength=100), + * @OA\Property(property="client_type", type="string", enum={"매입", "매출", "매입매출"}, nullable=true, description="거래처 유형"), * @OA\Property(property="contact_person", type="string", nullable=true, maxLength=100), * @OA\Property(property="phone", type="string", nullable=true, maxLength=20), + * @OA\Property(property="mobile", type="string", nullable=true, maxLength=20, description="모바일 번호"), + * @OA\Property(property="fax", type="string", nullable=true, maxLength=20, description="팩스 번호"), * @OA\Property(property="email", type="string", nullable=true, maxLength=100), * @OA\Property(property="address", type="string", nullable=true, maxLength=255), + * @OA\Property(property="manager_name", type="string", nullable=true, maxLength=50, description="담당자명"), + * @OA\Property(property="manager_tel", type="string", nullable=true, maxLength=20, description="담당자 전화"), + * @OA\Property(property="system_manager", type="string", nullable=true, maxLength=50, description="시스템 관리자"), + * @OA\Property(property="account_id", type="string", nullable=true, maxLength=50, description="계정 ID"), + * @OA\Property(property="account_password", type="string", nullable=true, maxLength=255, description="계정 비밀번호"), + * @OA\Property(property="purchase_payment_day", type="string", nullable=true, maxLength=20, description="매입 결제일"), + * @OA\Property(property="sales_payment_day", type="string", nullable=true, maxLength=20, description="매출 결제일"), * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, description="사업자등록번호"), * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, description="업태"), * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, description="업종"), + * @OA\Property(property="tax_agreement", type="boolean", nullable=true, description="세금 약정 여부"), + * @OA\Property(property="tax_amount", type="number", format="float", nullable=true, description="약정 금액"), + * @OA\Property(property="tax_start_date", type="string", format="date", nullable=true, description="약정 시작일"), + * @OA\Property(property="tax_end_date", type="string", format="date", nullable=true, description="약정 종료일"), + * @OA\Property(property="bad_debt", type="boolean", nullable=true, description="악성채권 여부"), + * @OA\Property(property="bad_debt_amount", type="number", format="float", nullable=true, description="악성채권 금액"), + * @OA\Property(property="bad_debt_receive_date", type="string", format="date", nullable=true, description="채권 발생일"), + * @OA\Property(property="bad_debt_end_date", type="string", format="date", nullable=true, description="채권 만료일"), + * @OA\Property(property="bad_debt_progress", type="string", enum={"협의중", "소송중", "회수완료", "대손처리"}, nullable=true, description="진행 상태"), + * @OA\Property(property="memo", type="string", nullable=true, description="메모"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}) * ) */ diff --git a/claudedocs/[PLAN-2025-12-04] quote-api-development-plan.md b/claudedocs/[PLAN-2025-12-04] quote-api-development-plan.md index e83822f..462e2b4 100644 --- a/claudedocs/[PLAN-2025-12-04] quote-api-development-plan.md +++ b/claudedocs/[PLAN-2025-12-04] quote-api-development-plan.md @@ -2,7 +2,7 @@ # 견적관리 API 개발 계획서 > **작성일**: 2025-12-04 > **요청서**: `docs/front/[API-2025-12-04] quote-api-request.md` -> **상태**: 🔄 Phase 1 진행 중 → 요청 변경으로 재계획 필요 +> **상태**: ✅ Phase 1 완료 → Phase 2 진행 중 --- @@ -30,9 +30,28 @@ #### 2. Model 생성 완료 - 스코프 메서드 (draft, finalized, search, dateRange 등) - 상태 검증 메서드 (isEditable, isDeletable, isFinalizable, isConvertible) -### ⏸️ 일시 중단 -- **사유**: 요청서 변경으로 계획 재수립 필요 -- **다음 단계**: 변경된 요청서 확인 후 계획 비교 분석 +### 🆕 변경 요청 분석 (2025-12-04) + +요청서에서 **문서 발송 API (Section 3.5)**가 신규 추가됨: + +| Method | Endpoint | 설명 | 비고 | +|--------|----------|------|------| +| `POST` | `/api/v1/quotes/{id}/send/email` | 이메일 발송 | 첨부파일 포함 | +| `POST` | `/api/v1/quotes/{id}/send/fax` | 팩스 발송 | 팩스 서비스 연동 | +| `POST` | `/api/v1/quotes/{id}/send/kakao` | 카카오톡 발송 | 알림톡/친구톡 | + +**영향 범위:** +- Phase 2: `QuoteDocumentService` 추가 필요 +- Phase 3: 문서 발송 관련 Controller 메서드 + FormRequest 추가 +- Phase 4: Swagger 문서에 발송 API 추가 + +**개발 우선순위 조정:** +- P1: 견적 CRUD, 자동 산출, 견적번호 생성 (기존 유지) +- P2: 상태 관리, 수정 이력 (기존 유지) +- P3: 문서 출력 + **문서 발송 API** (신규 추가) + +### ⏭️ 다음 단계 +- Phase 2: Service Layer 구현 (QuoteService, QuoteCalculationService, QuoteNumberService) --- @@ -272,6 +291,14 @@ ### 3.4 문서 출력 (P3 - 후순위) | `GET` | `/api/v1/quotes/{id}/document/calculation` | 산출내역서 PDF | | `GET` | `/api/v1/quotes/{id}/document/purchase-order` | 발주서 PDF | +### 3.5 문서 발송 (P3 - 신규 추가) + +| Method | Endpoint | 설명 | 비고 | +|--------|----------|------|------| +| `POST` | `/api/v1/quotes/{id}/send/email` | 이메일 발송 | 첨부파일 포함 | +| `POST` | `/api/v1/quotes/{id}/send/fax` | 팩스 발송 | 팩스 서비스 연동 | +| `POST` | `/api/v1/quotes/{id}/send/kakao` | 카카오톡 발송 | 알림톡/친구톡 | + --- ## 4. 개발 Phase 계획 @@ -303,6 +330,7 @@ ### Phase 2: 핵심 서비스 (Service Layer) 2. QuoteCalculationService - 자동산출 (FormulaEvaluator 연동) 3. QuoteNumberService - 번호채번 4. FormulaEvaluatorService 이식 (mng → api) +5. QuoteDocumentService - 문서 생성 및 발송 (신규 추가) **파일 목록:** ``` @@ -310,12 +338,15 @@ ### Phase 2: 핵심 서비스 (Service Layer) ├── QuoteService.php ├── QuoteCalculationService.php ├── QuoteNumberService.php +├── QuoteDocumentService.php (신규 - 문서 생성/발송) └── FormulaEvaluatorService.php (mng에서 이식) ``` **의존성:** - PricingService (단가 조회) - ClientService (발주처 연동) +- MailService (이메일 발송) +- NotificationService (카카오톡/팩스 발송) ### Phase 3: API 구현 (Controller + Routes) @@ -323,6 +354,7 @@ ### Phase 3: API 구현 (Controller + Routes) 1. QuoteController 구현 2. FormRequest 생성 (검증 규칙) 3. 라우트 추가 +4. 문서 발송 엔드포인트 추가 (신규) **파일 목록:** ``` @@ -334,7 +366,10 @@ ### Phase 3: API 구현 (Controller + Routes) ├── QuoteStoreRequest.php ├── QuoteUpdateRequest.php ├── QuoteCalculateRequest.php -└── QuoteFinalizeRequest.php +├── QuoteFinalizeRequest.php +├── QuoteSendEmailRequest.php (신규) +├── QuoteSendFaxRequest.php (신규) +└── QuoteSendKakaoRequest.php (신규) routes/ └── api_v1.php (라우트 추가) @@ -417,9 +452,9 @@ ## 6. 예상 산출물 요약 |------|------|------| | 마이그레이션 | 3개 | quotes, quote_items, quote_revisions | | Model | 3개 | Quote, QuoteItem, QuoteRevision | -| Service | 4개 | QuoteService, QuoteCalculationService, QuoteNumberService, FormulaEvaluatorService | +| Service | 5개 | QuoteService, QuoteCalculationService, QuoteNumberService, FormulaEvaluatorService, **QuoteDocumentService** | | Controller | 1개 | QuoteController | -| FormRequest | 5개 | Index, Store, Update, Calculate, Finalize | +| FormRequest | 8개 | Index, Store, Update, Calculate, Finalize, **SendEmail, SendFax, SendKakao** | | Swagger | 1개 | QuoteApi.php | | i18n | 2개 | message.php, error.php (키 추가) | @@ -446,7 +481,11 @@ ### Q3. 문서 출력 PDF 라이브러리 - 대안: Dompdf (HTML → PDF 변환) ### Q4. 알림 (이메일/카카오톡) -**답변:** 현재 요구사항에 없음, 추후 확장 가능 +**답변:** ✅ 요청서 Section 3.5에 신규 추가됨 +- 이메일 발송: `POST /api/v1/quotes/{id}/send/email` +- 팩스 발송: `POST /api/v1/quotes/{id}/send/fax` +- 카카오톡 발송: `POST /api/v1/quotes/{id}/send/kakao` +- P3 우선순위로 문서 출력과 함께 개발 예정 --- diff --git a/database/migrations/2025_12_04_205603_add_extended_fields_to_clients_table.php b/database/migrations/2025_12_04_205603_add_extended_fields_to_clients_table.php new file mode 100644 index 0000000..3465e42 --- /dev/null +++ b/database/migrations/2025_12_04_205603_add_extended_fields_to_clients_table.php @@ -0,0 +1,82 @@ +enum('client_type', ['매입', '매출', '매입매출'])->default('매입')->after('is_active')->comment('거래처 유형'); + + // 연락처 정보 + $table->string('mobile', 20)->nullable()->after('phone')->comment('모바일 번호'); + $table->string('fax', 20)->nullable()->after('mobile')->comment('팩스 번호'); + + // 담당자 정보 + $table->string('manager_name', 50)->nullable()->after('contact_person')->comment('담당자명'); + $table->string('manager_tel', 20)->nullable()->after('manager_name')->comment('담당자 전화'); + $table->string('system_manager', 50)->nullable()->after('manager_tel')->comment('시스템 관리자'); + + // 발주처 설정 + $table->string('account_id', 50)->nullable()->after('address')->comment('계정 ID'); + $table->string('account_password', 255)->nullable()->after('account_id')->comment('비밀번호 (암호화)'); + $table->string('purchase_payment_day', 20)->nullable()->after('account_password')->comment('매입 결제일'); + $table->string('sales_payment_day', 20)->nullable()->after('purchase_payment_day')->comment('매출 결제일'); + + // 약정 세금 + $table->boolean('tax_agreement')->default(false)->after('business_item')->comment('세금 약정 여부'); + $table->decimal('tax_amount', 15, 2)->nullable()->after('tax_agreement')->comment('약정 금액'); + $table->date('tax_start_date')->nullable()->after('tax_amount')->comment('약정 시작일'); + $table->date('tax_end_date')->nullable()->after('tax_start_date')->comment('약정 종료일'); + + // 악성채권 정보 + $table->boolean('bad_debt')->default(false)->after('tax_end_date')->comment('악성채권 여부'); + $table->decimal('bad_debt_amount', 15, 2)->nullable()->after('bad_debt')->comment('악성채권 금액'); + $table->date('bad_debt_receive_date')->nullable()->after('bad_debt_amount')->comment('채권 발생일'); + $table->date('bad_debt_end_date')->nullable()->after('bad_debt_receive_date')->comment('채권 만료일'); + $table->enum('bad_debt_progress', ['협의중', '소송중', '회수완료', '대손처리'])->nullable()->after('bad_debt_end_date')->comment('진행 상태'); + + // 기타 정보 + $table->text('memo')->nullable()->after('bad_debt_progress')->comment('메모'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('clients', function (Blueprint $table) { + $table->dropColumn([ + 'client_type', + 'mobile', + 'fax', + 'manager_name', + 'manager_tel', + 'system_manager', + 'account_id', + 'account_password', + 'purchase_payment_day', + 'sales_payment_day', + 'tax_agreement', + 'tax_amount', + 'tax_start_date', + 'tax_end_date', + 'bad_debt', + 'bad_debt_amount', + 'bad_debt_receive_date', + 'bad_debt_end_date', + 'bad_debt_progress', + 'memo', + ]); + }); + } +};