Páginas

Como executar um restore database via query no SQL Server

Recentemente tive problemas com o restore do SQL Server que deu um erro e não estava conseguindo restaurar um backup de uma base de projetos na base de desenvolvimento, conversando com meu amigo DBA Sandro Bichbich ele me mostrou uma maneira de criar uma query que faz o restore (bem mais rápido por sinal que a ferramenta do próprio SQL Server).

Ao invés de utilizarmos a ferramenta nativa do SQL:


Podemos criar uma query que faça esse trabalho, porém é importante que essa query não esteja sendo executada na database que estamos utilizando para ser restaurada, para isso vamos utilizar a database master.

Agora vamos executar o seguinte código:

RESTORE FILELISTONLY from disk = 'Local onde se encontra o backup'

Feito isso vamos pegar o resultado do LogicalName para criar o seguinte código:


RESTORE DATABASE [Base a ser restaurada] FROM DISK = 'Local onde se encontra o backup'

WITH  FILE = 1,  NOUNLOAD ,  STATS = 1,  RECOVERY , 
move 'NomeLógico'   to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\BaseASerRestaurada.mdf',
move 'NomeLógico_log'   to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\BaseASerRestaurada_log.LDF',
REPLACE
Go

No meu caso o resultado ficou assim:

RESTORE FILELISTONLY from disk = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Backup\DynamicsAX_Proj.bak'

RESTORE DATABASE [DynamicsAX_Dev] FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Backup\DynamicsAX_Proj.bak'

WITH  FILE = 1,  NOUNLOAD ,  STATS = 1,  RECOVERY , 
move 'DynamicsAx_Proj'   to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\DynamicsAx_Dev.mdf',
move 'DynamicsAx_Proj_log'   to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\DynamicsAx_Dev_log.LDF',
REPLACE
Go

[]'s
Alexssander

Criar um campo display em um formulário.

Ontem precisei criar um método display em um formulário e ao contrário de um display em um relatório é um pouco mais trabalhoso e meu amigo Ricardo Pichler me mostrou um artifício bem legal para consumir menos memória colocando o método em cache.

Utilizei em um formulário o Data Source CustTrans, porém necessitava retornar um campo da CustTransOpen.

Para isso criei na tabela CustTrans o seguinte método:
  1. display DueDate dueDateOrigin(CustTrans _custTrans)
  2. {
  3.     CustTransOpen   custTransOpen;
  4.     ;
  5.  
  6.     select firstonly DueDateOriginal from custTransOpen
  7.         where custTransOpen.RefRecId == _custTrans.RecId;
  8.  
  9.     return custTransOpen.DueDateOriginal;
  10. }
Em seguida, no método init do meu Data Source adicionei após o super() a seguinte linha de código:
  1. this.cacheAddMethod(tablemethodstr(CustTrans, dueDateOrigin));
Depois criei o campo arrastando o método direto da tabela para o design do formulário e alterei a propriedade DataSource para CustTrans.

[]'s 
Alexssander

Criando sub-reports através do Reporting Services

Esses dias precisei criar um sub-report para um determinado relatório que estava desenvolvendo, como já havia passado por um projeto onde vi alguns relatórios feitos dessa maneira fui atrás para saber como funciona e como fazer, não achei nenhuma referência na web além do MSDN Library e por isso estou escrevendo esse novo post.

Os sub-reports são relatórios que o usuário abre clicando em um item relacionado no relatório principal abrindo um detalhamento do registro.

Para exemplificar vou criar algo bem prático e simples que é o relatório baseado em  Diários.

Criei uma query simples apenas com a LedgerJournalTable, ordenado pelo JournalNum e uma query com a LedgerJournalTrans ordenado pelo AccountNum:



Em seguida no Visual Studio criei um novo projeto em C# com o template Dynamics AX Reporting Project, renomeei o projeto para AeDX_SubReport. 

Utilizei um layout padrão que existe no AX, para isso precisamos fazer o seguinte: 
  1. No menu File > Add > Existing Project from Dynamics AX. 
  2. Uma nova janela abrirá com os Report Library que existem na AOT. 
  3. Vamos selecionar Shared Library. 
  4. No Solution Explorer, vamos clicar com o botão direito no projeto AeDX_SubReport e selecionar AddReference.  
  5. Na tab Projects, vamos selecionar o projeto Shared Library, clicar em Add e depois em OK.
