feat: [finance] 경비계정 동기화에 증빙번호(receipt_no) 지원 추가
- StoreManualJournalRequest/UpdateJournalRequest에 receipt_no 필드 추가 - GeneralJournalEntryService: store/update 시 receipt_no 전달 - JournalSyncService: saveForSource에 receiptNo 파라미터 추가 - SyncsExpenseAccounts: 증빙번호 결정 우선순위 (명시 전달 > 바로빌 승인번호 > null) - SOURCE_BAROBILL_CARD를 카드결제 payment_method에 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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'],
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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 정리
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user