Mensaje    Asunto: Leer fichero de devoluciones SEPA en XML Responder citando

A todos nos ha pillado “fuera de juego” la implantación de los ficheros XML para la emisión de adeudos directos (los vulgares recibos). Si la construcción de los ficheros que se remiten a las entidades es relativamente sencillo, el procesamiento de los que nos envían las mismas con los rechazos o devoluciones al menos a mi se me ha hecho muy cuesta arriba por la poca información que hay.

Afortunadamente después de mucho bucear por la red gracias a Google fui a dar con una web (en inglés) con montañas de código y ejemplos; ya la he puesto en el foro.

Al grano. De acuerdo con la norma de la AEB (Órdenes en formato ISO 20022 para emisión de adeudos directos SEPA en euros) tenemos un fichero de devoluciones similar a este:
Cita:
<xml>
<Document>
<CstmrPmtStsRpt>
<GrpHdr>
<MsgId>RC20160505233641468200000000000000</MsgId>
<CreDtTm>2016-05-05T23:41:40</CreDtTm>
<InitgPty>
<Nm>NOMBRE ORDENANTE</Nm>
<Id>
<OrgId>
<Othr>
<Id>ESxxxxxxxxxxxxxx</Id>
<SchmeNm>
<Cd>COR1</Cd>
</SchmeNm>
</Othr>
</OrgId>
</Id>
</InitgPty>
<CdtrAgt>
<FinInstnId>
<BIC>CECAESMMXXX</BIC>
</FinInstnId>
</CdtrAgt>
</GrpHdr>
<OrgnlGrpInfAndSts>
<OrgnlMsgId>PRE201604300945350000020160503</OrgnlMsgId>
<OrgnlMsgNmId>pain.008.001.02</OrgnlMsgNmId>
<OrgnlNbOfTxs>3</OrgnlNbOfTxs>
<OrgnlCtrlSum>72.00</OrgnlCtrlSum>
</OrgnlGrpInfAndSts>
<OrgnlPmtInfAndSts>
<OrgnlPmtInfId>NOT PROVIDED</OrgnlPmtInfId>
<OrgnlNbOfTxs>3</OrgnlNbOfTxs>
<OrgnlCtrlSum>72.00</OrgnlCtrlSum>
<TxInfAndSts>
<OrgnlInstrId>PRE201604300945350000020160503</OrgnlInstrId>
<OrgnlEndToEndId>748</OrgnlEndToEndId>
<TxSts>RJCT</TxSts>
<StsRsnInf>
<Rsn>
<Cd>AC06</Cd>
</Rsn>
</StsRsnInf>
<OrgnlTxRef>
<Amt>
<InstdAmt>24.00</InstdAmt>
</Amt>
<ReqdColltnDt>2016-05-03</ReqdColltnDt>
<CdtrSchmeId>
<Id>
<PrvtId>
<Othr>
<Id>ESxxxxxxxxxxxxxx</Id>
<SchmeNm>
<Prtry>SEPA</Prtry>
</SchmeNm>
</Othr>
</PrvtId>
</Id>
</CdtrSchmeId>
<PmtTpInf>
<SvcLvl>
<Cd>SEPA</Cd>
</SvcLvl>
<LclInstrm>
<Cd>COR1</Cd>
</LclInstrm>
<SeqTp>RCUR</SeqTp>
</PmtTpInf>
<MndtRltdInf>
<MndtId>748</MndtId>
<DtOfSgntr>2009-10-31</DtOfSgntr>
</MndtRltdInf>
<RmtInf>
<Ustrd>ASOCIACION DE INTERNAUTAS - PERIODO RENOVADO: 01/05/2016 - 30/04/2017 - CONSULTA TU CORREO ELECTRONICO PARA MAS INFORMACION</Ustrd>
</RmtInf>
<Dbtr>
<Nm>NOMBRE DEL DEUDOR</Nm>
<CtryOfRes>ES</CtryOfRes>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>ESxxxxxxxxxxxxxxxxxxxxxx</IBAN>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>BSABESBBXXX</BIC>
</FinInstnId>
</DbtrAgt>
<CdtrAgt>
<FinInstnId>
<BIC>CECAESMMXXX</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>NOMBRE ORDENANTE</Nm>
<PstlAdr>
<Ctry>ES</Ctry>
</PstlAdr>
<Id>
<OrgId>
<Othr>
<Id>ESxxxxxxxxxxxxxx</Id>
</Othr>
</OrgId>
</Id>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>ESxxxxxxxxxxxxxxxxxxxxxx</IBAN>
</Id>
</CdtrAcct>
</OrgnlTxRef>
</TxInfAndSts>
<TxInfAndSts>
</TxInfAndSts>
<TxInfAndSts>
</TxInfAndSts>
</OrgnlPmtInfAndSts>
</CstmrPmtStsRpt>
</Document>

Perdón por el “tocho”.
Ahora el código que he usado (y probado) para leer este mamotreto:
Código:
_di_IXMLNode iNode0, iNode1, iNode2, iNode3, iNode4, iNode5, iNode6;
AnsiString cMotivo, cRefInt, cReferencia;
bool lDevol;
double nImporte;
TDateTime dFecCar, dFecDev;

Query->Close()
Query->SQL->Text = "UPDATE Recibos SET Situacion = :Situacion, Fichero = :Fichero, Devuelto = :FecDev, Motivo = :Motivo "
                   "WHERE Fecha = :FecCar AND RefInt = :RefInt AND Referencia = :Referencia";

