Grupos de Layouts

Antes de aplicar qualquer sugestão aqui descrita, verifique os termos do disclaimer do blog.

Grupos de Layouts do Configurador

Administração > Atividades Periódicas > Configurador > Grupos de layouts do configurador

Como eu faço para retirar os caracteres não-numéricos de um campo?

Por exemplo, como utilizar o campo CNPJCPFNum da tabela CompanyInfo transformando o valor “123456780001-23″ em “12345678000123”.

Usei esse exemplo porque já existe um método findCNPJ_BR() nessa tabela que faz exatamente isso usando a função stralpha(). De modo similar, existe o método unformattedCNPJCPFNum() na tabela CustTable, mas obtendo o mesmo resultado com a função strkeep().

Mas suponha que não exista um método. Antes de criar mais um, este caso pode ser solucionado usando o formato D(n). Ele não está documentado, mas está implementado e aplica as mesmas transformações dos dois métodos citadas.

Por que o método que selecionei está retornando brancos ou zeros?

Uma das desvantagens em usar métodos ao invés de campos é que não há uma verificação (via combobox “Nome do campo”) de que os campos necessários foram selecionados no Grupo de Definições.

Tomando os dois métodos de CNPJ citados acima, temos bons exemplos.

\Data Dictionary\Tables\CustTable::unformattedCNPJCPFNum()
public display CNPJCPFNum_BR unformattedCNPJCPFNum()
{
#define.Digits("0123456789")
;
return strkeep(this.cnpjcpfNum, #Digits);
}

O problema aqui é que o método utiliza o campo CustTable.cnpjCPFNum e, se esse campo não estiver selecionado no respectivo Grupo de Definição, então o método retornará vazio.

Caso mais sútil ainda é usar o método nfeCustCNPJCPF() da tabela FiscalDocJour_BR que requer que o campo OrderAccount tenha sido definido antes. Ou ainda o método issTaxValue() dessa mesma tabela que requer os campos Voucher e InvoiceDate.

Isso tudo para alertar que o uso de métodos no Grupo de Layouts requer uma investigação da implementação deles para verificar as dependências de campos.

Mas ainda falta discutir o outro método do exemplo anterior:

\Data Dictionary\Tables\CompanyInfo::findCNPJ_BR()

display CNPJCPFNum_BR findCNPJ_BR()
{
CompanyInfo companyInfo;
;
if (this.RecId)
    {
companyInfo = CompanyInfo::findRecId(this.RecId);
    }
    return stralpha(companyInfo.cnpjcpfNum);
}

Aqui temos um bom exemplo de como evitar o problema de esquecer campos no Grupo de Definições. O segredo está no refresh do buffer da tabela usando construções como:


if (this.RecId) myTable = MyTable::findRecId(this.RecId);

Assim, mesmo que o Grupo de Definição tenha selecionado apenas parte dos campos do buffer recebido (this), a linha acima irá preencher os demais campos no buffer devolvido (myTable).

Nota: Apesar de simples e universal, os métodos find*() não são performáticos por trazerem todos os campos da tabela. Se o tempo for crítico, melhor criar um método específico para trazer apenas o campo necessário.

Estou preenchendo os campos da conta bancária, mas o método dac_BR() da tabela BankTable gera uma exceção! E como faço para preencher o campo do dígito da agência?

O caso do método dac_BR() é fácil, tem um erro de lógica. Retire o ponto de exclamação da linha abaixo:

\Data Dictionary\Tables\BankTable::dac_BR()
if (!hasSecuritykeyAccess(securitykeynum(BankTables), AccessType::View))
if (hasSecuritykeyAccess(securitykeynum(BankTables), AccessType::View))

No caso do dígito da agência, é necessário criar dois novos métodos:

\Data Dictionary\Tables\BankTable::agencyDAC_BR()

display BankDAC_BR agencyDAC_br()
{
if (hasSecuritykeyAccess(securitykeynum(BankTables), AccessType::View))
{
return Bank_BR::parseAgencyDAC(this.AccountNum);
}
else
{
throw error("@SYS57330");
}
}

Seguimos o mesmo padrão dos demais métodos. Mantivemos também o mesmo EDT (BankDAC_BR), mas pode ser criado um novo se quiser deixar mais claro. O outro método é:

\Classes\Bank_BR::parseAgencyDAC()

public static BankDAC_BR parseAgencyDAC(BankAccount _bankAccountInfo)
{
int maxLen = strlen(_bankAccountInfo);
int pos;
;

pos = strfind(_bankAccountInfo, ' ', 1, maxLen);
pos = strfind(_bankAccountInfo, ' ', pos + 1, maxLen);

if (substr(_bankAccountInfo, pos - 2, 1) == '-')
{
pos--;
}
return substr(_bankAccountInfo, pos, 1);
}

Por que a coluna Tipo de soma está desabilitada ou nem todas as opções estão disponíveis?

Essa coluna tem a mesma lógica da página Soma no Assistente de Grupo de Definições, isto é, apenas se aplica a campos e métodos cujos tipos de retorno sejam Integer ou Real.

No caso de campos, só serão mostradas as opções (Average, Count, Total) conforme o que foi escolhido no Grupo de Definições (Média, Contar, Total). Para os métodos serão mostradas todas as opções sempre.

Nota: O suporte a métodos ainda não tinha sido implementado no Dynamics AX 2009 SP1 (veio na RU-3 ou RU-4).

Não consigo fazer o método cashDiscAmount() da tabela LedgerJournalTrans funcionar. O que faço?

Provalvamente mais um exemplo de falta de campo selecionado o Grupo de Definições. Esse método usa o campo Company dessa mesma tabela para funcionar. Alternativamente, você pode alterar a seguinte linha desse método:


where foundSpecTrans.SpecCompany == this.Company

por


where foundSpecTrans.SpecCompany == this.company()

O método company() está presente em todas as tabelas e retorna o valor do campo dataAreaId.

Selecionei no meu layout o método VendEPRemitInfo_BR::BarCodeInfo(). Tento gerar um arquivo de pagamento que possui esse campo preenchido, mas a mensagem recebida é “O campo código de barras deve ser preenchido”. O que fazer?

Realmente existe um erro na validação desse campo durante a geração do arquivo. No método VendOutPaymRecord_CNAB_BR::checkBarCode(), o registro correspondente ao pagamento na VendEPRemitInfo_BR é obtido por:


select vendEPRemitInfoLoc
join ledgerJournalTransLoc
where ledgerJournalTransLoc.RecId == vendEPRemitInfoLoc.RefRecId;

Essa consulta retornará sempre um mesmo registro (mesmo com status “Enviado”), independente de qual pagamento você esteja processando. Caso o campo do código de barras desse tal registro esteja vazio, você não conseguirá validar o seu pagamento.

Para corrigir o problema, substitua o trecho de código citado por:


select vendEPRemitInfoLoc
where vendEPRemitInfoLoc.RefRecId == _ledgerJournalTrans.RecId;

Ao gerar o arquivo, recebo a mensagem “A solicitação de permissão do tipo ‘ExecutePermission’ falhou.”. Isso sempre acontece quando tenho mais de um método configurado no layout. A chamada ao segundo método (na mesma linha do layout) é quem gera esse erro. Como resolver isso?

Existe um “desbalanceamento” no uso de objetos ExecutePermission nos métodos outputFormatedDS() e outputFormatedDSComplex() na classe ConfExport_BR. Para entender, cada chamada ao método ExecutePermission::assert() tem que estar pareada com um CodeAccessPermission::revertAssert().

Olhando os dois métodos da ConfExport_BR, note que o assert() é chamado um única vez no início dos métodos, mas o revertAssert() é chamado várias vezes (dentro do while select em um ConfLayoutLines_BR e ConfLayoutFields_BR). Apesar de simplesmente mover CodeAccessPermission::revertAssert() para o final do método pode resolver, isso não é muito seguro porque abre todo o método.

O correto a fazer é mover o assert() para onde ele é necessário. Passo a passo para cada método:

ConfExport_BR::outputFormatedDS()

No início do método, remova as linhas:


ExecutePermission executePermission;

executePermission = new ExecutePermission();
executePermission.assert();

Lá pelo meio do método, adicione a linha destacada:


if (layoutFields.MethodName)
{
new ExecutePermission().assert();
// chamadas ao dictTable.callObject(...)
CodeAccessPermission::revertAssert();
}

ConfExport_BR::outputFormatedDSComplex()

No início do método, remova:


ExecutePermission executePermission;

Um pouco mais abaixo, remova também:


executePermission = new ExecutePermission();
executePermission.assert();

Também no meio do método, adicione as linhas destacadas:


if(layoutFields.MethodName)
{
new ExecutePermission().assert();
// chamadas ao dictTable.callObject(...)
CodeAccessPermission::revertAssert();
}

Veja o método updateSummaryField() para exemplo de uso correto.

Importante: A criação do objeto dictTable está errada:


if (!dictTable)
{
dictTable = new DictTable(tableId);
}

Isto é, ele será criado apenas uma vez com o primeiro tableId que tiver um método. Caso métodos de duas tabelas diferentes sejam usados, a chamada ao callObject() falhará. Nos métodos outputFormatedDS(), outputFormatedDSComplex() e outputFormatted(), corrija para:


if (!dictTable || (dictTable && dictTable.id() != tableId))
{
dictTable = new DictTable(tableId);
}

Assim, o objeto dictTable será recriado se a tabela mudar.

Nota: Boa parte dessas alterações foram lançadas no Roll-up 6 (RU6) para o Microsoft Dynamics AX 2009. Veja o blog disclaimer.

4 Responses to Grupos de Layouts

  1. vanessa says:

    oi daniel td bem??? vc tem a configuração de algum layout de retorno de pagamento escritural? realizei os testes e não faz a leitura do meu arquivo

    obrigada

  2. Karen says:

    Daniel, muito obrigada pelos comentários…

    Foram de muita valia!

    Não deixe de postar.

    :)

  3. Fernando says:

    Ola Daniel.

    Tenho um grupo configurador de layout que no trailer do arquivo eu não estou conseguindo somar os totais de pagamentos.
    Ou seja dentro do meu arquivo tenho três titulos. No rodapé do arquivo sair que estou pagando 3 titulos. Entende?

    Agradeço atenção desde já.

    Fernando

  4. Ana Lúcia says:

    Olá. O campo do código de barras bloqueia para não permitir digitar duas vezes o mesmo conteúdo em diários diferentes?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>