feat: 세금계산서/거래명세서 일괄 발행 API 추가

- POST /api/v1/tax-invoices/bulk-issue: 세금계산서 일괄 발행
- POST /api/v1/sales/bulk-issue-statement: 거래명세서 일괄 발행
- FormRequest 검증 (최대 100건)
- Service 일괄 처리 로직 (개별 오류 처리)
- Swagger 문서 추가
- i18n 메시지 키 추가 (ko/en)
This commit is contained in:
2026-01-19 20:53:36 +09:00
parent 7dd683ace8
commit 0b94da0741
12 changed files with 444 additions and 0 deletions

View File

@@ -365,6 +365,68 @@ public function issueStatement(int $id): array
});
}
/**
* 거래명세서 일괄 발행
*
* @param array<int> $ids 발행할 매출 ID 배열
* @return array{issued: int, failed: int, errors: array<int, string>}
*/
public function bulkIssueStatement(array $ids): array
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$results = [
'issued' => 0,
'failed' => 0,
'errors' => [],
];
$sales = Sale::query()
->where('tenant_id', $tenantId)
->whereIn('id', $ids)
->get();
foreach ($sales as $sale) {
try {
// 확정된 매출만 거래명세서 발행 가능
if ($sale->status !== 'confirmed') {
$results['errors'][$sale->id] = __('error.sale.statement_requires_confirmed');
$results['failed']++;
continue;
}
// 이미 발행된 경우
if ($sale->statement_issued_at !== null) {
$results['errors'][$sale->id] = __('error.sale.statement_already_issued');
$results['failed']++;
continue;
}
// 발행 시간 기록
$sale->statement_issued_at = now();
$sale->statement_issued_by = $userId;
$sale->save();
$results['issued']++;
} catch (\Throwable $e) {
$results['errors'][$sale->id] = $e->getMessage();
$results['failed']++;
}
}
// 요청된 ID 중 찾지 못한 것들도 실패 처리
$foundIds = $sales->pluck('id')->toArray();
$notFoundIds = array_diff($ids, $foundIds);
foreach ($notFoundIds as $notFoundId) {
$results['errors'][$notFoundId] = __('error.sale.not_found');
$results['failed']++;
}
return $results;
}
/**
* 거래명세서 이메일 발송
*/