feat: 견적확정 밸리데이션, 작업지시 통계 공정별 카운트, 입고/재고 개선
- 견적확정 시 업체명/현장명/담당자/연락처 필수 검증 추가 (QuoteService) - 작업지시 stats API에 by_process 공정별 카운트 반환 추가 - 작업지시 목록/상세 쿼리에 수주 개소(rootNodes) 연관 로딩 - 작업지시 품목에 sourceOrderItem.node 관계 추가 - 입고관리 완료건 수정 허용 및 재고 차이 조정 - work_order_step_progress 테이블 마이그레이션 - receivings 테이블 options 컬럼 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -489,21 +489,12 @@ public function resolve(array $params): array
|
||||
];
|
||||
$categoryName = $categoryMapping[$category] ?? $category;
|
||||
|
||||
$template = DocumentTemplate::query()
|
||||
$baseQuery = DocumentTemplate::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('is_active', true)
|
||||
->where(function ($q) use ($category, $categoryName) {
|
||||
// category 필드가 code 또는 name과 매칭
|
||||
$q->where('category', $category)
|
||||
->orWhere('category', $categoryName)
|
||||
->orWhere('category', 'LIKE', "%{$categoryName}%");
|
||||
})
|
||||
->whereHas('links', function ($q) use ($itemId) {
|
||||
// 해당 item_id가 연결된 템플릿만
|
||||
$q->where('source_table', 'items')
|
||||
->whereHas('linkValues', function ($q2) use ($itemId) {
|
||||
$q2->where('linkable_id', $itemId);
|
||||
});
|
||||
->where(function ($q) use ($itemId) {
|
||||
$q->whereJsonContains('linked_item_ids', (int) $itemId)
|
||||
->orWhereJsonContains('linked_item_ids', (string) $itemId);
|
||||
})
|
||||
->with([
|
||||
'approvalLines',
|
||||
@@ -511,10 +502,22 @@ public function resolve(array $params): array
|
||||
'sections.items',
|
||||
'columns',
|
||||
'sectionFields',
|
||||
'links.linkValues',
|
||||
])
|
||||
]);
|
||||
|
||||
// 1차: category 매칭 + item_id
|
||||
$template = (clone $baseQuery)
|
||||
->where(function ($q) use ($category, $categoryName) {
|
||||
$q->where('category', $category)
|
||||
->orWhere('category', $categoryName)
|
||||
->orWhere('category', 'LIKE', "%{$categoryName}%");
|
||||
})
|
||||
->first();
|
||||
|
||||
// 2차: category 무관, item_id 연결만으로 fallback
|
||||
if (! $template) {
|
||||
$template = $baseQuery->first();
|
||||
}
|
||||
|
||||
if (! $template) {
|
||||
throw new NotFoundHttpException(__('error.document.template_not_found'));
|
||||
}
|
||||
@@ -607,6 +610,19 @@ public function upsert(array $data): Document
|
||||
*/
|
||||
private function formatTemplateForReact(DocumentTemplate $template): array
|
||||
{
|
||||
// common_codes에서 inspection_method 코드 목록 조회 (code → name 매핑)
|
||||
$tenantId = $this->tenantId();
|
||||
$methodCodes = DB::table('common_codes')
|
||||
->where('code_group', 'inspection_method')
|
||||
->where('is_active', true)
|
||||
->where(function ($q) use ($tenantId) {
|
||||
$q->where('tenant_id', $tenantId)
|
||||
->orWhereNull('tenant_id');
|
||||
})
|
||||
->orderByRaw('tenant_id IS NULL') // tenant 우선
|
||||
->pluck('name', 'code')
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'id' => $template->id,
|
||||
'name' => $template->name,
|
||||
@@ -620,6 +636,8 @@ private function formatTemplateForReact(DocumentTemplate $template): array
|
||||
'footer_judgement_options' => $template->footer_judgement_options,
|
||||
'approval_lines' => $template->approvalLines->map(fn ($line) => [
|
||||
'id' => $line->id,
|
||||
'name' => $line->name,
|
||||
'dept' => $line->dept,
|
||||
'role' => $line->role,
|
||||
'user_id' => $line->user_id,
|
||||
'sort_order' => $line->sort_order,
|
||||
@@ -648,23 +666,29 @@ private function formatTemplateForReact(DocumentTemplate $template): array
|
||||
'id' => $section->id,
|
||||
'name' => $section->name,
|
||||
'sort_order' => $section->sort_order,
|
||||
'items' => $section->items->map(fn ($item) => [
|
||||
'id' => $item->id,
|
||||
'field_values' => $item->field_values ?? [],
|
||||
// 레거시 필드도 포함 (하위 호환)
|
||||
'category' => $item->category,
|
||||
'item' => $item->item,
|
||||
'standard' => $item->standard,
|
||||
'standard_criteria' => $item->standard_criteria,
|
||||
'tolerance' => $item->tolerance,
|
||||
'method' => $item->method,
|
||||
'measurement_type' => $item->measurement_type,
|
||||
'frequency' => $item->frequency,
|
||||
'frequency_n' => $item->frequency_n,
|
||||
'frequency_c' => $item->frequency_c,
|
||||
'regulation' => $item->regulation,
|
||||
'sort_order' => $item->sort_order,
|
||||
])->toArray(),
|
||||
'items' => $section->items->map(function ($item) use ($methodCodes) {
|
||||
// method 코드를 한글 이름으로 변환
|
||||
$methodName = $item->method ? ($methodCodes[$item->method] ?? $item->method) : null;
|
||||
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'field_values' => $item->field_values ?? [],
|
||||
// 레거시 필드도 포함 (하위 호환)
|
||||
'category' => $item->category,
|
||||
'item' => $item->item,
|
||||
'standard' => $item->standard,
|
||||
'standard_criteria' => $item->standard_criteria,
|
||||
'tolerance' => $item->tolerance,
|
||||
'method' => $item->method,
|
||||
'method_name' => $methodName, // 검사방식 한글 이름 추가
|
||||
'measurement_type' => $item->measurement_type,
|
||||
'frequency' => $item->frequency,
|
||||
'frequency_n' => $item->frequency_n,
|
||||
'frequency_c' => $item->frequency_c,
|
||||
'regulation' => $item->regulation,
|
||||
'sort_order' => $item->sort_order,
|
||||
];
|
||||
})->toArray(),
|
||||
])->toArray(),
|
||||
'columns' => $template->columns->map(fn ($col) => [
|
||||
'id' => $col->id,
|
||||
@@ -707,9 +731,11 @@ private function formatDocumentForReact(Document $document): array
|
||||
'description' => $a->description,
|
||||
'file' => $a->file ? [
|
||||
'id' => $a->file->id,
|
||||
'original_name' => $a->file->original_name,
|
||||
'original_name' => $a->file->original_name ?? $a->file->display_name ?? $a->file->stored_name,
|
||||
'display_name' => $a->file->display_name,
|
||||
'file_path' => $a->file->file_path,
|
||||
'file_size' => $a->file->file_size,
|
||||
'mime_type' => $a->file->mime_type,
|
||||
] : null,
|
||||
])->toArray(),
|
||||
'approvals' => $document->approvals->map(fn ($ap) => [
|
||||
|
||||
Reference in New Issue
Block a user