lunedì 6 giugno 2016

AX 2012 - Export su Excel, scelta del client da utilizzare in RDP


In questo articolo vi presento un'opzione a mio avviso molto utile e che permette di risolvere i problemi di licenze Excel che potrebbero avere alcune aziende.
Nelle opzioni utente è possibile specificare quale Excel aprire, nel caso di export in Excel di una tabella mentre si è collegati in desktop remoto.
  • Andare in "Strumenti > Opzioni" ed posizionarsi sul tab 'Generale'.
  • Scorrere verso il basso fino alla sezione 'Varie'
  • Impostare il parametro 'Remote Desktop session exports to'


Il valore di default è 'Client Excel' ma è possibile sceglier anche 'Server Excel'. Nel primo caso i dati vengono esportati nell'Excel della macchina locale dell'utente, nel secondo viene aperto il programma sul server in cui ci si è collegati in RDP.

Nota: Dalle mie prove il parametro funziona bene se si utilizza il 'Remote Desktop Connection' di Windows, in caso di utilizzo di programmi per la gestione delle connessioni non sempre il comportamento è quello voluto.

mercoledì 13 aprile 2016

giovedì 18 febbraio 2016

AX 2012 - Creazione dimensioni finanziare tramite pattern di valori

Nell'eventualità vi fosse richiesto di creare delle righe di coge (LedgerJournalTrans) da codice, è possibile che dobbiate assegnare manualmente i conti delle dimensioni finanziarie rispetto alla dimensione default. Se i conti sono più di uno è conveniente effettuare un merge e ve la caverete con poche righe di codice.

Probabilmente avrete quindi a disposizione i nomi (DisplayValue--->EDT: Name) e i valori (EDT: DimensionValue).

Non dovete fare altro che crearvi un container avente questa struttura (pattern):

  [numero_delle_dimensioni_da_fondere, "DisplayValue1", "DimensionValue1", "DisplayValue2", "DimensionValue2",.... , "DisplayValueN", "DimensionValueN"]  

con il container appena creato preoccupatevi di avere una DefaultDimension iniziale (ad esempio se avete la LedgerDimension) e poi crearne una nuova con il pattern sopra descritto:

 DimensionDefault        dimDefault, newDimDefault;  
 dimDefault = DimensionStorage::getDefaultDimensionFromLedgerDimension(axLedgerJournalTrans.parmLedgerDimension());  
 newDimDefault= AxdDimensionUtil::getDimensionAttributeValueSetId(patternContainer);  

Dopodichè vi basterà assegnare alla DefaultDimension il risultato del metodo serviceMergeDefaultDimensions passandogli la nuova e la vecchia dimensione :

 axLedgerJournalTrans.parmDefaultDimension(DimensionDefaultingService::serviceMergeDefaultDimensions(newDimDefault, dimDefault));  

NB; per questo esempio è stata utilizzata la classe AxLedgerJournalTrans anzichè la tabella.

martedì 19 gennaio 2016

AX 2009 / AX 2012 - Utilizzo della macro #InventDimJoin

Per effettuare la join con le dimensioni inventariali AX mette a disposizione una serie di macro per agevolare la scrittura della query:

 static void InventDimJoin(Args _args)  
 {  
   InventTrans   inventTrans;  
   InventDim    inventDim;  
   ItemId     itemId;  
   inventDim    inventDimCriteria;  
   InventDimParm  InventDimParm;  
   FromDate    fromDate;  
   ToDate     toDate;  
   ;  
   itemId  = "2-719-400-00";  
   fromDate = mkdate(1,9,2015);  
   toDate  = mkdate(30,9,2015);
  
   inventDimCriteria.InventLocationId = "EG";  
   InventDimParm.InventLocationIdFlag = NoYes::Yes;
   
   //oppure
   InventDimParm.initFromInventDim(inventDimCriteria); 

   select sum(Qty) from inventTrans  
   where inventTrans.ItemId   == itemId  
   && (inventTrans.StatusReceipt == StatusReceipt::Purchased  
   || inventTrans.StatusReceipt == StatusReceipt::Received)  
   && inventTrans.DatePhysical  >= fromDate  
   && inventTrans.DatePhysical  <= toDate  
   #InventDimJoin(inventTrans.inventDimId, InventDim, inventDimCriteria, InventDimParm, dimIdx);
  
   info(StrFmt("%1",inventTrans.Qty));  
 }  

giovedì 10 dicembre 2015

AX 2012 - R2 CU7 - Errore 'Incorrect syntax' su cancellazione schema

La procedura consigliata per i rilasci in produzione delle modifiche su AX 2012 prevede l'utilizzo di comandi che lavorano sul Model DataBase di AX. In particolare, al fine di limitare la durata del fermo in produzione, è prevista la creazione e l'importazione del modelstore su di uno schema temporaneo e poi l'inversione di questo schema con il 'dbo' una volta fermate le attività.

Con clienti che utilizzano la versione R2 CU7, a volte ho notato che nel fare questa operazione non riuscivo ad applicare il cambio di schema e non mi era più possibile eliminare lo schema temporaneo, nemmeno utilizzando un DROP da SQL. Approfondendo la cosa, mi sono accorto che ciò avveniva quando utilizzavo dei numeri all'inizio del nome dello schema (e.g. 20150101_TemporarySchema).

L'errore che ottenevo era il seguente (riporto solo la parte principale):

 Incorrect syntax near '20150101'.

