diff --git a/app/Models/Tenants/ExpenseAccount.php b/app/Models/Tenants/ExpenseAccount.php index b57c677..c9fe540 100644 --- a/app/Models/Tenants/ExpenseAccount.php +++ b/app/Models/Tenants/ExpenseAccount.php @@ -34,6 +34,7 @@ class ExpenseAccount extends Model 'vendor_name', 'payment_method', 'card_no', + 'loan_id', 'created_by', 'updated_by', 'deleted_by', @@ -53,6 +54,9 @@ class ExpenseAccount extends Model public const TYPE_OFFICE = 'office'; + // 세부 유형 상수 (접대비) + public const SUB_TYPE_GIFT_CERTIFICATE = 'gift_certificate'; + // 세부 유형 상수 (복리후생) public const SUB_TYPE_MEAL = 'meal'; diff --git a/app/Services/LoanService.php b/app/Services/LoanService.php index d674e11..ac242ec 100644 --- a/app/Services/LoanService.php +++ b/app/Services/LoanService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\Tenants\ExpenseAccount; use App\Models\Tenants\Loan; use App\Models\Tenants\Withdrawal; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -261,9 +262,56 @@ public function update(int $id, array $data): Loan $loan->save(); + // 상품권 → 접대비 자동 연동 + if ($loan->category === Loan::CATEGORY_GIFT_CERTIFICATE) { + $this->syncGiftCertificateExpense($loan); + } + return $loan->fresh(['user:id,name,email', 'creator:id,name']); } + /** + * 상품권 → 접대비 자동 연동 + * + * 상태가 'used' + entertainment_expense='applicable' → expense_accounts에 INSERT + * 그 외 → 기존 연결된 expense_accounts 삭제 + */ + private function syncGiftCertificateExpense(Loan $loan): void + { + $metadata = $loan->metadata ?? []; + $isEntertainment = ($loan->status === Loan::STATUS_USED) + && ($metadata['entertainment_expense'] ?? '') === 'applicable'; + + if ($isEntertainment) { + // upsert: loan_id 기준으로 있으면 업데이트, 없으면 생성 + ExpenseAccount::query() + ->updateOrCreate( + [ + 'tenant_id' => $loan->tenant_id, + 'loan_id' => $loan->id, + ], + [ + 'account_type' => ExpenseAccount::TYPE_ENTERTAINMENT, + 'sub_type' => ExpenseAccount::SUB_TYPE_GIFT_CERTIFICATE, + 'expense_date' => $loan->settlement_date ?? $loan->loan_date, + 'amount' => $loan->amount, + 'description' => ($metadata['cert_name'] ?? '상품권') . ' 접대비 전환', + 'vendor_name' => $metadata['vendor_name'] ?? null, + 'vendor_id' => ! empty($metadata['vendor_id']) ? (int) $metadata['vendor_id'] : null, + 'payment_method' => ExpenseAccount::PAYMENT_CASH, + 'created_by' => $loan->updated_by ?? $loan->created_by, + 'updated_by' => $loan->updated_by ?? $loan->created_by, + ] + ); + } else { + // 접대비 해당이 아니면 연결된 레코드 삭제 + ExpenseAccount::query() + ->where('tenant_id', $loan->tenant_id) + ->where('loan_id', $loan->id) + ->delete(); + } + } + /** * 가지급금 삭제 */ @@ -280,6 +328,14 @@ public function destroy(int $id): bool throw new BadRequestHttpException(__('error.loan.not_deletable')); } + // 상품권 연결 접대비 레코드도 삭제 + if ($loan->category === Loan::CATEGORY_GIFT_CERTIFICATE) { + ExpenseAccount::query() + ->where('tenant_id', $tenantId) + ->where('loan_id', $loan->id) + ->delete(); + } + $loan->deleted_by = $userId; $loan->save(); $loan->delete(); diff --git a/database/migrations/2026_03_05_210000_add_loan_id_to_expense_accounts_table.php b/database/migrations/2026_03_05_210000_add_loan_id_to_expense_accounts_table.php new file mode 100644 index 0000000..10d4a43 --- /dev/null +++ b/database/migrations/2026_03_05_210000_add_loan_id_to_expense_accounts_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('loan_id')->nullable()->after('card_no') + ->comment('연결된 가지급금 ID (상품권→접대비 전환 시)'); + + $table->index('loan_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('expense_accounts', function (Blueprint $table) { + $table->dropIndex(['loan_id']); + $table->dropColumn('loan_id'); + }); + } +};