feat: 매입 일괄 업데이트 API 추가
- 매입유형 일괄 변경 API (POST /purchases/bulk-update-type) - 세금계산서 수취 일괄 설정 API (POST /purchases/bulk-update-tax-received) - FormRequest 검증 클래스 추가 - Swagger 문서 추가
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\V1\Purchase\BulkUpdatePurchaseTypeRequest;
|
||||
use App\Http\Requests\V1\Purchase\BulkUpdateTaxReceivedRequest;
|
||||
use App\Http\Requests\V1\Purchase\StorePurchaseRequest;
|
||||
use App\Http\Requests\V1\Purchase\UpdatePurchaseRequest;
|
||||
use App\Services\PurchaseService;
|
||||
@@ -103,4 +105,36 @@ public function summary(Request $request)
|
||||
|
||||
return ApiResponse::success($summary, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 매입유형 일괄 변경
|
||||
*/
|
||||
public function bulkUpdatePurchaseType(BulkUpdatePurchaseTypeRequest $request)
|
||||
{
|
||||
$updatedCount = $this->service->bulkUpdatePurchaseType(
|
||||
$request->getIds(),
|
||||
$request->getPurchaseType()
|
||||
);
|
||||
|
||||
return ApiResponse::success(
|
||||
['updated_count' => $updatedCount],
|
||||
__('message.bulk_updated')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 세금계산서 수취 일괄 설정
|
||||
*/
|
||||
public function bulkUpdateTaxReceived(BulkUpdateTaxReceivedRequest $request)
|
||||
{
|
||||
$updatedCount = $this->service->bulkUpdateTaxReceived(
|
||||
$request->getIds(),
|
||||
$request->getTaxInvoiceReceived()
|
||||
);
|
||||
|
||||
return ApiResponse::success(
|
||||
['updated_count' => $updatedCount],
|
||||
__('message.bulk_updated')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\V1\Purchase;
|
||||
|
||||
use App\Models\Tenants\Purchase;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* 매입유형 일괄 변경 요청 검증
|
||||
*/
|
||||
class BulkUpdatePurchaseTypeRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* 권한 확인
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 유효성 검사 규칙
|
||||
*
|
||||
* @return array<string, array<int, mixed>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => ['required', 'array', 'min:1'],
|
||||
'ids.*' => ['required', 'integer', 'min:1'],
|
||||
'purchase_type' => ['required', 'string', Rule::in(array_keys(Purchase::PURCHASE_TYPES))],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 유효성 검사 메시지
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'ids.required' => __('validation.required', ['attribute' => 'ID 목록']),
|
||||
'ids.array' => __('validation.array', ['attribute' => 'ID 목록']),
|
||||
'ids.min' => __('validation.min.array', ['attribute' => 'ID 목록', 'min' => 1]),
|
||||
'ids.*.required' => __('validation.required', ['attribute' => 'ID']),
|
||||
'ids.*.integer' => __('validation.integer', ['attribute' => 'ID']),
|
||||
'ids.*.min' => __('validation.min.numeric', ['attribute' => 'ID', 'min' => 1]),
|
||||
'purchase_type.required' => __('validation.required', ['attribute' => '매입유형']),
|
||||
'purchase_type.string' => __('validation.string', ['attribute' => '매입유형']),
|
||||
'purchase_type.in' => __('validation.in', ['attribute' => '매입유형']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증된 ID 배열 반환
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public function getIds(): array
|
||||
{
|
||||
return $this->validated('ids');
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증된 매입유형 반환
|
||||
*/
|
||||
public function getPurchaseType(): string
|
||||
{
|
||||
return $this->validated('purchase_type');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\V1\Purchase;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* 세금계산서 수취 일괄 설정 요청 검증
|
||||
*/
|
||||
class BulkUpdateTaxReceivedRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* 권한 확인
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 유효성 검사 규칙
|
||||
*
|
||||
* @return array<string, array<int, mixed>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => ['required', 'array', 'min:1'],
|
||||
'ids.*' => ['required', 'integer', 'min:1'],
|
||||
'tax_invoice_received' => ['required', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 유효성 검사 메시지
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'ids.required' => __('validation.required', ['attribute' => 'ID 목록']),
|
||||
'ids.array' => __('validation.array', ['attribute' => 'ID 목록']),
|
||||
'ids.min' => __('validation.min.array', ['attribute' => 'ID 목록', 'min' => 1]),
|
||||
'ids.*.required' => __('validation.required', ['attribute' => 'ID']),
|
||||
'ids.*.integer' => __('validation.integer', ['attribute' => 'ID']),
|
||||
'ids.*.min' => __('validation.min.numeric', ['attribute' => 'ID', 'min' => 1]),
|
||||
'tax_invoice_received.required' => __('validation.required', ['attribute' => '세금계산서 수취 여부']),
|
||||
'tax_invoice_received.boolean' => __('validation.boolean', ['attribute' => '세금계산서 수취 여부']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증된 ID 배열 반환
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public function getIds(): array
|
||||
{
|
||||
return $this->validated('ids');
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증된 세금계산서 수취 여부 반환
|
||||
*/
|
||||
public function getTaxInvoiceReceived(): bool
|
||||
{
|
||||
return $this->validated('tax_invoice_received');
|
||||
}
|
||||
}
|
||||
@@ -264,6 +264,44 @@ public function summary(array $params): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 매입유형 일괄 변경
|
||||
*
|
||||
* @param array<int> $ids
|
||||
*/
|
||||
public function bulkUpdatePurchaseType(array $ids, string $purchaseType): int
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return Purchase::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $ids)
|
||||
->update([
|
||||
'purchase_type' => $purchaseType,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 세금계산서 수취 일괄 설정
|
||||
*
|
||||
* @param array<int> $ids
|
||||
*/
|
||||
public function bulkUpdateTaxReceived(array $ids, bool $taxInvoiceReceived): int
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return Purchase::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $ids)
|
||||
->update([
|
||||
'tax_invoice_received' => $taxInvoiceReceived,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 매입번호 자동 생성
|
||||
*/
|
||||
|
||||
@@ -333,4 +333,92 @@ public function destroy() {}
|
||||
* )
|
||||
*/
|
||||
public function confirm() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/purchases/bulk-update-type",
|
||||
* tags={"Purchases"},
|
||||
* summary="매입유형 일괄 변경",
|
||||
* description="선택한 매입 건들의 매입유형을 일괄 변경합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* required={"ids","purchase_type"},
|
||||
*
|
||||
* @OA\Property(property="ids", type="array", description="매입 ID 목록", @OA\Items(type="integer"), example={1,2,3}),
|
||||
* @OA\Property(property="purchase_type", type="string", description="매입유형", enum={"unset","raw_material","subsidiary_material","product","outsourcing","consumables","repair","transportation","office_supplies","rent","utilities","communication","vehicle","entertainment","insurance","other_service"}, example="raw_material")
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="변경 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(
|
||||
*
|
||||
* @OA\Property(property="data", type="object",
|
||||
* @OA\Property(property="updated_count", type="integer", example=3, description="변경된 건수")
|
||||
* )
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=400, description="잘못된 요청", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
||||
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
||||
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function bulkUpdatePurchaseType() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/purchases/bulk-update-tax-received",
|
||||
* tags={"Purchases"},
|
||||
* summary="세금계산서 수취 일괄 설정",
|
||||
* description="선택한 매입 건들의 세금계산서 수취 여부를 일괄 설정합니다.",
|
||||
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* required={"ids","tax_invoice_received"},
|
||||
*
|
||||
* @OA\Property(property="ids", type="array", description="매입 ID 목록", @OA\Items(type="integer"), example={1,2,3}),
|
||||
* @OA\Property(property="tax_invoice_received", type="boolean", description="세금계산서 수취 여부", example=true)
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="설정 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* allOf={
|
||||
*
|
||||
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
||||
* @OA\Schema(
|
||||
*
|
||||
* @OA\Property(property="data", type="object",
|
||||
* @OA\Property(property="updated_count", type="integer", example=3, description="변경된 건수")
|
||||
* )
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=400, description="잘못된 요청", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
||||
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
||||
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
||||
* )
|
||||
*/
|
||||
public function bulkUpdateTaxReceived() {}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Http\Controllers\Api\Admin\GlobalMenuController;
|
||||
use App\Http\Controllers\Api\V1\AccountController;
|
||||
use App\Http\Controllers\Api\V1\Admin\FcmController;
|
||||
use App\Http\Controllers\Api\V1\AdminController;
|
||||
use App\Http\Controllers\Api\V1\AiReportController;
|
||||
use App\Http\Controllers\Api\V1\ApiController;
|
||||
@@ -13,6 +14,7 @@
|
||||
use App\Http\Controllers\Api\V1\BankAccountController;
|
||||
use App\Http\Controllers\Api\V1\BankTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillSettingController;
|
||||
use App\Http\Controllers\Api\V1\BiddingController;
|
||||
use App\Http\Controllers\Api\V1\BillController;
|
||||
use App\Http\Controllers\Api\V1\BoardController;
|
||||
use App\Http\Controllers\Api\V1\CardController;
|
||||
@@ -55,8 +57,8 @@
|
||||
use App\Http\Controllers\Api\V1\ItemMaster\ItemSectionController;
|
||||
use App\Http\Controllers\Api\V1\ItemMaster\SectionTemplateController;
|
||||
use App\Http\Controllers\Api\V1\ItemMaster\UnitOptionController;
|
||||
use App\Http\Controllers\Api\V1\ItemsBomController;
|
||||
// use App\Http\Controllers\Api\V1\MaterialController; // REMOVED: materials 테이블 삭제됨
|
||||
use App\Http\Controllers\Api\V1\ItemsBomController;
|
||||
use App\Http\Controllers\Api\V1\ItemsController;
|
||||
use App\Http\Controllers\Api\V1\ItemsFileController;
|
||||
use App\Http\Controllers\Api\V1\LaborController;
|
||||
@@ -70,9 +72,9 @@
|
||||
use App\Http\Controllers\Api\V1\PaymentController;
|
||||
use App\Http\Controllers\Api\V1\PayrollController;
|
||||
use App\Http\Controllers\Api\V1\PermissionController;
|
||||
use App\Http\Controllers\Api\V1\PlanController;
|
||||
// use App\Http\Controllers\Api\V1\ProductBomItemController; // REMOVED: products 테이블 삭제됨
|
||||
// use App\Http\Controllers\Api\V1\ProductController; // REMOVED: products 테이블 삭제됨
|
||||
use App\Http\Controllers\Api\V1\PlanController;
|
||||
use App\Http\Controllers\Api\V1\PopupController;
|
||||
use App\Http\Controllers\Api\V1\PositionController;
|
||||
use App\Http\Controllers\Api\V1\PostController;
|
||||
@@ -684,6 +686,8 @@
|
||||
Route::get('', [PurchaseController::class, 'index'])->name('v1.purchases.index');
|
||||
Route::post('', [PurchaseController::class, 'store'])->name('v1.purchases.store');
|
||||
Route::get('/summary', [PurchaseController::class, 'summary'])->name('v1.purchases.summary');
|
||||
Route::post('/bulk-update-type', [PurchaseController::class, 'bulkUpdatePurchaseType'])->name('v1.purchases.bulk-update-type');
|
||||
Route::post('/bulk-update-tax-received', [PurchaseController::class, 'bulkUpdateTaxReceived'])->name('v1.purchases.bulk-update-tax-received');
|
||||
Route::get('/{id}', [PurchaseController::class, 'show'])->whereNumber('id')->name('v1.purchases.show');
|
||||
Route::put('/{id}', [PurchaseController::class, 'update'])->whereNumber('id')->name('v1.purchases.update');
|
||||
Route::delete('/{id}', [PurchaseController::class, 'destroy'])->whereNumber('id')->name('v1.purchases.destroy');
|
||||
@@ -870,13 +874,13 @@
|
||||
|
||||
// Admin FCM API (MNG 관리자용 FCM 발송) - API Key 인증만 사용
|
||||
Route::prefix('admin/fcm')->group(function () {
|
||||
Route::post('/send', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'send'])->name('v1.admin.fcm.send'); // FCM 발송
|
||||
Route::get('/preview-count', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'previewCount'])->name('v1.admin.fcm.preview'); // 대상 토큰 수 미리보기
|
||||
Route::get('/tokens', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'tokens'])->name('v1.admin.fcm.tokens'); // 토큰 목록
|
||||
Route::get('/tokens/stats', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'tokenStats'])->name('v1.admin.fcm.tokens.stats'); // 토큰 통계
|
||||
Route::patch('/tokens/{id}/toggle', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'toggleToken'])->name('v1.admin.fcm.tokens.toggle'); // 토큰 상태 토글
|
||||
Route::delete('/tokens/{id}', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'deleteToken'])->name('v1.admin.fcm.tokens.delete'); // 토큰 삭제
|
||||
Route::get('/history', [\App\Http\Controllers\Api\V1\Admin\FcmController::class, 'history'])->name('v1.admin.fcm.history'); // 발송 이력
|
||||
Route::post('/send', [FcmController::class, 'send'])->name('v1.admin.fcm.send'); // FCM 발송
|
||||
Route::get('/preview-count', [FcmController::class, 'previewCount'])->name('v1.admin.fcm.preview'); // 대상 토큰 수 미리보기
|
||||
Route::get('/tokens', [FcmController::class, 'tokens'])->name('v1.admin.fcm.tokens'); // 토큰 목록
|
||||
Route::get('/tokens/stats', [FcmController::class, 'tokenStats'])->name('v1.admin.fcm.tokens.stats'); // 토큰 통계
|
||||
Route::patch('/tokens/{id}/toggle', [FcmController::class, 'toggleToken'])->name('v1.admin.fcm.tokens.toggle'); // 토큰 상태 토글
|
||||
Route::delete('/tokens/{id}', [FcmController::class, 'deleteToken'])->name('v1.admin.fcm.tokens.delete'); // 토큰 삭제
|
||||
Route::get('/history', [FcmController::class, 'history'])->name('v1.admin.fcm.history'); // 발송 이력
|
||||
});
|
||||
|
||||
// 회원 프로필(테넌트 기준)
|
||||
@@ -1003,6 +1007,21 @@
|
||||
Route::post('/{id}/send/email', [QuoteController::class, 'sendEmail'])->whereNumber('id')->name('v1.quotes.send-email'); // 이메일 발송
|
||||
Route::post('/{id}/send/kakao', [QuoteController::class, 'sendKakao'])->whereNumber('id')->name('v1.quotes.send-kakao'); // 카카오톡 발송
|
||||
Route::get('/{id}/send/history', [QuoteController::class, 'sendHistory'])->whereNumber('id')->name('v1.quotes.send-history'); // 발송 이력
|
||||
|
||||
// 입찰 전환
|
||||
Route::post('/{id}/convert-to-bidding', [QuoteController::class, 'convertToBidding'])->whereNumber('id')->name('v1.quotes.convert-to-bidding'); // 입찰 전환
|
||||
});
|
||||
|
||||
// Biddings (입찰관리)
|
||||
Route::prefix('biddings')->group(function () {
|
||||
Route::get('', [BiddingController::class, 'index'])->name('v1.biddings.index'); // 목록
|
||||
Route::post('', [BiddingController::class, 'store'])->name('v1.biddings.store'); // 생성
|
||||
Route::get('/stats', [BiddingController::class, 'stats'])->name('v1.biddings.stats'); // 통계
|
||||
Route::delete('/bulk', [BiddingController::class, 'bulkDestroy'])->name('v1.biddings.bulk-destroy'); // 일괄 삭제
|
||||
Route::get('/{id}', [BiddingController::class, 'show'])->whereNumber('id')->name('v1.biddings.show'); // 단건
|
||||
Route::put('/{id}', [BiddingController::class, 'update'])->whereNumber('id')->name('v1.biddings.update'); // 수정
|
||||
Route::delete('/{id}', [BiddingController::class, 'destroy'])->whereNumber('id')->name('v1.biddings.destroy'); // 삭제
|
||||
Route::patch('/{id}/status', [BiddingController::class, 'updateStatus'])->whereNumber('id')->name('v1.biddings.status'); // 상태 변경
|
||||
});
|
||||
|
||||
// Pricing (단가 관리)
|
||||
|
||||
Reference in New Issue
Block a user