-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclsExcelFile.cls
More file actions
412 lines (351 loc) · 16.9 KB
/
Copy pathclsExcelFile.cls
File metadata and controls
412 lines (351 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "clsExcelFile"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
' Representa un Workbook abierto en el contexto de la aplicación.
' Es la única capa que conoce directamente al Workbook.
' Todas las operaciones que requieran wb deben pasar por aquí.
'@Folder "2-Servicios.Archivos.Tipos de archivos"
Option Explicit
Private p_wb As Workbook
Private p_wbinf As mod_ConstantsGlobals.T_InfoArchivo
Private p_ObjectKey As Double
Private p_PrivSheetsCol As Collection ' Colección de NOMBRES de hojas PRIVADAS (solo para uso interno)
Private p_PublSheetsCol As Collection ' Colección de NOMBRES de hojas PUBLICAS (SE PUEDEN COMPARTIR COMO DOCUMENTO EDITABLE)
' ==================================================================
' IMPLEMENTA LA INTERFAZ IFile
' ==================================================================
Implements IFile
'--------------------------------------------------------------
' Estas propiedades Private son generadas automáticamente por VBA
' cuando escribes "Implements IFile"
' Delegan a las propiedades públicas
'--------------------------------------------------------------
Private Property Get IFile_ObjectKey() As Double
IFile_ObjectKey = Me.ObjectKey
End Property
Private Property Get IFile_Path() As String
IFile_Path = Me.Path
End Property
Private Property Get IFile_Name() As String
IFile_Name = Me.Name
End Property
'--------------------------------------------------------------
' @Description: Asocia esta instancia a un Workbook.
' Solo se puede llamar una vez por instancia.
'--------------------------------------------------------------
Public Static Sub BindTo(wb As Workbook)
If Not p_wb Is Nothing Then
Err.Raise vbObjectError + 570, TypeName(Me), "La instancia ya está asociada a un Workbook."
End If
If wb Is Nothing Then
Err.Raise vbObjectError + 571, TypeName(Me), "No se puede asociar a Nothing."
End If
Set p_wb = wb
p_ObjectKey = ObjPtr(p_wb)
'Debug.Print "[clsExcelFile.BindTo] Asociado a: " & Me.Path & " (key: " & p_ObjectKey & ")"
End Sub
' Factory Method estático: "Creador especializado"
Public Static Function CreateFromWorkbook(wb As Workbook) As clsExcelFile
Dim instance As clsExcelFile
Set instance = New clsExcelFile
instance.BindTo wb
Set CreateFromWorkbook = instance
End Function
' ==================================================================
' PROPIEDADES PÚBLICAS (API pública de la clase)
' ==================================================================
Friend Property Get ObjectKey() As Double
ObjectKey = p_ObjectKey
End Property
'--------------------------------------------------------------
' @Description: Ruta completa (FullName), con fallback seguro.
'--------------------------------------------------------------
Public Property Get Path() As String
On Error Resume Next
If p_ObjectKey <> 0 Then
Path = p_wb.FullName
Else
Path = "(cerrado)"
End If
On Error GoTo 0
End Property
Public Property Get Name() As String
On Error Resume Next
If p_ObjectKey <> 0 Then
Name = p_wb.Name
Else
Name = "(cerrado)"
End If
On Error GoTo 0
End Property
'--------------------------------------------------------------
' @Description: Acceso seguro al Workbook subyacente.
' Lanza error si el wb ya no es válido.
'--------------------------------------------------------------
Public Property Get wb() As Workbook
If p_ObjectKey = 0 Then
Err.Raise vbObjectError + 572, TypeName(Me), "Workbook ya no está disponible (cerrado o inválido)."
End If
Set wb = p_wb
End Property
'--------------------------------------------------------------
' @Description: Tipo detectado (con análisis bajo demanda).
'--------------------------------------------------------------
Public Property Get tipo() As TipoArchivo
If p_ObjectKey = 0 Then Exit Property
If p_wbinf.TipoDetectado = TipoArchivo.UnDef Or IsEmpty(p_wbinf.TipoDetectado) Then
Call Me.AnalizarWorkBook
End If
tipo = p_wbinf.TipoDetectado
End Property
'--------------------------------------------------------------
' @Description: Realiza el análisis completo del Workbook.
' Actualiza internamente `p_wbinf`.
'--------------------------------------------------------------
Public Function AnalizarWorkBook(Optional ByVal wb As Workbook = Nothing) As mod_ConstantsGlobals.T_InfoArchivo
If p_ObjectKey = 0 Then Exit Function
If Not wb Is Nothing And p_wb Is Nothing Then
Set p_wb = wb
ElseIf Not (p_wb Is wb) Then
' El libro ya estaba definido y se intenta analizar uno distinto
Debug.Print "[clsExcelFile.AnalizarWorkBook] ADVERTENCIA: Intentando analizar un libro diferente al asociado"
Err.Raise vbObjectError + 573, TypeName(Me), _
"Esta instancia ya esta asociada a un Workbook diferente. (no debería pasar, revisa tu codigo)"
End If
' Extraer información del nombre DE LA CARPETA DE OPORTUNIDAD, ... o de otros ficheros en ella!!!
p_wbinf.Customer = Customer(p_wb)
'p_wbinf.OpportunityNr = OpportunityNr(p_wb)
' Determinar tipo
p_wbinf.TipoDetectado = DeterminarTipoArchivo()
p_wbinf.EsValido = (p_wbinf.TipoDetectado > TipoArchivo.Unknown)
AnalizarWorkBook = p_wbinf
End Function
'--------------------------------------------------------------
' @Description: Información analizada (estructura T_InfoArchivo).
'--------------------------------------------------------------
Public Property Get info() As mod_ConstantsGlobals.T_InfoArchivo
' Forzar análisis si no se ha hecho
If p_wbinf.TipoDetectado = TipoArchivo.UnDef Then
Call Me.AnalizarWorkBook
End If
info = p_wbinf
End Property
'--------------------------------------------------------------
' @Description: Determina si este libro debe moverse a una oportunidad dada.
' Solo depende de su estado interno y la ruta objetivo.
' NOTA: de momento usa funciones globales (futuro: encapsular aquí).
'--------------------------------------------------------------
Public Function DebeMoverseAOp(rutaOportunidadRelativa As String, rutaBaseOportunidades As String) As Boolean
' Usa Me.Path, Me.AnalizarWorkBook, OpportunityNr(Me.WB), etc.
Dim wb As Workbook, rutaOportunidad As String
rutaOportunidad = rutaBaseOportunidades & "\" & rutaOportunidadRelativa
Set wb = p_wb
' 1. Verificar si es un fichero de oportunidad válido
If Not EsFicheroOportunidad() Then Exit Function
' 2. Extraer el número de oportunidad del nombre del archivo
Dim nrOportunidad As String
nrOportunidad = QuoteNr(wb)
' 3. Verificar si ya está en la carpeta correcta
If InStr(1, wb.Path, rutaOportunidad, vbTextCompare) > 0 Then
' Ya está en la carpeta correcta
Exit Function
End If
' 4. Si tiene un número de oportunidad válido pero no está en la carpeta correcta
' DE MOMENTO ESTA RESTRICCION SE ELIMINA: HAY QUE PEREFECCIONAR LA IDENTIFICACION DE LOS FICHEROS DE OPORTUNIDAD.
'If nrOportunidad <> "" Then
DebeMoverseAOp = True
'End If
End Function
'--------------------------------------------------------------
' @Description: Mueve físicamente el libro abierto a la oportunidad indicada.
' Usa SaveCopyAs + borrado del original (sin cerrar wb).
' Usa lógica robusta de backup/truncado.
'--------------------------------------------------------------
Public Sub MoverAOp(rutaOportunidadRelativa As String, rutaBaseOportunidades As String)
' Usa toda la lógica robusta (SaveCopyAs + backup + truncado...)
' Actualiza internamente `Me.m_wb` (el path no cambia en VBA, pero sabemos que está en nueva ubicación)
' Opcional: invalidar cache en FileManager
If p_ObjectKey = 0 Then Exit Sub
Dim nombreArchivo As String
nombreArchivo = p_wb.Name
Dim respuesta As VbMsgBoxResult
respuesta = MsgBox("¿Deseas mover el archivo '" & nombreArchivo & "' a la carpeta de la oportunidad seleccionada?", _
vbQuestion + vbYesNo, "Mover archivo a oportunidad")
If respuesta <> vbYes Then Exit Sub
On Error GoTo ErrorHandler
' 1. Guardar el libro si hay cambios
If p_wb.Saved = False Then p_wb.Save
' 2. Construir nueva ruta
Dim rutaOrigen As String: rutaOrigen = p_wb.FullName
Dim nuevaRuta As String, rutaDestinoBase As String
rutaDestinoBase = rutaBaseOportunidades & rutaOportunidadRelativa & "\2.CALCULO TECNICO"
nuevaRuta = rutaDestinoBase & "\" & nombreArchivo
' 3. Mover el archivo
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
' 3.1 mover a old el fichero, si ya existe.
If fso.FileExists(nuevaRuta) Then
Dim rutaDestinoBackup As String
Dim contador As Long: contador = 1
Do
rutaDestinoBackup = fso.BuildPath(rutaDestinoBase, _
fso.GetBaseName(nombreArchivo) & "_old (" & contador & ")." & fso.GetExtensionName(nombreArchivo))
contador = contador + 1
Loop While fso.FileExists(rutaDestinoBackup)
' Renombrar el existente
fso.MoveFile nuevaRuta, rutaDestinoBackup
Debug.Print "[MoverAOp] Archivo existente renombrado a: " & rutaDestinoBackup
End If
' === 3.2 Verificar longitud de ruta (MAX_PATH = 260 en Windows sin \\?\) ===
If Len(nuevaRuta) > 259 Then
' Acortar nombre de archivo manteniendo extensión y sufijo "_trunc"
Dim maxNombreLen As Long
maxNombreLen = 259 - Len(rutaDestinoBase) - 1 ' -1 por barra
If maxNombreLen < 6 Then
Err.Raise vbObjectError + 574, "MoverAOp", _
"Ruta de destino demasiado larga incluso tras truncar el nombre."
End If
Dim nuevoNombre As String
nuevoNombre = Left(fso.GetBaseName(nombreArchivo), maxNombreLen - Len(fso.GetExtensionName(nombreArchivo)) - 8) _
& fso.GetExtensionName(nombreArchivo)
nuevaRuta = fso.BuildPath(rutaDestinoBase, nuevoNombre)
MsgBox "Nombre de archivo truncado para cumplir con límite de ruta de Windows: " & vbCrLf & nuevoNombre, vbExclamation
End If
' === 4. Guardar copia en destino ===
'p_wb.SaveCopyAs nuevaRuta
p_wb.SaveAs fileName:=nuevaRuta, FileFormat:=p_wb.FileFormat, Password:="", CreateBackup:=False, ReadOnlyRecommended:=False
Debug.Print "[MoverAOp] Copia guardada en: " & nuevaRuta
' === 5. Borrar original (solo si la copia fue exitosa) ===
If fso.FileExists(nuevaRuta) Then
fso.DeleteFile rutaOrigen, True ' True = Force
Debug.Print "[MoverAOp] Original eliminado: " & rutaOrigen
Else
Err.Raise vbObjectError + 575, "MoverAOp", _
"No se pudo verificar la copia en destino. Operación cancelada."
End If
' === 6. Reasignar .Path y .FullName (simbólicamente, VBA no lo permite directamente) ===
' El libro sigue abierto, pero su ruta "efectiva" ahora es la nueva.
' Excel lo manejará correctamente al guardar de nuevo.
MsgBox "El archivo '" & nombreArchivo & "' se ha movido a la carpeta de la oportunidad.", vbInformation
Exit Sub
ErrorHandler:
MsgBox "Error al mover el archivo: " & Err.Description & " (código " & Err.Number & ")", vbCritical
' No hacer nada más: el libro sigue abierto y seguro en su ubicación original
End Sub
'--------------------------------------------------------------
' @Description: Determina el tipo de archivo según nombre y contenido
'--------------------------------------------------------------
Private Function DeterminarTipoArchivo() As TipoArchivo
Dim nombreArchivo As String
nombreArchivo = p_wb.Name
' Verificar patrones en el nombre
If EsFicheroGasVBNet Then
If InStr(1, nombreArchivo, "_calc", vbTextCompare) > 0 Then
DeterminarTipoArchivo = TipoArchivo.CGASING_Calcs
ElseIf EsLibroDeTablasCurvasRto Then
DeterminarTipoArchivo = TipoArchivo.CGASING_CurvasRendimiento
End If
ElseIf InStr(1, nombreArchivo, "Template", vbTextCompare) > 0 Or _
InStr(1, nombreArchivo, "Plantilla", vbTextCompare) > 0 Then
DeterminarTipoArchivo = TipoArchivo.PlantillaOferta
Else
' Verificar si tiene el patrón de oportunidad
If QuoteNr(p_wb) <> "" And Customer(p_wb) <> "" Then
DeterminarTipoArchivo = TipoArchivo.oportunidad
Else
DeterminarTipoArchivo = TipoArchivo.UnDef
End If
End If
End Function
Private Function EsFicheroGasVBNet() As Boolean
Dim re As Object: Set re = CreateObject("VBScript.RegExp")
re.Pattern = "^[A-Z]{3}\d{5}_\d{2}" ' patrón esperado en el nombre del fichero
re.IgnoreCase = True
EsFicheroGasVBNet = re.Test(p_wb.Name)
End Function
Private Function EsLibroDeTablasCurvasRto() As Boolean
'On Error GoTo ErrorHandler
Dim hoja As Worksheet
Dim r As Range
Dim encabezados As Range, datos As Range
Dim formula As String
For Each hoja In p_wb.Worksheets
If Not IsNumeric(hoja.Name) Then GoTo Siguiente
' Validar que solo hay una tabla en la hoja
If hoja.ListObjects.Count <> 1 Then GoTo ErrorHandler
' Validar que la tabla comience en A1
If hoja.ListObjects(1).Range.Cells(1, 1).Address <> "$A$1" Then GoTo ErrorHandler
Set r = hoja.Range("A1").CurrentRegion
If r.Rows.Count < 2 Or r.Columns.Count < 2 Then GoTo Siguiente
' Validar encabezados (fila 1, desde la segunda columna)
Set encabezados = r.Rows(1).Offset(0, 1).Resize(1, r.Columns.Count - 1)
formula = "SUMPRODUCT(--ISNUMBER(SEARCH(""("", " & encabezados.Address(External:=True) & ")))"
If Evaluate(formula) <> encabezados.Columns.Count Then GoTo ErrorHandler
' Validar datos numéricos (todo menos la primera fila y primera columna)
Set datos = r.Offset(1, 1).Resize(r.Rows.Count - 1, r.Columns.Count - 1)
formula = "SUMPRODUCT(--ISNUMBER(" & datos.Address(External:=True) & "))"
If Evaluate(formula) <> datos.Cells.Count Then GoTo ErrorHandler
EsLibroDeTablasCurvasRto = True
Exit Function
Siguiente:
Next hoja
ErrorHandler:
EsLibroDeTablasCurvasRto = False
End Function
'--------------------------------------------------------------
' @Description: Comprueba si el gráfico activo es válido para invertir ejes
'--------------------------------------------------------------
Public Function EsValidoInvertirEjes() As Boolean
On Error GoTo SafeExit
Dim ctx As clsExecutionContext
Set ctx = App().executionContext
' 1. Validación segura de contexto mínimo
If Not ctx.HasWorkbook Then Exit Function
If Not ctx.HasSelection Then Exit Function
' 2. Obtener chart (seguro: nunca falla)
Dim Ch As Chart
Set Ch = ctx.Chart
If Ch Is Nothing Then Exit Function
' 3. Caso de hoja de gráfico activa (ChartSheet)
If TypeName(ctx.Worksheet) = "Chart" Then
EsValidoInvertirEjes = True
GoTo CheckSeries
End If
' 4. Caso de gráfico incrustado en hoja de cálculo (ChartObject)
Select Case TypeName(ctx.Selection)
Case "ChartObject", "ChartArea"
' No tengo nada claro que en este tipo de objetos se puedan invertir los ejes; en todo caso, NO son los que yo creo
EsValidoInvertirEjes = True
Case "DrawingObjects", "Picture", "Shape", "GroupObject", "OLEObject", "TextBox"
' Explícitamente NO es un gráfico
EsValidoInvertirEjes = False
Exit Function
Case Else
' Otras selecciones no válidas
EsValidoInvertirEjes = False
Exit Function
End Select
CheckSeries:
' 5. Comprobar al menos una serie en cada eje (tu lógica exacta)
Dim tienePrimario As Boolean, tieneSecundario As Boolean
Dim s As Series
For Each s In Ch.SeriesCollection
If s.AxisGroup = xlPrimary Then
tienePrimario = True
ElseIf s.AxisGroup = xlSecondary Then
tieneSecundario = True
End If
Next s
EsValidoInvertirEjes = tienePrimario And tieneSecundario
Exit Function
SafeExit:
EsValidoInvertirEjes = False
' Opcional: loguear solo en modo depuración:
' If bVerbose Then Debug.Print "[EsValidoInvertirEjes] Safe exit: " & Err.Description
End Function