new functions
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled

This commit is contained in:
2025-12-14 23:59:32 +01:00
parent e42ce8b092
commit 047e155238
11 changed files with 1324 additions and 481 deletions

View File

@@ -1,134 +0,0 @@
<?php
class DocumentIdentifier {
// Diccionarios para validar disciplinas y tipos
private $disciplinasValidas = [
'ENG' => 'Ingeniería',
'ARC' => 'Arquitectura',
'CIV' => 'Civil',
'MEC' => 'Mecánica',
'ELC' => 'Eléctrica',
'INS' => 'Instrumentación',
'PIP' => 'Piping',
'STR' => 'Estructural'
];
private $tiposDocumentoValidos = [
'DRW' => 'Dibujo',
'ESP' => 'Especificación',
'LST' => 'Lista de materiales',
'PRO' => 'Procedimiento',
'INF' => 'Informe',
'MAN' => 'Manual',
'CAL' => 'Cálculo',
'REP' => 'Reporte'
];
public function analizarDocumento($codigoCompleto) {
// Validar formato básico
if (strpos($codigoCompleto, ' - ') === false) {
return $this->crearResultadoError("Formato inválido: falta separador ' - '");
}
list($codigo, $nombre) = explode(' - ', $codigoCompleto, 2);
$segmentos = explode('-', $codigo);
// Validar número de segmentos
if (count($segmentos) != 5) {
return $this->crearResultadoError("Número incorrecto de segmentos");
}
// Extraer y validar cada parte
$codigoProyecto = $segmentos[0];
$codigoMaker = $segmentos[1];
$disciplina = $segmentos[2];
$tipoDocumento = $segmentos[3];
$revisionCompleta = $segmentos[4];
// Validar formato de revisión
if (strpos($revisionCompleta, 'REV.') !== 0) {
return $this->crearResultadoError("Formato de revisión inválido");
}
$numeroRevision = substr($revisionCompleta, 4); // Remover "REV."
// Validar número de revisión
if (!ctype_digit($numeroRevision) || strlen($numeroRevision) != 2) {
return $this->crearResultadoError("Número de revisión inválido");
}
// Validar disciplinas y tipos
$disciplinaValida = $this->validarDisciplina($disciplina);
$tipoValido = $this->validarTipoDocumento($tipoDocumento);
return [
'codigo_completo' => $codigoCompleto,
'codigo_proyecto' => $codigoProyecto,
'codigo_maker' => $codigoMaker,
'disciplina' => $disciplina,
'disciplina_desc' => $disciplinaValida,
'tipo_documento' => $tipoDocumento,
'tipo_documento_desc' => $tipoValido,
'revision' => $numeroRevision,
'nombre_documento' => $nombre,
'estructura_valida' => true,
'errores' => []
];
}
private function validarDisciplina($codigo) {
return isset($this->disciplinasValidas[$codigo])
? $this->disciplinasValidas[$codigo]
: "Desconocida";
}
private function validarTipoDocumento($codigo) {
return isset($this->tiposDocumentoValidos[$codigo])
? $this->tiposDocumentoValidos[$codigo]
: "Desconocido";
}
private function crearResultadoError($mensaje) {
return [
'estructura_valida' => false,
'errores' => [$mensaje]
];
}
// Método para generar un nuevo código
public function generarCodigo($proyecto, $maker, $disciplina, $tipo, $revision, $nombre = '') {
$revisionFormateada = str_pad($revision, 2, '0', STR_PAD_LEFT);
$codigo = "{$proyecto}-{$maker}-{$disciplina}-{$tipo}-REV.{$revisionFormateada}";
if (!empty($nombre)) {
$codigo .= " - {$nombre}";
}
return $codigo;
}
}
// EJEMPLOS DE USO
/*
$analizador = new DocumentIdentifier();
// Analizar un código existente
$codigo1 = "MP00002-SOGOS-ENG-DRW-REV.01 - Plano principal";
$resultado1 = $analizador->analizarDocumento($codigo1);
echo "Análisis del documento:\n";
print_r($resultado1);
// Generar un nuevo código
$nuevoCodigo = $analizador->generarCodigo(
'MP00002',
'SOGOS',
'CIV',
'ESP',
'03',
'Especificación técnica de cimientos'
);
echo "\nNuevo código generado: " . $nuevoCodigo . "\n";*/
?>

