Luis Fernando, podria poner aca un ejemplo de archivo.xml que funcione con esta function, porfa?
Code: Select all
/*
DESARROLLADO POR: LUIS FERNANDO RUBIO RUBIO
CONTACTO: fernando@csoluciones.com.mx
*/
#include "fivewin.ch"
#include "directry.ch"
#include "tarray.ch"
#include "objects.ch"
#DEFINE PAD_LEFT 0
#DEFINE PAD_RIGHT 1
#DEFINE PAD_CENTER 2
// Line styles
#DEFINE xlContinuous 1
#DEFINE xlDash -4115
#DEFINE xlDashDot 4
#DEFINE xlDashDotDot 5
#DEFINE xlDot -4118
#DEFINE xlDouble -4119
#DEFINE xlSlantDashDot 13
#DEFINE xlLineStyleNone -4142
// Border positions
#DEFINE xlEdgeLeft 7
#DEFINE xlEdgeTop 8
#DEFINE xlEdgeBottom 9
#DEFINE xlEdgeRight 10
// Alineacion Horizontal
#DEFINE xlHorRight -4152
#DEFINE xlHorLeft -4131
#DEFINE xlHorCenter -4108
#DEFINE xlHorJust -4130
#DEFINE xlHorCombCen 7
STATIC oTArrayTmp := {}
FUNCTION Main( pcDirTrabajo )
LOCAL hFile
LOCAL aFileXML
LOCAL oXmlDoc, oXmlIter, oTagActual
LOCAL lRenombrar := .F.
DEFAULT pcDirTrabajo := cFilePath( GetModuleFileName( GetInstance( ) ) )
IF MsgNoYes( "El directorio de trabajo actual es: " + pcDirTrabajo + ", desea seleccionar otro diferente ?", "LeeXML" )
IF EMPTY( pcDirTrabajo := cGetDir32( "Seleccione el directorio de trabajo...", pcDirTrabajo ) )
MsgStop( "No selecciono ningun directorio de trabajo...", "LeeXML" ); RETURN(NIL)
ENDIF
pcDirTrabajo += "\"
ENDIF
lRenombrar := MsgYesNo( "Desea renombrar los archivos con un formato mas entendible visualmente ?", "LeeXML" )
oDatos := NIL
DEFINE STRUCT oDatos
STRUCT FIELD cEmiNom INIT ""
STRUCT FIELD cEmiRFC INIT ""
STRUCT FIELD cRecNom INIT ""
STRUCT FIELD cRecRFC INIT ""
STRUCT FIELD cTipoCo INIT ""
STRUCT FIELD cFecha INIT ""
STRUCT FIELD cSerie INIT ""
STRUCT FIELD cFolio INIT ""
STRUCT FIELD cMoneda INIT ""
STRUCT FIELD cTCambio INIT ""
STRUCT FIELD cfPago INIT ""
STRUCT FIELD cmPago INIT ""
STRUCT FIELD cCtaPago INIT ""
STRUCT FIELD nSubtot INIT 0.0000
STRUCT FIELD nTotIT INIT 0.0000
STRUCT FIELD nTotIR INIT 0.0000
STRUCT FIELD nTotal INIT 0.0000
STRUCT FIELD cOrden INIT ""
END STRUCT
oDatos:Zap()
aFileXML := Directory( pcDirTrabajo + "*.XML" )
FOR x := 1 TO LEN( aFileXML )
hFile := FOpen( pcDirTrabajo + aFileXML[x, F_NAME] )
oXmlDoc := TXmlDocument():New( hFile )
oXmlIter := TXmlIterator():New( oXmlDoc:oRoot )
cTagName := ""
cTagData := ""
oDatos:Blank()
DO WHILE .T.
oTagActual = oXmlIter:Next()
IF oTagActual != NIL
cTagName := oTagActual:cName
cTagData := oTagActual:cData
HEval( oTagActual:aAttributes, { | cKey, cValue | f_Guarda_XML_Valores( oDatos, cKey, cValue, oTagActual:cName ) } )
ELSE
Exit
ENDIF
ENDDO
oDatos:cOrden := oDatos:cEmiRFC + oDatos:cFecha
oDatos:Add()
FClose( hFile )
IF lRenombrar
FRENAME( pcDirTrabajo + cFileNoExt( aFileXML[x, F_NAME] ) + ".XML", pcDirTrabajo + SUBSTR( oDatos:cFecha, 1, 10 ) + "_" + oDatos:cEmiRFC + "_" + ALLTRIM(oDatos:cSerie) + ALLTRIM(oDatos:cFolio) + "_" + oDatos:cRecRFC + ".XML" )
FRENAME( pcDirTrabajo + cFileNoExt( aFileXML[x, F_NAME] ) + ".PDF", pcDirTrabajo + SUBSTR( oDatos:cFecha, 1, 10 ) + "_" + oDatos:cEmiRFC + "_" + ALLTRIM(oDatos:cSerie) + ALLTRIM(oDatos:cFolio) + "_" + oDatos:cRecRFC + ".PDF" )
ENDIF
NEXT
IF LEN(oDatos:aDatos) = 0
MsgStop("No se encontraron archivos XML en el directorio, verifique...", "LeeXML")
RETURN(NIL)
ENDIF
oDatos:Sort("corden")
oDatos:GoTop()
cTitulo := "Reporte de XML'S"
cRango1 := pcDirTrabajo
cRango2 := ""
aTitulos := { cTitulo, cRango1, cRango2 }
aDatos := { { "emisor", "ceminom", .F., "IZQ", .F. },;
{ "rfc emisor", "cemirfc", .F., "IZQ", .F. },;
{ "receptor", "crecnom", .F., "IZQ", .F. },;
{ "rfc receptor", "crecrfc", .F., "IZQ", .F. },;
{ "tipo", "ctipoco", .F., "IZQ", .F. },;
{ "fecha", "cfecha", .F., "IZQ", .F. },;
{ "serie", "cserie", .F., "IZQ", .F. },;
{ "folio", "cfolio", .F., "IZQ", .F. },;
{ "moneda", "cmoneda", .F., "IZQ", .F. },;
{ "tcambio", "ctcambio", .F., "DER", .F. },;
{ "f.pago", "cfpago", .F., "IZQ", .F. },;
{ "m.pago", "cmpago", .F., "IZQ", .F. },;
{ "cta.pago", "cctapago", .F., "IZQ", .F. },;
{ "subtotal", "nsubtot", .F., "DER", .T. },;
{ "i.trasla", "ntotit", .F., "DER", .T. },;
{ "i.reteni", "ntotir", .F., "DER", .T. },;
{ "total", "ntotal", .F., "DER", .T. } }
MsgMeter( { | oMeter, oText, oDlg, lEnd | _ToExcel( oMeter, oText, oDlg, @lEnd, oDatos, aTitulos, aDatos ) },;
"Procesando Reporte: " + aTitulos[1] + CRLF + "Por favor sea paciente se procesaran " + ALLTRIM( TRANSFORM( LEN( oDatos:aDatos ), "999,999,999" ) ) + " registro(s)...", "LeeXML" )
RETURN(NIL)
FUNCTION f_Guarda_XML_Valores( poDatos, pcKey, pcValue, pcTagName )
//? cTagName, cTagData, pcKey, pcValue
//? pcTagName, pcKey, pcValue
IF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="MONEDA"; poDatos:cMoneda := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="NUMCTAPAGO"; poDatos:cCtaPago := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="TIPOCAMBIO"; poDatos:cTCambio := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="FECHA"; poDatos:cFecha := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="FOLIO"; poDatos:cFolio := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="FORMADEPAGO"; poDatos:cfPago := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="METODODEPAGO"; poDatos:cmPago := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="SERIE"; poDatos:cSerie := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="SUBTOTAL"; poDatos:nSubtot := VAL(pcValue)
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="TOTAL"; poDatos:nTotal := VAL(pcValue)
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:COMPROBANTE,COMPROBANTE" .AND. UPPER(ALLTRIM(pcKey))="TIPODECOMPROBANTE"; poDatos:cTipoCo := pcValue
// EMISOR
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:EMISOR,EMISOR" .AND. UPPER(ALLTRIM(pcKey))="NOMBRE"; poDatos:cEmiNom := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:EMISOR,EMISOR" .AND. UPPER(ALLTRIM(pcKey))="RFC"; poDatos:cEmiRFC := pcValue
// RECEPTOR
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:RECEPTOR,RECEPTOR" .AND. UPPER(ALLTRIM(pcKey))="NOMBRE"; poDatos:cRecNom := pcValue
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:RECEPTOR,RECEPTOR" .AND. UPPER(ALLTRIM(pcKey))="RFC"; poDatos:cRecRFC := pcValue
// IMPUESTOS
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:IMPUESTOS,IMPUESTOS" .AND. UPPER(ALLTRIM(pcKey))="TOTALIMPUESTOSTRASLADADOS"; poDatos:nTotIT := VAL(pcValue)
ELSEIF UPPER(ALLTRIM(pcTagName))$"CFDI:IMPUESTOS,IMPUESTOS" .AND. UPPER(ALLTRIM(pcKey))="TOTALIMPUESTOSRETENIDOS"; poDatos:nTotIR := VAL(pcValue)
ENDIF
RETURN(NIL)
/*
poDatos := Objeto TArray con la informacion recopilada y que se mostrara en el libro de Excel
paDatos := { { "enc1", campo1, .F., "CEN", .T. }, { "enc2", campo2, .T., "IZQ", .F. }, { "enc3", campo3, .T., "DER", .T. }... }
donde el primer parametro es: el nombre del encabezado
el segundo parametro es: el campo del Tarray
el tercer parametro es: la alineacion del dato en general
el cuarto parametro es: si se muestra el total de la columna
el quinto parametro es: si se totalizara la columna
paTitulos := { cReporte, cRango, cRango2 }
*/
FUNCTION _ToExcel( poMeter, poText, poDlg, plEnd, poDatos, paTitulos, paDatos, pcFile )
LOCAL oExcel := nil, oHoja := nil, nLinea := 0, oWorkBook := NIL
LOCAL oError := NIL
LOCAL nTotal := LEN(poDatos:aDatos)
DEFAULT pcFile := ""
TRY
oExcel := ExcelObj()//CreateObject( "Excel.Application" )
CATCH
MsgInfo( "Excel no esta Instalado en el equipo, verifique...", "Error de Excel" ); RETURN(nil)
END
TRY
poMeter:SetTotal(nTotal)
WITH OBJECT oExcel
WITH OBJECT oWorkBook := oExcel:WorkBooks:Add()
:BuiltinDocumentProperties("Author"):Value := "CSOLUCIONES"
:BuiltinDocumentProperties("Title"):Value := "PROPIEDAD DE CSOLUCIONES"
:BuiltinDocumentProperties("Comments"):Value := "PROHIBIDA SU COPIA O REPRODUCCION, DERECHOS RESERVADOS"
END WITH
WITH OBJECT oHoja := oExcel:Get( "ActiveSheet" )
// Aqui definimos en general cual sera el tama;o de las fuentes en todas las celdas
:Cells:Font:Size := 08
:Cells:Font:Name := "Lucida Console"
nxPosTitCen := ROUND( LEN(paDatos) / 2, 0 )
nxPosTitDer := 1
nxPosTitIzq := IIF( LEN(paDatos) == nxPosTitCen, LEN(paDatos) + 1, LEN(paDatos) )
nLinea ++
:Cells( nLinea, nxPosTitIzq ):Value := time()
:Cells( nLinea, nxPosTitIzq ):Font:Size := 12
:Cells( nLinea, nxPosTitIzq ):Font:Name := "Lucida Console"
:Cells( nLinea, nxPosTitIzq ):Set( "HorizontalAlignment", xlHorJust )
:Cells( nLinea, nxPosTitCen ):Value := "cSoluciones ®"
:Cells( nLinea, nxPosTitCen ):Font:Size := 14
:Cells( nLinea, nxPosTitCen ):Font:Name := "Lucida Console"
:Cells( nLinea, nxPosTitCen ):Font:Bold := .T.
:Cells( nLinea, nxPosTitCen ):Set( "HorizontalAlignment", xlHorCombCen )
:Cells( nLinea, nxPosTitDer ):Value := dtoc(date())
:Cells( nLinea, nxPosTitDer ):Font:Size := 12
:Cells( nLinea, nxPosTitDer ):Font:Name := "Lucida Console"
:Cells( nLinea, nxPosTitDer ):Set( "HorizontalAlignment", xlHorJust )
FOR x := 1 TO LEN(paTitulos)
IF ! EMPTY(paTitulos[x])
nLinea ++
:Cells( nLinea, nxPosTitCen ):Value := paTitulos[x]
:Cells( nLinea, nxPosTitCen ):Font:Size := 14
:Cells( nLinea, nxPosTitCen ):Font:Name := "Lucida Console"
:Cells( nLinea, nxPosTitCen ):Font:Bold := .T.
:Cells( nLinea, nxPosTitCen ):Set( "HorizontalAlignment", xlHorCombCen )
ENDIF
NEXT
// ahora imprimimos el encabezado de la lista
nLinea ++
nLinea ++
FOR x := 1 TO LEN(paDatos)
// AQUI IMPRIMIMOS EL TITULO
:Cells( nLinea, x ):Value := paDatos[x,1]
:Cells( nLinea, x ):Font:Bold := .T.
:Range( cXlsColToLet(x) + STRZERO( nLinea, 2 ) ):Borders( xlEdgeTop ):LineStyle := xlDouble //xlDot
// AQUI SELECCIONAMOS LA ALINEACION HORIZONTAL DEL TITULO
DO CASE
CASE UPPER(paDatos[x,4])$"CEN"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorCenter )
CASE UPPER(paDatos[x,4])$"IZQ"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorLeft )
CASE UPPER(paDatos[x,4])$"DER"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorRight )
OTHERWISE; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorCenter )
ENDCASE
NEXT
nRenInicia := 0
aCamposTot := {} //{ 03, 05, 11, 13, 14 }
aCamposLet := {} //{ "C", "E", "K", "M", "N" }
acPosSum := {} //{ "", "", "", "", "" }
FOR x := 1 TO LEN(paDatos)
IF VALTYPE( paDatos[x,5] )$"L"
IF paDatos[x,5]
//? x, cXlsColToLet(x)
AADD( aCamposTot, x )
AADD( aCamposLet, cXlsColToLet(x) )
AADD( acPosSum, "" )
ENDIF
ENDIF
NEXT
nLinea ++
nLinea ++
nRenInicia := nLinea
TRY
poDatos:GoTop()
DO WHILE ! poDatos:Eof()
poMeter:Set(poDatos:Recno())
FOR x := 1 TO LEN(paDatos)
:Cells( nLinea, x ):Value := poDatos:FieldGetByName( paDatos[x,2] )
:Cells( nLinea, x ):Font:Bold := paDatos[x,3]
// aqui agregamos comentarios a las celdas si es necesario
//:Cells( nLinea, x ):AddComment()
//:Cells( nLinea, x ):Comment:Text( ALLTRIM( "pruebas" + str(x) ) )
// aqui definimos la mascara para que imprima los datos
DO CASE
CASE VALTYPE( poDatos:FieldGetByName( paDatos[x,2] ) )$"D"; :Cells( nLinea, x ):Set( "NumberFormat", "dd/mm/aaaa" )
CASE VALTYPE( poDatos:FieldGetByName( paDatos[x,2] ) )$"N"; :Cells( nLinea, x ):Set( "NumberFormat", "#,###,##0.0000" )
CASE VALTYPE( poDatos:FieldGetByName( paDatos[x,2] ) )$"C"; :Cells( nLinea, x ):Set( "NumberFormat", "@" )
ENDCASE
// AQUI SELECCIONAMOS LA ALINEACION HORIZONTAL
DO CASE
CASE UPPER(paDatos[x,4])$"CEN"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorCenter )
CASE UPPER(paDatos[x,4])$"IZQ"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorLeft )
CASE UPPER(paDatos[x,4])$"DER"; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorRight )
OTHERWISE; :Cells( nLinea, x ):Set( "HorizontalAlignment", xlHorCenter )
ENDCASE
NEXT
nLinea ++
poDatos:Skip(1)
ENDDO
CATCH oError
MsgStop( "Error de datos, verifique el archivo creado..." + CRLF +;
"Error: " + CHR(9) + TRANSFORM( oError:GenCode, NIL) + CRLF +;
"SubC: " + CHR(9) + TRANSFORM( oError:SubCode, NIL) + CRLF +;
"OSCode: " + CHR(9) + TRANSFORM( oError:OsCode, NIL) + CRLF +;
"SubSystem: " + CHR(9) + TRANSFORM( oError:SubSystem, NIL) + CRLF +;
"Mensaje: " + CHR(9) + oError:Description, "LeeXML" )
END
// si se selecciono que quieren totales se muestran en las columnas requeridas...
nLinea ++
FOR x := 1 TO LEN(aCamposTot)
:Range( cXlsColToLet(aCamposTot[x]) + ALLTRIM(STR( nLinea) ) ):Borders( xlEdgeTop ):LineStyle := xlDouble //xlDot
:Cells( nLinea, aCamposTot[x] ):Value := "=@SUMA(" + cXlsColToLet(aCamposTot[x]) + ALLTRIM( STR( nRenInicia ) ) + ":" + cXlsColToLet(aCamposTot[x]) + ALLTRIM( STR( nLinea - 2 ) ) + ")"
:Cells( nLinea, aCamposTot[x] ):Set( "NumberFormat", "#,###,##0.0000" )
//:Range( aCamposLet[x] + ALLTRIM(STR(nlinea)) + ":" + aCamposLet[x] + ALLTRIM(STR(nlinea)) ):Interior:Color := CLR_YELLOW
//acPosSum[x] += ( aCamposLet[x] + ALLTRIM(STR(nLinea)) + IIF( ! oDatos:Eof(), ",", "" ) )
NEXT
nLinea ++
nLinea ++
:Cells( nLinea, 1 ):Value := ALLTRIM( TRANSFORM( LEN( poDatos:aDatos ), "999,999,999" ) ) + " registro(s) procesados..."
IF ! EMPTY(pcFile)
oWorkBook:SaveAS( ALLTRIM( pcFile ) )
ENDIF
END WITH // oHoja
poMeter:Set( nTotal )
:Visible := .T.
END WITH // oexcel
CATCH oError
MsgStop( "Error :" + oError:Description, "LeeXML" )
END
oHoja := nil
oExcel := nil
RETURN(NIL)
FUNCTION cXlsColToLet(pnCol)
LOCAL cLetra := ""
DO CASE
CASE pnCol = 1; cLetra := "A"
CASE pnCol = 2; cLetra := "B"
CASE pnCol = 3; cLetra := "C"
CASE pnCol = 4; cLetra := "D"
CASE pnCol = 5; cLetra := "E"
CASE pnCol = 6; cLetra := "F"
CASE pnCol = 7; cLetra := "G"
CASE pnCol = 8; cLetra := "H"
CASE pnCol = 9; cLetra := "I"
CASE pnCol = 10; cLetra := "J"
CASE pnCol = 11; cLetra := "K"
CASE pnCol = 12; cLetra := "L"
CASE pnCol = 13; cLetra := "M"
CASE pnCol = 14; cLetra := "N"
CASE pnCol = 15; cLetra := "O"
CASE pnCol = 16; cLetra := "P"
CASE pnCol = 17; cLetra := "Q"
CASE pnCol = 18; cLetra := "R"
CASE pnCol = 19; cLetra := "S"
CASE pnCol = 20; cLetra := "T"
CASE pnCol = 21; cLetra := "U"
CASE pnCol = 22; cLetra := "V"
CASE pnCol = 23; cLetra := "W"
CASE pnCol = 24; cLetra := "X"
CASE pnCol = 25; cLetra := "Y"
CASE pnCol = 26; cLetra := "Z"
ENDCASE
RETURN(cLetra)
FUNCTION nXlsLetToCol( pcLet )
LOCAL nNum := 0
DO CASE
CASE pcLet$"A"; nNum := 1
CASE pcLet$"B"; nNum := 2
CASE pcLet$"C"; nNum := 3
CASE pcLet$"D"; nNum := 4
CASE pcLet$"E"; nNum := 5
CASE pcLet$"F"; nNum := 6
CASE pcLet$"G"; nNum := 7
CASE pcLet$"H"; nNum := 8
CASE pcLet$"I"; nNum := 9
CASE pcLet$"J"; nNum := 10
CASE pcLet$"K"; nNum := 11
CASE pcLet$"L"; nNum := 12
CASE pcLet$"M"; nNum := 13
CASE pcLet$"N"; nNum := 14
CASE pcLet$"O"; nNum := 15
CASE pcLet$"P"; nNum := 16
CASE pcLet$"Q"; nNum := 17
CASE pcLet$"R"; nNum := 18
CASE pcLet$"S"; nNum := 19
CASE pcLet$"T"; nNum := 20
CASE pcLet$"U"; nNum := 21
CASE pcLet$"V"; nNum := 22
CASE pcLet$"W"; nNum := 23
CASE pcLet$"X"; nNum := 24
CASE pcLet$"Y"; nNum := 25
CASE pcLet$"Z"; nNum := 26
ENDCASE
RETURN(nNum)
// CLASSE - TARRAY.PRG
/*
#include "objects.ch"
#include "fivewin.ch"
*/
//----------------------------------------------------------------------------//
// TArray: by Hernan Diego Ceccarelli - hceccarelli@cesotech.com.ar
// Lenguaje: CA-Clipper/[x]Harbour [+ FiveWin / FiveWin for [x]Harbour]
// Fecha: 01/05/1999
// Modificaciones:
// Agosto 2000 por Enrique Guerra (Quique) quique@quiquesoft.com
// Permite anidar objetos TArray ( utilizar objetos TArray como si
// fueran campos
// El valor lógico para vacío es .F.
// Se agregó un método FieldPos() para que devuelva la posición del
// campo como los DBF
// Se agreguó el método aEval() el cual hace un simple aEval a ::aBuffer
// Se agreguó el método dbEval() el cual funciona parecido al dbeval en
// los DBF, pero en este caso es para ::aDatos
// El comando STRUCT Field acepta nombres de campos o variables
// STRUCT Field nombre => agrega el campo nombre
// STRUCT Field ( cNombre ) => agrega el campo con el nombre que
// contenga cNombre
// Se modificó la función valorVacio para que por DEFAULT revise:
// si el valor es una matriz, vacía los elementos de la matriz
// si es un objeto TArray vacía el objeto (obj:zap())
// los valores no contemplados (nil, codeblocks, objetos) devuelven
// el mismo valor que tienen, no una cadena vacía (nil, codeblocks,
// objetos)
// Se modificó el método append para que borre el buffer, de forma
// parecida a la del append de las DBF devuelven valores vacíos, para
// esto, también modifiqué el método add() para que siguiera
// funcionando igual, ya que se puede utilizar el metodo add() para
// agregar un registro sin borrar el buffer
// Se agregó el método dbResize(), el cual sirve si se tiene un campo
// que contenga un objeto TArray o un array, y durante el transcurso
// del programa se aumentan o se eliminan campos o elementos en
// algunos registros, lo que hace es que todoslos registros tengan
// ese campo con el mismo tamaño (mismo número de elementos )
// Se agregó el método SortFields() el cual ordena todos _ en
// orden ascendente o descendente, tomando como base los nombres de
// _
// Los métodos Sort() y seek() también se puede enviar el nombre del campo
// en lugar del número (ojo, estos son los únicos métodos que lo permite,
// todos los métodos que se agregaron únicamente permiten el nombre
// no el número del campo, pero esto es muy fácil de solucionar en caso
// de que así se necesite)
// Se puede indicar el el valor que se quiere utilizar en cada campo como
// valor vacio, por si no se quieren utilizar los valores por defaul,
// en caso de que se envíe el valor vacio y no el valor inicial,
// tomará el valor vacío como valor inical
// STRUCT Field fecha INIT date() DEFAULT ctod("01/01/2000")
// En este ejemplo, el campo fecha va a tener el valor de date(),
// pero para cada registro nuevo, va a tener ctod("01/01/2000")
// El método load borra el buffer si se encuentra en un registro mayor
// al total de registros, como en el caso de eof() en los DBF
// Si se hace un oArray:goto(0) envía a eof() como en los DBF
// 02/Nov/00 - quique@quiquesoft.com
// Se aumentó la opción de cargar un el objeto desde un DBF desde el
// si en el comando el valor de la clausula INIT es un álias
// DEFINE STRUCT oArray INIT "archivo" // **OJO** va entre comillas
// 09/Nov/00 - quique@quiquesoft.com
// se aumento el método SetFilter
// 10/Nov/00 - quique@quiquesoft.com
// se aumento que los codeblocks FOR y WHILE del método dbEval()
// recibieran como parámetro Self
// 19/Feb/01 - Por Ing. Mario González - mgonzalez@ing.unne.edu.ar
// Se introdujo capacidad de busqueda blanda en el método
// Seek( uClave, cnField, lSoft)
// Por defecto lSoft es FALSO (búsqueda exacta )
// 29/Sep/04 - Hernan Diego Ceccarelli hceccarelli@cesotech.com.ar
// Adaptacion a motor de objetos Harbour/xHarbour
//----------------------------------------------------------------------------//
/*
STATIC oTArrayTmp := {}
*/
//----------------------------------------------------------------------------//
function __StructNew( aInit )
local nLen:= Len( oTArrayTmp ), oArray:= TArray():New()
aAdd(oTArrayTmp,{oArray,.F.})
if ValType( aInit )$"NC" .and. Select(aInit) > 0
aTail(oTArrayTmp)[2]:=.T.
aEval((aInit)->(dbStruct()),{|x|oArray:addField(x[1])})
endif
if aInit != Nil
oArray:aDatos:= aInit
endif
if nLen > 0
oTArrayTmp[nLen,1]:aBuffer[Len(oTArrayTmp[nLen,1]:aBuffer)]:= oArray
endif
return oArray
//----------------------------------------------------------------------------//
function __StructField(cName,uInit,uDefault)
oTArrayTmp[Len(oTArrayTmp),1]:AddField(cName,uInit,uDefault)
return .T.
//----------------------------------------------------------------------------//
function __StructEnd()
local oArray,nCampos,cAlias,aMatriz
if aTail(oTArrayTmp)[2]
oArray := aTail(oTArrayTmp)[1]
cAlias := oArray:aDatos
nCampos := (cAlias)->(fCount())
oArray:aDatos := {}
(cAlias)->(dbEval({||aMatriz:={},aAdd(oArray:aDatos,aMatriz),;
aEval(oArray:aBuffer,{|x,y|aAdd(aMatriz,FieldGet(y))},1,nCampos),;
aEval(oArray:aBuffer,{|x,y|aAdd(aMatriz,oArray:aDefault[y])},nCampos+1)}))
oArray:goTop()
endif
return aSize(oTArrayTmp,len(oTArrayTmp)-1)
//----------------------------------------------------------------------------//
CLASS TArray
DATA aDatos // Matriz real de datos
DATA aBuffer // Según cantidad de Datos
DATA aDEFAULT // valores por DEFAULT para cada campo
DATA aFields // Nombres de _
data bSetFilter // Codeblock que indica el filtro para los registros
DATA nRecNo INIT 1 // Número del registro
DATA lBof INIT .T. // Inicio de archivo
DATA lCheckMayuscula INIT .T.
DATA lEof INIT .T. // Fin de archivo
METHOD New() CONSTRUCTOR
METHOD AddField( cName, uInit, uDEFAULT ) // Aumenta un campo
METHOD Eof() INLINE If( Len(::aDatos) == 0 .or. ::lEof ,.T.,.F.) // Fin de archivo
METHOD Bof() INLINE If( Len(::aDatos) == 0 .or. ::lBof ,.T.,.F.) // Principio de archivo
METHOD Load() // Carga el buffer
METHOD Save() // Guarda el buffer
METHOD Blank() // Borra el buffer
METHOD ValorVacio( uValor, nPos ) // devuelve el valor por DEFAULT de uValor
METHOD Skip( nRecs ) // cambia de registro nRecs registros
METHOD GoTop() INLINE ::Goto( 1 ), if(ValType(::bSetFilter)="B" .and. !Eval(::bSetFilter,Self),::SkipFilter(1),) // va al inicio del arreglo
METHOD GoTo(nRecGoto) // cambia al registro nRecGoto
METHOD GoBottom() INLINE ::Goto( Len(::aDatos) ),if(ValType(::bSetFilter)="B" .and. !Eval(::bSetFilter,Self),::Skip(-1),) // va al final del registro
METHOD Append( nPosRecNo ) // aumenta un registro en nPosRecNo, si no se envía, se aumenta al final
METHOD Add( nPos ) // Graba los datos del buffer en nPos, si no se envía se agregan al final del arreglo
MESSAGE Delete() METHOD _Delete( nPosRecNo ) // borra el registro nPosRecNo, si no se envía borra ::RecNo()
METHOD RecNo() INLINE ::nRecNo // devuelve el registro actual
METHOD LastRec() INLINE Len(::aDatos) // devuelve el número total de registros
METHOD Zap() INLINE ::nRecNo:= 0,::aDatos:={}, ::Blank() // borra todos los datos de la matriz
METHOD Sort( cnField, lAscendente, nIni, nNro ) // ordena la matriz de acuerdo al campo cnField
METHOD Seek( uClave, cnField, lSoft ) // busca un valor en un campo. lSoft=.T.para búsqueda blanda. Por defecto es falso: busqueda exacta
MESSAGE FieldPos METHOD _FieldPos( cField ) // devuelve la posición del campo cField
METHOD FieldName( nField ) INLINE ::aFields[nField] // devuelve el nombre del camopo nField
METHOD aEval( bBloque, nInicio, nElementos ) INLINE aEval( ::aBuffer, bBloque, nInicio, nElementos ) // realiza un aEval con el buffer
METHOD dbResize( cField, nRecord ) // ajusta el campo cField de todos los registros a un mismo tamaño si es matriz u objeto TArray, toma el tamaño del registro nRecord o el último si este no se envia
METHOD SortFields( lDesen ) // ordena _ del objeto
METHOD dbSortField( cField , lDesen ) // ordena _ del objeto TArray del campo cField a toda la matriz
METHOD Field2Array(caField) // devuelve un campo en una matriz, o una matriz donde cada elemento es una matriz de _ seleccionados
METHOD SetFilter(bSetFilter) INLINE ::bSetFilter:=bSetFilter // define un filtro para los registros
METHOD SkipFilter( nRecs )
METHOD IsField( cField ) INLINE ( aScan( ::aFields, Upper(AllTrim(cField)) ) > 0 )
METHOD FieldPos( cField ) INLINE aScan( ::aFields, Upper(AllTrim(cField)) )
METHOD FieldGet( nField ) INLINE if( ::LastRec()>0, ::aDatos[::RecNo(),nField], ::aDefault[nField] )
METHOD FieldGetByName( cField ) INLINE ::FieldGet( ::FieldPos( cField ) )
METHOD FieldPut( nField, uVal ) INLINE if( ::LastRec()>0, ::aDatos[::RecNo(), nField]:= uVal, )
METHOD FieldPutByName( cField, uVal ) INLINE ::FieldPut( ::FieldPos( cField ), uVal )
MESSAGE dbEval METHOD _dbEval( bBloque, bFor, bWhile, nNext, nRecord, lRest ) // realiza un aEval a toda la matriz con las mismas caracteristicas que el dbEval de clipper
ERROR HANDLER ArrayErrorHand()
ENDCLASS
//----------------------------------------------------------------------------//
METHOD New() CLASS TArray
::aDatos := {} /// Matriz real de datos
::aFields:= {} ///
::aBuffer:= {} /// Seg£n cantidad de Datos
::aDefault:={}
return Self
//----------------------------------------------------------------------------//
METHOD AddField( cName, uInit, uDEFAULT ) CLASS TArray
Aadd( ::aFields , Upper(cName) )
Aadd( ::aBuffer , if(uInit=nil,uDefault,uInit) )
Aadd( ::aDEFAULT , uDEFAULT )
return .T.
//----------------------------------------------------------------------------//
METHOD Load() CLASS TArray
local aDatos := ::aDatos
local aBuffer:= ::aBuffer
local nLen := Len ( aDatos )
local nRecNo := ::nRecNo
if nLen > 0 // .or. nRecNo <= ::LastRec()
nRecNo := Max( Min( nRecNo, nLen ), 1 )
::nRecNo:= nRecNo
nLen := len(aDatos[nRecNo])
if nLen<len(aBuffer)
aSize(aDatos[nRecNo],len(aBuffer))
aEval(aBuffer,{|uValor,nPos|aDatos[nRecNo,nPos] := ::valorVacio(uValor,nPos)},nLen+1)
endif
::aBuffer:= aClone( aDatos[nRecNo] )
Else
::Blank()
endif
return .T.
//----------------------------------------------------------------------------//
METHOD Blank() CLASS TArray
local aBuffer:= ::aBuffer
AEval( aBuffer, {|uValor,nPos| aBuffer[nPos]:= ::valorVacio(uValor,nPos) } )
return .T.
//----------------------------------------------------------------------------//
METHOD ValorVacio( uValor, nPos, aDEFAULT ) CLASS TArray
local cType:= ValType( uValor ),uDev
DEFAULT aDefault := ::aDefault
do case
case cType == 'N'
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,0,aDefault[nPos])
case cType == 'D'
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,CtoD(''),aDefault[nPos])
case cType == 'L'
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,.F.,aDefault[nPos])
case cType == 'C' .or. cType == 'M'
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,Space(Len(uValor)),aDefault[nPos])
case cType == 'A'
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,aEval(aClone(uValor),{|x,y|uDev[y]:=::valorVacio( x )}),aClone(aDefault[nPos]))
case cType == 'O' .and. uValor:classname() == "TArray"
if nPos=nil .or. aDefault[nPos]=nil
uDev:= oClone(uValor)
uDev:zap()
else
oClone(aDefault[nPos])
endif
otherwise
uDev:= if(nPos=nil .or. aDefault[nPos]=nil,uValor,aDefault[nPos])
endcase
return uDev
//----------------------------------------------------------------------------//
METHOD Save() CLASS TArray
local aDatos := ::aDatos
local aBuffer:= ::aBuffer
local nLen := Len ( aDatos )
local nRecNo := ::nRecNo
if nLen > 0
nRecNo := Max( Min( nRecNo, nLen ), 1 )
::nRecNo:= nRecNo
::aDatos[nRecNo] := aClone(aBuffer)
endif
return .T.
//----------------------------------------------------------------------------//
METHOD Skip( nRecs ) CLASS TArray
local nLen := Len ( ::aDatos )
local nRecNo := ::nRecNo
local nSalto := 0
DEFAULT nRecs:= 1
if nLen > 0 .and. nRecs <> 0
// SetFilter
if ValType(::bSetFilter)="B"
nSalto := ::SkipFilter(nRecs)
else // sin SetFilter
nRecNo := Max( Min( nRecNo, nLen ), 1 )
nSalto := nRecs
if nRecs > 0 // Pa' delante
if nRecNo + nRecs > nLen
nSalto-= nRecNo + nRecs - nLen
endif
Else // Pa' tras
if nRecNo + nRecs < 1
nSalto+= 1 - ( nRecNo + nRecs )
endif
endif
endif
if nSalto <> 0
nRecNo+= nSalto
::nRecNo:= nRecNo
::Load()
endif
endif
if nSalto == 0
if nRecs <> 0
if nRecs > 0
::lEof:= .T.
::lBof:= .F.
Else
::lEof:= .F.
::lBof:= .T.
endif
endif
Else
::lEof:= .F.
::lBof:= .F.
endif
return nSalto
//----------------------------------------------------------------------------//
METHOD Goto( nRecGoto ) CLASS TArray
local nLen := Len ( ::aDatos )
local nRecNo := ::nRecNo
if nRecGoto=0
::nRecNo := ::LastRec()+1
::lEof := .T.
::lBof := ::LastRec()=0
::Blank()
else
nRecGoto:= Min( nLen, nRecGoto )
if nLen > 0
::nRecNo:= nRecGoto
::Load()
::lEof:= .F.
::lBof:= .F.
Else
::Blank()
endif
endif
return .T.
//----------------------------------------------------------------------------//
METHOD Append( nPosRecNo ) CLASS TArray
local nLen := Len ( ::aDatos )
local nRecNo := ::nRecNo
local aDatos := ::aDatos
if nLen > 0 .and. nPosRecNo != Nil .and. nPosRecNo >= 1
nPosRecNo:= Min( nLen, nPosRecNo )
ASize( aDatos, nLen+1 )
AIns( aDatos, nPosRecNo )
::nRecNo:= nPosRecNo
aDatos[ nPosRecNo ]:= Array( Len(::aFields) )
AEval( ::aBuffer, {|uValor,nPos| aDatos[nPosRecNo,nPos]:= ::valorVacio(uValor,nPos) } )
else
Aadd( aDatos, Array( Len(::aFields) ) )
nPosRecNo:= nLen + 1
::nRecNo := nPosRecNo
endif
AEval( ::aBuffer, {|uValor,nPos| aDatos[nPosRecNo,nPos]:= ::valorVacio(uValor,nPos) } )
::aBuffer := aClone( aDatos[nPosRecNo] )
::lEof:= .F.
::lBof:= .F.
return .T.
//----------------------------------------------------------------------------//
METHOD Add( nPos ) CLASS TArray
local aBuffer:= aClone(::aBuffer)
::Append( nPos )
::aBuffer := aBuffer
::Save()
return .T.
//----------------------------------------------------------------------------//
METHOD _Delete( nPosRecNo ) CLASS TArray
local nLen := Len ( ::aDatos )
local nRecNo := ::nRecNo
local aDatos := ::aDatos
DEFAULT nPosRecNo:= ::nRecNo
if nLen > 0
nPosRecNo:= Max( Min( nLen, nPosRecNo ), 1)
ADel( aDatos, nPosRecNo )
ASize( aDatos, nLen-1 )
if nPosRecNo > nLen-1
nPosRecNo--
endif
::nRecNo:= nPosRecNo
::Load()
endif
return .T.
//----------------------------------------------------------------------------//
METHOD Sort( cnField, lAscendente, nIni, nNro ) CLASS TArray
local bBlock, uValor, nPosNueva
local nField:=if(ValType(cnField)="N",cnField,if(ValType(cnField)="C",::FieldPos(cnField),1))
DEFAULT nField:= 1,;
lAscendente:= .T.
if Len( ::aDatos ) > 0
if lAscendente
bBlock:= {|x,y| x[nField] < y[nField] }
Else
bBlock:= {|x,y| x[nField] > y[nField] }
endif
uValor:= ::aDatos[::nRecNo,nField] /// Valor Donde Estaba
ASort( ::aDatos, nIni, nNro, bBlock )
if ( nPosNueva:= AScan( ::aDatos, {|x| x[nField] == uValor } ) ) > 0
::nRecNo:= nPosNueva
endif
endif
return .T.
//----------------------------------------------------------------------------//
METHOD Seek( uClave, cnField, lSoft ) CLASS TArray
local lEncontro:= .F.
local nPosNueva:=0
local bSetFilter := if(ValType(::bSetFilter)="B",::bSetFilter,{||.T.})
DEFAULT lSoft:= .F.
if(ValType(cnField)="C",cnField:=::FieldPos(cnField),)
// if ( nPosNueva:= AScan( ::aDatos, {|x| x[cnField] == uClave }, nPosNueva ) ) > 0
// ::nRecNo:= nPosNueva
// ::Load()
// ::lEof:= .F.
// ::lBof:= .F.
// lEncontro:= .T.
// else
// ::lEof := .T.
// ::nRecNo := 0
// ::blank()
// endif
if lSoft // Busqueda blanda
do while .T.
nPosNueva := AScan( ::aDatos, {|x| AllTrim( x[cnField] ) = uClave }, nPosNueva+1 )
::Goto(nPosNueva)
if nPosNueva = 0
exit
elseif Eval(bSetFilter,Self)
lEncontro := .T.
exit
endif
enddo
else
do while .T.
nPosNueva := AScan( ::aDatos, {|x| AllTrim( x[cnField] )== uClave }, nPosNueva+1 )
::Goto(nPosNueva)
if nPosNueva = 0
exit
elseif Eval(bSetFilter,Self)
lEncontro := .T.
exit
endif
enddo
end
return lEncontro
//----------------------------------------------------------------------------//
METHOD _FieldPos( cField ) CLASS TArray
cField := if(ValType(cField)="C",upper(cField),"")
return aScan( ::aFields, {|x|x==cField} )
//----------------------------------------------------------------------------//
METHOD _dbEval( bBloque, bFor, bWhile, nNext, nRecord, lRest ) CLASS TArray
local nCont,nSkip:=1,bSetFilter:=if(ValType(::bSetFilter)="B",::bSetFilter,{||.T.})
DEFAULT bFor := {||.T.}
DEFAULT bWhile := {||.T.}
DEFAULT nNext := ::LastRec()
if ValType(nRecord)="N"
::goto(nRecord)
nNext := 1
nSkip := 0
elseif pCount()<4 .or. !Empty(lRest)
::GoTop()
else
::Load()
endif
do while --nNext>=0 .and. !::eof() .and. Eval(bWhile,Self)
if Eval(bFor,Self) .and. Eval(bSetFilter,Self)
Eval(bBloque,Self)
endif
::Skip(nSkip)
enddo
return nil
//----------------------------------------------------------------------------//
METHOD dbResize( cField, nRecord ) CLASS TArray
local nCampo:=::FieldPos(cField),nTamActual,nTamNuevo,aBuffer,aFields,aDefault
DEFAULT nRecord := ::LastRec()
::Goto(nRecord)
if ValType(::aBuffer[nCampo])="A"
nTamNuevo := len(::aBuffer[nCampo])
aEval(::aDatos,{|aReg|aBuffer:=aReg,nTamActual:=len(aReg[nCampo]),;
aSize(aReg[nCampo],nTamNuevo),;
aEval(aReg[nCampo],{|uValor,nPos|aBuffer[nPos]:= ::valorVacio(uValor,nPos)},nTamActual+1)})
elseif ValType(::aBuffer[nCampo])="O" .and. ::aBuffer[nCampo]:classname() == "TArray"
aFields := aClone(::aBuffer[nCampo]:aFields)
aDEFAULT := aClone(::aBuffer[nCampo]:aDefault)
aBuffer := aClone(::aBuffer[nCampo]:aBuffer)
nTamNuevo := len(aBuffer)
aEval(aBuffer,{|uValor,nPos|aBuffer[nPos]:= ::valorVacio(uValor,nPos,aDefault)})
::dbEval({|aBuf|::aBuffer[nCampo]:aFields:=aClone(aFields),;
::aBuffer[nCampo]:aDefault:=aClone(aDefault),;
aBuf:=::aBuffer[nCampo]:aBuffer,nTamActual:=len(aBuf),aSize(aBuf,nTamNuevo),;
aEval(aBuffer,{|uValor,nPos|::aBuffer[nCampo]:aBuffer[nPos]:= ::valorVacio(uValor,nPos,aDefault)},nTamActual+1),;
::aBuffer[nCampo]:save()})
endif
::GoTop()
return nil
//----------------------------------------------------------------------------//
METHOD SortFields( lDesen ) CLASS TArray
local aFields := aClone(::aFields),aNuevo[len(::aFields)]
aSort(::aFields,,,if(Empty(lDesen),{|x,y|x<y},{|x,y|x>y}))
aEval(::aFields,{|cField,nPos|aNuevo[nPos]:=aScan(aFields,cField)})
aEval(aNuevo,{|nField,nPos|aFields[nPos]:=aScan(aNuevo,nPos)})
aEval(aClone(::aDefault),{|uValor,nPos|::aDefault[aFields[nPos]]:=uValor})
if Empty(::aDatos)
aEval(aClone(::aBuffer),{|uValor,nPos|::aBuffer[aFields[nPos]]:=uValor})
else
::dbEval({||aEval(aClone(::aBuffer),{|uValor,nPos|::aBuffer[aFields[nPos]]:=uValor}),::save()})
endif
return nil
//----------------------------------------------------------------------------//
METHOD dbSortField( cField , lDesen ) CLASS TArray
local nField:=::FieldPos(cField)
return ::dbEval({||::aBuffer[nField]:SortFields( lDesen )})
//----------------------------------------------------------------------------//
METHOD Field2Array(caField) CLASS TArray
local aArray:={}, nPos
if ValType(caField)="C" .and. (nPos:=::FieldPos(caField))>0
::dbEval({||aAdd(aArray,::aBuffer[nPos])})
elseif ValType(caField)="A"
aEval(caField,{|cField,nPos| aAdd( aArray[nPos],::Field2Array(cField) ) })
endif
return aArray
//-------------------------------------------------------------------------------------
METHOD SkipFilter(nRecs) class TArray
local nSalto:=0,nRecNo:=::RecNo(),bSetFilter:=::bSetFilter,nSkipFilt,nSkip
DEFAULT nRecs := 1
::bSetFilter := nil
nSkipFilt := nRecs
nSkip := if(nSkipFilt>0,1,-1)
do while nSkipFilt<>0
::Skip(nSkip)
if ::bof() .or. ::eof()
::goto(nRecNo)
exit
elseif Eval(bSetFilter,Self)
nSkipFilt -= nSkip
nRecNo := ::nRecNo
nSalto += nSkip
endif
enddo
::bSetFilter := bSetFilter
return nSalto
//-------------------------------------------------------------------------------------
#ifdef __HARBOUR__
METHOD ArrayErrorHand( uParam1 ) CLASS TArray
local cMetodo:= __GetMessage()
local nError := If( SubStr( cMetodo, 1, 1 ) == "_", 1005, 1004 )
#else
METHOD ArrayErrorHand( cMetodo, nError ) CLASS TArray
local uParam1 := GetParam( 1, 1 )
#endif
local lAsignacion:= .F.
local nId
local lExact:= Set( _SET_EXACT, .T. )
local uDev
local bMetodo := {|x|x==cMetodo}, oError
if SubStr( cMetodo, 1, 1 ) == '_'
lAsignacion:= .T.
cMetodo:= SubStr( cMetodo, 2 )
#ifndef __HARBOUR
bMetodo := {|x|left(x,9)==cMetodo}
#endif
endif
if ::lCheckMayuscula
AEval( ::aFields, {|cVal,nId| ::aFields[nId]:= Upper(cVal) } )
::lCheckMayuscula:= .F.
endif
if ( nId:= AScan( ::aFields, bMetodo ) ) > 0
if lAsignacion
uDev:= uParam1
::aBuffer[ nId ]:= uDev
Else
uDev:= ::aBuffer[ nId ]
endif
Else
// uDev:= _ClsSetErr( _GenError( nError, ::ClassName, cMetodo ) )
oError:= ErrorNew()
oError:description:= "CLASE TArray " + ;
"Campo " + cMetodo + " Inexistente."
uDev:= Eval( ErrorBlock(), oError )
endif
Set( _SET_EXACT, lExact )
return uDev
//----------------------------------------------------------------------------//
// TARRAY.CH
//----------------------------------------------------------------------------//
// Creando Estructuras de Datos // Arrays orientados a objetos
//----------------------------------------------------------------------------//
/*
#command DEFINE STRUCT [<oTArray>] [INIT <aInit>] => ;
[<oTArray>:= ]__StructNew(<aInit>)
#command STRUCT FIELD <(cName)> [INIT <uInit>] [DEFAULT <uDefault>] => ;
__StructField(<(cName)>,<uInit>,<uDefault> )
#command END STRUCT => __StructEnd()
*/
Gracias, saludos.