public void markTransactions(InventTransId _inventTransId,InventTransId _refInventTransId,InventDim _inventDim,InventQty _qtyTomark)
{
InventTransOriginId inventTransOriginId = InventTransOrigin::findByInventTransId(_inventTransId).RecId;
InventTransOriginId refInventTransOriginId = InventTransOrigin::findByInventTransId(_refInventTransId).RecId;
InventTransOrigin::updateMarking(inventTransOriginId,
refInventTransOriginId,
_qtyTomark);
InventTransOrigin::updateMarking(refInventTransOriginId,
inventTransOriginId,
-_qtyTomark,//il marking è possibile solo tra qty di segno opposto
"",
SortOrder::Ascending,
false,
_inventDim);
}
martedì 18 ottobre 2016
AX 2012 - Gestione del marking via X++
Il codice sottostante serve per mettere in marking due transazioni:
venerdì 15 luglio 2016
AX 7 - Gestione Eventi
Da questo link una panoramica sulla gestione degli eventi:
- eventi del form
- eventi del datasource di un form
- eventi di un controllo specifico di un form
Li riporto come dal sito segnalato:
- eventi del form
- eventi del datasource di un form
- eventi di un controllo specifico di un form
Li riporto come dal sito segnalato:
Form datasource from xFormRun
[FormEventHandler(formStr(SomeForm), FormEventType::Initialized)] public static void SomeForm_OnInitialized(xFormRun sender, FormEventArgs e) { FormDataSource MyRandomTable_ds = sender.dataSource(formDataSourceStr(SomeForm, MyRandomTableDS)); ... }
Get FormRun from form datasource
[FormDataSourceEventHandler(formDataSourceStr(MyForm, MyRandomTableDS), FormDataSourceEventType::Written)] public static void MyRandomTableDS_OnWritten(FormDataSource sender, FormDataSourceEventArgs e) { FormRun formRun = sender.formRun() as FormRun; // you can even call custom methods (I think IntelliSense won't work though) formRun.myCustomMethod(); }
Get FormRun from form control
[FormControlEventHandler(formControlStr(MyForm, MyButton), FormControlEventType::Clicked)] public static void MyButton_OnClicked(FormControl sender, FormControlEventArgs e) { FormRun formRun = sender.formRun() as FormRun; formRun.myCustomMethod(); }
Access form control from xFormRun
[FormEventHandler(formStr(SomeForm), FormEventType::Initialized)] public static void SomeForm_OnInitialized(xFormRun sender, FormEventArgs e) { // set the control to invisible as an example sender.design().controlName(formControlStr(SomeForm, MyControl)).visible(false); }
Get current record in form control event
[FormControlEventHandler(formControlStr(SomeForm, SomeButton), FormControlEventType::Clicked)] public static void SomeButton_OnClicked(FormControl sender, FormControlEventArgs e) { // as an example the datasource number is used for access; I perceive the formDataSourceStr as more robust SomeTable callerRec = sender.formRun().dataSource(1).cursor(); }
Convert Common and use DataEventArgs
[DataEventHandler(tableStr(AnyTable), DataEventType::ValidatedWrite)] public static void InventLocation_onValidatedWrite(Common sender, DataEventArgs e) { // convert Common to AnyTable AnyTable anyTable = sender; // the DataEventArgs actually are ValidateEventArgs and can be converted ValidateEventArgs validateEventArgs = e; // the ValidateEventArgs carry the validation result (so far) boolean ret = validateEventArgs.parmValidateResult(); // the table has some additional validation logic and gives back the result ret = anyTable.doSomeAdditionalCustomValidation(ret); // provide the args with the validation result validateEventArgs.parmValidateResult(ret); }
Use the onValidatedFieldValue event properly
[DataEventHandler(tableStr(SomeTable), DataEventType::ValidatedFieldValue)] public static void SomeTable_onValidatedFieldValue(Common sender, DataEventArgs e) { SomeTable someTable = sender; // the clue is to know that the DataEventArgs actually are ValidateFieldValueEventArgs and that you can get the field name from them ValidateFieldValueEventArgs validateEventArgs = e; boolean ret = validateEventArgs.parmValidateResult(); FieldName fieldName = validateEventArgs.parmFieldName(); switch (fieldName) { case fieldStr(SomeTable, SomeCustomField): ... do some magic break; } validateEventArgs.parmValidateResult(ret); }
Use the MappedEntityToDataSource event
[DataEventHandler(tableStr(MyTableEntity), DataEventType::MappedEntityToDataSource)] public static void MyTableEntity_onMappedEntityToDataSource(Common _sender, DataEventArgs _eventArgs) { DataEntityContextEventArgs eventArgs = _eventArgs; MyTableEntity entity = _sender; if (eventArgs.parmEntityDataSourceContext().name() == dataEntityDataSourceStr(MyTableEntity, MyTable)) { MyTable myTable = eventArgs.parmEntityDataSourceContext().getBuffer(); ... do some magic with it } }
lunedì 6 giugno 2016
AX 2012 - Export su Excel, scelta del client da utilizzare in RDP
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.
mercoledì 13 aprile 2016
AX 7 - The new Dynamics AX, ecco le novità!
Segnalo questo blog sono elencate e spiegate tutte le novità per gli sviluppatori sul nuovo ERP:
Cliccate qui
Cliccate qui
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):
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:
Dopodichè vi basterà assegnare alla DefaultDimension il risultato del metodo serviceMergeDefaultDimensions passandogli la nuova e la vecchia dimensione :
NB; per questo esempio è stata utilizzata la classe AxLedgerJournalTrans anzichè la tabella.
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,
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.
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.
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,
- 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.
- 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.
Iscriviti a:
Post (Atom)