View File

@@ -1,47 +0,0 @@
<?php
namespace App\Helpers;
class ProjectNamingSchema
{
/**
* Generate a project name based on ISO 19650 schema.
*
* @param array $fields Associative array of required and optional fields.
* @return string Generated project name.
*/
public static function generate(array $fields): string
{
// Validate required fields
$requiredFields = ['project', 'creator', 'volume', 'level', 'documentType', 'discipline', 'number'];
foreach ($requiredFields as $field) {
if (empty($fields[$field])) {
throw new \InvalidArgumentException("The field '{$field}' is required.");
}
}
// Build the project name
$projectName = [
strtoupper($fields['project']),
strtoupper($fields['creator']),
strtoupper($fields['volume']),
strtoupper($fields['level']),
strtoupper($fields['documentType']),
strtoupper($fields['discipline']),
str_pad($fields['number'], 3, '0', STR_PAD_LEFT),
];
// Add optional fields if provided
if (!empty($fields['description'])) {
$projectName[] = $fields['description'];
}
if (!empty($fields['status'])) {
$projectName[] = strtoupper($fields['status']);
}
if (!empty($fields['revision'])) {
$projectName[] = str_pad($fields['revision'], 4, '0', STR_PAD_LEFT);
}
return implode('-', $projectName);
}
}

View File

