50 KiB
Line List Triggers System - Complete Documentation
Non-Technical Summary | Teknik Olmayan Özet
English: What Does LineList Trigger System Do?
The LineList Trigger System is an automatic data synchronization engine that activates whenever a pipe line specification is saved or updated. It acts as a central coordinator that ensures all related quality control, inspection, and painting records stay up-to-date.
What It Updates:
- 👷 Weld Logs: Updates 15+ fields in all welding records for the line
- 🎨 Paint Matrix: Creates/updates painting specifications and color codes
- 🧪 NDE Matrix: Manages non-destructive testing requirements for each joint type
- 🎨 Paint Systems: Updates painting system standards and specifications
- 🏭 Paint Follow-Ups: Creates records for both workshop (SHOP) and field (FIELD) painting
- 🏗️ Construction Paint Logs: Tracks painting progress for each spool
- 📊 Color Systems: Manages fluid code colors and RAL color mappings
- 🔄 Spool Status: Automatically updates spool completion status
- 🧹 Cleanup: Removes orphaned paint records
Special Features:
- 🛡️ Date Protection: Never overwrites records where work has already started (dates are filled)
- 🔄 Painting Cycle Changes: When paint specs change, old records are preserved (marked as HOLD) and new ones created
- 🌡️ Temperature Calculations: Automatically calculates substrate and ambient temperatures
- 📏 Volume Calculations: Computes paint volumes for both shop and field work
- 🎨 RAL to Russian: Converts RAL color codes to Russian color descriptions
How It Works:
- Engineer updates a line specification (e.g., changes painting cycle)
- System detects which fields changed
- Automatically synchronizes data to 8+ related tables
- Protects completed work (records with dates) from being overwritten
- Creates new records for new specifications while preserving old work
Why It's Important:
- ✅ Each change automatically updates only the relevant related records
- ✅ Prevents accidental data loss (completed work is protected)
- ✅ Maintains complete history when specifications change
- ✅ Eliminates need for manual updates across multiple modules
- ✅ Ensures painting, testing, and welding records are always synchronized
Türkçe: LineList Trigger Sistemi Ne İşe Yarar?
LineList Trigger Sistemi, bir boru hattı spesifikasyonu kaydedildiğinde veya güncellendiğinde otomatik olarak devreye giren merkezi bir veri senkronizasyon motorudur. İlgili tüm kalite kontrol, muayene ve boya kayıtlarının güncel kalmasını sağlayan merkezi bir koordinatör görevi görür.
Neleri Günceller:
- 👷 Kaynak Kayıtları (Weld Logs): Hat için tüm kaynak kayıtlarında 15+ alanı günceller
- 🎨 Boya Matrisi (Paint Matrix): Boya spesifikasyonlarını ve renk kodlarını oluşturur/günceller
- 🧪 NDE Matrisi: Her ek tipi için tahribatsız muayene gereksinimlerini yönetir
- 🎨 Boya Sistemleri: Boya sistemi standartlarını ve spesifikasyonlarını günceller
- 🏭 Boya Takip (Paint Follow-Ups): Hem atölye (SHOP) hem saha (FIELD) boyama için kayıtlar oluşturur
- 🏗️ İnşaat Boya Kayıtları: Her spool için boyama ilerlemesini takip eder
- 📊 Renk Sistemleri: Akışkan kodu renklerini ve RAL renk eşleştirmelerini yönetir
- 🔄 Spool Durumu: Spool tamamlanma durumunu otomatik günceller
- 🧹 Temizlik: Sahipsiz boya kayıtlarını siler
Özel Özellikler:
- 🛡️ Tarih Koruması: İş başlamış kayıtların (tarihler dolmuş) üzerine asla yazmaz
- 🔄 Boya Döngüsü Değişiklikleri: Boya spesifikasyonları değiştiğinde, eski kayıtlar korunur (HOLD işaretlenir) ve yenileri oluşturulur
- 🌡️ Sıcaklık Hesaplamaları: Substrat ve ortam sıcaklıklarını otomatik hesaplar
- 📏 Hacim Hesaplamaları: Hem atölye hem saha işi için boya hacimlerini hesaplar
- 🎨 RAL'dan Rusça'ya: RAL renk kodlarını Rusça renk tanımlarına dönüştürür
Nasıl Çalışır:
- Mühendis bir hat spesifikasyonunu günceller (örn. boya döngüsünü değiştirir)
- Sistem hangi alanların değiştiğini algılar
- Otomatik olarak 8+ ilgili tabloya veri senkronize eder
- Tamamlanmış işleri korur (tarihi olan kayıtların üzerine yazmaz)
- Yeni spesifikasyonlar için yeni kayıtlar oluştururken eski işleri korur
Neden Önemli:
- ✅ Her değişiklik sadece ilgili kayıtları otomatik günceller
- ✅ Kazara veri kaybını önler (tamamlanmış iş korunur)
- ✅ Spesifikasyonlar değiştiğinde tam geçmiş tutar
- ✅ Birden fazla modülde manuel güncelleme ihtiyacını ortadan kaldırır
- ✅ Boya, test ve kaynak kayıtlarının her zaman senkronize olmasını sağlar
Quick Reference: What Happens in Each Column / Table
This section provides a simple, non-technical overview of what each trigger does, which tables it affects, and in what order.
Execution Order Summary
| Order | Trigger Name | Tables Affected | Operation Type | When It Runs |
|---|---|---|---|---|
| 1 | Paint Cycle Change | weld_logs, construction_paint_logs |
UPDATE, DELETE | When painting_cycle changes |
| 2 | WeldLog Fields Sync | weld_logs |
UPDATE | Always (syncs 15+ fields) |
| 3 | Paint System Sync | paint_systems, color_systems |
CREATE/UPDATE | When painting_cycle or fluid_code changes |
| 4 | Construction Paint Logs Sync | construction_paint_logs |
CREATE/UPDATE | When line has painting_cycle and shop joints |
| 5 | Paint Follow Ups Sync | paint_follow_ups, paint_matrices |
CREATE/UPDATE | When line has painting_cycle |
| 6 | Paint Matrix Operations | paint_matrices, paint_follow_ups |
CREATE/UPDATE | When painting_cycle changes |
| 7 | NDE Matrix Sync | nde_matrices |
CREATE/UPDATE | When line_no or fluid_code changes |
| 8 | Painting Cycle Deletion | paint_matrices, paint_follow_ups, construction_paint_logs, nde_matrices |
DELETE | When painting_cycle is deleted |
| 9 | Spool Status Changer | weld_logs |
UPDATE | When line_no changes |
| 10 | Paint System To Matrix | paint_matrices |
UPDATE | When painting_cycle changes |
| 11 | Cleanup Operations | paint_follow_ups |
DELETE | When line_no changes |
Trigger 1: Paint Cycle Change (When painting_cycle changes)
What it does: When engineer changes painting cycle specification, updates all related welding records and removes old painting records.
Tables Updated:
weld_logs→ Updatespainting_cyclecolumn for all welds on this lineconstruction_paint_logs→ Deletes records that haven't started work yet (no dates filled)
Columns Changed in weld_logs:
painting_cycle= new value from line_lists
Business Logic:
- If painting_cycle becomes EMPTY → Deletes construction_paint_logs (only if blasting/painting hasn't started)
- If painting_cycle has NEW VALUE → Updates all weld logs with new painting cycle
Trigger 2: WeldLog Fields Sync (Always runs)
What it does: Copies 17 specification fields from line specification to all welding records.
Tables Updated:
weld_logs→ Updates all records matching this line number
Columns Changed in weld_logs:
painting_cycle ← line_lists.painting_cycle
main_nps ← line_lists.dn
fluid_code ← line_lists.fluid_code
service_category ← line_lists.category
fluid_group ← line_lists.fluid_group
piping_class ← line_lists.pipe_material_class
external_finish_type ← line_lists.external_finish_type
circuit_number ← line_lists.circuit_number
p_id ← line_lists.p_id
type_of_test ← line_lists.test_media
test_pressure ← line_lists.test_pressure_mpa
line_specification ← line_lists.line_specification
design_pressure_mpa ← line_lists.design_pressure_mpa
design_temperature_s ← line_lists.design_temperature
operating_pressure_mpa ← line_lists.working_pressure_mpa
operating_temperature_s← line_lists.working_temperature
ndt_percent ← line_lists.ndt
Trigger 3: Paint System Sync (When painting_cycle or fluid_code changes)
What it does: Creates or updates paint system standards and color system specifications.
Tables Updated:
paint_systems→ Creates/updates one record per painting_cyclecolor_systems→ Creates/updates one record per fluid_code
Columns Changed in paint_systems:
paint_cycle= line_lists.painting_cyclerevision= line_lists.rev
Columns Changed in color_systems:
fluid_code_description= line_lists.fluid_rufluid_code= line_lists.fluid_codedesign_temperature= line_lists.design_temperatureworking_temperature= line_lists.working_temperatureunit= line_lists.unit
Trigger 4: Construction Paint Logs Sync (When painting_cycle exists)
What it does: Creates painting tracking records for each spool that has shop joints.
Tables Updated:
construction_paint_logs→ Creates/updates one record per spool (only spools with shop joints)
Columns Created/Updated:
construction_report_no ← weld_logs.weld_map_no
test_package ← weld_logs.test_package_no
rev ← line_lists.rev
engineering ← line_lists.engineering
area ← weld_logs.project
unit ← weld_logs.design_area
line ← line_lists.line_no
iso_drawings ← weld_logs.iso_number
fluid_code ← line_lists.fluid_code
fluid_code_description ← line_lists.fluid_ru
isolation_info ← line_lists.external_finish_type
spool ← weld_logs.spool_number
spool_status ← weld_logs.spool_status (default: 'Waiting')
painting_system_type_1 ← paint_matrices.paint_cycle
painting_system_type_2 ← paint_matrices.paint_cycle
brend_name_1/2/3 ← paint_matrices (3 coat brands)
ral_1/2/3 ← paint_matrices (3 RAL codes)
color_1/2/3 ← paint_matrices (3 Russian colors)
thickness_1/2/3 ← paint_matrices (3 coat thicknesses)
dn_1/2/3 ← weld_logs (pipe diameters)
Business Logic:
- Only creates records for spools with
type_of_joint = 'S'(shop joints) - Protects records where painting has started (date fields are not null)
Trigger 5: Paint Follow Ups Sync (When painting_cycle exists)
What it does: Creates detailed painting follow-up records for both shop and field work. Most complex trigger with temperature and volume calculations.
Tables Updated:
paint_matrices→ Creates if doesn't existpaint_follow_ups→ Creates two types of records: SHOP and FIELD
For SHOP Records (one per spool):
location = 'SHOP'
spool_no_joint_no ← weld_logs.spool_number
project ← weld_logs.project
area ← line_lists.unit
line ← line_lists.line_no
iso_number ← weld_logs.iso_number
fluid_code ← line_lists.fluid_code
fluid_code_description ← line_lists.fluid_ru
cycle ← line_lists.painting_cycle
surface_roughness ← paint_matrices.surface_preparation
# 3 paint coats (primer, intermediate, final)
primer_coat_name_1 ← paint_matrices.primer_coat
brend_name_1 ← paint_matrices.brend_name_1
colour_1 ← paint_matrices.colour_1 (Russian)
ral_code_1 ← paint_matrices.ral_code_1
thickness_1 ← paint_matrices.thickness_1
(same pattern for coat 2 and 3)
# Calculated fields
substrate_temprature ← calculated from temperature settings (shop)
ambient_temprature ← calculated from temperature settings (shop)
volume_1/2/3 ← calculated from MTO table
total_volume = volume_1 + volume_2 + volume_3
status = 'In Progress'
For FIELD Records (one per joint):
location = 'FIELD'
spool_no_joint_no ← weld_logs.no_of_the_joint_as_per_as_built_survey
(same fields as SHOP, but different values)
surface_roughness ← paint_matrices.touch_up_of_damaged_parts
substrate_temprature ← calculated from temperature settings (field)
ambient_temprature ← calculated from temperature settings (field)
volume_1/2/3 ← calculated: π * (avg_nps)² * 200
Business Logic:
- Date Protection: If ANY date field is filled (primer_coating_start_date, etc.), keeps existing record
- Painting Cycle Changes:
- If dates filled → Marks old record as 'HOLD', creates new record
- If dates empty → Updates existing record to new cycle
- Temperature Calculation: Based on blasting_date and day of year from settings
- Volume Calculation: Different formulas for SHOP (from MTO) vs FIELD (from pipe diameter)
Trigger 6: Paint Matrix Operations (When painting_cycle changes)
What it does: Creates/updates paint matrix (master painting specification) and syncs paint details to follow-up records.
Tables Updated:
paint_matrices→ Creates/updates one record per line + fluid_codepaint_follow_ups→ Updates surface roughness for all records
Columns Created/Updated in paint_matrices:
project ← weld_logs.project (zone info)
area ← line_lists.unit
description = 'PIPE'
line ← line_lists.line_no
fluid_code_description ← line_lists.fluid_ru
fluid_code ← line_lists.fluid_code
design_temperature ← line_lists.design_temperature
operation_temperature ← line_lists.working_temperature
paint_cycle ← line_lists.painting_cycle
Columns Updated in paint_follow_ups:
surface_roughness ← paint_matrices.surface_preparation (if spool starts with 'SPL')
← paint_matrices.touch_up_of_damaged_parts (if joint number)
cycle ← paint_matrices.paint_cycle
primer_coat_name_1 ← paint_matrices.primer_coat
brend_name_1/2/3 ← paint_matrices (3 coat brands)
colour_1/2/3 ← paint_matrices (3 coat colors)
ral_code_1/2/3 ← paint_matrices (3 RAL codes)
thickness_1/2/3 ← paint_matrices (3 coat thicknesses)
Business Logic:
- Only updates paint_follow_ups if ALL date fields are null (hasn't started work)
- Distinguishes SHOP records (SPL prefix) from FIELD records for surface roughness
Trigger 7: NDE Matrix Sync (When line_no or fluid_code changes)
What it does: Creates testing requirement records for each type of weld joint on the line.
Tables Updated:
nde_matrices→ Creates/updates one record per unique combination of (line + joint_type + fluid_code)
Columns Created/Updated:
design_area ← line_lists.unit
fluid ← line_lists.fluid_code
line ← line_lists.line_no
line_spec ← line_lists.line_specification
operation_temp ← line_lists.working_temperature
operations_pressure_kg ← line_lists.working_pressure_mpa
pipe_material_class ← line_lists.pipe_material_class
type_of_joint ← weld_logs.type_of_welds (collected from all welds)
pwht ← line_lists.pwht
pwht_field ← line_lists.pwht
ht = 100 if pwht='YES', else 0
ndt ← line_lists.ndt
piping_class_according_to_gost ← line_lists.category
piping_group ← line_lists.fluid_group
rev ← line_lists.rev
Business Logic:
- Collects unique joint types from weld_logs (e.g., 'BW', 'SW', 'FW')
- Creates one nde_matrices record for each joint type
- Calculates ht (heat treatment) = 100 if PWHT required, else 0
Trigger 8: Painting Cycle Deletion (When painting_cycle deleted)
What it does: Removes all painting-related records when painting cycle is removed from line specification.
Tables Updated (ALL DELETE operations):
paint_matrices→ Deletes records for this line + fluid_codepaint_follow_ups→ Deletes records (only if no dates filled)construction_paint_logs→ Deletes records (only if no dates filled)nde_matrices→ Deletes records for this line
Conditions for Deletion:
-
paint_follow_ups: Only deletes if ALL these are NULL:
- primer_coating_start_date, primer_coating_finish_date
- start_intermediate_date2, finish_intermediate_date2
- final_coat_start_date3, final_coat_finish_date3
-
construction_paint_logs: Only deletes if ALL these are NULL:
- blasting_date, blasting_finish_date
- painting_date_1, painting_finish_date_1
- painting_date_2, painting_finish_date_2
- painting_date_3, painting_finish_date_3
Business Logic: Protects completed work - never deletes records where work has started
Trigger 9: Spool Status Changer (When line_no changes)
What it does: Recalculates completion status for each spool based on all testing and painting results.
Tables Updated:
weld_logs→ Updatesspool_statuscolumn for all spools
Business Logic:
- Uses complex view logic (cron.spool-status-changer)
- Checks NDT results, painting completion, testing status
- Updates spool_status to values like: 'Waiting', 'In Progress', 'Completed', 'Failed'
Trigger 10: Paint System To Matrix Sync (When painting_cycle changes)
What it does: Syncs detailed paint specifications from paint_systems and color_systems tables to paint_matrices.
Tables Updated:
paint_matrices→ Updates all records matching this painting_cycle
Columns Updated:
# From paint_systems
surface_preparation ← paint_systems.surface_preparation
touch_up_of_damaged_parts ← paint_systems.surface_roughness
primer_coat ← paint_systems.primer_coat_name_1
brend_name_1 ← paint_systems.brand_name_1
thickness_1 ← paint_systems.thickness_1
intermediate_coat ← paint_systems.primer_coat_name_2
brend_name_2 ← paint_systems.brand_name_2
thickness_2 ← paint_systems.thickness_2
final_coat ← paint_systems.primer_coat_name_3
brend_name_3 ← paint_systems.brand_name_3
thickness_3 ← paint_systems.thickness_3
total_thickness = thickness_1 + thickness_2 + thickness_3
# From color_systems
fluid_code_description ← color_systems.fluid_code_description
design_temperature ← color_systems.design_temperature
operation_temperature ← color_systems.working_temperature
ral_code_1/2/3 ← color_systems.ral_1/2/3
colour_1/2/3 ← Converted from RAL codes to Russian descriptions
Business Logic:
- Converts RAL codes to Russian color names using setting("ral-codes")
- Example: RAL 5015 → "Синий небесный" (Sky Blue in Russian)
Trigger 11: Cleanup Operations (Non-critical cleanup)
What it does: Removes orphaned paint follow-up records that are no longer needed.
Tables Updated:
paint_follow_ups→ Deletes orphaned records
Business Logic:
- Uses view logic (cron.delete-no-paint-followup)
- Non-critical operation - logs warning if fails but doesn't stop execution
Complete Data Flow Summary
Line List Save
↓
1. Paint Cycle Change (if painting_cycle changed)
→ weld_logs.painting_cycle = NEW VALUE
→ construction_paint_logs DELETE (if no dates)
↓
2. WeldLog Fields Sync (always)
→ weld_logs: 17 columns updated
↓
3. Paint System Sync
→ paint_systems: CREATE/UPDATE
→ color_systems: CREATE/UPDATE
↓
4. Construction Paint Logs Sync
→ construction_paint_logs: CREATE/UPDATE (per spool with shop joints)
↓
5. Paint Follow Ups Sync
→ paint_matrices: CREATE if missing
→ paint_follow_ups: CREATE SHOP records (per spool)
→ paint_follow_ups: CREATE FIELD records (per joint)
↓
6. Paint Matrix Operations
→ paint_matrices: CREATE/UPDATE
→ paint_follow_ups: UPDATE surface_roughness + paint details
↓
7. NDE Matrix Sync
→ nde_matrices: CREATE/UPDATE (per joint type)
↓
8. Painting Cycle Deletion (if painting_cycle deleted)
→ paint_matrices: DELETE
→ paint_follow_ups: DELETE (if no dates)
→ construction_paint_logs: DELETE (if no dates)
→ nde_matrices: DELETE
↓
9. Spool Status Changer
→ weld_logs.spool_status: UPDATE (recalculate)
↓
10. Paint System To Matrix Sync
→ paint_matrices: UPDATE (detailed specs + RAL to Russian)
↓
11. Cleanup Operations
→ paint_follow_ups: DELETE orphaned records
Table of Contents
- Introduction
- Architecture Overview
- System Components
- Trigger Breakdown
- Execution Flow
- Data Flow Diagrams
- Key Concepts
- Development Guide
- Troubleshooting
- Performance Optimization
Introduction
The Line List Triggers System is a modular, service-based architecture designed to handle complex business logic when Line List records are created or updated. It replaces a monolithic 2070-line trigger file with 11 independent, testable, and maintainable trigger classes.
Problem Solved
Before: A single 2070-line file with 11 different operations mixed together, making it:
- Difficult to understand and maintain
- Hard to test individual operations
- Prone to bugs due to tight coupling
- Challenging to extend with new features
After: 11 independent trigger classes, each with:
- Single Responsibility Principle
- Clear dependencies and execution order
- Comprehensive logging
- Easy to test and extend
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ LineList Save Event │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ line_lists.php (Entry Point) │
│ - Detects changed fields │
│ - Determines if new record │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LineListTriggerManager (Orchestrator) │
│ - Initializes registry │
│ - Gets triggers in order │
│ - Executes shouldRun() for each │
│ - Calls execute() for enabled triggers │
│ - Logs results │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LineListTriggerRegistry (Trigger Manager) │
│ - Registers all 11 triggers │
│ - Provides triggers sorted by order │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Individual Trigger Classes │
│ (11 triggers executed in sequence) │
│ │
│ 1. Paint Cycle Change │
│ 2. Painting Cycle Deletion │
│ 3. WeldLog Fields Sync │
│ 4. Paint Matrix Operations │
│ 5. NDE Matrix Sync │
│ 6. Paint System Sync │
│ 7. Paint System To Matrix Sync │
│ 8. Construction Paint Logs Sync │
│ 9. Paint Follow Ups Sync │
│ 10. Spool Status Changer Final │
│ 11. Cleanup Operations │
└─────────────────────────────────────────────────────────────┘
System Components
1. LineListTriggerInterface
Location: app/Services/LineListTriggers/Contracts/LineListTriggerInterface.php
Defines the contract that all triggers must implement:
interface LineListTriggerInterface
{
public function getName(): string;
public function getOrder(): int;
public function shouldRun(array $changedFields, bool $isNewRecord): bool;
public function getDependentFields(): array;
public function execute($lineListData, $beforeData = null, array $context = []): array;
public function isAsync(): bool;
}
2. BaseLineListTrigger
Location: app/Services/LineListTriggers/Base/BaseLineListTrigger.php
Provides common functionality:
- Execution timing
- Logging (start/end with duration)
- Default
shouldRun()implementation - Error handling
3. LineListTriggerRegistry
Location: app/Services/LineListTriggers/LineListTriggerRegistry.php
- Registers all triggers
- Provides sorted trigger list
- Enables/disables triggers
4. LineListTriggerManager
Location: app/Services/LineListTriggers/LineListTriggerManager.php
- Orchestrates trigger execution
- Handles
shouldRunchecks - Provides overall logging
- Manages errors
Trigger Breakdown
Trigger 1: Paint Cycle Change Operations
File: PaintCycleChangeTrigger.php
Order: 1
Execution Condition: painting_cycle has changed
Purpose: Handles comprehensive operations when the painting cycle is modified
Operations:
-
Spool Status Changer Trigger
- Finds all weld logs with shop joints for this line
- Triggers spool-status-changer view for each unique spool
-
WeldLogs Painting Cycle Update
- Updates
painting_cyclefield in all weld logs for this line
- Updates
-
Comprehensive Paint Sync
- Creates/updates Paint Follow Ups (SHOP and FIELD records)
- Creates/updates Construction Paint Logs
- Uses Paint Matrix and Paint System data
Key Logic:
- Only runs if
beforeDataexists andpainting_cyclechanged - Processes both SHOP (spool-based) and FIELD (joint-based) records
- Pulls data from
paint_matricesandpaint_systemstables
Trigger 2: Painting Cycle Deletion
File: PaintingCycleDeletionTrigger.php
Order: 2
Execution Condition: painting_cycle was deleted (changed from value to empty)
Purpose: Cleanup operations when painting cycle is removed
Operations:
- Delete
paint_matricesrecords - Delete
paint_follow_upsrecords (only if date fields are null) - Delete
construction_paint_logsrecords (only if date fields are null) - Delete
nde_matricesrecords
Date Field Protection: Does NOT delete records if any of these date fields are filled:
primer_coating_start_date,primer_coating_finish_datestart_intermediate_date2,finish_intermediate_date2final_coat_start_date3,final_coat_finish_date3- (for construction_paint_logs: blasting dates, painting dates, rfi dates)
Trigger 3: WeldLog Fields Sync
File: WeldLogFieldsSyncTrigger.php
Order: 3
Execution Condition: Always runs
Purpose: Synchronize 15+ fields from line_lists to weld_logs
Fields Synchronized:
- painting_cycle
- main_nps (from dn)
- fluid_code
- service_category (from category)
- fluid_group
- piping_class (from pipe_material_class)
- external_finish_type
- circuit_number
- p_id
- type_of_test (from test_media)
- test_pressure (from test_pressure_mpa)
- line_specification
- design_pressure_mpa
- design_temperature_s (from design_temperature)
- operating_pressure_mpa (from working_pressure_mpa)
- operating_temperature_s (from working_temperature)
- ndt_percent (from ndt)
Logic: Updates all weld_logs records where line_number matches line_lists.line_no
Trigger 4: Paint Matrix Operations
File: PaintMatrixOperationsTrigger.php
Order: 4
Execution Condition: painting_cycle, line_no, fluid_code, or unit changed
Purpose: Create/update Paint Matrix and sync to Paint Follow Ups
Operations:
-
Get Zone Information
- Fetches
projectfield from weld logs for zone assignment
- Fetches
-
Create/Update Paint Matrix
- Unique constraint:
line + fluid_code - Updates or creates paint matrix record
- Unique constraint:
-
Surface Preparation Sync
- Syncs
surface_preparation(for SHOP) andtouch_up_of_damaged_parts(for FIELD) - From
paint_matricestopaint_follow_ups
- Syncs
-
Paint Coat Details Sync
- Syncs 3 coats (primer, intermediate, final)
- Brand names, colors, RAL codes, thickness
- Only updates if ALL date fields are empty
Key Data Structures:
$whereData = ['line' => $line_no, 'fluid_code' => $fluid_code];
$updateData = [
'project', 'area', 'description', 'line', 'fluid_code_description',
'fluid_code', 'design_temperature', 'operation_temperature', 'paint_cycle'
];
Trigger 5: NDE Matrix Sync
File: NdeMatrixSyncTrigger.php
Order: 5
Execution Condition: Multiple fields (line_no, fluid_code, unit, etc.)
Purpose: Comprehensive NDE Matrix synchronization from WeldLogs and LineList
Operations:
-
Collect Spec Joint Types
- Reads all weld logs for this line
- Groups by
fluid_code,line_number, andtype_of_welds - Creates array:
$specJointTypes[$fluid_code][$line_no][] = type_of_welds
-
Update Fluid Codes in WeldLogs
- If weld log has empty fluid_code, copies from line list
-
Create/Update NDE Matrices
- Unique constraint:
line + type_of_joint + fluid - For each spec joint type, creates or updates NDE matrix
- Calculates
ht(100 if pwht is "YES", else 0)
- Unique constraint:
PWHT Logic:
$ht = 0;
if($lineListData->pwht == "YES") $ht = 100;
Data Fields:
$data = [
'design_area', 'fluid', 'line', 'line_spec', 'operation_temp',
'operations_pressure_kg', 'pipe_material_class', 'type_of_joint',
'pwht', 'pwht_field', 'ht', 'ndt',
'piping_class_according_to_gost', 'piping_group', 'rev'
];
Trigger 6: Paint System Sync
File: PaintSystemSyncTrigger.php
Order: 6
Execution Condition: painting_cycle, rev, fluid_code, etc. changed
Purpose: Sync to paint_systems and color_systems tables
Operations:
-
Paint Systems
- Updates or inserts into
paint_systemstable - Key:
paint_cycle - Data:
paint_cycle,revision
- Updates or inserts into
-
Color Systems
- Updates or inserts into
color_systemstable - Key:
fluid_code - Data:
fluid_code_description,fluid_code,design_temperature,working_temperature,unit
- Updates or inserts into
Trigger 7: Paint System To Matrix Sync
File: PaintSystemToMatrixSyncTrigger.php
Order: 7
Execution Condition: painting_cycle, fluid_code, line_no changed
Purpose: Sync paint_systems + color_systems data to paint_matrices
Operations:
-
Get Paint System and Color System
- Fetches from respective tables
-
RAL Code to Russian Color Mapping
- Uses
setting("ral-codes")JSON - Maps
ral_1,ral_2,ral_3to Russian color descriptions
- Uses
-
Update Paint Matrices
- Finds all paint matrices with matching
paint_cycle - Updates with:
- Surface preparation data
- 3 coat details (primer, intermediate, final)
- Brand names, thickness, total thickness
- RAL codes and Russian color descriptions
- Finds all paint matrices with matching
RAL Mapping Logic:
foreach ($ralCodes as $entry) {
if (isset($entry['ral_code']) && $entry['ral_code'] == $ralCode && isset($entry['ru'])) {
return $entry['ru'];
}
}
Trigger 8: Construction Paint Logs Sync
File: ConstructionPaintLogsSyncTrigger.php
Order: 8
Execution Condition: line_no, painting_cycle, fluid_code, etc. changed
Purpose: Comprehensive Construction Paint Logs synchronization
Operations:
-
Get Paint Matrix and Paint System
- Fetches related data for RAL values and paint system details
-
Process Each Weld Log
- Only processes spools with shop joints
- Creates unique constraint:
line + spool + unit + painting_system_type_1
-
Handle Painting Cycle Changes
- If record with same painting cycle exists: Update it
- If records with different painting cycles exist:
- Check if any date fields are filled
- If dates filled: Keep old record with current status (protect completed work)
- If dates empty: Update old record to new painting cycle
-
Create New Records
- Only if no existing record can be updated
- Sets all date fields to null
Date Fields Checked:
$hasAnyDateFilled = !empty($blasting_date) ||
!empty($blasting_finish_date) ||
!empty($painting_date_1) || !empty($painting_finish_date_1) ||
!empty($painting_date_2) || !empty($painting_finish_date_2) ||
!empty($painting_date_3) || !empty($painting_finish_date_3) ||
!empty($rfi_date_1) || !empty($rfi_date_2) || !empty($rfi_date_3);
Trigger 9: Paint Follow Ups Sync
File: PaintFollowUpsSyncTrigger.php
Order: 9
Execution Condition: line_no, painting_cycle, fluid_code, etc. changed
Purpose: Most complex trigger - comprehensive Paint Follow Ups synchronization
Operations:
-
Get or Create Paint Matrix
- Ensures paint matrix exists for this line
-
Temperature Calculations
- Loads temperature settings from system settings
- Gets blasting_date from construction_paint_logs
- Calculates day of year and looks up temperature data
-
Volume Calculations
- F Volume (Field):
π * (avg_nps)² * 200 - S Volume (Shop): Sum from MTO table
- F Volume (Field):
-
Process SHOP Records
- Based on
spool_number - Only if shop joint exists for this spool
- Unique constraint:
line + spool_no_joint_no + cycle + location - Handles painting cycle changes with date protection
- Based on
-
Process FIELD Records
- Based on
no_of_the_joint_as_per_as_built_survey - Unique constraint:
line + spool_no_joint_no + cycle + location - Handles painting cycle changes with date protection
- Based on
Date Protection Logic:
$hasAllEmptyDates = empty($primer_coating_start_date) &&
empty($primer_coating_finish_date) &&
empty($start_intermediate_date2) &&
empty($finish_intermediate_date2) &&
empty($final_coat_start_date3) &&
empty($final_coat_finish_date3);
if ($hasAllEmptyDates) {
// Full update allowed
} else {
// Only update temperature and volume if empty
}
Temperature and Volume Updates:
- Even if dates are filled, updates temp/volume fields if they are empty
- SHOP: Uses
temp_material_shop,shop_ambient - FIELD: Uses
temp_material_field,field_ambient
Trigger 10: Spool Status Changer Final
File: SpoolStatusChangerFinalTrigger.php
Order: 10
Execution Condition: line_no changed
Purpose: Final spool status changer after all syncs
Operations:
- Renders
cron.spool-status-changerview - Re-calculates spool statuses based on all updated data
Trigger 11: Cleanup Operations
File: CleanupOperationsTrigger.php
Order: 11
Execution Condition: line_no changed
Purpose: Non-critical cleanup operations
Operations:
- Renders
cron.delete-no-paint-followupview - Deletes orphaned paint follow up records
Error Handling: Non-critical - logs warning if fails, doesn't throw exception
Execution Flow
Step-by-Step Flow
-
LineList Save Triggered
// AdminController or DevExtreme save operation $lineList->save(); -
Trigger File Called
// app/Http/Controllers/SaveTrigger/line_lists.php $id = $request['key']; $lineList = db($tableName)->where("id", $id)->first(); -
Detect Changes
$isNewRecord = is_null($beforeData); $changedFields = detectChangedFields($lineList, $beforeData); -
Initialize Manager
$registry = new LineListTriggerRegistry(); $manager = new LineListTriggerManager($registry); -
Execute Triggers
$results = $manager->executeTriggers( $lineList, $beforeData, $changedFields, $isNewRecord ); -
For Each Trigger:
- Check
shouldRun() - If true, call
execute() - Log start time
- Run
process()method - Log completion with duration
- Return results
- Check
-
Log Overall Completion
- Total execution time
- Peak memory usage
- Results summary
Data Flow Diagrams
Paint Cycle Change Flow
LineList.painting_cycle changed
│
├─> Trigger Spool Status Changer
│ └─> For each spool with shop joint
│ └─> view('cron.spool-status-changer')
│
├─> Update WeldLogs
│ └─> UPDATE weld_logs SET painting_cycle = ?
│
└─> Comprehensive Paint Sync
├─> Get Paint Matrix data
├─> Get Paint System data
│
├─> Paint Follow Ups
│ ├─> SHOP records (spool-based)
│ └─> FIELD records (joint-based)
│
└─> Construction Paint Logs
└─> For each spool with shop joint
Paint Follow Ups Sync Flow
LineList saved
│
├─> Get/Create Paint Matrix
│
├─> Load Temperature Settings
│ └─> Get blasting_date from construction_paint_logs
│ └─> Calculate day of year
│ └─> Lookup temperature data
│
├─> Calculate Volumes
│ ├─> F Volume = π * (avg_nps)² * 200
│ └─> S Volume = SUM from MTO table
│
├─> For each Weld Log
│ │
│ ├─> SHOP Record Processing
│ │ ├─> Check if shop joint exists
│ │ ├─> Build base data + temp + volume
│ │ ├─> Check unique constraint
│ │ ├─> Find existing record with same cycle
│ │ ├─> Find records with different cycles
│ │ │
│ │ ├─> IF existing record with same cycle
│ │ │ ├─> IF all dates empty → Full update
│ │ │ └─> IF dates filled → Update only empty temp/volume
│ │ │
│ │ └─> IF no existing record with same cycle
│ │ ├─> Handle old records with different cycles
│ │ │ ├─> IF dates filled → Mark as HOLD
│ │ │ └─> IF dates empty → Update to new cycle
│ │ └─> Create new record
│ │
│ └─> FIELD Record Processing
│ └─> (Same logic as SHOP, but for joint_no)
│
└─> Return results
Key Concepts
1. Unique Constraints
Different tables have different unique constraints:
NDE Matrix: line + type_of_joint + fluid
Paint Follow Ups: line + spool_no_joint_no + cycle + location
Construction Paint Logs: line + spool + unit + painting_system_type_1
Paint Matrix: line + fluid_code
2. Date Field Protection
When painting cycles change, the system protects records where work has already started (date fields filled):
Paint Follow Ups Date Fields:
primer_coating_start_date,primer_coating_finish_datestart_intermediate_date2,finish_intermediate_date2final_coat_start_date3,final_coat_finish_date3
Construction Paint Logs Date Fields:
blasting_date,blasting_finish_datepainting_date_1,painting_finish_date_1painting_date_2,painting_finish_date_2painting_date_3,painting_finish_date_3rfi_date_1,rfi_date_2,rfi_date_3
Protection Logic:
- If ALL dates empty: Full record update allowed
- If ANY date filled:
- Keep existing record
- Mark as HOLD or keep current status
- Create new record for new painting cycle
- Only update empty temperature/volume fields
3. SHOP vs FIELD Records
SHOP Records:
- Based on
spool_number - Location: 'SHOP'
- Only created if shop joint (type_of_joint = 'S') exists for spool
- Uses S Volume
- Uses shop temperature data
FIELD Records:
- Based on
no_of_the_joint_as_per_as_built_survey(joint number) - Location: 'FIELD'
- Created for every joint with a joint number
- Uses F Volume
- Uses field temperature data
4. Temperature and Volume Calculations
Temperature Data:
- Stored in system settings as JSON
- Indexed by day of year (0-365)
- Includes:
temp_material_shop,shop_ambient,temp_material_field,field_ambient - Based on
blasting_datefrom construction_paint_logs
Volume Calculations:
// F Volume (Field)
$fAvgNps = weld_logs->where("type_of_joint", "F")->avg("nps_1");
$fVolume = round(π * pow($fAvgNps, 2) * 200, 2);
// S Volume (Shop)
$mtoTotal = SUM(quantity * POWER(odmm_1/2000, 2) * π) from m_t_o_s;
$sVolume = round($mtoTotal, 2);
Development Guide
Adding a New Trigger
- Create Trigger Class
<?php
namespace App\Services\LineListTriggers\Triggers;
use App\Services\LineListTriggers\Base\BaseLineListTrigger;
use Illuminate\Support\Facades\Log;
class MyNewTrigger extends BaseLineListTrigger
{
public function getName(): string
{
return 'My New Trigger';
}
public function getOrder(): int
{
return 12; // Choose appropriate order
}
public function getDependentFields(): array
{
return ['field1', 'field2']; // Fields this trigger cares about
}
protected function process($lineListData, $beforeData, array $context): array
{
// Your logic here
Log::debug("My trigger is processing", [
'line_no' => $lineListData->line_no
]);
// Do your operations
return [
'success' => true,
'records_processed' => 10
];
}
}
- Register in Registry
// app/Services/LineListTriggers/LineListTriggerRegistry.php
protected function registerTriggers()
{
// ... existing triggers ...
$this->register(new MyNewTrigger());
}
- Test
- Create a test line list record
- Modify dependent fields
- Check logs
- Verify operations
Modifying Existing Trigger
-
Find the Trigger Class
- Navigate to
app/Services/LineListTriggers/Triggers/ - Open the appropriate trigger file
- Navigate to
-
Modify
process()Method- Make your changes
- Add logging for debugging
- Return results array
-
Update Documentation
- Update this guide if logic changed
- Update trigger README if needed
Testing
Unit Testing (Recommended):
use App\Services\LineListTriggers\Triggers\MyTrigger;
$trigger = new MyTrigger();
$lineList = (object) [
'line_no' => 'TEST-001',
'painting_cycle' => 'PC-01'
];
$result = $trigger->execute($lineList, null, []);
$this->assertTrue($result['success']);
Integration Testing:
- Create test line list record in database
- Modify fields
- Check logs in
storage/logs/laravel.log - Verify database changes
Troubleshooting
Common Issues
1. Trigger Not Running
Symptoms: Trigger not executing when expected
Diagnosis:
- Check
shouldRun()logic - Verify dependent fields are actually changing
- Check logs for "Skipping trigger" messages
Solution:
// Override shouldRun for debugging
public function shouldRun(array $changedFields, bool $isNewRecord): bool
{
Log::debug("shouldRun check", [
'trigger' => $this->getName(),
'changedFields' => $changedFields,
'dependentFields' => $this->getDependentFields(),
'isNewRecord' => $isNewRecord
]);
return parent::shouldRun($changedFields, $isNewRecord);
}
2. Date Fields Not Protected
Symptoms: Records with filled dates being overwritten
Diagnosis:
- Check
hasAllEmptyDates()logic - Verify date field names match database
Solution:
// Add detailed logging
$hasAllEmptyDates = $this->hasAllEmptyDates($record);
Log::debug("Date protection check", [
'record_id' => $record->id,
'hasAllEmptyDates' => $hasAllEmptyDates,
'date_fields' => [
'primer_coating_start_date' => $record->primer_coating_start_date,
'primer_coating_finish_date' => $record->primer_coating_finish_date,
// ... other dates
]
]);
3. Duplicate Records Created
Symptoms: Multiple records with same unique constraint
Diagnosis:
- Check unique constraint conditions
- Verify
whereConditionvsuniqueConstraintCondition
Solution:
// Add logging before insert
Log::debug("Checking for existing record", [
'uniqueConstraint' => $uniqueConstraintCondition,
'existing' => $existingRecord ? $existingRecord->id : 'none'
]);
4. Performance Issues
Symptoms: Trigger execution takes too long
Diagnosis:
- Check logs for execution duration
- Identify slow triggers
Solution:
- Add indexes to frequently queried fields
- Optimize database queries
- Consider caching
Debugging Tips
- Enable Detailed Logging:
// Add at start of process() method
Log::debug("=== {$this->getName()} DETAILED DEBUG ===", [
'lineList' => $lineListData,
'beforeData' => $beforeData,
'context' => $context
]);
- Check Execution Order:
// In LineListTriggerManager
Log::debug("Executing triggers in order", [
'triggers' => array_map(fn($t) => [
'name' => $t->getName(),
'order' => $t->getOrder()
], $triggers)
]);
- Monitor Database Changes:
-- Check recent changes
SELECT * FROM paint_follow_ups
WHERE updated_at > NOW() - INTERVAL 1 MINUTE
ORDER BY updated_at DESC;
Performance Optimization
Current Performance
- Total Execution Time: ~2-5 seconds for typical line list save
- Memory Usage: ~50-100 MB peak
- Database Queries: ~50-100 queries per save
Optimization Strategies
1. Query Optimization
Before:
foreach ($weldLogs as $weldLog) {
$hasShopJoint = db("weld_logs")
->where('line_number', $lineList->line_no)
->where('spool_number', $weldLog->spool_number)
->where('type_of_joint', 'S')
->exists();
}
// N+1 query problem
After:
$spoolsWithShopJoints = db("weld_logs")
->where('line_number', $lineList->line_no)
->where('type_of_joint', 'S')
->pluck('spool_number')
->toArray();
foreach ($weldLogs as $weldLog) {
$hasShopJoint = in_array($weldLog->spool_number, $spoolsWithShopJoints);
}
// Single query
2. Caching
// Cache paint matrix for this line
$cacheKey = "paint_matrix_{$lineList->line_no}_{$lineList->fluid_code}";
$paintMatrix = Cache::remember($cacheKey, 60, function() use ($lineList) {
return PaintMatrix::where('line', $lineList->line_no)
->where('fluid_code', $lineList->fluid_code)
->first();
});
3. Batch Operations
// Instead of individual inserts
foreach ($data as $item) {
db("paint_follow_ups")->insert($item);
}
// Use batch insert
db("paint_follow_ups")->insert($dataArray);
4. Conditional Execution
// Skip expensive operations if data hasn't changed
if (!in_array('painting_cycle', $changedFields)) {
return ['skipped' => true, 'reason' => 'painting_cycle_unchanged'];
}
Monitoring
Add Performance Logging:
protected function process($lineListData, $beforeData, array $context): array
{
$queryCount = DB::getQueryLog();
$memoryStart = memory_get_usage();
// Your logic here
$memoryEnd = memory_get_usage();
$memoryUsed = $memoryEnd - $memoryStart;
Log::info("Performance metrics", [
'trigger' => $this->getName(),
'queries_executed' => count(DB::getQueryLog()) - count($queryCount),
'memory_used_mb' => round($memoryUsed / 1024 / 1024, 2)
]);
return $results;
}
Conclusion
The Line List Triggers System provides a robust, maintainable, and extensible architecture for handling complex business logic. By breaking down the monolithic trigger file into 11 independent classes, we've achieved:
✅ Better Maintainability: Each trigger has a single responsibility
✅ Improved Testability: Easy to unit test individual triggers
✅ Clear Documentation: Each trigger is well-documented
✅ Enhanced Logging: Detailed execution tracking
✅ Easy Extension: Simple to add new triggers
✅ Performance Monitoring: Built-in execution time tracking
For questions or issues, please refer to:
- Service README
- WeldLog Triggers System
- DevQMS Developer Documentation