- feat: [barobill] 바로빌 카드/은행/홈택스 REST API 구현 - feat: [equipment] 설비관리 API 백엔드 구현 - feat: [payroll] 급여관리 계산 엔진 및 일괄 처리 API - feat: [QMS] 점검표 템플릿 관리 + 로트심사 개선 - feat: [생산/출하] 수주 단위 출하 자동생성 + 상태 흐름 개선 - feat: [receiving] 입고 성적서 파일 연결 - feat: [견적] 제어기 타입 체계 변경 - feat: [email] 테넌트 메일 설정 마이그레이션 및 모델 - feat: [pmis] 시공관리 테이블 마이그레이션 - feat: [R2] 파일 업로드 커맨드 + filesystems 설정 - feat: [배포] Jenkinsfile 롤백 기능 추가 - fix: [approval] SAM API 규칙 준수 코드 개선 - fix: [account-codes] 계정과목 중복 데이터 정리 - fix: [payroll] 일괄 생성 시 삭제된 사용자 건너뛰기 - fix: [db] codebridge DB 분리 후 깨진 FK 제약조건 제거 - refactor: [barobill] 바로빌 연동 코드 전면 개선
597 lines
22 KiB
PHP
597 lines
22 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Orders\Order;
|
|
use App\Models\Production\WorkOrder;
|
|
use App\Models\Production\WorkOrderMaterialInput;
|
|
use App\Models\Qualitys\Inspection;
|
|
use App\Models\Qualitys\QualityDocument;
|
|
use App\Models\Qualitys\QualityDocumentLocation;
|
|
use App\Models\Qualitys\QualityDocumentOrder;
|
|
use App\Models\Tenants\Shipment;
|
|
use App\Models\Tenants\StockLot;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
|
|
class QmsLotAuditService extends Service
|
|
{
|
|
/**
|
|
* 품질관리서 목록 (로트 추적 심사용)
|
|
* completed 상태의 품질관리서를 PerformanceReport 기반으로 필터링
|
|
*/
|
|
public function index(array $params): array
|
|
{
|
|
$query = QualityDocument::with([
|
|
'documentOrders.order.item',
|
|
'locations',
|
|
'performanceReport',
|
|
])
|
|
->where('status', QualityDocument::STATUS_COMPLETED);
|
|
|
|
// 연도 필터
|
|
if (! empty($params['year'])) {
|
|
$year = (int) $params['year'];
|
|
$query->where(function ($q) use ($year) {
|
|
$q->whereHas('performanceReport', fn ($pr) => $pr->where('year', $year))
|
|
->orWhereDoesntHave('performanceReport');
|
|
});
|
|
}
|
|
|
|
// 분기 필터
|
|
if (! empty($params['quarter'])) {
|
|
$quarter = (int) $params['quarter'];
|
|
$query->whereHas('performanceReport', fn ($pr) => $pr->where('quarter', $quarter));
|
|
}
|
|
|
|
// 검색어 필터
|
|
if (! empty($params['q'])) {
|
|
$term = trim($params['q']);
|
|
$query->where(function ($q) use ($term) {
|
|
$q->where('quality_doc_number', 'like', "%{$term}%")
|
|
->orWhere('site_name', 'like', "%{$term}%");
|
|
});
|
|
}
|
|
|
|
$query->orderByDesc('id');
|
|
$perPage = (int) ($params['per_page'] ?? 20);
|
|
$paginated = $query->paginate($perPage);
|
|
|
|
$items = $paginated->getCollection()->map(fn ($doc) => $this->transformReportToFrontend($doc));
|
|
|
|
return [
|
|
'items' => $items,
|
|
'current_page' => $paginated->currentPage(),
|
|
'last_page' => $paginated->lastPage(),
|
|
'per_page' => $paginated->perPage(),
|
|
'total' => $paginated->total(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 품질관리서 상세 — 수주/개소 목록 (RouteItem[])
|
|
*/
|
|
public function show(int $id): array
|
|
{
|
|
$doc = QualityDocument::with([
|
|
'documentOrders.order',
|
|
'documentOrders.locations.orderItem',
|
|
])->findOrFail($id);
|
|
|
|
return $doc->documentOrders->map(fn ($docOrder) => $this->transformRouteToFrontend($docOrder, $doc))->values()->all();
|
|
}
|
|
|
|
/**
|
|
* 수주 로트별 8종 서류 목록 (Document[])
|
|
*/
|
|
public function routeDocuments(int $qualityDocumentOrderId): array
|
|
{
|
|
$docOrder = QualityDocumentOrder::with([
|
|
'order.workOrders.process',
|
|
'locations.orderItem',
|
|
'locations.document',
|
|
'qualityDocument',
|
|
])->findOrFail($qualityDocumentOrderId);
|
|
|
|
$order = $docOrder->order;
|
|
$qualityDoc = $docOrder->qualityDocument;
|
|
$workOrders = $order->workOrders;
|
|
|
|
$documents = [];
|
|
|
|
// 1. 수입검사 성적서 (IQC): WorkOrderMaterialInput → StockLot → lot_no → Inspection(IQC)
|
|
$investedLotIds = WorkOrderMaterialInput::whereIn('work_order_id', $workOrders->pluck('id'))
|
|
->pluck('stock_lot_id')
|
|
->unique();
|
|
|
|
$investedLotNos = StockLot::whereIn('id', $investedLotIds)
|
|
->whereNotNull('lot_no')
|
|
->pluck('lot_no')
|
|
->unique();
|
|
|
|
$iqcInspections = Inspection::where('inspection_type', 'IQC')
|
|
->whereIn('lot_no', $investedLotNos)
|
|
->where('status', 'completed')
|
|
->get();
|
|
|
|
$documents[] = $this->formatDocument('import', '수입검사 성적서', $iqcInspections);
|
|
|
|
// 2. 수주서
|
|
$documents[] = $this->formatDocument('order', '수주서', collect([$order]));
|
|
|
|
// 3. 작업일지 & 4. 중간검사 성적서 (인식 가능한 공정만 — 공정별 1개씩)
|
|
$recognizedWorkOrders = $workOrders->filter(function ($wo) {
|
|
$subType = $this->mapProcessToSubType($wo->process?->process_name);
|
|
|
|
return $subType !== null;
|
|
})->groupBy('process_id')->map(fn ($group) => $group->first());
|
|
|
|
$documents[] = $this->formatDocumentWithSubType('log', '작업일지', $recognizedWorkOrders);
|
|
$documents[] = $this->formatDocumentWithSubType('report', '중간검사 성적서', $recognizedWorkOrders);
|
|
|
|
// 5. 납품확인서
|
|
$shipments = $order->shipments()->get();
|
|
$documents[] = $this->formatDocument('confirmation', '납품확인서', $shipments);
|
|
|
|
// 6. 출고증
|
|
$documents[] = $this->formatDocument('shipping', '출고증', $shipments);
|
|
|
|
// 7. 제품검사 성적서 (FQC 문서 또는 inspection_data 완료건)
|
|
$locationsWithInspection = $docOrder->locations->filter(
|
|
fn ($loc) => $loc->document_id || $loc->inspection_status === 'completed'
|
|
);
|
|
$documents[] = $this->formatDocument('product', '제품검사 성적서', $locationsWithInspection);
|
|
|
|
// 8. 품질관리서
|
|
$documents[] = $this->formatDocument('quality', '품질관리서', collect([$qualityDoc]));
|
|
|
|
return $documents;
|
|
}
|
|
|
|
/**
|
|
* 서류 상세 조회 (2단계 로딩 — 모달 렌더링용)
|
|
*/
|
|
public function documentDetail(string $type, int $id): array
|
|
{
|
|
return match ($type) {
|
|
'import' => $this->getInspectionDetail($id, 'IQC'),
|
|
'order' => $this->getOrderDetail($id),
|
|
'log' => $this->getWorkOrderLogDetail($id),
|
|
'report' => $this->getWorkOrderLogDetail($id),
|
|
'confirmation', 'shipping' => $this->getShipmentDetail($id),
|
|
'product' => $this->getLocationDetail($id),
|
|
'quality' => $this->getQualityDocDetail($id),
|
|
default => throw new NotFoundHttpException(__('error.not_found')),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 개소별 로트 심사 확인 토글
|
|
*/
|
|
public function confirm(int $locationId, array $data): array
|
|
{
|
|
$location = QualityDocumentLocation::findOrFail($locationId);
|
|
$confirmed = (bool) $data['confirmed'];
|
|
$userId = $this->apiUserId();
|
|
|
|
DB::transaction(function () use ($location, $confirmed, $userId) {
|
|
$location->lockForUpdate();
|
|
|
|
$options = $location->options ?? [];
|
|
$options['lot_audit_confirmed'] = $confirmed;
|
|
$options['lot_audit_confirmed_at'] = $confirmed ? now()->toIso8601String() : null;
|
|
$options['lot_audit_confirmed_by'] = $confirmed ? $userId : null;
|
|
$location->options = $options;
|
|
$location->save();
|
|
});
|
|
|
|
$location->refresh();
|
|
|
|
return [
|
|
'id' => (string) $location->id,
|
|
'name' => $this->buildLocationName($location),
|
|
'location' => $this->buildLocationCode($location),
|
|
'is_completed' => (bool) data_get($location->options, 'lot_audit_confirmed', false),
|
|
];
|
|
}
|
|
|
|
// ===== Private: Transform Methods =====
|
|
|
|
private function transformReportToFrontend(QualityDocument $doc): array
|
|
{
|
|
$performanceReport = $doc->performanceReport;
|
|
$confirmedCount = $doc->locations->filter(function ($loc) {
|
|
return data_get($loc->options, 'lot_audit_confirmed', false);
|
|
})->count();
|
|
|
|
return [
|
|
'id' => (string) $doc->id,
|
|
'code' => $doc->quality_doc_number,
|
|
'site_name' => $doc->site_name,
|
|
'item' => $this->getFgProductName($doc),
|
|
'route_count' => $confirmedCount,
|
|
'total_routes' => $doc->locations->count(),
|
|
'quarter' => $performanceReport
|
|
? $performanceReport->year.'년 '.$performanceReport->quarter.'분기'
|
|
: '',
|
|
'year' => $performanceReport?->year ?? now()->year,
|
|
'quarter_num' => $performanceReport?->quarter ?? 0,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 수주 대표 제품명 추출
|
|
* Order.item_id → Item.name
|
|
*/
|
|
private function getFgProductName(QualityDocument $doc): string
|
|
{
|
|
$order = $doc->documentOrders->first()?->order;
|
|
|
|
return $order?->item?->name ?? '';
|
|
}
|
|
|
|
private function transformRouteToFrontend(QualityDocumentOrder $docOrder, QualityDocument $qualityDoc): array
|
|
{
|
|
return [
|
|
'id' => (string) $docOrder->id,
|
|
'code' => $docOrder->order->order_no,
|
|
'date' => $docOrder->order->received_at?->toDateString(),
|
|
'client' => $docOrder->order->client_name ?? '',
|
|
'site' => $docOrder->order->site_name ?? '',
|
|
'location_count' => $docOrder->locations->count(),
|
|
'sub_items' => $docOrder->locations->values()->map(fn ($loc, $idx) => [
|
|
'id' => (string) $loc->id,
|
|
'name' => $qualityDoc->quality_doc_number.'-'.str_pad($idx + 1, 2, '0', STR_PAD_LEFT),
|
|
'location' => $this->buildLocationCode($loc),
|
|
'is_completed' => (bool) data_get($loc->options, 'lot_audit_confirmed', false),
|
|
])->all(),
|
|
];
|
|
}
|
|
|
|
private function buildLocationName(QualityDocumentLocation $location): string
|
|
{
|
|
$qualityDoc = $location->qualityDocument;
|
|
if (! $qualityDoc) {
|
|
return '';
|
|
}
|
|
|
|
// location의 순번을 구하기 위해 같은 문서의 location 목록 조회
|
|
$locations = QualityDocumentLocation::where('quality_document_id', $qualityDoc->id)
|
|
->orderBy('id')
|
|
->pluck('id');
|
|
|
|
$index = $locations->search($location->id);
|
|
|
|
return $qualityDoc->quality_doc_number.'-'.str_pad(($index !== false ? $index + 1 : 1), 2, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
private function buildLocationCode(QualityDocumentLocation $location): string
|
|
{
|
|
$orderItem = $location->orderItem;
|
|
if (! $orderItem) {
|
|
return '';
|
|
}
|
|
|
|
return trim(($orderItem->floor_code ?? '').' '.($orderItem->symbol_code ?? ''));
|
|
}
|
|
|
|
// ===== Private: Document Format Helpers =====
|
|
|
|
private function formatDocument(string $type, string $title, $collection): array
|
|
{
|
|
return [
|
|
'id' => $type,
|
|
'type' => $type,
|
|
'title' => $title,
|
|
'count' => $collection->count(),
|
|
'items' => $collection->values()->map(fn ($item) => $this->formatDocumentItem($type, $item))->all(),
|
|
];
|
|
}
|
|
|
|
private function formatDocumentWithSubType(string $type, string $title, $collection, ?string $workOrderRelation = null): array
|
|
{
|
|
return [
|
|
'id' => $type,
|
|
'type' => $type,
|
|
'title' => $title,
|
|
'count' => $collection->count(),
|
|
'items' => $collection->values()->map(function ($item) use ($type, $workOrderRelation) {
|
|
$formatted = $this->formatDocumentItem($type, $item);
|
|
|
|
// subType: process.process_name 기반 + work_order_id 전달
|
|
$workOrder = $workOrderRelation ? $item->{$workOrderRelation} : $item;
|
|
if ($workOrder instanceof WorkOrder) {
|
|
$processName = $workOrder->process?->process_name;
|
|
$formatted['sub_type'] = $this->mapProcessToSubType($processName);
|
|
$formatted['work_order_id'] = $workOrder->id;
|
|
}
|
|
|
|
return $formatted;
|
|
})->all(),
|
|
];
|
|
}
|
|
|
|
private function formatDocumentItem(string $type, $item): array
|
|
{
|
|
return match ($type) {
|
|
'import' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->inspection_no ?? '',
|
|
'date' => $item->inspection_date?->toDateString() ?? $item->request_date?->toDateString() ?? '',
|
|
'code' => $item->inspection_no ?? '',
|
|
],
|
|
'report' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->process?->process_name ?? '중간검사 성적서',
|
|
'date' => $item->created_at?->toDateString() ?? '',
|
|
'code' => $item->work_order_no ?? '',
|
|
],
|
|
'order' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->order_no,
|
|
'date' => $item->received_at?->toDateString() ?? '',
|
|
'code' => $item->order_no,
|
|
],
|
|
'log' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->process?->process_name ?? '작업일지',
|
|
'date' => $item->created_at?->toDateString() ?? '',
|
|
'code' => $item->work_order_no ?? '',
|
|
],
|
|
'confirmation', 'shipping' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->shipment_no ?? '',
|
|
'date' => $item->scheduled_date?->toDateString() ?? '',
|
|
'code' => $item->shipment_no ?? '',
|
|
],
|
|
'product' => [
|
|
'id' => (string) $item->id,
|
|
'title' => trim(($item->orderItem?->floor_code ?? '').' '.($item->orderItem?->symbol_code ?? '')) ?: '제품검사 성적서',
|
|
'date' => $item->updated_at?->toDateString() ?? '',
|
|
'code' => $item->document?->document_no ?? '',
|
|
],
|
|
'quality' => [
|
|
'id' => (string) $item->id,
|
|
'title' => $item->quality_doc_number ?? '',
|
|
'date' => $item->received_date?->toDateString() ?? '',
|
|
'code' => $item->quality_doc_number ?? '',
|
|
],
|
|
default => [
|
|
'id' => (string) $item->id,
|
|
'title' => '',
|
|
'date' => '',
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* process_name → subType 매핑
|
|
*/
|
|
private function mapProcessToSubType(?string $processName): ?string
|
|
{
|
|
if (! $processName) {
|
|
return null;
|
|
}
|
|
|
|
$name = mb_strtolower($processName);
|
|
|
|
return match (true) {
|
|
str_contains($name, 'screen') || str_contains($name, '스크린') => 'screen',
|
|
str_contains($name, 'bending') || str_contains($name, '절곡') => 'bending',
|
|
str_contains($name, 'slat') || str_contains($name, '슬랫') => 'slat',
|
|
str_contains($name, 'jointbar') || str_contains($name, '조인트바') || str_contains($name, 'joint') => 'jointbar',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
// ===== Private: Document Detail Methods (2단계 로딩) =====
|
|
|
|
private function getInspectionDetail(int $id, string $type): array
|
|
{
|
|
$inspection = Inspection::where('inspection_type', $type)
|
|
->with(['item', 'workOrder.process'])
|
|
->findOrFail($id);
|
|
|
|
return [
|
|
'type' => $type === 'IQC' ? 'import' : 'report',
|
|
'data' => [
|
|
'id' => $inspection->id,
|
|
'inspection_no' => $inspection->inspection_no,
|
|
'inspection_type' => $inspection->inspection_type,
|
|
'status' => $inspection->status,
|
|
'result' => $inspection->result,
|
|
'request_date' => $inspection->request_date?->toDateString(),
|
|
'inspection_date' => $inspection->inspection_date?->toDateString(),
|
|
'lot_no' => $inspection->lot_no,
|
|
'item_name' => $inspection->item?->name,
|
|
'process_name' => $inspection->workOrder?->process?->process_name,
|
|
'meta' => $inspection->meta,
|
|
'items' => $inspection->items,
|
|
'attachments' => $inspection->attachments,
|
|
'extra' => $inspection->extra,
|
|
],
|
|
];
|
|
}
|
|
|
|
private function getOrderDetail(int $id): array
|
|
{
|
|
$order = Order::with(['nodes' => fn ($q) => $q->whereNull('parent_id')])->findOrFail($id);
|
|
|
|
return [
|
|
'type' => 'order',
|
|
'data' => [
|
|
'id' => $order->id,
|
|
'order_no' => $order->order_no,
|
|
'status' => $order->status,
|
|
'received_at' => $order->received_at?->toDateString(),
|
|
'site_name' => $order->site_name,
|
|
'nodes_count' => $order->nodes->count(),
|
|
],
|
|
];
|
|
}
|
|
|
|
private function getWorkOrderLogDetail(int $id): array
|
|
{
|
|
$workOrder = WorkOrder::with('process')->findOrFail($id);
|
|
|
|
return [
|
|
'type' => 'log',
|
|
'data' => [
|
|
'id' => $workOrder->id,
|
|
'project_name' => $workOrder->project_name,
|
|
'status' => $workOrder->status,
|
|
'process_name' => $workOrder->process?->process_name,
|
|
'options' => $workOrder->options,
|
|
'created_at' => $workOrder->created_at?->toDateString(),
|
|
],
|
|
];
|
|
}
|
|
|
|
private function getShipmentDetail(int $id): array
|
|
{
|
|
$shipment = Shipment::findOrFail($id);
|
|
|
|
return [
|
|
'type' => 'shipping',
|
|
'data' => [
|
|
'id' => $shipment->id,
|
|
'shipment_no' => $shipment->shipment_no,
|
|
'status' => $shipment->status,
|
|
'scheduled_date' => $shipment->scheduled_date?->toDateString(),
|
|
'customer_name' => $shipment->customer_name,
|
|
'site_name' => $shipment->site_name,
|
|
'delivery_address' => $shipment->delivery_address,
|
|
'delivery_method' => $shipment->delivery_method,
|
|
'vehicle_no' => $shipment->vehicle_no,
|
|
'driver_name' => $shipment->driver_name,
|
|
'remarks' => $shipment->remarks,
|
|
],
|
|
];
|
|
}
|
|
|
|
private function getLocationDetail(int $id): array
|
|
{
|
|
$location = QualityDocumentLocation::with([
|
|
'orderItem',
|
|
'document.template.sections.items',
|
|
'document.template.columns',
|
|
'document.template.approvalLines',
|
|
'document.template.basicFields',
|
|
'document.data',
|
|
])->findOrFail($id);
|
|
|
|
$result = [
|
|
'type' => 'product',
|
|
'data' => [
|
|
'id' => $location->id,
|
|
'inspection_status' => $location->inspection_status,
|
|
'inspection_data' => $location->inspection_data,
|
|
'post_width' => $location->post_width,
|
|
'post_height' => $location->post_height,
|
|
'floor_code' => $location->orderItem?->floor_code,
|
|
'symbol_code' => $location->orderItem?->symbol_code,
|
|
'document_id' => $location->document_id,
|
|
],
|
|
];
|
|
|
|
// FQC 문서가 있으면 template + data 포함
|
|
if ($location->document) {
|
|
$doc = $location->document;
|
|
$result['data']['fqc_document'] = [
|
|
'id' => $doc->id,
|
|
'template_id' => $doc->template_id,
|
|
'document_no' => $doc->document_no,
|
|
'title' => $doc->title,
|
|
'status' => $doc->status,
|
|
'created_at' => $doc->created_at?->toIso8601String(),
|
|
'template' => $this->formatFqcTemplate($doc->template),
|
|
'data' => $doc->data->map(fn ($d) => [
|
|
'section_id' => $d->section_id,
|
|
'column_id' => $d->column_id,
|
|
'row_index' => $d->row_index,
|
|
'field_key' => $d->field_key,
|
|
'field_value' => $d->field_value,
|
|
])->all(),
|
|
];
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function formatFqcTemplate($template): ?array
|
|
{
|
|
if (! $template) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'id' => $template->id,
|
|
'name' => $template->name,
|
|
'category' => $template->category,
|
|
'title' => $template->title,
|
|
'approval_lines' => $template->approvalLines->map(fn ($a) => [
|
|
'id' => $a->id,
|
|
'name' => $a->name,
|
|
'department' => $a->department,
|
|
'sort_order' => $a->sort_order,
|
|
])->all(),
|
|
'basic_fields' => $template->basicFields->map(fn ($f) => [
|
|
'id' => $f->id,
|
|
'label' => $f->label,
|
|
'field_key' => $f->field_key,
|
|
'field_type' => $f->field_type,
|
|
'default_value' => $f->default_value,
|
|
'is_required' => $f->is_required,
|
|
'sort_order' => $f->sort_order,
|
|
])->all(),
|
|
'sections' => $template->sections->map(fn ($s) => [
|
|
'id' => $s->id,
|
|
'name' => $s->name,
|
|
'title' => $s->title,
|
|
'description' => $s->description,
|
|
'image_path' => $s->image_path,
|
|
'sort_order' => $s->sort_order,
|
|
'items' => $s->items->map(fn ($i) => [
|
|
'id' => $i->id,
|
|
'section_id' => $i->section_id,
|
|
'item_name' => $i->item ?? '',
|
|
'standard' => $i->standard,
|
|
'tolerance' => $i->tolerance,
|
|
'measurement_type' => $i->measurement_type,
|
|
'frequency' => $i->frequency,
|
|
'sort_order' => $i->sort_order,
|
|
'category' => $i->category,
|
|
'method' => $i->method,
|
|
])->all(),
|
|
])->all(),
|
|
'columns' => $template->columns->map(fn ($c) => [
|
|
'id' => $c->id,
|
|
'label' => $c->label,
|
|
'column_type' => $c->column_type,
|
|
'width' => $c->width,
|
|
'group_name' => $c->group_name,
|
|
'sort_order' => $c->sort_order,
|
|
])->all(),
|
|
];
|
|
}
|
|
|
|
private function getQualityDocDetail(int $id): array
|
|
{
|
|
$doc = QualityDocument::with(['client', 'inspector:id,name'])->findOrFail($id);
|
|
|
|
return [
|
|
'type' => 'quality',
|
|
'data' => [
|
|
'id' => $doc->id,
|
|
'quality_doc_number' => $doc->quality_doc_number,
|
|
'site_name' => $doc->site_name,
|
|
'status' => $doc->status,
|
|
'received_date' => $doc->received_date?->toDateString(),
|
|
'client_name' => $doc->client?->name,
|
|
'inspector_name' => $doc->inspector?->name,
|
|
'options' => $doc->options,
|
|
],
|
|
];
|
|
}
|
|
}
|