Buenas,
Estoy analizando la información que habéis proporcionado, y decir que esta gente de AET , ostras, técnicamente impecable a nivel de XSD, pero enrevesados a más no poder.
Os explico el camino que voy a seguir , ya realizadas nn otras implementaciones que tenemos.
La idea básicamente , a su nivel más básico, para que lo entendais, es crear las clases que nos facilitarán la construcción automaticamente del XML y su envio.
Para nosotros, como programadores, será totalmente transparente, solo nos preocuparemos de montar los objetos con nuestra información.
A modo de ejemlo:
- Code: Select all Expand view RUN
Function test()
Local oFacturaEmitidas
oFacturaEmitidas := SuministroLRFacturasEmitidas():New()
oFacturaEmitidas:oCabecera:oTitular:NombreRazon := "La razon social"
oFacturaEmitidas:oCabecera:oTitular:NIF := "A0101010101"
while TengoFacturas
oFactura := oFactura():New()
// ... aqui datos ...
oFacturaEmitidas:Add( oFactura )
end while
if oFacturaEmitidas:SendSoap()
? "Ey, todo bien"
else
? "Analizar respuesta" , oFacturaEmitidas:oResponse
endif
RETURN nil
Como podéis ver, no veréis nada, simplemente, alimentaremos nuestros objectos que sera compatibles con la Agencia Tributaria, cada cual con sus datos desde Dbf , Sql, lo que sea.
Este nivel de encapsulamiento es el que llevo años con los webservices y la verdad es que funciona muy bien.
¿ Y como se hace esto ?
Muy simple, la idea es replicar en clases la definicion del XSD, por poner un ejemplo, la
clase SuministroLRFacturasEmitidas- Code: Select all Expand view RUN
/*
Sii - Suministro Inmediato de Información, compuesto por datos de contexto y una secuencia de 1 o más registros.
*Facturas emitidas
*/
CLASS SuministroLRFacturasEmitidas FROM ResponseSoap
DATA Operacion INIT "siiLR:SuministroLRFacturasEmitidas"
DATA oCabecera
DATA aoRegistroLRFacturasEmitidas
METHOD New() CONSTRUCTOR
METHOD Add( oRegistroLRFacturasEmitidas ) INLINE aadd(::aoRegistroLRFacturasEmitidas, oRegistroLRFacturasEmitidas )
METHOD WriteXML( pNode, NameSpace )
END CLASS
METHOD New() CLASS SuministroInformacion
::oCabecera := Cabecera():New()
::aoRegistroLRFacturasEmitidas := {}
RETURN Self
METHOD WriteXML( pNode, NameSpace ) CLASS SuministroInformacion
Local oFactura, pList
if !empty( NameSpace )
::NameSpace := NameSpace
endif
pNode := ::CreateXML( )
::oCabecera:WriteXML( pNode )
if !empty( ::aoRegistroLRFacturasEmitidas )
for each oFactura in ::aoRegistroLRFacturasEmitidas
pList = mxmlNewElement( pNode, ::NameSpace + "RegistroLRFacturasEmitidas" )
oFactura:WriteXML( pList, ::NameSpace )
next
endif
RETURN NIL
A través del
method WriteXML, cada clase SOLO va escribir la parte del XML que le toca. De esta manera, cualquier modificación del esquema va a ser instantánea.
Veamos pues la clase Cabecera, que a su vez contiene la clase Titular ;
- Code: Select all Expand view RUN
CLASS Cabecera
DATA IDVersionSii INI "0.4"
DATA oTitular
DATA TipoComunicacion INIT "A0" // Enum A0, A1, A4
METHOD New() CONSTRUCTOR
METHOD WriteXML( pNode, NameSpace )
END CLASS
METHOD New() CLASS Cabecera
::oTitular := PersonaFisicaJuridicaESType():New()
RETURN Self
METHOD WriteXML( pNode, NameSpace ) CLASS Cabecera
Local pParent
pParent = mxmlNewElement( pNode, NameSpace + "Cabecera" )
SetNodeValue( pParent , NameSpace + "IDVersionSii", ::IDVersionSii )
SetNodeValue( pParent , NameSpace + "TipoComunicacion", ::TipoComunicacion )
::oTitular:WriteXML( pParent, NameSpace )
RETURN NIL
/*
Datos de una persona física o jurídica Española con un NIF asociado
*/
CLASS PersonaFisicaJuridicaESType
DATA NombreRazon
DATA NIFRepresentante // Optional
DATA NIF
METHOD New() CONSTRUCTOR
METHOD WriteXML( pNode, NameSpace )
END CLASS
METHOD New() CLASS PersonaFisicaJuridicaESType
RETURN Self
METHOD WriteXML( pNode, NameSpace ) CLASS PersonaFisicaJuridicaESType
Local pParent
pParent = mxmlNewElement( pNode, NameSpace + "Titular" )
SetNodeValue( pParent , NameSpace + "NombreRazon", ::NombreRazon )
SetNodeValue( pParent , NameSpace + "NIF", ::NIF )
if !empty( ::NIFRepresentante )
SetNodeValue( pParent , NameSpace + "NIFRepresentante", ::NIFRepresentante )
endif
RETURN NIL
function SetNodeValue( pNode , cKey, uValue )
Local hTree
if uValue != NIL
hTree := mxmlNewElement( pNode, cKey )
mxmlNewText( hTree, 0, cValtoChar( uValue ) )
endif
return hTree
Pues así, con todo lo demás
La clase ResponseSoap, se encarga de crear el XML y enviarlo, pero el patrón es siempre el mismo, crear la clase correspondiente, y que haga solo lo que le toca.
Esto es asi, porque, por ejemplo, la clase titular, se usará en otras partes, PERO NO TENEMOS QUE HACER NADA, PUES YA LA TENEMOS, solo instanciarla donde nos haga falta.
Si creéis que podéis mejorar este sistema, quedo a la escucha de alguna alternativa mejor.
Saludos Cordiales