Em seguida vamos criar nossos relatórios: um relatório de nível superior e um relatório de detalhamento. O relatório superior irá conter uma lista com todos os diários da tabela LedgerJournalTable. E o relatório de detalhamento sera um relatório mais detalhado que irá exibir as informações das linhas dos diários selecionado no relatório de nível superior. Vamos adicionar um parâmetro e um filtro para o relatório de detalhamento. O filtro vai filtrar os dados no relatório de análise com base no valor do parâmetro.

Para criar o relatório de nível superior:
  1. Na Solution Explorer no projeto AeDX_SubReport vamos renomear o relatório Report1.moxl para LedgerJournalTable.moxl.
  2. Vamos adicionar um novo Dataset.
  3. Dê um duplo clique e vamos selecionar a nossa query AeDX_LedgerJournalTable e os campos que iremos utilizar, nesse exemplo vou selecionar os campos JournalNum, JournalName, JournalTotalCredit, JournalTotalDebit.
  4. Selecione o Dataset  e clique com o botão direto e vá em Properties e vamos alterar a propriedade Name para: LedgerJournalTable.
  5. Agora vamos gerar o design do nosso relatório arrastando nosso Dataset para o nó Design, o design automaticamente é nomeado AutoDesign1.
  6. Selecione o nó AutoDesign1, clique com o botão direito e selecione Properties.
  7. Na propriedade LayoutTemplate vamos selecionar ReportLayoutStyleTemplate e na propriedade Title vamos alterar para Diários.
  8. Agora vamos expandir o nó AutoDesign1 e selecionar o nó table LedgerJournalTableTable e em Properties, defina o modelo Style Template para TableStyleTemplate e alterar para LedgerJournalTable a propriedade Title.
Para criar o relatório de detalhamento:
  1. Na Solution Explorer vamos clicar com o botão direito no projeto AeDX_SubReport > Add > New Item.
  2. Vamos selecionar Report e renomeá-lo para LedgerJournalTrans.moxl e clicar em Add.
  3. Vamos adicionar um novo Dataset.
  4. Dê um duplo clique e vamos selecionar a nossa query AeDX_LedgerJournalTrans e os campos que iremos utilizar, nesse exemplo vou selecionar os campos JournalNum, AccountNum, BankAccountId, AmountCurCredit, AmountCurDebit.
  5. Selecione o Dataset  e clique com o botão direto e vá em Properties e vamos alterar a propriedade Name para: LedgerJournalTrans e a propriedade Default Layout para TopDownList.
  6. Agora vamos gerar o design do nosso relatório arrastando nosso Dataset para o nó Design, o design automaticamente é nomeado AutoDesign1.
  7. Selecione o nó AutoDesign1, clique com o botão direito e selecione Properties.
  8. Na propriedade LayoutTemplate vamos selecionar ReportLayoutStyleTemplate e na propriedade Title vamos alterar para Linhas do Diário.
  9. Agora vamos expandir o nó AutoDesign1 e selecionar o nó table LedgerJournalTransList e em Properties, defina o modelo Style Template para ListStyleTemplate e alterar para LedgerJournalTrans a propriedade Title.
Para criar o parâmetro que será usado para transmitir os dados para o relatório de detalhamento:
  1. No relatório LedgerJournalTrans.moxl selecione o nó Parameters e clique com o botão direito e Add Parameter.
  2. Selecione o parâmetro criado e clique com o botão direito em Propriedades altere as propriedades Data Type para System.String, Name para JournalNum, e Prompt String para Número do Diário:.
Para definir um filtro para o relatório de detalhamento:
  1. Vamos expandir o nó Design em seguida expandir o nó LedgerJournalTransList e adicionar um novo filtro clicando com o botão direito sobre Filter > Add Filter.
  2. Selecione o novo filtro e com o botão direito selecione Properties e especifique nas propriedades Expression =Fields!JournalNum.Value,  Name para JornalNum, Operator para Equals e na propriedade Value =Parameters!JournalNum.Value.
Adicionando ao relatório Drill Through Action:

Agora que criamos o relatório de nível superior e o relatório de detalhamento, vamos adicionar uma ação drill through, através de uma ação para ligar os dois relatórios. Uma ação drill through é definida sobre o relatório de nível superior.

