feat: I-1 미지급비용 관리 API 개발
- ExpectedExpense 모델 및 마이그레이션 생성 - ExpectedExpenseService 구현 (CRUD, 일괄삭제, 지급일 변경, 요약) - ExpectedExpenseController REST API 구현 - FormRequest 검증 클래스 3개 생성 - Swagger API 문서 작성 - 라우트 추가 (8개 엔드포인트) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
127
app/Http/Controllers/Api/V1/ExpectedExpenseController.php
Normal file
127
app/Http/Controllers/Api/V1/ExpectedExpenseController.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\V1\ExpectedExpense\StoreExpectedExpenseRequest;
|
||||
use App\Http\Requests\V1\ExpectedExpense\UpdateExpectedExpenseRequest;
|
||||
use App\Http\Requests\V1\ExpectedExpense\UpdateExpectedPaymentDateRequest;
|
||||
use App\Services\ExpectedExpenseService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ExpectedExpenseController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ExpectedExpenseService $service
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 미지급비용 목록
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'search',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'client_id',
|
||||
'transaction_type',
|
||||
'payment_status',
|
||||
'approval_status',
|
||||
'sort_by',
|
||||
'sort_dir',
|
||||
'per_page',
|
||||
'page',
|
||||
]);
|
||||
|
||||
$expenses = $this->service->index($params);
|
||||
|
||||
return ApiResponse::success($expenses, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 등록
|
||||
*/
|
||||
public function store(StoreExpectedExpenseRequest $request)
|
||||
{
|
||||
$expense = $this->service->store($request->validated());
|
||||
|
||||
return ApiResponse::success($expense, __('message.created'), [], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 상세
|
||||
*/
|
||||
public function show(int $id)
|
||||
{
|
||||
$expense = $this->service->show($id);
|
||||
|
||||
return ApiResponse::success($expense, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 수정
|
||||
*/
|
||||
public function update(int $id, UpdateExpectedExpenseRequest $request)
|
||||
{
|
||||
$expense = $this->service->update($id, $request->validated());
|
||||
|
||||
return ApiResponse::success($expense, __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 삭제
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$this->service->destroy($id);
|
||||
|
||||
return ApiResponse::success(null, __('message.deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 일괄 삭제
|
||||
*/
|
||||
public function destroyMany(Request $request)
|
||||
{
|
||||
$ids = $request->input('ids', []);
|
||||
|
||||
if (empty($ids)) {
|
||||
return ApiResponse::error(__('error.no_ids_provided'), 400);
|
||||
}
|
||||
|
||||
$count = $this->service->destroyMany($ids);
|
||||
|
||||
return ApiResponse::success(['deleted_count' => $count], __('message.deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 예상 지급일 일괄 변경
|
||||
*/
|
||||
public function updateExpectedPaymentDate(UpdateExpectedPaymentDateRequest $request)
|
||||
{
|
||||
$count = $this->service->updateExpectedPaymentDate(
|
||||
$request->input('ids'),
|
||||
$request->input('expected_payment_date')
|
||||
);
|
||||
|
||||
return ApiResponse::success(['updated_count' => $count], __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 요약 (기간별 합계)
|
||||
*/
|
||||
public function summary(Request $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'start_date',
|
||||
'end_date',
|
||||
'payment_status',
|
||||
]);
|
||||
|
||||
$summary = $this->service->summary($params);
|
||||
|
||||
return ApiResponse::success($summary, __('message.fetched'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\ExpectedExpense;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreExpectedExpenseRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'expected_payment_date' => ['required', 'date'],
|
||||
'settlement_date' => ['nullable', 'date'],
|
||||
'transaction_type' => ['required', 'string', 'in:purchase,advance,suspense,rent,salary,insurance,tax,utilities,other'],
|
||||
'amount' => ['required', 'numeric', 'min:0'],
|
||||
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
|
||||
'client_name' => ['nullable', 'string', 'max:100'],
|
||||
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
|
||||
'account_code' => ['nullable', 'string', 'max:50'],
|
||||
'payment_status' => ['nullable', 'string', 'in:pending,partial,paid,overdue'],
|
||||
'approval_status' => ['nullable', 'string', 'in:none,pending,approved,rejected'],
|
||||
'description' => ['nullable', 'string', 'max:1000'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'expected_payment_date.required' => __('validation.required', ['attribute' => '예상 지급일']),
|
||||
'transaction_type.required' => __('validation.required', ['attribute' => '거래유형']),
|
||||
'transaction_type.in' => __('validation.in', ['attribute' => '거래유형']),
|
||||
'amount.required' => __('validation.required', ['attribute' => '금액']),
|
||||
'amount.min' => __('validation.min.numeric', ['attribute' => '금액', 'min' => 0]),
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'expected_payment_date' => '예상 지급일',
|
||||
'settlement_date' => '결제일',
|
||||
'transaction_type' => '거래유형',
|
||||
'amount' => '금액',
|
||||
'client_id' => '거래처',
|
||||
'client_name' => '거래처명',
|
||||
'bank_account_id' => '계좌',
|
||||
'account_code' => '계정과목',
|
||||
'payment_status' => '지급상태',
|
||||
'approval_status' => '결재상태',
|
||||
'description' => '적요',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\ExpectedExpense;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateExpectedExpenseRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'expected_payment_date' => ['sometimes', 'date'],
|
||||
'settlement_date' => ['nullable', 'date'],
|
||||
'transaction_type' => ['sometimes', 'string', 'in:purchase,advance,suspense,rent,salary,insurance,tax,utilities,other'],
|
||||
'amount' => ['sometimes', 'numeric', 'min:0'],
|
||||
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
|
||||
'client_name' => ['nullable', 'string', 'max:100'],
|
||||
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
|
||||
'account_code' => ['nullable', 'string', 'max:50'],
|
||||
'payment_status' => ['sometimes', 'string', 'in:pending,partial,paid,overdue'],
|
||||
'approval_status' => ['sometimes', 'string', 'in:none,pending,approved,rejected'],
|
||||
'description' => ['nullable', 'string', 'max:1000'],
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'expected_payment_date' => '예상 지급일',
|
||||
'settlement_date' => '결제일',
|
||||
'transaction_type' => '거래유형',
|
||||
'amount' => '금액',
|
||||
'client_id' => '거래처',
|
||||
'client_name' => '거래처명',
|
||||
'bank_account_id' => '계좌',
|
||||
'account_code' => '계정과목',
|
||||
'payment_status' => '지급상태',
|
||||
'approval_status' => '결재상태',
|
||||
'description' => '적요',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\V1\ExpectedExpense;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateExpectedPaymentDateRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => ['required', 'array', 'min:1'],
|
||||
'ids.*' => ['required', 'integer'],
|
||||
'expected_payment_date' => ['required', 'date'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'ids.required' => __('validation.required', ['attribute' => '대상 ID']),
|
||||
'ids.min' => __('validation.min.array', ['attribute' => '대상 ID', 'min' => 1]),
|
||||
'expected_payment_date.required' => __('validation.required', ['attribute' => '예상 지급일']),
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'ids' => '대상 ID',
|
||||
'expected_payment_date' => '예상 지급일',
|
||||
];
|
||||
}
|
||||
}
|
||||
134
app/Models/Tenants/ExpectedExpense.php
Normal file
134
app/Models/Tenants/ExpectedExpense.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Tenants;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ExpectedExpense extends Model
|
||||
{
|
||||
use BelongsToTenant, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'expected_payment_date',
|
||||
'settlement_date',
|
||||
'transaction_type',
|
||||
'amount',
|
||||
'client_id',
|
||||
'client_name',
|
||||
'bank_account_id',
|
||||
'account_code',
|
||||
'payment_status',
|
||||
'approval_status',
|
||||
'description',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'expected_payment_date' => 'date',
|
||||
'settlement_date' => 'date',
|
||||
'amount' => 'decimal:2',
|
||||
'client_id' => 'integer',
|
||||
'bank_account_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* 거래유형 목록
|
||||
*/
|
||||
public const TRANSACTION_TYPES = [
|
||||
'purchase' => '매입',
|
||||
'advance' => '선급금',
|
||||
'suspense' => '가지급금',
|
||||
'rent' => '임대료',
|
||||
'salary' => '급여',
|
||||
'insurance' => '보험료',
|
||||
'tax' => '세금',
|
||||
'utilities' => '공과금',
|
||||
'other' => '기타',
|
||||
];
|
||||
|
||||
/**
|
||||
* 지급상태 목록
|
||||
*/
|
||||
public const PAYMENT_STATUSES = [
|
||||
'pending' => '미지급',
|
||||
'partial' => '부분지급',
|
||||
'paid' => '지급완료',
|
||||
'overdue' => '연체',
|
||||
];
|
||||
|
||||
/**
|
||||
* 결재상태 목록
|
||||
*/
|
||||
public const APPROVAL_STATUSES = [
|
||||
'none' => '미신청',
|
||||
'pending' => '결재대기',
|
||||
'approved' => '결재완료',
|
||||
'rejected' => '반려',
|
||||
];
|
||||
|
||||
/**
|
||||
* 거래처 관계
|
||||
*/
|
||||
public function client(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Orders\Client::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 계좌 관계
|
||||
*/
|
||||
public function bankAccount(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(BankAccount::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 생성자 관계
|
||||
*/
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Members\User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래처명 조회 (회원/비회원 통합)
|
||||
*/
|
||||
public function getDisplayClientNameAttribute(): string
|
||||
{
|
||||
if ($this->client) {
|
||||
return $this->client->name;
|
||||
}
|
||||
|
||||
return $this->client_name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래유형 라벨
|
||||
*/
|
||||
public function getTransactionTypeLabelAttribute(): string
|
||||
{
|
||||
return self::TRANSACTION_TYPES[$this->transaction_type] ?? $this->transaction_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 지급상태 라벨
|
||||
*/
|
||||
public function getPaymentStatusLabelAttribute(): string
|
||||
{
|
||||
return self::PAYMENT_STATUSES[$this->payment_status] ?? $this->payment_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 결재상태 라벨
|
||||
*/
|
||||
public function getApprovalStatusLabelAttribute(): string
|
||||
{
|
||||
return self::APPROVAL_STATUSES[$this->approval_status] ?? $this->approval_status;
|
||||
}
|
||||
}
|
||||
302
app/Services/ExpectedExpenseService.php
Normal file
302
app/Services/ExpectedExpenseService.php
Normal file
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Tenants\ExpectedExpense;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ExpectedExpenseService extends Service
|
||||
{
|
||||
/**
|
||||
* 미지급비용 목록 조회
|
||||
*/
|
||||
public function index(array $params): LengthAwarePaginator
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$query = ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->with(['client:id,name', 'bankAccount:id,bank_name,account_name']);
|
||||
|
||||
// 검색어 필터
|
||||
if (! empty($params['search'])) {
|
||||
$search = $params['search'];
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('client_name', 'like', "%{$search}%")
|
||||
->orWhere('account_code', 'like', "%{$search}%")
|
||||
->orWhere('description', 'like', "%{$search}%")
|
||||
->orWhereHas('client', function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 날짜 범위 필터 (예상 지급일 기준)
|
||||
if (! empty($params['start_date'])) {
|
||||
$query->where('expected_payment_date', '>=', $params['start_date']);
|
||||
}
|
||||
if (! empty($params['end_date'])) {
|
||||
$query->where('expected_payment_date', '<=', $params['end_date']);
|
||||
}
|
||||
|
||||
// 거래처 필터
|
||||
if (! empty($params['client_id'])) {
|
||||
$query->where('client_id', $params['client_id']);
|
||||
}
|
||||
|
||||
// 거래유형 필터
|
||||
if (! empty($params['transaction_type'])) {
|
||||
$query->where('transaction_type', $params['transaction_type']);
|
||||
}
|
||||
|
||||
// 지급상태 필터
|
||||
if (! empty($params['payment_status'])) {
|
||||
$query->where('payment_status', $params['payment_status']);
|
||||
}
|
||||
|
||||
// 결재상태 필터
|
||||
if (! empty($params['approval_status'])) {
|
||||
$query->where('approval_status', $params['approval_status']);
|
||||
}
|
||||
|
||||
// 정렬
|
||||
$sortBy = $params['sort_by'] ?? 'expected_payment_date';
|
||||
$sortDir = $params['sort_dir'] ?? 'asc';
|
||||
$query->orderBy($sortBy, $sortDir);
|
||||
|
||||
// 페이지네이션
|
||||
$perPage = $params['per_page'] ?? 50;
|
||||
|
||||
return $query->paginate($perPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 상세 조회
|
||||
*/
|
||||
public function show(int $id): ExpectedExpense
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->with(['client:id,name', 'bankAccount:id,bank_name,account_name', 'creator:id,name'])
|
||||
->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 등록
|
||||
*/
|
||||
public function store(array $data): ExpectedExpense
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($data, $tenantId, $userId) {
|
||||
$expense = new ExpectedExpense;
|
||||
$expense->tenant_id = $tenantId;
|
||||
$expense->expected_payment_date = $data['expected_payment_date'];
|
||||
$expense->settlement_date = $data['settlement_date'] ?? null;
|
||||
$expense->transaction_type = $data['transaction_type'];
|
||||
$expense->amount = $data['amount'];
|
||||
$expense->client_id = $data['client_id'] ?? null;
|
||||
$expense->client_name = $data['client_name'] ?? null;
|
||||
$expense->bank_account_id = $data['bank_account_id'] ?? null;
|
||||
$expense->account_code = $data['account_code'] ?? null;
|
||||
$expense->payment_status = $data['payment_status'] ?? 'pending';
|
||||
$expense->approval_status = $data['approval_status'] ?? 'none';
|
||||
$expense->description = $data['description'] ?? null;
|
||||
$expense->created_by = $userId;
|
||||
$expense->updated_by = $userId;
|
||||
$expense->save();
|
||||
|
||||
return $expense->load(['client:id,name', 'bankAccount:id,bank_name,account_name']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 수정
|
||||
*/
|
||||
public function update(int $id, array $data): ExpectedExpense
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
|
||||
$expense = ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->findOrFail($id);
|
||||
|
||||
if (isset($data['expected_payment_date'])) {
|
||||
$expense->expected_payment_date = $data['expected_payment_date'];
|
||||
}
|
||||
if (array_key_exists('settlement_date', $data)) {
|
||||
$expense->settlement_date = $data['settlement_date'];
|
||||
}
|
||||
if (isset($data['transaction_type'])) {
|
||||
$expense->transaction_type = $data['transaction_type'];
|
||||
}
|
||||
if (isset($data['amount'])) {
|
||||
$expense->amount = $data['amount'];
|
||||
}
|
||||
if (array_key_exists('client_id', $data)) {
|
||||
$expense->client_id = $data['client_id'];
|
||||
}
|
||||
if (array_key_exists('client_name', $data)) {
|
||||
$expense->client_name = $data['client_name'];
|
||||
}
|
||||
if (array_key_exists('bank_account_id', $data)) {
|
||||
$expense->bank_account_id = $data['bank_account_id'];
|
||||
}
|
||||
if (array_key_exists('account_code', $data)) {
|
||||
$expense->account_code = $data['account_code'];
|
||||
}
|
||||
if (isset($data['payment_status'])) {
|
||||
$expense->payment_status = $data['payment_status'];
|
||||
}
|
||||
if (isset($data['approval_status'])) {
|
||||
$expense->approval_status = $data['approval_status'];
|
||||
}
|
||||
if (array_key_exists('description', $data)) {
|
||||
$expense->description = $data['description'];
|
||||
}
|
||||
|
||||
$expense->updated_by = $userId;
|
||||
$expense->save();
|
||||
|
||||
return $expense->fresh(['client:id,name', 'bankAccount:id,bank_name,account_name']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 삭제
|
||||
*/
|
||||
public function destroy(int $id): bool
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($id, $tenantId, $userId) {
|
||||
$expense = ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->findOrFail($id);
|
||||
|
||||
$expense->deleted_by = $userId;
|
||||
$expense->save();
|
||||
$expense->delete();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 일괄 삭제
|
||||
*/
|
||||
public function destroyMany(array $ids): int
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($ids, $tenantId, $userId) {
|
||||
$count = 0;
|
||||
foreach ($ids as $id) {
|
||||
$expense = ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($id);
|
||||
|
||||
if ($expense) {
|
||||
$expense->deleted_by = $userId;
|
||||
$expense->save();
|
||||
$expense->delete();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 예상 지급일 일괄 변경
|
||||
*/
|
||||
public function updateExpectedPaymentDate(array $ids, string $newDate): int
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($ids, $newDate, $tenantId, $userId) {
|
||||
return ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $ids)
|
||||
->update([
|
||||
'expected_payment_date' => $newDate,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 미지급비용 요약 (기간별 합계)
|
||||
*/
|
||||
public function summary(array $params): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$query = ExpectedExpense::query()
|
||||
->where('tenant_id', $tenantId);
|
||||
|
||||
// 날짜 범위 필터
|
||||
if (! empty($params['start_date'])) {
|
||||
$query->where('expected_payment_date', '>=', $params['start_date']);
|
||||
}
|
||||
if (! empty($params['end_date'])) {
|
||||
$query->where('expected_payment_date', '<=', $params['end_date']);
|
||||
}
|
||||
|
||||
// 지급상태 필터
|
||||
if (! empty($params['payment_status'])) {
|
||||
$query->where('payment_status', $params['payment_status']);
|
||||
}
|
||||
|
||||
// 전체 합계
|
||||
$total = (clone $query)->sum('amount');
|
||||
$count = (clone $query)->count();
|
||||
|
||||
// 지급상태별 합계
|
||||
$byPaymentStatus = (clone $query)
|
||||
->select('payment_status', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count'))
|
||||
->groupBy('payment_status')
|
||||
->get()
|
||||
->keyBy('payment_status')
|
||||
->toArray();
|
||||
|
||||
// 거래유형별 합계
|
||||
$byTransactionType = (clone $query)
|
||||
->select('transaction_type', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count'))
|
||||
->groupBy('transaction_type')
|
||||
->get()
|
||||
->keyBy('transaction_type')
|
||||
->toArray();
|
||||
|
||||
// 월별 합계
|
||||
$byMonth = (clone $query)
|
||||
->select(
|
||||
DB::raw("DATE_FORMAT(expected_payment_date, '%Y-%m') as month"),
|
||||
DB::raw('SUM(amount) as total'),
|
||||
DB::raw('COUNT(*) as count')
|
||||
)
|
||||
->groupBy('month')
|
||||
->orderBy('month')
|
||||
->get()
|
||||
->keyBy('month')
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'total_amount' => (float) $total,
|
||||
'total_count' => $count,
|
||||
'by_payment_status' => $byPaymentStatus,
|
||||
'by_transaction_type' => $byTransactionType,
|
||||
'by_month' => $byMonth,
|
||||
];
|
||||
}
|
||||
}
|
||||
300
app/Swagger/v1/ExpectedExpenseApi.php
Normal file
300
app/Swagger/v1/ExpectedExpenseApi.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace App\Swagger\v1;
|
||||
|
||||
/**
|
||||
* @OA\Tag(
|
||||
* name="ExpectedExpense",
|
||||
* description="미지급비용(지출예상내역) 관리"
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ExpectedExpense",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="integer", example=1),
|
||||
* @OA\Property(property="tenant_id", type="integer", example=1),
|
||||
* @OA\Property(property="expected_payment_date", type="string", format="date", example="2025-01-15"),
|
||||
* @OA\Property(property="settlement_date", type="string", format="date", nullable=true, example="2025-01-10"),
|
||||
* @OA\Property(property="transaction_type", type="string", enum={"purchase","advance","suspense","rent","salary","insurance","tax","utilities","other"}, example="purchase"),
|
||||
* @OA\Property(property="amount", type="number", format="float", example=1500000),
|
||||
* @OA\Property(property="client_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="client_name", type="string", nullable=true, example="(주)삼성전자"),
|
||||
* @OA\Property(property="bank_account_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="account_code", type="string", nullable=true, example="매입비용"),
|
||||
* @OA\Property(property="payment_status", type="string", enum={"pending","partial","paid","overdue"}, example="pending"),
|
||||
* @OA\Property(property="approval_status", type="string", enum={"none","pending","approved","rejected"}, example="none"),
|
||||
* @OA\Property(property="description", type="string", nullable=true, example="월정산"),
|
||||
* @OA\Property(property="created_at", type="string", format="datetime"),
|
||||
* @OA\Property(property="updated_at", type="string", format="datetime"),
|
||||
* @OA\Property(
|
||||
* property="client",
|
||||
* type="object",
|
||||
* nullable=true,
|
||||
* @OA\Property(property="id", type="integer"),
|
||||
* @OA\Property(property="name", type="string")
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="bank_account",
|
||||
* type="object",
|
||||
* nullable=true,
|
||||
* @OA\Property(property="id", type="integer"),
|
||||
* @OA\Property(property="bank_name", type="string"),
|
||||
* @OA\Property(property="account_name", type="string")
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ExpectedExpensePagination",
|
||||
* type="object",
|
||||
* @OA\Property(property="current_page", type="integer", example=1),
|
||||
* @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/ExpectedExpense")),
|
||||
* @OA\Property(property="first_page_url", type="string"),
|
||||
* @OA\Property(property="from", type="integer"),
|
||||
* @OA\Property(property="last_page", type="integer"),
|
||||
* @OA\Property(property="last_page_url", type="string"),
|
||||
* @OA\Property(property="next_page_url", type="string", nullable=true),
|
||||
* @OA\Property(property="path", type="string"),
|
||||
* @OA\Property(property="per_page", type="integer"),
|
||||
* @OA\Property(property="prev_page_url", type="string", nullable=true),
|
||||
* @OA\Property(property="to", type="integer"),
|
||||
* @OA\Property(property="total", type="integer")
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ExpectedExpenseCreateRequest",
|
||||
* type="object",
|
||||
* required={"expected_payment_date", "transaction_type", "amount"},
|
||||
* @OA\Property(property="expected_payment_date", type="string", format="date", example="2025-01-15"),
|
||||
* @OA\Property(property="settlement_date", type="string", format="date", nullable=true, example="2025-01-10"),
|
||||
* @OA\Property(property="transaction_type", type="string", enum={"purchase","advance","suspense","rent","salary","insurance","tax","utilities","other"}, example="purchase"),
|
||||
* @OA\Property(property="amount", type="number", format="float", example=1500000),
|
||||
* @OA\Property(property="client_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="client_name", type="string", nullable=true, example="(주)삼성전자"),
|
||||
* @OA\Property(property="bank_account_id", type="integer", nullable=true, example=1),
|
||||
* @OA\Property(property="account_code", type="string", nullable=true, example="매입비용"),
|
||||
* @OA\Property(property="payment_status", type="string", enum={"pending","partial","paid","overdue"}, example="pending"),
|
||||
* @OA\Property(property="approval_status", type="string", enum={"none","pending","approved","rejected"}, example="none"),
|
||||
* @OA\Property(property="description", type="string", nullable=true, example="월정산")
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ExpectedExpenseUpdateRequest",
|
||||
* type="object",
|
||||
* @OA\Property(property="expected_payment_date", type="string", format="date", example="2025-01-20"),
|
||||
* @OA\Property(property="settlement_date", type="string", format="date", nullable=true),
|
||||
* @OA\Property(property="transaction_type", type="string", enum={"purchase","advance","suspense","rent","salary","insurance","tax","utilities","other"}),
|
||||
* @OA\Property(property="amount", type="number", format="float"),
|
||||
* @OA\Property(property="client_id", type="integer", nullable=true),
|
||||
* @OA\Property(property="client_name", type="string", nullable=true),
|
||||
* @OA\Property(property="bank_account_id", type="integer", nullable=true),
|
||||
* @OA\Property(property="account_code", type="string", nullable=true),
|
||||
* @OA\Property(property="payment_status", type="string", enum={"pending","partial","paid","overdue"}),
|
||||
* @OA\Property(property="approval_status", type="string", enum={"none","pending","approved","rejected"}),
|
||||
* @OA\Property(property="description", type="string", nullable=true)
|
||||
* )
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="ExpectedExpenseSummary",
|
||||
* type="object",
|
||||
* @OA\Property(property="total_amount", type="number", format="float", example=15000000),
|
||||
* @OA\Property(property="total_count", type="integer", example=12),
|
||||
* @OA\Property(property="by_payment_status", type="object"),
|
||||
* @OA\Property(property="by_transaction_type", type="object"),
|
||||
* @OA\Property(property="by_month", type="object")
|
||||
* )
|
||||
*/
|
||||
class ExpectedExpenseApi
|
||||
{
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expected-expenses",
|
||||
* summary="미지급비용 목록 조회",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\Parameter(name="search", in="query", description="검색어 (거래처명, 계정과목, 적요)", @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="start_date", in="query", description="시작일", @OA\Schema(type="string", format="date")),
|
||||
* @OA\Parameter(name="end_date", in="query", description="종료일", @OA\Schema(type="string", format="date")),
|
||||
* @OA\Parameter(name="client_id", in="query", description="거래처 ID", @OA\Schema(type="integer")),
|
||||
* @OA\Parameter(name="transaction_type", in="query", description="거래유형", @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="payment_status", in="query", description="지급상태", @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="approval_status", in="query", description="결재상태", @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="sort_by", in="query", description="정렬 기준", @OA\Schema(type="string", default="expected_payment_date")),
|
||||
* @OA\Parameter(name="sort_dir", in="query", description="정렬 방향", @OA\Schema(type="string", enum={"asc","desc"}, default="asc")),
|
||||
* @OA\Parameter(name="per_page", in="query", description="페이지당 항목 수", @OA\Schema(type="integer", default=50)),
|
||||
* @OA\Parameter(name="page", in="query", description="페이지 번호", @OA\Schema(type="integer", default=1)),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", ref="#/components/schemas/ExpectedExpensePagination")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function index() {}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/expected-expenses",
|
||||
* summary="미지급비용 등록",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(ref="#/components/schemas/ExpectedExpenseCreateRequest")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=201,
|
||||
* description="생성 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", ref="#/components/schemas/ExpectedExpense")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expected-expenses/{id}",
|
||||
* summary="미지급비용 상세 조회",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="미지급비용 ID", @OA\Schema(type="integer")),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", ref="#/components/schemas/ExpectedExpense")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function show() {}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/v1/expected-expenses/{id}",
|
||||
* summary="미지급비용 수정",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="미지급비용 ID", @OA\Schema(type="integer")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(ref="#/components/schemas/ExpectedExpenseUpdateRequest")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="수정 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", ref="#/components/schemas/ExpectedExpense")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function update() {}
|
||||
|
||||
/**
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/expected-expenses/{id}",
|
||||
* summary="미지급비용 삭제",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\Parameter(name="id", in="path", required=true, description="미지급비용 ID", @OA\Schema(type="integer")),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="삭제 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function destroy() {}
|
||||
|
||||
/**
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/expected-expenses",
|
||||
* summary="미지급비용 일괄 삭제",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="ids", type="array", @OA\Items(type="integer"), example={1, 2, 3})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="일괄 삭제 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", type="object",
|
||||
* @OA\Property(property="deleted_count", type="integer", example=3)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function destroyMany() {}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/v1/expected-expenses/update-payment-date",
|
||||
* summary="예상 지급일 일괄 변경",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="ids", type="array", @OA\Items(type="integer"), example={1, 2, 3}),
|
||||
* @OA\Property(property="expected_payment_date", type="string", format="date", example="2025-02-01")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="일괄 변경 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", type="object",
|
||||
* @OA\Property(property="updated_count", type="integer", example=3)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function updateExpectedPaymentDate() {}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expected-expenses/summary",
|
||||
* summary="미지급비용 요약 (기간별 합계)",
|
||||
* tags={"ExpectedExpense"},
|
||||
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
||||
* @OA\Parameter(name="start_date", in="query", description="시작일", @OA\Schema(type="string", format="date")),
|
||||
* @OA\Parameter(name="end_date", in="query", description="종료일", @OA\Schema(type="string", format="date")),
|
||||
* @OA\Parameter(name="payment_status", in="query", description="지급상태", @OA\Schema(type="string")),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="data", ref="#/components/schemas/ExpectedExpenseSummary")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function summary() {}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('expected_expenses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->date('expected_payment_date')->comment('예상 지급일');
|
||||
$table->date('settlement_date')->nullable()->comment('결제일');
|
||||
$table->string('transaction_type', 30)->comment('거래유형: purchase/advance/suspense/rent/salary/insurance/tax/utilities/other');
|
||||
$table->decimal('amount', 15, 2)->comment('지출금액');
|
||||
$table->unsignedBigInteger('client_id')->nullable()->comment('거래처 ID');
|
||||
$table->string('client_name', 100)->nullable()->comment('비회원 거래처명');
|
||||
$table->unsignedBigInteger('bank_account_id')->nullable()->comment('계좌 ID');
|
||||
$table->string('account_code', 50)->nullable()->comment('계정과목');
|
||||
$table->string('payment_status', 20)->default('pending')->comment('지급상태: pending/partial/paid/overdue');
|
||||
$table->string('approval_status', 20)->default('none')->comment('결재상태: none/pending/approved/rejected');
|
||||
$table->text('description')->nullable()->comment('적요/메모');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['tenant_id', 'expected_payment_date'], 'idx_tenant_expected_date');
|
||||
$table->index(['tenant_id', 'payment_status'], 'idx_tenant_payment_status');
|
||||
$table->index('client_id', 'idx_client');
|
||||
$table->index('transaction_type', 'idx_transaction_type');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('expected_expenses');
|
||||
}
|
||||
};
|
||||
@@ -34,6 +34,7 @@
|
||||
use App\Http\Controllers\Api\V1\Design\DesignModelController;
|
||||
use App\Http\Controllers\Api\V1\Design\ModelVersionController as DesignModelVersionController;
|
||||
use App\Http\Controllers\Api\V1\EmployeeController;
|
||||
use App\Http\Controllers\Api\V1\ExpectedExpenseController;
|
||||
use App\Http\Controllers\Api\V1\EstimateController;
|
||||
use App\Http\Controllers\Api\V1\FileStorageController;
|
||||
use App\Http\Controllers\Api\V1\FolderController;
|
||||
@@ -66,6 +67,7 @@
|
||||
use App\Http\Controllers\Api\V1\PostController;
|
||||
use App\Http\Controllers\Api\V1\PricingController;
|
||||
use App\Http\Controllers\Api\V1\PurchaseController;
|
||||
use App\Http\Controllers\Api\V1\ReceivingController;
|
||||
use App\Http\Controllers\Api\V1\PushNotificationController;
|
||||
use App\Http\Controllers\Api\V1\QuoteController;
|
||||
use App\Http\Controllers\Api\V1\RefreshController;
|
||||
@@ -89,6 +91,7 @@
|
||||
use App\Http\Controllers\Api\V1\UserInvitationController;
|
||||
use App\Http\Controllers\Api\V1\UserRoleController;
|
||||
use App\Http\Controllers\Api\V1\WithdrawalController;
|
||||
use App\Http\Controllers\Api\V1\WorkOrderController;
|
||||
use App\Http\Controllers\Api\V1\WorkSettingController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -445,6 +448,18 @@
|
||||
Route::patch('/{id}/status', [SalaryController::class, 'updateStatus'])->whereNumber('id')->name('v1.salaries.update-status');
|
||||
});
|
||||
|
||||
// Expected Expense API (미지급비용 관리)
|
||||
Route::prefix('expected-expenses')->group(function () {
|
||||
Route::get('', [ExpectedExpenseController::class, 'index'])->name('v1.expected-expenses.index');
|
||||
Route::post('', [ExpectedExpenseController::class, 'store'])->name('v1.expected-expenses.store');
|
||||
Route::get('/summary', [ExpectedExpenseController::class, 'summary'])->name('v1.expected-expenses.summary');
|
||||
Route::delete('', [ExpectedExpenseController::class, 'destroyMany'])->name('v1.expected-expenses.destroy-many');
|
||||
Route::put('/update-payment-date', [ExpectedExpenseController::class, 'updateExpectedPaymentDate'])->name('v1.expected-expenses.update-payment-date');
|
||||
Route::get('/{id}', [ExpectedExpenseController::class, 'show'])->whereNumber('id')->name('v1.expected-expenses.show');
|
||||
Route::put('/{id}', [ExpectedExpenseController::class, 'update'])->whereNumber('id')->name('v1.expected-expenses.update');
|
||||
Route::delete('/{id}', [ExpectedExpenseController::class, 'destroy'])->whereNumber('id')->name('v1.expected-expenses.destroy');
|
||||
});
|
||||
|
||||
// Loan API (가지급금 관리)
|
||||
Route::prefix('loans')->group(function () {
|
||||
Route::get('', [LoanController::class, 'index'])->name('v1.loans.index');
|
||||
@@ -533,6 +548,17 @@
|
||||
Route::post('/{id}/confirm', [PurchaseController::class, 'confirm'])->whereNumber('id')->name('v1.purchases.confirm');
|
||||
});
|
||||
|
||||
// Receiving API (입고 관리)
|
||||
Route::prefix('receivings')->group(function () {
|
||||
Route::get('', [ReceivingController::class, 'index'])->name('v1.receivings.index');
|
||||
Route::post('', [ReceivingController::class, 'store'])->name('v1.receivings.store');
|
||||
Route::get('/stats', [ReceivingController::class, 'stats'])->name('v1.receivings.stats');
|
||||
Route::get('/{id}', [ReceivingController::class, 'show'])->whereNumber('id')->name('v1.receivings.show');
|
||||
Route::put('/{id}', [ReceivingController::class, 'update'])->whereNumber('id')->name('v1.receivings.update');
|
||||
Route::delete('/{id}', [ReceivingController::class, 'destroy'])->whereNumber('id')->name('v1.receivings.destroy');
|
||||
Route::post('/{id}/process', [ReceivingController::class, 'process'])->whereNumber('id')->name('v1.receivings.process');
|
||||
});
|
||||
|
||||
// Barobill Setting API (바로빌 설정)
|
||||
Route::prefix('barobill-settings')->group(function () {
|
||||
Route::get('', [BarobillSettingController::class, 'show'])->name('v1.barobill-settings.show');
|
||||
@@ -926,6 +952,28 @@
|
||||
Route::post('/preview/{model_set_id}', [EstimateController::class, 'previewCalculation'])->name('v1.estimates.preview'); // 견적 계산 미리보기
|
||||
});
|
||||
|
||||
// 작업지시 관리 API (Production)
|
||||
Route::prefix('work-orders')->group(function () {
|
||||
// 기본 CRUD
|
||||
Route::get('', [WorkOrderController::class, 'index'])->name('v1.work-orders.index'); // 목록
|
||||
Route::get('/stats', [WorkOrderController::class, 'stats'])->name('v1.work-orders.stats'); // 통계
|
||||
Route::post('', [WorkOrderController::class, 'store'])->name('v1.work-orders.store'); // 생성
|
||||
Route::get('/{id}', [WorkOrderController::class, 'show'])->whereNumber('id')->name('v1.work-orders.show'); // 상세
|
||||
Route::put('/{id}', [WorkOrderController::class, 'update'])->whereNumber('id')->name('v1.work-orders.update'); // 수정
|
||||
Route::delete('/{id}', [WorkOrderController::class, 'destroy'])->whereNumber('id')->name('v1.work-orders.destroy'); // 삭제
|
||||
|
||||
// 상태 및 담당자 관리
|
||||
Route::patch('/{id}/status', [WorkOrderController::class, 'updateStatus'])->whereNumber('id')->name('v1.work-orders.status'); // 상태 변경
|
||||
Route::patch('/{id}/assign', [WorkOrderController::class, 'assign'])->whereNumber('id')->name('v1.work-orders.assign'); // 담당자 배정
|
||||
|
||||
// 벤딩 공정 상세 토글
|
||||
Route::patch('/{id}/bending/toggle', [WorkOrderController::class, 'toggleBendingField'])->whereNumber('id')->name('v1.work-orders.bending-toggle');
|
||||
|
||||
// 이슈 관리
|
||||
Route::post('/{id}/issues', [WorkOrderController::class, 'addIssue'])->whereNumber('id')->name('v1.work-orders.issues.store'); // 이슈 등록
|
||||
Route::patch('/{id}/issues/{issueId}/resolve', [WorkOrderController::class, 'resolveIssue'])->whereNumber('id')->name('v1.work-orders.issues.resolve'); // 이슈 해결
|
||||
});
|
||||
|
||||
// 파일 저장소 API
|
||||
Route::prefix('files')->group(function () {
|
||||
Route::post('/upload', [FileStorageController::class, 'upload'])->name('v1.files.upload'); // 파일 업로드 (임시)
|
||||
|
||||
Reference in New Issue
Block a user