Provando ad utilizzare apici (',"), parentesi ('[', '(' ) o altri caratteri speciali non riuscivo a risolvere il problema. Approfondendo allora il funzionamento dei comandi Management Shell e di AxUtil per la cancellazione dello schema ho visto che tutti richiamavano la stessa store procedure: XU_DropSchema .

All'interno di questa store procedure, nella parte finale viene richiamato il comando sql per il drop dello schema,

  1. SELECT @DropStatement = 'DROP SCHEMA ' + @schemaName

scritto così però non tiene in considerazione l'eventualità che vi siano numeri nella parte iniziale del nome e SQL Server non riesce ad interpretare correttamente il comando. Ho risolto il problema correggendo questa riga, aggiungendo le parentesi '[' per isolare il nome dello schema.

  1. SELECT @DropStatement = 'DROP SCHEMA [' + @schemaName + ']'

Posso confermare, per esperienza diretta, che la correzione risolve il problema. Inoltre aprendo la stessa store procedure in ambienti R3, ho visto che è stata riscritta e che non presenta più l'errore sulla parte finale. Non ho ancora verificato se il problema sussiste in kernel R2 CU8\CU9.

lunedì 16 novembre 2015

AX 2012 - Fare il merge di 2 ledger dimension

In questo post vediamo come effettuare il merge di 2 dimensioni finanziarie. Nel mio dovevo creare una nuova ledger dimension facendo il merge tra una dimensione che mi sono calcolato ( http://somarisuax.blogspot.it/2014/12/ax-2012-creare-una-ledgerdimension.html)



 e la dimensione di default nella mainAccountLegalEntity che nel mio caso aveva la dimensione "Commessa" fissa:


Basterà chiamare il metodo

 combinedLedgerDimension = DimensionDefaultingService::ServiceCreateLedgerDimension(ledgerDimension, mainAccountLegalEntity.DefaultDimension);  

combinedLedgerDimension contiene la dimensione combinata:



martedì 10 novembre 2015

AX 2012 - Query sql per estrarre il codice sorgente dal model

Ciao! con questa query sql potete estrarre il codice degli dal database model di Ax


 use "Model_db"  
 IF (OBJECT_ID('tempdb..#SOURCES') IS NULL)  
 begin  
         --drop table #SOURCES  
         SELECT  
                  b.Name  
                  ,b.ElementHandle  
                  ,coalesce(e.ElementTypeName, el.ElementTypeName) [Type]  
                  ,coalesce(pr.Name, b.Name) [Internal Name]  
                  ,parent.Name [Parent Name]  
                  ,d.Name [Layer]  
                  ,a.[LayerId]  
                  ,c.DisplayName [Model Display Name]  
                  ,c.Name [Model Name]  
                  ,a.[ModelId]  
                  ,a.[MODIFIEDDATETIME]  
                  ,a.[MODIFIEDBY]  
                  ,a.[CREATEDDATETIME]  
                  ,a.[CREATEDBY]  
                  ,cast(coalesce(sr.SourceText, sr1.SourceText) as nvarchar(max)) [Source]  
                 INTO #SOURCES   
         --select count(*)  
          FROM [ModelElementData] a   
                                 inner join [ModelElement] b  
                                        on     a.ElementHandle = b.ElementHandle  
                                 inner join [ModelManifest] c  
                                        on a.ModelId = c.ModelId  
                                 inner join [Layer] d  
                                        on a.LayerId = d.Id  
                                 left join [ModelElement] parent  
                                        on b.ParentHandle = parent.ElementHandle  
                                 left join [ModelElement] pr  
                                        on b.ElementHandle = pr.ParentHandle  
                                 left join [ElementTypes] e  
                                        on pr.ElementType = e.ElementType  
                                 left join [Sources] sr  
                                        on sr.SourceHandle = pr.ElementHandle   
                                 left join [ElementTypes] el  
                                        on b.ElementType = el.ElementType  
                                 left join [Sources] sr1  
                                        on sr1.SourceHandle = b.ElementHandle   
          where not b.Name is null  
                 --and  b.Name like '%InterfaceMacroservice%'   
                 --and coalesce(e.ElementTypeName, el.ElementTypeName) like '%Method%'  
                 --and cast(coalesce(sr.SourceText, sr1.SourceText) as nvarchar(max)) like '%InterfaceMacroService%'  
           --order by 1  
         SELECT TBL.name AS ObjName   
                  ,STAT.row_count AS StatRowCount   
                  ,STAT.used_page_count * 8 AS UsedSizeKB   
                  ,STAT.reserved_page_count * 8 AS RevervedSizeKB   
         FROM tempdb.sys.partitions AS PART   
                 INNER JOIN tempdb.sys.dm_db_partition_stats AS STAT   
                         ON PART.partition_id = STAT.partition_id   
                                 AND PART.partition_number = STAT.partition_number   
                 INNER JOIN tempdb.sys.tables AS TBL   
                         ON STAT.object_id = TBL.object_id   
                         --where TBL.name = '#SOURCES'  
         ORDER BY TBL.name;  
         CREATE INDEX IDX_NAME ON #SOURCES(Name)  
 end  
 --------------------------------------------------------------------  
 select top 10 *  
 from #SOURCES  
 where Name ='SalesFormLetter'  

Nella clausola Where dell'ultima riga potete fare i filtri sul nome dell'oggetto.

L'output è fatto così: