İlk temizlik tamamlandı bir önceki projeden

This commit is contained in:
Ümit Tunç
2026-04-28 21:14:25 +03:00
commit f80443aec0
10000 changed files with 959965 additions and 0 deletions
@@ -0,0 +1,46 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Redirect;
use Illuminate\Support\Facades\Auth;
class AdminAjaxController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index(Request $request, string $var) {
$u = Auth::user();
//$izin = explode(",","Admin,Head Of Department");
if(!$u || !isset($u->level)) {
echo("permission error");
exit();
}
//print_r($u->level); exit();
/*
$this->middleware('auth');
$path = "AdminAjax/$var.php";
include($path);
return $return; // return işlemi include da çalışmadığından bir değişken ile buraya aktardık
*/
$return = null;
$url = "admin-ajax.$var";
return view($url,array(
"request" => $request
));
/*
try {
include("AdminAjax/$var.php");
} catch (\Exception $e) {
return abort(404);
}
*/
}
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AjaxController extends Controller
{
public function index(Request $request, string $var) {
$this->middleware('auth');
$path = "Ajax/$var.php";
try {
include("Ajax/$var.php");
return $return;
} catch (\Exception $e) {
echo $e;
//return abort(404);
}
}
}
@@ -0,0 +1,829 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Annotation;
use App\Models\DocumentRevision;
use App\Models\WeldLog;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Schema;
class AnnotationController extends Controller
{
public function store(Request $request): JsonResponse
{
$request->validate([
'annotations' => 'required|array',
'annotations.*.shape_id' => 'required|string',
'annotations.*.spool_no' => 'nullable|string',
'annotations.*.joint_type' => 'nullable|string',
'annotations.*.welder_id' => 'nullable|string',
'annotations.*.classification' => 'nullable|string',
'annotations.*.page_number' => 'required|integer',
'annotations.*.x_coordinate' => 'required|numeric',
'annotations.*.y_coordinate' => 'required|numeric',
'annotations.*.width' => 'required|numeric',
'annotations.*.height' => 'required|numeric',
'annotations.*.shape_type' => 'required|in:rect,circle,triangle,spool,weld,shop_weld,leader_line,text,line',
'annotations.*.pdf_file_path' => 'required|string',
'annotations.*.created_by' => 'required|integer',
'annotations.*.original_start_point' => 'nullable|string',
'annotations.*.connected_shape_id' => 'nullable|string',
]);
try {
$annotations = [];
foreach ($request->annotations as $annotationData) {
// Check if annotation already exists
$existingAnnotation = Annotation::where('shape_id', $annotationData['shape_id'])->first();
if ($existingAnnotation) {
// Update existing annotation
$existingAnnotation->update($annotationData);
$annotations[] = $existingAnnotation;
} else {
// Create new annotation
$annotations[] = Annotation::create($annotationData);
}
}
return response()->json([
'success' => true,
'data' => $annotations,
'message' => 'Annotations saved successfully!'
], 201);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'message' => 'Failed to save annotations'
], 500);
}
}
public function index(Request $request): JsonResponse
{
$pdfFile = $request->get('pdf_file');
$annotations = Annotation::when($pdfFile, function ($query, $pdfFile) {
return $query->where('pdf_file_path', $pdfFile);
})
->where('is_active', 1)
->orderBy('created_at', 'asc')
->get();
return response()->json([
'success' => true,
'data' => $annotations
]);
}
public function show(Annotation $annotation): JsonResponse
{
return response()->json([
'success' => true,
'data' => $annotation
]);
}
public function update(Request $request, Annotation $annotation): JsonResponse
{
$request->validate([
'spool_no' => 'nullable|string',
'joint_type' => 'nullable|string',
'welder_id' => 'nullable|string',
'classification' => 'nullable|string',
]);
$annotation->update($request->only(['spool_no', 'joint_type', 'welder_id', 'classification']));
return response()->json([
'success' => true,
'data' => $annotation
]);
}
public function destroy(Annotation $annotation): JsonResponse
{
$annotation->delete();
return response()->json([
'success' => true,
'message' => 'Annotation deleted successfully'
]);
}
public function deleteMultiple(Request $request): JsonResponse
{
$request->validate([
'shape_ids' => 'required|array',
'shape_ids.*' => 'required|string',
'pdf_file' => 'required|string',
'page_number' => 'required|integer'
]);
try {
$deletedCount = Annotation::whereIn('shape_id', $request->shape_ids)
->where('pdf_file_path', $request->pdf_file)
->where('page_number', $request->page_number)
->delete();
return response()->json([
'success' => true,
'deleted_count' => $deletedCount,
'message' => "Successfully deleted {$deletedCount} annotations from page {$request->page_number}"
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'message' => 'Failed to delete annotations'
], 500);
}
}
public function sync(Request $request): JsonResponse
{
$request->validate([
'annotations' => 'required|array',
'pdf_file' => 'required|string',
'page_number' => 'required|integer'
]);
try {
$pdfFile = $request->pdf_file;
$pageNumber = $request->page_number;
$userId = $request->user_id ?? auth()->id() ?? 1;
$lastVersion = Annotation::where('pdf_file_path', 'like', '%' . basename($pdfFile) . '%')
->where('page_number', $pageNumber)
->max('version');
$newVersion = ($lastVersion ?? 0) + 1;
$saved = [];
foreach ($request->annotations as $data) {
// Use updateOrCreate to prevent duplicates - update existing annotation with same shape_id, pdf_file_path, page_number
// Only create new version if shape_id doesn't exist in current version
$annotation = Annotation::updateOrCreate(
[
'shape_id' => $data['shape_id'],
'pdf_file_path' => $pdfFile,
'page_number' => $pageNumber,
'version' => $newVersion,
'is_active' => 1
],
[
'spool_no' => $data['spool_no'] ?? null,
'joint_type' => $data['joint_type'] ?? null,
'welder_id' => $data['welder_id'] ?? null,
'classification' => $data['classification'] ?? null,
'x_coordinate' => $data['x_coordinate'] ?? 0,
'y_coordinate' => $data['y_coordinate'] ?? 0,
'width' => $data['width'] ?? 0,
'height' => $data['height'] ?? 0,
'shape_type' => $data['shape_type'] ?? 'rect',
'qr_code_data' => $data['qr_code_data'] ?? null,
'created_by' => $data['created_by'] ?? $userId,
'original_start_point' => $data['original_start_point'] ?? null,
'connected_shape_id' => $data['connected_shape_id'] ?? null,
'stroke_color' => $data['stroke_color'] ?? '#000000',
'stroke_width' => $data['stroke_width'] ?? 2,
]
);
$saved[] = $annotation;
}
return response()->json([
'success' => true,
'message' => "Page synced successfully as version {$newVersion}",
'version' => $newVersion,
'data' => $saved
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'message' => 'Failed to sync annotations'
], 500);
}
}
public function exportPDF(Request $request)
{
try {
$request->validate([
'pdf_file' => 'required|string',
'annotations' => 'required|array',
'canvas_image' => 'required|string',
'export_format' => 'required|string',
// Optional parameters for saving to Document Revision
'save_to_revision' => 'nullable|boolean',
'stage' => 'nullable|in:pdf_spool_iso,pdf_as_build',
'drawing_no' => 'nullable|string',
'revision_id' => 'nullable|integer',
]);
// Canvas image'i decode et
$imageData = $request->canvas_image;
// Remove data:image/png;base64, part
$imageData = str_replace('data:image/png;base64,', '', $imageData);
$imageData = str_replace(' ', '+', $imageData);
$decodedImage = base64_decode($imageData);
// Temporary image file oluştur
$tempImagePath = storage_path('app/temp_export_' . time() . '.png');
file_put_contents($tempImagePath, $decodedImage);
// mPDF ile PDF oluştur
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4-L', // Landscape orientation
'margin_left' => 0,
'margin_right' => 0,
'margin_top' => 0,
'margin_bottom' => 0
]);
// Image'i PDF'e ekle
$html = '<img src="' . $tempImagePath . '" style="width: 100%; height: auto;" />';
$mpdf->WriteHTML($html);
// Generate filename
$originalFilename = basename($request->pdf_file, '.pdf');
$filename = 'annotated_' . $originalFilename . '_' . time() . '.pdf';
// Temporary file'ı sil
if (file_exists($tempImagePath)) {
unlink($tempImagePath);
}
// Check if we should save to Document Revision
$saveToRevision = $request->input('save_to_revision', false);
if ($saveToRevision && $request->filled('stage') && $request->filled('drawing_no')) {
$stage = $request->input('stage');
$drawingNo = $request->input('drawing_no');
$revisionId = $request->input('revision_id');
// Determine storage folder based on stage
$folderMap = [
'pdf_spool_iso' => 'storage/documents/001_Drawings/002_Iso',
'pdf_as_build' => 'storage/documents/001_Drawings/003_As_Build(ID)',
];
$folder = $folderMap[$stage] ?? 'storage/documents/001_Drawings/003_As_Build(ID)';
$storagePath = public_path($folder);
// Ensure directory exists
if (!is_dir($storagePath)) {
mkdir($storagePath, 0755, true);
}
// Generate unique filename with drawing number
$safeDrawingNo = preg_replace('/[^A-Za-z0-9\-\_\.]/', '_', $drawingNo);
$savedFilename = $safeDrawingNo . '_annotated_' . date('Ymd_His') . '.pdf';
$fullSavePath = $storagePath . '/' . $savedFilename;
// Save PDF to storage
$pdfContent = $mpdf->Output('', 'S');
file_put_contents($fullSavePath, $pdfContent);
\Log::info('PDF saved to storage', [
'path' => $fullSavePath,
'stage' => $stage,
'drawing_no' => $drawingNo
]);
// Update document_revisions table
$relativePath = $folder . '/' . $savedFilename;
$query = DocumentRevision::query()
->where('drawing_no', $drawingNo);
if ($revisionId) {
$query->where('id', $revisionId);
}
$updatedRows = $query->update([
$stage => $relativePath,
]);
\Log::info('Document revision updated', [
'updated_rows' => $updatedRows,
'stage' => $stage,
'pdf_path' => $relativePath
]);
return response()->json([
'success' => true,
'message' => 'PDF saved to ' . $stage . ' successfully',
'pdf_path' => $relativePath,
'updated_rows' => $updatedRows,
'filename' => $savedFilename,
]);
}
// Return as download (default behavior)
return response($mpdf->Output($filename, 'S'), 200)
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
} catch (\Exception $e) {
\Log::error('Export PDF failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'message' => 'Failed to export PDF'
], 500);
}
}
public function actionHistory(Request $request): JsonResponse
{
$pdfFile = $request->query('pdf_file');
$logs = Annotation::query()
->with(['updatedUser:id,name'])
->when($pdfFile, fn($q) => $q->where('pdf_file_path', 'like', "%{$pdfFile}%"))
->orderByDesc('updated_at')
->select('version', 'updated_by', 'updated_at', 'is_active')
->limit(200)
->get();
$grouped = $logs
->sortByDesc('updated_at')
->groupBy('version')
->map(fn($items) => $items->sortByDesc('updated_at')->first());
return response()->json([
'success' => true,
'data' => $grouped->map(fn($log) => [
'version' => $log->version,
'user' => $log->updatedUser->name ?? 'System',
'action' => $log->version == 1 ? 'New Record' : 'Updated',
'time' => $log->updated_at?->format('Y-m-d H:i:s'),
])->values(),
]);
}
public function versionAnnotations(Request $request): JsonResponse
{
$request->validate([
'pdf_file' => 'required|string',
'page_number' => 'required|integer',
'version' => 'required|integer',
]);
$pdfFile = $request->pdf_file;
$page = (int) $request->page_number;
$version = (int) $request->version;
$items = Annotation::where('pdf_file_path', 'like', '%' . basename($pdfFile) . '%')
->where('page_number', $page)
->where('version', $version)
->orderBy('id', 'asc')
->get();
return response()->json([
'success' => true,
'data' => $items,
]);
}
/**
* Save joint data to Base Weldmap (weld_logs table)
* Phase 3.2: Base Weldmap Integration
*/
public function saveJointData(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'drawing_number' => 'required|string',
'joint_data' => 'required|array',
'joint_data.*.joint_no' => 'nullable|string',
'joint_data.*.type_of_joint' => 'nullable|string|in:F,S',
'joint_data.*.type_of_welds' => 'nullable|string',
'joint_data.*.welding_date' => 'nullable|date',
'joint_data.*.spool_no1' => 'nullable|string',
'joint_data.*.spool_no2' => 'nullable|string',
'joint_data.*.pose_no1' => 'nullable|string',
'joint_data.*.pose_no2' => 'nullable|string',
'joint_data.*.member1' => 'nullable|string',
'joint_data.*.member2' => 'nullable|string',
'joint_data.*.material' => 'nullable|string',
'joint_data.*.dia1' => 'nullable|numeric',
'joint_data.*.dia2' => 'nullable|numeric',
'joint_data.*.nps1' => 'nullable|string',
'joint_data.*.nps2' => 'nullable|string',
'joint_data.*.thickness1' => 'nullable|numeric',
'joint_data.*.thickness2' => 'nullable|numeric',
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors()
], 422);
}
try {
DB::beginTransaction();
$drawingNumber = $request->drawing_number;
$jointDataArray = $request->joint_data;
$savedJoints = [];
foreach ($jointDataArray as $jointData) {
// Map widget form data to weld_logs table structure
$weldLogData = [
'iso_number' => $drawingNumber,
'no_of_the_joint_as_per_as_built_survey' => $jointData['joint_no'] ?? null,
'type_of_joint' => $jointData['type_of_joint'] ?? null,
'type_of_welds' => $jointData['type_of_welds'] ?? null,
'welding_date' => $jointData['welding_date'] ?? null,
];
// Handle spool numbers - use spool_number_1/2 if exists, otherwise use spool_number/spool_number2
if (isset($jointData['spool_no1'])) {
if (Schema::hasColumn('weld_logs', 'spool_number_1')) {
$weldLogData['spool_number_1'] = $jointData['spool_no1'];
} else {
$weldLogData['spool_number'] = $jointData['spool_no1'];
}
}
if (isset($jointData['spool_no2'])) {
if (Schema::hasColumn('weld_logs', 'spool_number_2')) {
$weldLogData['spool_number_2'] = $jointData['spool_no2'];
} elseif (Schema::hasColumn('weld_logs', 'spool_number2')) {
$weldLogData['spool_number2'] = $jointData['spool_no2'];
}
}
// Handle pose numbers - use pose_number_1/2 if exists, otherwise use pose_no_1/2
if (isset($jointData['pose_no1'])) {
if (Schema::hasColumn('weld_logs', 'pose_number_1')) {
$weldLogData['pose_number_1'] = $jointData['pose_no1'];
} else {
$weldLogData['pose_no_1'] = $jointData['pose_no1'];
}
}
if (isset($jointData['pose_no2'])) {
if (Schema::hasColumn('weld_logs', 'pose_number_2')) {
$weldLogData['pose_number_2'] = $jointData['pose_no2'];
} else {
$weldLogData['pose_no_2'] = $jointData['pose_no2'];
}
}
// Handle members - use member_1/2 if exists, otherwise use member_no_1/2
if (isset($jointData['member1'])) {
if (Schema::hasColumn('weld_logs', 'member_1')) {
$weldLogData['member_1'] = $jointData['member1'];
} else {
$weldLogData['member_no_1'] = $jointData['member1'];
}
}
if (isset($jointData['member2'])) {
if (Schema::hasColumn('weld_logs', 'member_2')) {
$weldLogData['member_2'] = $jointData['member2'];
} else {
$weldLogData['member_no_2'] = $jointData['member2'];
}
}
// Handle material - use material_1/2 if exists, otherwise use material_no_1/2
if (isset($jointData['material'])) {
if (Schema::hasColumn('weld_logs', 'material_1')) {
$weldLogData['material_1'] = $jointData['material'];
$weldLogData['material_2'] = $jointData['material'];
} else {
$weldLogData['material_no_1'] = $jointData['material'];
$weldLogData['material_no_2'] = $jointData['material'];
}
}
// Handle diameter - use dia_1/2 if exists, otherwise use outside_diameter_1/2
if (isset($jointData['dia1'])) {
if (Schema::hasColumn('weld_logs', 'dia_1')) {
$weldLogData['dia_1'] = $jointData['dia1'];
} else {
$weldLogData['outside_diameter_1'] = $jointData['dia1'];
}
}
if (isset($jointData['dia2'])) {
if (Schema::hasColumn('weld_logs', 'dia_2')) {
$weldLogData['dia_2'] = $jointData['dia2'];
} else {
$weldLogData['outside_diameter_2'] = $jointData['dia2'];
}
}
// Handle NPS - nps_1/2 should exist in both cases
if (isset($jointData['nps1'])) {
$weldLogData['nps_1'] = $jointData['nps1'];
}
if (isset($jointData['nps2'])) {
$weldLogData['nps_2'] = $jointData['nps2'];
}
// Handle thickness - use thickness_1/2 if exists, otherwise use wall_thickness_1/2
if (isset($jointData['thickness1'])) {
if (Schema::hasColumn('weld_logs', 'thickness_1')) {
$weldLogData['thickness_1'] = $jointData['thickness1'];
} else {
$weldLogData['wall_thickness_1'] = $jointData['thickness1'];
}
}
if (isset($jointData['thickness2'])) {
if (Schema::hasColumn('weld_logs', 'thickness_2')) {
$weldLogData['thickness_2'] = $jointData['thickness2'];
} else {
$weldLogData['wall_thickness_2'] = $jointData['thickness2'];
}
}
// Use updateOrCreate to prevent duplicates
// Match by iso_number and joint_no (if provided)
// CRITICAL: Try multiple matching strategies to find existing records
$matchConditions = [
'iso_number' => $drawingNumber,
];
// CRITICAL: Try to find existing record using multiple strategies
// Strategy 1: Match by joint_no (e.g., "53A")
// Strategy 2: Match by draw_joint_no (e.g., "F53A") if joint_no doesn't match
// Strategy 3: Match by type_of_joint + joint_no combination
$existingRecord = null;
if (!empty($jointData['joint_no'])) {
$jointNo = trim($jointData['joint_no']);
// Strategy 1: Try exact match with joint_no
$existingRecord = WeldLog::where('iso_number', $drawingNumber)
->where('no_of_the_joint_as_per_as_built_survey', $jointNo)
->first();
// Strategy 2: If not found, try with draw_joint_no (Type + Joint No, e.g., "F53A")
if (!$existingRecord && !empty($jointData['draw_joint_no'])) {
$drawJointNo = trim($jointData['draw_joint_no']);
$existingRecord = WeldLog::where('iso_number', $drawingNumber)
->where('no_of_the_joint_as_per_as_built_survey', $drawJointNo)
->first();
}
// Strategy 3: If still not found, try with type_of_joint + joint_no
if (!$existingRecord && !empty($jointData['type_of_joint'])) {
$existingRecord = WeldLog::where('iso_number', $drawingNumber)
->where('type_of_joint', $jointData['type_of_joint'])
->where('no_of_the_joint_as_per_as_built_survey', $jointNo)
->first();
}
// Set match conditions based on what we found
if ($existingRecord) {
// If we found an existing record, match by its ID to ensure update
$matchConditions = [
'id' => $existingRecord->id
];
} else {
// If no existing record, match by iso_number + joint_no for new record
$matchConditions['no_of_the_joint_as_per_as_built_survey'] = $jointNo;
}
}
$weldLog = WeldLog::updateOrCreate(
$matchConditions,
$weldLogData
);
$savedJoints[] = $weldLog;
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Joint data saved successfully',
'data' => $savedJoints,
'count' => count($savedJoints)
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Failed to save joint data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Get joint data by shape_id or joint_no
* Phase 3.1: API Endpoints for Joint Data
*/
public function getJointData(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'drawing_number' => 'required|string',
'joint_no' => 'nullable|string',
'shape_id' => 'nullable|string',
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors()
], 422);
}
try {
$drawingNumber = $request->drawing_number;
$query = WeldLog::where('iso_number', $drawingNumber);
if ($request->has('joint_no') && !empty($request->joint_no)) {
$query->where('no_of_the_joint_as_per_as_built_survey', $request->joint_no);
}
// Dynamic column selection based on schema
$selectColumns = [
'id',
'no_of_the_joint_as_per_as_built_survey as joint_no',
'type_of_joint',
'type_of_welds',
'welding_date',
];
// Add spool numbers (check which columns exist)
if (Schema::hasColumn('weld_logs', 'spool_number_1')) {
$selectColumns[] = 'spool_number_1 as spool_no1';
} else {
$selectColumns[] = 'spool_number as spool_no1';
}
if (Schema::hasColumn('weld_logs', 'spool_number_2')) {
$selectColumns[] = 'spool_number_2 as spool_no2';
} elseif (Schema::hasColumn('weld_logs', 'spool_number2')) {
$selectColumns[] = 'spool_number2 as spool_no2';
}
// Add pose numbers
if (Schema::hasColumn('weld_logs', 'pose_number_1')) {
$selectColumns[] = 'pose_number_1 as pose_no1';
} else {
$selectColumns[] = 'pose_no_1 as pose_no1';
}
if (Schema::hasColumn('weld_logs', 'pose_number_2')) {
$selectColumns[] = 'pose_number_2 as pose_no2';
} else {
$selectColumns[] = 'pose_no_2 as pose_no2';
}
// Add members
if (Schema::hasColumn('weld_logs', 'member_1')) {
$selectColumns[] = 'member_1 as member1';
} else {
$selectColumns[] = 'member_no_1 as member1';
}
if (Schema::hasColumn('weld_logs', 'member_2')) {
$selectColumns[] = 'member_2 as member2';
} else {
$selectColumns[] = 'member_no_2 as member2';
}
// Add material
if (Schema::hasColumn('weld_logs', 'material_1')) {
$selectColumns[] = 'material_1 as material';
} else {
$selectColumns[] = 'material_no_1 as material';
}
// Add diameter
if (Schema::hasColumn('weld_logs', 'dia_1')) {
$selectColumns[] = 'dia_1 as dia1';
} else {
$selectColumns[] = 'outside_diameter_1 as dia1';
}
if (Schema::hasColumn('weld_logs', 'dia_2')) {
$selectColumns[] = 'dia_2 as dia2';
} else {
$selectColumns[] = 'outside_diameter_2 as dia2';
}
// Add NPS
$selectColumns[] = 'nps_1 as nps1';
$selectColumns[] = 'nps_2 as nps2';
// Add thickness
if (Schema::hasColumn('weld_logs', 'thickness_1')) {
$selectColumns[] = 'thickness_1 as thickness1';
} else {
$selectColumns[] = 'wall_thickness_1 as thickness1';
}
if (Schema::hasColumn('weld_logs', 'thickness_2')) {
$selectColumns[] = 'thickness_2 as thickness2';
} else {
$selectColumns[] = 'wall_thickness_2 as thickness2';
}
// Add welders
$selectColumns[] = 'welder_1';
$selectColumns[] = 'welder_2';
$joints = $query->get($selectColumns);
return response()->json([
'success' => true,
'data' => $joints
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to get joint data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Update welder assignment in Weldlog
* Phase 3.3: Weldlog Integration
*/
public function updateWelderAssignment(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'drawing_number' => 'required|string',
'joint_no' => 'required|string',
'welder_id1' => 'nullable|string',
'welder_id2' => 'nullable|string',
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors()
], 422);
}
try {
DB::beginTransaction();
$weldLog = WeldLog::where('iso_number', $request->drawing_number)
->where('no_of_the_joint_as_per_as_built_survey', $request->joint_no)
->first();
if (!$weldLog) {
return response()->json([
'success' => false,
'message' => 'Joint not found'
], 404);
}
// Update welder assignments
if ($request->has('welder_id1')) {
$weldLog->welder_1 = $request->welder_id1;
// Also update real_welder_1 if it exists
if (Schema::hasColumn('weld_logs', 'real_welder_1')) {
$weldLog->real_welder_1 = $request->welder_id1;
}
}
if ($request->has('welder_id2')) {
$weldLog->welder_2 = $request->welder_id2;
// Also update real_welder_2 if it exists
if (Schema::hasColumn('weld_logs', 'real_welder_2')) {
$weldLog->real_welder_2 = $request->welder_id2;
}
}
$weldLog->save();
DB::commit();
return response()->json([
'success' => true,
'message' => 'Welder assignment updated successfully',
'data' => $weldLog
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Failed to update welder assignment',
'error' => $e->getMessage()
], 500);
}
}
}
@@ -0,0 +1,401 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* @group Read Only Endpoints
*
* APIs for retrieving read-only data from various system modules.
*/
class ApiReadOnlyController extends Controller
{
public function __construct()
{
// Auth handled at route level with 'auth:sanctum,web' to support both token and session auth
}
private function renderLegacyScript($scriptPath, Request $request)
{
try {
// Merge request parameters into global $_GET for legacy script compatibility
if (!empty($request->all())) {
$_GET = array_merge($_GET, $request->all());
}
// Define path to the view
$fullPath = resource_path("views/admin-ajax/{$scriptPath}");
if (!file_exists($fullPath)) {
return response()->json(['error' => 'Module not found: ' . $scriptPath], 404);
}
// Capture output
ob_start();
include($fullPath);
$output = ob_get_clean();
// Attempt to decode JSON to ensure it's valid, otherwise return raw string if appropriate or wrap it
$json = json_decode($output);
if (json_last_error() === JSON_ERROR_NONE) {
return response()->json($json);
}
// If not JSON, it might be an error or HTML. For this API, we expect JSON.
// We'll return it as a 'raw_output' field if it's not valid JSON
return response()->json(['raw_output' => $output]);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
public function ndtRequest(Request $request)
{
return $this->renderLegacyScript('request-ndt.blade.php', $request);
}
public function ndtCalculation(Request $request)
{
return $this->renderLegacyScript('ndt-calculation.blade.php', $request);
}
/**
* Spool Area Release
*
* Get spool area release data with pagination and filtering.
*
* @queryParam take int Number of items to take (pagination). Default: 100. Example: 10
* @queryParam skip int Number of items to skip (pagination). Default: 0. Example: 0
* @queryParam filter_iso_number string Filter by ISO Number. Example: 10-BFW-RS1-0001-AH06HS-HC
* @queryParam filter_spool_number string Filter by Spool Number. Example: SPL-0004
* @queryParam filter_line_number string Filter by Line Number. Example: LN-001
* @queryParam filter_painting_cycle string Filter by Painting Cycle. Example: C1
* @queryParam filter_spool_status string Filter by Spool Status. Example: Completed
* @queryParam module string Specific module filter (e.g., 'spool-release-paint'). Example: spool-release-paint
*
* @response {
* "summary": {"Completed": 10, "Waiting": 5},
* "esummary": {"Completed": 2},
* "datagrid": [
* {
* "id": 1,
* "iso_number": "ISO-001",
* "spool_number": "SPL-001",
* "spool_status": "Completed",
* "project": "PROJECT A"
* }
* ]
* }
*/
public function spoolAreaRelease(Request $request)
{
$limit = $request->get('take', 100);
$offset = $request->get('skip', 0);
$isFiltered = $request->has('filter_iso_number') || $request->has('filter_spool_number');
$module = $request->get('module');
// Spool status order mapping
$statusOrder = [
'Waiting' => 1,
'Manufacturing' => 2,
'QC' => 3,
'Paint' => 4,
'Completed' => 5,
];
$caseSql = "CASE spool_status ";
foreach ($statusOrder as $status => $order) {
$caseSql .= "WHEN '$status' THEN $order ";
}
$caseSql .= "ELSE 99 END";
// Subquery for min status order
$minStatusSub = apply_welded_filter(db("weld_logs"))
->select(
'iso_number',
'spool_number',
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN {$caseSql} END) as min_status_order")
)
->groupBy('iso_number', 'spool_number');
// Base query
$query = apply_welded_filter(db("weld_logs"))
->leftJoinSub($minStatusSub, 'min_status_order_tbl', function($join) {
$join->on('weld_logs.iso_number', '=', 'min_status_order_tbl.iso_number')
->on('weld_logs.spool_number', '=', 'min_status_order_tbl.spool_number');
});
// Apply filters
if ($request->has('filter_iso_number')) {
$query->where("weld_logs.iso_number", "like", "%" . $request->get("filter_iso_number") . "%");
}
if ($request->has('filter_spool_number')) {
$query->where("weld_logs.spool_number", "like", "%" . $request->get("filter_spool_number") . "%");
}
if ($request->has('filter_line_number')) {
$query->where("weld_logs.line_number", "like", "%" . $request->get("filter_line_number") . "%");
}
if ($request->has('filter_painting_cycle')) {
$query->where("weld_logs.painting_cycle", "like", "%" . $request->get("filter_painting_cycle") . "%");
}
if ($request->has('filter_spool_status')) {
$query->where("weld_logs.spool_status", $request->get("filter_spool_status"));
}
if ($module === "spool-release-paint") {
$query->whereNotNull("painting_cycle");
}
// Field selection - always group by ISO and Spool to get single row per spool
$query->select(
'weld_logs.id',
'weld_logs.iso_number',
'weld_logs.spool_number',
\DB::raw("GROUP_CONCAT(DISTINCT no_of_the_joint_as_per_as_built_survey ORDER BY CAST(no_of_the_joint_as_per_as_built_survey AS UNSIGNED) SEPARATOR ', ') AS joint_no"),
\DB::raw("GROUP_CONCAT(DISTINCT type_of_joint SEPARATOR '/') as type_of_joint"),
'project',
'iso_rev',
'painting_cycle',
'spool_status',
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN clearance_no END) as clearance_no"),
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN clearance_date END) as min_clearance_date"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN clearance_date END) as clearance_date"),
\DB::raw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN COALESCE(clearance_date, 'NULL_VALUE') END) as clearance_date_count"),
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN ndt_release_date END) as min_ndt_release_date"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN ndt_release_date END) as ndt_release_date"),
\DB::raw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN COALESCE(ndt_release_date, 'NULL_VALUE') END) as ndt_release_date_count"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN register_paint_no END) as register_paint_no"),
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN register_date END) as min_register_date"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN register_date END) as register_date"),
\DB::raw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN COALESCE(register_date, 'NULL_VALUE') END) as register_date_count"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN ndt_release_no END) as ndt_release_no"),
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN paint_release_date END) as min_paint_release_date"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN paint_release_date END) as paint_release_date"),
\DB::raw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN COALESCE(paint_release_date, 'NULL_VALUE') END) as paint_release_date_count"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN paint_release_no END) as paint_release_no"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_zone END) as spool_zone"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_ndt_check END) as spool_ndt_check"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_paint_check END) as spool_paint_check"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_ndt_check_no END) as spool_ndt_check_no"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN ndt_release_check END) as ndt_release_check"),
\DB::raw("MIN(CASE WHEN type_of_joint = 'S' THEN spool_ndt_check_date END) as min_spool_ndt_check_date"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_ndt_check_date END) as spool_ndt_check_date"),
\DB::raw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN COALESCE(spool_ndt_check_date, 'NULL_VALUE') END) as spool_ndt_check_date_count"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN paint_release_check END) as paint_release_check"),
\DB::raw("MAX(CASE WHEN type_of_joint = 'S' THEN spool_remarks END) as spool_remarks"),
'min_status_order_tbl.min_status_order'
)
->groupBy("weld_logs.iso_number", "weld_logs.spool_number");
// Get total results before pagination for summaries
$allResults = $query->get();
// Summary calculations
$summary = [];
$esummary = [];
$users = usersArray();
$documentRevisions = [];
$revisionQuery = db("document_revisions")->select('drawing_no', 'revision_no')->get();
foreach ($revisionQuery as $revision) {
$documentRevisions[$revision->drawing_no] = $revision->revision_no;
}
$allSpoolStatusesQuery = db("weld_logs")
->select('spool_status', 'iso_number', 'spool_number')->where("type_of_joint", "S")->get();
$allSpoolStatuses = [];
foreach ($allSpoolStatusesQuery as $statusQuery) {
$allSpoolStatuses[$statusQuery->iso_number][$statusQuery->spool_number][] = $statusQuery->spool_status;
}
$orderToStatus = array_flip($statusOrder);
foreach ($allResults as $q) {
// Processing logic from Blade
if (strpos($q->type_of_joint, 'S') !== false && isset($q->min_status_order) && $q->min_status_order >= 1 && $q->min_status_order <= 5) {
$q->spool_status = $orderToStatus[$q->min_status_order];
}
$q->revision_no = $documentRevisions[$q->iso_number] ?? null;
$q->project = strtoupper($q->project);
if ($q->spool_ndt_check != "") {
foreach (['spool_ndt_check', 'spool_paint_check', 'ndt_release_check', 'paint_release_check'] as $field) {
if (isset($users[$q->$field])) {
$q->$field = "✒️" . $users[$q->$field]->name;
}
}
}
if (isset($q->spool_status)) {
$types = explode('/', $q->type_of_joint);
$hasS = in_array('S', $types) || in_array('F/S', $types);
$hasF = in_array('F', $types);
if ($hasS) {
if (!isset($summary[$q->spool_status])) $summary[$q->spool_status] = 0;
$summary[$q->spool_status]++;
}
if ($hasF) {
if (!isset($esummary[$q->spool_status])) $esummary[$q->spool_status] = 0;
$esummary[$q->spool_status]++;
}
}
$spoolStatuses = $allSpoolStatuses[$q->iso_number][$q->spool_number] ?? [];
$q->spool_status = getLowestSpoolStatus($spoolStatuses);
$q->latest_status = getLatestSpoolStatus($spoolStatuses);
$dateWarnings = [
'ndt_release_date_count' => ['min_ndt_release_date', 'ndt_release_date'],
'clearance_date_count' => ['min_clearance_date', 'clearance_date'],
'register_date_count' => ['min_register_date', 'register_date'],
'paint_release_date_count' => ['min_paint_release_date', 'paint_release_date'],
'spool_ndt_check_date_count' => ['min_spool_ndt_check_date', 'spool_ndt_check_date'],
];
foreach ($dateWarnings as $countField => $dateFields) {
if ($q->$countField > 1 || $q->{$dateFields[0]} != $q->{$dateFields[1]}) {
$q->spool_status .= "⚠️";
break;
}
}
}
// Apply skip/take to datagrid
$datagrid = array_slice($allResults->toArray(), $offset, $limit);
return response()->json([
'summary' => $summary,
'esummary' => $esummary,
'datagrid' => $datagrid,
], 200, [], JSON_UNESCAPED_UNICODE);
}
public function rfiDashboard(Request $request)
{
return $this->renderLegacyScript('rfi-dashboard.blade.php', $request);
}
public function welderQualificationStatus(Request $request)
{
return $this->renderLegacyScript('welder-qualification-status.php', $request);
}
public function punchList(Request $request)
{
return $this->renderLegacyScript('punch-summary/punch-list.blade.php', $request);
}
public function naksWelders(Request $request)
{
// "Naks (Welder, Consu, Equipment)" - Checking specific files usually in admin-ajax
// Assuming welder-qualification-status covers welders.
// Need to check for Consumables and Equipment if separate files exist.
return $this->renderLegacyScript('welder-qualification-status.php', $request);
}
public function testPackagePivot(Request $request) {
return $this->renderLegacyScript('test-package/pivot.blade.php', $request);
}
public function testPackageStatusPivot(Request $request) {
return $this->renderLegacyScript('test-package/status-pivot.blade.php', $request);
}
public function testPackageSummary(Request $request) {
return $this->renderLegacyScript('test-package/summary.blade.php', $request);
}
public function testPackageSummary2(Request $request) {
return $this->renderLegacyScript('test-package/summary2.blade.php', $request);
}
/**
* Spool Release Detail
*
* Get detailed spool release information with filtering options.
*
* @queryParam filter_iso_number string Filter by ISO Number. Example: 10-BFW-RS1-0001-AH06HS-HC
* @queryParam filter_spool_number string Filter by Spool Number. Example: SPL-0004
*/
public function spoolReleaseDetail(Request $request) {
return $this->renderLegacyScript('spool-release-detail.blade.php', $request);
}
/**
* Repair Table Statistics
*
* Get repair table statistics including defect types by welder, NPS, and material.
*
* @queryParam d1 string Start date (YYYY-MM-DD). Example: 2024-01-01
* @queryParam d2 string End date (YYYY-MM-DD). Example: 2024-03-31
*/
public function repairTable(Request $request)
{
return $this->renderLegacyScript('repair-table.blade.php', $request);
}
public function welderBasedRepairRatioDistribution(Request $request)
{
return $this->renderLegacyScript('repair-table/welder-based-repair-ratio-distribution.blade.php', $request);
}
public function defectTypeByWelder(Request $request)
{
return $this->renderLegacyScript('repair-table/defect-type-by-welder.blade.php', $request);
}
public function welderQualificationTable(Request $request)
{
return $this->renderLegacyScript('welder-qualification-table.blade.php', $request);
}
public function rfiTasks(Request $request)
{
return $this->renderLegacyScript('rfi-tasks.blade.php', $request);
}
public function rfiStatus(Request $request)
{
$settingValue = setting('rfi-status');
$data = json_decode($settingValue, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$data = $settingValue;
}
return response()->json([
'status' => 'success',
'data' => $data
]);
}
/**
* NDT Release List
*
* Get list of NDT ready spools grouped by ISO and spool number.
*/
public function ndtReleaseList(Request $request)
{
return $this->renderLegacyScript('ndt-list.blade.php', $request);
}
/**
* Paint Release List
*
* Get list of paint released spools with paint release details.
*/
public function paintReleaseList(Request $request)
{
return $this->renderLegacyScript('ndt-paint-list.blade.php', $request);
}
// Placeholder for other modules as they are identified
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
/**
* @group Authentication
*
* API endpoints for user authentication and token management.
*/
class AuthController extends Controller
{
/**
* Login (Get Token)
*
* Authenticates a user via email and password, returning a Bearer Token.
* @unauthenticated
*
* @bodyParam email string required User's email address. Example: admin@example.com
* @bodyParam password string required User's password. Example: password
*/
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
return response()->json([
'status' => 'error',
'message' => 'Invalid credentials'
], 401);
}
// Create Token (Device name is optional)
$token = $user->createToken($request->device_name ?? 'api-client')->plainTextToken;
return response()->json([
'status' => 'success',
'message' => 'Login successful',
'data' => [
'user' => $user,
'access_token' => $token,
'token_type' => 'Bearer',
]
]);
}
/**
* Get User Profile
*
* Returns the authenticated user's information.
*/
public function me(Request $request)
{
return response()->json([
'status' => 'success',
'data' => [
'user' => $request->user()
]
]);
}
/**
* Logout
*
* Revokes the user's current access token.
*/
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json([
'status' => 'success',
'message' => 'Token revoked'
]);
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AutocompleteTypeController extends Controller
{
/**
* Mobile API wrapper for AdminController@autocompleteType.
*
* Web uses "/{hash}/autocomplete_type/{type}". Mobile cannot know {hash},
* so we expose a safe, whitelisted endpoint under "/api/autocomplete-type/{type}".
*/
public function handle(Request $request, string $type)
{
if (!auth()->check() && !$request->user()) {
return response()->json(['message' => 'Unauthenticated.'], 401);
}
// Security: whitelist the only types needed by mobile screens.
$allowedTypes = [
'wps_no2',
'real_welder_1',
'real_welder_2',
];
if (!in_array($type, $allowedTypes, true)) {
return response()->json([
'status' => 'error',
'message' => 'Unsupported autocomplete type.',
], 400);
}
// Permission: treat this as weldmap read (options lookup).
if (!isAuth('weldmap', 'read')) {
return response()->json([
'status' => 'error',
'message' => 'Unauthorized action.',
'required_permission' => 'weldmap:read',
], 403);
}
// Provide $rowData to legacy autocomplete scripts.
$rowData = (object) $request->all();
// Support "*" mapping convention used in AdminController.
$type = str_replace('*', '/', $type);
$file = base_path("app/Http/Controllers/AutoCompleteType/{$type}.php");
if (!file_exists($file)) {
return response()->json([
'status' => 'error',
'message' => 'Autocomplete script not found.',
], 404);
}
$response = [];
include $file;
return response()->json($response);
}
}
@@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\JsonResponse;
class DashboardController extends Controller
{
/**
* Get dashboard summary data extracted from the cached blade file.
*
* @return JsonResponse
*/
public function summary(): JsonResponse
{
try {
// Get the cached dashboard content from the 'documents' disk
$cachedContent = Storage::disk('local')->get('cache/dashboard.blade.php');
if (!$cachedContent) {
return response()->json([
'status' => 'error',
'message' => 'Cached dashboard content not found.'
], 404);
}
// Regex pattern to extract icon, value, and label
// Matches block-content, img src, font-size-h3 (value), and font-size-sm (label)
$pattern = '/<div class="block-content[^>]*>.*?<img src="([^"]+)"[^>]*>.*?<div class="font-size-h3[^>]*>(.*?)<\/div>\s*<div class="font-size-sm[^>]*>(.*?)<\/div>/s';
preg_match_all($pattern, $cachedContent, $matches, PREG_SET_ORDER);
$data = [];
$baseUrl = url('/');
foreach ($matches as $match) {
$iconPath = $match[1];
$value = trim(strip_tags($match[2]));
$label = trim(strip_tags($match[3]));
// Clean up excessive whitespace and newlines
$value = preg_replace('/\s+/', ' ', $value);
$label = preg_replace('/\s+/', ' ', $label);
// Make the icon URL absolute
$iconUrl = str_starts_with($iconPath, 'http') ? $iconPath : rtrim($baseUrl, '/') . '/' . ltrim($iconPath, '/');
if ($label) {
$data[] = [
'label' => $label,
'value' => $value,
'icon' => $iconUrl
];
}
}
return response()->json([
'status' => 'success',
'data' => $data
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'An error occurred while extracting dashboard data: ' . $e->getMessage()
], 500);
}
}
}
@@ -0,0 +1,119 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\DocumentHistory;
use Illuminate\Http\Request;
/**
* @group Document Management
*
* APIs for managing documents and their history.
*/
class DocumentHistoryController extends Controller
{
/**
* Get Document History
*
* Retrieve the history of a document based on file name or table reference.
*
* @bodyParam table string The name of the table (e.g., document_revisions). Example: document_revisions
* @bodyParam column string The name of the column (e.g., pdf_spool_iso). Example: pdf_spool_iso
* @bodyParam row_id integer The ID of the row. Example: 3021
* @bodyParam file_name string The name of the file to search for. Example: MST-80-AH01PR-FE31.803.pdf
*
* @response {
* "id": 345,
* "user_name": "Linga Samiii",
* "action": "Archive",
* "file_name": "MST-80-AH01PR-FE31.803.pdf",
* "file_size": "474.4 KB",
* "moved_from": "001_Drawings",
* "moved_to": "Archive/001_Drawings",
* "action_date": "2025-07-19 16:11:00",
* "file_path": "storage/documents/...",
* "notes": "File replaced with new version..."
* }
*/
public function index(Request $request)
{
$table = $request->input('table');
$column = $request->input('column');
$row_id = $request->input('row_id');
$file_name = $request->input('file_name');
$query = DocumentHistory::query();
if ($file_name) {
// File name'e göre arama
$base_name = str_replace(".pdf", "", $file_name);
// Sadece tam dosya adlarını veya '_' ile ayrılmış arşiv adlarını (örn: _2025_08_30...) eşleştir
$query->where(function ($q) use ($base_name, $file_name) {
$q->where('file_name', $file_name)
->orWhere('file_name', 'LIKE', $base_name . '_%.pdf');
});
} elseif ($table && $row_id) {
// Table ve row_id'ye göre dosyayı bulup arama yap
try {
$row = \Illuminate\Support\Facades\DB::table($table)->find($row_id);
if ($row) {
$filenames = [];
if ($column && isset($row->$column)) {
// Belirli bir kolon varsa
$value = $row->$column;
if ($value && is_string($value)) {
$filenames[] = basename($value);
}
} else {
// Kolon belirtilmemişse, olası dosya kolonlarını tara
foreach ((array) $row as $key => $value) {
if ($value && is_string($value) && preg_match('/\.(pdf|xls|xlsx|doc|docx|jpg|png|jpeg)$/i', $value)) {
$filenames[] = basename($value);
}
}
}
if (!empty($filenames)) {
$query->whereIn('file_name', $filenames);
} else {
// Dosya bulunamadıysa boş dön
return response()->json([]);
}
} else {
return response()->json([]);
}
} catch (\Exception $e) {
// Tablo hatası vs.
return response()->json([]);
}
} else {
// Hiçbir geçerli filtre yoksa boş döndür
return response()->json([]);
}
$documentHistory = $query->orderBy('action_date', 'DESC')
->get()
->map(function ($item) {
return [
'id' => $item->id,
'user_name' => $item->user_name,
'action' => $item->action,
'file_name' => $item->file_name,
'file_size' => $item->file_size,
'moved_from' => $item->moved_from,
'moved_to' => $item->moved_to,
'action_date' => $item->action_date,
'file_path' => $item->file_path,
'notes' => $item->notes
];
});
return response()->json($documentHistory);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,434 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
/**
* @group Endpoints
*
* API endpoints for serving admin-ajax blade content.
* These endpoints provide access to backend blade files that return JSON or HTML responses.
*/
class EndpointController extends Controller
{
/**
* List Available Endpoints
*
* Returns a list of all available admin-ajax endpoints that can be accessed via the API.
* Each endpoint corresponds to a blade file in the admin-ajax directory.
*
* The response includes all endpoints with their names, URLs, HTTP methods, response types, and categories.
*
* @responseFile storage/app/scribe/endpoints-response.json
*/
public function index()
{
$endpoints = $this->getAvailableEndpoints();
return response()->json([
'status' => 'success',
'data' => [
'endpoints' => $endpoints,
'total' => count($endpoints)
]
]);
}
/**
* Execute Endpoint
*
* Executes the specified admin-ajax endpoint and returns the response.
* The response type is automatically detected:
* - If the output is valid JSON, returns JSON response
* - Otherwise, returns HTML response
*
* @urlParam endpoint string required The endpoint name (e.g., users, ndt-calculation). Example: users
*
* @queryParam _format string Optional. Force response format: 'json' or 'html'. Example: json
*
* @response 200 scenario="JSON Response" {
* "status": "success",
* "data": [{"id": 1, "name": "John Doe"}],
* "format": "json"
* }
*
* @response 200 scenario="HTML Response" {
* "status": "success",
* "html": "<div>Content here</div>",
* "format": "html"
* }
*
* @response 404 {
* "status": "error",
* "message": "Endpoint not found"
* }
*/
public function handle(Request $request, string $endpoint)
{
// Sanitize endpoint name
$endpoint = $this->sanitizeEndpoint($endpoint);
// Check if endpoint exists
if (!$this->endpointExists($endpoint)) {
return response()->json([
'status' => 'error',
'message' => 'Endpoint not found',
'endpoint' => $endpoint
], 404);
}
// SECURITY: Only allow readonly endpoints via API
if (!$this->isReadonlyEndpoint($endpoint)) {
return response()->json([
'status' => 'error',
'message' => 'This endpoint is not available via API. Only readonly endpoints are accessible.',
'endpoint' => $endpoint
], 403);
}
try {
// Render the blade view and capture output
$output = $this->renderEndpoint($endpoint, $request);
// Detect response format
$format = $request->input('_format');
if ($format === 'html') {
return $this->htmlResponse($output);
}
if ($format === 'json' || $this->isJson($output)) {
return $this->jsonResponse($output);
}
return $this->htmlResponse($output);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage(),
'endpoint' => $endpoint
], 500);
}
}
/**
* Get Endpoint Info
*
* Returns detailed information about a specific endpoint.
*
* @urlParam endpoint string required The endpoint name. Example: users
*
* @response 200 {
* "status": "success",
* "data": {
* "name": "users",
* "url": "/api/endpoints/users",
* "methods": ["GET", "POST"],
* "exists": true,
* "api_accessible": true,
* "path": "resources/views/admin-ajax/users.blade.php"
* }
* }
*/
public function info(Request $request, string $endpoint)
{
$endpoint = $this->sanitizeEndpoint($endpoint);
$exists = $this->endpointExists($endpoint);
$isReadonly = $exists ? $this->isReadonlyEndpoint($endpoint) : false;
return response()->json([
'status' => 'success',
'data' => [
'name' => $endpoint,
'url' => "/api/endpoints/{$endpoint}",
'methods' => ['GET', 'POST'],
'exists' => $exists,
'api_accessible' => $isReadonly,
'path' => $exists ? "resources/views/admin-ajax/{$endpoint}.blade.php" : null,
'note' => !$isReadonly && $exists ? 'This endpoint is not marked as @api-readonly and cannot be accessed via API' : null
]
]);
}
/**
* Sanitize endpoint name to prevent directory traversal
*/
protected function sanitizeEndpoint(string $endpoint): string
{
// Remove any path traversal attempts
$endpoint = str_replace(['..', '/', '\\'], '', $endpoint);
// Convert to lowercase and replace spaces with dashes
$endpoint = Str::slug($endpoint);
return $endpoint;
}
/**
* Check if endpoint blade file exists
*/
protected function endpointExists(string $endpoint): bool
{
return View::exists("admin-ajax.{$endpoint}");
}
/**
* Check if endpoint is marked as readonly (safe for API access)
*
* Endpoints must have the annotation {{-- @api-readonly --}} at the beginning
* to be accessible via the API. This prevents write/delete operations from
* being exposed externally.
*/
protected function isReadonlyEndpoint(string $endpoint): bool
{
$path = resource_path("views/admin-ajax/{$endpoint}.blade.php");
if (!File::exists($path)) {
return false;
}
$content = File::get($path);
// Check for @api-readonly annotation
// Supports: {{-- @api-readonly --}} or <?php // @api-readonly
return Str::contains($content, '@api-readonly');
}
/**
* Get the file path for an endpoint
*/
protected function getEndpointPath(string $endpoint): ?string
{
// Check main directory
$path = resource_path("views/admin-ajax/{$endpoint}.blade.php");
if (File::exists($path)) {
return $path;
}
// Check subdirectories
$parts = explode('/', $endpoint);
if (count($parts) > 1) {
$subPath = resource_path("views/admin-ajax/" . implode('/', $parts) . ".blade.php");
if (File::exists($subPath)) {
return $subPath;
}
}
return null;
}
/**
* Render the endpoint blade file
*/
protected function renderEndpoint(string $endpoint, Request $request): string
{
// Start output buffering to capture any echo/print statements
ob_start();
try {
// Render the view with request data
$view = View::make("admin-ajax.{$endpoint}", [
'request' => $request
]);
// Get rendered content
$rendered = $view->render();
// Get any buffered output (from echo statements in blade)
$buffered = ob_get_clean();
// Return buffered output if exists, otherwise rendered content
return !empty($buffered) ? $buffered : $rendered;
} catch (\Exception $e) {
ob_end_clean();
throw $e;
}
}
/**
* Check if string is valid JSON
*/
protected function isJson(string $string): bool
{
if (empty($string)) {
return false;
}
$string = trim($string);
// Quick check for JSON structure
if (!in_array($string[0], ['{', '['])) {
return false;
}
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
/**
* Return JSON response
*/
protected function jsonResponse(string $output)
{
$data = json_decode($output, true);
// If already has status wrapper, return as-is
if (is_array($data) && isset($data['status'])) {
return response()->json($data);
}
return response()->json([
'status' => 'success',
'data' => $data,
'format' => 'json'
]);
}
/**
* Return HTML response
*/
protected function htmlResponse(string $output)
{
return response()->json([
'status' => 'success',
'html' => $output,
'format' => 'html'
]);
}
/**
* Get all available endpoints from admin-ajax directory
* Only returns endpoints marked with @api-readonly annotation
*/
public function getAvailableEndpoints(): array
{
$endpoints = [];
$path = resource_path('views/admin-ajax');
if (!File::isDirectory($path)) {
return $endpoints;
}
// Scan all blade files recursively
$this->scanEndpointsRecursive($path, '', $endpoints);
// Sort alphabetically
usort($endpoints, fn($a, $b) => strcmp($a['name'], $b['name']));
return $endpoints;
}
/**
* Recursively scan directory for readonly endpoints
*/
protected function scanEndpointsRecursive(string $basePath, string $prefix, array &$endpoints): void
{
$files = File::files($basePath);
foreach ($files as $file) {
$filename = $file->getFilename();
// Only process .blade.php files
if (!Str::endsWith($filename, '.blade.php')) {
continue;
}
// Extract endpoint name
$name = str_replace('.blade.php', '', $filename);
$fullName = $prefix ? "{$prefix}/{$name}" : $name;
// Check file content for @api-readonly annotation
$content = File::get($file->getPathname());
// SECURITY: Only include endpoints marked as readonly
if (!Str::contains($content, '@api-readonly')) {
continue;
}
$endpoints[] = [
'name' => $fullName,
'url' => "/api/endpoints/{$fullName}",
'methods' => ['GET', 'POST']
];
}
// Scan subdirectories
$directories = File::directories($basePath);
foreach ($directories as $dir) {
$dirName = basename($dir);
$newPrefix = $prefix ? "{$prefix}/{$dirName}" : $dirName;
$this->scanEndpointsRecursive($dir, $newPrefix, $endpoints);
}
}
/**
* Get endpoints grouped by category (for documentation)
*/
public function getEndpointsByCategory(): array
{
$endpoints = $this->getAvailableEndpoints();
$categories = [];
foreach ($endpoints as $endpoint) {
$name = $endpoint['name'];
// Determine category based on prefix
$category = $this->categorizeEndpoint($name);
if (!isset($categories[$category])) {
$categories[$category] = [];
}
$categories[$category][] = $endpoint;
}
ksort($categories);
return $categories;
}
/**
* Categorize endpoint based on name prefix
*/
protected function categorizeEndpoint(string $name): string
{
$prefixes = [
'ndt-' => 'NDT Operations',
'welder-' => 'Welder Management',
'weld' => 'Welding',
'register-' => 'Register Creator',
'test-package' => 'Test Packages',
'tp-' => 'Test Packages',
'document-' => 'Document Management',
'report-' => 'Reports',
'summary' => 'Summaries',
'excel-' => 'Excel Operations',
'pdf' => 'PDF Operations',
'user' => 'User Management',
'permission' => 'Permissions',
'content-' => 'Content Management',
'spool-' => 'Spool Operations',
'material-' => 'Materials',
'paint-' => 'Paint Operations',
'repair-' => 'Repair Operations',
'rfi-' => 'RFI Operations',
'punch-' => 'Punch Lists',
'mto' => 'MTO Operations',
];
foreach ($prefixes as $prefix => $category) {
if (Str::startsWith($name, $prefix)) {
return $category;
}
}
return 'General';
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Types;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
class MobileTypeController extends Controller
{
public function index()
{
$types = Types::where('is_mobile', true)->where('enabled', true)->orderBy("s", "ASC")->get();
$result = [];
foreach ($types as $type) {
if (!isAuth($type->slug, 'read')) {
continue;
}
$bladePath = resource_path("views/admin/type/{$type->slug}.blade.php");
$tableName = null;
if (File::exists($bladePath)) {
$content = File::get($bladePath);
if (preg_match('/\$tableName\s*=\s*["\']([^"\']+)["\']\s*;/', $content, $matches)) {
$tableName = $matches[1];
}
}
// Get saved mobile columns from settings
$mobileColumnsJson = setting("type_{$type->id}_mobile_columns", false, '');
$columns = [];
if ($mobileColumnsJson) {
$decoded = json_decode($mobileColumnsJson, true);
if (is_array($decoded)) {
$columns = $decoded;
}
}
$result[] = [
'title' => $type->title,
'slug' => $type->slug,
'table_name' => $tableName,
'order' => $type->s,
'parent' => $type->kid,
'icon' => asset("assets/icons/{$type->icon}.png"),
'columns' => $columns,
];
}
return response()->json($result);
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Services\NaksSyncService;
use Illuminate\Support\Facades\Log;
class NaksSyncController extends Controller
{
/**
* Handle upcoming sync trigger from another project.
* This endpoint is called by other projects when they update a record.
*/
public function triggerSync(Request $request, NaksSyncService $syncService)
{
$validated = $request->validate([
'module' => 'required|string',
'source_project' => 'required|string'
]);
$module = $validated['module'];
$source = $validated['source_project'];
Log::info("Received NAKS Sync Trigger from [$source] for module [$module]");
// Prevent infinite loops if logic isn't careful, but NaksSyncService checks timestamps.
// We will queue the actual sync to perform it in background
// We use 'dispatch' helper or creating a Job closure
// We can run the sync service directly here if we want immediate feedback, but it might take time.
// Best practice is to queue it.
// Let's create a closure job or reuse existing mechanism.
// Since we don't have a dedicated "RunSyncJob", we'll do it inline with fast execution or
// dispatch a closure.
dispatch(function () use ($syncService, $module) {
try {
// Sync specific module from all projects (or finding the specific source, but the service supports 'all' or filtered)
// The service reads from 'api/project-app-urls' list.
// We should sync 'all' projects to be safe, or we could optimize later.
$syncService->sync($module);
} catch (\Exception $e) {
Log::error("Error in Queued NAKS Sync: " . $e->getMessage());
}
});
return response()->json([
'message' => 'Sync triggered successfully',
'status' => 'queued'
]);
}
}
@@ -0,0 +1,914 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\DocumentRevision;
use App\Models\WeldLog;
use App\Models\WelderTest;
use App\Models\MaterialGroupMap;
use App\Models\IncomingControl;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class PdfEditorLookupController extends Controller
{
private const DRAWING_TYPES = [
'engineering' => [
'label' => 'Engineering Drawing',
'column' => 'pdf_engineering',
'folder' => 'storage/documents/001_Drawings/001_Engineering',
],
'spool' => [
'label' => 'Spool Drawing',
'column' => 'pdf_spool_iso',
'folder' => 'storage/documents/001_Drawings/002_Iso',
],
'as_build' => [
'label' => 'As-Build Drawing',
'column' => 'pdf_as_build',
'folder' => 'storage/documents/001_Drawings/003_As_Build(ID)',
],
];
public function drawingTypes(): JsonResponse
{
$types = collect(self::DRAWING_TYPES)->map(function (array $config, string $key) {
return [
'value' => $key,
'label' => $config['label'],
];
})->values();
return response()->json([
'success' => true,
'data' => $types,
]);
}
public function drawings(Request $request): JsonResponse
{
$type = $request->query('type', 'engineering');
$config = self::DRAWING_TYPES[$type] ?? null;
if ($config === null) {
return response()->json([
'success' => false,
'message' => 'Unsupported drawing type',
], 422);
}
$column = $config['column'];
$query = DocumentRevision::query()
->whereNotNull('drawing_no')
->where('drawing_no', '!=', '');
if ($column) {
$query->whereNotNull($column)->where($column, '!=', '');
}
$drawings = $query
->select('drawing_no')
->distinct()
->orderBy('drawing_no')
->get()
->map(fn ($row) => [
'value' => $row->drawing_no,
'label' => $row->drawing_no,
]);
if ($drawings->isEmpty() && isset($config['folder'])) {
$drawings = collect($this->scanDrawingFolder($config['folder']))
->map(fn (string $name) => [
'value' => $name,
'label' => $name,
]);
}
return response()->json([
'success' => true,
'data' => $drawings,
]);
}
public function files(Request $request): JsonResponse
{
$type = $request->query('type');
$drawing = $request->query('drawing');
if (!$type || !$drawing) {
return response()->json([
'success' => false,
'message' => 'Drawing type and drawing number are required',
], 422);
}
$config = self::DRAWING_TYPES[$type] ?? null;
if ($config === null) {
return response()->json([
'success' => false,
'message' => 'Unsupported drawing type',
], 422);
}
$column = $config['column'];
$files = collect();
if ($column) {
$files = $this->buildFileListFromColumn($drawing, $column);
}
if ($files->isEmpty() && $type === 'as_build') {
$fallbackColumns = ['working_documentation', 'axonometry'];
foreach ($fallbackColumns as $fallbackColumn) {
$files = $this->buildFileListFromColumn($drawing, $fallbackColumn);
if ($files->isNotEmpty()) {
break;
}
}
}
if ($files->isEmpty() && isset($config['folder'])) {
$publicFolder = public_path($config['folder']);
if (is_dir($publicFolder)) {
$pattern = $publicFolder . '/' . $drawing . '*.pdf';
$matches = glob($pattern) ?: [];
$files = collect($matches)->map(function (string $filePath) {
$relativePath = $this->relativePublicPath($filePath);
$filename = basename($filePath);
return [
'id' => md5($filePath),
'revision' => null,
'label' => $filename,
'url' => $relativePath ? url($relativePath) : null,
'path' => $relativePath,
'is_latest' => false,
'exists' => true,
'updated_at' => Carbon::createFromTimestamp(filemtime($filePath))->toDateTimeString(),
];
})->sortByDesc('updated_at')->values();
if ($files->isNotEmpty()) {
$files = $files->map(function (array $item, int $index) {
$item['is_latest'] = $index === 0;
return $item;
});
}
}
}
return response()->json([
'success' => true,
'data' => $files->values(),
]);
}
public function updateDocumentStage(Request $request): JsonResponse
{
$validated = $request->validate([
'drawing' => ['required', 'string'],
'stage' => ['required', Rule::in(['pdf_spool_iso', 'pdf_as_build'])],
'pdf_path' => ['required', 'string'],
'revision_id' => ['nullable', 'integer'],
]);
$pdfPath = trim($validated['pdf_path']);
if ($pdfPath === '') {
return response()->json([
'success' => false,
'message' => 'PDF path cannot be empty.',
], 422);
}
$query = DocumentRevision::query()
->where('drawing_no', $validated['drawing']);
if (!empty($validated['revision_id'])) {
$query->where('id', $validated['revision_id']);
}
$updatedRows = $query->update([
$validated['stage'] => $pdfPath,
]);
if ($updatedRows === 0) {
return response()->json([
'success' => false,
'message' => 'No matching document revisions found for update.',
], 404);
}
return response()->json([
'success' => true,
'updated_rows' => $updatedRows,
'stage' => $validated['stage'],
'pdf_path' => $pdfPath,
]);
}
private function buildFileListFromColumn(string $drawing, string $column): \Illuminate\Support\Collection
{
$records = DocumentRevision::query()
->where('drawing_no', $drawing)
->whereNotNull($column)
->where($column, '!=', '')
->orderByDesc('publish_date')
->orderByDesc('updated_at')
->get(['id', 'drawing_no', 'revision_no', 'publish_date', 'updated_at', $column]);
if ($records->isEmpty()) {
return collect();
}
$latestId = optional($records->first())->id;
return $records->map(function (DocumentRevision $revision) use ($latestId, $column) {
$path = $revision->{$column};
if (!$path) {
return null;
}
$fullPath = public_path($path);
$exists = $fullPath ? file_exists($fullPath) : false;
$timestamp = $revision->publish_date
? Carbon::parse($revision->publish_date)->startOfDay()
: ($revision->updated_at instanceof Carbon ? $revision->updated_at : Carbon::parse($revision->updated_at));
return [
'id' => $revision->id,
'revision' => $revision->revision_no,
'label' => $this->formatLabel($revision->revision_no, $path),
'url' => $path ? url($path) : null,
'path' => $path,
'is_latest' => $revision->id === $latestId,
'exists' => $exists,
'updated_at' => $timestamp?->toDateTimeString(),
];
})->filter()->values();
}
public function weldData(Request $request): JsonResponse
{
$drawing = trim((string) $request->query('drawing', ''));
if ($drawing === '') {
return response()->json([
'success' => false,
'message' => 'Drawing number is required',
], 422);
}
$logs = WeldLog::query()
->where('iso_number', $drawing)
->select([
'spool_number',
'spool_number2',
'no_of_the_joint_as_per_as_built_survey',
'type_of_joint',
'welder_1',
'welder_2',
'real_welder_1',
'real_welder_2',
])
->orderBy('spool_number')
->get();
if ($logs->isEmpty()) {
return response()->json([
'success' => true,
'data' => [
'assignments' => [],
],
]);
}
$assignmentMap = [];
foreach ($logs as $log) {
$spool = $this->sanitizeValue($log->spool_number) ?? $this->sanitizeValue($log->spool_number2);
$joint = $this->sanitizeValue($log->no_of_the_joint_as_per_as_built_survey);
$jointType = $this->sanitizeValue($log->type_of_joint);
if (!$spool && !$joint) {
continue;
}
$key = $spool . '|' . $joint;
if (!isset($assignmentMap[$key])) {
$assignmentMap[$key] = [
'spool' => $spool,
'joint' => $joint,
'joint_type' => $jointType ? strtoupper($jointType) : null,
'welders' => [],
];
} elseif ($jointType && empty($assignmentMap[$key]['joint_type'])) {
$assignmentMap[$key]['joint_type'] = strtoupper($jointType);
}
$welders = [
$this->sanitizeValue($log->real_welder_1),
$this->sanitizeValue($log->real_welder_2),
$this->sanitizeValue($log->welder_1),
$this->sanitizeValue($log->welder_2),
];
foreach ($welders as $welder) {
if ($welder) {
$assignmentMap[$key]['welders'][$welder] = $welder;
}
}
}
$assignments = collect($assignmentMap)
->map(function (array $assignment) {
$assignment['welders'] = array_values($assignment['welders']);
return $assignment;
})
->values();
return response()->json([
'success' => true,
'data' => [
'assignments' => $assignments,
],
]);
}
private function formatLabel(?string $revision, ?string $path): string
{
$filename = $path ? basename($path) : null;
if ($revision && $filename) {
return sprintf('Rev %s — %s', $revision, $filename);
}
if ($revision) {
return sprintf('Rev %s', $revision);
}
return $filename ?? 'Unknown PDF';
}
private function relativePublicPath(string $absolutePath): ?string
{
$publicPath = public_path();
if (Str::startsWith($absolutePath, $publicPath)) {
return ltrim(str_replace($publicPath, '', $absolutePath), '/');
}
return null;
}
private function sanitizeValue($value): ?string
{
if ($value === null) {
return null;
}
$trimmed = trim((string) $value);
return $trimmed === '' ? null : $trimmed;
}
/**
* @return string[]
*/
private function scanDrawingFolder(string $relativeFolder): array
{
$basePath = public_path($relativeFolder);
if (!is_dir($basePath)) {
return [];
}
$files = glob($basePath . DIRECTORY_SEPARATOR . '*.pdf') ?: [];
if (empty($files)) {
return [];
}
$names = collect($files)
->map(fn (string $path) => pathinfo($path, PATHINFO_FILENAME))
->filter()
->unique()
->sort()
->values()
->all();
return $names;
}
/**
* Get IC Log members for a drawing
* Note: Currently drawing_number field is empty in database, so we return all members
* TODO: When drawing_number is populated, filter by drawing_number
*/
public function getICLogMembers(Request $request): JsonResponse
{
$request->validate(['drawing' => 'required|string']);
$drawingNumber = $request->drawing;
// First try to get by drawing_number (when it's populated)
$members = IncomingControl::where('drawing_number', $drawingNumber)
->whereNotNull('description_ru')
->where('description_ru', '!=', '')
->distinct('description_ru')
->pluck('description_ru')
->filter()
->values()
->toArray();
// If no results found by drawing_number, return all members
// (This is a fallback since drawing_number field is currently empty)
if (empty($members)) {
$members = IncomingControl::whereNotNull('description_ru')
->where('description_ru', '!=', '')
->distinct('description_ru')
->pluck('description_ru')
->filter()
->values()
->toArray();
}
return response()->json([
'success' => true,
'data' => $members,
'filtered_by_drawing' => !empty($members) && IncomingControl::where('drawing_number', $drawingNumber)->exists()
]);
}
/**
* Get IC Log member details
*/
public function getICLogMemberDetails(Request $request): JsonResponse
{
$request->validate(['member_description' => 'required|string']);
$memberDescription = $request->member_description;
$details = IncomingControl::where('description_ru', $memberDescription)
->select(
'material',
'odmm_1 as diameter', // Outer diameter in mm
'dn_1 as nps', // Nominal pipe size
'thicknessmm_1 as thickness' // Thickness in mm
)
->first(); // Get the first matching record
return response()->json(['success' => true, 'data' => $details]);
}
/**
* Check existing weldmap data for a drawing
*/
public function checkExistingWeldmapData(Request $request): JsonResponse
{
$request->validate(['drawing' => 'required|string']);
$drawingNumber = $request->drawing;
$existingJoints = WeldLog::where('iso_number', $drawingNumber)
->select(
'no_of_the_joint_as_per_as_built_survey as joint_no',
'joint_type',
'welder_1',
'welder_2',
'spool_number_1 as spool_no_1',
'spool_number_2 as spool_no_2',
'pose_number_1 as pose_no_1',
'pose_number_2 as pose_no_2',
'member_1',
'member_2',
'material_1',
'material_2',
'dia_1',
'dia_2',
'nps_1',
'nps_2',
'thickness_1',
'thickness_2',
'welding_date'
)
->get();
return response()->json([
'success' => true,
'has_data' => $existingJoints->isNotEmpty(),
'joints' => $existingJoints
]);
}
/**
* Get qualified welders based on material, welding method, and expiry dates
* Phase 3.1: API Endpoints for Joint Data
* Phase 6: Welder Qualification Filtering
*/
public function getQualifiedWelders(Request $request): JsonResponse
{
$request->validate([
'drawing_number' => 'required|string',
'material' => 'nullable|string',
'welding_method' => 'nullable|string',
'joint_no' => 'nullable|string',
'skip_qualification' => 'nullable|boolean', // Skip qualification check if true
]);
try {
$drawingNumber = $request->drawing_number;
$jointNo = $request->joint_no;
$skipQualification = $request->boolean('skip_qualification', false);
Log::debug('Phase 6: getQualifiedWelders called', [
'drawing_number' => $drawingNumber,
'joint_no' => $jointNo,
'skip_qualification' => $skipQualification
]);
// If skip_qualification is true, return all welders from weld_logs (legacy behavior)
if ($skipQualification) {
return $this->getWeldersFromWeldLogs($drawingNumber, $jointNo);
}
// Get joint data from weld_logs to determine qualification criteria
$jointData = null;
if (!empty($jointNo)) {
$jointData = WeldLog::where('iso_number', $drawingNumber)
->where('no_of_the_joint_as_per_as_built_survey', $jointNo)
->first();
}
// If no joint data found, try to get any record for this drawing
if (!$jointData) {
$jointData = WeldLog::where('iso_number', $drawingNumber)->first();
}
// If still no data, return empty list with message
if (!$jointData) {
Log::debug('Phase 6: No joint data found for qualification', [
'drawing_number' => $drawingNumber,
'joint_no' => $jointNo
]);
return response()->json([
'success' => true,
'data' => [],
'count' => 0,
'message' => 'No joint data found for qualification filtering'
]);
}
// Extract qualification criteria from joint data
$criteria = $this->extractQualificationCriteria($jointData);
Log::debug('Phase 6: Qualification criteria extracted', $criteria);
// Get qualified welders from WelderTest table
$qualifiedWelders = $this->filterWeldersByQualification($criteria, $drawingNumber);
return response()->json([
'success' => true,
'data' => $qualifiedWelders,
'count' => count($qualifiedWelders),
'criteria' => $criteria,
'message' => 'Welders filtered by qualification'
]);
} catch (\Exception $e) {
Log::error('Phase 6: getQualifiedWelders error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'success' => false,
'message' => 'Failed to get qualified welders',
'error' => $e->getMessage()
], 500);
}
}
/**
* Get welders from weld_logs without qualification filtering (legacy behavior)
*/
private function getWeldersFromWeldLogs(string $drawingNumber, ?string $jointNo): JsonResponse
{
$query = WeldLog::where('iso_number', $drawingNumber)
->whereNotNull('welder_1')
->where('welder_1', '!=', '');
if (!empty($jointNo)) {
$query->where('no_of_the_joint_as_per_as_built_survey', $jointNo);
}
$welders1 = $query->pluck('welder_1')->unique()->filter()->values();
$query2 = WeldLog::where('iso_number', $drawingNumber)
->whereNotNull('welder_2')
->where('welder_2', '!=', '');
if (!empty($jointNo)) {
$query2->where('no_of_the_joint_as_per_as_built_survey', $jointNo);
}
$welders2 = $query2->pluck('welder_2')->unique()->filter()->values();
$allWelders = $welders1->merge($welders2)->unique()->sort()->values();
return response()->json([
'success' => true,
'data' => $allWelders->toArray(),
'count' => $allWelders->count(),
'message' => 'Welders from weld_logs (no qualification filtering)'
]);
}
/**
* Extract qualification criteria from joint data
*/
private function extractQualificationCriteria(WeldLog $jointData): array
{
return [
'material_group_1' => $jointData->ru_material_group_1 ?? null,
'material_group_2' => $jointData->ru_material_group_2 ?? null,
'welding_method' => $jointData->welding_method ?? null,
'welding_date' => $jointData->welding_date ?? now()->toDateString(),
'outside_diameter' => $jointData->outside_diameter_1 ?? null,
'wall_thickness' => $jointData->wall_thickness_1 ?? null,
'iso_number' => $jointData->iso_number ?? null,
];
}
/**
* Filter welders by qualification criteria using WelderTest table
* Based on existing welder-assignment/welder_1.php logic
*/
private function filterWeldersByQualification(array $criteria, string $drawingNumber): array
{
$logKey = 'PHASE6_WELDER_FILTER_' . uniqid();
Log::debug("[$logKey] Starting welder qualification filtering", $criteria);
$qualifiedWelders = [];
$today = Carbon::now();
$welderQualificationExpiredDays = (int) setting("WelderQualitificationStatusExpiredDate", 180);
// Build material combinations for qualification check
$qualifiedCombinations = $this->buildMaterialCombinations(
$criteria['material_group_1'],
$criteria['material_group_2']
);
Log::debug("[$logKey] Material combinations", [
'combinations' => $qualifiedCombinations
]);
// If no material info, skip qualification and return welders from weld_logs
if (empty($qualifiedCombinations)) {
Log::debug("[$logKey] No material combinations, falling back to weld_logs welders");
return $this->getWeldersFromWeldLogsArray($drawingNumber);
}
// Build base query with WelderTest
$query = WelderTest::leftJoin('naks_welders', function($join) {
$join->on('naks_welders.naks_certificate_no', '=', 'welder_tests.naks_no');
});
// Filter by material combinations
$query->where(function ($q) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$q->orWhere(function($subQ) use ($combo) {
$subQ->where("material_group_1", "like", "%{$combo['mat1']}%")
->where("material_group_2", "like", "%{$combo['mat2']}%");
});
}
});
// Filter by diameter if available
if (!empty($criteria['outside_diameter']) && is_numeric($criteria['outside_diameter'])) {
$diameter = (float) $criteria['outside_diameter'];
$query->where("dia_min", "<=", $diameter)
->where(function($q) use ($diameter) {
$q->where("dia_max", ">=", $diameter)
->orWhereNull("dia_max")
->orWhere("dia_max", 0);
});
}
// Filter by thickness if available
if (!empty($criteria['wall_thickness']) && is_numeric($criteria['wall_thickness'])) {
$thickness = (float) $criteria['wall_thickness'];
$query->where("thk_min", "<=", $thickness)
->where(function($q) use ($thickness) {
$q->where("thk_max", ">=", $thickness)
->orWhereNull("thk_max")
->orWhere("thk_max", 0);
});
}
// Filter by welding method if available
if (!empty($criteria['welding_method'])) {
$weldingMethod = $criteria['welding_method'];
$weldingMethodParts = explode(",", $weldingMethod);
$reverseWeldingMethod = implode(",", array_reverse($weldingMethodParts));
$query->where(function($q) use ($weldingMethod, $reverseWeldingMethod) {
$q->where("welding_method", $weldingMethod)
->orWhere("welding_method", $reverseWeldingMethod);
});
}
// Filter by approval date (must be approved before welding date)
if (!empty($criteria['welding_date'])) {
$query->whereDate("approval_date", "<=", $criteria['welding_date']);
}
// Select and group results
$results = $query->select(
'welder_tests.naks_id',
'welder_tests.naks_validity',
DB::raw('MIN(naks_welders.period_of_validity) as period_of_validity')
)
->groupBy('welder_tests.naks_id', 'welder_tests.naks_validity')
->get();
Log::debug("[$logKey] Query results", [
'count' => $results->count()
]);
// Process results and add status indicators
foreach ($results as $welder) {
$welderDisplay = $welder->naks_id;
// Check NAKS validity
if (!empty($welder->naks_validity)) {
try {
$naksValidity = Carbon::parse($welder->naks_validity);
$remainingDays = $today->diffInDays($naksValidity, false);
if ($remainingDays < 10 && $remainingDays >= 0) {
$welderDisplay = "" . $welderDisplay; // Expiring soon
} elseif ($remainingDays < 0) {
$welderDisplay = "" . $welderDisplay; // Expired
continue; // Skip expired welders
}
} catch (\Exception $e) {
// Invalid date format, skip validity check
}
}
// Check if welder is already assigned to this ISO
$isAssigned = WeldLog::where('iso_number', $drawingNumber)
->where(function($q) use ($welder) {
$q->where('welder_1', 'like', "%{$welder->naks_id}%")
->orWhere('real_welder_1', 'like', "%{$welder->naks_id}%");
})
->exists();
if ($isAssigned) {
$welderDisplay = "🟡" . $welderDisplay; // Already assigned
}
// Check period of validity
if (!empty($welder->period_of_validity) && !empty($criteria['welding_date'])) {
try {
$weldingDate = Carbon::parse($criteria['welding_date']);
$periodOfValidity = Carbon::parse($welder->period_of_validity);
$expiredDate = $periodOfValidity->copy()->addDays($welderQualificationExpiredDays);
if ($weldingDate->greaterThan($expiredDate)) {
$welderDisplay = "❌📅" . $welderDisplay; // Welding date expired
continue; // Skip
}
} catch (\Exception $e) {
// Invalid date format, skip check
}
}
$qualifiedWelders[] = $welderDisplay;
}
Log::debug("[$logKey] Qualified welders", [
'count' => count($qualifiedWelders),
'welders' => $qualifiedWelders
]);
return array_values(array_unique($qualifiedWelders));
}
/**
* Build material combinations for qualification check
*/
private function buildMaterialCombinations(?string $mat1, ?string $mat2): array
{
if (empty($mat1) && empty($mat2)) {
return [];
}
$mat1 = $mat1 ?? '';
$mat2 = $mat2 ?? $mat1;
$isSameMaterial = ($mat1 === $mat2);
$materialCombination = $isSameMaterial ? $mat1 : "{$mat1}+{$mat2}";
$qualifiedCombinations = [];
// Check MaterialGroupMap for qualified materials
$exactMatch = MaterialGroupMap::where("material_name", $materialCombination)->first();
if ($exactMatch && !empty($exactMatch->qualified_materials)) {
$qualifiedMaterialsList = explode(",", $exactMatch->qualified_materials);
foreach ($qualifiedMaterialsList as $combo) {
$combo = trim($combo);
$parts = explode("+", $combo);
if (count($parts) == 2) {
$qualifiedCombinations[] = ['mat1' => trim($parts[0]), 'mat2' => trim($parts[1])];
} elseif (count($parts) == 1) {
$qualifiedCombinations[] = ['mat1' => trim($parts[0]), 'mat2' => trim($parts[0])];
}
}
}
// Always include original combination
if ($isSameMaterial) {
$qualifiedCombinations[] = ['mat1' => $mat1, 'mat2' => $mat2];
} else {
$qualifiedCombinations[] = ['mat1' => $mat1, 'mat2' => $mat2];
$qualifiedCombinations[] = ['mat1' => $mat2, 'mat2' => $mat1]; // Reverse
}
return $qualifiedCombinations;
}
/**
* Get welders from weld_logs as array (helper for fallback)
*/
private function getWeldersFromWeldLogsArray(string $drawingNumber): array
{
$welders1 = WeldLog::where('iso_number', $drawingNumber)
->whereNotNull('welder_1')
->where('welder_1', '!=', '')
->pluck('welder_1')
->unique()
->filter()
->values();
$welders2 = WeldLog::where('iso_number', $drawingNumber)
->whereNotNull('welder_2')
->where('welder_2', '!=', '')
->pluck('welder_2')
->unique()
->filter()
->values();
return $welders1->merge($welders2)->unique()->sort()->values()->toArray();
}
/**
* Get type of weld options from joint_types table
* Phase 3: Type of Weld dropdown options
*/
public function getTypeOfWeldOptions(Request $request): JsonResponse
{
try {
$options = \DB::table('joint_types')
->select('short_name_en')
->whereNotNull('short_name_en')
->where('short_name_en', '!=', '')
->orderBy('short_name_en')
->distinct()
->pluck('short_name_en')
->filter()
->values()
->toArray();
// Fallback: If no options found, use default types
if (empty($options)) {
$options = ['BW', 'FW', 'SW', 'TW', 'BJ', 'FJ', 'TH', 'CJ'];
}
return response()->json([
'success' => true,
'data' => $options
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to get type of weld options',
'error' => $e->getMessage()
], 500);
}
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Spool;
use App\Models\Element;
use App\Models\Joint;
use App\Models\Welder;
use App\Models\WelderAssignment;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class PdfmController extends Controller
{
// Spools CRUD
public function getSpools(): JsonResponse
{
$spools = Spool::orderBy('spool_no')->get();
return response()->json([
'success' => true,
'data' => $spools
]);
}
public function createSpool(Request $request): JsonResponse
{
$request->validate([
'spool_no' => 'required|string|unique:spools',
'description' => 'nullable|string',
'material' => 'nullable|string',
'diameter' => 'nullable|string',
'thickness' => 'nullable|string',
'length' => 'nullable|string',
'classification' => 'nullable|string'
]);
$spool = Spool::create($request->all());
return response()->json([
'success' => true,
'data' => $spool,
'message' => 'Spool created successfully!'
], 201);
}
// Elements CRUD
public function getElements(): JsonResponse
{
$elements = Element::orderBy('element_code')->get();
return response()->json([
'success' => true,
'data' => $elements
]);
}
public function createElement(Request $request): JsonResponse
{
$request->validate([
'element_code' => 'required|string|unique:elements',
'element_name' => 'required|string',
'element_type' => 'required|string',
'material' => 'nullable|string',
'diameter' => 'nullable|string',
'thickness' => 'nullable|string',
'classification' => 'nullable|string',
'weight' => 'nullable|numeric'
]);
$element = Element::create($request->all());
return response()->json([
'success' => true,
'data' => $element,
'message' => 'Element created successfully!'
], 201);
}
// Joints CRUD
public function getJoints(): JsonResponse
{
$joints = Joint::with(['spool', 'elementFrom', 'elementTo', 'welderAssignments.welder'])
->orderBy('joint_no')
->get();
return response()->json([
'success' => true,
'data' => $joints
]);
}
public function createJoint(Request $request): JsonResponse
{
$request->validate([
'joint_no' => 'required|string|unique:joints',
'spool_no' => 'required|string|exists:spools,spool_no',
'element_from' => 'nullable|string|exists:elements,element_code',
'element_to' => 'nullable|string|exists:elements,element_code',
'joint_type' => 'nullable|string',
'welding_type' => 'nullable|string',
'classification' => 'nullable|string'
]);
$joint = Joint::create($request->all());
return response()->json([
'success' => true,
'data' => $joint,
'message' => 'Joint created successfully!'
], 201);
}
// Welders CRUD
public function getWelders(): JsonResponse
{
$welders = Welder::orderBy('welder_name')->get();
return response()->json([
'success' => true,
'data' => $welders
]);
}
public function createWelder(Request $request): JsonResponse
{
$request->validate([
'welder_code' => 'required|string|unique:welders',
'name' => 'required|string',
'certification' => 'nullable|string',
'specialization' => 'nullable|string'
]);
$welder = Welder::create($request->all());
return response()->json([
'success' => true,
'data' => $welder,
'message' => 'Welder created successfully!'
], 201);
}
// Welder Assignments
public function assignWelder(Request $request): JsonResponse
{
$request->validate([
'joint_no' => 'required|string|exists:joints,joint_no',
'welder_code' => 'required|string|exists:welders,welder_code',
'assigned_date' => 'nullable|date'
]);
$assignment = WelderAssignment::create($request->all());
return response()->json([
'success' => true,
'data' => $assignment,
'message' => 'Welder assigned successfully!'
], 201);
}
// Auto Generate Spool Number
public function generateSpoolNumber(): JsonResponse
{
$lastSpool = Spool::orderBy('id', 'desc')->first();
$nextNumber = $lastSpool ? (int)substr($lastSpool->spool_no, 3) + 1 : 1;
$spoolNumber = 'SP-' . str_pad($nextNumber, 3, '0', STR_PAD_LEFT);
// Auto-create the spool in database so it persists
$spool = Spool::create([
'spool_no' => $spoolNumber,
'description' => 'Generated from PDF Editor'
]);
return response()->json([
'success' => true,
'data' => ['spool_no' => $spoolNumber, 'id' => $spool->id]
]);
}
// Auto Generate Joint Number
public function generateJointNumber(Request $request): JsonResponse
{
// Make spool_no optional with nullable validation
$validated = $request->validate(['spool_no' => 'nullable|string']);
$spoolNo = $validated['spool_no'] ?? 'SP-001'; // Default spool if not provided
$lastJoint = Joint::where('spool_no', $spoolNo)
->orderBy('id', 'desc')
->first();
$nextNumber = $lastJoint ? (int)substr($lastJoint->joint_no, 3) + 1 : 1;
$jointNumber = 'J-' . str_pad($nextNumber, 3, '0', STR_PAD_LEFT);
// Auto-create the joint in database so it persists
$joint = Joint::create([
'joint_no' => $jointNumber,
'spool_no' => $spoolNo,
'joint_type' => 'Auto-generated',
'description' => 'Generated from PDF Editor'
]);
return response()->json([
'success' => true,
'data' => ['joint_no' => $jointNumber, 'id' => $joint->id]
]);
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
/**
* @group Project Settings
*
* APIs for project-related settings and configurations.
*/
class ProjectAppUrlsController extends Controller
{
/**
* Get Project App URLs
*
* Returns a list of configured project names and their corresponding application URLs.
* This endpoint is public and requires no authentication.
*
* @response 200 {
* "status": "success",
* "data": [
* {
* "project_name": "208 - Dzerzhinsk",
* "url": "https://208.qms.stellarcons.com/admin"
* },
* {
* "project_name": "102 - VIKSA OMK",
* "url": "https://viksaqms.stellarcons.com/"
* }
* ]
* }
*
* @return \Illuminate\Http\JsonResponse
*/
public function index()
{
// "project-app-urls" ayarını getir
$settings = setting('project-app-urls');
// Eğer string gelirse decode et, zaten array/object ise olduğu gibi kullan
$data = is_string($settings) ? json_decode($settings, true) : $settings;
// Veri yoksa boş array döndür
if (is_null($data)) {
$data = [];
}
return response()->json([
'status' => 'success',
'data' => $data
]);
}
}
@@ -0,0 +1,110 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Endroid\QrCode\QrCode as EndroidQrCode;
use Endroid\QrCode\Writer\PngWriter;
class QRCodeController extends Controller
{
public function generate(Request $request): JsonResponse
{
try {
$request->validate([
'shape_id' => 'required|string',
]);
$shapeId = $request->get('shape_id');
// QR kod içeriği - shape ID ile URL oluştur
$qrContent = url('/api/pdf-editor/annotations/' . $shapeId);
// QR kod PNG olarak üret (Endroid QR Code v6)
$qrCode = new EndroidQrCode(
data: $qrContent,
size: 200,
margin: 10
);
$writer = new PngWriter();
$result = $writer->write($qrCode);
$qrCodeData = $result->getString();
// QR kod dosyasını kaydet
$fileName = 'qr_' . $shapeId . '_' . time() . '.png';
// Klasörü oluştur
$qrDir = storage_path('app/public/qr-codes');
if (!file_exists($qrDir)) {
mkdir($qrDir, 0755, true);
}
// QR kod dosyasını kaydet
$filePath = $qrDir . '/' . $fileName;
file_put_contents($filePath, $qrCodeData);
// Public klasörüne de kopyala
$publicQrDir = public_path('storage/qr-codes');
if (!file_exists($publicQrDir)) {
mkdir($publicQrDir, 0755, true);
}
$publicFilePath = $publicQrDir . '/' . $fileName;
file_put_contents($publicFilePath, $qrCodeData);
return response()->json([
'success' => true,
'data' => [
'shape_id' => $shapeId,
'qr_content' => $qrContent,
'qr_code_url' => url('storage/qr-codes/' . $fileName)
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'data' => null
], 500);
}
}
public function generateForAnnotation($shapeId): JsonResponse
{
try {
// QR kod içeriği
$qrContent = url('/api/annotations/' . $shapeId);
// QR kod PNG olarak üret (Endroid QR Code v6)
$qrCode = new EndroidQrCode(
data: $qrContent,
size: 150,
margin: 5
);
$writer = new PngWriter();
$result = $writer->write($qrCode);
$qrCodeData = $result->getString();
// Base64 encode for frontend
$base64QrCode = 'data:image/png;base64,' . base64_encode($qrCodeData);
return response()->json([
'success' => true,
'data' => [
'shape_id' => $shapeId,
'qr_code' => $base64QrCode,
'qr_content' => $qrContent
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'data' => null
], 500);
}
}
}
@@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SettingController extends Controller
{
/**
* Get a specific setting value for mobile.
*
* @param Request $request
* @param string $key
* @return \Illuminate\Http\JsonResponse
*/
public function get(Request $request, $key)
{
$allowedKeys = [
'unit_kgmetc',
'akt_status',
];
if (!in_array($key, $allowedKeys)) {
return response()->json([
'status' => 'error',
'message' => 'Unauthorized or invalid key'
], 404);
}
$value = setting($key);
$decoded = json_decode($value, true);
if (json_last_error() === JSON_ERROR_NONE && (is_array($decoded) || is_object($decoded))) {
$data = $decoded;
} else {
$data = $value;
}
return response()->json([
'status' => 'success',
'key' => $key,
'data' => $data
]);
}
/**
* Get all common settings.
*
* @return \Illuminate\Http\JsonResponse
*/
public function common()
{
$keys = [
'project_name',
'project_name_ru',
'project_number',
'company_code',
'row_count',
'summary_mails',
'register_creator_file_name_pattern',
'register_creator_start_row',
'register_creator_path',
'register_creator_column_based',
'hand_over_zone_column',
'tp_register_creator_path',
'main_subcontractor',
'cron_job_batch_process_count',
'cron_job_period',
'repair_table_target_rate',
'ndt_limit',
'ndt_wdi',
'repair_limit',
'repair_wdi',
'WelderQualitificationStatusExpiredDate',
'WelderQualitificationStatusRemaningDays',
'ndt_date_validation_active',
'header_color',
'DevExpress_Theme',
'pdf_template_path',
'handover_control_count'
];
$data = [];
foreach ($keys as $key) {
$value = setting($key);
// Attempt to decode JSON if it looks like an array/object
$decoded = json_decode($value, true);
if (json_last_error() === JSON_ERROR_NONE && (is_array($decoded) || is_object($decoded))) {
$data[$key] = $decoded;
} else {
$data[$key] = $value;
}
}
return response()->json([
'status' => 'success',
'data' => $data
]);
}
}
@@ -0,0 +1,273 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
/**
* @group Spool Release Workflow
*
* API endpoints for managing spool release workflow steps and rollbacks.
*/
class SpoolReleaseWorkflowController extends Controller
{
/**
* Advance Spool Release Workflow
*
* Advance the status of a spool through the quality control and painting workflow.
*
* @authenticated
*
* @bodyParam action string required The action to perform (paint, ndt_release, ndt_paint_release, spool_release). Example: paint
* @bodyParam iso_number string Filter by ISO Number (for single operation). Example: 10-BFW-RS1-0001
* @bodyParam spool_number string Filter by Spool Number (for single operation). Example: SPL-001
* @bodyParam iso_spool_pairs string Comma-separated ISO|SPOOL pairs for bulk operations. Example: ISO-001|SPL-001,ISO-001|SPL-002
*
* @response {
* "status": "success",
* "message": "1 row affected",
* "count": 1
* }
*/
public function advance(Request $request)
{
$action = $request->input('action');
$isoNumber = $request->input('iso_number');
$spoolNumber = $request->input('spool_number');
$isoSpoolPairs = $request->input('iso_spool_pairs');
if (!$isoSpoolPairs && (!$isoNumber || !$spoolNumber)) {
return response()->json(['status' => 'error', 'message' => 'Missing ISO and Spool information']);
}
// Prepare pairs array
$pairs = [];
if ($isoSpoolPairs) {
$rawPairs = explode(",", $isoSpoolPairs);
foreach ($rawPairs as $p) {
$parts = explode("|", trim($p));
if (count($parts) == 2) {
$pairs[] = ['iso' => trim($parts[0]), 'spool' => trim($parts[1])];
}
}
} else {
$pairs[] = ['iso' => trim($isoNumber), 'spool' => trim($spoolNumber)];
}
if (empty($pairs)) {
return response()->json(['status' => 'error', 'message' => 'No valid ISO|Spool pairs found']);
}
// Reconstruct pairs string for side effects if needed
$reconstructedPairs = implode(',', array_map(fn($p) => "{$p['iso']}|{$p['spool']}", $pairs));
// Mock $_POST for legacy view calls (side effects)
$_POST['iso_spool_pairs'] = $reconstructedPairs;
$_POST['iso_number'] = implode(',', array_column($pairs, 'iso'));
$_POST['spool_number'] = implode(',', array_column($pairs, 'spool'));
// Ensure legacy u() function works
if ($request->user()) {
\Session::put('cached_user', $request->user());
\Session::put('uid', $request->user()->id);
}
$userId = $request->user() ? $request->user()->id : 0;
$totalAffected = 0;
try {
DB::beginTransaction();
if ($action == 'paint') {
$pattern = setting("paint_release_number_pattern");
$checkNo = str_replace("{number}", get_counter($pattern), $pattern);
foreach ($pairs as $pair) {
$affected = db("weld_logs")
->where("iso_number", $pair['iso'])
->where("spool_number", $pair['spool'])
->whereNotIn("spool_status", ['Completed'])
->whereIn("spool_status", ['Paint', 'Spool Release', 'NDT Release', 'QC', 'Manufacturing', 'Weld Complete'])
->update([
"spool_paint_check" => $userId,
"paint_release_no" => $checkNo,
"paint_release_date" => simdi(),
"spool_status" => "Completed"
]);
$totalAffected += $affected;
if ($affected > 0) {
try {
ob_start();
view('admin.type.spool-release.paint-follow-up-updater', [
'isoNumber' => $pair['iso'],
'spoolNumber' => $pair['spool'],
])->render();
ob_end_clean();
} catch (\Exception $e) {
ob_end_clean();
\Log::warning("Paint follow up updater error: " . $e->getMessage());
}
}
}
} elseif ($action == 'ndt_release') {
$pattern = setting("ndt_release_number_pattern");
$newReleaseNo = str_replace("{number}", get_counter($pattern), $pattern);
foreach ($pairs as $pair) {
$affected = db("weld_logs")
->where("iso_number", $pair['iso'])
->where("spool_number", $pair['spool'])
->whereNotNull("spool_ndt_check_no")
->whereNull("ndt_release_no")
->update([
"ndt_release_no" => $newReleaseNo,
"ndt_release_check" => $userId,
"ndt_release_date" => Carbon::now(),
'spool_status' => "Paint",
]);
$totalAffected += $affected;
}
} elseif ($action == 'ndt_paint_release') {
$pattern = setting("paint_release_number_pattern");
foreach ($pairs as $pair) {
$newRequestNo = str_replace("{number}", get_counter($pattern), $pattern);
$checkPaintingCycle = db("weld_logs")
->where('iso_number', $pair['iso'])
->where('spool_number', $pair['spool'])
->select("painting_cycle")
->first();
$spoolStatus = (!empty($checkPaintingCycle) && !empty($checkPaintingCycle->painting_cycle)) ? "Paint" : "Completed";
$affected = db("weld_logs")
->where('iso_number', $pair['iso'])
->where('spool_number', $pair['spool'])
->whereNotNull("spool_ndt_check_no")
->whereNull("ndt_release_date")
->update([
"ndt_release_no" => $newRequestNo,
"ndt_release_check" => $userId,
"ndt_release_date" => Carbon::now(),
'spool_status' => $spoolStatus,
]);
$totalAffected += $affected;
if ($affected > 0) {
try {
ob_start();
view('admin.type.spool-release.paint-follow-up-updater', [
'isoNumber' => $pair['iso'],
'spoolNumber' => $pair['spool'],
])->render();
ob_end_clean();
} catch (\Exception $e) {
ob_end_clean();
\Log::warning("Paint follow up updater error: " . $e->getMessage());
}
}
}
} elseif ($action == 'spool_release' || $action == 'checklist_ok') {
$pattern = setting("spool_release_check_number_pattern");
$checkNo = str_replace("{number}", get_counter($pattern), $pattern);
foreach ($pairs as $pair) {
$affected = db("weld_logs")
->where("iso_number", $pair['iso'])
->where("spool_number", $pair['spool'])
->whereIn("spool_status", ["Spool Release", "Manufacturing", "Weld Complete"])
->update([
"spool_ndt_check" => $userId,
"spool_ndt_check_no" => $checkNo,
"spool_ndt_check_date" => simdi(),
"spool_status" => "NDT Release"
]);
$totalAffected += $affected;
}
// Side effects
ob_start();
view('admin.type.spool-release.spool-document-generate')->render();
view('cron.spool-status-changer', ['iso_spool_pairs' => $reconstructedPairs])->render();
ob_end_clean();
} else {
DB::rollBack();
return response()->json(['status' => 'error', 'message' => 'Invalid action']);
}
dispatchCacheBladeViews();
DB::commit();
return response()->json([
'status' => 'success',
'message' => $totalAffected . ' row(s) affected',
'count' => $totalAffected
]);
} catch (\Exception $e) {
DB::rollBack();
\Log::error("SpoolReleaseWorkflow error: " . $e->getMessage());
return response()->json([
'status' => 'error',
'message' => 'Internal server error: ' . $e->getMessage()
], 500);
}
}
/**
* Rollback Spool Release
*
* Reverts a spool release action using the legacy rollback system.
*
* @authenticated
*
* @bodyParam id integer required The ID of the spool or record to rollback. Example: 123
* @bodyParam iso_number string required The ISO number to rollback. Example: 10-BFW-RS1-0001
* @bodyParam spool_number string required The Spool number to rollback. Example: SPL-001
*
* @response {
* "status": "success",
* "message": "Rollback applied"
* }
* @response {
* "status": "error",
* "message": "Missing parameters for rollback"
* }
*/
public function rollback(Request $request)
{
$id = $request->input('id');
$isoNumber = $request->input('iso_number');
$spoolNumber = $request->input('spool_number');
if (!$id || !$isoNumber || !$spoolNumber) {
return response()->json(['status' => 'error', 'message' => 'Missing parameters for rollback']);
}
// Ensure u() acts like authenticated API user inside rollback
$_GET['rollback'] = $id;
$_GET['iso_number'] = $isoNumber;
$_GET['spool_number'] = $spoolNumber;
ob_start();
include resource_path('views/admin/type/spool-release/rollback.blade.php');
$output = ob_get_clean();
// Rollback outputs bilgi() which sets session flash and might render a Pjax wrapper
// But the rollback was successful if it gets to the end. Let's just return success JSON
return response()->json([
'status' => 'success',
'message' => 'Rollback applied'
]);
}
}
@@ -0,0 +1,656 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\WeldLog;
use App\Models\Handover;
use App\Models\WeldingEquipment;
/**
* @group Stats
*
* İstatistikler ve Hesaplamalar ile ilgili endpointler.
*/
class StatsController extends Controller
{
/**
* NDT Calculation (General)
*
* Genel NDT hesaplama istatistiklerini getirir.
* Bu endpoint varsayılan 'ndt-calculation' blade cache sonucunu döndürür.
*
* @response 200 {
* "status": "success",
* "type": "general",
* "content": "..."
* }
*/
public function ndtCalculation()
{
// Varsayılan hesaplama (cacheBladeLoad('ndt-calculation'))
// cacheBladeLoad echo yaptığı için çıktıyı yakalıyoruz.
ob_start();
cacheBladeLoad('ndt-calculation');
$content = ob_get_clean();
// Eğer content bir JSON string ise decode et
$decoded = json_decode($content);
if (json_last_error() === JSON_ERROR_NONE) {
$content = $decoded;
}
return response()->json([
'status' => 'success',
'type' => 'general',
'content' => $content
]);
}
/**
* NDT Calculation (Test Packs)
*
* Test Paketleri için NDT hesaplama istatistiklerini getirir.
* 'ndtCalculationTestPack' ayarını döndürür.
*
* @response 200 {
* "status": "success",
* "type": "test_packs",
* "content": "..."
* }
*/
public function ndtCalculationTestPacks()
{
$content = setting('ndtCalculationTestPack');
// Eğer content bir JSON string ise decode et
$decoded = json_decode($content);
if (json_last_error() === JSON_ERROR_NONE) {
$content = $decoded;
}
return response()->json([
'status' => 'success',
'type' => 'test_packs',
'content' => $content
]);
}
/**
* NDT Calculation (Backlogs)
*
* Backloglar için NDT hesaplama istatistiklerini getirir.
* 'ndtCalculationBacklogs' ayarını döndürür.
*
* @queryParam type string Filtreleme türü (opsiyonel). Example: backlogs
*
* @response 200 {
* "status": "success",
* "type": "backlogs",
* "content": "..."
* }
*/
public function ndtCalculationBacklogs()
{
request()->merge(['type' => 'backlogs']);
$_GET['type'] = 'backlogs';
ob_start();
cacheBladeLoad('ndt-calculation');
$content = ob_get_clean();
// Eğer content bir JSON string ise decode et
$decoded = json_decode($content);
if (json_last_error() === JSON_ERROR_NONE) {
$content = $decoded;
}
return response()->json([
'status' => 'success',
'type' => 'backlogs',
'content' => $content
]);
}
/**
* Dashboard Overview Stats
*
* Get aggregated statistics for project overview dashboard.
* This endpoint is protected by a static hash.
* Includes:
* - WDI Monthly (Shop/Field/Total)
* - Spool Progress
* - NDT/Repair Backlog
* - Hand-over Status
* - Welding Equipment Status
*
* @header X-Api-Hash string required The static API hash for authentication. Example: your-secret-hash-key
*
* @response 200 {
* "status": "success",
* "data": {
* "wdi_monthly": [...],
* "spool_progress": [...],
* "ndt_backlog": [...],
* "handover_status": [...],
* "welding_equipment": [...]
* }
* }
*/
public function dashboardStats(\Illuminate\Http\Request $request)
{
$year = $request->input('year', date('Y'));
// 1. WDI Monthly Data (Global Aggregate) - Calendar Year
$wdiMonthly = WeldLog::selectRaw("
DATE_FORMAT(welding_date, '%Y-%m') as month,
MONTH(welding_date) as month_num,
SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as shop,
SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field,
SUM(nps_1) as total
")
->whereNotNull('welding_date')
// ->whereYear('welding_date', $year) // Removed to allow client-side filtering
->groupBy('month', 'month_num')
->orderBy('month', 'asc')
->get();
// 1b. WDI Monthly Data (By Project)
$wdiByProjectRaw = WeldLog::selectRaw("
project,
DATE_FORMAT(welding_date, '%Y-%m') as month,
MONTH(welding_date) as month_num,
SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as shop,
SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field,
SUM(nps_1) as total
")
->whereNotNull('welding_date')
->whereNotNull('project')
->where('project', '!=', '')
->whereYear('welding_date', $year) // Keep this for now or remove if needed, but it's unused by dashboard
->groupBy('project', 'month', 'month_num')
->get();
$wdiByProject = [];
foreach($wdiByProjectRaw as $row) {
// Use month_num (1-12) as key for the frontend table columns which expect 1..12
// For the new 'wdiByProject' which is custom for the table, we simply need the integer month key
// because the table logic iterates 1 through 12.
$wdiByProject[$row->project][$row->month_num] = [
'shop' => (float)$row->shop,
'field' => (float)$row->field,
'total' => (float)$row->total,
'month_str' => $row->month // Useful for debugging or generic logic
];
}
// 2. Spool Progress (Time-independent)
// Logic: Group by iso_number + spool_number (Shop only), find effective status based on priority logic.
// Priority: Waiting < Manufacturing < QC < Paint < Completed
$statusCase = "CASE spool_status
WHEN 'Waiting' THEN 1
WHEN 'Manufacturing' THEN 2
WHEN 'QC' THEN 3
WHEN 'Paint' THEN 4
WHEN 'Completed' THEN 5
ELSE 99 END";
$spoolStats = DB::table('weld_logs')
->selectRaw("status_val, COUNT(*) as count")
->fromSub(function($query) use ($statusCase) {
$query->select('iso_number', 'spool_number')
->selectRaw("MIN($statusCase) as status_order")
->selectRaw("
CASE MIN($statusCase)
WHEN 1 THEN 'Waiting'
WHEN 2 THEN 'Manufacturing'
WHEN 3 THEN 'QC'
WHEN 4 THEN 'Paint'
WHEN 5 THEN 'Completed'
ELSE 'Other'
END as status_val
")
->from('weld_logs')
->where('type_of_joint', 'S') // Shop joints drive spool status
->whereNotNull('spool_number')
->where('spool_number', '!=', '')
->whereNotNull('welding_date')
->groupBy('iso_number', 'spool_number');
}, 'sub')
->groupBy('status_val')
->get();
// Transform to array format for frontend
$spoolProgress = [];
foreach ($spoolStats as $row) {
$spoolProgress[] = [
'status' => $row->status_val,
'count' => (int)$row->count
];
}
// 3. NDT/Repair Backlog
// Using existing setting logic from ndt-backlog.blade.php
$ndtData = json_decode(setting("ndtCalculation"), true);
$ndtBacklogStats = [];
if (is_array($ndtData)) {
foreach($ndtData as $d) {
foreach($d as $field => $value) {
if(strpos($field, "backlog") !== false) {
$key = strtoupper(str_replace("_backlog", "", $field));
if(!isset($ndtBacklogStats[$key])) {
$ndtBacklogStats[$key] = 0;
}
if($value > 0) {
$ndtBacklogStats[$key] += $value;
}
}
}
}
}
// Add dynamic limits from Settings
$ndtBacklogStats['ndt_limit'] = setting('ndt_limit');
$ndtBacklogStats['ndt_wdi'] = setting('ndt_wdi');
$ndtBacklogStats['repair_limit'] = setting('repair_limit');
$ndtBacklogStats['repair_wdi'] = setting('repair_wdi');
// 4. Hand-over Status
// Statuses: Dynamically fetched from id_status
$handoverProgress = Handover::selectRaw('id_status, COUNT(*) as count')
->whereNotNull('id_status')
->where('id_status', '!=', '')
->groupBy('id_status')
->pluck('count', 'id_status')
->toArray();
// 5. Welding Equipment Status
// Statuses: Working, Repair, Out of use
$equipmentStats = WeldingEquipment::selectRaw('working_status, COUNT(*) as count')
->whereNotNull('working_status')
->groupBy('working_status')
->get()
->pluck('count', 'working_status');
$targetEquipmentStatuses = ['Working', 'Repair', 'Out of use'];
$equipmentProgress = [];
foreach ($targetEquipmentStatuses as $status) {
$equipmentProgress[$status] = $equipmentStats[$status] ?? 0;
}
// Add others
$otherEquipmentCount = 0;
foreach ($equipmentStats as $status => $count) {
if (!in_array($status, $targetEquipmentStatuses)) {
$otherEquipmentCount += $count;
}
}
if ($otherEquipmentCount > 0) {
$equipmentProgress['Other'] = $otherEquipmentCount;
}
// 6. Welding Status (Welder Distribution by Site)
// Logic: Group by project (Site).
// CS = distinct real_welder_1 & real_welder_2 where ru_material_group_1 matches 'M01'
// SS/AS = distinct real_welder_1 & real_welder_2 where ru_material_group_1 does NOT match 'M01'
$weldingStatusData = DB::table(function ($query) {
$query->select('project', 'type_of_joint', 'ru_material_group_1', 'real_welder_1 as welder', 'welding_date')
->from('weld_logs')
->whereNotNull('real_welder_1')
->where('real_welder_1', '!=', '')
->whereNotNull('welding_date')
->unionAll(
DB::table('weld_logs')
->select('project', 'type_of_joint', 'ru_material_group_2', 'real_welder_1 as welder', 'welding_date')
->from('weld_logs')
->whereNotNull('real_welder_2')
->where('real_welder_2', '!=', '')
->whereNotNull('welding_date')
);
}, 'u')
->select('project')
->selectRaw("YEAR(welding_date) as year")
->selectRaw("MONTH(welding_date) as month")
->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop")
->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field")
->selectRaw("COUNT(DISTINCT CASE WHEN (ru_material_group_1 LIKE '%M01%' OR ru_material_group_1 LIKE '%М01%') THEN welder END) as cs")
->selectRaw("COUNT(DISTINCT CASE WHEN (ru_material_group_1 NOT LIKE '%M01%' AND ru_material_group_1 NOT LIKE '%М01%') OR ru_material_group_1 IS NULL THEN welder END) as ss")
->selectRaw("COUNT(DISTINCT welder) as actual_total")
->whereNotNull('project')
->where('project', '!=', '')
->groupBy('project', 'year', 'month')
->get();
$weldingStatus = [];
foreach($weldingStatusData as $row) {
$weldingStatus[] = [
'site' => $row->project,
'year' => (int)$row->year,
'month' => (int)$row->month,
'shop' => (int)$row->shop,
'field' => (int)$row->field,
'cs' => (int)$row->cs,
'ss' => (int)$row->ss,
'total' => (int)$row->actual_total
];
}
// 7. Welding Status Overall Summary (No Site Breakdown)
// Count unique welders across ALL projects for Shop and Field
// 7. Welding Status Overall Summary (No Site Breakdown)
// Count unique welders across ALL projects for Shop and Field
$weldingSummaryRaw = DB::table(function ($query) {
$query->select('type_of_joint', 'real_welder_1 as welder')
->from('weld_logs')
->whereNotNull('real_welder_1')
->where('real_welder_1', '!=', '')
->whereNotNull('welding_date')
->unionAll(
DB::table('weld_logs')
->select('type_of_joint', 'real_welder_2 as welder')
->from('weld_logs')
->whereNotNull('real_welder_2')
->where('real_welder_2', '!=', '')
->whereNotNull('welding_date')
);
}, 'u')
->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop")
->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field")
->selectRaw("COUNT(DISTINCT welder) as actual_total")
->first();
$weldingStatusSummary = [
'shop' => (int)$weldingSummaryRaw->shop,
'field' => (int)$weldingSummaryRaw->field,
'total' => (int)$weldingSummaryRaw->actual_total
];
// 8. Welding Status Monthly (Shop/Field/Total per Month)
// Groups distinct welders by month and project
// 8. Welding Status Monthly (Shop/Field/Total per Month)
// Groups distinct welders by month (Global Site Scope)
$weldingStatusMonthly = DB::table(function ($query) use ($year) {
$query->select('project', 'welding_date', 'type_of_joint', 'real_welder_1 as welder')
->from('weld_logs')
->whereNotNull('real_welder_1')
->where('real_welder_1', '!=', '')
->whereNotNull('welding_date')
->unionAll(
DB::table('weld_logs')
->select('project', 'welding_date', 'type_of_joint', 'real_welder_2 as welder')
->from('weld_logs')
->whereNotNull('real_welder_2')
->where('real_welder_2', '!=', '')
->whereNotNull('welding_date')
);
}, 'u')
->selectRaw("
DATE_FORMAT(welding_date, '%Y-%m') as month_key,
MONTH(welding_date) as month,
YEAR(welding_date) as year,
COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop,
COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field,
COUNT(DISTINCT welder) as actual_total
")
->groupBy('month_key', 'month', 'year')
->orderBy('month_key', 'asc')
->get()
->map(function($item) {
return [
'project' => 'Total', // Value is now aggregated for the whole site
'month_key' => $item->month_key,
'month' => (int)$item->month,
'year' => (int)$item->year,
'shop' => (int)$item->shop,
'field' => (int)$item->field,
'total' => (int)$item->actual_total
];
});
// 9. NDT Summary (RT & UT)
// New calculation logic mirrored from summary-nocache.blade.php
$ndtSummary = [];
// RT Logic
$rtTotal = DB::table('radiographic_tests')->whereNotNull('rt_result')->count();
$rtWDI = DB::table('radiographic_tests')->whereNotNull('rt_result')->sum('nps_1');
$rtAccepted = DB::table('radiographic_tests')->where('rt_result', 'Accept / Годен')->count();
$rtRejected = DB::table('radiographic_tests')->whereNotNull('rt_result')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count();
// RT Repair Rate logic: (ShopRepair + FieldRepair) / (ShopTotal + FieldTotal)
$rtShopTotal = DB::table('radiographic_tests')->where('type_of_joint', 'S')->whereNotIn('rt_result', [''])->count();
$rtShopRepair = DB::table('radiographic_tests')->where('type_of_joint', 'S')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count();
$rtFieldTotal = DB::table('radiographic_tests')->where('type_of_joint', 'F')->whereNotIn('rt_result', [''])->count();
$rtFieldRepair = DB::table('radiographic_tests')->where('type_of_joint', 'F')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count();
$rtRepairRate = ($rtShopTotal + $rtFieldTotal) > 0
? round(($rtShopRepair + $rtFieldRepair) * 100 / ($rtShopTotal + $rtFieldTotal), 2)
: 0;
$ndtSummary['rt'] = [
'total' => $rtTotal,
'wdi' => (float)$rtWDI,
'accepted' => $rtAccepted,
'rejected' => $rtRejected,
'repair_rate' => $rtRepairRate
];
// UT Logic
$utTotal = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->count();
$utWDI = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->sum('nps_1');
$utAccepted = DB::table('ultrasonic_tests')->where('ut_result', 'Accept / Годен')->count();
$utRejected = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->where('ut_result', '!=', 'Accept / Годен')->count();
// UT Repair Rate
$utShopTotal = DB::table('ultrasonic_tests')->where('type_of_joint', 'S')->whereNotIn('ut_result', [''])->count();
$utShopRepair = DB::table('ultrasonic_tests')->where('type_of_joint', 'S')->whereNotIn('ut_result', ['', 'Accept / Годен'])->count();
$utFieldTotal = DB::table('ultrasonic_tests')->where('type_of_joint', 'F')->whereNotIn('ut_result', [''])->count();
$utFieldRepair = DB::table('ultrasonic_tests')->where('type_of_joint', 'F')->whereNotIn('ut_result', ['', 'Accept / Годен'])->count();
$utRepairRate = ($utShopTotal + $utFieldTotal) > 0
? round(($utShopRepair + $utFieldRepair) * 100 / ($utShopTotal + $utFieldTotal), 2)
: 0;
$ndtSummary['ut'] = [
'total' => $utTotal,
'wdi' => (float)$utWDI,
'accepted' => $utAccepted,
'rejected' => $utRejected,
'repair_rate' => $utRepairRate
];
// 10. WDI Statistics (CS vs SS/AS) by Project
// CS: Material Group contains M01
// SS: Material Group does not contain M01
$wdiStatsData = DB::table('weld_logs')
->select('project')
->selectRaw("YEAR(welding_date) as year")
->selectRaw("MONTH(welding_date) as month")
->selectRaw("SUM(CASE WHEN (ru_material_group_1 LIKE '%M01%' OR ru_material_group_1 LIKE '%М01%') THEN nps_1 ELSE 0 END) as cs_wdi")
->selectRaw("SUM(CASE WHEN (ru_material_group_1 NOT LIKE '%M01%' AND ru_material_group_1 NOT LIKE '%М01%') OR ru_material_group_1 IS NULL THEN nps_1 ELSE 0 END) as ss_wdi")
->whereNotNull('welding_date')
->where('project', '!=', '')
->whereNotNull('project')
->groupBy('project', 'year', 'month')
->get();
$wdiStats = [];
foreach($wdiStatsData as $row) {
$wdiStats[] = [
'project' => $row->project,
'year' => (int)$row->year,
'month' => (int)$row->month,
'cs' => (float)$row->cs_wdi,
'ss' => (float)$row->ss_wdi,
'total' => (float)$row->cs_wdi + (float)$row->ss_wdi
];
}
// 11. Weld Backlog (Prefab vs Field) by Project
// Backlog: Items with no welding date, grouped by year/month from fit_up_date for frontend filtering
$weldBacklogData = DB::table('weld_logs')
->select('project')
->selectRaw("SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as prefab_backlog")
->selectRaw("SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field_backlog")
->selectRaw("SUM(nps_1) as total_backlog")
->whereNull('welding_date')
->where('project', '!=', '')
->whereNotNull('project')
->groupBy('project')
->get();
$weldBacklog = [];
foreach($weldBacklogData as $row) {
$weldBacklog[] = [
'project' => $row->project,
'prefab' => (float)$row->prefab_backlog,
'field' => (float)$row->field_backlog,
'total' => (float)$row->total_backlog
];
}
// 12. Welder Current (from naks_welders table)
// Total unique welders at each site, categorized by CS/SS
// CS: material_1 contains М01 or M01
// SS: material_1 does NOT contain М01/M01
// No date filter - shows total registered welders per site
$welderCurrentData = DB::table('welder_tests')
->selectRaw("COUNT(DISTINCT CASE WHEN (material_group_1 LIKE '%M01%' OR material_group_1 LIKE '%М01%') THEN naks_id END) as cs")
->selectRaw("COUNT(DISTINCT CASE WHEN (material_group_1 NOT LIKE '%M01%' AND material_group_1 NOT LIKE '%М01%') OR material_group_1 IS NULL THEN naks_id END) as ss")
->selectRaw("COUNT(DISTINCT naks_id) as total")
->where('status', 'Active')
->get();
$welderCurrent = [];
foreach($welderCurrentData as $row) {
$welderCurrent[] = [
'project' => '',
'cs' => (int)$row->cs,
'ss' => (int)$row->ss,
'total' => (int)$row->total
];
}
return response()->json([
'status' => 'success',
'data' => [
'wdi_monthly' => $wdiMonthly,
'spool_progress' => $spoolProgress,
'ndt_backlog' => $ndtBacklogStats,
'handover_status' => $handoverProgress,
'welding_equipment' => $equipmentProgress,
'welding_status' => $weldingStatus,
'welding_status_monthly' => $weldingStatusMonthly,
'welding_status_summary' => $weldingStatusSummary,
'ndt_summary' => $ndtSummary,
'wdi_by_project' => $wdiByProject,
'wdi_stats' => $wdiStats,
'weld_backlog' => $weldBacklog,
'repair_stats_monthly' => DB::table('weld_logs')
->selectRaw("
DATE_FORMAT(welding_date, '%Y-%m') as month_key,
YEAR(welding_date) as year,
MONTH(welding_date) as month,
SUM(CASE WHEN type_of_joint = 'S' AND rt_result IS NOT NULL AND rt_result != '' THEN 1 ELSE 0 END) as rt_shop_total,
SUM(CASE WHEN type_of_joint = 'S' AND rt_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as rt_shop_repair,
SUM(CASE WHEN type_of_joint = 'F' AND rt_result IS NOT NULL AND rt_result != '' THEN 1 ELSE 0 END) as rt_field_total,
SUM(CASE WHEN type_of_joint = 'F' AND rt_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as rt_field_repair,
SUM(CASE WHEN type_of_joint = 'S' AND ut_result IS NOT NULL AND ut_result != '' THEN 1 ELSE 0 END) as ut_shop_total,
SUM(CASE WHEN type_of_joint = 'S' AND ut_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as ut_shop_repair,
SUM(CASE WHEN type_of_joint = 'F' AND ut_result IS NOT NULL AND ut_result != '' THEN 1 ELSE 0 END) as ut_field_total,
SUM(CASE WHEN type_of_joint = 'F' AND ut_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as ut_field_repair
")
->whereNotNull('welding_date')
->groupBy('month_key', 'year', 'month')
->get(),
'test_packages' => DB::table('test_packages')
->leftJoinSub(function ($query) {
$query->from('weld_logs')
->select(
'test_package_no',
DB::raw('MAX(project) as project'),
DB::raw('MAX(welding_date) as last_weld_date')
)
->whereNotNull('test_package_no')
->where('test_package_no', '!=', '')
->whereNotNull('welding_date')
->groupBy('test_package_no');
}, 'projects', 'projects.test_package_no', '=', 'test_packages.test_package_number')
->selectRaw("
COALESCE(projects.project, 'Unknown') as project,
count(test_packages.id) as total_tp,
SUM(CASE WHEN walkdown_date IS NOT NULL THEN 1 ELSE 0 END) as line_check,
SUM(COALESCE(a_punch_point_open, 0)) as punch_a,
SUM(CASE WHEN test_date IS NOT NULL THEN 1 ELSE 0 END) as test,
SUM(CASE WHEN reinstatement_date IS NOT NULL THEN 1 ELSE 0 END) as reinstatement
")
->groupBy(
DB::raw("COALESCE(projects.project, 'Unknown')")
)
->get(),
'welder_current' => $welderCurrent,
]
]);
}
/**
* Welding Status By Project
*
* Get summary data for "Sahaya Göre Kaynak Durumu" (Welding Status by Site).
* Breakdown of WDI (NPS-1) by project, grouped into SHOP and FIELD.
*
* @response 200 {
* "status": "success",
* "data": [
* {
* "project": "COOLER TOWER",
* "shop": 82,
* "field": 122,
* "total": 204,
* "percentage": 93
* },
* ...
* ]
* }
*/
public function weldingByProject()
{
$weldingData = DB::table('weld_logs')
->select('project')
->selectRaw("SUM(CASE WHEN type_of_joint = 'S' AND welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as shop")
->selectRaw("SUM(CASE WHEN (type_of_joint != 'S' OR type_of_joint IS NULL) AND welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as field")
->selectRaw("SUM(CASE WHEN welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as total_welded")
->selectRaw("SUM(nps_1) as total_scope")
->whereNotNull('project')
->where('project', '!=', '')
->groupBy('project')
->get();
$result = $weldingData->map(function ($row) {
$totalWelded = (float)$row->total_welded;
$totalScope = (float)$row->total_scope;
$percentage = $totalScope > 0 ? round(($totalWelded / $totalScope) * 100) : 0;
return [
'project' => $row->project,
'shop' => (float)$row->shop,
'field' => (float)$row->field,
'total' => $totalWelded,
'percentage' => (int)$percentage
];
});
return response()->json([
'status' => 'success',
'data' => $result
]);
}
}
@@ -0,0 +1,327 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\SurfaceAreaConstant;
use App\Models\PaintFollowUp;
use Illuminate\Support\Facades\Schema;
use DB;
class SurfaceAreaController extends Controller
{
/**
* Get distinct element types for a given standard
*/
public function getElements(Request $request)
{
$standard = $request->get('standard', 'ASME');
$elements = SurfaceAreaConstant::where('standard_type', $standard)
->where('is_active', true)
->select('element_type_en', 'element_type_ru')
->distinct()
->orderBy('element_type_en')
->get();
return response()->json($elements);
}
/**
* Get diameter options for a given standard and element
*/
public function getDiameters(Request $request)
{
$standard = $request->get('standard', 'ASME');
$element = $request->get('element');
// Element can be either EN or RU, so we need to check both
$diameters = SurfaceAreaConstant::where('standard_type', $standard)
->where(function($query) use ($element) {
$query->where('element_type_en', $element)
->orWhere('element_type_ru', $element);
})
->where('is_active', true)
->select('diameter_inch', 'diameter_mm', 'surface_area_m2', 'manufacturing_standard', 'element_type_en', 'element_type_ru', 'general_name')
->orderBy('diameter_mm')
->get();
return response()->json($diameters);
}
/**
* Get MTO total for a given iso_drawings
*/
public function getMtoTotal(Request $request)
{
$isoDrawings = $request->get('iso_drawings');
if (!$isoDrawings) {
return response()->json(['error' => 'iso_drawings parameter is required'], 400);
}
// MTO tablosundan pipe verilerini hesapla
// Formula: SUM(quantity * PI * (odmm_1/2000)^2)
$mtoTotal = DB::table('m_t_o_s')
->where('description_en', 'like', '%pipe%')
->where('line', $isoDrawings)
->selectRaw('SUM(quantity * POWER(odmm_1/2000, 2) * PI()) AS total')
->first()->total;
return response()->json([
'success' => true,
'iso_drawings' => $isoDrawings,
'mto_total' => $mtoTotal ?? 0
]);
}
/**
* Get saved surface area items for a construction paint log
*/
public function getSavedItems(Request $request)
{
$constructionPaintLogId = $request->get('construction_paint_log_id');
if (!$constructionPaintLogId) {
return response()->json(['error' => 'construction_paint_log_id parameter is required'], 400);
}
$paintLog = DB::table('construction_paint_logs')
->where('id', $constructionPaintLogId)
->select('surface_area_items', 'fittings_m2', 'pipe_m2', 'total_m2')
->first();
if (!$paintLog) {
return response()->json(['error' => 'Construction Paint Log not found'], 404);
}
$items = [];
if ($paintLog->surface_area_items) {
$items = json_decode($paintLog->surface_area_items, true) ?: [];
}
return response()->json([
'success' => true,
'items' => $items,
'fittings_m2' => $paintLog->fittings_m2,
'pipe_m2' => $paintLog->pipe_m2,
'total_m2' => $paintLog->total_m2
]);
}
/**
* Save surface area calculation to construction_paint_logs and paint_follow_ups
*/
public function save(Request $request)
{
$constructionPaintLogId = $request->get('construction_paint_log_id');
$fittingsTotal = $request->get('fittings_total');
$pipeTotal = $request->get('pipe_total');
$grandTotal = $request->get('grand_total');
$cuttingMm = $request->get('cutting_mm');
$items = json_decode($request->get('items', '[]'), true);
$dn1 = $request->get('dn_1');
$dn2 = $request->get('dn_2');
$linePipeMm1 = $request->get('line_pipe_mm_1');
$linePipeMm2 = $request->get('line_pipe_mm_2');
$totalLineMm = $request->get('total_line_mm');
// Calculate total_line_mm if not provided (sum of line_pipe_mm_1 and line_pipe_mm_2)
if ($totalLineMm === null || $totalLineMm === '') {
$linePipeMm1Val = (float)($linePipeMm1 ?? 0);
$linePipeMm2Val = (float)($linePipeMm2 ?? 0);
$totalLineMm = $linePipeMm1Val + $linePipeMm2Val;
}
// Get construction paint log record
$paintLog = DB::table('construction_paint_logs')->find($constructionPaintLogId);
if (!$paintLog) {
return response()->json(['error' => 'Construction Paint Log not found'], 404);
}
// Process items to calculate fitting quantities and areas
$elbowPcs = 0;
$elbow45Pcs = 0;
$elbowM2 = 0;
$teePcs = 0;
$teeM2 = 0;
$reductionsPcs = 0;
$reductionsM2 = 0;
$flangePcs = 0;
$flangeM2 = 0;
$ventPcs = 0;
$ventM2 = 0;
$capPcs = 0;
$capM2 = 0;
foreach ($items as $item) {
$elementEn = $item['element_en'] ?? '';
$elementRu = $item['element_ru'] ?? '';
$quantity = (int)($item['quantity'] ?? 0);
$total = (float)($item['total'] ?? 0);
// Check both EN and RU element names (use EN first, fallback to RU)
$elementName = trim($elementEn ?: $elementRu);
$elementNameLower = strtolower($elementName);
// Elbow 90° 1.5D
if (stripos($elementNameLower, 'elbow') !== false &&
(stripos($elementNameLower, '90') !== false || stripos($elementName, '90°') !== false)) {
$elbowPcs += $quantity;
$elbowM2 += $total;
}
// Elbow 45° 1.5D
elseif (stripos($elementNameLower, 'elbow') !== false &&
(stripos($elementNameLower, '45') !== false || stripos($elementName, '45°') !== false)) {
$elbow45Pcs += $quantity;
$elbowM2 += $total;
}
// Reductions (Concentric, Eccentric, any reducer)
elseif (stripos($elementNameLower, 'reducer') !== false ||
stripos($elementNameLower, 'reduction') !== false) {
$reductionsPcs += $quantity;
$reductionsM2 += $total;
}
// Slip On Flange (Class 150)
elseif (stripos($elementNameLower, 'slip on flange') !== false ||
(stripos($elementNameLower, 'slip') !== false && stripos($elementNameLower, 'flange') !== false)) {
$flangePcs += $quantity;
$flangeM2 += $total;
}
// Weldneck Flange (Class 150)
elseif (stripos($elementNameLower, 'weldneck flange') !== false ||
(stripos($elementNameLower, 'weldneck') !== false && stripos($elementNameLower, 'flange') !== false)) {
$flangePcs += $quantity;
$flangeM2 += $total;
}
// Tee (Equal Tee, Reducing Tee, Cross Tee - all types)
elseif (stripos($elementNameLower, 'tee') !== false ||
stripos($elementNameLower, 'cross') !== false) {
$teePcs += $quantity;
$teeM2 += $total;
}
// Nipple/Olet (replaces Vent)
elseif (stripos($elementNameLower, 'nipple') !== false ||
stripos($elementNameLower, 'olet') !== false ||
stripos($elementNameLower, 'nipel') !== false ||
stripos($elementNameLower, 'nippel') !== false) {
$ventPcs += $quantity;
$ventM2 += $total;
}
// Cap
elseif (stripos($elementNameLower, 'cap') !== false) {
$capPcs += $quantity;
$capM2 += $total;
}
}
// Prepare update data
$updateData = [
'fittings_m2' => $fittingsTotal,
'pipe_m2' => $pipeTotal,
'total_m2' => $grandTotal,
'cutting_mm' => $cuttingMm,
'surface_area_items' => json_encode($items),
'elbow_pcs' => $elbowPcs,
'elbow_45_pcs' => $elbow45Pcs,
'elbow_m2' => round($elbowM2, 2),
'tee_pcs' => $teePcs,
'tee_m2' => round($teeM2, 2),
'reductions_pcs' => $reductionsPcs,
'reductions_m2' => round($reductionsM2, 2),
'flange_pcs' => $flangePcs,
'flange_m2' => round($flangeM2, 2),
'vent_pcs' => $ventPcs,
'vent_m2' => round($ventM2, 2),
'cap_pcs' => $capPcs,
'cap_m2' => round($capM2, 2),
'updated_at' => now()
];
// Add pipe dimensions if provided (only if not empty and not zero)
if ($dn1 !== null && $dn1 !== '') {
$updateData['dn_1'] = $dn1;
}
if ($dn2 !== null && $dn2 !== '') {
$updateData['dn_2'] = $dn2;
}
// Check if columns exist before updating line_pipe_mm fields
$hasLinePipeMm1 = Schema::hasColumn('construction_paint_logs', 'line_pipe_mm_1');
$hasLinePipeMm2 = Schema::hasColumn('construction_paint_logs', 'line_pipe_mm_2');
$hasTotalLineMm = Schema::hasColumn('construction_paint_logs', 'total_line_mm');
if ($hasLinePipeMm1 && $linePipeMm1 !== null && $linePipeMm1 !== '' && $linePipeMm1 != 0) {
$updateData['line_pipe_mm_1'] = (int)$linePipeMm1;
}
if ($hasLinePipeMm2 && $linePipeMm2 !== null && $linePipeMm2 !== '' && $linePipeMm2 != 0) {
$updateData['line_pipe_mm_2'] = (int)$linePipeMm2;
}
// Add total_line_mm (sum of line_pipe_mm_1 and line_pipe_mm_2)
// Only update if total is greater than 0 and column exists
if ($hasTotalLineMm && $totalLineMm !== null && $totalLineMm > 0) {
$updateData['total_line_mm'] = (float)$totalLineMm;
}
// Update construction paint log with surface area values and fitting details
DB::table('construction_paint_logs')
->where('id', $constructionPaintLogId)
->update($updateData);
// Find or create paint follow up record based on spool/line
$paintFollowUp = PaintFollowUp::where('spool_no_joint_no', $paintLog->spool)
->where('line', $paintLog->line)
->first();
$paintFollowUpAction = '';
$paintFollowUpId = null;
if ($paintFollowUp) {
$paintFollowUp->update([
'volume_1' => $grandTotal,
'volume_2' => $grandTotal,
'volume_3' => $grandTotal,
'total_volume' => $grandTotal * 3,
]);
$paintFollowUpAction = 'updated';
$paintFollowUpId = $paintFollowUp->id;
} else {
$newRecord = PaintFollowUp::create([
'line' => $paintLog->line,
'iso_number' => $paintLog->iso_drawings,
'spool_no_joint_no' => $paintLog->spool,
'volume_1' => $grandTotal,
'volume_2' => $grandTotal,
'volume_3' => $grandTotal,
'total_volume' => $grandTotal * 3,
]);
$paintFollowUpAction = 'created';
$paintFollowUpId = $newRecord->id;
}
return response()->json([
'success' => true,
'message' => 'Surface area saved successfully',
'construction_paint_log' => [
'id' => $constructionPaintLogId,
'spool' => $paintLog->spool,
'line' => $paintLog->line,
'fittings_m2' => $fittingsTotal,
'pipe_m2' => $pipeTotal,
'total_m2' => $grandTotal
],
'paint_follow_up' => [
'action' => $paintFollowUpAction,
'id' => $paintFollowUpId,
'spool_no_joint_no' => $paintLog->spool,
'line' => $paintLog->line,
'volume_1' => $grandTotal,
'volume_2' => $grandTotal,
'volume_3' => $grandTotal,
'total_volume' => $grandTotal * 3
]
]);
}
}
@@ -0,0 +1,176 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class WorkPermitHierarchyController extends Controller
{
/**
* Get Work Permit Hierarchy
*
* Returns a structured hiearchy of work permit projects, companies, and personnel.
*/
public function index(Request $request)
{
try {
// Get all column names from both tables for placeholders
$workPermitColumns = Schema::getColumnListing('work_permit_documents');
$subcontractorColumns = Schema::getColumnListing('subcontractors');
$query = DB::table("work_permit_documents")
->select([
'work_permit_documents.id',
'work_permit_documents.name_surname',
'work_permit_documents.company',
'work_permit_documents.certificates_number',
'work_permit_documents.duty',
'work_permit_documents.document_number',
'work_permit_documents.attorney',
'work_permit_documents.attorney_date',
'work_permit_documents.zone as project',
'work_permit_documents.sign_order as personnel_sign_order',
'subcontractors.company_name_ru',
'subcontractors.company_name_en',
'subcontractors.job_description',
'subcontractors.company_code',
'subcontractors.operation_type',
'subcontractors.city',
'subcontractors.address',
'subcontractors.logo',
'subcontractors.sign_order as company_sign_order'
])
->join("subcontractors", "work_permit_documents.company", "subcontractors.company_name_ru")
->orderBy('subcontractors.operation_type')
->orderBy('work_permit_documents.sign_order');
// Apply project filter if provided
if ($request->has('project') && !empty($request->project)) {
$query->where('work_permit_documents.zone', 'LIKE', '%' . $request->project . '%');
}
$workPermitUsers = $query->get();
$projectMapping = [];
// Parse comma-separated projects and organize
foreach ($workPermitUsers as $user) {
$projectParts = explode(',', $user->project);
foreach ($projectParts as $singleProject) {
$singleProject = trim($singleProject);
if (empty($singleProject)) continue;
if ($request->has('project') && !empty($request->project)) {
if (stripos($singleProject, $request->project) === false) {
continue;
}
}
if (!isset($projectMapping[$singleProject])) {
$projectMapping[$singleProject] = [];
}
// Avoid duplicates in mapping
$found = false;
foreach ($projectMapping[$singleProject] as $existingUser) {
if ($existingUser->name_surname === $user->name_surname &&
$existingUser->operation_type === $user->operation_type &&
$existingUser->personnel_sign_order === $user->personnel_sign_order) {
$found = true;
break;
}
}
if (!$found) {
// Enhance user object with placeholders
$user->placeholders = $this->generatePlaceholders($user, $workPermitColumns, $subcontractorColumns);
$projectMapping[$singleProject][] = $user;
}
}
}
// Group into hiearchy: Project -> Operation Type -> Companies -> Users
$response = [];
foreach ($projectMapping as $projectName => $users) {
$operationGroups = collect($users)->groupBy('operation_type');
$projectData = [
'project_name' => $projectName,
'operation_types' => []
];
foreach ($operationGroups as $operationType => $operationGroup) {
$companies = [];
$byCompany = collect($operationGroup)->groupBy('company_name_ru');
foreach ($byCompany as $companyName => $companyUsers) {
$companies[] = [
'company_name' => $companyName,
'company_sign_order' => $companyUsers->first()->company_sign_order,
'users' => $companyUsers->sortBy('personnel_sign_order')->values()->all()
];
}
$projectData['operation_types'][] = [
'type' => $operationType,
'companies' => $companies
];
}
$response[] = $projectData;
}
// Sort alphabetical by project name
usort($response, function($a, $b) {
return strcmp($a['project_name'], $b['project_name']);
});
return response()->json([
'status' => 'success',
'data' => $response
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage()
], 500);
}
}
/**
* Generate placeholders for a user
*/
private function generatePlaceholders($user, $wpColumns, $sbColumns)
{
$job = $user->job_description ?? '';
$fso = $user->company_sign_order ?? '';
$pso = $user->personnel_sign_order ?? '';
$prefix = "{$job}{$fso}_{$pso}_";
$placeholders = [
'work_permit' => [],
'subcontractor' => [],
'common' => [
'name_surname' => "{" . $prefix . "name_surname}",
'company_name' => "{" . $prefix . "company_name}",
'certificate_no' => "{" . $prefix . "certificate_no}",
'duty' => "{" . $prefix . "duty}",
]
];
foreach ($wpColumns as $col) {
$placeholders['work_permit'][$col] = "{" . $prefix . $col . "}";
}
foreach ($sbColumns as $col) {
$placeholders['subcontractor'][$col] = "{" . $prefix . "sub_" . $col . "}";
}
return $placeholders;
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}
@@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
@@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
}
@@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}
@@ -0,0 +1,11 @@
<?php
$getShortName = db("welding_consumables")->where("brend", $rowData->product_name)->first();
$weldingConsumables = db("welding_consumables")->where("short_name", $getShortName->short_name)->get();
$response = [];
foreach($weldingConsumables AS $weldingConsumable) {
if(trim($weldingConsumable->aws_class) !="") {
$response[] = "{$weldingConsumable->aws_class} {$weldingConsumable->aws_specification}";
}
}
//dd($weldingConsumables) ?>
@@ -0,0 +1,11 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->groupBy("work_type")
->get("work_type")
->pluck("work_type");
$response = $query;
?>
@@ -0,0 +1,32 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->get();
$matGroups = [];
foreach($query AS $q) {
$matGroups[] = explode("-", $q->mat_group_1)[1];
$matGroups[] = explode("-", $q->mat_group_2)[1];
}
$materials = db("materials");
if(!getesit("value", "")) {
$value = get("value");
$materials = $materials->where("steel_grade","like","%$value%");
}
if(count($matGroups)>0)
{
$response = $materials->whereIn("short_name", $matGroups)->groupBy("steel_grade")->get()->pluck("steel_grade")->toArray();
} else {
$response['error'] = "No data found in Naks Certificates data: Certificate No: $certificateNo Technology Category: {$rowData->technology_category}";
}
?>
@@ -0,0 +1,23 @@
<?php
$response = db("incoming_controls");
$notExcept = ['null', 'undefined', ''];
$ruGroups = explode(",", get("ru_group"));
$materials = db("materials")->whereIn("ru_group", $ruGroups)->select("steel_grade")->get()->pluck("steel_grade")->toArray();
if(!in_array(get("line"), $notExcept))
{
$response = $response->where("component_code", get("line"));
}
if(count($materials)>0) {
$response = $response->whereIn("material", $materials);
}
$response = $response->groupBy("certificate_no")
->get("certificate_no")
->pluck("certificate_no");
?>
@@ -0,0 +1,50 @@
<?php
if(!isset($rowData->naks_no) || $rowData->naks_no == "")
{
$response['error'] = "Plase select naks no";
} else {
$naksCertificates = explode(",", $rowData->naks_no);
$naksWelders = db("naks_welders")->whereIn("naks_certificate_no", $naksCertificates)->get();
$materials = [];
// Dönen her bir sonucu işle
foreach ($naksWelders as $welder) {
// material_1'den material_4'e kadar değerleri parçala ve diziye ekle
for ($i = 1; $i <= 4; $i++) {
$column = "material_$i";
if (!empty($welder->$column)) {
$explodedValues = explode(',', $welder->$column);
$materials = array_merge($materials, $explodedValues);
}
}
}
// `+` işaretine göre değerleri böl ve düz bir diziye dönüştür
$expandedMaterials = [];
foreach ($materials as $material) {
$splitMaterials = explode('+', $material); // '+' işaretine göre böl
foreach ($splitMaterials as $splitMaterial) {
$expandedMaterials[] = trim($splitMaterial); // Değerleri temizle ve diziye ekle
}
}
// Tekrar eden değerleri kaldır
$expandedMaterials = array_unique($expandedMaterials);
$steelGrades = db("materials")
->whereIn("main_material", $expandedMaterials)
->groupBy("steel_grade")
->get()
->pluck("steel_grade")
->toArray();
if (empty($steelGrades)) {
$response['error'] = "No materials found for main_materials: " . implode(", ", $expandedMaterials);
} else {
$response = $steelGrades;
}
}
?>
@@ -0,0 +1,51 @@
<?php
if(!isset($rowData->naks_no) || $rowData->naks_no == "")
{
$response['error'] = "Plase select naks no";
} else {
$naksCertificates = explode(",", $rowData->naks_no);
$naksWelders = db("naks_welders")->whereIn("naks_certificate_no", $naksCertificates)->get();
$materials = [];
// Dönen her bir sonucu işle
foreach ($naksWelders as $welder) {
// material_1'den material_4'e kadar değerleri parçala ve diziye ekle
for ($i = 1; $i <= 4; $i++) {
$column = "material_$i";
if (!empty($welder->$column)) {
$explodedValues = explode(',', $welder->$column);
$materials = array_merge($materials, $explodedValues);
}
}
}
// `+` işaretine göre değerleri böl ve düz bir diziye dönüştür
$expandedMaterials = [];
foreach ($materials as $material) {
$splitMaterials = explode('+', $material); // '+' işaretine göre böl
foreach ($splitMaterials as $splitMaterial) {
$expandedMaterials[] = trim($splitMaterial); // Değerleri temizle ve diziye ekle
}
}
// Tekrar eden değerleri kaldır
$expandedMaterials = array_unique($expandedMaterials);
$steelGrades = db("materials")
->whereIn("main_material", $expandedMaterials)
->groupBy("steel_grade")
->get()
->pluck("steel_grade")
->toArray();
if (empty($steelGrades)) {
$response['error'] = "No materials found for main_materials: " . implode(", ", $expandedMaterials);
} else {
$response = $steelGrades;
}
}
?>
@@ -0,0 +1,7 @@
<?php
$response = db("incoming_controls")
->where("component_code", get("line"))
->where("certificate_no", get("certificate_number"))
->get("heat_number")
->pluck("heat_number");
?>
@@ -0,0 +1,9 @@
<?php
//definition_en
if(isset($rowData->joint_type))
$response = db("joint_types")->where("short_name_en", $rowData->joint_type)->first();
else {
$response = [];
}
?>
@@ -0,0 +1,15 @@
<?php
if(!isset($rowData->grade_1) || $rowData->grade_1 == "") {
$response['error'] = "Please select grade 1";
return;
}
$grades = explode(",", $rowData->grade_1);
$response = db("materials")
->groupBy("ru_group")
->whereIn("steel_grade", $grades)
->get()
->pluck("ru_group")
->toArray();
?>
@@ -0,0 +1,15 @@
<?php
if(!isset($rowData->grade_2) || $rowData->grade_2 == "") {
$response['error'] = "Please select grade 2";
return;
}
$grades = explode(",", $rowData->grade_2);
$response = db("materials")
->groupBy("ru_group")
->whereIn("steel_grade", $grades)
->get()
->pluck("ru_group")
->toArray();
?>
@@ -0,0 +1,32 @@
<?php
$groups = [];
if(isset($rowData->material_group_1) && $rowData->material_group_1 != "") {
$groups[] = $rowData->material_group_1;
}
if(isset($rowData->material_group_2) && $rowData->material_group_2 != "") {
$groups[] = $rowData->material_group_2;
}
if(empty($groups)) {
$response = [];
} else {
$mainMaterials = db("materials")
->whereIn("ru_group", $groups)
->pluck("main_material")
->toArray();
$mainMaterials = array_filter($mainMaterials, function($value) {
return !is_null($value) && $value !== '';
});
$mainMaterials = array_unique($mainMaterials);
sort($mainMaterials);
if(count($mainMaterials) > 0){
$response = [implode("+", $mainMaterials)];
} else {
$response = [];
}
}
?>
@@ -0,0 +1,22 @@
<?php
$pqr = db("prosedure_qualification_records")->where("pqr_no", $rowData->pqr_no)->first();
$wpsGroups = [
$rowData->russian_standard_group_no_1,
$rowData->russian_standard_group_no_2
];
$pqrGroups = explode(",", $pqr->qualitication_group_of_parent_material);
$ruGroups = array_merge($wpsGroups, $pqrGroups);
$response = db("materials")
->whereIn("ru_group", $ruGroups
)
->groupBy("steel_grade")
->get("steel_grade")
->pluck("steel_grade")
->toArray();
?>
@@ -0,0 +1,20 @@
<?php
$naksCertificates = explode(",", get("naks"));
$naksWelders = db("naks_welders")
->where("welder_id", get("naks_id"))
->whereIn("naks_certificate_no", $naksCertificates)
->orderBy("period_of_validity", "DESC")
->get();
$response = [];
$response['welding_method'] = [];
foreach($naksWelders AS $naks)
{
$weldingMethod = db("welding_methods")->where("ru_short_name", $naks->process)->first();
$response['period_of_validity'] = $naks->period_of_validity;
$response['welding_method'][] = "{$weldingMethod->ru_short_name}/{$weldingMethod->en_welding_number}";
}
$response['welding_method'] = implode(",", $response['welding_method']);
?>
@@ -0,0 +1,7 @@
<?php
$response = db("naks_welders")
->where("welder_id", $rowData->naks_id)
->get("naks_certificate_no")
->pluck("naks_certificate_no")
->toArray();
?>
@@ -0,0 +1,43 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->orWhere("material_4", "like", "%" . $materials[0] . "+" . $materials[1] . "%")
->orWhere("material_4", "like", "%" . $materials[0] . "," . $materials[1] . "%")
->get();
$group = 4;
}
if($naks)
{
$min_outside_diameter = $naks->min('diameter_min_' . $group);
$start_min = $naks->max('diameter_min_' . $group);
$max_outside_diameter = $naks->max('diameter_max_' . $group);
$response['max_outside_diameter'] = $max_outside_diameter;
$response['min_outside_diameter'] = $min_outside_diameter;
$response['start_min'] = $start_min;
} else {
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,64 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
// Retrieve main_material values from materials table
$searchMaterials = [];
foreach($materials as $matGroup) {
$matRecord = db("materials")->where("ru_group", $matGroup)->first();
if($matRecord && !empty($matRecord->main_material)) {
$searchMaterials[] = $matRecord->main_material;
} else {
// Fallback: try to extract content in parentheses (e.g., M01 from 1(M01))
if(preg_match('/\((.*?)\)/', $matGroup, $matches)) {
$searchMaterials[] = $matches[1];
} else {
$searchMaterials[] = $matGroup;
}
}
}
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->where(function($subQuery) use($searchMaterials){
$m1 = $searchMaterials[0] ?? '';
$m2 = $searchMaterials[1] ?? '';
$subQuery->orWhere("material_4", "like", "%" . $m1 . "+" . $m2 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m1 . "," . $m2 . "%");
// Check reverse order as well to be safe
$subQuery->orWhere("material_4", "like", "%" . $m2 . "+" . $m1 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m2 . "," . $m1 . "%");
})
->get();
$group = 4;
}
if($naks && $naks->isNotEmpty()) {
// Tüm 'diameter_max_' değerlerini alıyoruz
$diameter_values = $naks->pluck('diameter_max_' . $group)->toArray();
// Eğer 'diameter_max_' değerlerinde sadece null varsa, $start_max'ı null olarak belirliyoruz
$start_max = in_array(null, $diameter_values, true) ? null : max($diameter_values);
$response['start_max'] = $start_max;
} else {
// Veri bulunamazsa hata mesajı
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,63 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
// Retrieve main_material values from materials table
$searchMaterials = [];
foreach($materials as $matGroup) {
$matRecord = db("materials")->where("ru_group", $matGroup)->first();
if($matRecord && !empty($matRecord->main_material)) {
$searchMaterials[] = $matRecord->main_material;
} else {
// Fallback: try to extract content in parentheses (e.g., M01 from 1(M01))
if(preg_match('/\((.*?)\)/', $matGroup, $matches)) {
$searchMaterials[] = $matches[1];
} else {
$searchMaterials[] = $matGroup;
}
}
}
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->where(function($subQuery) use($searchMaterials){
$m1 = $searchMaterials[0] ?? '';
$m2 = $searchMaterials[1] ?? '';
$subQuery->orWhere("material_4", "like", "%" . $m1 . "+" . $m2 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m1 . "," . $m2 . "%");
// Check reverse order as well to be safe
$subQuery->orWhere("material_4", "like", "%" . $m2 . "+" . $m1 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m2 . "," . $m1 . "%");
})
->get();
$group = 4;
}
if($naks)
{
$start_min = $naks->min('diameter_min_' . $group);
$response['start_min'] = $start_min;
} else {
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,43 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->orWhere("material_4", "like", "%" . $materials[0] . "+" . $materials[1] . "%")
->orWhere("material_4", "like", "%" . $materials[0] . "," . $materials[1] . "%")
->get();
$group = 4;
}
if($naks)
{
$min_thick = $naks->min('min_thick_' . $group);
$start_min = $naks->max('min_thick_' . $group);
$max_thick = $naks->max('max_thick_' . $group);
$response['max_thick'] = $max_thick;
$response['min_thick'] = $min_thick;
$response['start_min'] = $start_min;
} else {
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,64 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
// Retrieve main_material values from materials table
$searchMaterials = [];
foreach($materials as $matGroup) {
$matRecord = db("materials")->where("ru_group", $matGroup)->first();
if($matRecord && !empty($matRecord->main_material)) {
$searchMaterials[] = $matRecord->main_material;
} else {
// Fallback: try to extract content in parentheses (e.g., M01 from 1(M01))
if(preg_match('/\((.*?)\)/', $matGroup, $matches)) {
$searchMaterials[] = $matches[1];
} else {
$searchMaterials[] = $matGroup;
}
}
}
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->where(function($subQuery) use($searchMaterials){
$m1 = $searchMaterials[0] ?? '';
$m2 = $searchMaterials[1] ?? '';
$subQuery->orWhere("material_4", "like", "%" . $m1 . "+" . $m2 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m1 . "," . $m2 . "%");
// Check reverse order as well to be safe
$subQuery->orWhere("material_4", "like", "%" . $m2 . "+" . $m1 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m2 . "," . $m1 . "%");
})
->get();
$group = 4;
}
if($naks && $naks->isNotEmpty()) {
// Tüm 'max_thick' değerlerini alıyoruz
$thickness_values = $naks->pluck('max_thick_' . $group)->toArray();
// Eğer 'max_thick' değerlerinde sadece null varsa, $start_max'ı null olarak belirliyoruz
$start_max = in_array(null, $thickness_values, true) ? null : max($thickness_values);
$response['start_max'] = $start_max;
} else {
// Veri bulunamazsa hata mesajı
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,65 @@
<?php
$certificates = explode(",", get("value"));
if(!getisset("materials"))
{
$response['error'] = "Please select Material Group 2";
} else {
$materials = explode(",", get("materials"));
// Retrieve main_material values from materials table
$searchMaterials = [];
foreach($materials as $matGroup) {
$matRecord = db("materials")->where("ru_group", $matGroup)->first();
if($matRecord && !empty($matRecord->main_material)) {
$searchMaterials[] = $matRecord->main_material;
} else {
// Fallback: try to extract content in parentheses (e.g., M01 from 1(M01))
if(preg_match('/\((.*?)\)/', $matGroup, $matches)) {
$searchMaterials[] = $matches[1];
} else {
$searchMaterials[] = $matGroup;
}
}
}
if($materials[0] == $materials[1])
{
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->get();
$group = 1;
} else {
$naks = db("naks_welders")
->whereIn("naks_certificate_no", $certificates)
->where(function($subQuery) use($searchMaterials){
$m1 = $searchMaterials[0] ?? '';
$m2 = $searchMaterials[1] ?? '';
$subQuery->orWhere("material_4", "like", "%" . $m1 . "+" . $m2 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m1 . "," . $m2 . "%");
// Check reverse order as well to be safe
$subQuery->orWhere("material_4", "like", "%" . $m2 . "+" . $m1 . "%");
$subQuery->orWhere("material_4", "like", "%" . $m2 . "," . $m1 . "%");
})
->get();
$group = 4;
}
if($naks)
{
$start_min = $naks->min('min_thick_' . $group);
$response['start_min'] = $start_min;
} else {
$response['error'] = "Naks Certificate data not found in Naks Welder";
}
}
?>
@@ -0,0 +1,12 @@
<?php
$subContractors = db("subcontractors")
->where("job_description", "NDT")
->get();
$response = [];
foreach($subContractors AS $subContractor) {
$response[] ="{$subContractor->company_name_en}" ;
}
?>
@@ -0,0 +1,12 @@
<?php
$welders = db("welder_tests")
->groupBy("naks_id")
->get();
$response = [];
foreach($welders AS $welder) {
$response[] ="[{$welder->naks_id}] {$welder->name_surname}" ;
}
?>
@@ -0,0 +1,5 @@
<?php
$response = db("incoming_control_paints")->where([
'ral_code' => $rowData->ral_code_1,
'component_name' => $rowData->primer_coat_name_1,
])->get()->pluck("akt_number")->toArray(); ?>
@@ -0,0 +1,5 @@
<?php
$response = db("incoming_control_paints")->where([
'ral_code' => $rowData->ral_code_2,
'component_name' => $rowData->intermediate_coat_name_2,
])->get()->pluck("akt_number")->toArray(); ?>
@@ -0,0 +1,5 @@
<?php
$response = db("incoming_control_paints")->where([
'ral_code' => $rowData->ral_code_3,
'component_name' => $rowData->final_coat_name_3,
])->get()->pluck("akt_number")->toArray(); ?>
@@ -0,0 +1,5 @@
<?php
$columnName = "brend_name_" . get("no");
$response = db("incoming_control_paints")->where([
'brend_name' => $rowData->$columnName,
])->get()->pluck("certificate_no")->toArray(); ?>
@@ -0,0 +1,2 @@
<?php
$response = db("incoming_control_paints")->get()->pluck("certificate_no")->toArray(); ?>
@@ -0,0 +1,26 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->where("mat_group_1", "like", "%". $rowData->russian_standart_group_no ."%")
->where("mat_group_2", "like", "%". $rowData->russian_standart_group_no_2 ."%")
->first();
$weldingConsumables = db("welding_consumables");
if($query) {
$matGroup1 = explode("-", $query->mat_group_1)[1];
$matGroup2 = explode("-", $query->mat_group_2)[1];
$weldingConsumables->whereIn("short_name", [$matGroup1, $matGroup2]);
}
if(!getesit("value", "")) {
$value = get("value");
$weldingConsumables = $weldingConsumables->where("brend","like","%$value%");
}
$response = $weldingConsumables->get()->pluck("brend")->toArray();
?>
@@ -0,0 +1,17 @@
<?php
$valid_from = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->orderBy("valid_from", "DESC")
->first();
$valid_to = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->orderBy("valid_to", "ASC")
->first();
$date['valid_from'] = $valid_from->valid_from;
$date['valid_to'] = $valid_to->valid_to;
$response = $date;
?>
@@ -0,0 +1,8 @@
<?php
if(isset($rowData->pqr_no))
$response = db("prosedure_qualification_records")->where("pqr_no", $rowData->pqr_no)->first();
else {
$response = [];
}
?>
@@ -0,0 +1,40 @@
<?php
$from =(int) $rowData->p_no_from;
$to =(int) $rowData->p_no_to;
$incomingValue = $from."+".$to;
$query = db("materials")
->whereNotIn("ru_group", ['*', ''])
->where(function($q) use ($from, $to) {
$q->where("asme_number", $from)
->orWhere("asme_number", $to);
})
->groupBy("ru_group")
->get("ru_group")
->pluck("ru_group")->toArray();
$material_group_test_pieces = db("material_group_test_pieces")
->where("incoming_value", $incomingValue)
->get("provision_value")
->pluck("provision_value");
// Explode all strings by commas and flatten into a single array
$result = [];
foreach ($material_group_test_pieces as $piece) {
$items = explode(',', $piece);
$result = array_merge($result, $items);
}
// Remove duplicates and empty strings
// Optional: Sort the array for readability
$query = array_merge($query, $result);
$query = array_values(array_filter(array_unique($query)));
$response = $query;
?>
@@ -0,0 +1,109 @@
<?php
use App\Models\MaterialGroupMap;
$response = [];
// Step 1: Build search term from ru_material_group_1 and ru_material_group_2
$ru1 = trim($rowData->ru_material_group_1 ?? '');
$ru2 = trim($rowData->ru_material_group_2 ?? '');
// If ru_material_group_1 and ru_material_group_2 are different, combine them with "+"
if ($ru1 !== '' && $ru2 !== '' && $ru1 !== $ru2) {
$searchTerm = $ru1 . '+' . $ru2;
$searchTermReverse = $ru2 . '+' . $ru1;
} elseif ($ru1 !== '') {
$searchTerm = $ru1;
$searchTermReverse = $ru1;
} else {
$searchTerm = $ru2;
$searchTermReverse = $ru2;
}
// Step 2: Search for this term in qualified_materials column
// Find all records where qualified_materials contains this search term
$materialGroupMapRecords = MaterialGroupMap::where("qualified_materials", "like", "%{$searchTerm}%")
->orWhere("qualified_materials", "like", "%{$searchTermReverse}%")
->get();
if ($materialGroupMapRecords->isEmpty()) {
$response["error"] = "No matching qualified_materials found in MaterialGroupMap for: {$searchTerm}";
} else {
// Step 3: Get all material_names from matching records
$materialNamesToSearch = [];
foreach ($materialGroupMapRecords as $record) {
$materialName = trim($record->material_name ?? '');
if (!empty($materialName)) {
$materialNamesToSearch[] = $materialName;
}
}
if (empty($materialNamesToSearch)) {
$response["error"] = "No material_names found in matching records.";
} else {
// Step 4: Determine location
$location = "";
if ($rowData->type_of_joint == "S") {
$location = "Shop Active";
} elseif ($rowData->type_of_joint == "F") {
$location = "Field Active";
}
// Step 5: Query for welders - search each material_name in welder_tests
$welders = db("welder_tests")
->select("welder_tests.*")
->leftJoin('welder_locations', 'welder_tests.naks_id', '=', 'welder_locations.naks_id')
->where(function($query) use ($materialNamesToSearch) {
foreach ($materialNamesToSearch as $materialName) {
$materialName = trim($materialName);
if (empty($materialName)) continue;
// Each material_name can have format like "8(M11)" or "1(M01)+9(M11)"
// Search in material_group_1 and material_group_2 columns
$query->orWhere(function($q) use ($materialName) {
// Check if material_name contains "+"
if (strpos($materialName, '+') !== false) {
// Split by + and check both parts
$parts = array_map('trim', explode('+', $materialName));
if (count($parts) >= 2) {
$q->where(function($q2) use ($parts) {
$q2->where("material_group_1", "like", "%{$parts[0]}%")
->where("material_group_2", "like", "%{$parts[1]}%");
})->orWhere(function($q2) use ($parts) {
$q2->where("material_group_1", "like", "%{$parts[1]}%")
->where("material_group_2", "like", "%{$parts[0]}%");
});
}
} else {
// Single material - check both columns
$q->where("material_group_1", "like", "%{$materialName}%")
->orWhere("material_group_2", "like", "%{$materialName}%");
}
});
}
})
->groupBy("welder_tests.naks_id")
->where("welder_tests.status", "Active");
if(!is_stellar())
{
$u = u();
$welders = $welders->where("welder_locations.subcontructor", $u->subcontructer);
}
$welders = $welders->get();
// Step 6: Check if welders were found
if ($welders->isEmpty()) {
$response["error"] = "No welders found for specified criteria.
Criteria used:
- Type of Joint: {$rowData->type_of_joint} (mapped to Location: {$location})
- Search Term: {$searchTerm}
- Matching Material Names: " . implode(", ", $materialNamesToSearch);
} else {
foreach ($welders as $welder) {
$response[] = "[{$welder->naks_id}] {$welder->name_surname}";
}
}
}
}
?>
@@ -0,0 +1,109 @@
<?php
use App\Models\MaterialGroupMap;
$response = [];
// Step 1: Build search term from ru_material_group_1 and ru_material_group_2
$ru1 = trim($rowData->ru_material_group_1 ?? '');
$ru2 = trim($rowData->ru_material_group_2 ?? '');
// If ru_material_group_1 and ru_material_group_2 are different, combine them with "+"
if ($ru1 !== '' && $ru2 !== '' && $ru1 !== $ru2) {
$searchTerm = $ru1 . '+' . $ru2;
$searchTermReverse = $ru2 . '+' . $ru1;
} elseif ($ru1 !== '') {
$searchTerm = $ru1;
$searchTermReverse = $ru1;
} else {
$searchTerm = $ru2;
$searchTermReverse = $ru2;
}
// Step 2: Search for this term in qualified_materials column
// Find all records where qualified_materials contains this search term
$materialGroupMapRecords = MaterialGroupMap::where("qualified_materials", "like", "%{$searchTerm}%")
->orWhere("qualified_materials", "like", "%{$searchTermReverse}%")
->get();
if ($materialGroupMapRecords->isEmpty()) {
$response["error"] = "No matching qualified_materials found in MaterialGroupMap for: {$searchTerm}";
} else {
// Step 3: Get all material_names from matching records
$materialNamesToSearch = [];
foreach ($materialGroupMapRecords as $record) {
$materialName = trim($record->material_name ?? '');
if (!empty($materialName)) {
$materialNamesToSearch[] = $materialName;
}
}
if (empty($materialNamesToSearch)) {
$response["error"] = "No material_names found in matching records.";
} else {
// Step 4: Determine location
$location = "";
if ($rowData->type_of_joint == "S") {
$location = "Shop Active";
} elseif ($rowData->type_of_joint == "F") {
$location = "Field Active";
}
// Step 5: Query for welders - search each material_name in welder_tests
$welders = db("welder_tests")
->select("welder_tests.*")
->leftJoin('welder_locations', 'welder_tests.naks_id', '=', 'welder_locations.naks_id')
->where(function($query) use ($materialNamesToSearch) {
foreach ($materialNamesToSearch as $materialName) {
$materialName = trim($materialName);
if (empty($materialName)) continue;
// Each material_name can have format like "8(M11)" or "1(M01)+9(M11)"
// Search in material_group_1 and material_group_2 columns
$query->orWhere(function($q) use ($materialName) {
// Check if material_name contains "+"
if (strpos($materialName, '+') !== false) {
// Split by + and check both parts
$parts = array_map('trim', explode('+', $materialName));
if (count($parts) >= 2) {
$q->where(function($q2) use ($parts) {
$q2->where("material_group_1", "like", "%{$parts[0]}%")
->where("material_group_2", "like", "%{$parts[1]}%");
})->orWhere(function($q2) use ($parts) {
$q2->where("material_group_1", "like", "%{$parts[1]}%")
->where("material_group_2", "like", "%{$parts[0]}%");
});
}
} else {
// Single material - check both columns
$q->where("material_group_1", "like", "%{$materialName}%")
->orWhere("material_group_2", "like", "%{$materialName}%");
}
});
}
})
->groupBy("welder_tests.naks_id")
->where("welder_tests.status", "Active");
if(!is_stellar())
{
$u = u();
$welders = $welders->where("welder_locations.subcontructor", $u->subcontructer);
}
$welders = $welders->get();
// Step 6: Check if welders were found
if ($welders->isEmpty()) {
$response["error"] = "No welders found for specified criteria.
Criteria used:
- Type of Joint: {$rowData->type_of_joint} (mapped to Location: {$location})
- Search Term: {$searchTerm}
- Matching Material Names: " . implode(", ", $materialNamesToSearch);
} else {
foreach ($welders as $welder) {
$response[] = "[{$welder->naks_id}] {$welder->name_surname}";
}
}
}
}
?>
@@ -0,0 +1,7 @@
<?php
$response = db("materials")
->where("steel_grade", $rowData->material_no_1)
->whereNotIn("ru_group", ['','*'])
->groupBy("ru_group")
->get("ru_group")->pluck("ru_group");
?>
@@ -0,0 +1,7 @@
<?php
$response = db("materials")
->where("steel_grade", $rowData->material_no_2)
->whereNotIn("ru_group", ['','*'])
->groupBy("ru_group")
->get("ru_group")->pluck("ru_group");
?>
@@ -0,0 +1,10 @@
<?php
$pqr = db("prosedure_qualification_records")->where("pqr_no", $rowData->pqr_no)->first();
$groups = explode(",", $pqr->qualitication_group_of_parent_material);
$response = db("materials")
->whereIn("ru_group", $groups)
->whereNotIn("ru_group", ['','*'])
->groupBy("ru_group")
->get("ru_group")->pluck("ru_group");
?>
@@ -0,0 +1,17 @@
<?php
$naksNo = explode(",", $rowData->naks_no);
$naksWelders = db("naks_welders")->whereIn("naks_certificate_no", $naksNo)->get();
$response = [];
foreach($naksWelders AS $naksWelder) {
$devices = explode(",", $naksWelder->group_of_technical_device);
foreach($devices AS $device) {
if(!in_array($device, $response)) {
$response[] = $device;
}
}
}
?>
@@ -0,0 +1,28 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->get();
$matGroups = [];
foreach($query AS $q) {
$data = explode("-", $q->mat_group_1)[1];
if(!in_array($data, $matGroups)) {
$matGroups[] = $data;
}
}
$materials = db("materials");
if(!getesit("value", "")) {
$value = get("value");
$materials = $materials->where("steel_grade","like","%$value%");
}
$response = $materials->whereIn("short_name", $matGroups)->groupBy("steel_grade")->get()->pluck("steel_grade")->toArray();
?>
@@ -0,0 +1,29 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->get();
$matGroups = [];
foreach($query AS $q) {
$data = explode("-", $q->mat_group_2)[1];
if(!in_array($data, $matGroups)) {
$matGroups[] = $data;
}
}
$materials = db("materials");
if(!getesit("value", "")) {
$value = get("value");
$materials = $materials->where("steel_grade","like","%$value%");
}
$response = $materials
->whereIn("short_name", $matGroups)->groupBy("steel_grade")->get()->pluck("steel_grade");
?>
@@ -0,0 +1,507 @@
<?php
use Carbon\Carbon;
use App\Models\WelderTest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
// Generate unique log key for tracking this request
$logKey = 'WELDER_ASSIGNMENT_' . uniqid();
Log::debug("[$logKey] ===== WELDER ASSIGNMENT AUTOCMPLETE START =====");
$rowData = j(get("row"));
Log::debug("[$logKey] Input data received", ['rowData' => $rowData]);
$weldingMethod = explode(",", $rowData['wm']);
$reverseWeldingMethod = array_reverse($weldingMethod);
$reverseWeldingMethod = implode(",", $reverseWeldingMethod);
Log::debug("[$logKey] Welding method processed", [
'original' => $rowData['wm'],
'reversed' => $reverseWeldingMethod
]);
$response = [
'welders' => []
];
$welderQualitificationStatusExpiredDate = setting("WelderQualitificationStatusExpiredDate");
Log::debug("[$logKey] Welder qualification status expired date setting", [
'days' => $welderQualitificationStatusExpiredDate
]);
// Get qualified materials from material group map using exact combination
// If both materials are the same, use single material name
// If different materials, use combination with + sign
$isSameMaterial = ($rowData['ru_1'] === $rowData['ru_2']);
if ($isSameMaterial) {
// Same material on both sides - use single material
$materialCombination = $rowData['ru_1'];
} else {
// Different materials - use combination
$materialCombination = $rowData['ru_1'] . "+" . $rowData['ru_2'];
}
Log::debug("[$logKey] Material combination check", [
'ru_1' => $rowData['ru_1'],
'ru_2' => $rowData['ru_2'],
'is_same_material' => $isSameMaterial,
'material_combination' => $materialCombination
]);
$exactMatch = \App\Models\MaterialGroupMap::where("material_name", $materialCombination)->first();
$qualifiedCombinations = [];
if ($exactMatch && $exactMatch->qualified_materials) {
// Found exact combination match, use its qualified materials
$qualifiedMaterialsList = explode(",", $exactMatch->qualified_materials);
foreach ($qualifiedMaterialsList as $combo) {
$combo = trim($combo);
$parts = explode("+", $combo);
if (count($parts) == 2) {
// Different materials combination
$qualifiedCombinations[] = ['mat1' => trim($parts[0]), 'mat2' => trim($parts[1])];
} elseif (count($parts) == 1) {
// Single material (same on both sides)
$qualifiedCombinations[] = ['mat1' => trim($parts[0]), 'mat2' => trim($parts[0])];
}
}
}
// Always include the original combination
if ($isSameMaterial) {
// Same material - add only once (no reverse needed)
$qualifiedCombinations[] = ['mat1' => $rowData['ru_1'], 'mat2' => $rowData['ru_2']];
} else {
// Different materials - add both original and reverse
$qualifiedCombinations[] = ['mat1' => $rowData['ru_1'], 'mat2' => $rowData['ru_2']];
$qualifiedCombinations[] = ['mat1' => $rowData['ru_2'], 'mat2' => $rowData['ru_1']];
}
Log::debug("[$logKey] Material group map qualified materials", [
'ru_1' => $rowData['ru_1'],
'ru_2' => $rowData['ru_2'],
'is_same_material' => $isSameMaterial,
'material_combination' => $materialCombination,
'exact_match_found' => $exactMatch !== null,
'qualified_combinations' => $qualifiedCombinations,
'total_combinations' => count($qualifiedCombinations)
]);
$failedCriteria = [];
// 1. material_group eşleşmeleri (with qualified combinations)
Log::debug("[$logKey] Checking material group criteria", [
'ru_1' => $rowData['ru_1'],
'ru_2' => $rowData['ru_2'],
'qualified_combinations' => $qualifiedCombinations
]);
$materialGroupCheck = WelderTest::where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->exists();
Log::debug("[$logKey] Material group check result", [
'passed' => $materialGroupCheck,
'checked_combinations_count' => count($qualifiedCombinations)
]);
if (!$materialGroupCheck) {
$qualifiedCombosStr = implode(', ', array_map(function($c) { return $c['mat1'].'+'.$c['mat2']; }, $qualifiedCombinations));
$failedCriteria[] = "Material group mismatch - No matching record found in WPQ Follow Up module. Please check material_group_1 and material_group_2 columns for combinations: " . $qualifiedCombosStr;
Log::debug("[$logKey] Material group check FAILED", [
'ru_1' => $rowData['ru_1'],
'ru_2' => $rowData['ru_2'],
'qualified_combinations' => $qualifiedCombinations
]);
}
// 2. dia_min ve dia_max eşleşmeleri
Log::debug("[$logKey] Checking diameter criteria", [
'odmm' => $rowData['odmm']
]);
$diameterCheck = WelderTest::where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})->exists();
Log::debug("[$logKey] Diameter check result", [
'passed' => $diameterCheck
]);
if (!$diameterCheck) {
$failedCriteria[] = "Diameter range mismatch - No matching record found in WPQ Follow Up module. Please check dia_min and dia_max columns for diameter value: '{$rowData['odmm']}' mm";
Log::debug("[$logKey] Diameter check FAILED", [
'odmm' => $rowData['odmm']
]);
}
// 3. thk_min ve thk_max eşleşmeleri
Log::debug("[$logKey] Checking thickness criteria", [
'tck' => $rowData['tck']
]);
$thicknessCheck = WelderTest::where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})->exists();
Log::debug("[$logKey] Thickness check result", [
'passed' => $thicknessCheck
]);
if (!$thicknessCheck) {
$failedCriteria[] = "Thickness range mismatch - No matching record found in WPQ Follow Up module. Please check thk_min and thk_max columns for thickness value: '{$rowData['tck']}' mm";
Log::debug("[$logKey] Thickness check FAILED", [
'tck' => $rowData['tck']
]);
}
// 4. approval_date eşleşmesi
Log::debug("[$logKey] Checking approval date criteria", [
'welding_date' => $rowData['welding_date']
]);
$approvalDateCheck = WelderTest::whereDate("approval_date", "<=", $rowData['welding_date'])->exists();
Log::debug("[$logKey] Approval date check result", [
'passed' => $approvalDateCheck
]);
if (!$approvalDateCheck) {
$failedCriteria[] = "Approval date mismatch - No matching record found in WPQ Follow Up module. Please check approval_date column for dates on or before: '{$rowData['welding_date']}'";
Log::debug("[$logKey] Approval date check FAILED", [
'welding_date' => $rowData['welding_date']
]);
}
// 5. welding_method eşleşmesi
Log::debug("[$logKey] Checking welding method criteria", [
'original' => $rowData['wm'],
'reversed' => $reverseWeldingMethod
]);
$weldingMethodCheck = WelderTest::where(function($query) use($rowData, $reverseWeldingMethod) {
$query->orWhere("welding_method", $rowData['wm']);
$query->orWhere("welding_method", $reverseWeldingMethod);
})->exists();
Log::debug("[$logKey] Welding method check result", [
'passed' => $weldingMethodCheck
]);
if (!$weldingMethodCheck) {
$failedCriteria[] = "Welding method mismatch - No matching record found in WPQ Follow Up module. Please check welding_method column for values: '{$rowData['wm']}' or '{$reverseWeldingMethod}'";
Log::debug("[$logKey] Welding method check FAILED", [
'original' => $rowData['wm'],
'reversed' => $reverseWeldingMethod
]);
}
// Sonuçları kontrol et
Log::debug("[$logKey] All criteria checks completed", [
'total_failed' => count($failedCriteria),
'failed_criteria' => $failedCriteria
]);
if (empty($failedCriteria)) {
Log::debug("[$logKey] All criteria passed successfully");
} else {
// Eşleşmeyen kriterleri listele
// echo "Aşağıdaki kriterler eşleşmedi: " . implode(", ", $failedCriteria);
$response['error'] = implode(", ", $failedCriteria);
Log::debug("[$logKey] Criteria validation failed", [
'errors' => $failedCriteria
]);
}
Log::debug("[$logKey] Starting database query to fetch matching welders");
// Step 1: Base query with join
$baseQuery = WelderTest::leftJoin('naks_welders', function($join) {
$join->on('naks_welders.naks_certificate_no', '=', 'welder_tests.naks_no');
});
// Step 2: Material group criteria (with qualified combinations)
Log::debug("[$logKey] Step 1: Testing with MATERIAL GROUP only (including qualified combinations)");
$step1 = (clone $baseQuery)->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->count();
Log::debug("[$logKey] Step 1 result: $step1 records found");
// Step 3: Add diameter criteria
Log::debug("[$logKey] Step 2: Testing with MATERIAL GROUP + DIAMETER");
$step2 = (clone $baseQuery)->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})->count();
Log::debug("[$logKey] Step 2 result: $step2 records found (lost " . ($step1 - $step2) . " records)");
// Step 4: Add thickness criteria
Log::debug("[$logKey] Step 3: Testing with MATERIAL GROUP + DIAMETER + THICKNESS");
$step3 = (clone $baseQuery)->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})->where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})->count();
Log::debug("[$logKey] Step 3 result: $step3 records found (lost " . ($step2 - $step3) . " records)");
// Step 5: Add approval date criteria
Log::debug("[$logKey] Step 4: Testing with MATERIAL GROUP + DIAMETER + THICKNESS + APPROVAL DATE");
$step4 = (clone $baseQuery)->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})->where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})->whereDate("approval_date", "<=", $rowData['welding_date'])->count();
Log::debug("[$logKey] Step 4 result: $step4 records found (lost " . ($step3 - $step4) . " records)");
// Step 6: Add welding method criteria
Log::debug("[$logKey] Step 5: Testing with ALL CRITERIA (including WELDING METHOD)");
$step5 = (clone $baseQuery)->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", $combo['mat1'])
->where("material_group_2", $combo['mat2']);
});
}
})->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})->where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})->whereDate("approval_date", "<=", $rowData['welding_date'])
->where(function($query) use($rowData, $weldingMethod, $reverseWeldingMethod){
$query->orWhere("welding_method", $rowData['wm']);
$query->orWhere("welding_method", $reverseWeldingMethod);
})->count();
Log::debug("[$logKey] Step 5 result: $step5 records found (lost " . ($step4 - $step5) . " records)");
// Final query with all criteria
Log::debug("[$logKey] Final query: Executing with ALL CRITERIA + GROUP BY");
$data = WelderTest::leftJoin('naks_welders', function($join) {
$join->on('naks_welders.naks_certificate_no', '=', 'welder_tests.naks_no');
})
->where(function ($query) use ($qualifiedCombinations) {
foreach ($qualifiedCombinations as $combo) {
$query->orWhere(function($q) use ($combo) {
$q->where("material_group_1", "like", "%{$combo['mat1']}%")
->where("material_group_2", "like", "%{$combo['mat2']}%");
});
}
})
->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})
->where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})
->whereDate("approval_date", "<=", $rowData['welding_date'])
->where(function($query) use($rowData, $weldingMethod, $reverseWeldingMethod){
$query->orWhere("welding_method", $rowData['wm']);
$query->orWhere("welding_method", $reverseWeldingMethod);
})
->select('welder_tests.naks_id',
'welder_tests.material_group_1',
'welder_tests.material_group_2',
'welder_tests.dia_min',
'welder_tests.dia_max',
'welder_tests.thk_min',
'welder_tests.thk_max',
'welder_tests.approval_date',
'welder_tests.welding_method',
'welder_tests.naks_validity',
DB::raw('MIN(naks_welders.period_of_validity) as period_of_validity'))
->groupBy("welder_tests.material_group_1",
"welder_tests.material_group_2",
"welder_tests.naks_id",
"welder_tests.wpq_document_no",
"welder_tests.welding_method",
"welder_tests.naks_no")
->get();
Log::debug("[$logKey] Database query completed", [
'total_welders_found' => $data->count(),
'step_summary' => [
'step1_material_only' => $step1,
'step2_+_diameter' => $step2,
'step3_+_thickness' => $step3,
'step4_+_approval_date' => $step4,
'step5_+_welding_method' => $step5,
'final_with_groupby' => $data->count()
]
]);
// Add step summary to response for UI display
$response['step_summary'] = [
'step1_material_only' => $step1,
'step2_diameter' => $step2,
'step3_thickness' => $step3,
'step4_approval_date' => $step4,
'step5_welding_method' => $step5,
'final_with_groupby' => $data->count(),
'criteria_details' => [
'material_ru_1' => $rowData['ru_1'],
'material_ru_2' => $rowData['ru_2'],
'diameter_odmm' => $rowData['odmm'],
'thickness_tck' => $rowData['tck'],
'welding_date' => $rowData['welding_date'],
'welding_method' => $rowData['wm'],
'welding_method_reversed' => $reverseWeldingMethod
]
];
//dd($response);
$today = Carbon::now();
Log::debug("[$logKey] Starting welder processing loop", [
'today' => $today->toDateString(),
'total_welders_to_process' => $data->count()
]);
foreach($data AS $index => $d) {
Log::debug("[$logKey] Processing welder #" . ($index + 1), [
'naks_id' => $d->naks_id,
'naks_validity' => $d->naks_validity,
'period_of_validity' => $d->period_of_validity
]);
$naksValidity = Carbon::createFromFormat('Y-m-d', $d->naks_validity);
$remainingDays = $today->diffInDays($naksValidity, false);
$welder = $d->naks_id;
Log::debug("[$logKey] Checking welder assignment status for ISO", [
'welder' => $welder,
'iso_number' => $rowData['iso_number']
]);
$isWorkThisIsoNumber = db("weld_logs")->where("welder_1", "like", "%$welder%")->where("iso_number", $rowData['iso_number'])->first();
$isRealWorkThisIsoNumber = db("weld_logs")->where("real_welder_1", "like", "%$welder%")->where("iso_number", $rowData['iso_number'])->first();
if($isWorkThisIsoNumber) {
$welder = "🟡 $welder";
Log::debug("[$logKey] Welder already assigned to this ISO (yellow)", [
'welder' => $d->naks_id
]);
}
if($isRealWorkThisIsoNumber) {
$welder = "🔴 $welder";
Log::debug("[$logKey] Welder is real worker for this ISO (red)", [
'welder' => $d->naks_id
]);
}
if($remainingDays<10) {
$welder = "$welder";
Log::debug("[$logKey] Welder NAKS validity expiring soon", [
'welder' => $d->naks_id,
'remaining_days' => $remainingDays
]);
}
// Kaynak tarihinin geçerlilik kontrolü
$weldingDate = Carbon::parse($rowData['welding_date']);
$testWeldingDate = Carbon::parse($d->period_of_validity);
$expiredDate = $testWeldingDate->copy()->addDays($welderQualitificationStatusExpiredDate);
Log::debug("[$logKey] Checking welding date validity", [
'welder' => $d->naks_id,
'row_welding_date' => $weldingDate->toDateString(),
'test_welding_date' => $testWeldingDate->toDateString(),
'expired_date' => $expiredDate->toDateString(),
'qualification_expired_days' => $welderQualitificationStatusExpiredDate
]);
if($weldingDate->greaterThan($expiredDate)) {
$welder = "❌📅 $welder";
Log::debug("[$logKey] Welding date validity check FAILED", [
'welder' => $d->naks_id,
'row_welding_date' => $weldingDate->toDateString(),
'test_welding_date' => $testWeldingDate->toDateString()
]);
}
Log::debug("[$logKey] Final welder status", [
'welder_id' => $d->naks_id,
'final_display' => $welder,
'row_welding_date' => $weldingDate->toDateString(),
'test_welding_date' => $testWeldingDate->toDateString(),
'expired_date' => $expiredDate->toDateString()
]);
$response['welders'][] = $welder;
}
Log::debug("[$logKey] Welder processing completed", [
'total_welders_processed' => count($response['welders']),
'response' => $response
]);
Log::debug("[$logKey] ===== WELDER ASSIGNMENT AUTOCMPLETE END =====");
?>
@@ -0,0 +1,42 @@
<?php
use App\Models\WelderTest;
$rowData = j(get("row"));
$weldingMethod = explode(",", $rowData['wm']);
$reverseWeldingMethod = array_reverse($weldingMethod);
$reverseWeldingMethod = implode(",", $reverseWeldingMethod);
$response = WelderTest::where(function ($query) use ($rowData) {
$query->where("material_group_1", "like", "%{$rowData['ru_1']}%")
->where("material_group_2", "like", "%{$rowData['ru_2']}%")
->orWhere(function($query) use ($rowData) {
$query->where("material_group_1", "like", "%{$rowData['ru_2']}%")
->where("material_group_2", "like", "%{$rowData['ru_1']}%");
});
})
->where("dia_min", "<=", $rowData['odmm'])
->where(function($query) use($rowData) {
$query->orWhere("dia_max", ">=", $rowData['odmm']);
$query->orWhereNull("dia_max");
$query->orWhere("dia_max", 0);
})
->where("thk_min", "<=", $rowData['tck'])
->where(function($query) use($rowData) {
$query->orWhere("thk_max", ">=", $rowData['tck']);
$query->orWhere("thk_max", 0);
$query->orWhereNull("thk_max");
})
->whereDate("approval_date", "<=", $rowData['welding_date'])
->where(function($query) use($rowData, $weldingMethod, $reverseWeldingMethod){
$query->orWhere("welding_method", $rowData['wm']);
$query->orWhere("welding_method", $reverseWeldingMethod);
})
->where("naks_id", get("welder"))
->first();
if($response) {
$response->toArray();
}
?>
@@ -0,0 +1,18 @@
<?php
$naksCertificates = explode(",", $rowData->naks_no);
$naksWelderProceses = db("naks_welders")
->whereIn("naks_certificate_no", $naksCertificates)
->get()?->pluck('process')
->toArray();
$weldingMethods = db("welding_methods")->whereIn("ru_short_name", $naksWelderProceses)->get();
$response = [];
foreach($weldingMethods AS $weldingMethod)
{
$response[] = "{$weldingMethod->ru_short_name}/{$weldingMethod->en_welding_number}";
}
?>
@@ -0,0 +1,17 @@
<?php
$query = db("naks_certificates")
->whereIn("certificate_no", $certificateNo)
->where("technology_category", $rowData->technology_category)
->groupBy("welding_method")
->get("welding_method")
->pluck("welding_method")
->toArray();
$weldingMethods = db("welding_methods")->whereIn("ru_short_name", $query)->select("ru_short_name", "en_welding_number")->get();
$response = [];
foreach($weldingMethods AS $weldingMethod) {
$response[] = $weldingMethod->ru_short_name . '/' . $weldingMethod->en_welding_number;
}
?>
@@ -0,0 +1,11 @@
<?php
$pqr = db("prosedure_qualification_records")
->where("pqr_no", $rowData->pqr_no)
->first();
$date['start'] = $pqr->approved_date;
$response = $date;
?>
@@ -0,0 +1,6 @@
<?php
$response = db("w_p_s")
->where("details", get("wps"))
->first();
?>
@@ -0,0 +1,160 @@
<?php
if(!isset($rowData->material_group_gost) || $rowData->material_group_gost == "") {
$response['error'] = "Please select material group";
} elseif ($rowData->welding_method == "") {
$response['error'] = "Please select welding method";
} else {
$group1 = isset($rowData->material_group_1) ? trim($rowData->material_group_1) : null;
$group2 = (!empty($rowData->material_group_2)) ? trim($rowData->material_group_2) : $group1;
$naksCertificates = explode(",", $rowData->naks_no);
$welding_methods = explode(",", $rowData->welding_method);
// Debug info collection
$debug = [];
$debug['input'] = [
'material_group_1' => $rowData->material_group_1 ?? 'NULL',
'material_group_2' => $rowData->material_group_2 ?? 'NULL',
'group1_parsed' => $group1,
'group2_parsed' => $group2,
'welding_method' => $rowData->welding_method,
'welding_methods_array' => $welding_methods,
];
// Step 1: Check total WPS records with approved_date
$totalApproved = db("w_p_s")->whereNotNull("approved_date")->count();
$debug['step1_total_approved'] = $totalApproved;
// Step 2: Check material group filter only (group1 in col1, group2 in col2)
$materialFilter1 = db("w_p_s")
->whereNotNull("approved_date")
->where("russian_standard_group_no_1", "like", "%" . $group1 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group2 . "%")
->count();
$debug['step2_material_filter_g1_g2'] = $materialFilter1;
// Step 3: Check material group filter only (group2 in col1, group1 in col2)
$materialFilter2 = db("w_p_s")
->whereNotNull("approved_date")
->where("russian_standard_group_no_1", "like", "%" . $group2 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group1 . "%")
->count();
$debug['step3_material_filter_g2_g1'] = $materialFilter2;
// Step 4: Check combined material filter (OR condition)
$materialFilterCombined = db("w_p_s")
->whereNotNull("approved_date")
->where(function ($query) use ($group1, $group2) {
$query->where(function($q) use ($group1, $group2){
$q->where("russian_standard_group_no_1", "like", "%" . $group1 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group2 . "%");
})->orWhere(function($q) use ($group1, $group2){
$q->where("russian_standard_group_no_1", "like", "%" . $group2 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group1 . "%");
});
})
->count();
$debug['step4_material_filter_combined'] = $materialFilterCombined;
// Step 5: Check welding method filter only
$weldingFilter = db("w_p_s")
->whereNotNull("approved_date")
->where(function ($query) use ($welding_methods) {
if(count($welding_methods) == 1) {
foreach ($welding_methods as $method) {
$query->where("welding_process", $method);
}
} else {
foreach ($welding_methods as $method) {
$query->where("welding_process", "like", "%$method%");
}
}
})
->count();
$debug['step5_welding_filter_only'] = $weldingFilter;
// Step 6: Sample WPS records with the material groups to see actual values
$sampleRecords = db("w_p_s")
->whereNotNull("approved_date")
->select("details", "russian_standard_group_no_1", "russian_standard_group_no_2", "welding_process")
->limit(5)
->get()
->toArray();
$debug['step6_sample_records'] = $sampleRecords;
// Step 7: Full query
$results = db("w_p_s")
->where(function ($query) use ($group1, $group2) {
if($group1 && $group2) {
$query->where(function($q) use ($group1, $group2){
$q->where("russian_standard_group_no_1", "like", "%" . $group1 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group2 . "%");
})->orWhere(function($q) use ($group1, $group2){
$q->where("russian_standard_group_no_1", "like", "%" . $group2 . "%")
->where("russian_standard_group_no_2", "like", "%" . $group1 . "%");
});
} else {
$query->whereRaw("1 = 0");
}
})
->where(function ($query) use ($welding_methods) {
if(count($welding_methods) == 1)
{
foreach ($welding_methods as $method) {
$query->where("welding_process", $method);
}
} else {
foreach ($welding_methods as $method) {
$query->where("welding_process", "like", "%$method%");
}
}
})
->whereNotNull("approved_date")
->get("details")
->pluck("details")
->toArray();
$debug['step7_final_result_count'] = count($results);
if(empty($results)) {
// Build detailed error message
$errorMsg = "<b>No WPS found - Debug Report:</b><br><br>";
$errorMsg .= "<b>Input Values:</b><br>";
$errorMsg .= "- material_group_1: " . ($debug['input']['material_group_1']) . "<br>";
$errorMsg .= "- material_group_2: " . ($debug['input']['material_group_2']) . "<br>";
$errorMsg .= "- group1 (parsed): " . ($debug['input']['group1_parsed'] ?? 'NULL') . "<br>";
$errorMsg .= "- group2 (parsed): " . ($debug['input']['group2_parsed'] ?? 'NULL') . "<br>";
$errorMsg .= "- welding_method: " . ($debug['input']['welding_method']) . "<br>";
$errorMsg .= "- welding_methods (array): " . implode(", ", $debug['input']['welding_methods_array']) . "<br><br>";
$errorMsg .= "<b>Filter Results (step by step):</b><br>";
$errorMsg .= "1. Total approved WPS records: " . $debug['step1_total_approved'] . "<br>";
$errorMsg .= "2. Material filter (group1 in col1, group2 in col2): " . $debug['step2_material_filter_g1_g2'] . "<br>";
$errorMsg .= "3. Material filter (group2 in col1, group1 in col2): " . $debug['step3_material_filter_g2_g1'] . "<br>";
$errorMsg .= "4. Material filter combined (OR): " . $debug['step4_material_filter_combined'] . "<br>";
$errorMsg .= "5. Welding method filter only: " . $debug['step5_welding_filter_only'] . "<br>";
$errorMsg .= "7. Final combined result: " . $debug['step7_final_result_count'] . "<br><br>";
$errorMsg .= "<b>Analysis:</b><br>";
if($debug['step4_material_filter_combined'] == 0) {
$errorMsg .= "❌ PROBLEM: Material group filter returns 0 results. Check if WPS table has records with russian_standard_group_no_1/2 matching '" . $group1 . "' and '" . $group2 . "'<br>";
} elseif($debug['step5_welding_filter_only'] == 0) {
$errorMsg .= "❌ PROBLEM: Welding method filter returns 0 results. Check if WPS table has records with welding_process matching '" . implode(", ", $welding_methods) . "'<br>";
} else {
$errorMsg .= "❌ PROBLEM: Both filters work individually but combined they return 0. No WPS has BOTH the material groups AND the welding method.<br>";
}
$errorMsg .= "<br><b>Sample WPS Records (first 5):</b><br>";
foreach($debug['step6_sample_records'] as $record) {
$rec = (array)$record;
$errorMsg .= "- " . ($rec['details'] ?? 'N/A') . " | RU1: " . ($rec['russian_standard_group_no_1'] ?? 'N/A') . " | RU2: " . ($rec['russian_standard_group_no_2'] ?? 'N/A') . " | Process: " . ($rec['welding_process'] ?? 'N/A') . "<br>";
}
$response['error'] = $errorMsg;
} else {
$response = $results;
}
}
?>
@@ -0,0 +1,482 @@
<?php
$outsideDiameter = $rowData->outside_diameter_1;
$wallThickness = $rowData->wall_thickness_1;
$run = true;
if(is_null($wallThickness))
{
$errorText = "Wall Thickness 1 is required";
$run = false;
}
if(is_null($outsideDiameter))
{
$errorText = "Outside Diameter 1 is required";
$run = false;
}
if($run)
{
$errorText = "No matching WPS found with the following parameters and filter results:<br><br>";
$errorText .= "Input Parameters:<br>";
$errorText .= "- Outside Diameter: $outsideDiameter<br>";
$errorText .= "- Wall Thickness: $wallThickness<br>";
$errorText .= "- Joint Type: {$rowData->type_of_welds}<br>";
$errorText .= "- PWHT: {$rowData->pwht}<br>";
$errorText .= "- Material Groups 1: {$rowData->ru_material_group_1}<br>";
$errorText .= "- Material Groups 2: {$rowData->ru_material_group_2}<br><br>";
$errorText .= "Individual Filter Results:<br>";
$errorText .= "- Records with Work Type 'PIPE': {debug_results['pipe_count']}<br>";
$errorText .= "- Records matching Outside Diameter ($outsideDiameter): {debug_results['diameter_count']}<br>";
$errorText .= "- Records matching Wall Thickness ($wallThickness): {debug_results['thickness_count']}<br>";
$errorText .= "- Records matching Joint Type ({$rowData->type_of_welds}): {debug_results['joint_type_count']}<br>";
$errorText .= "- Records matching PWHT requirements: {debug_results['pwht_count']}<br>";
$errorText .= "- Records with matching PQR: {debug_results['pqr_count']}<br><br>";
$errorText .= "Combined Parameter Results:<br>";
$errorText .= "Two Parameter Combinations:<br>";
$errorText .= "- Outside Diameter + Wall Thickness: {debug_results['diameter_thickness_count']}<br>";
$errorText .= "- Outside Diameter + Joint Type: {debug_results['diameter_joint_count']}<br>";
$errorText .= "- Wall Thickness + Joint Type: {debug_results['thickness_joint_count']}<br><br>";
$errorText .= "Three Parameter Combinations:<br>";
$errorText .= "- Outside Diameter + Wall Thickness + Joint Type: {debug_results['main_params_count']}<br>";
$errorText .= "- Outside Diameter + Wall Thickness + PWHT: {debug_results['dimensions_pwht_count']}<br><br>";
$errorText .= "Four Parameter Combination:<br>";
$errorText .= "- Outside Diameter + Wall Thickness + Joint Type + PWHT: {debug_results['four_params_count']}<br><br>";
$errorText .= "All Parameters Combined:<br>";
$errorText .= "- All parameters (including PQR): {debug_results['all_params_count']}<br><br>";
$errorText .= "Analysis:<br>";
$errorText .= "1. Check the individual filter results to see which single parameter is most restrictive.<br>";
$errorText .= "2. Compare with combined results to understand how parameters interact.<br>";
$errorText .= "3. The combination with the lowest count indicates where the restriction is coming from.<br>";
$errorText .= "4. Compare four and five parameter results to see if PQR matching is the limiting factor.<br>";
$ruGroups1 = array_map('trim', explode(",", $rowData->ru_material_group_1));
$ruGroups2 = array_map('trim', explode(",", $rowData->ru_material_group_2));
$ruGroups = array_merge($ruGroups1, $ruGroups2);
$pqrs = db("prosedure_qualification_records");
foreach ($ruGroups1 as $ruGroup1) {
foreach ($ruGroups2 as $ruGroup2) {
// İkisi eşitse, sadece virgülle ayrılmış ve '+' içermeyen değerleri kontrol et
if ($ruGroup1 === $ruGroup2) {
$errorText .= "The Ru Group same.";
$pqrs->where(function($query) use ($ruGroup1) {
$query->where("qualitication_group_of_parent_material", "like", "$ruGroup1")
->where("qualitication_group_of_parent_material", "not like", "%+%")
->orWhere("qualitication_group_of_parent_material", "like", "$ruGroup1,%")
->orWhere("qualitication_group_of_parent_material", "like", "%,$ruGroup1")
->orWhere("qualitication_group_of_parent_material", "like", "%,$ruGroup1,%");
});
} else {
// '+' ile birleştirilmiş değerler için kontrol et
$pqrs->orWhere(function($query) use ($ruGroup1, $ruGroup2) {
$query->where("qualitication_group_of_parent_material", "like", "%$ruGroup1+$ruGroup2%")
->orWhere("qualitication_group_of_parent_material", "like", "%$ruGroup2+$ruGroup1%");
});
// Her iki grup için bağımsız koşulları kontrol et
$pqrs->orWhere(function($subQuery) use ($ruGroup1, $ruGroup2) {
$subQuery->where(function($q) use ($ruGroup1) {
$q->where("qualitication_group_of_parent_material", "like", "%$ruGroup1%")
->where("qualitication_group_of_parent_material", "not like", "%+$ruGroup1%")
->where("qualitication_group_of_parent_material", "not like", "%$ruGroup1+%");
})
->where(function($q) use ($ruGroup2) {
$q->where("qualitication_group_of_parent_material", "like", "%$ruGroup2%")
->where("qualitication_group_of_parent_material", "not like", "%+$ruGroup2%")
->where("qualitication_group_of_parent_material", "not like", "%$ruGroup2+%");
});
});
}
}
}
$pqrs = $pqrs->select("pqr_no")->get()->pluck("pqr_no")->toArray();
try {
$errorText .= "<br> Matching PQRs: " . implode(", ", $pqrs) . "<br>";
} catch (\Exception $e) {
}
if (empty(array_filter($ruGroups, function($value) {
return !empty($value); // Boş olmayan elemanları kontrol et
}))) {
$response['error'] = "The Ru Group must not be empty.";
} else {
$response = db("w_p_s")
->whereNotNull("approved_date")
/*
->where(function($query) use ($ruGroups1, $ruGroups2) {
foreach ($ruGroups1 as $key => $ruGroup1) {
$ruGroup1 = trim($ruGroup1);
// Eğer ruGroup1 boşsa işlemi atla
if ($ruGroup1 == "") {
continue;
}
// ruGroups2'deki karşılık gelen değer
$ruGroup2 = isset($ruGroups2[$key]) ? trim($ruGroups2[$key]) : null;
if ($ruGroup1 === $ruGroup2) {
// Eğer eşitlerse yalnızca russian_standard_group_no_1 içinde arat
$query->orWhere("russian_standard_group_no_1", "like", "%$ruGroup1%");
} else {
// Farklılarsa her iki sütunda birlikte arat
$query->orWhere(function($subQuery) use ($ruGroup1, $ruGroup2) {
$subQuery->where("russian_standard_group_no_1", "like", "%$ruGroup1%")
->where("russian_standard_group_no_2", "like", "%$ruGroup2%");
});
$query->orWhere(function($subQuery) use ($ruGroup1, $ruGroup2) {
$subQuery->where("russian_standard_group_no_2", "like", "%$ruGroup1%")
->where("russian_standard_group_no_1", "like", "%$ruGroup2%");
});
}
}
})
*/
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0);
$query->orWhere("max_thick", ">=", $wallThickness);
})
->where("joint_type", $rowData->type_of_welds);
if(count($pqrs)>0) {
$response = $response->whereIn("pqr_no", $pqrs);
} else {
$response = [];
$materials = implode(",", array_merge($ruGroups1, $ruGroups2));
$response['error'] = "No PQR data found for the selected qualification group of parent material.
Ru Groups: $materials";
return $response;
}
if($rowData->pwht == "YES") {
$response = $response
->where(function($query) {
$query->whereNotNull("pwht_temp_range")
->where("pwht_temp_range", "!=", "0")
->where("pwht_temp_range", "!=", "");
})
->where(function($query) {
$query->whereNotNull("pwht_min_time")
->where("pwht_min_time", "!=", "0")
->where("pwht_min_time", "!=", "");
});
} elseif($rowData->pwht == "NO") {
$response = $response
->where(function($query) {
$query->whereNull("pwht_temp_range")
->orWhere("pwht_temp_range", "0")
->orWhere("pwht_temp_range", "");
})
->where(function($query) {
$query->whereNull("pwht_min_time")
->orWhere("pwht_min_time", "0")
->orWhere("pwht_min_time", "");
});
}
if(strpos($rowData->piping_type ?? '', "Support") !== false) {
$response = $response->where("work_type", "like", "%Support%");
} else {
$response = $response
->where("work_type", "like", "%PIPE%")
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0);
$query->orWhere("max_outside_diameter", ">=", $outsideDiameter);
});
}
$response = $response->get(["details"])
->pluck("details");
// Her bir filtreyi ayrı ayrı test edelim
$debugResults = [];
// Temel sorgu
$baseQuery = db("w_p_s")->whereNotNull("approved_date");
// PIPE kontrolü
$pipeCheck = clone $baseQuery;
$pipeCheck = $pipeCheck->where("work_type", "like", "%PIPE%")->count();
if(strpos($rowData->piping_type ?? '', "Support") !== false) {
$baseQuery = $baseQuery->where("work_type", "like", "%Support%");
$debugResults['diameter_count'] = 0;
} else {
// Dış çap kontrolü
$diameterCheck = clone $baseQuery;
$diameterCheck = $diameterCheck->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})->count();
$debugResults['diameter_count'] = $diameterCheck;
}
$debugResults['pipe_count'] = $pipeCheck;
// Et kalınlığı kontrolü
$thicknessCheck = clone $baseQuery;
$thicknessCheck = $thicknessCheck->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
})->count();
$debugResults['thickness_count'] = $thicknessCheck;
// Kaynak tipi kontrolü
$jointTypeCheck = clone $baseQuery;
$jointTypeCheck = $jointTypeCheck->where("joint_type", $rowData->type_of_welds)->count();
$debugResults['joint_type_count'] = $jointTypeCheck;
// PWHT kontrolü
$pwhtCheck = clone $baseQuery;
if($rowData->pwht == "YES") {
$pwhtCheck = $pwhtCheck->where(function($query) {
$query->whereNotNull("pwht_temp_range")
->where("pwht_temp_range", "!=", "0")
->where("pwht_temp_range", "!=", "");
})->where(function($query) {
$query->whereNotNull("pwht_min_time")
->where("pwht_min_time", "!=", "0")
->where("pwht_min_time", "!=", "");
});
} elseif($rowData->pwht == "NO") {
$pwhtCheck = $pwhtCheck->where(function($query) {
$query->whereNull("pwht_temp_range")
->orWhere("pwht_temp_range", "0")
->orWhere("pwht_temp_range", "");
})->where(function($query) {
$query->whereNull("pwht_min_time")
->orWhere("pwht_min_time", "0")
->orWhere("pwht_min_time", "");
});
}
$debugResults['pwht_count'] = $pwhtCheck->count();
// PQR kontrolü
$debugResults['pqr_count'] = count($pqrs);
// İkili kombinasyonlar
// Dış çap + Et kalınlığı
$diameterThicknessCheck = clone $baseQuery;
$diameterThicknessCheck = $diameterThicknessCheck
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
})->count();
$debugResults['diameter_thickness_count'] = $diameterThicknessCheck;
// Dış çap + Kaynak tipi
$diameterJointCheck = clone $baseQuery;
$diameterJointCheck = $diameterJointCheck
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})
->where("joint_type", $rowData->type_of_welds)
->count();
$debugResults['diameter_joint_count'] = $diameterJointCheck;
// Et kalınlığı + Kaynak tipi
$thicknessJointCheck = clone $baseQuery;
$thicknessJointCheck = $thicknessJointCheck
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
})
->where("joint_type", $rowData->type_of_welds)
->count();
$debugResults['thickness_joint_count'] = $thicknessJointCheck;
// Üçlü kombinasyonlar
// Dış çap + Et kalınlığı + Kaynak tipi
$mainParamsCheck = clone $baseQuery;
$mainParamsCheck = $mainParamsCheck
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
})
->where("joint_type", $rowData->type_of_welds)
->count();
$debugResults['main_params_count'] = $mainParamsCheck;
// Dış çap + Et kalınlığı + PWHT
$dimensionsPwhtCheck = clone $baseQuery;
$dimensionsPwhtCheck = $dimensionsPwhtCheck
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
});
if($rowData->pwht == "YES") {
$dimensionsPwhtCheck = $dimensionsPwhtCheck
->where(function($query) {
$query->whereNotNull("pwht_temp_range")
->where("pwht_temp_range", "!=", "0")
->where("pwht_temp_range", "!=", "");
})
->where(function($query) {
$query->whereNotNull("pwht_min_time")
->where("pwht_min_time", "!=", "0")
->where("pwht_min_time", "!=", "");
});
} elseif($rowData->pwht == "NO") {
$dimensionsPwhtCheck = $dimensionsPwhtCheck
->where(function($query) {
$query->whereNull("pwht_temp_range")
->orWhere("pwht_temp_range", "0")
->orWhere("pwht_temp_range", "");
})
->where(function($query) {
$query->whereNull("pwht_min_time")
->orWhere("pwht_min_time", "0")
->orWhere("pwht_min_time", "");
});
}
$debugResults['dimensions_pwht_count'] = $dimensionsPwhtCheck->count();
// Dörtlü kombinasyonlar
// Dış çap + Et kalınlığı + Kaynak tipi + PWHT
$fourParamsCheck = clone $baseQuery;
$fourParamsCheck = $fourParamsCheck
->where("min_outside_diameter", "<=", $outsideDiameter)
->where(function($query) use($outsideDiameter) {
$query->orWhere("max_outside_diameter", 0)
->orWhere("max_outside_diameter", ">=", $outsideDiameter);
})
->where("min_thick", "<=", $wallThickness)
->where(function($query) use($wallThickness) {
$query->orWhere("max_thick", 0)
->orWhere("max_thick", ">=", $wallThickness);
})
->where("joint_type", $rowData->type_of_welds);
if($rowData->pwht == "YES") {
$fourParamsCheck = $fourParamsCheck
->where(function($query) {
$query->whereNotNull("pwht_temp_range")
->where("pwht_temp_range", "!=", "0")
->where("pwht_temp_range", "!=", "");
})
->where(function($query) {
$query->whereNotNull("pwht_min_time")
->where("pwht_min_time", "!=", "0")
->where("pwht_min_time", "!=", "");
});
} elseif($rowData->pwht == "NO") {
$fourParamsCheck = $fourParamsCheck
->where(function($query) {
$query->whereNull("pwht_temp_range")
->orWhere("pwht_temp_range", "0")
->orWhere("pwht_temp_range", "");
})
->where(function($query) {
$query->whereNull("pwht_min_time")
->orWhere("pwht_min_time", "0")
->orWhere("pwht_min_time", "");
});
}
$debugResults['four_params_count'] = $fourParamsCheck->count();
// Beşli kombinasyon (Tüm parametreler)
// Dış çap + Et kalınlığı + Kaynak tipi + PWHT + PQR
$allParamsCheck = clone $fourParamsCheck;
if(count($pqrs) > 0) {
$allParamsCheck = $allParamsCheck->whereIn("pqr_no", $pqrs);
}
$debugResults['all_params_count'] = $allParamsCheck->count();
// Debug bilgilerini response'a ekleyelim
if(count($response) == 0) {
// Debug sonuçlarını errorText içindeki placeholder'lar ile değiştirelim
$errorText = str_replace(
[
"{debug_results['pipe_count']}",
"{debug_results['diameter_count']}",
"{debug_results['thickness_count']}",
"{debug_results['joint_type_count']}",
"{debug_results['pwht_count']}",
"{debug_results['pqr_count']}",
"{debug_results['diameter_thickness_count']}",
"{debug_results['diameter_joint_count']}",
"{debug_results['thickness_joint_count']}",
"{debug_results['main_params_count']}",
"{debug_results['dimensions_pwht_count']}",
"{debug_results['four_params_count']}",
"{debug_results['all_params_count']}"
],
[
$debugResults['pipe_count'],
$debugResults['diameter_count'],
$debugResults['thickness_count'],
$debugResults['joint_type_count'],
$debugResults['pwht_count'],
$debugResults['pqr_count'],
$debugResults['diameter_thickness_count'],
$debugResults['diameter_joint_count'],
$debugResults['thickness_joint_count'],
$debugResults['main_params_count'],
$debugResults['dimensions_pwht_count'],
$debugResults['four_params_count'],
$debugResults['all_params_count']
],
$errorText
);
$response['error'] = $errorText;
}
}
} else {
$response['error'] = $errorText;
}
?>
@@ -0,0 +1,4 @@
<?php
$response = db("welder_tests")->select("naks_id")->groupBy("naks_id")->get()->pluck("naks_id");
?>
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreBeveelingRequest;
use App\Http\Requests\UpdateBeveelingRequest;
use App\Models\Beveeling;
class BeveelingController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreBeveelingRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreBeveelingRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Beveeling $beveeling
* @return \Illuminate\Http\Response
*/
public function show(Beveeling $beveeling)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Beveeling $beveeling
* @return \Illuminate\Http\Response
*/
public function edit(Beveeling $beveeling)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateBeveelingRequest $request
* @param \App\Models\Beveeling $beveeling
* @return \Illuminate\Http\Response
*/
public function update(UpdateBeveelingRequest $request, Beveeling $beveeling)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Beveeling $beveeling
* @return \Illuminate\Http\Response
*/
public function destroy(Beveeling $beveeling)
{
//
}
}
@@ -0,0 +1,6 @@
<?php
// Procedure Qualification Records specific clone logic
if($table == "prosedure_qualification_records") {
$data['pqr_no'] = $data['pqr_no'] . "-clone";
}
@@ -0,0 +1,6 @@
<?php
// WPS specific clone logic
if($table == "w_p_s") {
$data['details'] = $data['details'] . "-clone";
}
@@ -0,0 +1,113 @@
<?php
// Weld Logs specific clone logic
if($table == "weld_logs") {
unset($data['fit_up_date']);
unset($data['welding_date']);
unset($data['wps_no']);
unset($data['wps_approval_date']);
unset($data['welding_method']);
unset($data['welding_materials_1']);
unset($data['welding_materials_1_lot_no']);
unset($data['welding_materials_1_certificate_no']);
unset($data['welding_materials_2']);
unset($data['welding_materials_2_lot_no']);
unset($data['welding_materials_2_certificate_no']);
unset($data['welding_materials_3']);
unset($data['welding_materials_3_lot_no']);
unset($data['welding_materials_3_certificate_no']);
unset($data['real_welder_1']);
unset($data['real_welder_2']);
unset($data['mechanic_supervisor']);
unset($data['mechanic_supervisor_control_date']);
unset($data['welding_supervisor']);
unset($data['welding_supervisor_control_date']);
unset($data['welder_1']);
unset($data['welder_2']);
unset($data['certificate_no_1']);
unset($data['wpq_report_1']);
unset($data['certificate_no_2']);
unset($data['wpq_report_2']);
unset($data['vt_request_no']);
unset($data['vt_request_date']);
unset($data['vt_report']);
unset($data['vt_test_date']);
unset($data['test_laboratory_vt']);
unset($data['vt_result']);
unset($data['pt_request_no']);
unset($data['pt_request_date']);
unset($data['pt_report']);
unset($data['pt_test_date']);
unset($data['pt_test_lab']);
unset($data['test_laboratory_pt']);
unset($data['rt_request_no']);
unset($data['rt_request_date']);
unset($data['rt_report']);
unset($data['rt_test_date']);
unset($data['test_laboratory_rt']);
unset($data['rt_result']);
unset($data['ut_request_no']);
unset($data['ut_type']);
unset($data['ut_request_date']);
unset($data['ut_report']);
unset($data['ut_test_date']);
unset($data['ut_test_lab']);
unset($data['ut_result']);
unset($data['mt_request_no']);
unset($data['mt_request_date']);
unset($data['mt_report']);
unset($data['mt_test_date']);
unset($data['test_laboratory_mt']);
unset($data['mt_result']);
unset($data['pmi_request_no']);
unset($data['pmi_request_date']);
unset($data['test_report_no']);
unset($data['pmi_test_date']);
unset($data['test_laboratory_pmi']);
unset($data['pmi']);
unset($data['pmi_result']);
unset($data['pwht_request_no']);
unset($data['pwht_request_date']);
unset($data['pwht_date']);
unset($data['pwht_diagram_number']);
unset($data['diagram_number_pwht']);
unset($data['no_of_pwht_report']);
unset($data['test_laboratory_pwht']);
unset($data['pwht_operator_name']);
unset($data['pwht_operator_id']);
unset($data['ht_request_no']);
unset($data['ht_request_date']);
unset($data['ht_test_report_no']);
unset($data['ht_test_date']);
unset($data['test_laboratory_ht']);
unset($data['ht_result']);
unset($data['ferrite_request_no']);
unset($data['ferrite_request_date']);
unset($data['ferrite_report_no']);
unset($data['ferrite_test_date']);
unset($data['test_laboratory_ferrite']);
unset($data['ferrite_result']);
// Spool Area Release için ek unset işlemleri
unset($data['clearance_no']);
unset($data['clearance_date']);
unset($data['ndt_release_date']);
unset($data['register_paint_no']);
unset($data['register_date']);
unset($data['ndt_release_no']);
unset($data['paint_release_date']);
unset($data['paint_release_no']);
unset($data['spool_zone']);
unset($data['spool_ndt_check']);
unset($data['spool_paint_check']);
unset($data['spool_ndt_check_no']);
unset($data['ndt_release_check']);
unset($data['spool_ndt_check_date']);
unset($data['paint_release_check']);
unset($data['spool_remarks']);
$data['spool_status'] = "Waiting";
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreColorSystemRequest;
use App\Http\Requests\UpdateColorSystemRequest;
use App\Models\ColorSystem;
class ColorSystemController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreColorSystemRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreColorSystemRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\ColorSystem $colorSystem
* @return \Illuminate\Http\Response
*/
public function show(ColorSystem $colorSystem)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\ColorSystem $colorSystem
* @return \Illuminate\Http\Response
*/
public function edit(ColorSystem $colorSystem)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateColorSystemRequest $request
* @param \App\Models\ColorSystem $colorSystem
* @return \Illuminate\Http\Response
*/
public function update(UpdateColorSystemRequest $request, ColorSystem $colorSystem)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\ColorSystem $colorSystem
* @return \Illuminate\Http\Response
*/
public function destroy(ColorSystem $colorSystem)
{
//
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers;
use App\Models\ColumnComment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ColumnCommentController extends Controller
{
/**
* Save a new column comment
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function saveComment(Request $request)
{
// Validate the request
$validated = $request->validate([
'table_name' => 'required|string',
'record_id' => 'required|numeric',
'column_name' => 'required|string',
'comment' => 'required|string',
'column_value' => 'nullable|string',
]);
try {
// Create the comment
$comment = ColumnComment::create([
'table_name' => $validated['table_name'],
'record_id' => $validated['record_id'],
'column_name' => $validated['column_name'],
'comment' => $validated['comment'],
'column_value' => $validated['column_value'] ?? null,
'user_id' => u()->id,
'ip_address' => $request->ip(),
]);
return response()->json([
'success' => true,
'message' => 'Comment saved successfully',
'comment' => $comment
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to save comment: ' . $e->getMessage()
], 500);
}
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreColumnDescriptionRequest;
use App\Http\Requests\UpdateColumnDescriptionRequest;
use App\Models\ColumnDescription;
class ColumnDescriptionController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreColumnDescriptionRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreColumnDescriptionRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\ColumnDescription $columnDescription
* @return \Illuminate\Http\Response
*/
public function show(ColumnDescription $columnDescription)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\ColumnDescription $columnDescription
* @return \Illuminate\Http\Response
*/
public function edit(ColumnDescription $columnDescription)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateColumnDescriptionRequest $request
* @param \App\Models\ColumnDescription $columnDescription
* @return \Illuminate\Http\Response
*/
public function update(UpdateColumnDescriptionRequest $request, ColumnDescription $columnDescription)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\ColumnDescription $columnDescription
* @return \Illuminate\Http\Response
*/
public function destroy(ColumnDescription $columnDescription)
{
//
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreConstructionPaintLogRequest;
use App\Http\Requests\UpdateConstructionPaintLogRequest;
use App\Models\ConstructionPaintLog;
class ConstructionPaintLogController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreConstructionPaintLogRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreConstructionPaintLogRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\ConstructionPaintLog $constructionPaintLog
* @return \Illuminate\Http\Response
*/
public function show(ConstructionPaintLog $constructionPaintLog)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\ConstructionPaintLog $constructionPaintLog
* @return \Illuminate\Http\Response
*/
public function edit(ConstructionPaintLog $constructionPaintLog)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateConstructionPaintLogRequest $request
* @param \App\Models\ConstructionPaintLog $constructionPaintLog
* @return \Illuminate\Http\Response
*/
public function update(UpdateConstructionPaintLogRequest $request, ConstructionPaintLog $constructionPaintLog)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\ConstructionPaintLog $constructionPaintLog
* @return \Illuminate\Http\Response
*/
public function destroy(ConstructionPaintLog $constructionPaintLog)
{
//
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
namespace App\Http\Controllers;
use App;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Contents;
use Artesaos\SEOTools\Facades\SEOMeta;
use Artesaos\SEOTools\Facades\OpenGraph;
use Artesaos\SEOTools\Facades\TwitterCard;
use Artesaos\SEOTools\Facades\JsonLd;
// OR
use Artesaos\SEOTools\Facades\SEOTools;
class ContentsController extends Controller
{
public function index()
{
$url = env("APP_URL");
$title = __('Web Site Title');
return view('index');
}
public function store(Request $request) {
$content = new Contents;
$content -> slug = str_slug($request->title,"-");
$content -> title = $request->title;
$content -> html = $request -> html;
$content -> uid = session("uid");
$content -> json = $request;
$content -> cover = $cover;
$content -> pics = $pics;
}
public function default_lang(Request $request, string $lang="tr",string $slug) {
//App::setLocale($lang);
return ContentsController::default($request,$slug,$lang);
}
public function default(Request $request, string $slug,string $lang="tr")
{
if($slug =="/" || $slug == "index" || $slug=="home" || $slug=="") {
$view = "index";
} else {
$view = "default";
}
$c = null; // Contents::where('slug', $slug)->first();
if(!is_null($c) && isset($type)) {
$c->type = $type;
}
$url = __('URL');
try {
$c->title = __($c->title);
$c->html = __($c->html);
} catch (\Exception $e) {
}
$var = [
'language' => [],
'c' => "",
'slug'=>$slug,
'menu'=>[]
];
try{
return view($slug,$var);
} catch(\Exception $e) {
if($c=="") {
return abort(404);
} else {
$var = [
'language' => $language,
'c' => "",
'slug'=>$slug,
'menu'=>$menu
];
try{
return view($c->slug,$var);
} catch(\Exception $e) {
try {
return view(str_slug($c->type,"-"),$var);
} catch (\Exception $e) {
return view($view, $var);
}
}
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreCounterRequest;
use App\Http\Requests\UpdateCounterRequest;
use App\Models\Counter;
class CounterController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreCounterRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreCounterRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Counter $counter
* @return \Illuminate\Http\Response
*/
public function show(Counter $counter)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Counter $counter
* @return \Illuminate\Http\Response
*/
public function edit(Counter $counter)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateCounterRequest $request
* @param \App\Models\Counter $counter
* @return \Illuminate\Http\Response
*/
public function update(UpdateCounterRequest $request, Counter $counter)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Counter $counter
* @return \Illuminate\Http\Response
*/
public function destroy(Counter $counter)
{
//
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Models\CurrentType;
use App\Http\Requests\StoreCurrentTypeRequest;
use App\Http\Requests\UpdateCurrentTypeRequest;
class CurrentTypeController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreCurrentTypeRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreCurrentTypeRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\CurrentType $currentType
* @return \Illuminate\Http\Response
*/
public function show(CurrentType $currentType)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\CurrentType $currentType
* @return \Illuminate\Http\Response
*/
public function edit(CurrentType $currentType)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateCurrentTypeRequest $request
* @param \App\Models\CurrentType $currentType
* @return \Illuminate\Http\Response
*/
public function update(UpdateCurrentTypeRequest $request, CurrentType $currentType)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\CurrentType $currentType
* @return \Illuminate\Http\Response
*/
public function destroy(CurrentType $currentType)
{
//
}
}
@@ -0,0 +1,664 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use App\Models\DocumentManagerPermission;
class CustomElfinderController extends Controller
{
/**
* Cache for access results to avoid repeated calculations
*/
private static $accessCache = [];
/**
* Cached user instance
*/
private static $currentUser = null;
/**
* Custom access control for Elfinder
*
* @param string $attr - Operation type (read, write, rm, etc.)
* @param string $path - File/folder path
* @param array $data - Additional data
* @param object $volume - Volume object
* @return bool - Whether access is allowed
*/
public static function access($attr, $path, $data, $volume)
{
// 1. Check access cache (Critical Optimization)
// Using path + attr as key
$cacheKey = $path . '#' . $attr;
if (isset(self::$accessCache[$cacheKey])) {
return self::$accessCache[$cacheKey];
}
// 2. Get User (Cached)
if (self::$currentUser === null) {
self::$currentUser = Auth::user();
}
$user = self::$currentUser;
// If user is not authenticated, deny access
if (!$user) {
return self::$accessCache[$cacheKey] = false;
}
// ---------------------------------------------------------
// GLOBAL EXCLUSION LIST (Hidden for EVERYONE - INCLUDING ADMINS)
// ---------------------------------------------------------
// Moved BEFORE admin check to ensure these are hidden for admins too
// Get folder name from path early (needed for checks)
$folderName = self::getFolderNameFromPath($path);
// Default exclusions if setting is empty
$defaultExclusions = [
'.tmb',
'cache',
'register_creator_errors',
'test',
'NewFolder 1',
'files',
'document-templates',
'Excel Form Templates',
'Archive'
];
// Get from settings - Cache this if possible or trust Laravel's config cache
static $globalExcludedFolders = null;
if ($globalExcludedFolders === null) {
$settingsJson = setting('global_excluded_folders');
$settingsExclusions = [];
if (!empty($settingsJson)) {
$settingsExclusions = json_decode($settingsJson, true);
}
$globalExcludedFolders = !empty($settingsExclusions) ? $settingsExclusions : $defaultExclusions;
}
// Check if it is a root folder (no slashes in normalized path)
$isRootFolder = strpos($folderName, '/') === false;
if ($isRootFolder && in_array($folderName, $globalExcludedFolders)) {
// Hide from list
if ($attr === 'hidden') {
return self::$accessCache[$cacheKey] = true;
}
// Block read access to effectively hide contents too
if ($attr === 'read') {
return self::$accessCache[$cacheKey] = false;
}
}
// ---------------------------------------------------------
// 3. Admin Optimization (Fast Track)
if ($user->level == "Admin") {
// Validate path for security (fast check)
if (strpos($path, '..') !== false) {
return self::$accessCache[$cacheKey] = false;
}
// Special handling for operations that need false (NOT locked/hidden)
if ($attr === 'hidden' || $attr === 'locked') {
return self::$accessCache[$cacheKey] = false; // false means NOT hidden/locked (writable/deletable)
}
return self::$accessCache[$cacheKey] = true; // true means ALLOW operation (read/write/rm/etc)
}
// Validate path for security (Normal users)
if (!self::validatePath($path)) {
Log::warning("Path validation failed", ['path' => $path, 'user' => $user->level]);
return self::$accessCache[$cacheKey] = false;
}
// folderName already retrieved above for global exclusion check
// $folderName = self::getFolderNameFromPath($path);
// ---------------------------------------------------------
// GLOBAL EXCLUSION LIST (Moved to top)
// ---------------------------------------------------------
// For non-admin users, handle system folders based on operation
if (self::isSystemFolder($folderName)) {
// For these operations, explicitly hide/lock system folders
if (in_array($attr, ['read', 'write', 'upload', 'mkdir', 'mkfile', 'rename', 'copy', 'move', 'rm', 'edit'])) {
return self::$accessCache[$cacheKey] = false; // Block these operations
}
// For hidden check, return true to hide
if ($attr === 'hidden') {
return self::$accessCache[$cacheKey] = true; // Hide system folders from non-admin
}
// For locked check, return true to lock
if ($attr === 'locked') {
return self::$accessCache[$cacheKey] = true; // Lock system folders for non-admin
}
// For other operations, return null (use default behavior)
return self::$accessCache[$cacheKey] = null;
}
// For other users, implement basic permission checks
$module = "document-manager";
// Check basic module permission first
// Cache basic permissions
static $modulePermissions = [];
if (!isset($modulePermissions[$user->level])) {
$modulePermissions[$user->level] = [
'read' => isAuth($module, "read"),
'write' => isAuth($module, "write"),
'modify' => isAuth($module, "modify")
];
}
$hasModuleRead = $modulePermissions[$user->level]['read'];
$hasModuleWrite = $modulePermissions[$user->level]['write'];
$hasModuleModify = $modulePermissions[$user->level]['modify'];
/*
// DEBUG: Check if PTO user has basic permissions
if ($user->level === 'Manager (PTO)') {
error_log("PTO User Basic Permissions - Read: " . ($hasModuleRead ? 'YES' : 'NO') . ", Write: " . ($hasModuleWrite ? 'YES' : 'NO') . ", Modify: " . ($hasModuleModify ? 'YES' : 'NO'));
}
*/
// Debug logging - enabled for testing
/*
Log::info("Elfinder Access Check", [
'user_level' => $user->level,
'user_id' => $user->id ?? 'unknown',
'attr' => $attr,
'path' => $path,
'folder_name' => $folderName,
'module' => $module,
'permission_separation' => 'write_edit_separated',
'is_dir' => is_dir($path),
'is_file' => is_file($path),
'path_exists' => file_exists($path),
'has_module_read' => $hasModuleRead,
'has_module_write' => $hasModuleWrite,
'has_module_modify' => $hasModuleModify
]);
*/
switch($attr) {
case 'read':
// For folders, check if user should see this folder at all
if (is_dir($path)) {
// First check if this is a system folder for non-admin users
if ($user->level !== "Admin" && self::isSystemFolder($folderName)) {
return self::$accessCache[$cacheKey] = false; // Block system folders from non-admin
}
return self::$accessCache[$cacheKey] = self::checkFolderVisibility($user, $folderName, $module);
} else {
// For files, check if parent folder is system folder for non-admin
if ($user->level !== "Admin" && self::isSystemFolder($folderName)) {
return self::$accessCache[$cacheKey] = false; // Block files in system folders from non-admin
}
// For files, check read permission
return self::checkFolderReadPermission($user, $folderName, $module);
}
case 'write':
case 'upload':
case 'mkdir':
case 'mkfile':
case 'rename':
case 'copy':
case 'move':
// Check if user has write permission for this specific folder
$writeAuth = self::checkFolderWritePermission($user, $folderName, $module);
// Log::info("Write permission check", ['result' => $writeAuth, 'folder' => $folderName, 'operation' => $attr]);
return self::$accessCache[$cacheKey] = $writeAuth;
case 'edit':
// Special handling for edit operations - check file type and edit permission
$editAuth = self::checkFileEditPermission($user, $path, $module);
// Log::info("Edit permission check", ['result' => $editAuth, 'path' => $path, 'operation' => $attr]);
return self::$accessCache[$cacheKey] = $editAuth;
case 'rm':
// Check if user has delete permission for this specific folder
$rmAuth = self::checkFolderDeletePermission($user, $folderName, $module);
// Log::info("Remove permission check", ['result' => $rmAuth, 'folder' => $folderName]);
// Additional protection for critical files
if (self::isCriticalFile($path)) {
// Log::info("Critical file protection - delete blocked", ['path' => $path]);
return self::$accessCache[$cacheKey] = false;
}
return self::$accessCache[$cacheKey] = $rmAuth;
case 'hidden':
// Check if folder should be hidden from user
// For non-admin users, hide system folders
if ($user->level !== "Admin") {
if (self::isSystemFolder($folderName)) {
return self::$accessCache[$cacheKey] = true; // Hide system folders from non-admin
}
}
// Check general visibility
return self::$accessCache[$cacheKey] = !self::checkFolderVisibility($user, $folderName, $module);
default:
// For other operations, return null for default behavior
/*
Log::info("Default permission check", [
'attr' => $attr,
'folder' => $folderName,
'user_level' => $user->level
]);
*/
return self::$accessCache[$cacheKey] = null; // Use Elfinder's default behavior
}
}
/**
* Cache for folder names to improve performance
*/
private static $folderNameCache = [];
/**
* Get folder name from path with improved path normalization (supports subfolders)
*/
private static function getFolderNameFromPath($path)
{
// Check cache first
if (isset(self::$folderNameCache[$path])) {
return self::$folderNameCache[$path];
}
$originalPath = $path;
// Normalize path separators
$path = str_replace('\\', '/', $path);
// Remove common path prefixes (case-insensitive)
$path = preg_replace('#^.*?/storage/documents/?#i', '', $path);
$path = preg_replace('#^.*?/storage/office-server/?#i', '', $path);
$path = preg_replace('#^.*?/public/storage/documents/?#i', '', $path);
$path = preg_replace('#^.*?/public/storage/office-server/?#i', '', $path);
$path = preg_replace('#^.*?/Applications/MAMP/htdocs/stellar/public/storage/documents/?#i', '', $path);
$path = preg_replace('#^.*?/Applications/MAMP/htdocs/stellar/public/storage/office-server/?#i', '', $path);
// Additional path patterns for Elfinder
$path = preg_replace('#^.*?/storage/documents$#i', '', $path);
$path = preg_replace('#^.*?/storage/documents/$#i', '', $path);
// Clean up any remaining leading/trailing slashes
$path = trim($path, '/');
// For subfolder support, return the full relative path instead of just first folder
// This allows us to check permissions for specific subfolders
$folderName = $path;
// Log path processing for debugging
/*
Log::info("Path processing", [
'original_path' => $originalPath,
'normalized_path' => $path,
'folder_name' => $folderName,
'has_slashes' => strpos($folderName, '/') !== false,
'is_empty' => empty($folderName)
]);
*/
self::$folderNameCache[$originalPath] = $folderName;
return $folderName;
}
/**
* Check if folder should be visible to user (for folder listing)
*/
private static function checkFolderVisibility($user, $folderName, $module)
{
// Root path - always visible if user has basic read permission
if (empty($folderName)) {
return isAuth($module, "read");
}
// Office Server files - only Admin can see
if (self::isOfficeServerPath($folderName)) {
return $user->level == "Admin";
}
// Check if this is a system folder - only Admin can see
if (self::isSystemFolder($folderName)) {
return $user->level == "Admin";
}
// Check if user has any permission for this folder (read, write, or edit)
// This includes both explicit permissions and inherited permissions
$hasReadPermission = self::checkFolderReadPermission($user, $folderName, $module);
$hasWritePermission = self::checkFolderWritePermission($user, $folderName, $module);
$hasEditPermission = self::checkFolderEditPermission($user, $folderName, $module);
return $hasReadPermission || $hasWritePermission || $hasEditPermission;
}
/**
* Check if user has read permission for specific folder
*/
private static function checkFolderReadPermission($user, $folderName, $module)
{
// Root path - allow access if user has basic read permission
if (empty($folderName)) {
return isAuth($module, "read");
}
// Office Server files - only Admin can read
if (self::isOfficeServerPath($folderName)) {
return $user->level == "Admin";
}
// Check dynamic permission using cached/recursive check
return self::checkDynamicPermission($user->level, $folderName, 'read');
}
/**
* Check if user has write permission for specific folder
* Write permission covers: upload, create, rename, copy, move, delete
*/
private static function checkFolderWritePermission($user, $folderName, $module)
{
// Office Server files - only Admin can write
if (self::isOfficeServerPath($folderName)) {
return $user->level == "Admin";
}
// Check dynamic permission using cached/recursive check
return self::checkDynamicPermission($user->level, $folderName, 'write');
}
/**
* Check if user has edit permission for specific folder
* Edit permission covers: edit file content only (not file operations)
*/
private static function checkFolderEditPermission($user, $folderName, $module)
{
// Office Server files - only Admin can edit
if (self::isOfficeServerPath($folderName)) {
return $user->level == "Admin";
}
// Check dynamic permission using cached/recursive check
return self::checkDynamicPermission($user->level, $folderName, 'edit');
}
/**
* Check if user has delete permission for specific folder
*/
private static function checkFolderDeletePermission($user, $folderName, $module)
{
// Office Server files - only Admin can delete
if (self::isOfficeServerPath($folderName)) {
return $user->level == "Admin";
}
// Delete permission uses write permission (write includes delete operations)
return self::checkFolderWritePermission($user, $folderName, $module);
}
/**
* Check if user can edit specific file types based on edit permission
* Edit permission covers: edit file content only (not file operations)
*/
private static function checkFileEditPermission($user, $path, $module)
{
// Get file extension
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
// Define editable file types
$editableFileTypes = ['pdf', 'xlsx', 'xlsb', 'csv', 'doc', 'docx', 'txt'];
// Check if file type is editable
if (!in_array($extension, $editableFileTypes)) {
Log::info("File type not editable", ['extension' => $extension, 'path' => $path]);
return false;
}
// Check folder-based permissions for edit
$folderName = self::getFolderNameFromPath($path);
// Check dynamic permission using cached/recursive check
return self::checkDynamicPermission($user->level, $folderName, 'edit');
}
/**
* Static cache for permissions during a single request
* Structure: ['UserLevel' => ['folder/path' => ['read' => bool, 'write' => bool]]]
*/
private static $permissionsCache = [];
/**
* Load all permissions for a user level into static cache
*/
private static function loadPermissionsIntoCache($userLevel)
{
if (isset(self::$permissionsCache[$userLevel])) {
return;
}
// Get all permissions from Model (which uses Redis/File Cache)
// This returns a collection grouped by folder_path
$rawPermissions = \App\Models\DocumentManagerPermission::getUserLevelPermissions($userLevel);
$formatted = [];
foreach ($rawPermissions as $path => $permissions) {
$formatted[$path] = [];
foreach ($permissions as $perm) {
// Store the boolean value directly
$formatted[$path][$perm->permission_type] = $perm->is_allowed;
}
}
self::$permissionsCache[$userLevel] = $formatted;
Log::info("Permissions loaded into static cache", [
'user_level' => $userLevel,
'folder_count' => count($formatted)
]);
}
/**
* Check dynamic permission using cached data with inheritance
*/
private static function checkDynamicPermission($userLevel, $folderName, $permissionType)
{
// Ensure cache is loaded
self::loadPermissionsIntoCache($userLevel);
// 1. Check for explicit permission on this folder
if (isset(self::$permissionsCache[$userLevel][$folderName])) {
$folderPerms = self::$permissionsCache[$userLevel][$folderName];
if (isset($folderPerms[$permissionType])) {
// Explicit permission found (either true or false)
// Return exactly what is set, do not inherit if explicitly set
return $folderPerms[$permissionType];
}
}
// 2. If no explicit permission, check parent folder inheritance
// BUT ONLY for subfolders, not for main folders
$parentFolder = self::getParentFolder($folderName);
if ($parentFolder && $parentFolder !== $folderName && strpos($folderName, '/') !== false) {
$inherited = self::checkDynamicPermission($userLevel, $parentFolder, $permissionType);
if ($inherited) {
// Log only if allowed via inheritance (to reduce noise)
// Log::info("Permission granted via inheritance", ['folder' => $folderName, 'parent' => $parentFolder, 'type' => $permissionType]);
}
return $inherited;
}
// 3. No explicit permission and no parent - default deny
return false;
}
/**
* Get parent folder from a folder path
*/
private static function getParentFolder($folderPath)
{
// Remove trailing slash if exists
$folderPath = rtrim($folderPath, '/');
// Split by slash
$parts = explode('/', $folderPath);
// If only one part, no parent
if (count($parts) <= 1) {
return null;
}
// Remove last part to get parent
array_pop($parts);
return implode('/', $parts);
}
/**
* Check if file is a critical system file that should be protected
*/
private static function isCriticalFile($path)
{
$fileName = basename($path);
$criticalFiles = [
'index.php',
'config.php',
'.htaccess',
'web.config',
'robots.txt',
'sitemap.xml',
'.env',
'composer.json',
'composer.lock',
'package.json',
'yarn.lock'
];
return in_array(strtolower($fileName), array_map('strtolower', $criticalFiles));
}
/**
* Validate file path for security
*/
private static function validatePath($path)
{
// Check for path traversal attempts
if (strpos($path, '..') !== false || strpos($path, '//') !== false) {
Log::warning("Path traversal attempt detected", ['path' => $path]);
return false;
}
// Check for suspicious file extensions
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$dangerousExtensions = ['php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'bat', 'cmd', 'sh', 'js'];
if (in_array($extension, $dangerousExtensions)) {
Log::warning("Dangerous file extension detected", ['path' => $path, 'extension' => $extension]);
return false;
}
return true;
}
/**
* Get file size in human readable format
*/
private static function formatFileSize($bytes)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, 2) . ' ' . $units[$i];
}
/**
* Check if path is in Office Server directory
*/
private static function isOfficeServerPath($folderName)
{
// Check if the path contains office-server
return strpos($folderName, 'office-server') !== false ||
strpos($folderName, 'Office_Server') !== false ||
strpos($folderName, 'office_server') !== false;
}
/**
* Check if path is a network drive path
*/
private static function isNetworkDrivePath($path)
{
// Check for network drive patterns
$networkPatterns = [
'/^\\\\/', // Windows UNC path
'/^\/\/[^\/]+/', // Unix network path
'/^smb:\/\//', // SMB protocol
'/^cifs:\/\//', // CIFS protocol
];
foreach ($networkPatterns as $pattern) {
if (preg_match($pattern, $path)) {
return true;
}
}
return false;
}
/**
* Check if folder is a system folder (only visible to Admin)
* System folders are defined in config/document-folders.php
* Uses static cache for performance optimization
*/
private static function isSystemFolder($folderName)
{
// Static cache to avoid repeated config reads
static $exactMatches = null;
static $regexPatterns = null;
static $cache = [];
// Return cached result if available
if (isset($cache[$folderName])) {
return $cache[$folderName];
}
// Load patterns once
if ($exactMatches === null) {
$exactMatches = config('document-folders.system_patterns.exact', []);
$regexPatterns = config('document-folders.system_patterns.regex', []);
}
// Check exact matches first (faster)
if (in_array($folderName, $exactMatches)) {
$cache[$folderName] = true;
return true;
}
// Check regex patterns
foreach ($regexPatterns as $pattern) {
if (preg_match($pattern, $folderName)) {
$cache[$folderName] = true;
return true;
}
}
$cache[$folderName] = false;
return false;
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Models\CutList;
use App\Http\Requests\StoreCutListRequest;
use App\Http\Requests\UpdateCutListRequest;
class CutListController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreCutListRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreCutListRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\CutList $cutList
* @return \Illuminate\Http\Response
*/
public function show(CutList $cutList)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\CutList $cutList
* @return \Illuminate\Http\Response
*/
public function edit(CutList $cutList)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateCutListRequest $request
* @param \App\Models\CutList $cutList
* @return \Illuminate\Http\Response
*/
public function update(UpdateCutListRequest $request, CutList $cutList)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\CutList $cutList
* @return \Illuminate\Http\Response
*/
public function destroy(CutList $cutList)
{
//
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Models\Defect;
use App\Http\Requests\StoreDefectRequest;
use App\Http\Requests\UpdateDefectRequest;
class DefectController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreDefectRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreDefectRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Defect $defect
* @return \Illuminate\Http\Response
*/
public function show(Defect $defect)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Defect $defect
* @return \Illuminate\Http\Response
*/
public function edit(Defect $defect)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateDefectRequest $request
* @param \App\Models\Defect $defect
* @return \Illuminate\Http\Response
*/
public function update(UpdateDefectRequest $request, Defect $defect)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Defect $defect
* @return \Illuminate\Http\Response
*/
public function destroy(Defect $defect)
{
//
}
}
@@ -0,0 +1,94 @@
<?php
$id = $request['key'];
// Get the line list record before deletion
$lineList = $oldData;
$lineNumber = $lineList->line_no ?? null;
$area = $lineList->unit ?? null;
if ($lineNumber) {
// Delete from paint_follow_ups where primer coating has not started
$deletedFollowUps = db('paint_follow_ups')
->where(
[
'line' => $lineNumber,
'area' => $area,
]
)
->whereNull('primer_coating_start_date')
->whereNull('primer_coating_finish_date')
->whereNull('start_intermediate_date2')
->whereNull('finish_intermediate_date2')
->whereNull('final_coat_start_date3')
->whereNull('final_coat_finish_date3')
->delete();
// Delete from construction_paint_logs (spool-based, where painting has not started)
// Get all spools for this line from weld_logs
$spools = db("weld_logs")
->where("line_number", $lineNumber)
->whereNotNull('spool_number')
->where('spool_number', '!=', '')
->distinct()
->pluck('spool_number');
$deletedConstructionLogs = 0;
if ($spools->isNotEmpty()) {
// Spool-based deletion
$deletedConstructionLogs = db('construction_paint_logs')
->where('line', $lineNumber)
->whereIn('spool', $spools)
->whereNull('blasting_date')
->whereNull('blasting_finish_date')
->whereNull('painting_date_1')
->whereNull('painting_finish_date_1')
->whereNull('painting_date_2')
->whereNull('painting_finish_date_2')
->whereNull('painting_date_3')
->whereNull('painting_finish_date_3')
->delete();
Log::debug("Construction Paint Logs deleted (spool-based)", [
'line' => $lineNumber,
'spool_count' => $spools->count(),
'deleted_count' => $deletedConstructionLogs
]);
} else {
// Fallback to unit-based deletion if no spools found
$deletedConstructionLogs = db('construction_paint_logs')
->where([
'line' => $lineNumber,
'unit' => $area,
])
->whereNull('blasting_date')
->whereNull('blasting_finish_date')
->whereNull('painting_date_1')
->whereNull('painting_finish_date_1')
->whereNull('painting_date_2')
->whereNull('painting_finish_date_2')
->whereNull('painting_date_3')
->whereNull('painting_finish_date_3')
->delete();
Log::debug("Construction Paint Logs deleted (unit-based fallback)", [
'line' => $lineNumber,
'unit' => $area,
'deleted_count' => $deletedConstructionLogs
]);
}
spoolStatusChanger($lineNumber);
Log::info("Deleted related paint records for line: $lineNumber (Line List ID: $id)", [
'deleted_follow_ups' => $deletedFollowUps,
'deleted_construction_logs' => $deletedConstructionLogs,
'spool_based' => $spools->isNotEmpty()
]);
} else {
Log::error("Line number not found for line list ID: $id");
}
?>
@@ -0,0 +1,10 @@
<?php
$uniqueFields = [
'line_number' => $oldData->line,
'support_code' => $oldData->component_code_id,
];
echo db('supports')->where($uniqueFields)->delete();
dump("delete trigger");
dump($oldData);
?>
@@ -0,0 +1,28 @@
<?php
$id = $request['key'];
// Get the paint matrix record before deletion
$paintMatrix = db('paint_matrices')->where('id', $id)->first();
if ($paintMatrix) {
// Delete from paint_follow_ups
db('paint_follow_ups')
->where([
'line' => $paintMatrix->line,
'area' => $paintMatrix->area,
])
->whereNull('primer_coating_start_date') // Only delete if primer coating start date is null
->delete();
// Delete from construction_paint_logs
db('construction_paint_logs')
->where([
'line' => $paintMatrix->line,
'unit' => $paintMatrix->area,
])
->whereNull('painting_date_1') // Only delete if primer coating start date is null
->delete();
dump("Deleted related records for paint matrix ID: $id");
}
?>
@@ -0,0 +1,10 @@
<?php
$uniqueFields = [
'line' => $oldData->line_number,
'component_code_id' => $oldData->support_code,
];
echo db('m_t_o_s')->where($uniqueFields)->delete();
dump("delete trigger");
dump($oldData);
?>
@@ -0,0 +1,42 @@
<?php
// Get the test_pack_base_status that is being deleted
$testPackBaseStatus = db("test_pack_base_statuses")->where("id", $request['key'])->first();
if ($testPackBaseStatus) {
try {
$testPackageNo = $testPackBaseStatus->test_package_no;
// Check if there are any remaining test_pack_base_statuses records with the same test_package_no
$remainingCount = db("test_pack_base_statuses")
->where("test_package_no", $testPackageNo)
->where("id", "!=", $request['key']) // Exclude the current record being deleted
->count();
// Only delete test_packages if no other test_pack_base_statuses records exist for this test_package_no
if ($remainingCount == 0) {
// Check if there are any weld_logs records with this test_package_no
$weldLogsCount = db("weld_logs")
->where("test_package_no", $testPackageNo)
->count();
if ($weldLogsCount > 0) {
dump("Cannot delete test_packages. Found $weldLogsCount weld_logs records for test_package_no: $testPackageNo");
} else {
$deletedCount = db("test_packages")
->where("test_package_number", $testPackageNo)
->delete();
dump("No remaining test_pack_base_statuses for test_package_no: $testPackageNo. Deleted $deletedCount test_packages records.");
}
} else {
dump("Found $remainingCount remaining test_pack_base_statuses for test_package_no: $testPackageNo. Keeping test_packages records.");
}
} catch (\Throwable $th) {
dump("Error processing test_packages deletion: " . $th->getMessage());
Log::error("DeleteTrigger test_pack_base_statuses error: " . $th->getMessage());
}
} else {
dump("Test pack base status not found with id: " . $request['key']);
}
?>
@@ -0,0 +1,30 @@
<?php
// Get the test package that is being deleted
$testPackage = db("test_packages")->where("id", $request['key'])->first();
if ($testPackage) {
try {
// Check if there are any weld_logs records with this test_package_number
$weldLogsCount = db("weld_logs")
->where("test_package_no", $testPackage->test_package_number)
->count();
if ($weldLogsCount > 0) {
dump("Cannot delete test_pack_base_statuses. Found $weldLogsCount weld_logs records for test_package_no: " . $testPackage->test_package_number);
} else {
// Delete related test_pack_base_statuses records based on test_package_number
$deletedCount = db("test_pack_base_statuses")
->where("test_package_no", $testPackage->test_package_number)
->delete();
dump("Deleted $deletedCount test_pack_base_statuses records for test_package_no: " . $testPackage->test_package_number);
}
} catch (\Throwable $th) {
dump("Error deleting test_pack_base_statuses: " . $th->getMessage());
Log::error("DeleteTrigger test_packages error: " . $th->getMessage());
}
} else {
dump("Test package not found with id: " . $request['key']);
}
?>
@@ -0,0 +1,38 @@
<?php
if(isset($oldData->test_package_no))
{
$testPackWhereData = [
'test_package_number' => $oldData->test_package_no
];
$testPackBaseStatusWhereData = [
'test_package_no' => $oldData->test_package_no,
'drawing_no' => $oldData->iso_number,
];
// Önce weld_logs tablosunda aynı test_package_no var mı kontrol et
$weldLogCount = db("weld_logs")
->where(['test_package_no' => $oldData->test_package_no])
->count();
if ($weldLogCount > 0) {
Log::info("{$oldData->test_package_no} nolu test package weld_logs tablosunda mevcut, silme işlemi yapılmadı.");
} else {
$testPackagesChanged = db("test_packages")
->where($testPackWhereData)
->delete();
$tpBaseChanged = db("test_pack_base_statuses")
->where($testPackBaseStatusWhereData)
->delete();
Log::info("{$oldData->test_package_no} nolu test package {$oldData->iso_number} iso number dan silindi\ntpBaseDeleted: $tpBaseChanged\ntestPackagesDeleted: $testPackagesChanged\n");
}
} else {
Log::error("Weld log testp package no not found for ISO number: {$oldData->iso_number}");
}
spoolStatusChanger($oldData->iso_number);
?>
@@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreDeletedJointRequest;
use App\Http\Requests\UpdateDeletedJointRequest;
use App\Models\DeletedJoint;
class DeletedJointController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreDeletedJointRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreDeletedJointRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\DeletedJoint $deletedJoint
* @return \Illuminate\Http\Response
*/
public function show(DeletedJoint $deletedJoint)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\DeletedJoint $deletedJoint
* @return \Illuminate\Http\Response
*/
public function edit(DeletedJoint $deletedJoint)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateDeletedJointRequest $request
* @param \App\Models\DeletedJoint $deletedJoint
* @return \Illuminate\Http\Response
*/
public function update(UpdateDeletedJointRequest $request, DeletedJoint $deletedJoint)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\DeletedJoint $deletedJoint
* @return \Illuminate\Http\Response
*/
public function destroy(DeletedJoint $deletedJoint)
{
//
}
}
@@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DevextremeController extends Controller
{
}

Some files were not shown because too many files have changed in this diff Show More