feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능: - UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger - 초대 발송/수락/취소/재발송 API 5.2 알림설정 확장: - NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger - 채널별/유형별 알림 설정 관리 5.3 계정정보 수정 API: - 회원탈퇴, 사용중지, 약관동의 관리 - AccountService, AccountController, Swagger 5.4 매출 거래명세서 API: - 거래명세서 조회/발행/이메일발송 - SaleService 확장, Swagger 문서화
This commit is contained in:
@@ -275,4 +275,155 @@ private function generateSaleNumber(int $tenantId, string $saleDate): string
|
||||
|
||||
return $prefix.str_pad($newSeq, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래명세서 조회
|
||||
*/
|
||||
public function getStatement(int $id): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$sale = Sale::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->with(['client', 'deposit', 'creator:id,name'])
|
||||
->findOrFail($id);
|
||||
|
||||
// 거래명세서 데이터 구성
|
||||
return [
|
||||
'statement_number' => $this->generateStatementNumber($sale),
|
||||
'issued_at' => $sale->statement_issued_at,
|
||||
'sale' => $sale,
|
||||
'seller' => $this->getSellerInfo($tenantId),
|
||||
'buyer' => $this->getBuyerInfo($sale->client),
|
||||
'items' => $this->getSaleItems($sale),
|
||||
'summary' => [
|
||||
'supply_amount' => $sale->supply_amount,
|
||||
'tax_amount' => $sale->tax_amount,
|
||||
'total_amount' => $sale->total_amount,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래명세서 발행
|
||||
*/
|
||||
public function issueStatement(int $id): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($id, $tenantId, $userId) {
|
||||
$sale = Sale::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->findOrFail($id);
|
||||
|
||||
// 확정된 매출만 거래명세서 발행 가능
|
||||
if ($sale->status !== 'confirmed') {
|
||||
throw new \Exception(__('error.sale.statement_requires_confirmed'));
|
||||
}
|
||||
|
||||
// 발행 시간 기록
|
||||
$sale->statement_issued_at = now();
|
||||
$sale->statement_issued_by = $userId;
|
||||
$sale->save();
|
||||
|
||||
return [
|
||||
'statement_number' => $this->generateStatementNumber($sale),
|
||||
'issued_at' => $sale->statement_issued_at->toIso8601String(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래명세서 이메일 발송
|
||||
*/
|
||||
public function sendStatement(int $id, array $data): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$sale = Sale::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->with(['client'])
|
||||
->findOrFail($id);
|
||||
|
||||
// 수신자 이메일 확인
|
||||
$recipientEmail = $data['email'] ?? $sale->client?->email;
|
||||
if (empty($recipientEmail)) {
|
||||
throw new \Exception(__('error.sale.recipient_email_required'));
|
||||
}
|
||||
|
||||
// TODO: 실제 이메일 발송 로직 구현
|
||||
// Mail::to($recipientEmail)->send(new SaleStatementMail($sale, $data['message'] ?? null));
|
||||
|
||||
return [
|
||||
'sent_to' => $recipientEmail,
|
||||
'sent_at' => now()->toIso8601String(),
|
||||
'statement_number' => $this->generateStatementNumber($sale),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래명세서 번호 생성
|
||||
*/
|
||||
private function generateStatementNumber(Sale $sale): string
|
||||
{
|
||||
return 'ST'.$sale->sale_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 판매자 정보 조회
|
||||
*/
|
||||
private function getSellerInfo(int $tenantId): array
|
||||
{
|
||||
$tenant = \App\Models\Tenants\Tenant::find($tenantId);
|
||||
|
||||
return [
|
||||
'name' => $tenant->name ?? '',
|
||||
'business_number' => $tenant->business_number ?? '',
|
||||
'representative' => $tenant->representative ?? '',
|
||||
'address' => $tenant->address ?? '',
|
||||
'tel' => $tenant->tel ?? '',
|
||||
'fax' => $tenant->fax ?? '',
|
||||
'email' => $tenant->email ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 구매자 정보 조회
|
||||
*/
|
||||
private function getBuyerInfo($client): array
|
||||
{
|
||||
if (! $client) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $client->name ?? '',
|
||||
'business_number' => $client->business_number ?? '',
|
||||
'representative' => $client->representative ?? '',
|
||||
'address' => $client->address ?? '',
|
||||
'tel' => $client->tel ?? '',
|
||||
'fax' => $client->fax ?? '',
|
||||
'email' => $client->email ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 매출 품목 조회 (향후 확장)
|
||||
*/
|
||||
private function getSaleItems(Sale $sale): array
|
||||
{
|
||||
// TODO: 매출 품목 테이블이 있다면 조회
|
||||
// 현재는 기본 매출 정보만 반환
|
||||
return [
|
||||
[
|
||||
'description' => $sale->description ?? '매출',
|
||||
'quantity' => 1,
|
||||
'unit_price' => $sale->supply_amount,
|
||||
'supply_amount' => $sale->supply_amount,
|
||||
'tax_amount' => $sale->tax_amount,
|
||||
'total_amount' => $sale->total_amount,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user