Files
2026-04-28 21:14:25 +03:00

759 lines
29 KiB
PHP

<?php
namespace App\Services\WeldLogTriggers\Triggers;
use App\Services\WeldLogTriggers\Base\BaseTrigger;
use App\Models\PaintMatrix;
use App\Helpers\TransactionHelper;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
/**
* Paint Follow Ups Sync Trigger
*
* Mirrors the LineList trigger logic while running during WeldLog saves:
* - Requires matching Line List + painting cycle
* - Generates SHOP + FIELD entries with temperature/volume data
* - Protects finished records, manages cycle changes, cleans up orphans
*/
class PaintFollowUpsSyncTrigger extends BaseTrigger
{
public function getName(): string
{
return 'Paint Follow Ups Sync';
}
public function getOrder(): int
{
return 11;
}
public function getDependentFields(): array
{
return [
'line_number',
'spool_number',
'no_of_the_joint_as_per_as_built_survey',
'fluid_code',
'iso_number',
'project',
'design_area',
'type_of_joint'
];
}
protected function process($data, $beforeData, array $context): array
{
$paintFollowUpCreated = 0;
$paintFollowUpUpdated = 0;
$deletedOrphanedCount = 0;
$finalCleanupCount = 0;
try {
$weldLog = db("weld_logs")->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);
}
}