diff --git a/app/Http/Requests/V1/GeneralJournalEntry/StoreManualJournalRequest.php b/app/Http/Requests/V1/GeneralJournalEntry/StoreManualJournalRequest.php index 4508680a..389f8e48 100644 --- a/app/Http/Requests/V1/GeneralJournalEntry/StoreManualJournalRequest.php +++ b/app/Http/Requests/V1/GeneralJournalEntry/StoreManualJournalRequest.php @@ -16,6 +16,7 @@ public function rules(): array return [ 'journal_date' => ['required', 'date'], 'description' => ['nullable', 'string', 'max:500'], + 'receipt_no' => ['nullable', 'string', 'max:100'], 'rows' => ['required', 'array', 'min:2'], 'rows.*.side' => ['required', 'in:debit,credit'], 'rows.*.account_subject_id' => ['required', 'string', 'max:10'], diff --git a/app/Http/Requests/V1/GeneralJournalEntry/UpdateJournalRequest.php b/app/Http/Requests/V1/GeneralJournalEntry/UpdateJournalRequest.php index cf3d3648..7b22451b 100644 --- a/app/Http/Requests/V1/GeneralJournalEntry/UpdateJournalRequest.php +++ b/app/Http/Requests/V1/GeneralJournalEntry/UpdateJournalRequest.php @@ -15,6 +15,7 @@ public function rules(): array { return [ 'journal_memo' => ['sometimes', 'nullable', 'string', 'max:1000'], + 'receipt_no' => ['nullable', 'string', 'max:100'], 'rows' => ['sometimes', 'array', 'min:1'], 'rows.*.side' => ['required_with:rows', 'in:debit,credit'], 'rows.*.account_subject_id' => ['required_with:rows', 'string', 'max:10'], diff --git a/app/Services/GeneralJournalEntryService.php b/app/Services/GeneralJournalEntryService.php index 7fa36249..30eda5c7 100644 --- a/app/Services/GeneralJournalEntryService.php +++ b/app/Services/GeneralJournalEntryService.php @@ -329,7 +329,8 @@ public function store(array $data): JournalEntry $this->createLines($entry, $data['rows'], $tenantId); // expense_accounts 동기화 (복리후생비/접대비 → CEO 대시보드) - $this->syncExpenseAccounts($entry); + $receiptNo = $data['receipt_no'] ?? null; + $this->syncExpenseAccounts($entry, $receiptNo); return $entry->load('lines'); }); @@ -379,7 +380,8 @@ public function updateJournal(int $id, array $data): JournalEntry $entry->save(); // expense_accounts 동기화 (복리후생비/접대비 → CEO 대시보드) - $this->syncExpenseAccounts($entry); + $receiptNo = $data['receipt_no'] ?? null; + $this->syncExpenseAccounts($entry, $receiptNo); return $entry->load('lines'); }); diff --git a/app/Services/JournalSyncService.php b/app/Services/JournalSyncService.php index f42cd5a3..046923a0 100644 --- a/app/Services/JournalSyncService.php +++ b/app/Services/JournalSyncService.php @@ -33,10 +33,11 @@ public function saveForSource( string $entryDate, ?string $description, array $rows, + ?string $receiptNo = null, ): JournalEntry { $tenantId = $this->tenantId(); - return DB::transaction(function () use ($sourceType, $sourceKey, $entryDate, $description, $rows, $tenantId) { + return DB::transaction(function () use ($sourceType, $sourceKey, $entryDate, $description, $rows, $tenantId, $receiptNo) { // 기존 전표가 있으면 삭제 후 재생성 (교체 방식) $existing = JournalEntry::query() ->where('tenant_id', $tenantId) @@ -96,7 +97,7 @@ public function saveForSource( } // expense_accounts 동기화 (복리후생비/접대비 → CEO 대시보드) - $this->syncExpenseAccounts($entry); + $this->syncExpenseAccounts($entry, $receiptNo); return $entry->load('lines'); }); diff --git a/app/Traits/SyncsExpenseAccounts.php b/app/Traits/SyncsExpenseAccounts.php index 4216994f..5c43ad28 100644 --- a/app/Traits/SyncsExpenseAccounts.php +++ b/app/Traits/SyncsExpenseAccounts.php @@ -2,6 +2,7 @@ namespace App\Traits; +use App\Models\Barobill\BarobillCardTransaction; use App\Models\Tenants\ExpenseAccount; use App\Models\Tenants\JournalEntry; @@ -23,8 +24,10 @@ trait SyncsExpenseAccounts /** * 전표 저장/수정 후 expense_accounts 동기화 * 복리후생비/접대비 계정과목이 포함된 lines → expense_accounts에 반영 + * + * @param string|null $receiptNo 수기 전표 등에서 직접 전달하는 증빙번호 */ - protected function syncExpenseAccounts(JournalEntry $entry): void + protected function syncExpenseAccounts(JournalEntry $entry, ?string $receiptNo = null): void { $tenantId = $entry->tenant_id; @@ -33,7 +36,10 @@ protected function syncExpenseAccounts(JournalEntry $entry): void ->where('journal_entry_id', $entry->id) ->forceDelete(); - // 2. 현재 lines 중 복리후생비/접대비 해당하는 것만 insert + // 2. 증빙번호 결정: 명시 전달 > 바로빌 승인번호 > null + $resolvedReceiptNo = $receiptNo ?? $this->resolveReceiptNo($entry); + + // 3. 현재 lines 중 복리후생비/접대비 해당하는 것만 insert $lines = $entry->lines()->get(); foreach ($lines as $line) { @@ -50,7 +56,8 @@ protected function syncExpenseAccounts(JournalEntry $entry): void // source_type에 따라 payment_method 결정 $paymentMethod = match ($entry->source_type) { - JournalEntry::SOURCE_CARD_TRANSACTION => ExpenseAccount::PAYMENT_CARD, + JournalEntry::SOURCE_CARD_TRANSACTION, + JournalEntry::SOURCE_BAROBILL_CARD => ExpenseAccount::PAYMENT_CARD, default => ExpenseAccount::PAYMENT_TRANSFER, }; @@ -61,7 +68,7 @@ protected function syncExpenseAccounts(JournalEntry $entry): void 'expense_date' => $entry->entry_date, 'amount' => $amount, 'description' => $line->description ?? $entry->description, - 'receipt_no' => null, + 'receipt_no' => $resolvedReceiptNo, 'vendor_id' => $line->trading_partner_id, 'vendor_name' => $line->trading_partner_name, 'payment_method' => $paymentMethod, @@ -72,6 +79,24 @@ protected function syncExpenseAccounts(JournalEntry $entry): void } } + /** + * source_type에 따라 증빙번호(receipt_no) 자동 조회 + * - 바로빌 카드 거래: approval_num (카드 승인번호) + */ + private function resolveReceiptNo(JournalEntry $entry): ?string + { + if ($entry->source_type === JournalEntry::SOURCE_BAROBILL_CARD && $entry->source_key) { + // source_key = "barobill_card_{id}" → BarobillCardTransaction.approval_num + if (preg_match('/barobill_card_(\d+)/', $entry->source_key, $matches)) { + $tx = BarobillCardTransaction::find((int) $matches[1]); + + return $tx?->approval_num; + } + } + + return null; + } + /** * 전표 삭제 시 expense_accounts 정리 */