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

1485 lines
50 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 motoru**dur. İ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](#introduction)
2. [Architecture Overview](#architecture-overview)
3. [System Components](#system-components)
4. [Trigger Breakdown](#trigger-breakdown)
5. [Execution Flow](#execution-flow)
6. [Data Flow Diagrams](#data-flow-diagrams)
7. [Key Concepts](#key-concepts)
8. [Development Guide](#development-guide)
9. [Troubleshooting](#troubleshooting)
10. [Performance Optimization](#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:
```php
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**:
```php
- 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**:
```php
$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**:
```php
$ht = 0;
if($lineListData->pwht == "YES") $ht = 100;
```
**Data Fields**:
```php
$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**:
```php
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**:
```php
$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**:
```php
$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**
```php
// AdminController or DevExtreme save operation
$lineList->save();
```
2. **Trigger File Called**
```php
// app/Http/Controllers/SaveTrigger/line_lists.php
$id = $request['key'];
$lineList = db($tableName)->where("id", $id)->first();
```
3. **Detect Changes**
```php
$isNewRecord = is_null($beforeData);
$changedFields = detectChangedFields($lineList, $beforeData);
```
4. **Initialize Manager**
```php
$registry = new LineListTriggerRegistry();
$manager = new LineListTriggerManager($registry);
```
5. **Execute Triggers**
```php
$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**:
```php
// 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
<?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
];
}
}
```
2. **Register in Registry**
```php
// app/Services/LineListTriggers/LineListTriggerRegistry.php
protected function registerTriggers()
{
// ... existing triggers ...
$this->register(new MyNewTrigger());
}
```
3. **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):
```php
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**:
```php
// 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**:
```php
// 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**:
```php
// 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**:
```php
// Add at start of process() method
Log::debug("=== {$this->getName()} DETAILED DEBUG ===", [
'lineList' => $lineListData,
'beforeData' => $beforeData,
'context' => $context
]);
```
2. **Check Execution Order**:
```php
// In LineListTriggerManager
Log::debug("Executing triggers in order", [
'triggers' => array_map(fn($t) => [
'name' => $t->getName(),
'order' => $t->getOrder()
], $triggers)
]);
```
3. **Monitor Database Changes**:
```sql
-- 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**:
```php
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**:
```php
$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
```php
// 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
```php
// 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
```php
// 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**:
```php
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](../../app/Services/LineListTriggers/README.md)
- [WeldLog Triggers System](weld-log-triggers-system.md)
- DevQMS Developer Documentation