@@ -0,0 +1,317 @@
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Project;
use App\Models\ProjectDocumentStatus;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ProjectDocumentStatusManager extends Component
{
public $project;
public $statuses = [];
public $showForm = false;
public $formData = [
'id' => null,
'name' => '',
'color' => '#6b7280',
'text_color' => '#ffffff',
'description' => '',
'allow_upload' => true,
'allow_edit' => true,
'allow_delete' => false,
'requires_approval' => false,
'is_default' => false,
];
public $showDeleteModal = false;
public $statusToDelete = null;
public $orderedStatusIds = [];
protected $rules = [
'formData.name' => 'required|string|min:2|max:100',
'formData.color' => 'required|string|max:7',
'formData.text_color' => 'nullable|string|max:7',
'formData.description' => 'nullable|string|max:500',
'formData.allow_upload' => 'boolean',
'formData.allow_edit' => 'boolean',
'formData.allow_delete' => 'boolean',
'formData.requires_approval' => 'boolean',
'formData.is_default' => 'boolean',
];
public function mount(Project $project)
{
$this->project = $project;
$this->loadStatuses();
}
public function loadStatuses()
{
$this->statuses = $this->project->documentStatuses()
->orderBy('order')
->get()
->toArray();
$this->statuses = [];
$this->orderedStatusIds = collect($this->statuses)->pluck('id')->toArray();
}
public function openForm($statusId = null)
{
$this->resetForm();
if ($statusId) {
$status = ProjectDocumentStatus::find($statusId);
if ($status && $status->project_id === $this->project->id) {
$this->formData = [
'id' => $status->id,
'name' => $status->name,
'color' => $status->color,
'text_color' => $status->text_color,
'description' => $status->description,
'allow_upload' => $status->allow_upload,
'allow_edit' => $status->allow_edit,
'allow_delete' => $status->allow_delete,
'requires_approval' => $status->requires_approval,
'is_default' => $status->is_default,
];
}
}
$this->showForm = true;
}
public function closeForm()
{
$this->showForm = false;
$this->resetForm();
}
public function resetForm()
{
$this->formData = [
'id' => null,
'name' => '',
'color' => '#6b7280',
'text_color' => '#ffffff',
'description' => '',
'allow_upload' => true,
'allow_edit' => true,
'allow_delete' => false,
'requires_approval' => false,
'is_default' => false,
];
$this->resetErrorBag();
}
public function saveStatus()
{
$this->validate();
try {
DB::beginTransaction();
// Si se marca como default, quitar el default de otros estados
if ($this->formData['is_default']) {
$this->project->documentStatuses()->update(['is_default' => false]);
}
$data = [
'name' => $this->formData['name'],
'color' => $this->formData['color'],
'text_color' => $this->formData['text_color'] ?: $this->calculateTextColor($this->formData['color']),
'description' => $this->formData['description'],
'allow_upload' => $this->formData['allow_upload'],
'allow_edit' => $this->formData['allow_edit'],
'allow_delete' => $this->formData['allow_delete'],
'requires_approval' => $this->formData['requires_approval'],
'is_default' => $this->formData['is_default'],
];
if ($this->formData['id']) {
// Editar estado existente
$status = ProjectDocumentStatus::find($this->formData['id']);
if ($status && $status->project_id === $this->project->id) {
$status->update($data);
}
} else {
// Crear nuevo estado
$data['project_id'] = $this->project->id;
$data['slug'] = $this->generateUniqueSlug($this->formData['name']);
$data['order'] = $this->project->documentStatuses()->count();
ProjectDocumentStatus::create($data);
}
DB::commit();
$this->loadStatuses();
$this->closeForm();
$this->dispatch('show-message', [
'type' => 'success',
'message' => $this->formData['id'] ? 'Estado actualizado correctamente.' : 'Estado creado correctamente.'
]);
} catch (\Exception $e) {
DB::rollBack();
$this->dispatch('show-message', [
'type' => 'error',
'message' => 'Error al guardar: ' . $e->getMessage()
]);
}
}
private function generateUniqueSlug($name)
{
$slug = Str::slug($name);
$originalSlug = $slug;
$counter = 1;
while ($this->project->documentStatuses()->where('slug', $slug)->exists()) {
$slug = $originalSlug . '-' . $counter;
$counter++;
}
return $slug;
}
private function calculateTextColor($backgroundColor)
{
// Convertir hex a RGB
$hex = str_replace('#', '', $backgroundColor);
if (strlen($hex) == 3) {
$hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
}
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
// Calcular luminosidad
$luminosity = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
return $luminosity > 0.5 ? '#000000' : '#ffffff';
}
public function confirmDelete($statusId)
{
$this->statusToDelete = ProjectDocumentStatus::find($statusId);
if ($this->statusToDelete && $this->statusToDelete->project_id === $this->project->id) {
$this->showDeleteModal = true;
}
}
public function closeDeleteModal()
{
$this->showDeleteModal = false;
$this->statusToDelete = null;
}
public function deleteStatus()
{
if (!$this->statusToDelete) {
return;
}
try {
// Verificar que no haya documentos con este estado
if ($this->statusToDelete->documents()->exists()) {
$this->dispatch('show-message', [
'type' => 'error',
'message' => 'No se puede eliminar el estado porque hay documentos asociados.'
]);
$this->closeDeleteModal();
return;
}
// Si es el estado por defecto, establecer otro como default
if ($this->statusToDelete->is_default) {
$newDefault = $this->project->documentStatuses()
->where('id', '!=', $this->statusToDelete->id)
->first();
if ($newDefault) {
$newDefault->update(['is_default' => true]);
}
}
$this->statusToDelete->delete();
$this->loadStatuses();
$this->dispatch('show-message', [
'type' => 'success',
'message' => 'Estado eliminado correctamente.'
]);
} catch (\Exception $e) {
$this->dispatch('show-message', [
'type' => 'error',
'message' => 'Error al eliminar: ' . $e->getMessage()
]);
}
$this->closeDeleteModal();
}
public function updateOrder()
{
try {
foreach ($this->orderedStatusIds as $index => $statusId) {
$status = ProjectDocumentStatus::find($statusId);
if ($status && $status->project_id === $this->project->id) {
$status->update(['order' => $index]);
}
}
$this->loadStatuses();
$this->dispatch('show-message', [
'type' => 'success',
'message' => 'Orden actualizado correctamente.'
]);
} catch (\Exception $e) {
$this->dispatch('show-message', [
'type' => 'error',
'message' => 'Error al actualizar el orden: ' . $e->getMessage()
]);
}
}
public function moveUp($statusId)
{
$index = array_search($statusId, $this->orderedStatusIds);
if ($index > 0) {
$temp = $this->orderedStatusIds[$index];
$this->orderedStatusIds[$index] = $this->orderedStatusIds[$index - 1];
$this->orderedStatusIds[$index - 1] = $temp;
$this->updateOrder();
}
}
public function moveDown($statusId)
{
$index = array_search($statusId, $this->orderedStatusIds);
if ($index < count($this->orderedStatusIds) - 1) {
$temp = $this->orderedStatusIds[$index];
$this->orderedStatusIds[$index] = $this->orderedStatusIds[$index + 1];
$this->orderedStatusIds[$index + 1] = $temp;
$this->updateOrder();
}
}
public function render()
{
return view('livewire.project-document-status-manager');
}
}