Para adicionar a ação drill through:
  1. Na Solution Explorer vamos dar um duplo clique no relatório LedgerJournalTable.moxl.
  2. Vamos expandir o nó Designs, expandir o nó AutoDesign1, expandir o nó LedgerJournalTable e expandir o nó Data.
  3. Vamos selecionar o campo JournalNum e clicar com o botão direito Add > Report Drill Through Action.
  4. Selecione o nó criado e clique com o botão direito em Properties.
  5. Na propriedade ReportDesign clique no botão (…) selecione o relatório de detalhamento (LedgerJournalTrans) e selecione o design AutoDesing1 e em seguida clique em OK.
  6. Selecione o parâmetro JournalNum e especifique na propriedade Value =Fields!JournalNum.Value.
  7. Selecione o parâmetro AX_CompanyName e especifique na propriedade Value =Parameters! AX_CompanyName.Value.
Essa é a visão dos relatórios de nível superior e detalhamento:

  
Para visualizarmos o relatório de detalhamento:
  1. Dê um duplo clique no relatório LedgerJournal.moxl clique com o botão direito no AutoDesign1 > Preview. O relatório é exibido com todos os diários criados.
  2. Clique no número do diário e o relatório de detalhamento é exibido. O relatório de detalhamento exibe os detalhes do diário selecionado.
O resultado é o seguinte:

Diário (Relatório de Nível Superior)
Quando clico no Journal Num 000002_008:

Linhas do Diário (Relatório Detalhamento)
PS. Deixo uma dica, no momento em que estiver criando a query aproveite para criar o range ao invés de criá-lo no Visual Studio pois melhora e muito a performance, pois no momento em que executa a query ele retorna os resultados filtrados.

[]'s 
Alexssander

Exibir relatório com totais por data (dia, mês ou ano).

Tive que fazer um relatório outro dia em que precisava retornar o total que recebi de despesas no mês de um determinado cliente e o prazo médio de faturamento (vencimento – emissão) e recebimento (recebimento – emissão), para isso fiz um relatório todo baseado no fetch() que guardava uma variável com o total por data e um total geral e fazia uma verificação ao mudar a data ele chamava a seção do relatório que retornava o total.

O relatório que desenvolvi ficou dessa maneira:

Estava hoje fuçando em alguns relatórios do AX e acabei descobrindo algo bem interessante que faz exatamente o que eu precisei fazer, só que de uma maneira muito mais simples.

Para isso fiz um exemplo muito para postar a solução no blog.

Primeiramente criei uma tabela temporária com os campos que quero retornar no relatório:


Em seguida criei um relatório baseado na minha tabela temporária e no Sorting ou Order By do meu Data Source adicionei o campo TransDate e nesse momento vem a grande sacada.

Nas propriedades do campo que quero ordenar existe a propriedade AutoSum é o que vai me retornar a soma com base nesse campo.

A propriedade SumDetailLevel é o que vai nos dizer se queremos retornar o total por ano da data (1), pelo mês e ano (2) ou se retorna o total por dia, por mês e por ano (3).



Em seguida no método run() realizei vários inserts direto na tabela para criar uma um exemplo para demonstrar.

No método prompt(), antes do return utilizei o método setCursor para selecionar o registro atual.
  1. queryRun.setCursor(tmpMediumTerm);
Em seguida criei o design do relatório e ele ficou da seguinte maneira:


O projeto encontra-se no meu SkyDrive.

Caso alguém tenha alguma sugestão ou dúvida, comentem.
[]'s
Alexssander

1º aniversário do blog.

Pessoal,

Dia 14/05/2011 o blog completou 1 ano no ar.

Sei que estou há um tempo sem postar nada, porém não estou passando por muitas situações diferentes no trabalho, espero que nesse novo ano do blog seja de mais desafios na minha caminhada para que possa surgir vários posts novos.

Obrigado a todos que acompanham e contribuem com o blog.

[]'s
Alexssander

MATERIAL CFOP + CST

Bom Dia a todos,

Nas ultimas semanas tenho demonstrado este material a muitos consultores que questionam o uso da CFOP + CST no AX, O mesmo é bem simples mas demonstra como utilizarmos estas caracteristicas. Aproveito e faço minha primeira Publicação no site.

TABELA II
CÓDIGO DA SITUAÇÃO TRIBUTÁRIA –CST
(Códigos a que se refere o art.230 deste regulamento)

AS OPÇÕES SÃO:



Poderemos cadastrar de 2 formas:

1) Caso o Item sempre ter uma compra em 1(uma) determinada situação acima demonstrada, este poderá ser fixado no cadastro de itens :

CAMINHO AX: Gerenciamento de estoque > Detalhes do item > Informações Fiscais:



