feat: [재무] 어음 V8 + 상품권 접대비 연동 + 일반전표/계정과목 API
- Bill 확장 필드 (V8), Loan 상품권 카테고리/접대비 자동 연동 - GeneralJournalEntry CRUD, AccountSubject API - 접대비/복리후생비 날짜 필터, 매출채권 soft delete 제외 - 바로빌 연동 API 엔드포인트 추가 - 부가세 상세 조회 API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ public function rules(): array
|
||||
return [
|
||||
'user_id' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'status' => ['nullable', 'string', Rule::in(Loan::STATUSES)],
|
||||
'category' => ['nullable', 'string', Rule::in(Loan::CATEGORIES)],
|
||||
'start_date' => ['nullable', 'date', 'date_format:Y-m-d'],
|
||||
'end_date' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:start_date'],
|
||||
'search' => ['nullable', 'string', 'max:100'],
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Http\Requests\Loan;
|
||||
|
||||
use App\Models\Tenants\Loan;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class LoanStoreRequest extends FormRequest
|
||||
{
|
||||
@@ -21,12 +23,27 @@ public function authorize(): bool
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$isGiftCertificate = $this->input('category') === Loan::CATEGORY_GIFT_CERTIFICATE;
|
||||
|
||||
return [
|
||||
'user_id' => ['required', 'integer', 'exists:users,id'],
|
||||
'user_id' => [$isGiftCertificate ? 'nullable' : 'required', 'integer', 'exists:users,id'],
|
||||
'loan_date' => ['required', 'date', 'date_format:Y-m-d'],
|
||||
'amount' => ['required', 'numeric', 'min:0', 'max:999999999999.99'],
|
||||
'purpose' => ['nullable', 'string', 'max:1000'],
|
||||
'withdrawal_id' => ['nullable', 'integer', 'exists:withdrawals,id'],
|
||||
'category' => ['nullable', 'string', Rule::in(Loan::CATEGORIES)],
|
||||
'status' => ['nullable', 'string', Rule::in(Loan::STATUSES)],
|
||||
'metadata' => ['nullable', 'array'],
|
||||
'metadata.serial_number' => ['nullable', 'string', 'max:100'],
|
||||
'metadata.cert_name' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.vendor_id' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.vendor_name' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.purchase_purpose' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.entertainment_expense' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.recipient_name' => ['nullable', 'string', 'max:100'],
|
||||
'metadata.recipient_organization' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.usage_description' => ['nullable', 'string', 'max:1000'],
|
||||
'metadata.memo' => ['nullable', 'string', 'max:2000'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Http\Requests\Loan;
|
||||
|
||||
use App\Models\Tenants\Loan;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class LoanUpdateRequest extends FormRequest
|
||||
{
|
||||
@@ -27,6 +29,20 @@ public function rules(): array
|
||||
'amount' => ['sometimes', 'numeric', 'min:0', 'max:999999999999.99'],
|
||||
'purpose' => ['nullable', 'string', 'max:1000'],
|
||||
'withdrawal_id' => ['nullable', 'integer', 'exists:withdrawals,id'],
|
||||
'category' => ['sometimes', 'string', Rule::in(Loan::CATEGORIES)],
|
||||
'status' => ['sometimes', 'string', Rule::in(Loan::STATUSES)],
|
||||
'settlement_date' => ['nullable', 'date', 'date_format:Y-m-d'],
|
||||
'metadata' => ['nullable', 'array'],
|
||||
'metadata.serial_number' => ['nullable', 'string', 'max:100'],
|
||||
'metadata.cert_name' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.vendor_id' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.vendor_name' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.purchase_purpose' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.entertainment_expense' => ['nullable', 'string', 'max:50'],
|
||||
'metadata.recipient_name' => ['nullable', 'string', 'max:100'],
|
||||
'metadata.recipient_organization' => ['nullable', 'string', 'max:200'],
|
||||
'metadata.usage_description' => ['nullable', 'string', 'max:1000'],
|
||||
'metadata.memo' => ['nullable', 'string', 'max:2000'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\AccountSubject;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreAccountSubjectRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'code' => ['required', 'string', 'max:10'],
|
||||
'name' => ['required', 'string', 'max:100'],
|
||||
'category' => ['nullable', 'string', 'in:asset,liability,capital,revenue,expense'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'code.required' => '계정과목 코드를 입력하세요.',
|
||||
'name.required' => '계정과목명을 입력하세요.',
|
||||
'category.in' => '유효한 분류를 선택하세요.',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public function rules(): array
|
||||
$tenantId = app('tenant_id') ?? 0;
|
||||
|
||||
return [
|
||||
// === 기존 필드 ===
|
||||
'bill_number' => [
|
||||
'nullable',
|
||||
'string',
|
||||
@@ -30,16 +31,99 @@ public function rules(): array
|
||||
'client_name' => ['nullable', 'string', 'max:100'],
|
||||
'amount' => ['required', 'numeric', 'min:0'],
|
||||
'issue_date' => ['required', 'date'],
|
||||
'maturity_date' => ['required', 'date', 'after_or_equal:issue_date'],
|
||||
'status' => ['nullable', 'string', 'in:stored,maturityAlert,maturityResult,paymentComplete,dishonored,collectionRequest,collectionComplete,suing'],
|
||||
'maturity_date' => ['nullable', 'date', 'after_or_equal:issue_date'],
|
||||
'status' => ['nullable', 'string', 'max:30'],
|
||||
'reason' => ['nullable', 'string', 'max:255'],
|
||||
'installment_count' => ['nullable', 'integer', 'min:0'],
|
||||
'note' => ['nullable', 'string', 'max:1000'],
|
||||
'is_electronic' => ['nullable', 'boolean'],
|
||||
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
|
||||
|
||||
// === V8 증권종류/매체/구분 ===
|
||||
'instrument_type' => ['nullable', 'string', 'in:promissory,exchange,cashierCheck,currentCheck'],
|
||||
'medium' => ['nullable', 'string', 'in:electronic,paper'],
|
||||
'bill_category' => ['nullable', 'string', 'in:commercial,other'],
|
||||
|
||||
// === 전자어음 ===
|
||||
'electronic_bill_no' => ['nullable', 'string', 'max:100'],
|
||||
'registration_org' => ['nullable', 'string', 'in:kftc,bank'],
|
||||
|
||||
// === 환어음 ===
|
||||
'drawee' => ['nullable', 'string', 'max:100'],
|
||||
'acceptance_status' => ['nullable', 'string', 'in:accepted,pending,refused'],
|
||||
'acceptance_date' => ['nullable', 'date'],
|
||||
'acceptance_refusal_date' => ['nullable', 'date'],
|
||||
'acceptance_refusal_reason' => ['nullable', 'string', 'max:50'],
|
||||
|
||||
// === 받을어음 전용 ===
|
||||
'endorsement' => ['nullable', 'string', 'in:endorsable,nonEndorsable'],
|
||||
'endorsement_order' => ['nullable', 'string', 'max:5'],
|
||||
'storage_place' => ['nullable', 'string', 'in:safe,bank,other'],
|
||||
'issuer_bank' => ['nullable', 'string', 'max:100'],
|
||||
|
||||
// 할인
|
||||
'is_discounted' => ['nullable', 'boolean'],
|
||||
'discount_date' => ['nullable', 'date'],
|
||||
'discount_bank' => ['nullable', 'string', 'max:100'],
|
||||
'discount_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||
'discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
|
||||
// 배서양도
|
||||
'endorsement_date' => ['nullable', 'date'],
|
||||
'endorsee' => ['nullable', 'string', 'max:100'],
|
||||
'endorsement_reason' => ['nullable', 'string', 'in:payment,guarantee,collection,other'],
|
||||
|
||||
// 추심
|
||||
'collection_bank' => ['nullable', 'string', 'max:100'],
|
||||
'collection_request_date' => ['nullable', 'date'],
|
||||
'collection_fee' => ['nullable', 'numeric', 'min:0'],
|
||||
'collection_complete_date' => ['nullable', 'date'],
|
||||
'collection_result' => ['nullable', 'string', 'in:success,partial,failed,pending'],
|
||||
'collection_deposit_date' => ['nullable', 'date'],
|
||||
'collection_deposit_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
|
||||
// === 지급어음 전용 ===
|
||||
'settlement_bank' => ['nullable', 'string', 'max:100'],
|
||||
'payment_method' => ['nullable', 'string', 'in:autoTransfer,currentAccount,other'],
|
||||
'actual_payment_date' => ['nullable', 'date'],
|
||||
|
||||
// === 공통 ===
|
||||
'payment_place' => ['nullable', 'string', 'max:30'],
|
||||
'payment_place_detail' => ['nullable', 'string', 'max:200'],
|
||||
|
||||
// 개서
|
||||
'renewal_date' => ['nullable', 'date'],
|
||||
'renewal_new_bill_no' => ['nullable', 'string', 'max:50'],
|
||||
'renewal_reason' => ['nullable', 'string', 'in:maturityExtension,amountChange,conditionChange,other'],
|
||||
|
||||
// 소구
|
||||
'recourse_date' => ['nullable', 'date'],
|
||||
'recourse_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'recourse_target' => ['nullable', 'string', 'max:100'],
|
||||
'recourse_reason' => ['nullable', 'string', 'in:endorsedDishonor,discountDishonor,other'],
|
||||
|
||||
// 환매
|
||||
'buyback_date' => ['nullable', 'date'],
|
||||
'buyback_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'buyback_bank' => ['nullable', 'string', 'max:100'],
|
||||
|
||||
// 부도/법적절차
|
||||
'dishonored_date' => ['nullable', 'date'],
|
||||
'dishonored_reason' => ['nullable', 'string', 'max:30'],
|
||||
'has_protest' => ['nullable', 'boolean'],
|
||||
'protest_date' => ['nullable', 'date'],
|
||||
'recourse_notice_date' => ['nullable', 'date'],
|
||||
'recourse_notice_deadline' => ['nullable', 'date'],
|
||||
|
||||
// 분할배서
|
||||
'is_split' => ['nullable', 'boolean'],
|
||||
|
||||
// === 차수 관리 ===
|
||||
'installments' => ['nullable', 'array'],
|
||||
'installments.*.date' => ['required_with:installments', 'date'],
|
||||
'installments.*.amount' => ['required_with:installments', 'numeric', 'min:0'],
|
||||
'installments.*.type' => ['nullable', 'string', 'max:30'],
|
||||
'installments.*.counterparty' => ['nullable', 'string', 'max:100'],
|
||||
'installments.*.note' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public function authorize(): bool
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// === 기존 필드 ===
|
||||
'bill_number' => ['nullable', 'string', 'max:50'],
|
||||
'bill_type' => ['nullable', 'string', 'in:received,issued'],
|
||||
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
|
||||
@@ -21,15 +22,72 @@ public function rules(): array
|
||||
'amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'issue_date' => ['nullable', 'date'],
|
||||
'maturity_date' => ['nullable', 'date', 'after_or_equal:issue_date'],
|
||||
'status' => ['nullable', 'string', 'in:stored,maturityAlert,maturityResult,paymentComplete,dishonored,collectionRequest,collectionComplete,suing'],
|
||||
'status' => ['nullable', 'string', 'max:30'],
|
||||
'reason' => ['nullable', 'string', 'max:255'],
|
||||
'installment_count' => ['nullable', 'integer', 'min:0'],
|
||||
'note' => ['nullable', 'string', 'max:1000'],
|
||||
'is_electronic' => ['nullable', 'boolean'],
|
||||
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
|
||||
|
||||
// === V8 확장 ===
|
||||
'instrument_type' => ['nullable', 'string', 'in:promissory,exchange,cashierCheck,currentCheck'],
|
||||
'medium' => ['nullable', 'string', 'in:electronic,paper'],
|
||||
'bill_category' => ['nullable', 'string', 'in:commercial,other'],
|
||||
'electronic_bill_no' => ['nullable', 'string', 'max:100'],
|
||||
'registration_org' => ['nullable', 'string', 'in:kftc,bank'],
|
||||
'drawee' => ['nullable', 'string', 'max:100'],
|
||||
'acceptance_status' => ['nullable', 'string', 'in:accepted,pending,refused'],
|
||||
'acceptance_date' => ['nullable', 'date'],
|
||||
'acceptance_refusal_date' => ['nullable', 'date'],
|
||||
'acceptance_refusal_reason' => ['nullable', 'string', 'max:50'],
|
||||
'endorsement' => ['nullable', 'string', 'in:endorsable,nonEndorsable'],
|
||||
'endorsement_order' => ['nullable', 'string', 'max:5'],
|
||||
'storage_place' => ['nullable', 'string', 'in:safe,bank,other'],
|
||||
'issuer_bank' => ['nullable', 'string', 'max:100'],
|
||||
'is_discounted' => ['nullable', 'boolean'],
|
||||
'discount_date' => ['nullable', 'date'],
|
||||
'discount_bank' => ['nullable', 'string', 'max:100'],
|
||||
'discount_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||
'discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'endorsement_date' => ['nullable', 'date'],
|
||||
'endorsee' => ['nullable', 'string', 'max:100'],
|
||||
'endorsement_reason' => ['nullable', 'string', 'in:payment,guarantee,collection,other'],
|
||||
'collection_bank' => ['nullable', 'string', 'max:100'],
|
||||
'collection_request_date' => ['nullable', 'date'],
|
||||
'collection_fee' => ['nullable', 'numeric', 'min:0'],
|
||||
'collection_complete_date' => ['nullable', 'date'],
|
||||
'collection_result' => ['nullable', 'string', 'in:success,partial,failed,pending'],
|
||||
'collection_deposit_date' => ['nullable', 'date'],
|
||||
'collection_deposit_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'settlement_bank' => ['nullable', 'string', 'max:100'],
|
||||
'payment_method' => ['nullable', 'string', 'in:autoTransfer,currentAccount,other'],
|
||||
'actual_payment_date' => ['nullable', 'date'],
|
||||
'payment_place' => ['nullable', 'string', 'max:30'],
|
||||
'payment_place_detail' => ['nullable', 'string', 'max:200'],
|
||||
'renewal_date' => ['nullable', 'date'],
|
||||
'renewal_new_bill_no' => ['nullable', 'string', 'max:50'],
|
||||
'renewal_reason' => ['nullable', 'string', 'in:maturityExtension,amountChange,conditionChange,other'],
|
||||
'recourse_date' => ['nullable', 'date'],
|
||||
'recourse_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'recourse_target' => ['nullable', 'string', 'max:100'],
|
||||
'recourse_reason' => ['nullable', 'string', 'in:endorsedDishonor,discountDishonor,other'],
|
||||
'buyback_date' => ['nullable', 'date'],
|
||||
'buyback_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'buyback_bank' => ['nullable', 'string', 'max:100'],
|
||||
'dishonored_date' => ['nullable', 'date'],
|
||||
'dishonored_reason' => ['nullable', 'string', 'max:30'],
|
||||
'has_protest' => ['nullable', 'boolean'],
|
||||
'protest_date' => ['nullable', 'date'],
|
||||
'recourse_notice_date' => ['nullable', 'date'],
|
||||
'recourse_notice_deadline' => ['nullable', 'date'],
|
||||
'is_split' => ['nullable', 'boolean'],
|
||||
|
||||
// === 차수 관리 ===
|
||||
'installments' => ['nullable', 'array'],
|
||||
'installments.*.date' => ['required_with:installments', 'date'],
|
||||
'installments.*.amount' => ['required_with:installments', 'numeric', 'min:0'],
|
||||
'installments.*.type' => ['nullable', 'string', 'max:30'],
|
||||
'installments.*.counterparty' => ['nullable', 'string', 'max:100'],
|
||||
'installments.*.note' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\GeneralJournalEntry;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreManualJournalRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'journal_date' => ['required', 'date'],
|
||||
'description' => ['nullable', 'string', 'max:500'],
|
||||
'rows' => ['required', 'array', 'min:2'],
|
||||
'rows.*.side' => ['required', 'in:debit,credit'],
|
||||
'rows.*.account_subject_id' => ['required', 'string', 'max:10'],
|
||||
'rows.*.vendor_id' => ['nullable', 'integer'],
|
||||
'rows.*.debit_amount' => ['required', 'integer', 'min:0'],
|
||||
'rows.*.credit_amount' => ['required', 'integer', 'min:0'],
|
||||
'rows.*.memo' => ['nullable', 'string', 'max:300'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'journal_date.required' => '전표일자를 입력하세요.',
|
||||
'rows.required' => '분개 행을 입력하세요.',
|
||||
'rows.min' => '최소 2개 이상의 분개 행이 필요합니다.',
|
||||
'rows.*.side.required' => '차/대 구분을 선택하세요.',
|
||||
'rows.*.side.in' => '차/대 구분이 올바르지 않습니다.',
|
||||
'rows.*.account_subject_id.required' => '계정과목을 선택하세요.',
|
||||
'rows.*.debit_amount.required' => '차변 금액을 입력하세요.',
|
||||
'rows.*.credit_amount.required' => '대변 금액을 입력하세요.',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\GeneralJournalEntry;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateJournalRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'journal_memo' => ['sometimes', 'nullable', 'string', 'max:1000'],
|
||||
'rows' => ['sometimes', 'array', 'min:1'],
|
||||
'rows.*.side' => ['required_with:rows', 'in:debit,credit'],
|
||||
'rows.*.account_subject_id' => ['required_with:rows', 'string', 'max:10'],
|
||||
'rows.*.vendor_id' => ['nullable', 'integer'],
|
||||
'rows.*.debit_amount' => ['required_with:rows', 'integer', 'min:0'],
|
||||
'rows.*.credit_amount' => ['required_with:rows', 'integer', 'min:0'],
|
||||
'rows.*.memo' => ['nullable', 'string', 'max:300'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'rows.*.side.required_with' => '차/대 구분을 선택하세요.',
|
||||
'rows.*.side.in' => '차/대 구분이 올바르지 않습니다.',
|
||||
'rows.*.account_subject_id.required_with' => '계정과목을 선택하세요.',
|
||||
'rows.*.debit_amount.required_with' => '차변 금액을 입력하세요.',
|
||||
'rows.*.credit_amount.required_with' => '대변 금액을 입력하세요.',
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user