static void LIL_Ax2012To365ClassesSourceCode(Args _args)
{
SysDictClass dictClass;
SysDictMethod dictMethod;
int i;
Source Source;
TextIo textIo;
Set methodsSet;
SetEnumerator methodsSetEnum;
str filePath;
ClassId classId;
str addSpaceToNewLineRegex;
container conFilter = ["TXT", "*.txt"];
#File
#xppTexts
classId = classNum(Tutorial_RunbaseBatch);
dictClass = new SysDictClass(classId);
for (i=1;i<=dictClass.objectMethodCnt();i++)
{
dictMethod = new SysDictMethod(UtilElementType::ClassInstanceMethod, dictClass.id(), dictClass.objectMethod(i));
if(dictMethod.name() == identifierStr(classDeclaration))
{
Source += dictMethod.getSource() + #newline;
Source = strRem(Source, "}");
}
}
methodsSet = dictClass.methods(true,true,false);
methodsSetEnum = methodsSet.getEnumerator();
methodsSetEnum.reset();
while(methodsSetEnum.moveNext())
{
dictMethod = methodsSetEnum.current();
Source += dictMethod.getSource();
Source += #newline + #newline;
}
source += "}";
filePath = WinAPI::getSaveFileName(
0,
conFilter,
"",
"@SYS56237",
".txt",
dictClass.name());
textIo = new TextIo(filePath, #IO_WRITE);
textIo.write(Source);
}
martedì 12 marzo 2024
AX 2012 - Job per scrivere i metodi di una classe in un unico file di testo
venerdì 24 novembre 2023
D365FFO - How to release to warehouse cross docking works even in case of warehouse "full reservation" release method
In case in the warehouse setup we have the following parameters set
- Requirement for inventory reservation: Require full reservation
- Insufficient quantity response: Generate an error
It is still possible to release a sales order to warehouse even if there is no stock but at least a purchase order is marked against a sales order.
The crossdocking template is set in the following way:
- Demand requirements set as Marking
- Work template defined in the work template
- Supply source defined as "Purchase order"
- Location type: Location directives
We create the sales order as follows.
We create the purchase order directly from the sales order as follows.
In order to release the sales order to the warehouse it is required to launch the batch procedure as follows:
- The value "Quantity to release": Reserved physically and cross dock
The shipment has a planned cross docking defined as follows.
Instead if we use the fulfillment policy on the sales order header blocking the release to warehouse in case of missing items in the stock, the system is not able to skip the planned cross docking lines.giovedì 23 novembre 2023
D365FFO - How to create a transfer order using the warehouse mobile app
Using the warehouse mobile app it is possible to generate a transfer order declaring the warehouse to we want to ship the goods and the license plate we want to move.
Before performing the transaction we have to set the following menu item.
Then we access, in the warehouse mobile app, the menu item previously created.
We scan the destination warehouse.
We scan the license plate we want to move.
Once we complete the transfer order we can click on the button complete order underlined above.
Then we have to run the procedure "Process warehouse app events".
martedì 21 novembre 2023
D365FFO - Register material consumption using warehouse mobile device
It is now possible to register the material consumption for production orders directly using the warehouse mobile device. In order to do it, the following feature "Register material consumption on the mobile app on a scale unit" must be switched on.
We create a production order, not setting any location directive for picking components or wave / work templates. At the end we release the production order not generating any work for picking goods, as reported below.
Then we can start the production order using the mobile app or the feature on the production order form (ensuring to not consume components while doing it).
After that we can access the warehouse mobile app feature related to the following menu item defined into the parameters.
Using the mobile app it is possible to generate automatically a picking list journal directly related to the production order.
After scanning the production order, it is required to scan the picking location as reported below.
Then it is required to scan all the other information:
- itemid
- batchid (if needed)
- serial number (if needed)
and to confirm the quantity to be picked as shown below.
Once completed the picking procedure the button Done must be clicked as reported below.
After clicking on the Done the system generates a picking journal posted against the production order scanned during the procedure.
lunedì 20 novembre 2023
D365FFO - How to backflush components using RAF from warehouse mobile device
In the warehouse mobile device app there is the option to report as finish (RAF from now on) the production orders using for instance the following setup in the menu item for mobile app.
While reporting as finished a production order it can be needed to consume all the production order components (backflush of components).
In order to do it the following parameters (Respect flushing principle during overpicking set to YES) must be set in the report as finished warehouse app menu item.
And the following must be set in the production order defaults form under the tab "Report as finished":
- Picking list journal name
- Automatic bom consumption set to Flushing principle
A picking list journal for consuming the components of the production order and a report as finished journal for producing the final item.
martedì 24 ottobre 2023
D365 FFO - Creare una form di selezione / deselezione
Per creare queste form AX mette disposizione l'oggetto "SysListPanelRelationTableCallback"
Allo scopo occorrono 3 Tabelle:
1) Anagrafica dei gruppi (LILGroupTable)
2) Anagrafica degli elementi che assegneremo di volta in volta ai gruppi (LILElementTable)
3) Tabella che contiene il legame (LILGroupElementTable), che sarà interemente gestita tramite l'oggetto "SysListPanelRelationTableCallback"
Ogni elemento inoltre potrà appartenere contemporaneamente a più di un gruppo alla volta.
Le tre tabelle saranno così fatte:
Definiamo a questo punto una nuova form con il seguente design:
E che conterrà il seguente codice: [Form]
class LILGroupElementInfo extends FormRun
{
SysListPanelRelationTableCallback sysListPanel;
Num groupId;
Description elementNameFilter;
public void close()
{
sysListPanel.finalize();
super();
}
void init()
{
#ResAppl
sysListPanel = SysListPanelRelationTableCallback::newForm(element,
element.controlId(formControlStr(LILGroupElementInfo, ListPanelGroup)),
"@SYS113180", "@SYS78228", #ImageUser,
tablenum(LILGroupElementTable),
fieldnum(LILGroupElementTable, ElementId),
fieldnum(LILGroupElementTable, GroupId),
tablenum(LILElementTable),
fieldnum(LILElementTable, ElementId),
[fieldnum(LILElementTable, ElementId), fieldnum(LILGroupTable, Description)],
fieldNum(LILElementTable, Description), '',
identifierstr(Validate), '', '');
sysListPanel.parmDataRangeAsFilter(true);
super();
sysListPanel.init();
}
public edit str elementNameFilter(boolean _set, str _elementName)
{
if (_set && _elementName != elementNameFilter)
{
elementNameFilter = _elementName;
}
return elementNameFilter;
}
void tabChanged(int fromTab, int toTab)
{
#define.TabGroups(1)
#define.TabGroupElement(2)
groupId = LILGroupTable.GroupID;
switch (toTab)
{
case #TabGroups:
break;
case #TabGroupElement:
sysListPanel.parmDataRangeValue(elementNameFilter ? elementNameFilter : '*');
sysListPanel.parmRelationRangeValue(LILGroupTable.GroupID);
sysListPanel.parmRelationRangeRecId(LILGroupTable.RecId);
sysListPanel.fill();
break;
}
}
boolean validate(userId _userId, AddRemove _addRemove, SysListPanelRelationTable _listViewHandler)
{
return true;
}
[DataSource]
class LILGroupTable
{
public boolean validateWrite()
{
boolean ret;
str leftTrimedGroupId;
str rightTrimedGroupId;
leftTrimedGroupId = strltrim(LILGroupTable.GroupID);
rightTrimedGroupId = strrtrim(leftTrimedGroupId);
LILGroupTable.GroupID = rightTrimedGroupId;
ret = super();
return ret;
}
int active()
{
int ret;
ret = super();
element.elementNameFilter(true, '');
element.tabChanged(0, tab.tab());
return ret;
}
}
[Control("Tab")]
class Tab
{
void tabChanged(int fromTab, int toTab)
{
element.tabChanged(fromTab, toTab);
super(fromTab, toTab);
}
}
[Control("TabPage")]
class Groups
{
public boolean allowPageDeactivate()
{
boolean ret;
ret = super();
if (!LILGroupTable.RecId)
{
if (LILGroupTable_ds.validateWrite())
LILGroupTable.write();
else
return false;
}
return ret;
}
}
[Control("Button")]
class FilterButton
{
public void clicked()
{
super();
sysListPanel.parmDataRangeValue(elementNameFilter ? elementNameFilter : '*');
sysListPanel.fill(true);
}
}
}
public final class LILSysListPanel_VendDlvModeAssociation extends SysListPanel
{
public void addData(container _data)
{
int i;
LILDlvModeAssociation relation;
DlvMode dlvMode;
for (i = 1; i <= conlen(_data); i++)
{
relation.DlvModeRecId = conpeek(_data, i);
relation.DlvModeId = DlvMode::findRecId(relation.DlvModeRecId).Code;
relation.insert();
}
}
public container getData()
{
container selectedData;
container availableData;
LILDlvModeAssociation relation;
DlvMode dlvMode;
while select TableId from relation
join RecId, Code from dlvMode
where dlvMode.RecId == relation.DlvModeRecId
{
selectedData += [[dlvMode.RecId, dlvMode.Code]];
}
while select RecId, Code from dlvMode
order by Code
notexists join relation
where relation.DlvModeRecId == dlvMode.recId
{
availableData += [[dlvMode.RecId,dlvMode.Code]];
}
return [availableData, selectedData];
}
public int numOfColumns()
{
return 2;
}
public void removeData(container _data)
{
int i;
RefRecId dlvModeRecId;
LILDlvModeAssociation relation;
for (i = 1; i <= conlen(_data); i++)
{
ttsBegin;
dlvModeRecId = conpeek(_data, i);
delete_from relation
where relation.DlvModeRecId == dlvModeRecId;
ttsCommit;
}
}
public static LILSysListPanel_VendDlvModeAssociation construct()
{
return new LILSysListPanel_VendDlvModeAssociation();
}
internal static SysListPanel newForm(FormRun _formRun,
int _parentId)
{
LILSysListPanel_VendDlvModeAssociation sysListPanel = LILSysListPanel_VendDlvModeAssociation::construct();
sysListPanel.parmFormRun(_formRun);
sysListPanel.parmParentId(_parentId);
sysListPanel.parmCaptionSelected("@SYS113180");
sysListPanel.parmCaptionAvailable("@SYS179894");
sysListPanel.parmHasAddAllButton(true);
sysListPanel.parmHasRemoveAllButton(true);
sysListPanel.parmHasUpDownButton(true);
sysListPanel.build();
return sysListPanel;
}
protected FormListSort sortMethod()
{
return FormListSort::NoSort;
}
}
3) Create l'estensione della VendParameters aggiungendo un nuovo tab. Il nuovo tab và laciato vuoto perchè sarà poi gestito dalla classe che abbiamo creato al passo 2 [ExtensionOf(formStr(VendParameters))]
final class LILVendParameters_Form_Extension
{
public LILSysListPanel_VendDlvModeAssociation sysListPanelDlvMode;
public LILSysListPanel_VendDlvModeAssociation parmSysListPanel(LILSysListPanel_VendDlvModeAssociation _sysListPanelDlvMode = sysListPanelDlvMode)
{
sysListPanelDlvMode = _sysListPanelDlvMode;
return sysListPanelDlvMode;
}
public void init()
{
next init();
this.initListPanels();
}
protected void initListPanels()
{
sysListPanelDlvMode.init();
sysListPanelDlvMode.fill();
}
}
class LILVendParametersFormEH
{
[FormEventHandler(formStr(VendParameters), FormEventType::Initializing)]
public static void VendParameters_OnInitializing(xFormRun sender, FormEventArgs e)
{
FormRun fr = sender as FormRun;
LILSysListPanel_VendDlvModeAssociation sysListPanelDlvMode = LILSysListPanel_VendDlvModeAssociation::newForm(fr, fr.controlId(formControlStr(VendParameters, LILDlvMode)));
fr.parmSysListPanel(sysListPanelDlvMode);
}
}
mercoledì 27 settembre 2023
D365FFO - Messaggio di errore: "Stopped (error): X++ Exception: The text associated with this work item cannot be found in the assignee’s language"
Eseguire i seguenti passaggi:
- Richiamare il Workflow;
- Andare alla versione attiva del flusso e controllare che la configurazione del flusso di lavoro disponga delle traduzioni nelle lingue utilizzate dagli utenti come lingue predefinite;
- Controllare che la lingua predefinita degli utenti del flusso sia la stessa in uso dal workflow.