2) Caso o Item ter mais de uma origem das situações acima demonstradas, este poderá ser identificado no cadastro de informações fiscais da Ordem de Venda :

CAMINHO AX: Contas a receber > Detalhe da Ordem de Venda > Itens > Informações Fiscais:



1) Grupo de Impostos Sobre Vendas do Item:

CAMINHO AX: Contabilidade > Configuração > Imposto sobre vendas > Grupo de impostos sobre venda do item:




Para impostos que existir mais de um Valor fiscal ou código de Tributação, o mesmo deverá ser cadastrado separado em:

CAMINHO AX: Contabilidade > Configuração > Imposto sobre vendas > Código de imposto sobre vendas

E identificado através da regra da matriz de impostos em qual momento deverá ser utilizado cada código de tributação nas notas fiscais de vendas. Exemplo:


Espero que seja de grande utílidade.

Nelson Romero

Como enviar e-mail pelo AX sem configurar o Outlook

Essa semana precisei fazer o AX enviar e-mails direto, sem precisar configurar o Outlook.

Para isso verifiquei em alguns blogs de referência e no final criei um código simples e com algumas configurações do sistema fiz o envio diretamente pelo AX.

Antes de alterar qualquer código, vamos parametrizar a conta que o AX utiliza para fazer o envio, para isso vamos acessar o módulo Administração > Configuração > Parâmetros de email.
 

Nessa tela vamos configurar o servidor de saída, computador local, porta SMTP, tamanho do anexo e outras configurações como nome do usuário, senha e se iremos utilizar autenticação NTLM (que é um protocolo de autenticação para transações entre dois computadores em que um ou ambos estejam executando o Windows NT 4.0 ou anterior).

Ao enviar os e-mail eu não quero que apareça a conta que configuramos como remetente para executar o envio, para isso o usuário que está logado no sistema precisa ter a sua conta configurada nas opções de usuário.

Para configurar vamos no Painel de Navegação do Microsoft Dynamics AX > Ferramentas > Opções e vamos configurar a conta de email do usuário no campo Email:
 

Feito isso vamos alterar o código reportSendMail() da classe Info:
  1. void reportSendMail(PrintJobSettings p1)
  2. {
  3.     System.Net.Mail.MailMessage myMailMessage;
  4.     System.Net.Mail.Attachment attachment;
  5.     System.Net.Mail.AttachmentCollection attachementCollection;
  6.     System.Net.Mail.SmtpClient myMail;
  7.     System.Net.NetworkCredential myNC;
  8.     //Atribui o nome de usuário e senha dos parâmetros para as variáveis 
  9.     str myLogin = SysEmailParameters::find(true).SMTPUserName;
  10.     str myPassword = SysEmailParameters::password();
  11.     str userMailAddress; 
  12.     str receiverMailAddress;
  13.     str mailBody;
  14.     str smtpServer;
  15.     str mail;
  16.     str fileName = 'axaptareport';
  17.     FileNameOpen    fileNameForEmail; 
  18.     FileIOPermission perm;
  19.     ;
  20.     //Verifica o tipo do Anexo 
  21.     if (p1.format() == PrintFormat::ASCII)
  22.     {
  23.         fileNameForEmail = subStr(p1.fileName(),strLen(p1.fileName())-3,-999)+'TXT'; 
  24.     }
  25.     else if (p1.format() == PrintFormat::RTF)
  26.     {
  27.         fileNameForEmail = subStr(p1.fileName(),strLen(p1.fileName())-3,-999)+'RTF';
  28.     }
  29.     else if (p1.format() == PrintFormat::HTML)
  30.     {
  31.         fileNameForEmail = subStr(p1.fileName(),strLen(p1.fileName())-3,-999)+'HTM';
  32.     }
  33.     else if (p1.format() == PrintFormat::PDF || p1.format() == PrintFormat::PDF_EMBED_FONTS) 
  34.     {
  35.         fileNameForEmail = subStr(p1.fileName(),strLen(p1.fileName())-3,-999)+'PDF';
  36.     }
  37.     mail = subStr(fileNameforEmail,(strlen(fileNameforEmail)-8),9); 
  38.     fileNameforEmail = winApi::getTempPath() + mail;
  39.     perm = new FileIOPermission(fileNameforEmail,'w');
  40.     //Verifica se o usuário tem permissão 
  41.     if (!perm)
  42.     {
  43.         throw error("Não é possível mover o anexo para a pasta Temp.");
  44.         return;
  45.     }
  46.     else
  47.     {
  48.         try
  49.         {
  50.             perm.assert();
  51.         }
  52.         catch
  53.         {
  54.             throw error("Não é possível acessar a pasta Temp.");
  55.             return;
  56.         }
  57.         userMailAddress = SysUserInfo::find().Email;
  58.         receiverMailAddress = p1.mailTo() + "," + p1.mailCc();
  59.         mailBody = "E-mail enviado a partir da " + CompanyInfo::name() + ", pelo Microsoft Dynamics AX";
  60.         smtpServer = SysEmaiLParameters::find(false).SMTPRelayServerName;
  61.         myMailMessage = new System.Net.Mail.MailMessage(userMailAddress,receiverMailAddress);
  62.         myMailmessage.set_Subject(p1.mailSubject());
  63.         myMailmessage.set_Body(mailBody);
  64.         winapi::moveFile(p1.fileName(), fileNameforEmail);
  65.         attachementCollection = myMailMessage.get_Attachments();
  66.         attachment = new System.Net.Mail.Attachment(fileNameforEmail);
  67.         attachementCollection.Add(attachment);
  68.         myMail = new System.Net.Mail.SmtpClient(smtpServer);
  69.         myNC= new System.Net.NetworkCredential(mylogin,mypassword);
  70.         myMail.set_Credentials(myNC);
  71.         myMail.Send(myMailmessage);
  72.         Box::info("E-mail enviado com sucesso!");
  73.     }
  74. }
