where("id", $data->id)->first(); if (!$weldLog) { return ['success' => false, 'reason' => 'weld_log_not_found']; } if (empty($weldLog->line_number)) { return ['success' => false, 'reason' => 'line_number_empty']; } $lineList = db("line_lists") ->where('line_no', $weldLog->line_number) ->first(); if (!$lineList) { return ['success' => false, 'reason' => 'line_list_not_found']; } if (empty($lineList->painting_cycle)) { return ['skipped' => true, 'reason' => 'painting_cycle_empty']; } $matchingWeldLogExists = db("weld_logs") ->where("line_number", $lineList->line_no) ->where("design_area", $lineList->unit) ->where("fluid_code", $lineList->fluid_code) ->exists(); if (!$matchingWeldLogExists) { return ['skipped' => true, 'reason' => 'no_matching_weld_log']; } $deletedOrphanedCount = $this->cleanupOrphanedRecords($weldLog, $beforeData); $resultFields = [ 'vt_result', 'rt_result', 'ut_result', 'pt_result', 'mt_result', 'pmi_result', 'ht_result', 'ferrite_result' ]; $allWeldLogs = db("weld_logs") ->where("line_number", $lineList->line_no) ->where("design_area", $lineList->unit) ->where("fluid_code", $lineList->fluid_code) ->where(function ($query) { $query->where("no_of_the_joint_as_per_as_built_survey", "not like", "%clone%"); }) ->where(function ($query) use ($resultFields) { foreach ($resultFields as $field) { $query->where(function ($subQuery) use ($field) { $subQuery->whereNull($field) ->orWhere($field, '') ->orWhere($field, 'Accept / Годен'); }); } }) ->orderBy('id', 'ASC') ->get(); if ($allWeldLogs->isEmpty()) { return ['skipped' => true, 'reason' => 'no_weld_logs_for_line']; } $paintMatrix = PaintMatrix::where('line', $lineList->line_no) ->where('fluid_code', $lineList->fluid_code) ->where('area', $lineList->unit) ->where('paint_cycle', $lineList->painting_cycle) ->first(); if (!$paintMatrix) { $paintMatrix = PaintMatrix::create([ 'project' => $allWeldLogs->first()->project ?? '', 'area' => $lineList->unit, 'description' => "PIPE", 'line' => $lineList->line_no, 'fluid_code_description' => $lineList->fluid_ru, 'fluid_code' => $lineList->fluid_code, 'paint_cycle' => $lineList->painting_cycle, 'created_at' => now(), 'updated_at' => now() ]); } $temperatures = j(setting("temperatures")); $todayTempData = null; $blastingDateRecord = db('construction_paint_logs') ->where('line', $lineList->line_no) ->whereNotNull('blasting_date') ->first(); if (!is_null($temperatures) && $blastingDateRecord && !empty($blastingDateRecord->blasting_date)) { $blastingDate = Carbon::parse($blastingDateRecord->blasting_date); $dayOfYear = $blastingDate->format('z'); $todayTempData = $temperatures[$dayOfYear] ?? null; } $fAvgNps = $allWeldLogs->where("type_of_joint", "F")->avg("nps_1"); $sAvgNps = $allWeldLogs->where("type_of_joint", "S")->avg("nps_1"); $mtoTotal = db("m_t_o_s") ->where("description_en", "like", "%pipe%") ->where("line", $lineList->line_no) ->selectRaw("SUM(quantity * POWER(odmm_1/2000, 2) * PI()) AS total") ->first()->total ?? 0; $fVolume = round(pi() * pow($fAvgNps, 2) * 200, 2); $sVolume = round($mtoTotal, 2); $processedPaintFollowUpIds = []; $processedShopSpools = []; $replacedJointNumbers = $this->determineReplacedJointNumbers($allWeldLogs); TransactionHelper::chunkTransaction( $allWeldLogs, function ($paintWeldLogChunk) use ( $lineList, $paintMatrix, $todayTempData, $fVolume, $sVolume, &$paintFollowUpCreated, &$paintFollowUpUpdated, &$processedPaintFollowUpIds, &$processedShopSpools, $replacedJointNumbers ) { foreach ($paintWeldLogChunk as $currentWeldLog) { if (empty($currentWeldLog->fluid_code)) { continue; } if ($this->shouldSkipJoint($currentWeldLog, $replacedJointNumbers)) { continue; } $basePaintFollowUpData = [ 'project' => $currentWeldLog->project ?? '', 'description' => "PIPE", 'area' => $lineList->unit ?? '', 'line' => $lineList->line_no, 'iso_number' => $currentWeldLog->iso_number ?? '', 'fluid_code' => $lineList->fluid_code, 'fluid_code_description' => $lineList->fluid_ru ?? '', 'cycle' => $lineList->painting_cycle ?? '', 'primer_coat_name_1' => $paintMatrix->primer_coat ?? '', 'brend_name_1' => $paintMatrix->brend_name_1 ?? '', 'colour_1' => $paintMatrix->colour_1 ?? '', 'ral_code_1' => $paintMatrix->ral_code_1 ?? '', 'thickness_1' => $paintMatrix->thickness_1 ?? '', 'intermediate_coat_name_2' => $paintMatrix->intermediate_coat ?? '', 'brend_name_2' => $paintMatrix->brend_name_2 ?? '', 'colour_2' => $paintMatrix->colour_2 ?? '', 'ral_code_2' => $paintMatrix->ral_code_2 ?? '', 'thickness_2' => $paintMatrix->thickness_2 ?? '', 'final_coat_name_3' => $paintMatrix->final_coat ?? '', 'brend_name_3' => $paintMatrix->brend_name_3 ?? '', 'colour_3' => $paintMatrix->colour_3 ?? '', 'ral_code_3' => $paintMatrix->ral_code_3 ?? '', 'thickness_3' => $paintMatrix->thickness_3 ?? '', 'status' => 'In Progress', 'updated_at' => now() ]; $result = $this->processShopRecord( $lineList, $currentWeldLog, $basePaintFollowUpData, $paintMatrix, $todayTempData, $sVolume, $paintFollowUpCreated, $paintFollowUpUpdated, $processedShopSpools ); $paintFollowUpCreated = $result['created']; $paintFollowUpUpdated = $result['updated']; $processedShopSpools = $result['processed_spools']; $fieldResult = $this->processFieldRecord( $lineList, $currentWeldLog, $basePaintFollowUpData, $paintMatrix, $todayTempData, $fVolume, $paintFollowUpCreated, $paintFollowUpUpdated, $processedPaintFollowUpIds ); $paintFollowUpCreated = $fieldResult['created']; $paintFollowUpUpdated = $fieldResult['updated']; $processedPaintFollowUpIds = $fieldResult['processed_ids']; } return $paintWeldLogChunk->count(); }, 1, 10000 ); $finalCleanupCount = $this->cleanupNonMatchingRecords($lineList->line_no); } catch (\Throwable $th) { Log::error("Paint Follow Ups sync error: " . $th->getMessage(), [ 'weld_log_id' => $data->id ]); throw $th; } return [ 'success' => true, 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'deleted_orphaned' => $deletedOrphanedCount, 'deleted_final_cleanup' => $finalCleanupCount ]; } protected function processShopRecord( $lineList, $weldLog, $basePaintFollowUpData, $paintMatrix, $todayTempData, $sVolume, $paintFollowUpCreated, $paintFollowUpUpdated, $processedShopSpools ): array { $hasShopJoint = db("weld_logs") ->where('line_number', $lineList->line_no) ->where('spool_number', $weldLog->spool_number) ->where('type_of_joint', 'S') ->exists(); if (!$hasShopJoint || empty($weldLog->spool_number)) { return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_spools' => $processedShopSpools ]; } if (in_array($weldLog->spool_number, $processedShopSpools, true)) { return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_spools' => $processedShopSpools ]; } $processedShopSpools[] = $weldLog->spool_number; $shopData = $basePaintFollowUpData; $shopData['location'] = 'SHOP'; $shopData['spool_no_joint_no'] = $weldLog->spool_number; $shopData['surface_roughness'] = $paintMatrix->surface_preparation ?? ''; if ($todayTempData) { $shopData['substrate_temprature'] = $todayTempData['temp_material_shop'] ?? null; $shopData['ambient_temprature'] = $todayTempData['shop_ambient'] ?? null; } $shopData['volume_1'] = $sVolume; $shopData['volume_2'] = $sVolume; $shopData['volume_3'] = $sVolume; $shopData['total_volume'] = $sVolume * 3; $shopUniqueConstraintCondition = [ 'line' => $lineList->line_no, 'spool_no_joint_no' => $weldLog->spool_number, 'cycle' => $lineList->painting_cycle, 'location' => 'SHOP' ]; $shopWhereCondition = [ 'line' => $lineList->line_no, 'spool_no_joint_no' => $weldLog->spool_number, 'location' => 'SHOP' ]; $existingShopRecord = db("paint_follow_ups") ->where($shopUniqueConstraintCondition) ->first(); $recordsWithDifferentPaintCycle = db("paint_follow_ups") ->where($shopWhereCondition) ->where('cycle', '!=', $lineList->painting_cycle) ->get(); if ($existingShopRecord) { $hasAllEmptyDates = $this->hasAllEmptyDates($existingShopRecord); if ($hasAllEmptyDates) { db("paint_follow_ups") ->where('id', $existingShopRecord->id) ->update($shopData); $paintFollowUpUpdated++; } else { $tempVolumeData = $this->getTempVolumeUpdateData( $existingShopRecord, $todayTempData, $sVolume, 'SHOP' ); if (!empty($tempVolumeData)) { db("paint_follow_ups") ->where('id', $existingShopRecord->id) ->update($tempVolumeData); } } } else { if ($recordsWithDifferentPaintCycle->count() > 0) { foreach ($recordsWithDifferentPaintCycle as $oldRecord) { $hasAnyDateFilled = !$this->hasAllEmptyDates($oldRecord); if ($hasAnyDateFilled) { db("paint_follow_ups") ->where('id', $oldRecord->id) ->update([ 'status' => 'HOLD', 'updated_at' => now() ]); } else { db("paint_follow_ups") ->where('id', $oldRecord->id) ->update([ 'cycle' => $lineList->painting_cycle, 'updated_at' => now() ]); $paintFollowUpUpdated++; return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_spools' => $processedShopSpools ]; } } } $shopData['primer_coating_start_date'] = null; $shopData['primer_coating_finish_date'] = null; $shopData['start_intermediate_date2'] = null; $shopData['finish_intermediate_date2'] = null; $shopData['final_coat_start_date3'] = null; $shopData['final_coat_finish_date3'] = null; $shopData['created_at'] = now(); $shopData['updated_at'] = now(); db("paint_follow_ups")->insert($shopData); $paintFollowUpCreated++; } return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_spools' => $processedShopSpools ]; } protected function processFieldRecord( $lineList, $weldLog, $basePaintFollowUpData, $paintMatrix, $todayTempData, $fVolume, $paintFollowUpCreated, $paintFollowUpUpdated, $processedPaintFollowUpIds ): array { if (empty($weldLog->no_of_the_joint_as_per_as_built_survey)) { return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_ids' => $processedPaintFollowUpIds ]; } $jointNo = $weldLog->no_of_the_joint_as_per_as_built_survey; $fieldData = $basePaintFollowUpData; $fieldData['location'] = 'FIELD'; $fieldData['spool_no_joint_no'] = $jointNo; $fieldData['surface_roughness'] = $paintMatrix->touch_up_of_damaged_parts ?? ''; if ($todayTempData) { $fieldData['substrate_temprature'] = $todayTempData['temp_material_field'] ?? null; $fieldData['ambient_temprature'] = $todayTempData['field_ambient'] ?? null; } $fieldData['volume_1'] = $fVolume; $fieldData['volume_2'] = $fVolume; $fieldData['volume_3'] = $fVolume; $fieldData['total_volume'] = $fVolume * 3; $fieldUniqueConstraintCondition = [ 'line' => $lineList->line_no, 'spool_no_joint_no' => $jointNo, 'cycle' => $lineList->painting_cycle, 'location' => 'FIELD' ]; $fieldWhereCondition = [ 'line' => $lineList->line_no, 'spool_no_joint_no' => $jointNo, 'location' => 'FIELD' ]; $existingFieldRecord = db("paint_follow_ups") ->where($fieldUniqueConstraintCondition) ->first(); $recordsWithDifferentPaintCycle = db("paint_follow_ups") ->where($fieldWhereCondition) ->where('cycle', '!=', $lineList->painting_cycle) ->get(); if ($existingFieldRecord) { if (in_array($existingFieldRecord->id, $processedPaintFollowUpIds)) { return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_ids' => $processedPaintFollowUpIds ]; } $processedPaintFollowUpIds[] = $existingFieldRecord->id; $hasAllEmptyDates = $this->hasAllEmptyDates($existingFieldRecord); if ($hasAllEmptyDates) { db("paint_follow_ups") ->where('id', $existingFieldRecord->id) ->update($fieldData); $paintFollowUpUpdated++; } else { $tempVolumeData = $this->getTempVolumeUpdateData( $existingFieldRecord, $todayTempData, $fVolume, 'FIELD' ); if (!empty($tempVolumeData)) { db("paint_follow_ups") ->where('id', $existingFieldRecord->id) ->update($tempVolumeData); } } } else { if ($recordsWithDifferentPaintCycle->count() > 0) { foreach ($recordsWithDifferentPaintCycle as $oldRecord) { $hasAnyDateFilled = !$this->hasAllEmptyDates($oldRecord); if ($hasAnyDateFilled) { db("paint_follow_ups") ->where('id', $oldRecord->id) ->update([ 'status' => 'HOLD', 'updated_at' => now() ]); } else { db("paint_follow_ups") ->where('id', $oldRecord->id) ->update([ 'cycle' => $lineList->painting_cycle, 'updated_at' => now() ]); $paintFollowUpUpdated++; return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_ids' => $processedPaintFollowUpIds ]; } } } $fieldData['primer_coating_start_date'] = null; $fieldData['primer_coating_finish_date'] = null; $fieldData['start_intermediate_date2'] = null; $fieldData['finish_intermediate_date2'] = null; $fieldData['final_coat_start_date3'] = null; $fieldData['final_coat_finish_date3'] = null; $fieldData['created_at'] = now(); $fieldData['updated_at'] = now(); db("paint_follow_ups")->insert($fieldData); $paintFollowUpCreated++; } return [ 'created' => $paintFollowUpCreated, 'updated' => $paintFollowUpUpdated, 'processed_ids' => $processedPaintFollowUpIds ]; } protected function cleanupOrphanedRecords($weldLog, $beforeData): int { if (!$beforeData) { return 0; } $deletedCount = 0; try { if (isset($beforeData->no_of_the_joint_as_per_as_built_survey) && $beforeData->no_of_the_joint_as_per_as_built_survey != $weldLog->no_of_the_joint_as_per_as_built_survey && !empty($beforeData->no_of_the_joint_as_per_as_built_survey)) { $oldJointNo = $beforeData->no_of_the_joint_as_per_as_built_survey; $deleted = db("paint_follow_ups") ->where('line', $weldLog->line_number) ->where('spool_no_joint_no', $oldJointNo) ->where('location', 'FIELD') ->delete(); $deletedCount += $deleted; } if (isset($beforeData->spool_number) && $beforeData->spool_number != $weldLog->spool_number && !empty($beforeData->spool_number)) { $oldSpoolNumber = $beforeData->spool_number; $deleted = db("paint_follow_ups") ->where('line', $weldLog->line_number) ->where('spool_no_joint_no', $oldSpoolNumber) ->where('location', 'SHOP') ->delete(); $deletedCount += $deleted; } } catch (\Throwable $th) { Log::warning("Failed to cleanup orphaned records", [ 'error' => $th->getMessage(), 'weld_log_id' => $weldLog->id ]); } return $deletedCount; } protected function cleanupNonMatchingRecords(string $lineNumber): int { $deletedCount = 0; try { $validFieldJointNos = db("weld_logs") ->where("line_number", $lineNumber) ->whereNotNull("no_of_the_joint_as_per_as_built_survey") ->where("no_of_the_joint_as_per_as_built_survey", "!=", "") ->pluck("no_of_the_joint_as_per_as_built_survey") ->toArray(); $validShopSpoolNos = db("weld_logs") ->where("line_number", $lineNumber) ->where("type_of_joint", "S") ->whereNotNull("spool_number") ->where("spool_number", "!=", "") ->pluck("spool_number") ->toArray(); if (!empty($validFieldJointNos)) { $deletedField = db("paint_follow_ups") ->where('line', $lineNumber) ->where('location', 'FIELD') ->whereNotIn('spool_no_joint_no', $validFieldJointNos) ->delete(); $deletedCount += $deletedField; } else { $deletedField = db("paint_follow_ups") ->where('line', $lineNumber) ->where('location', 'FIELD') ->delete(); $deletedCount += $deletedField; } if (!empty($validShopSpoolNos)) { $deletedShop = db("paint_follow_ups") ->where('line', $lineNumber) ->where('location', 'SHOP') ->whereNotIn('spool_no_joint_no', $validShopSpoolNos) ->delete(); $deletedCount += $deletedShop; } else { $deletedShop = db("paint_follow_ups") ->where('line', $lineNumber) ->where('location', 'SHOP') ->delete(); $deletedCount += $deletedShop; } } catch (\Throwable $th) { Log::warning("Failed to cleanup non-matching records", [ 'error' => $th->getMessage(), 'line_number' => $lineNumber ]); } return $deletedCount; } protected function hasAllEmptyDates($record): bool { return empty($record->primer_coating_start_date) && empty($record->primer_coating_finish_date) && empty($record->start_intermediate_date2) && empty($record->finish_intermediate_date2) && empty($record->final_coat_start_date3) && empty($record->final_coat_finish_date3); } protected function getTempVolumeUpdateData($record, $todayTempData, $volume, $location): array { $updateData = []; if ($todayTempData) { $tempField = $location === 'SHOP' ? 'temp_material_shop' : 'temp_material_field'; $ambientField = $location === 'SHOP' ? 'shop_ambient' : 'field_ambient'; if (empty($record->substrate_temprature) && isset($todayTempData[$tempField])) { $updateData['substrate_temprature'] = $todayTempData[$tempField]; } if (empty($record->ambient_temprature) && isset($todayTempData[$ambientField])) { $updateData['ambient_temprature'] = $todayTempData[$ambientField]; } } if (empty($record->volume_1)) { $updateData['volume_1'] = $volume; } if (empty($record->volume_2)) { $updateData['volume_2'] = $volume; } if (empty($record->volume_3)) { $updateData['volume_3'] = $volume; } if (empty($record->total_volume)) { $updateData['total_volume'] = $volume * 3; } return $updateData; } protected function determineReplacedJointNumbers($weldLogs): array { $toSkip = []; foreach ($weldLogs as $log) { $jointNo = $log->no_of_the_joint_as_per_as_built_survey; if ($this->isRepairJoint($jointNo)) { $baseJoint = $this->stripRepairSuffix($jointNo); if (!empty($baseJoint)) { $toSkip[] = $baseJoint; } } } return array_unique($toSkip); } protected function shouldSkipJoint($weldLog, array $replacedJointNumbers): bool { $jointNo = $weldLog->no_of_the_joint_as_per_as_built_survey; if (empty($jointNo)) { return false; } return in_array($jointNo, $replacedJointNumbers, true); } protected function isRepairJoint(?string $jointNo): bool { return !empty($jointNo) && strpos($jointNo, 'R') !== false; } protected function stripRepairSuffix(?string $jointNo): ?string { if (empty($jointNo)) { return $jointNo; } $pos = strpos($jointNo, 'R'); if ($pos === false) { return $jointNo; } return substr($jointNo, 0, $pos); } }