El proyecto ha sido cambiado en profundidad en más de una vez... si de último te dí el proyecto completo y funcional, porqué ahora el código en tus funciones no vene a coincidir con el código que te pasé la última vez....?
Veamos, si tratas de crear una nueva facturación... se deben crear dos ficheros un fichero *.data y un fichero *.regdata (resultado de las últimas modificaciones), el caso es que siendo 'nueva' el número de registros será 0... porque se acaba de crear la facturación, el contenido de dichos ficheros será 0 registros...
Así que al invocar 'leer facturación' solo se ejecurtará esa parte del código si se da esta condición:
If (NumRegistros > 0) Then
Así que al crear la facturación no deberia acceder a esa zona... y por qué accede a esa zona si debiera haber 0 registros?. Porque o bien usas la versión vieja del código o bien lo has modificado eliminando código.... hasta probar que los cambios funcionan bien, mejor que eliminar es comentar código, si el cambio es bastante grande que no solo se borren alguna slíneas y tal, entonces es preferible copiar la función y dejarla comentada al completo, y por tanto hacer cambios sobre una copia... así, si algo falla (que antes funcionaba, esto es mismo código) puede verificarse la diferencia entre ambos códigos para reconocer que está sucediendo.
Tu tienes esté código:
Private Function CrearNuevaFacturacion(ByRef NombreFile As String) As Boolean
Dim Ruta As String
If (Abierto = True) Then Call Cerrar(Canal)
Ruta = (App.Path & "\" & NombreFile)
If (Abrir(Ruta, Canal, True) = True) Then
Call UpdateHeader(0, 0)
CrearNuevaFacturacion = True
Else
MsgBox "Parece que el fichero que intenta abrir ya existe, elija otro nombre (o bien ocurrio un error)..."
End If
End Function
Y para la misma función al final cambió a este:
' La creacion de una NUEVA facturacion supone crear ambos ficheros:
' 1 - "Facturacion xxx.reg"
' 2 - "Facturacion xxx.regdata"
Private Function CrearNuevaFacturacion(ByRef NombreFileLote As String, ByRef NombreFileProd As String, ByVal NFacturaInicial As Integer) As Boolean
Dim Ruta As String
Call CerrarFacturacion
Ruta = (App.Path & "\" & NombreFileLote)
If (Abrir(Ruta, CanalLote, True) = True) Then
Ruta = (App.Path & "\" & NombreFileProd)
If (Abrir(Ruta, CanalProducto, True) = True) Then
Get #CanalLote, 1, NumRegsLotes
Get #CanalLote, , AutoIncLote
' Usar el parámetro 'NFacturaInicial' (para que funcione correctamente), implica más cambios en el código que no se efectuaron, pues surgió como una sugerencia de una interpretación errónea de cierta petición que hiciste.
'If (NFacturaInicial > 1) Then
' AutoIncLote = (NFacturaInicial - 1) ' cuando se compre se incrementa en 1
AutoIncLote = 0
Put #CanalLote, 3, AutoIncLote
'End If
Get #CanalProducto, 1, NumRegsProds
Get #CanalProducto, , AutoIncProducto
mnuLotes(0).Enabled = True
CrearNuevaFacturacion = True
Exit Function
End If
End If
If (Err.Number = 0) Then
Call MsgBox("Parece que uno o ambos ficheros asociados a la facturacion que intenta abrir ya existe, elija otro nombre (o bien ocurrió un error)...", vbExclamation, "Crear Nueva Facturacion: Ya existe...")
Else
Err.Clear
Call MsgBox("Ocurrio un error inesperado al tratar de abrir la facturacion seleccionada..." & vbCrLf & Err.Description, vbExclamation, "Crear Nueva Facturacion: Error inesperado...")
End If
End Function
Como se ve, recibe dos parámetros para los ficheros, no solo uno, y observo que en tu código falta la llamada a la función 'CerrarFacturacion' que es la que pondrá a 0 todas las variables globales referidas a una facturación como es 'NumRegistros'. La función debe invocarse tanto si había abierta previamente una facturación como si no... allí mismo se verifica si estaba abierta y se cierra.
Además, según veo allí (y que de memoria ya no recordaba), la variable NumRegistros, fue cambiada de nombre al separar en dos ficheros la comprar de los productos de cada compra:
' Cierra los dos ficheros asociados a la misma facturacion.
Private Function CerrarFacturacion()
If (Abierto = True) Then
Call CerrarCarrito
Close #CanalLote
Close #CanalProducto
Call ActivarInterfaz(False) ' desactivar de la interfaz, lo que proceda...
End If
mnuLotes(0).Enabled = False
CanalLote = 0: NumRegsLotes = 0: AutoIncLote = 0
CanalProducto = 0: NumRegsProds = 0: AutoIncProducto = 0
TotalContado = 0: TotalCredito = 0
Call ShowTotales
End Function
NumRegsLotes y NumRegsProds (creo recordar), remplazan a NumRegistros.
El error que te arroja el código, se produce simplemente porque el tipo de datos del parámetro que se pasa no coincide con el tipo de datos que la función invocada espera... y es razonable porque al separar la factura en 2 ficheros uno que contiene los datos estrictos de la compra y otro el detalle de cada artículo comprado, exigió crear un registro específico para cada fichero, quedando el registro que antes se usaba para el único fichero obsoleto...
si se revisa la función que da el fallo:
Public Function RegCompraToString(ByRef Registro As RegLote, Optional ByRef Separador As String = CHAR_SEP) As String
Dim R As String, S As String, T As String
With Registro
R = (FillSizeStr(CStr(.IdPedido), 5, True) & Separador & FillSizeStr(CStr(.Cantidad), 5, True))
S = (FillSizeStr(CStr(.Descuento), 7, True, 3) & Separador & FillSizeStr(CStr(.Total), 7, True, 3))
T = GetMetodoPago(.MetodoDePago) & Separador & CStr(.FechaCompra)
RegCompraToString = (R & Separador & S & Separador & T)
End With
End Function
Reclama un parámetro del tipo 'RegLote' (ByRef Registro As RegLote). Dicha función fue modificada, para adaptarse a los cambios de la separación de la compra en sendos ficheros. No recuerdo si además cambié el nombre de la estructura (probablemente):
' Ahora el registro de lote, va aparte de los registro de los articulos especificos comprados en el lote.
Public Type RegLote ' 32 bytes por registro
IdPedido As Integer ' 01 Cada lote, comprado tiene su id unico que se aplica a todos los articulos comprados en ese lote.
Reservado1 As Integer ' 03 (Reservado: Por si se debe cambiar el Id a tipo long, sin alterar los ficheros).
Reservado2 As Integer ' 05 Para futurible ampliacion
Reservado3 As Long ' 07 Para futurible ampliacion
Descuento As Single ' 11 Descuento (Solo se aplica, para las ofertas de tipo: 1,2 y 4, en otro caso el valor es siempre 0).
Total As Single ' 15 Coste total del lote comprado.
FechaCompra As Date ' 19 Fecha de compra del lote (fecha y hora)
MetodoDePago As Byte ' 27 Metodo de pago
Cantidad As Byte ' 29 Cantidad de productos comprados en el lote.
Index As Long ' 29 Puntero a un fichero que contiene los idCompra de cada articulo comprado en el lote.
End Type
Como se ve, el registro solo contiene datos informativos del lote comprado, entre los que incluye los necesarios para rescatar los registros asociados con los detalles de los artículos comprados (campos: 'Cantidad' e 'Index'). Los comentarios, no se ponen por poner... detallan que son o porque están.
Tu tienes la función 'LeerFacturación', con el siguiente código:
Private Function LeerFacturacion(ByRef Ruta As String) As Boolean
Dim k As Integer
If (Abierto = True) Then Call Cerrar(Canal)
If (Abrir(Ruta, Canal) = True) Then
Get #Canal, 1, NumRegistros
Get #Canal, , AutoIncLote
Get #Canal, , AutoIncProducto
If (NumRegistros > 0) Then
With List1
.Clear
For k = 1 To NumRegistros
Get #Canal, , RegX
Call .AddItem(RegCompraToString(RegX, CHAR_SEP))
Next
' Ahora si se quiere puede leerse de nuevo el primer registro para transferirlo a los textbox...
.ListIndex = 0 ' para ello delegamos en el codigo que pondremos al listbox...
End With
End If
LeerFacturacion = True
End If
End Function
Revisando, en el último proyecto que te pasé.., ni siquiera tengo una función con ese nombre, la que se encarga de la lectura de una facturación se llama 'ListarFacturacion', y tiene este código:
' Abre y carga el fichero de facturacion. Tambien abre el fichero de productos comprados (solo carga los productos asociados al primer lote en el listado).
' NOTA: No establecer la propiedad SORTED a TRUE, en los listados afectados.
Private Function ListarFacturacion(ByRef Ruta1 As String, ByRef Ruta2 As String) As Boolean
Dim k As Integer
Call CerrarFacturacion
If ((Abrir(Ruta1, CanalLote) = True) And (Abrir(Ruta2, CanalProducto) = True)) Then
Get #CanalLote, 1, NumRegsLotes
Get #CanalLote, , AutoIncLote
Get #CanalProducto, 1, NumRegsProds
Get #CanalProducto, , AutoIncProducto
If (NumRegsLotes > 0) Then
With LisLotes
.Clear
Call PosicionarRegLote(1) ' alli se resta 1
For k = 0 To NumRegsLotes - 1
Get #CanalLote, , RegX
Call .AddItem(RegCompraToString(RegX))
.ItemData(k) = RegX.IdPedido
If (RegX.MetodoDePago = MetodosDePago.PAGO_AL_CONTADO) Then
TotalContado = (TotalContado + RegX.Total)
Else
TotalCredito = (TotalCredito + RegX.Total)
End If
Next
Call ShowTotales
' Ahora si se quiere puede leerse de nuevo el primer registro para transferirlo a los textbox...
.ListIndex = 0 ' para ello delegamos en el codigo que pondremos al listbox...
End With
End If
mnuLotes(0).Enabled = True
ListarFacturacion = True
End If
End Function
Como se puede ver, se abren 2 ficheros, el *.data y el *.regdata, e igualmente se lee de ambos, su cabecera y detrás los registros que tuviere el *.reg, si los tiene y en cuyo caso, se hace actual el primer registro (listindex =0) que exigirá leer los registros en *.regdata asociados al registro *.reg actual.
Lo siento, pero no tiene sentido que pidas cambios a un proyecto, y luego operes con código de alguna versión anterior que ahora no encaja con los cambios de la versión actual...
No puedes coger código de una versión anterior y remplazarlo alegremente por el código de una nueva versión y esperar que funcione así, sin más (si fue ampliamente cambiado, como sucede en este caso). Tampoco conservo el código de las versiones previas.Tienes que proceder con cierto orden y no mezclar versiones sin empaparte a fondo de los cambios que tiene una versión nueva respecto de la anterior (y desde luego al hacer cambios, conservar siempre copia del original, esto es, de la versión que funciona) y por tanto si al hacer cambios afectan o no y a qué afectan.
Esos detalles no suelen estar todos en la cabeza a un tiempo, especialmente cuando transcurre tiempo de olvidan, pero deberían constar en las especificaciones que uno debe haciendo previo al proyecto y mantener actualizado con los cambios. Como es un proyecto pequeño (y ni siquiera es para mi), yo no tengo necesidad de hacer tal especificación por escrito (susbsiste en mi cabeza mientras lo hago), y en cualquier caso la profusión de comentarios a los largo del código debería bastar al interesado...