Para testar, vamos realizar o envio do e-mail de um relatório de clientes no módulo Contas a receber > Relatórios > Dados Básicos > Cliente


Filtrei apenas alguns clientes e agora vamos no botão Opções, selecionar a opção Destinatário do email, Preencher o assunto, destinatário, formato do anexo e depois Ok:


Você terá uma mensagem informando que o e-mail foi enviado com sucesso e receberá o e-mail com o relatório.
Note que o e-mail do remetente é o e-mail que configuramos nas opções do usuário.
E por fim temos o relatório do cliente em anexo.



Fiz um código meio que as pressas para isso e tenho certeza que dá para melhorar, cheguei a ver até que você pode configurar um template do corpo do email, por isso aceito sugestões e dúvidas.

Quando tiver um tempo vou dar uma olhada com calma nessa parte dos templates e crio um novo post futuramente.

[]'s
Alexssander

Como criar uma conexão entre o SQL e o Dynamics AX

Eu estou trabalhando em uma migração de dados um tanto extensa, mas ao invés de trabalhar exportando dados do antigo sistema para um arquivo e importando o mesmo para o AX, decidimos criar uma conexão direta com o SQL, ler as informações e importá-las para o AX. Meu amigo ucraniano que trabalha comigo Kirill Val me deu as instruções e depois da primeira classe o resto é brincadeira de criança. Gostaria de que caso alguém tenha alguma sugestão de melhoria, por favor envie.

