Files
citrus-cms/resources/views/guide/line-list-triggers-system.md
T
2026-04-28 21:15:09 +03:00

50 KiB
Raw Blame History

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:

  1. Engineer updates a line specification (e.g., changes painting cycle)
  2. System detects which fields changed
  3. Automatically synchronizes data to 8+ related tables
  4. Protects completed work (records with dates) from being overwritten
  5. 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:

  1. Mühendis bir hat spesifikasyonunu günceller (örn. boya döngüsünü değiştirir)
  2. Sistem hangi alanların değiştiğini algılar
  3. Otomatik olarak 8+ ilgili tabloya veri senkronize eder
  4. Tamamlanmış işleri korur (tarihi olan kayıtların üzerine yazmaz)
  5. 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 → Updates painting_cycle column for all welds on this line
  • construction_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_cycle
  • color_systems → Creates/updates one record per fluid_code

Columns Changed in paint_systems:

  • paint_cycle = line_lists.painting_cycle
  • revision = line_lists.rev

Columns Changed in color_systems:

  • fluid_code_description = line_lists.fluid_ru
  • fluid_code = line_lists.fluid_code
  • design_temperature = line_lists.design_temperature
  • working_temperature = line_lists.working_temperature
  • unit = 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 exist
  • paint_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_code
  • paint_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_code
  • paint_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 → Updates spool_status column 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

  1. Introduction
  2. Architecture Overview
  3. System Components
  4. Trigger Breakdown
  5. Execution Flow
  6. Data Flow Diagrams
  7. Key Concepts
  8. Development Guide
  9. Troubleshooting
  10. 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 shouldRun checks
  • 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:

  1. Spool Status Changer Trigger

    • Finds all weld logs with shop joints for this line
    • Triggers spool-status-changer view for each unique spool
  2. WeldLogs Painting Cycle Update

    • Updates painting_cycle field in all weld logs for this line
  3. 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 beforeData exists and painting_cycle changed
  • Processes both SHOP (spool-based) and FIELD (joint-based) records
  • Pulls data from paint_matrices and paint_systems tables

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:

  1. Delete paint_matrices records
  2. Delete paint_follow_ups records (only if date fields are null)
  3. Delete construction_paint_logs records (only if date fields are null)
  4. Delete nde_matrices records

Date Field Protection: Does NOT delete records if any of these date fields are filled:

  • primer_coating_start_date, primer_coating_finish_date
  • start_intermediate_date2, finish_intermediate_date2
  • final_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:

  1. Get Zone Information

    • Fetches project field from weld logs for zone assignment
  2. Create/Update Paint Matrix

    • Unique constraint: line + fluid_code
    • Updates or creates paint matrix record
  3. Surface Preparation Sync

    • Syncs surface_preparation (for SHOP) and touch_up_of_damaged_parts (for FIELD)
    • From paint_matrices to paint_follow_ups
  4. 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:

  1. Collect Spec Joint Types

    • Reads all weld logs for this line
    • Groups by fluid_code, line_number, and type_of_welds
    • Creates array: $specJointTypes[$fluid_code][$line_no][] = type_of_welds
  2. Update Fluid Codes in WeldLogs

    • If weld log has empty fluid_code, copies from line list
  3. 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)

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:

  1. Paint Systems

    • Updates or inserts into paint_systems table
    • Key: paint_cycle
    • Data: paint_cycle, revision
  2. Color Systems

    • Updates or inserts into color_systems table
    • Key: fluid_code
    • Data: fluid_code_description, fluid_code, design_temperature, working_temperature, unit

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:

  1. Get Paint System and Color System

    • Fetches from respective tables
  2. RAL Code to Russian Color Mapping

    • Uses setting("ral-codes") JSON
    • Maps ral_1, ral_2, ral_3 to Russian color descriptions
  3. 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

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:

  1. Get Paint Matrix and Paint System

    • Fetches related data for RAL values and paint system details
  2. Process Each Weld Log

    • Only processes spools with shop joints
    • Creates unique constraint: line + spool + unit + painting_system_type_1
  3. 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
  4. 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:

  1. Get or Create Paint Matrix

    • Ensures paint matrix exists for this line
  2. Temperature Calculations

    • Loads temperature settings from system settings
    • Gets blasting_date from construction_paint_logs
    • Calculates day of year and looks up temperature data
  3. Volume Calculations

    • F Volume (Field): π * (avg_nps)² * 200
    • S Volume (Shop): Sum from MTO table
  4. 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
  5. 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

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-changer view
  • 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-followup view
  • Deletes orphaned paint follow up records

Error Handling: Non-critical - logs warning if fails, doesn't throw exception


Execution Flow

Step-by-Step Flow

  1. LineList Save Triggered

    // AdminController or DevExtreme save operation
    $lineList->save();
    
  2. Trigger File Called

    // app/Http/Controllers/SaveTrigger/line_lists.php
    $id = $request['key'];
    $lineList = db($tableName)->where("id", $id)->first();
    
  3. Detect Changes

    $isNewRecord = is_null($beforeData);
    $changedFields = detectChangedFields($lineList, $beforeData);
    
  4. Initialize Manager

    $registry = new LineListTriggerRegistry();
    $manager = new LineListTriggerManager($registry);
    
  5. Execute Triggers

    $results = $manager->executeTriggers(
        $lineList,
        $beforeData,
        $changedFields,
        $isNewRecord
    );
    
  6. For Each Trigger:

    • Check shouldRun()
    • If true, call execute()
    • Log start time
    • Run process() method
    • Log completion with duration
    • Return results
  7. 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_date
  • start_intermediate_date2, finish_intermediate_date2
  • final_coat_start_date3, final_coat_finish_date3

Construction Paint Logs Date Fields:

  • 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
  • rfi_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_date from 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

  1. 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
        ];
    }
}
  1. Register in Registry
// app/Services/LineListTriggers/LineListTriggerRegistry.php

protected function registerTriggers()
{
    // ... existing triggers ...
    $this->register(new MyNewTrigger());
}
  1. Test
    • Create a test line list record
    • Modify dependent fields
    • Check logs
    • Verify operations

Modifying Existing Trigger

  1. Find the Trigger Class

    • Navigate to app/Services/LineListTriggers/Triggers/
    • Open the appropriate trigger file
  2. Modify process() Method

    • Make your changes
    • Add logging for debugging
    • Return results array
  3. 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:

  1. Create test line list record in database
  2. Modify fields
  3. Check logs in storage/logs/laravel.log
  4. 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 whereCondition vs uniqueConstraintCondition

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

  1. Enable Detailed Logging:
// Add at start of process() method
Log::debug("=== {$this->getName()} DETAILED DEBUG ===", [
    'lineList' => $lineListData,
    'beforeData' => $beforeData,
    'context' => $context
]);
  1. Check Execution Order:
// In LineListTriggerManager
Log::debug("Executing triggers in order", [
    'triggers' => array_map(fn($t) => [
        'name' => $t->getName(),
        'order' => $t->getOrder()
    ], $triggers)
]);
  1. 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: