Facilitando um pouco as coisas no Padrão TISS Parte 3 Validação
Leonel Fraga de Oliveira
22/09/2015 09:30
No artigo passado da nossa série sobre TISS (Transferência de Informações na Saúde Suplementar), vimos como montar o arquivo XML que é lido pelas operadoras de plano de saúde, e principalmente uma forma de facilitar o cálculo do tão temido hash.
Mesmo que tenhamos feito tudo certo, chega uma hora que um arquivo é rejeitado pela operadora, seja que faltou algum dado, ou uma tag fora de lugar, entre outros motivos.
Cada operadora possui suas regras de negócio, e embora o padrão do arquivo seja o mesmo, algumas informações podem ser requeridas ou não. Porém se algo está mal-formatado no arquivo XML desde a origem, todas vão chiar.
Os arquivos XSD que descrevem o padrão TISS também servem para validá-los na origem. E este artigo mostrará como fazer isso de forma bem simples utilizando o .NET Framework.
Porém antes vamos fazer uma pequena alteração no arquivo XML.
Não sei se você reparou no arquivo que é gerado pelo código demonstrado no artigo anterior, logo na primeira tag. Embora na parte texto hard-coded essa tag esteja com o encoding ISO-8859-1, o arquivo gerado sai como UTF-8, e isto já é motivo para algumas operadoras rejeitarem o arquivo.
Isto ocorre quando carregamos o arquivo na classe XElement, na hora quando limpamos elementos em branco e convertemos os valores para maiúsculas, e depois salvamos esse arquivo. Temos que fazer com que o nosso arquivo saia com o encoding ISO 8859-1, então faremos duas pequenas alterações, logo após limparmos o arquivo.
//Elimina nós vazios, remove espaços (início e fim) dos valores e transforma em maiúsculos no XML
XElement docx = XElement.Parse(sbXML.ToString());
docx.Descendants().Where(e => String.IsNullOrEmpty(e.Value)).Remove();
docx.TrimWhiteSpaceFromValues();
docx.UpperValues();
/*até aqui, não muda nada*/
/*
Aqui tem uma pequena alteração. Vamos carregar o nosso XML limpo em um XDocument,
só que com a codificação ISO-8859-1, que é a que o padrão TISS pede.
Se salvar direto do XElement, gera um arquivo com codificação UTF-8
*/
XDocument xmlISO = new XDocument(new XDeclaration("1.0", "ISO-8859-1", "no"), docx);
//Recarrega o XML tratado no stringbuilder de origem, para gerar o hash.
using (MemoryStream ms = new MemoryStream())
{
xmlISO.Save(ms); //Note que não salvamos mais o objeto "docx", e sim o "xmlISO".
ms.Position = 0;
/*
Também alteramos o StreamReader, ele terá a entrada em ISO-8859-1
*/
using (StreamReader sr = new StreamReader(ms, Encoding.GetEncoding("ISO-8859-1")))
{
sbXML = new StringBuilder(sr.ReadToEnd());
}
}
/*
O cálculo do hash não altera nada, porém ao finalizar o arquivo, tiraremos o atributo
standalone=no do arquivo.
A linha abaixo vai depois que substituímos o </ans:mensagemTISS> pelo novo trecho do arquivo
com cálculo do hash.
*/
sbXML.Replace("standalone=\"no\"", "");
Depois dessa pequena alteração, vamos enfim fazer uma pré-validação do arquivo na origem, através do XSD do padrão TISS.
1. Baixe os arquivos XSD, e grave-os em uma pasta da sua aplicação. Estes arquivos devem ser distribuídos com a aplicação, inclusive Web (devem ser colocados no servidor e acessíveis pela aplicação): http://leonelfraga.com/downloads/XSD-TISS-v30201.zip
2. Ao descompactar o arquivo, note que temos duas versões do arquivo xmldsig-core-schema.xsd. Se utilizarmos a versão original, ocorre um erro na validação, então modificamos esse arquivo e salvamos as alterações no arquivo xmldsig-core-schema-2.xsd.
3. Utilizaremos aqui uma classe para fazer a validação. Criamos um StringBuider global chamado ErrosXML, e três funções que farão a validação. Veja o código abaixo.
public class ValidadorXML
{
private StringBuilder ErrosXML = new StringBuilder();
public string ValidarXML(string xmlOrigem)
{
ErrosXML = new StringBuilder();
try
{
//Pasta onde estão localizados os arquivos XSD
string xsdRoot = @"C:\GeradorTISS\xsdv30201";
XmlDocument xml = new XmlDocument();
xml.Schemas.Add("http://www.ans.gov.br/padroes/tiss/schemas", xsdRoot + @"\tissV3_02_01.xsd");
xml.Schemas.Add("http://www.w3.org/2000/09/xmldsig#", xsdRoot + @"\xmldsig-core-schema-2.xsd");
xml.LoadXml(xmlOrigem);
xml.Validate(xmlSettings_ValidationEventHandler);
}
catch (Exception ex)
{
ErrosXML.AppendLine(String.Format("Erro na validação do arquivo XML: {0}", ex.Message));
ErrosXML.AppendLine("------------");
}
return ErrosXML.ToString();
}
public void xmlSettings_ValidationEventHandler(object sender, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Error || e.Severity == XmlSeverityType.Warning)
{
XmlSchemaValidationException ex = (XmlSchemaValidationException)e.Exception;
XmlNode noGuia = capturaNoGuia((XmlElement)ex.SourceObject); //Se o nó é uma guia
if (noGuia != null) //Se o erro foi em um nó que representa uma guia
{
XmlNode numeroguia = noGuia.SelectSingleNode("descendant::*[local-name()='numeroGuiaPrestador']");
ErrosXML.AppendLine(String.Format("Erro na guia {0}: {1}", numeroguia.InnerText, e.Message));
ErrosXML.AppendLine("------------");
}
else //Se o erro foi em outra parte do arquivo que não seja uma guia
{
ErrosXML.AppendLine(String.Format("Erro no arquivo XML: {0}", e.Message));
ErrosXML.AppendLine("------------");
}
}
}
private XmlNode capturaNoGuia(XmlNode src)
{
if (src != null)
{
if ("guiaSP-SADT|guiaResumoInternacao".Contains(src.LocalName))
return src;
else
return capturaNoGuia(src.ParentNode);
}
else
{
return src;
}
}
}
A função ValidarXML() recebe o conteúdo do arquivo XML, e dentro dela instanciamos um XMLDocument, onde adicionamos na coleção Schemas o arquivo principal do esquema TISS (tissV3_02_01.xsd, ou outra versão) e o arquivo xmldsig-core-schema-2 (a versão alterada). Feito isso, carregamos a string de entrada no nosso objeto xml e executamos o método Validate().
Este método Validate() recebe como parâmetro um delegate do tipo ValidationEventHandler para capturar os erros a serem gerados. Criamos uma função nomeada xmlSettings_ValidationEventHandler() e passamos ela no Validate().
Dentro desse handler de validação, capturaremos a exceção que é gerada pela validação (XmlSchemaValidationException) e qual nó está disparando essa exceção. Para a mensagem ser um pouco mais amigável e dar uma indicação para o usuário de qual guia está dando problema, verificamos se o nó é uma guia através da função capturaNoGuia(). Feito isso, alimentamos o StrinBbuilder que criamos no início com as mensagens de erro.
Se o StringBuilder estiver vazio, não existem erros de formatação no arquivo XML. Caso alguma tag cujo tipo seja enumeração e nela estiver um valor não aceito, ou uma tag esteja fora de lugar, ou outra inconsistência estiver presente no arquivo de acordo com o XSD, serão disparados erros que alimentarão o StringBuilder.
Vimos que não é complicado fazer a pré-validação do arquivo TISS utilizando o próprio XSD para validá-lo, e ao fazer essa validação as chances do arquivo ser rejeitado pelas operadoras diminui bastante, pois elas também fazem essa validação após o upload. Agora, a validação das regras internas e distintas das operadoras são outra história.
Com isso, terminamos a nossa série sobre TISS. Espero que tenham aproveitado e que seja útil de alguma forma!