XMLDocument->LoadFromFile("Fichero.XML");
XMLDocument->Active = true;
iNode0 = XMLDocument->DocumentElement;
     iNode1 = iNode0->ChildNodes->FindNode("CstmrPmtStsRpt");
          iNode2 = iNode1->ChildNodes->FindNode("GrpHdr");

while (iNode2 != NULL)
{
     cAux = iNode2->LocalName;
     if (cAux == "GrpHdr")
     {
          iNode3  = iNode2->ChildNodes->FindNode("MsgId");
          cAux    = iNode3->Text;
          lDevol  = cAux.SubString(1, 2) == "DA" ? true : false;

          iNode3  = iNode2->ChildNodes->FindNode("CreDtTm");
          cAux    = iNode3->Text;
          dFecDev = EncodeDate((Word)StrToInt(cAux.SubString(1, 4)), (Word)StrToInt(cAux.SubString(6, 2)), (Word)StrToInt(cAux.SubString(9, 2)));
     }
     else if (cAux == "OrgnlGrpInfAndSts")
     {
          iNode3 = iNode2->ChildNodes->FindNode("OrgnlNbOfTxs");
          TryStrToInt(iNode3->Text, fForBln->Barra->MaxValue);
     }
     else if (cAux == "OrgnlPmtInfAndSts")
     {
          iNode3 = iNode2->ChildNodes->FindNode("TxInfAndSts");
          while (iNode3 != NULL)
          {
               iNode4 = iNode3->ChildNodes->FindNode("OrgnlInstrId");
               if (iNode4 != NULL)
               {
                    cAux    = iNode4->Text;
                    cRefInt = cAux.SubString(23, 9);

                    iNode4 = iNode3->ChildNodes->FindNode("OrgnlEndToEndId");
                    if (iNode4 != NULL)
                    {
                         cReferencia = iNode4->Text;
                         iNode4 = iNode3->ChildNodes->FindNode("StsRsnInf");
                         if (iNode4 != NULL)
                         {
                              iNode5 = iNode4->ChildNodes->FindNode("Rsn");
                              if (iNode5 != NULL)
                              {
                                   iNode6 = iNode5->ChildNodes->FindNode("Cd");
                                   if (iNode6 != NULL)
                                   {
                                        cMotivo = iNode6->Text;

                                        iNode4 = iNode3->ChildNodes->FindNode("OrgnlTxRef");
                                        if (iNode4 != NULL)
                                        {
                                             iNode5  = iNode4->ChildNodes->FindNode("ReqdColltnDt");
                                             if (iNode5 != NULL)
                                             {
                                                  cAux    = iNode5->Text;
                                                  dFecCar = EncodeDate((Word)StrToInt(cAux.SubString(1, 4)),
                                                                       (Word)StrToInt(cAux.SubString(6, 2)),
                                                                       (Word)StrToInt(cAux.SubString(9, 2)));

                                                  fPersona->Query->Close();
                                                  fPersona->Query->SQL->Text = cSql;
                                                  fPersona->Query->ParamByName("Fichero")->AsString    = ExtractFileName(XMLDocument->FileName);
                                                  fPersona->Query->ParamByName("FecDev")->AsDateTime   = dFecDev;
                                                  fPersona->Query->ParamByName("Motivo")->AsString     = cMotivo;
                                                  fPersona->Query->ParamByName("FecCar")->AsDateTime   = dFecCar;
                                                  fPersona->Query->ParamByName("RefInt")->AsString     = cRefInt;
                                                  fPersona->Query->ParamByName("Referencia")->AsString = cReferencia;
                                                  try
                                                  {
                                                       fPersona->Query->ExecSQL();
                                                       fPersona->Query->Transaction->CommitRetaining();
                                                  }
                                                  catch(...)
                                                  {
                                                  }
                                             }
                                        }
                                   }
                              }
                         }
                    }
               }
               iNode3 = iNode3->NextSibling();
          }
     }
     iNode2 = iNode2->NextSibling();
}
XMLDocument->Active = false;

Seguramente habrá un método más sencillo, pero para ir saliendo del paso mientras lo encontramos nos vale. A ver si logro explicarlo un poco:
    1. Los datos que guardo en la tabla de recibos (BB.DD. en FB 2,5) son: situación del recibo (pagado, rechazado o devuelto), fichero de devoluciones (en el que vino el impago), fecha de devolución, motivo de la misma (según la tabla que viene el anexo 5, página 76 del documento de la AEB).
    2. Los datos para localizar el recibo son fecha de cargo, referencia interna (que identifica de forma unívoca las órdenes de cobro) y referencia del deudor.


Estos datos en ese fichero de devoluciones están:

Dentro de la etiqueta GrpHdr:
Tipo de operación: Devolución o Rechazo. La etiqueta MsgId. Los dos primeros caracteres identifican el tipo de operación: Si son DA se trata de un devolución; cualesquiera otros son un rechazo. Desde le punto de vista del ordenante es lo mismo: el recibo no se ha cobrado. En el query es el parámetro :Situacion.
Fecha de devolución: Las diez primeras posiciones de la etiqueta CreDtTm en el formato yyyy-mm-dd. Parámetro :FecDev.

Dentro de cada una de las etiquetas TxInfAndSts
Motivo de la devolución. Etiqueta Cd dentro de StsRsnInf y Rsn. Parámetro :Motivo.
Fecha de cargo. Etiqueta ReqdColltnDt en OrgnlTxRef.
Referencia interna. Etiqueta OrgnlInstrId.
Referencia del deudor. Etiqueta OrgnlEndToEndId.

Espero que os sirva.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)