feat: [payables] 미지급금관리 전표 삭제 기능 추가

- 일반전표 상세 행에 삭제 버튼(휴지통 아이콘) 추가
- DELETE /finance/payables/journal-entry/{id} API 추가
- journal_entry_id 필드를 프론트에 전달하도록 쿼리 수정
- 삭제 후 데이터 자동 새로고침
This commit is contained in:
김보곤
2026-03-04 11:02:06 +09:00
parent fa0740bb17
commit 1c8d06eb99
3 changed files with 50 additions and 3 deletions

View File

@@ -313,6 +313,7 @@ public function integrated(Request $request): JsonResponse
$journalDetails = (clone $journalQuery)
->select(
'journal_entry_lines.id',
'journal_entry_lines.journal_entry_id',
'journal_entry_lines.trading_partner_name',
'journal_entry_lines.account_code',
'journal_entry_lines.account_name',
@@ -505,4 +506,21 @@ public function journalPayables(Request $request): JsonResponse
],
]);
}
/**
* 미지급금 관련 전표 강제 삭제 (soft delete)
*/
public function deleteJournalEntry(int $id): JsonResponse
{
$tenantId = session('selected_tenant_id', 1);
$entry = JournalEntry::where('tenant_id', $tenantId)->findOrFail($id);
$entry->delete();
return response()->json([
'success' => true,
'message' => "전표 {$entry->entry_no}이(가) 삭제되었습니다.",
]);
}
}

View File

@@ -48,6 +48,7 @@
const TrendingUp = createIcon('trending-up');
const TrendingDown = createIcon('trending-down');
const Layers = createIcon('layers');
const Trash2 = createIcon('trash-2');
const formatCurrency = (num) => num ? Number(num).toLocaleString() : '0';
const formatInputCurrency = (value) => {
@@ -81,6 +82,25 @@ function IntegratedTab({ startDate, endDate, account, vendorSearch }) {
useEffect(() => { fetchData(); }, [fetchData]);
const handleDeleteJournal = async (journalEntryId, entryNo) => {
if (!confirm(`전표 ${entryNo}을(를) 삭제하시겠습니까?`)) return;
try {
const res = await fetch(`/finance/payables/journal-entry/${journalEntryId}`, {
method: 'DELETE',
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content },
});
const json = await res.json();
if (json.success) {
alert(json.message);
fetchData();
} else {
alert('삭제 실패: ' + (json.message || ''));
}
} catch (err) {
alert('삭제 오류: ' + err.message);
}
};
if (loading) return <LoadingSpinner />;
if (!data) return <EmptyState message="데이터를 불러올 수 없습니다." />;
@@ -233,9 +253,17 @@ function IntegratedTab({ startDate, endDate, account, vendorSearch }) {
{j.dc_type === 'debit' ? '차변(상계)' : '대변(발생)'}
</span>
</div>
<span className={`font-medium ${j.dc_type === 'debit' ? 'text-emerald-600' : 'text-blue-600'}`}>
{formatCurrency(j.dc_type === 'debit' ? j.debit_amount : j.credit_amount)}
</span>
<div className="flex items-center gap-2">
<span className={`font-medium ${j.dc_type === 'debit' ? 'text-emerald-600' : 'text-blue-600'}`}>
{formatCurrency(j.dc_type === 'debit' ? j.debit_amount : j.credit_amount)}
</span>
{j.journal_entry_id && (
<button onClick={(e) => { e.stopPropagation(); handleDeleteJournal(j.journal_entry_id, j.entry_no); }}
className="p-1 text-gray-300 hover:text-red-500 hover:bg-red-50 rounded transition-colors" title="전표 삭제">
<Trash2 className="w-3.5 h-3.5" />
</button>
)}
</div>
</div>
))}
</div>

View File

@@ -1383,6 +1383,7 @@
Route::put('/{id}', [\App\Http\Controllers\Finance\PayableController::class, 'update'])->name('update');
Route::post('/{id}/pay', [\App\Http\Controllers\Finance\PayableController::class, 'pay'])->name('pay');
Route::delete('/{id}', [\App\Http\Controllers\Finance\PayableController::class, 'destroy'])->name('destroy');
Route::delete('/journal-entry/{id}', [\App\Http\Controllers\Finance\PayableController::class, 'deleteJournalEntry'])->name('delete-journal');
});
// 기타