View File

@@ -23,11 +23,9 @@ class ProjectNameCoder extends Component
// Inicializar con un componente vacío
$this->project = $project;
// Si hay configuración inicial, cargarla
if ($project->codingConfig) {
$this->loadDatabaseConfiguration();
} else {
// Inicializar con un componente vacío
$this->addComponent();
}
}
@@ -180,7 +178,7 @@ class ProjectNameCoder extends Component
// Preparar la configuración completa
$configData = [
'components' => $this->components,
//'total_components' => $this->componentsCount,
'total_components' => $this->componentsCount,
//'total_document_types' => $this->totalDocumentTypes,
//'generated_format' => $this->generateFormatString(),
//'last_updated' => now()->toDateTimeString(),

View File

@@ -11,7 +11,7 @@ use App\Models\Document;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Auth;
use App\Helpers\DocumentIdentifier;
use App\Services\ProjectCodeService;
class ProjectShow extends Component
{
@@ -35,10 +35,12 @@ class ProjectShow extends Component
protected $listeners = ['documents-updated' => '$refresh'];
protected ProjectCodeService $codeService;
public function mount(Project $project)
{
$this->project = $project->load('rootFolders');
$this->project = $project;
$this->project['javi'] = 'braña';
$this->currentFolder = $this->project->rootFolders->first() ?? null;
}
@@ -129,6 +131,13 @@ class ProjectShow extends Component
$this->validate([
'selectedFiles.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png'
]);
$isValid = app(ProjectCodeService::class)->validate($this->project, $files[0]->getClientOriginalName());
if ($isValid) {
echo "✅ Código válido\n";
} else {
echo "❌ Código inválido\n";
}
$this->selectedFiles = array_merge($this->selectedFiles, $files);
}
@@ -142,10 +151,16 @@ class ProjectShow extends Component
public function uploadFiles(): void
{
$validator = new ProjectCodeValidator($project->codingConfig);
foreach ($this->selectedFiles as $file) {
//$analizador = analizarDocumento();
//print_r($analizador);
//$resultado1 = $analizador->analizarDocumento($file->getClientOriginalName());
if ($validator->validate($file->getClientOriginalName())) {
echo "✅ Código válido\n";
} else {
echo "❌ Código inválido\n";
return;
}
$code = $this->project->reference;

View File

@@ -8,6 +8,9 @@ use App\Livewire\PdfViewer;
use App\Livewire\ProjectShow;
use App\Livewire\Toolbar;
use App\View\Components\Multiselect;
use App\Services\ProjectCodeService;
use App\Services\ProjectCodeValidator;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Validator;
@@ -21,7 +24,16 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
//
// Registrar el Service
$this->app->singleton(ProjectCodeService::class, function ($app) {
return new ProjectCodeService(new ProjectCodeValidator([]));
});
// O si prefieres, registrar el validador por separado
$this->app->bind(ProjectCodeValidator::class, function ($app, $params) {
// $params[0] contendría los datos del proyecto
return new ProjectCodeValidator($params[0] ?? []);
});
}
/**

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Services;
use App\Models\Project;
use App\Services\ProjectCodeValidator;
use Illuminate\Support\Arr;
class ProjectCodeService
{
protected ProjectCodeValidator $validator;
public function __construct(ProjectCodeValidator $validator)
{
$this->validator = $validator;
}
/**
* Crea un validador para un proyecto específico
*/
public function forProject(Project $project): ProjectCodeValidator
{
// Si es un modelo Eloquent, convertir a array
//$projectData = $project instanceof Project ? $project->toArray() : $project;
return new ProjectCodeValidator($project);
}
/**
* Valida un código rápidamente
*/
public function validate(Project $project, string $code): bool
{
$validator = $this->forProject($project);
return $validator->validate($code);
}
/**
* Analiza un código y devuelve detalles
*/
public function analyze(Project $project, string $code): array
{
$validator = $this->forProject($project);
return $validator->analyze($code);
}
/**
* Genera un nuevo código para el proyecto
*/
public function generate(
Project $project,
array $components,
string $documentName
): string
{
$validator = $this->forProject($project);
return $validator->generateCode($components, $documentName);
}
/**
* Obtiene la configuración de codificación
*/
public function getConfiguration(Project $project): array
{
$validator = $this->forProject($project);
return [
'format' => $validator->getFormat(),
'reference' => $validator->getReference(),
'separator' => $validator->getSeparator(),
'sequence_length' => $validator->getSequenceLength(),
'components' => [
'maker' => $validator->getAllowedCodesForComponent('maker'),
'system' => $validator->getAllowedCodesForComponent('system'),
'discipline' => $validator->getAllowedCodesForComponent('discipline'),
'document_type' => $validator->getAllowedCodesForComponent('document_type'),
]
];
}
}

View File

@@ -0,0 +1,543 @@
<?php
namespace App\Services;
use InvalidArgumentException;
class ProjectCodeValidator
{
private array $projectData;
private array $components;
private string $separator;
private string $reference;
private int $sequenceLength;
/**
* Constructor de la clase
*
* @param array|string $project Los datos del proyecto (array o JSON string)
* @throws InvalidArgumentException Si los datos no son válidos
*/
public function __construct($project)
{
//print_r($project);
// Convertir string JSON a array si es necesario
if (is_string($project)) {
$project = json_decode($project, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException('JSON inválido: ' . json_last_error_msg());
}
}
if (!is_array($project)) {
throw new InvalidArgumentException('Los datos del proyecto deben ser un array o JSON string');
}
$this->projectData = $project;
// Validar que exista la configuración de codificación
/*if (!isset($this->projectData['codingConfig'])) {
throw new InvalidArgumentException('El proyecto no tiene configuración de codificación');
}*/
$this->initializeConfiguration();
}
/**
* Inicializa la configuración a partir de los datos del proyecto
*/
private function initializeConfiguration(): void
{
$codingConfig = $this->projectData['codingConfig'];
// Obtener componentes ordenados por 'order'
$this->components = $codingConfig['elements']['components'] ?? [];
usort($this->components, fn($a, $b) => $a['order'] <=> $b['order']);
$this->separator = $codingConfig['separator'] ?? '-';
$this->reference = $this->projectData['reference'] ?? '';
$this->sequenceLength = $codingConfig['sequence_length'] ?? 4;
}
/**
* Valida si un código cumple con la configuración de codificación
*
* @param string $code El código a validar
* @return bool True si el código es válido
*/
public function validate(string $code): bool
{
// Validación básica: número de partes
$parts = explode($this->separator, $code);
if (count($parts) !== 7) {
return false;
}
// Validar referencia (primera parte)
if ($parts[0] !== $this->reference) {
return false;
}
// Validar cada componente (partes 2-6)
for ($i = 1; $i <= 5; $i++) {
if (!$this->validateComponent($i - 1, $parts[$i])) {
return false;
}
}
// La parte 7 (DOCUMENT_NAME) no tiene validación específica
return true;
}
/**
* Valida un componente específico del código
*
* @param int $componentIndex Índice del componente (0-4)
* @param string $value Valor a validar
* @return bool True si el valor es válido para el componente
*/
private function validateComponent(int $componentIndex, string $value): bool
{
if (!isset($this->components[$componentIndex])) {
return false;
}
$component = $this->components[$componentIndex];
$headerLabel = $component['headerLabel'] ?? '';
// Validar longitud máxima
$maxLength = $component['data']['maxLength'] ?? null;
if ($maxLength && strlen($value) > $maxLength) {
return false;
}
// Validar según el tipo de componente
if ($headerLabel === 'Version') {
return $this->validateVersionComponent($value);
}
// Validar contra códigos permitidos
return $this->validateAgainstAllowedCodes($component, $value);
}
/**
* Valida el componente de versión
*
* @param string $value Valor de la versión
* @return bool True si es válido
*/
private function validateVersionComponent(string $value): bool
{
// La versión debe ser numérica
if (!is_numeric($value)) {
return false;
}
// La versión debe tener la longitud especificada
if (strlen($value) !== $this->sequenceLength) {
return false;
}
return true;
}
/**
* Valida un valor contra los códigos permitidos de un componente
*
* @param array $component Configuración del componente
* @param string $value Valor a validar
* @return bool True si el valor está en los códigos permitidos
*/
private function validateAgainstAllowedCodes(array $component, string $value): bool
{
$documentTypes = $component['data']['documentTypes'] ?? [];
if (empty($documentTypes)) {
return true; // No hay restricciones de códigos
}
$allowedCodes = array_column($documentTypes, 'code');
return in_array($value, $allowedCodes, true);
}
/**
* Realiza un análisis detallado de un código
*
* @param string $code El código a analizar
* @return array Array con información detallada de la validación
*/
public function analyze(string $code): array
{
$result = [
'is_valid' => false,
'code' => $code,
'parts' => [],
'errors' => [],
'warnings' => []
];
// Dividir en partes
$parts = explode($this->separator, $code);
// Verificar número de partes
$expectedParts = 7;
if (count($parts) !== $expectedParts) {
$result['errors'][] = sprintf(
'El código debe tener %d partes separadas por "%s", tiene %d',
$expectedParts,
$this->separator,
count($parts)
);
return $result;
}
// Nombres de los componentes según el orden esperado
$componentNames = [
'reference',
'maker',
'system',
'discipline',
'document_type',
'version',
'document_name'
];
// Analizar cada parte
foreach ($parts as $index => $value) {
$analysis = $this->analyzePart($index, $value);
$analysis['name'] = $componentNames[$index] ?? 'unknown';
$result['parts'][] = $analysis;
if (!$analysis['is_valid']) {
$result['errors'][] = sprintf(
'Parte %d (%s): %s',
$index + 1,
$componentNames[$index],
$analysis['error'] ?? 'Error desconocido'
);
}
}
$result['is_valid'] = empty($result['errors']);
return $result;
}
/**
* Analiza una parte específica del código
*
* @param int $partIndex Índice de la parte (0-6)
* @param string $value Valor de la parte
* @return array Análisis detallado de la parte
*/
private function analyzePart(int $partIndex, string $value): array
{
$analysis = [
'index' => $partIndex,
'value' => $value,
'is_valid' => true,
'expected' => '',
'error' => '',
'notes' => []
];
switch ($partIndex) {
case 0: // Referencia
$analysis['expected'] = $this->reference;
if ($value !== $this->reference) {
$analysis['is_valid'] = false;
$analysis['error'] = sprintf(
'Referencia incorrecta. Esperado: %s',
$this->reference
);
}
break;
case 1: // Maker (componente 0)
case 2: // System (componente 1)
case 3: // Discipline (componente 2)
case 4: // Document Type (componente 3)
$componentIndex = $partIndex - 1;
if (isset($this->components[$componentIndex])) {
$component = $this->components[$componentIndex];
$analysis = array_merge($analysis, $this->analyzeComponent($component, $value));
}
break;
case 5: // Version (componente 4)
if (isset($this->components[4])) {
$component = $this->components[4];
$analysis = array_merge($analysis, $this->analyzeComponent($component, $value));
// Información adicional para la versión
$analysis['notes'][] = sprintf(
'Longitud de secuencia: %d',
$this->sequenceLength
);
}
break;
case 6: // Document Name
$analysis['notes'][] = 'Nombre del documento (sin restricciones específicas)';
break;
default:
$analysis['is_valid'] = false;
$analysis['error'] = 'Índice de parte no válido';
break;
}
return $analysis;
}
/**
* Analiza un componente específico
*
* @param array $component Configuración del componente
* @param string $value Valor a analizar
* @return array Análisis del componente
*/
private function analyzeComponent(array $component, string $value): array
{
$result = [
'is_valid' => true,
'expected' => '',
'error' => '',
'notes' => []
];
$headerLabel = $component['headerLabel'] ?? '';
$maxLength = $component['data']['maxLength'] ?? null;
$documentTypes = $component['data']['documentTypes'] ?? [];
// Validar longitud
if ($maxLength && strlen($value) > $maxLength) {
$result['is_valid'] = false;
$result['error'] = sprintf(
'Longitud máxima excedida: %d (actual: %d)',
$maxLength,
strlen($value)
);
}
// Validar tipo de componente
if ($headerLabel === 'Version') {
if (!is_numeric($value)) {
$result['is_valid'] = false;
$result['error'] = 'Debe ser numérico';
} elseif (strlen($value) !== $this->sequenceLength) {
$result['is_valid'] = false;
$result['error'] = sprintf(
'Longitud incorrecta. Esperado: %d, Actual: %d',
$this->sequenceLength,
strlen($value)
);
}
$result['expected'] = sprintf('Número de %d dígitos', $this->sequenceLength);
} else {
// Obtener códigos permitidos
$allowedCodes = array_column($documentTypes, 'code');
if (!empty($allowedCodes) && !in_array($value, $allowedCodes, true)) {
$result['is_valid'] = false;
$result['error'] = sprintf(
'Código no permitido. Permitidos: %s',
implode(', ', $allowedCodes)
);
}
$result['expected'] = $allowedCodes ? implode('|', $allowedCodes) : 'Sin restricciones';
$result['notes'][] = sprintf('Componente: %s', $headerLabel);
// Agregar etiquetas de los códigos permitidos
$labels = [];
foreach ($documentTypes as $type) {
if (isset($type['label'])) {
$labels[] = sprintf('%s = %s', $type['code'], $type['label']);
}
}
if (!empty($labels)) {
$result['notes'][] = 'Significados: ' . implode(', ', $labels);
}
}
return $result;
}
/**
* Genera un código válido basado en la configuración
*
* @param array $componentsValues Valores para cada componente
* @param string $documentName Nombre del documento
* @return string Código generado
* @throws InvalidArgumentException Si los valores no son válidos
*/
public function generateCode(array $componentsValues, string $documentName): string
{
// Validar que tengamos valores para todos los componentes
$requiredComponents = 5; // Maker, System, Discipline, Document Type, Version
if (count($componentsValues) < $requiredComponents) {
throw new InvalidArgumentException(
sprintf('Se requieren valores para %d componentes', $requiredComponents)
);
}
// Construir las partes del código
$parts = [$this->reference];
// Agregar componentes validados
for ($i = 0; $i < $requiredComponents; $i++) {
$value = $componentsValues[$i];
if (!$this->validateComponent($i, $value)) {
throw new InvalidArgumentException(
sprintf('Valor inválido para componente %d: %s', $i, $value)
);
}
$parts[] = $value;
}
// Agregar nombre del documento
$parts[] = $this->sanitizeDocumentName($documentName);
return implode($this->separator, $parts);
}
/**
* Sanitiza el nombre del documento para usarlo en el código
*
* @param string $documentName Nombre del documento
* @return string Nombre sanitizado
*/
private function sanitizeDocumentName(string $documentName): string
{
// Reemplazar caracteres no deseados
$sanitized = preg_replace('/[^a-zA-Z0-9_-]/', '_', $documentName);
// Limitar longitud si es necesario
return substr($sanitized, 0, 50);
}
/**
* Obtiene los códigos permitidos para un componente específico
*
* @param string $componentName Nombre del componente (maker, system, discipline, document_type)
* @return array Array de códigos permitidos
*/
public function getAllowedCodesForComponent(string $componentName): array
{
$componentMap = [
'maker' => 0,
'system' => 1,
'discipline' => 2,
'document_type' => 3
];
if (!isset($componentMap[$componentName])) {
return [];
}
$index = $componentMap[$componentName];
if (!isset($this->components[$index])) {
return [];
}
$component = $this->components[$index];
$documentTypes = $component['data']['documentTypes'] ?? [];
$result = [];
foreach ($documentTypes as $type) {
$result[] = [
'code' => $type['code'],
'label' => $type['label'] ?? $type['code'],
'max_length' => $type['max_length'] ?? $component['data']['maxLength'] ?? null
];
}
return $result;
}
/**
* Obtiene el formato de codificación
*
* @return string Formato de codificación
*/
public function getFormat(): string
{
return $this->projectData['coding_config']['format'] ?? '';
}
/**
* Obtiene la referencia del proyecto
*
* @return string Referencia del proyecto
*/
public function getReference(): string
{
return $this->reference;
}
/**
* Obtiene el separador usado en la codificación
*
* @return string Separador
*/
public function getSeparator(): string
{
return $this->separator;
}
/**
* Obtiene la longitud de secuencia para la versión
*
* @return int Longitud de secuencia
*/
public function getSequenceLength(): int
{
return $this->sequenceLength;
}
}
// Ejemplo de uso:
// Suponiendo que $projectDataVariable es la variable que contiene tus datos del proyecto
/*
$validator = new ProjectCodeValidator($projectDataVariable);
// Validar un código
$codigo = "SOGOS0001-SOGOS-PV0-CV-DWG-0001-InformeTecnico";
if ($validator->validate($codigo)) {
echo "✅ Código válido\n";
} else {
echo "❌ Código inválido\n";
}
// Analizar un código detalladamente
$analisis = $validator->analyze($codigo);
echo "Código analizado: " . $analisis['code'] . "\n";
echo "Válido: " . ($analisis['is_valid'] ? 'Sí' : 'No') . "\n";
if (!$analisis['is_valid']) {
echo "Errores:\n";
foreach ($analisis['errors'] as $error) {
echo "- $error\n";
}
}
// Generar un código nuevo
try {
$componentes = ['SOGOS', 'PV0', 'CV', 'DWG', '0014'];
$nuevoCodigo = $validator->generateCode($componentes, 'PlanosGenerales');
echo "Código generado: $nuevoCodigo\n";
} catch (InvalidArgumentException $e) {
echo "Error al generar código: " . $e->getMessage() . "\n";
}
// Obtener códigos permitidos para un componente
$systemCodes = $validator->getAllowedCodesForComponent('system');
echo "Códigos permitidos para System:\n";
foreach ($systemCodes as $code) {
echo "- {$code['code']}: {$code['label']}\n";
}
*/