static void LILRecordTableCount(Args _args)
{
Common common;
DictTable dt;
DataArea dataArea;
int64 totalCount;
int64 companyCount;
TableName _tableName = "CustTable";
//metodo 1: usare crossCompany, utile per un conteggio secco
dt = new DictTable(tableName2id(_tableName));
common = dt.makeRecord();
select crossCompany count(RecId) from common;
info(strFmt("Totale record su tutte le company: %1",common.recId));
//metodo 2: usare changeCompany, utile per escludere / includere company nel conteggio
if(dt.dataPrCompany())
{
while select dataArea
where dataArea.id != "DAT"
{
common = null;
changeCompany(dataArea.Id)
{
common = dt.makeRecord();
select count(RecId) from common;
companyCount = common.RecId;
totalCount += companyCount;
info(strFmt("Company %1: %2 record", dataArea.Id, companyCount));
}
}
}
else
{
common = dt.makeRecord();
select count(RecId) from common;
companyCount = common.RecId;
totalCount = companyCount;
}
info(strFmt("Totale record sulle company selezionate: %1", totalCount));
}
martedì 19 maggio 2026
D365FFO, AX2012 - Contare i record su una tabella
lunedì 12 gennaio 2026
D365FFO - Flexible sampling plan (skip lot)
I was eager in the last months to test the new flexible sampling plan (or skip lot for friends). It is a function always required by customers and not easy to implement, if not using vertical solutions.
In order to use it it is required to define the flexible sampling plans in the related form (Inventory management - Setup - Quality control - Flexible sampling plans).
In the example reported above, for the specific plan the test group TG would be applied 3 times in a row for the orders (the status 10 would be reiterated 3 times) and if all the tests are passed the status 20 is reached and the quality orders would be created 1 each 10 orders. The status 20 will be kept till all the quality orders are passed.
In order to apply the specific sampling plan, it is needed to apply it using the quality associations as shown below.
Basically the flag Flexible sampling must be switched on and the Flexible sampling plan code must be defined.
From the flexible sampling plan form it is possible to monitor the status of the sampling level as reported below.
giovedì 8 gennaio 2026
D365FFO - CAPA Management (Corrective and Preventive Action)
The CAPA case is managed using a workflow (defined in the field CASE process), using which different actions / tasks are created and assigned to specific users. All the activities are visible accessing the button Activities / View activities.
It is possible to manage as well the electronic signature for the approval / reset status of the activities.
martedì 23 dicembre 2025
D365FFO - Stampare le informazioni sugli indici delle tabelle
Con questo job è possibile recuperare le informazioni sugli indici e sulla tabelle a cui appartengono.
Questo link è stato molto utile:
https://gist.github.com/mazzy-ax/4d4d06ec2fddd885b67527623467aee8
internal final class LIL_getIndexInfo
{
public static void main(Args _args)
{
str60 packageName,tableName;
CLRObject packages, models,tables;
CLRObject packagesEnumerator, modelsEnumerator,tablesEnumerator;
str60 moduleVersion;
DictTable dictTable;
DictIndex dictIndex;
int i;
FieldId fieldId;
TableId tableId;
DictField dictField;
boolean isUnique;
str packageDir;
Microsoft.Dynamics.AX.Metadata.MetaModel.ModelInfo modelInfo;
// Recupera i package
packages = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetInstalledModuleNames();
packagesEnumerator = packages.GetEnumerator();
// Recupera la directory dei package
var environment = Microsoft.Dynamics.ApplicationPlatform.Environment.EnvironmentFactory::GetApplicationEnvironment();
packageDir = environment.get_Aos().get_PackageDirectory();
// Configurazione provider metadati
var runtimeProviderConfiguration = New Microsoft.Dynamics.AX.Metadata.Storage.Runtime.RuntimeProviderConfiguration(packageDir);
var metadataProviderFactory = New Microsoft.Dynamics.AX.Metadata.Storage.MetadataProviderFactory();
Microsoft.Dynamics.AX.Metadata.Providers.IMetadataProvider provider = metadataProviderFactory.CreateRuntimeProvider(runtimeProviderConfiguration);
;
//loop packages
while (packagesEnumerator.moveNext())
{
packageName = packagesEnumerator.get_Current();
models = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetModelsInModuleSortedByDisplayName(packageName);
modelsEnumerator = models.GetEnumerator();
//loop models in the package
while(modelsEnumerator.MoveNext())
{
modelInfo = modelsEnumerator.get_Current();
tables = provider.Tables.ListObjectsForModel(modelInfo.Name);
tablesEnumerator = tables.GetEnumerator();
while(tablesEnumerator.moveNext())
{
tableName = tablesEnumerator.get_Current();
if(tableName)
{
dictTable = new DictTable(tableName2Id(tableName));
if(dictTable)
{
for (i = 1; i <= dictTable.indexCnt(); i++)
{
dictIndex = new DictIndex(dictTable.id(), dictTable.indexCnt2Id(i));
isUnique = !dictIndex.allowDuplicates();
//print table and index info
info(strFmt("Table = %1 | Indice: %2 | Model = %3 | Package = %4 | Modules = %5 | Unique: %6",
tableName,
dictIndex.name(),
modelInfo.Name,
packageName,
dictTable.modules(),
isUnique ? "Yes" : "No"
));
}
}
}
}
}
}
}
}
lunedì 24 febbraio 2025
D365FFO - Salvare un file excel nel print archive
Questo post è interessante perchè mostra come salvare un file nel print archive:
https://www.linkedin.com/pulse/excel-reports-d365-finance-operations-sohan-prasad-kanti/
Possiamo usare questa logica per scrivere una classe (anche batch) per generare un file excel ed effettuare il salvataggio nel print archive:
private void LIL_GenerateFileAndSaveToPrintArchive()
{
SalesTable salesTable;
System.IO.Stream workbookStream = new System.IO.MemoryStream();
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
using(var package = new OfficeOpenXml.ExcelPackage(memoryStream))
{
var worksheets = package.get_Workbook().get_Worksheets();
var worksheet = worksheets.Add("Sheet1");
var cells = worksheet.get_Cells();
var currentRow=1 ;
var cell = cells.get_Item(currentRow,1);
cell.set_Value("SalesId");
while select firstonly10 salesTable
{
currentRow++;
cell= cells.get_Item(currentRow, 1);
cell.set_Value(salesTable.SalesId);
}
package.Save();
}
memoryStream.Seek(0,System.IO.SeekOrigin::Begin);
//salvataggio nel print archive
SRSPrintArchiveContract printArchiveContract;
printArchiveContract = SRSPrintArchiveContract::construct();
printArchiveContract.parmExecutionDate(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
printArchiveContract.parmExecutionTime(DateTimeUtil::getTimeNow(DateTimeUtil::getUserPreferredTimeZone()));
printArchiveContract.parmFileName("test.xlsx");
printArchiveContract.parmJobDescription("test 01");
printArchiveContract.savePrintArchiveDetails(Binary::constructFromMemoryStream(memoryStream).getContainer());
}
giovedì 23 gennaio 2025
AX2012 - D365: remittance address transaction line does not match the remittance address on the payment journal line. TRICKY MESSAGE EASY SOLUTION
The message seems high-sounding !
venerdì 29 novembre 2024
AX 2012 - Associare un contatto ad un indirizzo
Con questo job possiamo associare un contatto ad un qualsiasi indirizzo:
static void LIL_CreateAddressContact(Args _args)
{
DirPartyContactInfoView dirPartyContactInfoView;
LogisticsContactInfoView logisticsContactInfoView;
LogisticsElectronicAddress logisticsElectronicAddress;
dirPartypostalAddressView dirPartypostalAddressView;
logisticsLocation logisticsLocation;
CustTable custTable;
//identificare l'indirizzo a cui associare il contatto
select firstOnly dirPartypostalAddressView
join CustTable
where CustTable.Party == dirPartypostalAddressView.Party
&& CustTable.AccountNum == "US-002"
&& dirPartypostalAddressView.LocationName == "TEST03"
&& dirpartypostalAddressView.ValidFrom <= DateTimeUtil::utcNow()
&& dirpartypostalAddressView.ValidTo >= DateTimeUtil::utcNow();
if(dirPartypostalAddressView)
{
select firstOnly logisticsLocation
where logisticsLocation.ParentLocation == dirPartypostalAddressView.Location;
if(!logisticsLocation)
{
logisticsLocation.clear();
logisticsLocation.initValue();
logisticsLocation.ParentLocation = LogisticsLocation::find (dirPartypostalAddressView.Location).RecId;
logisticsLocation.IsPostalAddress = NoYes::No;
logisticsLocation.insert();
}
dirPartyContactInfoView.LocationName = "contatto mail";
dirPartyContactInfoView.Locator = "somarisuax@mail.com";
dirPartyContactInfoView.Type = LogisticsElectronicAddressMethodType::Email;
dirPartyContactInfoView.IsPrimary = NoYes::No;
dirPartyContactInfoView.Location = logisticsLocation.RecId;
logisticsElectronicAddress.initValue();
logisticsContactInfoView.initFromPartyContactInfoView(dirPartyContactInfoView);
logisticsElectronicAddress = LogisticsElectronicAddressEntity::initFromLogisticsContactInfoView(logisticsContactInfoView, logisticsElectronicAddress);
if (logisticsElectronicAddress.validateWrite())
{
logisticsElectronicAddress.insert();
info("Contatto creato!");
}
}
else
{
warning("Indirizzo non trovato!");
}
}
il risultato sarà il seguente:





