feat(pricing): 단가관리 stats, bulkDestroy API 추가
- GET /pricing/stats: 단가 통계 조회 (total, draft, finalized, expired) - DELETE /pricing/bulk: 단가 일괄 삭제 (확정된 단가 제외) - PriceBulkDeleteRequest FormRequest 추가 - PricingService.stats(), bulkDestroy() 메서드 구현 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Pricing\PriceBulkDeleteRequest;
|
||||
use App\Http\Requests\Pricing\PriceByItemsRequest;
|
||||
use App\Http\Requests\Pricing\PriceCostRequest;
|
||||
use App\Http\Requests\Pricing\PriceIndexRequest;
|
||||
@@ -124,4 +125,29 @@ public function cost(PriceCostRequest $request)
|
||||
return ['data' => $data, 'message' => __('message.fetched')];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 단가 통계 조회
|
||||
*/
|
||||
public function stats()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
$data = $this->service->stats();
|
||||
|
||||
return ['data' => $data, 'message' => __('message.fetched')];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 단가 일괄 삭제
|
||||
*/
|
||||
public function bulkDestroy(PriceBulkDeleteRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validated();
|
||||
$deletedCount = $this->service->bulkDestroy($validated['ids']);
|
||||
|
||||
return ['data' => ['deleted_count' => $deletedCount], 'message' => __('message.deleted')];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
32
app/Http/Requests/Pricing/PriceBulkDeleteRequest.php
Normal file
32
app/Http/Requests/Pricing/PriceBulkDeleteRequest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Pricing;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PriceBulkDeleteRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'required|integer|min:1',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'ids.required' => __('validation.required', ['attribute' => 'ids']),
|
||||
'ids.array' => __('validation.array', ['attribute' => 'ids']),
|
||||
'ids.min' => __('validation.min.array', ['attribute' => 'ids', 'min' => 1]),
|
||||
'ids.*.required' => __('validation.required', ['attribute' => 'id']),
|
||||
'ids.*.integer' => __('validation.integer', ['attribute' => 'id']),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -537,4 +537,65 @@ private function shouldRecalculateSalesPrice(array $data): bool
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 단가 통계 조회
|
||||
*/
|
||||
public function stats(): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$baseQuery = Price::where('tenant_id', $tenantId);
|
||||
|
||||
$total = (clone $baseQuery)->count();
|
||||
$draft = (clone $baseQuery)->where('status', 'draft')->count();
|
||||
$finalized = (clone $baseQuery)->where('status', 'finalized')->count();
|
||||
$expired = (clone $baseQuery)->where('status', 'expired')->count();
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'draft' => $draft,
|
||||
'finalized' => $finalized,
|
||||
'expired' => $expired,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 단가 일괄 삭제 (soft delete)
|
||||
*/
|
||||
public function bulkDestroy(array $ids): int
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($ids, $tenantId, $userId) {
|
||||
$prices = Price::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $ids)
|
||||
->get();
|
||||
|
||||
if ($prices->isEmpty()) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
$deletedCount = 0;
|
||||
foreach ($prices as $price) {
|
||||
// 확정된 단가는 삭제 불가
|
||||
if ($price->is_final) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 삭제 전 스냅샷 저장
|
||||
$beforeSnapshot = $price->toSnapshot();
|
||||
$this->createRevision($price, $beforeSnapshot, $userId, __('message.pricing.deleted'));
|
||||
|
||||
$price->deleted_by = $userId;
|
||||
$price->save();
|
||||
$price->delete();
|
||||
$deletedCount++;
|
||||
}
|
||||
|
||||
return $deletedCount;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -989,8 +989,10 @@
|
||||
// Pricing (단가 관리)
|
||||
Route::prefix('pricing')->group(function () {
|
||||
Route::get('', [PricingController::class, 'index'])->name('v1.pricing.index'); // 목록
|
||||
Route::get('/stats', [PricingController::class, 'stats'])->name('v1.pricing.stats'); // 통계
|
||||
Route::get('/cost', [PricingController::class, 'cost'])->name('v1.pricing.cost'); // 원가 조회
|
||||
Route::post('/by-items', [PricingController::class, 'byItems'])->name('v1.pricing.by-items'); // 품목별 단가 현황
|
||||
Route::delete('/bulk', [PricingController::class, 'bulkDestroy'])->name('v1.pricing.bulk-destroy'); // 일괄 삭제
|
||||
Route::post('', [PricingController::class, 'store'])->name('v1.pricing.store'); // 등록
|
||||
Route::get('/{id}', [PricingController::class, 'show'])->whereNumber('id')->name('v1.pricing.show'); // 상세
|
||||
Route::put('/{id}', [PricingController::class, 'update'])->whereNumber('id')->name('v1.pricing.update'); // 수정
|
||||
|
||||
Reference in New Issue
Block a user