İlk temizlik tamamlandı bir önceki projeden
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Services\RegisterCreator\ExcelRowHandler;
|
||||
|
||||
abstract class AbstractDocumentProcessor
|
||||
{
|
||||
protected array $weldLogData;
|
||||
protected array $document;
|
||||
protected Worksheet $sheet;
|
||||
protected int $currentRow;
|
||||
protected string $registerColumnBased;
|
||||
protected array $settings;
|
||||
protected string $logFilePath;
|
||||
protected ExcelRowHandler $excelRowHandler;
|
||||
protected int $documentsAdded = 0; // Counter for documents added to Excel
|
||||
|
||||
/**
|
||||
* Process the document
|
||||
*/
|
||||
abstract public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int;
|
||||
|
||||
/**
|
||||
* Initialize processor
|
||||
*/
|
||||
protected function initialize(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int $currentRow,
|
||||
array $settings
|
||||
): void {
|
||||
$this->weldLogData = $weldLogData;
|
||||
$this->document = $document;
|
||||
$this->sheet = $sheet;
|
||||
$this->currentRow = $currentRow;
|
||||
$this->settings = $settings;
|
||||
$this->registerColumnBased = $settings['register_column_based'] ?? 'line_number';
|
||||
$this->excelRowHandler = new ExcelRowHandler();
|
||||
$this->documentsAdded = 0; // Reset counter for each document type
|
||||
|
||||
// Set documents array for dynamic type order mapping
|
||||
if (isset($settings['all_documents']) && is_array($settings['all_documents'])) {
|
||||
$this->excelRowHandler->setDocuments($settings['all_documents']);
|
||||
|
||||
Log::debug("ExcelRowHandler initialized with documents", [
|
||||
'processor' => class_basename($this),
|
||||
'documents_count' => count($settings['all_documents'])
|
||||
]);
|
||||
}
|
||||
|
||||
// Setup log file path
|
||||
$lineIdentifier = $weldLogData[$this->registerColumnBased] ?? 'unknown';
|
||||
$basePath = $settings['path'] ?? '';
|
||||
$this->logFilePath = "{$basePath}/{$lineIdentifier}/log.txt";
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize search term for file matching
|
||||
*/
|
||||
protected function normalizeSearchTerm(string $term): string
|
||||
{
|
||||
$term = str_replace("/", "*", $term);
|
||||
$term = str_replace(" ", "*", $term);
|
||||
$term = str_replace("\\", "*", $term);
|
||||
return $term;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for files using glob pattern
|
||||
*/
|
||||
protected function searchFiles(string $pattern): array
|
||||
{
|
||||
Log::debug('Searching for files', ['pattern' => $pattern]);
|
||||
$files = glob($pattern);
|
||||
|
||||
if ($files === false) {
|
||||
$files = [];
|
||||
}
|
||||
|
||||
Log::debug('Files found', ['count' => count($files)]);
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full folder path for current line
|
||||
*/
|
||||
protected function getFullFolder(): string
|
||||
{
|
||||
$path = $this->settings['path'] ?? '';
|
||||
$lineIdentifier = $this->weldLogData[$this->registerColumnBased] ?? 'unknown';
|
||||
$basePath = "storage/documents/{$path}";
|
||||
return "{$basePath}/{$lineIdentifier}/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to log file
|
||||
* Note: This method is now silent to keep log files clean
|
||||
* Only errors and NOT FOUND messages are logged
|
||||
*/
|
||||
protected function log(string $message, string $level = 'info'): void
|
||||
{
|
||||
// Don't write info messages to log file anymore - keep it clean
|
||||
// Only errors will be logged via ExcelRowHandler
|
||||
|
||||
// Only log to Laravel debug for development purposes
|
||||
Log::debug($message, [
|
||||
'processor' => class_basename($this),
|
||||
'line' => $this->weldLogData[$this->registerColumnBased] ?? 'unknown'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contractor name
|
||||
*/
|
||||
protected function getContractor(): string
|
||||
{
|
||||
$subcontractors = \Cache::get("subcontractors", []);
|
||||
$contractorKey = $this->weldLogData['contractor'] ?? '';
|
||||
|
||||
if (isset($subcontractors[$contractorKey])) {
|
||||
return $subcontractors[$contractorKey]->company_name_ru ?? $contractorKey;
|
||||
}
|
||||
|
||||
return $contractorKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add row to Excel using ExcelRowHandler service
|
||||
*
|
||||
* @param array $search File paths to search for
|
||||
* @param string $lineNumber Line number or identifier
|
||||
* @param string $documentDate Document date
|
||||
*
|
||||
* @return int Next row position
|
||||
*/
|
||||
protected function addRowToExcel(
|
||||
array $search,
|
||||
string $lineNumber,
|
||||
string $documentDate
|
||||
): int {
|
||||
$fullFolder = $this->getFullFolder();
|
||||
$override = $this->settings['override'] ?? false;
|
||||
|
||||
// Calculate current row number based on starting row + documents added so far
|
||||
$startRowNo = $this->settings['row_no'] ?? 1;
|
||||
$currentRowNo = $startRowNo + $this->documentsAdded;
|
||||
|
||||
Log::info("🟢 AbstractDocumentProcessor->addRowToExcel() CALLED", [
|
||||
'processor' => class_basename($this),
|
||||
'row_no' => $currentRowNo,
|
||||
'start_row_no' => $startRowNo,
|
||||
'documents_added' => $this->documentsAdded,
|
||||
'doc_type' => $this->document['type'] ?? 'N/A',
|
||||
'doc_title2' => $this->document['title2'] ?? 'N/A',
|
||||
'line' => $lineNumber,
|
||||
'search_files' => count($search)
|
||||
]);
|
||||
|
||||
$oldCurrentRow = $this->currentRow;
|
||||
|
||||
// Ensure the actual line identifier is available in the document array
|
||||
// This allows processors (like TemplateProcessor) to override $lineNumber for Excel
|
||||
// while still preserving the real line number for filename generation.
|
||||
$this->document['real_line_identifier'] = $this->weldLogData[$this->registerColumnBased] ?? $lineNumber;
|
||||
|
||||
$newRow = $this->excelRowHandler->addRow(
|
||||
$search,
|
||||
$this->document,
|
||||
$fullFolder,
|
||||
$lineNumber,
|
||||
$documentDate,
|
||||
$currentRowNo,
|
||||
$this->sheet,
|
||||
$this->currentRow,
|
||||
$override
|
||||
);
|
||||
|
||||
// If a row was added, increment the documents counter and update current row
|
||||
if ($newRow > $oldCurrentRow) {
|
||||
$this->documentsAdded++;
|
||||
$this->currentRow = $newRow; // ← UPDATE CURRENT ROW!
|
||||
}
|
||||
|
||||
return $newRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of documents added during processing
|
||||
*/
|
||||
public function getDocumentsAdded(): int
|
||||
{
|
||||
return $this->documentsAdded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class DocumentProcedureProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(array $weldLogData, array $document, Worksheet $sheet, int &$currentRow, array $settings): int
|
||||
{
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$procedures = [];
|
||||
if (!empty($document['title2'])) {
|
||||
$procedures = array_map('trim', explode(",", $document['title2']));
|
||||
}
|
||||
|
||||
// Prepare procedures with dates for sorting
|
||||
$proceduresWithDates = [];
|
||||
foreach ($procedures as $procedureNo) {
|
||||
$procedure = db("document_procedures")->where("document_no", $procedureNo)->first();
|
||||
if (!$procedure) continue;
|
||||
|
||||
$proceduresWithDates[] = [
|
||||
'procedure' => $procedure,
|
||||
'date' => $procedure->publish_date ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by publish_date (oldest first) - reverse insertion order
|
||||
usort($proceduresWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
foreach ($proceduresWithDates as $procData) {
|
||||
$procedure = $procData['procedure'];
|
||||
$normalized = $this->normalizeSearchTerm($procedure->document_no);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
$document['title2'] = $procedure->document_no;
|
||||
$this->document = $document;
|
||||
$documentDate = $procData['date'];
|
||||
|
||||
$newRow = $this->addRowToExcel($search, $procedure->document_no, $documentDate);
|
||||
if ($newRow > $currentRow) { $currentRow = $newRow; }
|
||||
}
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use Exception;
|
||||
|
||||
class DocumentProcessorFactory
|
||||
{
|
||||
/**
|
||||
* Create document processor instance based on type
|
||||
*/
|
||||
public static function make(string $type, ?array $document = null): AbstractDocumentProcessor
|
||||
{
|
||||
// Check if this is a dynamic document
|
||||
if ($type === 'dynamic' || ($document && isset($document['is_dynamic']) && $document['is_dynamic'])) {
|
||||
return new DynamicMappingProcessor();
|
||||
}
|
||||
|
||||
return match($type) {
|
||||
'qa' => new QaDocumentProcessor(),
|
||||
'wdb' => new WdbDocumentProcessor(),
|
||||
'wps_naks_technology' => new WpsNaksTechnologyProcessor(),
|
||||
'naks_consumables_certificate' => new NaksConsumablesCertificateProcessor(),
|
||||
'naks_consumables_inspection_test_report' => new NaksConsumablesInspectionProcessor(),
|
||||
'drawings' => new DrawingsProcessor(),
|
||||
'materials' => new MaterialsProcessor(),
|
||||
'incoming_control_materials' => new IncomingControlProcessor(),
|
||||
'template' => new TemplateProcessor(),
|
||||
'prikaz' => new PrikazProcessor(),
|
||||
'document-procedure' => new DocumentProcedureProcessor(),
|
||||
'dynamic' => new DynamicMappingProcessor(),
|
||||
default => new GenericDocumentProcessor(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if processor exists for given type
|
||||
*/
|
||||
public static function exists(string $type): bool
|
||||
{
|
||||
try {
|
||||
self::make($type);
|
||||
return true;
|
||||
} catch (\Throwable $th) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class DrawingsProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process drawings documents
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing Drawings");
|
||||
|
||||
$lineIdentifier = $weldLogData[$this->registerColumnBased];
|
||||
$normalized = $this->normalizeSearchTerm($lineIdentifier);
|
||||
$search = $this->searchFiles("{$document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
// Use the new ExcelRowHandler service method
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$lineIdentifier,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DynamicMappingProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing dynamic mapped document: {$document['title2']}");
|
||||
|
||||
// Check if this document has SQL query configuration
|
||||
if (empty($document['sql_query'])) {
|
||||
$this->log("No SQL query found for dynamic document", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
return $this->processSqlBasedMapping($currentRow);
|
||||
}
|
||||
|
||||
private function processSqlBasedMapping(int &$currentRow): int
|
||||
{
|
||||
try {
|
||||
// Execute SQL query with placeholder replacement
|
||||
$results = $this->executeSqlQuery();
|
||||
|
||||
if (empty($results)) {
|
||||
$this->log("SQL query returned no results", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
$this->log("Found " . count($results) . " records from SQL query");
|
||||
|
||||
// Process each result
|
||||
foreach ($results as $result) {
|
||||
try {
|
||||
$identifier = $result['identifier'];
|
||||
$recordData = $result['data'];
|
||||
$documentDate = $result['document_date'];
|
||||
|
||||
// Generate row title using pattern
|
||||
$rowTitle = $this->generateRowTitle($recordData);
|
||||
|
||||
// Generate file search pattern
|
||||
$searchPattern = $this->generateFileSearchPattern($recordData);
|
||||
$fullPath = "storage/documents/{$this->document['path']}/{$searchPattern}";
|
||||
|
||||
// Search for files
|
||||
$files = $this->searchFiles($fullPath);
|
||||
|
||||
if (empty($files)) {
|
||||
$this->log("No files found for: {$identifier} (pattern: {$searchPattern})", 'warning');
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->log("✓ Found " . count($files) . " files for: {$identifier}");
|
||||
|
||||
// Update document titles
|
||||
$this->document['title2'] = $identifier;
|
||||
$this->document['title4'] = $rowTitle;
|
||||
|
||||
// Add row to Excel
|
||||
$newRow = $this->addRowToExcel(
|
||||
$files,
|
||||
$identifier,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
|
||||
$this->log("✓ Processed: {$identifier} (from SQL query)");
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing SQL result: {$th->getMessage()}", 'error');
|
||||
Log::error("Dynamic mapping result processing error", [
|
||||
'identifier' => $result['identifier'] ?? 'unknown',
|
||||
'error' => $th->getMessage(),
|
||||
'trace' => $th->getTraceAsString()
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("SQL query execution error: {$th->getMessage()}", 'error');
|
||||
Log::error("Dynamic mapping SQL execution error", [
|
||||
'sql_query' => $this->document['sql_query'] ?? 'N/A',
|
||||
'error' => $th->getMessage(),
|
||||
'trace' => $th->getTraceAsString()
|
||||
]);
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute SQL query with placeholder replacement
|
||||
*/
|
||||
private function executeSqlQuery(): array
|
||||
{
|
||||
$sqlQuery = $this->document['sql_query'] ?? '';
|
||||
|
||||
if (empty($sqlQuery)) {
|
||||
throw new \Exception("No SQL query defined for dynamic document");
|
||||
}
|
||||
|
||||
// Replace placeholders
|
||||
$executedQuery = $this->replacePlaceholders($sqlQuery);
|
||||
|
||||
Log::info("Executing dynamic SQL query", [
|
||||
'original_query' => $sqlQuery,
|
||||
'executed_query' => $executedQuery
|
||||
]);
|
||||
|
||||
try {
|
||||
$startTime = microtime(true);
|
||||
$results = DB::select($executedQuery);
|
||||
$executionTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||
|
||||
Log::info("Dynamic SQL query executed successfully", [
|
||||
'record_count' => count($results),
|
||||
'execution_time' => $executionTime . 'ms'
|
||||
]);
|
||||
|
||||
return $this->formatResults($results);
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Dynamic SQL query execution failed", [
|
||||
'query' => $executedQuery,
|
||||
'error' => $th->getMessage()
|
||||
]);
|
||||
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace :placeholder with actual values from register data
|
||||
*/
|
||||
private function replacePlaceholders(string $query): string
|
||||
{
|
||||
$result = $query;
|
||||
|
||||
// Find all :placeholder patterns
|
||||
preg_match_all('/:(\w+)/', $query, $matches);
|
||||
|
||||
foreach ($matches[1] as $placeholder) {
|
||||
$value = $this->weldLogData[$placeholder] ?? null;
|
||||
|
||||
if ($value !== null) {
|
||||
// Escape value for SQL
|
||||
$escapedValue = DB::getPdo()->quote($value);
|
||||
$result = str_replace(":{$placeholder}", $escapedValue, $result);
|
||||
} else {
|
||||
Log::warning("Placeholder value not found", [
|
||||
'placeholder' => $placeholder,
|
||||
'available_fields' => array_keys($this->weldLogData)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format SQL results to standard structure
|
||||
*/
|
||||
private function formatResults(array $results): array
|
||||
{
|
||||
$identifierField = $this->document['identifier_field'] ?? 'identifier';
|
||||
$dateField = $this->document['date_field'] ?? 'document_date';
|
||||
|
||||
$formatted = [];
|
||||
|
||||
foreach ($results as $result) {
|
||||
$recordArray = (array) $result;
|
||||
|
||||
$identifier = $recordArray[$identifierField] ?? $recordArray['id'] ?? 'unknown';
|
||||
$date = $recordArray[$dateField] ?? '';
|
||||
|
||||
$formatted[] = [
|
||||
'identifier' => $identifier,
|
||||
'document_date' => $date,
|
||||
'data' => $recordArray
|
||||
];
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate file search pattern with field replacements
|
||||
*/
|
||||
private function generateFileSearchPattern(array $recordData): string
|
||||
{
|
||||
$pattern = $this->document['file_search_pattern'] ?? '*{identifier}*.pdf';
|
||||
|
||||
// Replace {field_name} with actual values
|
||||
foreach ($recordData as $key => $value) {
|
||||
$pattern = str_replace("{{$key}}", $value, $pattern);
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate row title with pattern
|
||||
*/
|
||||
private function generateRowTitle(array $recordData): string
|
||||
{
|
||||
$pattern = $this->document['title4_pattern'] ?? '{identifier}';
|
||||
|
||||
// Replace {field_name} with actual values
|
||||
foreach ($recordData as $key => $value) {
|
||||
$pattern = str_replace("{{$key}}", $value, $pattern);
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
/**
|
||||
* Generic processor for document types that don't have specific processors
|
||||
*/
|
||||
class GenericDocumentProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process generic document
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing generic document: {$document['type']}");
|
||||
|
||||
$lineIdentifier = $weldLogData[$this->registerColumnBased];
|
||||
$normalized = $this->normalizeSearchTerm($lineIdentifier);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$lineIdentifier,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class IncomingControlProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(array $weldLogData, array $document, Worksheet $sheet, int &$currentRow, array $settings): int
|
||||
{
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$project = $weldLogData['line_number'] ?? '';
|
||||
if (empty($project)) return $currentRow;
|
||||
|
||||
$incomingControls = db("incoming_controls")->where("project", $project)->groupBy("certificate_no", "description_ru")->get()->toArray();
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
|
||||
// Sort by certificate_date (oldest first) - reverse insertion order
|
||||
usort($incomingControls, function($a, $b) use ($placeholderReplacer, $weldLogData) {
|
||||
$a = (object) $a;
|
||||
$b = (object) $b;
|
||||
$dateA = $a->certificate_date ?? $a->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
$dateB = $b->certificate_date ?? $b->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
$timestampA = !empty($dateA) ? strtotime($dateA) : 0;
|
||||
$timestampB = !empty($dateB) ? strtotime($dateB) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
foreach ($incomingControls as $control) {
|
||||
$control = (object) $control;
|
||||
if (empty($control->certificate_no)) continue;
|
||||
$normalized = $this->normalizeSearchTerm($control->certificate_no);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
$document['title2'] = $control->description_ru;
|
||||
$document['incoming_control_description'] = $control->description_ru;
|
||||
$document['title3'] = $control->certificate_no;
|
||||
$this->document = $document;
|
||||
$documentDate = $control->certificate_date ?? $control->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
$newRow = $this->addRowToExcel($search, $control->certificate_no, $documentDate);
|
||||
if ($newRow > $currentRow) { $currentRow = $newRow; }
|
||||
}
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class MaterialsProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process materials documents
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing Materials");
|
||||
|
||||
// Get all certificates from same line
|
||||
$allRecords = db("weld_logs")
|
||||
->where($this->registerColumnBased, $weldLogData['line_number'])
|
||||
->get();
|
||||
|
||||
$uniqueCertificates = [];
|
||||
|
||||
foreach ($allRecords as $record) {
|
||||
if (!empty($record->certificate_number_of_1) && !in_array($record->certificate_number_of_1, $uniqueCertificates)) {
|
||||
$uniqueCertificates[] = $record->certificate_number_of_1;
|
||||
}
|
||||
if (!empty($record->certificate_number_of_2) && !in_array($record->certificate_number_of_2, $uniqueCertificates)) {
|
||||
$uniqueCertificates[] = $record->certificate_number_of_2;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("Found " . count($uniqueCertificates) . " unique certificates");
|
||||
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
|
||||
// Prepare certificates with dates for sorting
|
||||
$certificatesWithDates = [];
|
||||
foreach ($uniqueCertificates as $certificateNumber) {
|
||||
$incomingControl = db("incoming_controls")
|
||||
->where("certificate_no", $certificateNumber)
|
||||
->first();
|
||||
|
||||
$documentDate = $incomingControl->certificate_date ?? $incomingControl->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
$certificatesWithDates[] = [
|
||||
'certificate_no' => $certificateNumber,
|
||||
'incoming_control' => $incomingControl,
|
||||
'date' => $documentDate
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by date (oldest first) - reverse insertion order
|
||||
usort($certificatesWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("Certificates sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($certificatesWithDates as $certData) {
|
||||
$certificateNumber = $certData['certificate_no'];
|
||||
$incomingControl = $certData['incoming_control'];
|
||||
$documentDate = $certData['date'];
|
||||
try {
|
||||
if ($incomingControl) {
|
||||
$document['title2'] = $incomingControl->description_ru;
|
||||
$document['incoming_control_description'] = $incomingControl->description_ru;
|
||||
$document['file_name'] = $certificateNumber;
|
||||
} else {
|
||||
$document['title2'] = "-";
|
||||
$document['incoming_control_description'] = "-";
|
||||
$document['file_name'] = $certificateNumber;
|
||||
}
|
||||
|
||||
$normalized = $this->normalizeSearchTerm($certificateNumber);
|
||||
$search = $this->searchFiles("{$document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
// Update document reference for current iteration
|
||||
$this->document = $document;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$certificateNumber,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing certificate: {$certificateNumber} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class NaksConsumablesCertificateProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(array $weldLogData, array $document, Worksheet $sheet, int &$currentRow, array $settings): int
|
||||
{
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$certificates = array_filter([
|
||||
$weldLogData['welding_materials_1_certificate_no'] ?? '',
|
||||
$weldLogData['welding_materials_2_certificate_no'] ?? '',
|
||||
$weldLogData['welding_materials_3_certificate_no'] ?? ''
|
||||
]);
|
||||
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
|
||||
foreach ($certificates as $certNo) {
|
||||
$normalized = $this->normalizeSearchTerm($certNo);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
$document['title2'] = $certNo;
|
||||
$this->document = $document;
|
||||
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
$newRow = $this->addRowToExcel($search, $certNo, $documentDate);
|
||||
if ($newRow > $currentRow) { $currentRow = $newRow; }
|
||||
}
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class NaksConsumablesInspectionProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(array $weldLogData, array $document, Worksheet $sheet, int &$currentRow, array $settings): int
|
||||
{
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$lotNumbers = array_filter([
|
||||
$weldLogData['welding_materials_1_lot_no'] ?? '',
|
||||
$weldLogData['welding_materials_2_lot_no'] ?? '',
|
||||
$weldLogData['welding_materials_3_lot_no'] ?? ''
|
||||
]);
|
||||
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
|
||||
foreach ($lotNumbers as $lotNo) {
|
||||
$normalized = $this->normalizeSearchTerm($lotNo);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
$document['title2'] = $lotNo;
|
||||
$this->document = $document;
|
||||
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
$newRow = $this->addRowToExcel($search, $lotNo, $documentDate);
|
||||
if ($newRow > $currentRow) { $currentRow = $newRow; }
|
||||
}
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class PrikazProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
public function process(array $weldLogData, array $document, Worksheet $sheet, int &$currentRow, array $settings): int
|
||||
{
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$project = $weldLogData['project'] ?? '';
|
||||
if (empty($project)) return $currentRow;
|
||||
|
||||
$workPermitDocs = db("work_permit_documents")->where("zone", "like", "%" . $project . "%")->get()->toArray();
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
|
||||
// Sort by issue_date (oldest first) - reverse insertion order
|
||||
usort($workPermitDocs, function($a, $b) use ($placeholderReplacer, $weldLogData) {
|
||||
$a = (object) $a;
|
||||
$b = (object) $b;
|
||||
$dateA = $a->issue_date ?? $a->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
$dateB = $b->issue_date ?? $b->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
$timestampA = !empty($dateA) ? strtotime($dateA) : 0;
|
||||
$timestampB = !empty($dateB) ? strtotime($dateB) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
foreach ($workPermitDocs as $doc) {
|
||||
$doc = (object) $doc;
|
||||
$normalized = $this->normalizeSearchTerm($doc->document_number);
|
||||
$search = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized}*.pdf");
|
||||
$document['title2'] = $doc->document_number;
|
||||
$document['title3'] = $doc->title ?? $doc->document_number;
|
||||
$this->document = $document;
|
||||
$documentDate = $doc->issue_date ?? $doc->created_at ?? $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
||||
|
||||
$newRow = $this->addRowToExcel($search, $doc->document_number, $documentDate);
|
||||
if ($newRow > $currentRow) { $currentRow = $newRow; }
|
||||
}
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class QaDocumentProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process QA type documents (NDT reports, procedures, etc.)
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing QA document: {$document['path']}");
|
||||
|
||||
// Get all joints with same line number
|
||||
$allJoints = apply_welded_filter(
|
||||
db("weld_logs")->where(
|
||||
$this->registerColumnBased,
|
||||
$this->weldLogData[$this->registerColumnBased]
|
||||
)
|
||||
)->get();
|
||||
|
||||
$this->log("Found {$allJoints->count()} joints for processing");
|
||||
|
||||
// Check if this is a procedure document
|
||||
if (strpos($document['path'], "Procedure") !== false) {
|
||||
return $this->processProcedureDocuments($currentRow);
|
||||
}
|
||||
|
||||
// Process NDT reports
|
||||
return $this->processNdtReports($allJoints, $currentRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process NDT (Non-Destructive Testing) reports
|
||||
*/
|
||||
private function processNdtReports($allJoints, int &$currentRow): int
|
||||
{
|
||||
$allReports = [];
|
||||
$uniqueReports = [];
|
||||
$duplicateCount = 0;
|
||||
|
||||
// LAYER 1: Get reports from weld_logs (synced data)
|
||||
$this->log("Layer 1: Extracting reports from weld_logs");
|
||||
foreach ($allJoints as $joint) {
|
||||
$jointArray = (array) $joint;
|
||||
$logTypes = array_keys(log_test_types());
|
||||
|
||||
foreach ($logTypes as $logType) {
|
||||
try {
|
||||
if (strpos(strtolower($this->document['path']), $logType) !== false) {
|
||||
$reportNoPrefix = $logType . "_report";
|
||||
|
||||
if ($logType == "pmi") {
|
||||
$reportNoPrefix = "no_of_testing_report";
|
||||
}
|
||||
|
||||
$reportNo = $jointArray[$reportNoPrefix] ?? '';
|
||||
|
||||
if (!empty($reportNo) && !in_array($reportNo, $uniqueReports)) {
|
||||
$uniqueReports[] = $reportNo;
|
||||
|
||||
$allReports[] = [
|
||||
'report_no' => $reportNo,
|
||||
'document_date' => $jointArray[$logType . '_test_date'] ?? '',
|
||||
'log_type' => $logType,
|
||||
'weld_log_array' => $jointArray
|
||||
];
|
||||
|
||||
$this->log("Layer 1 - Report added: {$reportNo} ({$logType})");
|
||||
} else if (!empty($reportNo)) {
|
||||
$duplicateCount++;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Error processing test type: {$logType}", [
|
||||
'error' => $th->getMessage()
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("Layer 1 complete: " . count($allReports) . " unique reports from weld_logs");
|
||||
|
||||
// Sort reports by joint number (A-Z)
|
||||
// Note: Excel rows are inserted in reverse (insertNewRowBefore),
|
||||
// so A-Z order becomes Z-A in Excel (correct order)
|
||||
usort($allReports, function($a, $b) {
|
||||
$jointNoA = $a['weld_log_array']['no_of_the_joint_as_per_as_built_survey'] ?? '';
|
||||
$jointNoB = $b['weld_log_array']['no_of_the_joint_as_per_as_built_survey'] ?? '';
|
||||
|
||||
// Check if joint numbers are empty - throw error
|
||||
if (empty($jointNoA)) {
|
||||
throw new \Exception("Joint number is empty for report: {$a['report_no']}");
|
||||
}
|
||||
if (empty($jointNoB)) {
|
||||
throw new \Exception("Joint number is empty for report: {$b['report_no']}");
|
||||
}
|
||||
|
||||
// Sort by joint number (natural/numeric order: 1, 2, 3, 10, 11)
|
||||
return strnatcmp($jointNoA, $jointNoB);
|
||||
});
|
||||
|
||||
$this->log("Total " . count($allReports) . " unique reports sorted by joint number (A-Z)");
|
||||
$this->log("Duplicate reports skipped: {$duplicateCount}");
|
||||
|
||||
// Process sorted reports
|
||||
foreach ($allReports as $reportData) {
|
||||
try {
|
||||
$normalizedReportNo = $this->normalizeSearchTerm($reportData['report_no']);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalizedReportNo}*.pdf");
|
||||
|
||||
$this->document['title2'] = $reportData['report_no'];
|
||||
|
||||
// Get translation safely - handle array return
|
||||
$translationKey = $reportData['log_type'] . "_register_title";
|
||||
$translatedValue = e2($translationKey);
|
||||
|
||||
// Ensure we have a string, not an array
|
||||
if (is_array($translatedValue)) {
|
||||
$this->document['title4'] = $translationKey; // Use key as fallback
|
||||
Log::warning("Translation returned array for key: {$translationKey}, using key as fallback");
|
||||
} else {
|
||||
$this->document['title4'] = (string) $translatedValue;
|
||||
}
|
||||
|
||||
$lineNumber = $reportData['report_no'];
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$lineNumber,
|
||||
$reportData['document_date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing report: {$reportData['report_no']} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process procedure documents
|
||||
*/
|
||||
private function processProcedureDocuments(int &$currentRow): int
|
||||
{
|
||||
$this->log("Processing Document Procedure");
|
||||
|
||||
$documentProcedures = [];
|
||||
|
||||
if (!empty($this->document['title2'])) {
|
||||
$customCertificates = array_map('trim', explode(",", $this->document['title2']));
|
||||
$documentProcedures = $customCertificates;
|
||||
}
|
||||
|
||||
foreach ($documentProcedures as $procedureNo) {
|
||||
try {
|
||||
$procedure = db("document_procedures")
|
||||
->where("document_no", $procedureNo)
|
||||
->first();
|
||||
|
||||
if ($procedure) {
|
||||
$this->log("Procedure found: {$procedure->document_no}");
|
||||
|
||||
$normalizedDocNo = $this->normalizeSearchTerm($procedure->document_no);
|
||||
$searchPath = "storage/documents/{$this->document['path']}/*{$normalizedDocNo}*.pdf";
|
||||
$search = $this->searchFiles($searchPath);
|
||||
|
||||
$documentDate = $procedure->publish_date ?? '';
|
||||
$lineNumber = $procedure->document_no;
|
||||
$this->document['title2'] = $procedure->document_no;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$lineNumber,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} else {
|
||||
$this->log("Procedure not found: {$procedureNo}", 'warning');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing procedure: {$procedureNo} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class TemplateProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process template type documents
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing Template document: {$document['path']}");
|
||||
|
||||
$lineIdentifier = $weldLogData[$this->registerColumnBased];
|
||||
|
||||
// Search by line number
|
||||
$normalized1 = $this->normalizeSearchTerm($lineIdentifier);
|
||||
$search1 = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized1}*.pdf");
|
||||
|
||||
// Search by test package numbers
|
||||
$testPackageNumbers = db("weld_logs")
|
||||
->where($this->registerColumnBased, $lineIdentifier)
|
||||
->whereNotNull("test_package_no")
|
||||
->where("test_package_no", "!=", "")
|
||||
->distinct()
|
||||
->pluck("test_package_no")
|
||||
->toArray();
|
||||
|
||||
$this->log("Found " . count($testPackageNumbers) . " test package numbers for search");
|
||||
|
||||
$search2 = [];
|
||||
foreach ($testPackageNumbers as $testPackageNo) {
|
||||
$normalized2 = $this->normalizeSearchTerm($testPackageNo);
|
||||
$result = $this->searchFiles("storage/documents/{$document['path']}/*{$normalized2}*.pdf");
|
||||
if (!empty($result)) {
|
||||
$search2 = array_merge($search2, $result);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge and remove duplicates
|
||||
$allFiles = array_unique(array_merge($search1, $search2));
|
||||
$this->log("Total files found: " . count($allFiles));
|
||||
|
||||
if (empty($allFiles)) {
|
||||
$this->log("No template files found", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
// Sort files by date extracted from filename (oldest first)
|
||||
// Note: Excel rows are inserted in reverse (insertNewRowBefore),
|
||||
// so oldest first becomes newest last in Excel (correct order)
|
||||
$self = $this;
|
||||
usort($allFiles, function($a, $b) use ($self) {
|
||||
$dateA = strtotime($self->extractDateFromFilename(basename($a))) ?: 0;
|
||||
$dateB = strtotime($self->extractDateFromFilename(basename($b))) ?: 0;
|
||||
|
||||
// Sort ascending (oldest first)
|
||||
return $dateA <=> $dateB;
|
||||
});
|
||||
|
||||
$this->log("Files sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($allFiles as $filePath) {
|
||||
try {
|
||||
$fileName = basename($filePath);
|
||||
$documentDate = $this->extractDateFromFilename($fileName);
|
||||
|
||||
// Process single file
|
||||
$templateFileName = str_replace(".pdf", "", $fileName);
|
||||
$lineNumber = $templateFileName;
|
||||
$document['title2'] = $templateFileName;
|
||||
|
||||
// Remove date from filename
|
||||
$templateFileName = preg_replace('/_\d{2}\.\d{2}\.\d{4}/', '', $templateFileName);
|
||||
$templateFileName = preg_replace('/_\d{4}-\d{2}-\d{2}/', '', $templateFileName);
|
||||
$document['templateFileName'] = $templateFileName;
|
||||
|
||||
// Special handling for Incoming_Control
|
||||
if ($document['id'] == "Incoming_Control") {
|
||||
$incomingControl = db("incoming_controls")
|
||||
->where("certificate_no", $lineNumber)
|
||||
->first();
|
||||
|
||||
if ($incomingControl) {
|
||||
$document['title2'] = $incomingControl->description_ru;
|
||||
$this->log("Incoming Control found: {$document['title2']}");
|
||||
}
|
||||
}
|
||||
|
||||
// Update document reference for current iteration
|
||||
$this->document = $document;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
[$filePath],
|
||||
$lineNumber,
|
||||
$documentDate
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing template file: " . basename($filePath) . " - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract date from filename
|
||||
*/
|
||||
private function extractDateFromFilename(string $fileName): string
|
||||
{
|
||||
// Try YYYY-MM-DD format
|
||||
if (preg_match('/(\d{4}-\d{2}-\d{2})/', $fileName, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
// Try DD.MM.YYYY format
|
||||
if (preg_match('/(\d{2}\.\d{2}\.\d{4})/', $fileName, $matches)) {
|
||||
try {
|
||||
return Carbon::createFromFormat('d.m.Y', $matches[1])->format('Y-m-d');
|
||||
} catch (\Throwable $th) {
|
||||
Log::debug("Could not parse date: {$matches[1]}");
|
||||
}
|
||||
}
|
||||
|
||||
// Default to latest date
|
||||
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
||||
return $placeholderReplacer->getLatestDate($this->weldLogData, $this->registerColumnBased);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class WdbDocumentProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Process WDB (Welding Database) type documents
|
||||
*/
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$this->log("Processing WDB document: {$document['path']}");
|
||||
|
||||
$path = $document['path'];
|
||||
$wpsData = $settings['wps_data'] ?? null;
|
||||
$startRowNo = $settings['row_no'] ?? 1;
|
||||
|
||||
// Process based on sub-type
|
||||
if (strpos($path, "Naks Technology") !== false) {
|
||||
return $this->processNaksTechnology($currentRow, $wpsData, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "Naks_Welder") !== false) {
|
||||
return $this->processNaksWelder($currentRow, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "Naks_Equipments") !== false) {
|
||||
return $this->processNaksEquipments($currentRow, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "Naks_Consumables") !== false) {
|
||||
return $this->processNaksConsumables($currentRow, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "Welding Experts") !== false) {
|
||||
return $this->processWeldingExperts($currentRow, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "WPQ") !== false) {
|
||||
return $this->processWpq($currentRow, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "WPS") !== false) {
|
||||
return $this->processWps($currentRow, $wpsData, $startRowNo);
|
||||
}
|
||||
|
||||
if (strpos($path, "PQR") !== false) {
|
||||
return $this->processPqr($currentRow, $wpsData, $startRowNo);
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Naks Technology certificates
|
||||
*/
|
||||
private function processNaksTechnology(int &$currentRow, $wpsData, int $startRowNo = 1): int
|
||||
{
|
||||
if (!$wpsData) {
|
||||
$this->log("WPS data not found, skipping Naks Technology", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
$naksCertificates = explode(" + ", $wpsData->naks_certificate_no);
|
||||
$this->log("Processing " . count($naksCertificates) . " Naks Technology certificates");
|
||||
|
||||
foreach ($naksCertificates as $naksCertificate) {
|
||||
try {
|
||||
$normalized = $this->normalizeSearchTerm($naksCertificate);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title3'] = $naksCertificate;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$naksCertificate,
|
||||
$wpsData->date ?? ''
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing Naks certificate: {$naksCertificate} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Naks Welder certificates
|
||||
*/
|
||||
private function processNaksWelder(int &$currentRow, int $startRowNo = 1): int
|
||||
{
|
||||
$this->log("Processing Naks Welders");
|
||||
|
||||
$weldersData = apply_welded_filter(
|
||||
db("weld_logs")
|
||||
->select("certificate_no_1", "certificate_no_2")
|
||||
->where($this->registerColumnBased, $this->weldLogData[$this->registerColumnBased])
|
||||
)->get();
|
||||
|
||||
$naksWelders = [];
|
||||
|
||||
foreach ($weldersData as $welderData) {
|
||||
if (!empty($welderData->certificate_no_1) && !in_array($welderData->certificate_no_1, $naksWelders)) {
|
||||
$naksWelders[] = $welderData->certificate_no_1;
|
||||
}
|
||||
if (!empty($welderData->certificate_no_2) && !in_array($welderData->certificate_no_2, $naksWelders)) {
|
||||
$naksWelders[] = $welderData->certificate_no_2;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("Found " . count($naksWelders) . " welder certificates");
|
||||
|
||||
// Prepare welders with their dates for sorting
|
||||
$weldersWithDates = [];
|
||||
foreach ($naksWelders as $naksWelder) {
|
||||
$welderInfo = db("naks_welders")->where("naks_certificate_no", $naksWelder)->first();
|
||||
$weldersWithDates[] = [
|
||||
'certificate_no' => $naksWelder,
|
||||
'date' => $welderInfo->period_of_validity ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by date (oldest first) - reverse insertion order
|
||||
usort($weldersWithDates, function($a, $b) {
|
||||
$dateA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$dateB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $dateA <=> $dateB;
|
||||
});
|
||||
|
||||
$this->log("Welders sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($weldersWithDates as $welderData) {
|
||||
try {
|
||||
$naksWelder = $welderData['certificate_no'];
|
||||
$normalized = $this->normalizeSearchTerm($naksWelder);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title2'] = $naksWelder;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$naksWelder,
|
||||
$welderData['date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing welder: {$naksWelder} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Naks Equipment certificates
|
||||
*/
|
||||
private function processNaksEquipments(int &$currentRow, int $startRowNo = 1): int
|
||||
{
|
||||
$this->log("Processing Naks Equipments");
|
||||
|
||||
$certificates = [];
|
||||
if (!empty($this->document['title2'])) {
|
||||
$certificates = array_map('trim', explode(",", $this->document['title2']));
|
||||
}
|
||||
|
||||
// Prepare equipments with dates for sorting
|
||||
$equipmentsWithDates = [];
|
||||
foreach ($certificates as $certificate) {
|
||||
$equipmentInfo = db("welding_equipment")->where("attestation", $certificate)->first();
|
||||
$equipmentsWithDates[] = [
|
||||
'certificate' => $certificate,
|
||||
'date' => $equipmentInfo->valid_until ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by valid_until date (oldest first) - reverse insertion order
|
||||
usort($equipmentsWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("Equipments sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($equipmentsWithDates as $equipData) {
|
||||
try {
|
||||
$certificate = $equipData['certificate'];
|
||||
$normalized = $this->normalizeSearchTerm($certificate);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title2'] = $certificate;
|
||||
$this->document['title3'] = $certificate;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$certificate,
|
||||
$equipData['date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing equipment: {$certificate} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Naks Consumables
|
||||
*/
|
||||
private function processNaksConsumables(int &$currentRow, int $startRowNo = 1): int
|
||||
{
|
||||
$this->log("Processing Naks Consumables");
|
||||
|
||||
$consumables = [];
|
||||
if (!empty($this->document['title2'])) {
|
||||
$consumables = array_map('trim', explode(",", $this->document['title2']));
|
||||
}
|
||||
|
||||
// Prepare consumables with dates for sorting
|
||||
$consumablesWithDates = [];
|
||||
foreach ($consumables as $consumable) {
|
||||
$consumableInfo = db("naks_consumables")->where("naks_certificate_no", $consumable)->first();
|
||||
$consumablesWithDates[] = [
|
||||
'consumable' => $consumable,
|
||||
'date' => $consumableInfo->certificate_date ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by certificate_date (oldest first) - reverse insertion order
|
||||
usort($consumablesWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("Consumables sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($consumablesWithDates as $consData) {
|
||||
try {
|
||||
$consumable = $consData['consumable'];
|
||||
$normalized = $this->normalizeSearchTerm($consumable);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title2'] = $consumable;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$consumable,
|
||||
$consData['date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing consumable: {$consumable} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Welding Experts
|
||||
*/
|
||||
private function processWeldingExperts(int &$currentRow, int $startRowNo = 1): int
|
||||
{
|
||||
$this->log("Processing Welding Experts");
|
||||
|
||||
$certificates = array_map('trim', explode(',', $this->document['title2'] ?? ''));
|
||||
|
||||
// Prepare experts with dates for sorting
|
||||
$expertsWithDates = [];
|
||||
foreach ($certificates as $certificate) {
|
||||
$expertInfo = db("register_of_experts")->where("certificate_no", $certificate)->first();
|
||||
$expertsWithDates[] = [
|
||||
'certificate' => $certificate,
|
||||
'date' => $expertInfo->expration_of_the_certificate ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by expiration date (oldest first) - reverse insertion order
|
||||
usort($expertsWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("Experts sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($expertsWithDates as $expertData) {
|
||||
try {
|
||||
$certificate = $expertData['certificate'];
|
||||
$normalized = $this->normalizeSearchTerm($certificate);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title2'] = $certificate;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$certificate,
|
||||
$expertData['date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing expert: {$certificate} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process WPQ documents
|
||||
*/
|
||||
private function processWpq(int &$currentRow, int $startRowNo = 1): int
|
||||
{
|
||||
$this->log("Processing WPQ");
|
||||
|
||||
$wpqs = db("welder_tests")->whereIn("wpq_document_no", [
|
||||
$this->weldLogData['wpq_report_1'] ?? '',
|
||||
$this->weldLogData['wpq_report_2'] ?? '',
|
||||
])->get()->toArray();
|
||||
|
||||
$this->log("Found " . count($wpqs) . " WPQ documents");
|
||||
|
||||
// Sort by naks_validity date (oldest first) - reverse insertion order
|
||||
usort($wpqs, function($a, $b) {
|
||||
$a = (object) $a;
|
||||
$b = (object) $b;
|
||||
$timestampA = !empty($a->naks_validity) ? strtotime($a->naks_validity) : 0;
|
||||
$timestampB = !empty($b->naks_validity) ? strtotime($b->naks_validity) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("WPQ documents sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($wpqs as $wpq) {
|
||||
$wpq = (object) $wpq;
|
||||
try {
|
||||
$normalized = $this->normalizeSearchTerm($wpq->wpq_document_no);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title3'] = $wpq->wpq_document_no;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$wpq->wpq_document_no,
|
||||
$wpq->naks_validity ?? ''
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error processing WPQ: {$wpq->wpq_document_no} - {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process WPS documents
|
||||
*/
|
||||
private function processWps(int &$currentRow, $wpsData, int $startRowNo = 1): int
|
||||
{
|
||||
if (!$wpsData) {
|
||||
$this->log("WPS data not found", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
$this->log("Processing WPS: {$wpsData->details}");
|
||||
|
||||
$normalized = $this->normalizeSearchTerm($wpsData->details);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title3'] = $wpsData->details;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$wpsData->details,
|
||||
$wpsData->date ?? ''
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process PQR documents
|
||||
*/
|
||||
private function processPqr(int &$currentRow, $wpsData, int $startRowNo = 1): int
|
||||
{
|
||||
if (!$wpsData) {
|
||||
$this->log("WPS data not found, cannot process PQR", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
$this->log("Processing PQR");
|
||||
|
||||
$pqr = db("prosedure_qualification_records")->where("pqr_no", $wpsData->pqr_no)->first();
|
||||
|
||||
if ($pqr) {
|
||||
$this->log("PQR found: {$pqr->pqr_no}");
|
||||
|
||||
$normalized = $this->normalizeSearchTerm($pqr->pqr_no);
|
||||
$search = $this->searchFiles("storage/documents/{$this->document['path']}/*{$normalized}*.pdf");
|
||||
|
||||
$this->document['title2'] = $pqr->pqr_no;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$pqr->pqr_no,
|
||||
$pqr->approved_date ?? ''
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\RegisterCreator\DocumentProcessors;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class WpsNaksTechnologyProcessor extends AbstractDocumentProcessor
|
||||
{
|
||||
/**
|
||||
* Search files using the same algorithm as pdf-db-naks-technology-sync
|
||||
* This algorithm handles zero-prefix patterns and multiple search strategies
|
||||
*/
|
||||
private function searchFilesWithFallback(string $basePath, string $certificateNo): array
|
||||
{
|
||||
$files = [];
|
||||
|
||||
// Parse certificate number to extract short_number and cert_no parts
|
||||
// Format examples: АЦСТ-20-01934, АЦСТ-161-00050
|
||||
$certParts = explode('-', $certificateNo);
|
||||
|
||||
if (count($certParts) >= 2) {
|
||||
$shortNumber = $certParts[0]; // e.g., АЦСТ
|
||||
$certNumber = isset($certParts[1]) ? $certParts[1] : '';
|
||||
|
||||
// If there's a third part, combine with second
|
||||
if (count($certParts) >= 3) {
|
||||
$certNumber = $certParts[1]; // e.g., 20 or 161
|
||||
$thirdPart = $certParts[2]; // e.g., 01934 or 00050
|
||||
|
||||
// Generate certificate patterns with different zero prefixes
|
||||
$certPatterns = [$thirdPart];
|
||||
|
||||
// Clean leading zeros and generate additional search patterns
|
||||
$trimmedCertNo = ltrim($thirdPart, '0');
|
||||
if ($trimmedCertNo != $thirdPart && $trimmedCertNo != '') {
|
||||
$certPatterns[] = $trimmedCertNo;
|
||||
|
||||
// Add versions with different numbers of leading zeros
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$certPatterns[] = str_pad($trimmedCertNo, $i, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
// Try each pattern until we find a match
|
||||
foreach ($certPatterns as $certPattern) {
|
||||
// Format: short_number-certNumber-certPattern
|
||||
$searchData = "*{$shortNumber}-{$certNumber}-{$certPattern}*";
|
||||
$searchData = str_replace("/", "*", $searchData);
|
||||
$searchData = str_replace(" ", "*", $searchData);
|
||||
$searchData = str_replace("\\", "*", $searchData);
|
||||
|
||||
$path = "{$basePath}/{$searchData}";
|
||||
$searchPDF = glob($path);
|
||||
|
||||
if (!empty($searchPDF)) {
|
||||
Log::debug("Found with pattern", [
|
||||
'pattern' => $searchData,
|
||||
'files' => count($searchPDF)
|
||||
]);
|
||||
return $searchPDF;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Format with only 2 parts: АЦСТ-161
|
||||
$certPatterns = [$certNumber];
|
||||
|
||||
$trimmedCertNo = ltrim($certNumber, '0');
|
||||
if ($trimmedCertNo != $certNumber && $trimmedCertNo != '') {
|
||||
$certPatterns[] = $trimmedCertNo;
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$certPatterns[] = str_pad($trimmedCertNo, $i, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($certPatterns as $certPattern) {
|
||||
$searchData = "*{$shortNumber}-{$certPattern}*";
|
||||
$searchData = str_replace("/", "*", $searchData);
|
||||
$searchData = str_replace(" ", "*", $searchData);
|
||||
$searchData = str_replace("\\", "*", $searchData);
|
||||
|
||||
$path = "{$basePath}/{$searchData}";
|
||||
$searchPDF = glob($path);
|
||||
|
||||
if (!empty($searchPDF)) {
|
||||
Log::debug("Found with 2-part pattern", [
|
||||
'pattern' => $searchData,
|
||||
'files' => count($searchPDF)
|
||||
]);
|
||||
return $searchPDF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Try just the short_number
|
||||
$searchData = "*{$shortNumber}*";
|
||||
$searchData = str_replace("/", "*", $searchData);
|
||||
$searchData = str_replace(" ", "*", $searchData);
|
||||
$searchData = str_replace("\\", "*", $searchData);
|
||||
|
||||
$path = "{$basePath}/{$searchData}";
|
||||
$searchPDF = glob($path);
|
||||
|
||||
if (!empty($searchPDF)) {
|
||||
Log::debug("Found with short_number fallback", [
|
||||
'pattern' => $searchData,
|
||||
'files' => count($searchPDF)
|
||||
]);
|
||||
return $searchPDF;
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback: Try exact certificate number
|
||||
$searchData = "*{$certificateNo}*";
|
||||
$searchData = str_replace("/", "*", $searchData);
|
||||
$searchData = str_replace(" ", "*", $searchData);
|
||||
$searchData = str_replace("\\", "*", $searchData);
|
||||
|
||||
$path = "{$basePath}/{$searchData}";
|
||||
$searchPDF = glob($path);
|
||||
|
||||
if (!empty($searchPDF)) {
|
||||
Log::debug("Found with exact match", [
|
||||
'pattern' => $searchData,
|
||||
'files' => count($searchPDF)
|
||||
]);
|
||||
return $searchPDF;
|
||||
}
|
||||
|
||||
Log::warning("No files found for certificate", [
|
||||
'certificate' => $certificateNo,
|
||||
'base_path' => $basePath
|
||||
]);
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
public function process(
|
||||
array $weldLogData,
|
||||
array $document,
|
||||
Worksheet $sheet,
|
||||
int &$currentRow,
|
||||
array $settings
|
||||
): int {
|
||||
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
||||
|
||||
$wpsData = $settings['wps_data'] ?? null;
|
||||
|
||||
if (!$wpsData || empty($wpsData->naks_certificate_no)) {
|
||||
$this->log("WPS Naks certificate not found", 'warning');
|
||||
return $currentRow;
|
||||
}
|
||||
|
||||
$naksCertificates = explode(" + ", $wpsData->naks_certificate_no);
|
||||
|
||||
// Prepare certificates with dates for sorting
|
||||
$certificatesWithDates = [];
|
||||
foreach ($naksCertificates as $naksCertificate) {
|
||||
$certInfo = db("naks_certificates")->where("certificate_no", $naksCertificate)->first();
|
||||
$certificatesWithDates[] = [
|
||||
'certificate_no' => $naksCertificate,
|
||||
'date' => $certInfo->valid_from ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by valid_from date (oldest first) - reverse insertion order
|
||||
usort($certificatesWithDates, function($a, $b) {
|
||||
$timestampA = !empty($a['date']) ? strtotime($a['date']) : 0;
|
||||
$timestampB = !empty($b['date']) ? strtotime($b['date']) : 0;
|
||||
return $timestampA <=> $timestampB;
|
||||
});
|
||||
|
||||
$this->log("Certificates sorted by date (oldest first for reverse insertion)");
|
||||
|
||||
foreach ($certificatesWithDates as $certData) {
|
||||
$naksCertificate = $certData['certificate_no'];
|
||||
try {
|
||||
Log::debug("Processing Naks certificate: " . $naksCertificate);
|
||||
|
||||
// Try multiple search strategies for Cyrillic characters and URL encoding
|
||||
$search = [];
|
||||
$basePath = "storage/documents/{$document['path']}";
|
||||
|
||||
// Strategy 1: Try with URL decoded path
|
||||
$decodedPath = urldecode($basePath);
|
||||
$files = $this->searchFilesWithFallback($decodedPath, $naksCertificate);
|
||||
if (!empty($files)) {
|
||||
$search = $files;
|
||||
Log::debug("Found files with decoded path", ['count' => count($files), 'path' => $decodedPath]);
|
||||
}
|
||||
|
||||
// Strategy 2: Try with original path if first strategy failed
|
||||
if (empty($search) && $decodedPath !== $basePath) {
|
||||
$files = $this->searchFilesWithFallback($basePath, $naksCertificate);
|
||||
if (!empty($files)) {
|
||||
$search = $files;
|
||||
Log::debug("Found files with original path", ['count' => count($files), 'path' => $basePath]);
|
||||
}
|
||||
}
|
||||
|
||||
$document['title2'] = $naksCertificate;
|
||||
$this->document = $document;
|
||||
|
||||
$newRow = $this->addRowToExcel(
|
||||
$search,
|
||||
$naksCertificate,
|
||||
$certData['date']
|
||||
);
|
||||
|
||||
if ($newRow > $currentRow) {
|
||||
$currentRow = $newRow;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$this->log("Error: {$th->getMessage()}", 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user