Criei uma classe chamada AXImport e no método classDeclaration() eu declaro duas variáveis, uma do tipo LoginProperty e outra do tipo ODBCConnection. Essa classe específica é usada também para update, por isso, para executá-la em batch, eu a fiz extendendo a classe RunBaseBatch, mas não é uma regra.
  1. public class AXImport extends RunBaseBatch
  2. {
  3.     LoginProperty         loginProperty;
  4.     ODBCConnection  connection;
Depois crie um método connect() que faça a conexão com o banco:
  1. void connect()
  2. {
  3.     ;
  4.     loginProperty = new LoginProperty();
  5.     loginProperty.setServer('localhost');//Endereço do server, no caso era local
  6.     loginProperty.setDatabase('DATABASE');//Nome da base, na dúvida, cheque no SQL
  7.  
  8.     connection = new ODBCConnection(loginProperty);
  9. }
Crie os métodos pack() e unpack():
  1. public container pack()
  2. {
  3.     container ret;
  4.     return ret;
  5. }

  1. public boolean unpack(container packedClass)
  2. {
  3.     boolean ret = true;
  4.     return ret;
  5. }
Agora crie um método que leia os dados do banco e os insira no AX. Só vamos criar um método que vai inserir alguns fornecedores. Como é um exemplo, só vou mandar buscar o id, nome e grupo de fornecedores. Você pode colocar quais campos você quer no select, ou usar o SELECT * FROM, mas neste caso, você vai precisar ver no SQL qual vai ser a ordem em que os campos aparecem, enquanto na primeira opção você escolhe a ordem dos campos. Saber a ordem dos campos é importante na hora de determinar qual campo do banco preencherá o campo da tabela no AX.
  1. void TransferData()
  2. {
  3.     Statement statement;
  4.     ResultSet  resultSet;
  5.     str            sql;
  6.     str            description;
  7.     SqlStatementExecutePermission perm;
  8.     VendTable      vendTable;
  9.     AxVendTable axVendTable;
  10.     VendGroupId vendGroup;
  11.     VendAccount vendAccount;
  12.     VendName    vendName;
  13.     ;
  14.     //Deve ser utilizado a sintaxe do SQL, não a do AX.
  15.     sql = "SELECT NANUM, NANAME, NAVENDG FROM TABELA_EXEMPLO";
  16.     perm = new SqlStatementExecutePermission(sql);
  17.     perm.assert();
  18.  
  19.     statement = connection.createStatement();
  20.     resultSet  = statement.executeQuery(sql);
  21.  
  22.     while (resultSet.next())
  23.     {
  24.         //Na mesma ordem do select ou, se usado o select * from, na que está no SQL
  25.         vendAccount = strrtrim(resultSet.getString(1));
  26.         vendName = strrtrim(resultSet.getString(2));
  27.         vendGroup = strrtrim(resultSet.getString(3));
  28.         vendTable = VendTable::find(vendAccount);
  29.  
  30.         if (!vendTable)
  31.         {
  32.             axVendTable = new AxVendTable();
  33.             axVendTable.parmAccountNum(vendAccount);
  34.             axVendTable.parmVendGroup(vendGroup);
  35.             axVendTable.parmName(vendName);
  36.             axVendTable.save();
  37.             info("Inserido Fornecedor: " + vendAccount);
  38.         }
  39.     }
  40.     resultSet.close();
  41.     statement.close();
  42. }
Crie o método run() que vai chamar primeiro o método da conexão e depois o da transferência de dados.
  1. public void run()
  2. {
  3.     this.connect();
  4.     this.TransferData();
  5. }
Crie o método main():
  1. static void main(Args args)
  2. {
  3.     AXImport axImport = new AXImport();
  4.     ;
  5.     if (axImport.prompt())
  6.     {
  7.         axImport.run();
  8.         info("Importação concluída");
  9.     }
  10. }
Bom, essa é a classe de exemplo, qualquer sugestão de melhoria ou críticas são bem-vindas.

Erro ao integrar NFe: 299 Rejeição: XML da área de cabeçalho com codificação diferente de UTF-8.

Hoje recebi um e-mail do David Alencar que participa do grupo Dynamics AX BR sobre um problema com o cabeçalho da NFe e achei bem interessante, pois uma vez estava gerando um arquivo para troca de informações com um outro sistema e tive um problema parecido, abaixo o e-mail que recebi do grupo.

Vínhamos há algum tempo sofrendo com a seguinte mensagem de erro ao integrar uma NFe: 299 Rejeição: XML da área de cabeçalho com codificação diferente de UTF-8.

De início não entendíamos o problema, pois o cabeçalho do XML que estava sendo enviado estava com o encoding correto (UTF-8).

Enfim descobrimos que a mensagem de erro estava validando um XML que era enviado dentro do XML principal no elemento "cabecMsg" e que este XML estava sem um cabeçalho.

Para resolver o problema alteramos o código que gera a mensagem do elemneto "cabecMsg" na classe "EFDocMsgHeaderXMLFormat_BR" método "build", de forma que o conteúdo dentro deste elemento também tivesse um cabeçalho de XML com o encoding solicitado pela NFe (UTF-8).

O código alterado ficou da seguinte maneira:
  1. public str build()
  2. {
  3.     XmlElement message; 
  4.     XmlElement msgFormatVersion;
  5.     //
  6.     str myxml;
  7.     //
  8.     ;
  9.     message = this.createElement('cabecMsg');
  10.     message.setAttribute('versao', '1.02');
  11.     msgFormatVersion = this.createElementWithValue('versaoDados', xmlFormatVersion);
  12.     message.appendChild(msgFormatVersion);
  13.     //
  14.     myxml = message.outerXml();
  15.     myxml = strfmt(@'%1',myxml);
  16.     return myxml;
  17.     //
  18. }
Caso alguém se interesse em acompanhar o grupo segue o link: Dynamics AX BR.
Lembrando que a solução foi enviada pelo David Alencar.

[]'s
Alexssander