Active record count in ValidTimeState tables

In the previous blog post we have explored how to tell how many records do we have per company account using a T-SQL Query. But AX does have a concept which SQL cannot cope with, and makes it a bit harder to tell the active record count in ValidTimeState tables.

The following job can pull back this value for us, in order to validate if data migration row counts are matching between AX 2012 and D365FO.

static void WIK_findValidTimeStateKey_Tables(Args _args)
{
    DictTable           dictTable;
    Dictionary          dict = new Dictionary();
    TableId             tableId;
    Common              common;
    date                currentDate = systemDateGet();
 
    setPrefix('Record count for ValidTimeStateKey tables');
    tableId = dict.tableNext(0);
 
    while (tableId)
    {
        dictTable = new DictTable(tableId);
        if (!dictTable.isTmp() && !dictTable.isTempDb() && !dictTable.isView()
            && (dictTable.configurationKeyId() ? isConfigurationKeyEnabled(dictTable.configurationKeyId()) : true)
            && dictTable.isValidTimeStateTable())
        {
            common = dictTable.makeRecord();
            select validTimeState(currentDate) count(RecId) from common;
 
            if (common.RecId)
            {
                info(strFmt('%1\t%2', dictTable.name(), common.RecId));
            }
        }
 
        tableId = dict.tableNext(tableId);
    }
}

SQL record count by company

As part of our Data Upgrade from AX 2012 to Dynamics 365 Finance and Operations we had to ensure a way of telling how many records do we have per data area. Data import via entities are company-based, so we had to find out a reliable way to tell if everything has been successfully transferred. The following script can tell exactly that, show the SQL record count by company:

SET NOCOUNT ON
DECLARE @tableName NVARCHAR(255);
DECLARE @statement NVARCHAR(MAX);
 
-- Temporary table for storing record counts
CREATE TABLE #jdm_count (TableName NVARCHAR(255), Company NVARCHAR(4), RecordCount int)
 
-- Cursor for getting list of User-created tables
DECLARE cur_name CURSOR FOR
       SELECT QUOTENAME(SCHEMA_NAME(sOBJ.schema_id))
              + '.' + QUOTENAME(sOBJ.name)
       FROM sys.objects as sOBJ
       WHERE
              sOBJ.type = 'U'
              AND sOBJ.is_ms_shipped = 0x0
       ORDER BY SCHEMA_NAME(sOBJ.schema_id), sOBJ.name;
 
OPEN cur_name
 
-- Loop tables
FETCH NEXT FROM cur_name INTO @tableName
WHILE @@FETCH_STATUS = 0
BEGIN
       -- Construct SQL Statement for getting company-specific record count
       SELECT @statement = 'SELECT ''' + @tableName + ''' AS [TableName]'
              + IIF(COL_LENGTH(@tableName, 'DATAAREAID') IS NOT NULL, ', DATAAREAID AS [Company]', ', '''' AS [COMPANY]')
              + ',COUNT(*) AS [RowCount] FROM ' + @tableName + ' WITH (NOLOCK)'
              + IIF(COL_LENGTH(@tableName, 'DATAAREAID') IS NOT NULL, ' GROUP BY [DATAAREAID]', '')
              + ' HAVING COUNT(*) > 0';
              
       -- Insert statement results in temporary table
       INSERT INTO #jdm_count (TableName, Company, RecordCount)
              EXEC sp_executeSQL @statement;
 
       FETCH NEXT FROM cur_name INTO @tableName
END
 
CLOSE cur_name
DEALLOCATE cur_name
 
-- Display results
SELECT * FROM #jdm_count
ORDER BY RecordCount DESC, TableName, Company
 
DROP TABLE #jdm_count
SET NOCOUNT OFF

Removing SSRS document archives

Throughout the years we have noticed that out SRSReport Document Type for Document attachments grew over the size of 90GB. This was getting filled by having the Print to document archive checkbox ticked in their saved Print settings. The generated printouts were just sitting in the archive without ever being actioned. We had to come up with a way to untick this setting for everyone and revoke the option to enable it again on the form. After that we could start removing SSRS document archives.

Did the following changes under \Classes\SysPrintOptions to only allow system administrator to set this flag:

private void init()
(...)
        storeInPrintArchive = Global::isSystemAdministrator() ? printJobSettings.storeInPrintArchive() : false;

public void new()
(...)
    showOutputPrintArchive  = Global::isSystemAdministrator() ? true : false;

public boolean parmStoreInPrintArchive(boolean _storeInPrintArchive = storeInPrintArchive)
(...)
    storeInPrintArchive = Global::isSystemAdministrator() ? _storeInPrintArchive : false;

Then the saved printer settings had to be changed in the SysLastValue table for all user accounts, without changing any other printer settings. They are stored differently based on whether they extend FormLetter classes, or not.

Please make sure you run and validate this in your Test environment first!

static void WIK_setPrintJobSettings(Args _args)
{
    SysLastValue                    sysLastValue;
    container                       lastValues;
    container                       con;
    ContainerClass                  containerClass;
    SRSPrintDestinationSettings     printSettings;
    PrintJobSettings                printJobSettings;
    Object                          object;
    Object                          contractObject;
 
    while select sysLastValue
            // Specific formletter reports
        where (sysLastValue.elementName == classStr(SalesFormLetter_Invoice)
            || sysLastValue.elementName == classStr(SalesFormLetter_PickingList)
            || sysLastValue.elementName == classStr(SalesFormLetter_Confirm)
            || sysLastValue.elementName == classStr(SalesFormLetter_FreeText)
            || sysLastValue.elementName == classStr(SalesFormLetter_PackingSlip)
            || sysLastValue.elementName == classStr(PurchFormLetter_Confirmation)
            || sysLastValue.elementName == classStr(PurchFormLetter_ConfirmationRequest)
            || sysLastValue.elementName == classStr(PurchFormLetter_Invoice)
            || sysLastValue.elementName == classStr(PurchFormLetter_PackingSlip)
            || sysLastValue.elementName == classStr(PurchFormLetter_PurchOrder)
            || sysLastValue.elementName == classStr(PurchFormLetter_ReceiptsList)
            // Specific SSRS reports via controller class
            || sysLastValue.elementName == classStr(AssetBalanceReportColumnsController)
            || sysLastValue.elementName == classStr(AssetReportsController)
            || sysLastValue.elementName == classStr(BankPaymAdviceChequeController)
            || sysLastValue.elementName == classStr(BankReconciliationController)
            || sysLastValue.elementName == classStr(BankReconciliationSummaryController)
            || sysLastValue.elementName == classStr(CustAccountStatementExtController)
            || sysLastValue.elementName == classStr(CustAgingReportController)
            || sysLastValue.elementName == classStr(CustInvoiceController)
            || sysLastValue.elementName == classStr(CustLedgerTransController)
            || sysLastValue.elementName == classStr(CustVendTransOpenPerDateController)
            || sysLastValue.elementName == classStr(HcmAccommodationListController)
            || sysLastValue.elementName == classStr(HcmCourseAttendeeStatListController)
            || sysLastValue.elementName == classStr(HcmCourseConfirmationController)
            || sysLastValue.elementName == classStr(HcmWorkersHiredInPeriodController)
            || sysLastValue.elementName == classStr(HRMEmployeeLeaveController)
            || sysLastValue.elementName == classStr(InventABCController)
            || sysLastValue.elementName == classStr(InventAgingController)
            || sysLastValue.elementName == classStr(InventCostReportController)
            || sysLastValue.elementName == classStr(InventJournalTransController)
            || sysLastValue.elementName == classStr(InventJournalTransTransferController)
            || sysLastValue.elementName == classStr(InventLedgerConflictController)
            || sysLastValue.elementName == classStr(InventPriceOverviewController)
            || sysLastValue.elementName == classStr(InventSettlementAdjustmentController)
            || sysLastValue.elementName == classStr(InventTableOverviewController)
            || sysLastValue.elementName == classStr(InventTransferOrdOverviewController)
            || sysLastValue.elementName == classStr(InventTransferShipReceiveController)
            || sysLastValue.elementName == classStr(InventValueReportController)
            || sysLastValue.elementName == classStr(JADRouteCashReconciliationController)
            || sysLastValue.elementName == classStr(LedgerAccountSchedController)
            || sysLastValue.elementName == classStr(LedgerJournalAcctMovementController)
            || sysLastValue.elementName == classStr(LedgerJournalCashController)
            || sysLastValue.elementName == classStr(LedgerJournalController)
            || sysLastValue.elementName == classStr(LedgerOpenTransactionsController)
            || sysLastValue.elementName == classStr(LedgerTotalAndBalanceListController)
            || sysLastValue.elementName == classStr(LedgerTransListAccountController)
            || sysLastValue.elementName == classStr(LedgerTransPerJournalController)
            || sysLastValue.elementName == classStr(LedgerTransStatementController)
            || sysLastValue.elementName == classStr(LedgerTrialBalanceController)
            || sysLastValue.elementName == classStr(PurchConfirmationRequestController)
            || sysLastValue.elementName == classStr(PurchFinalizeServiceController)
            || sysLastValue.elementName == classStr(PurchPackingSlipController)
            || sysLastValue.elementName == classStr(PurchPurchaseOrderController)
            || sysLastValue.elementName == classStr(PurchStatisticsController)
            || sysLastValue.elementName == classStr(SalesCODLabelController)
            || sysLastValue.elementName == classStr(SalesConfirmController)
            || sysLastValue.elementName == classStr(SalesHeadingController)
            || sysLastValue.elementName == classStr(SalesInvoiceController)
            || sysLastValue.elementName == classStr(SalesPackingSlipController)
            || sysLastValue.elementName == classStr(smmReportsController)
            || sysLastValue.elementName == classStr(SysInfoLogController)
            //|| sysLastValue.elementName == classStr(SysOperationQueryController)
            //|| sysLastValue.elementName == classStr(SysOperationServiceController)
            || sysLastValue.elementName == classStr(SysUsersOnlineController)
            || sysLastValue.elementName == classStr(TaxListController)
            || sysLastValue.elementName == classStr(TaxTransController)
            || sysLastValue.elementName == classStr(TaxTransDetailController)
            || sysLastValue.elementName == classStr(TaxWithholdController)
            || sysLastValue.elementName == classStr(VendAccountStatementIntController)
            || sysLastValue.elementName == classStr(VendAgingReportController)
            || sysLastValue.elementName == classStr(VendBalanceListController)
            || sysLastValue.elementName == classStr(VendInvoiceController)
            || sysLastValue.elementName == classStr(VendInvoiceDocumentController)
            || sysLastValue.elementName == classStr(VendRprtApproveCollectionController)
            || sysLastValue.elementName == classStr(WmsPickingList_OrderPickController)
            // Handle all SSRS-based reports as a generic type
            || sysLastValue.elementName == classStr(SrsReportRunController)
            ) && (
                sysLastValue.recordType == UtilElementType::Class
            )
    {
        if (xUserInfo::find(false, sysLastValue.userId).enable == NoYes::No)
        {
            continue;
        }
 
        lastValues = xSysLastValue::getValue(sysLastValue.company, sysLastValue.userId, sysLastValue.recordType, sysLastValue.elementName, sysLastValue.designName);
        printSettings = null;
        object = null;
 
        if (lastValues)
        {
            switch (sysLastValue.elementName)
            {
                // Formletter classes
                case classStr(PurchFormLetter_Confirmation) :
                    object = PurchFormLetter_Confirmation::newConfirmation();
                    break;
 
                case classStr(PurchFormLetter_ConfirmationRequest) :
                    object = PurchFormLetter_ConfirmationRequest::newConfirmationRequest();
                    break;
 
                case classStr(PurchFormLetter_Invoice) :
                case classStr(PurchFormLetter_PackingSlip) :
                case classStr(PurchFormLetter_PackingSlip) :
                case classStr(PurchFormLetter_PurchOrder) :
                case classStr(PurchFormLetter_ReceiptsList) :
                case classStr(SalesFormLetter_FreeText) :
                case classStr(SalesFormLetter_Confirm) :
                case classStr(SalesFormLetter_Invoice) :
                case classStr(SalesFormLetter_PickingList) :
                case classStr(SalesFormLetter_PackingSlip) :
                    object = classFactory.createClass(className2Id(sysLastValue.elementName));
                    break;
 
                // Controller classes
                case classStr(AssetBalanceReportColumnsController) :
                case classStr(AssetReportsController) :
                case classStr(BankPaymAdviceChequeController) :
                case classStr(BankReconciliationController) :
                case classStr(BankReconciliationSummaryController) :
                case classStr(CustAccountStatementExtController) :
                case classStr(CustAgingReportController) :
                case classStr(CustInvoiceController) :
                case classStr(CustLedgerTransController) :
                case classStr(CustVendTransOpenPerDateController) :
                case classStr(HcmAccommodationListController) :
                case classStr(HcmCourseAttendeeStatListController) :
                case classStr(HcmCourseConfirmationController) :
                case classStr(HcmWorkersHiredInPeriodController) :
                case classStr(HRMEmployeeLeaveController) :
                case classStr(InventABCController) :
                case classStr(InventAgingController) :
                case classStr(InventCostReportController) :
                case classStr(InventJournalTransController) :
                case classStr(InventJournalTransTransferController) :
                case classStr(InventLedgerConflictController) :
                case classStr(InventPriceOverviewController) :
                case classStr(InventSettlementAdjustmentController) :
                case classStr(InventTableOverviewController) :
                case classStr(InventTransferOrdOverviewController) :
                case classStr(InventTransferShipReceiveController) :
                case classStr(InventValueReportController) :
                case classStr(JADRouteCashReconciliationController) :
                case classStr(LedgerAccountSchedController) :
                case classStr(LedgerJournalAcctMovementController) :
                case classStr(LedgerJournalCashController) :
                case classStr(LedgerJournalController) :
                case classStr(LedgerOpenTransactionsController) :
                case classStr(LedgerTotalAndBalanceListController) :
                case classStr(LedgerTransListAccountController) :
                case classStr(LedgerTransPerJournalController) :
                case classStr(LedgerTransStatementController) :
                case classStr(LedgerTrialBalanceController) :
                case classStr(PurchConfirmationRequestController) :
                case classStr(PurchFinalizeServiceController) :
                case classStr(PurchPackingSlipController) :
                case classStr(PurchPurchaseOrderController) :
                case classStr(PurchStatisticsController) :
                case classStr(SalesCODLabelController) :
                case classStr(SalesConfirmController) :
                case classStr(SalesHeadingController) :
                case classStr(SalesInvoiceController) :
                case classStr(SalesPackingSlipController) :
                case classStr(smmReportsController) :
                case classStr(SysInfoLogController) :
                //case classStr(SysOperationQueryController) :
                //case classStr(SysOperationServiceController) :
                case classStr(SysUsersOnlineController) :
                case classStr(TaxListController) :
                case classStr(TaxTransController) :
                case classStr(TaxTransDetailController) :
                case classStr(TaxWithholdController) :
                case classStr(VendAccountStatementIntController) :
                case classStr(VendAgingReportController) :
                case classStr(VendBalanceListController) :
                case classStr(VendInvoiceController) :
                case classStr(VendInvoiceDocumentController) :
                case classStr(VendRprtApproveCollectionController) :
                case classStr(WmsPickingList_OrderPickController) :
                    object = classFactory.createClass(className2Id(sysLastValue.elementName));
                    // it might be packedSuper from SrsReportRunController, in which case we can only unpack value directly
                    if (conLen(lastValues) == 3)
                    {
                        lastValues = conPeek(lastValues, 3);
                    }
                    break;
 
                case classStr(SrsReportRunController) :
                    object = new SrsReportRunController();
                    break;
            }
 
            if (object == null)
            {
                continue;
            }
 
            try
            {
                object.unpack(lastValues);
            }
            catch
            {
                error(strFmt('%1 :: %2 :: %3 :: %4',
                    sysLastValue.elementName,
                    sysLastValue.designName,
                    sysLastValue.userId,
                    sysLastValue.company));
                continue;
            }
 
            if (SysDictClass::isEqualOrSuperclass(classIdGet(object), className2Id(classStr(FormLetterServiceController))))
            {
                printSettings = new SRSPrintDestinationSettings(object.printerSettingsFormletter());
            }
 
            if (SysDictClass::isEqualOrSuperclass(classIdGet(object), className2Id(classStr(SrsReportRunController))))
            {
                contractObject = object.parmReportContract();
                printSettings = contractObject.parmPrintSettings();
            }
 
            if (printSettings && printSettings.parmPrintToArchive() == true)
            {
                info(strFmt('Updating %1 :: %2 :: %3 :: %4 :: %5 :: %6 :: %7 ==> false',
                    sysLastValue.elementName,
                    sysLastValue.designName,
                    sysLastValue.userId,
                    sysLastValue.company,
                    printSettings.printMediumType(),
                    printSettings.printerName(),
                    printSettings.parmPrintToArchive()
                    ));
 
                printSettings.parmPrintToArchive(false);
 
                switch (sysLastValue.elementName)
                {
                    case classStr(PurchFormLetter_Confirmation) :
                    case classStr(PurchFormLetter_ConfirmationRequest) :
                    case classStr(PurchFormLetter_Invoice) :
                    case classStr(PurchFormLetter_PackingSlip) :
                    case classStr(PurchFormLetter_PackingSlip) :
                    case classStr(PurchFormLetter_PurchOrder) :
                    case classStr(PurchFormLetter_ReceiptsList) :
                    case classStr(SalesFormLetter_Confirm) :
                    case classStr(SalesFormLetter_Invoice) :
                    case classStr(SalesFormLetter_FreeText) :
                    case classStr(SalesFormLetter_PickingList) :
                    case classStr(SalesFormLetter_PackingSlip) :
                        object.updatePrinterSettingsFormLetter(printSettings.pack());
                        lastValues = object.pack();
                        break;
 
                    case classStr(AssetBalanceReportColumnsController) :
                    case classStr(AssetReportsController) :
                    case classStr(BankPaymAdviceChequeController) :
                    case classStr(BankReconciliationController) :
                    case classStr(BankReconciliationSummaryController) :
                    case classStr(CustAccountStatementExtController) :
                    case classStr(CustAgingReportController) :
                    case classStr(CustInvoiceController) :
                    case classStr(CustLedgerTransController) :
                    case classStr(CustVendTransOpenPerDateController) :
                    case classStr(HcmAccommodationListController) :
                    case classStr(HcmCourseAttendeeStatListController) :
                    case classStr(HcmCourseConfirmationController) :
                    case classStr(HcmWorkersHiredInPeriodController) :
                    case classStr(HRMEmployeeLeaveController) :
                    case classStr(InventABCController) :
                    case classStr(InventAgingController) :
                    case classStr(InventCostReportController) :
                    case classStr(InventJournalTransController) :
                    case classStr(InventJournalTransTransferController) :
                    case classStr(InventLedgerConflictController) :
                    case classStr(InventPriceOverviewController) :
                    case classStr(InventSettlementAdjustmentController) :
                    case classStr(InventTableOverviewController) :
                    case classStr(InventTransferOrdOverviewController) :
                    case classStr(InventTransferShipReceiveController) :
                    case classStr(InventValueReportController) :
                    case classStr(JADRouteCashReconciliationController) :
                    case classStr(LedgerAccountSchedController) :
                    case classStr(LedgerJournalAcctMovementController) :
                    case classStr(LedgerJournalCashController) :
                    case classStr(LedgerJournalController) :
                    case classStr(LedgerOpenTransactionsController) :
                    case classStr(LedgerTotalAndBalanceListController) :
                    case classStr(LedgerTransListAccountController) :
                    case classStr(LedgerTransPerJournalController) :
                    case classStr(LedgerTransStatementController) :
                    case classStr(LedgerTrialBalanceController) :
                    case classStr(PurchConfirmationRequestController) :
                    case classStr(PurchFinalizeServiceController) :
                    case classStr(PurchPackingSlipController) :
                    case classStr(PurchPurchaseOrderController) :
                    case classStr(PurchStatisticsController) :
                    case classStr(SalesCODLabelController) :
                    case classStr(SalesConfirmController) :
                    case classStr(SalesHeadingController) :
                    case classStr(SalesInvoiceController) :
                    case classStr(SalesPackingSlipController) :
                    case classStr(smmReportsController) :
                    case classStr(SysInfoLogController) :
                    //case classStr(SysOperationQueryController) :
                    //case classStr(SysOperationServiceController) :
                    case classStr(SysUsersOnlineController) :
                    case classStr(TaxListController) :
                    case classStr(TaxTransController) :
                    case classStr(TaxTransDetailController) :
                    case classStr(TaxWithholdController) :
                    case classStr(VendAccountStatementIntController) :
                    case classStr(VendAgingReportController) :
                    case classStr(VendBalanceListController) :
                    case classStr(VendInvoiceController) :
                    case classStr(VendInvoiceDocumentController) :
                    case classStr(VendRprtApproveCollectionController) :
                    case classStr(WmsPickingList_OrderPickController) :
                    case classStr(SrsReportRunController) :
                        lastValues = object.pack();
                        break;
                }
 
                xSysLastValue::putValue(lastValues, sysLastValue.company, sysLastValue.userId, sysLastValue.recordType, sysLastValue.elementName, sysLastValue.designName);
            }
        }
    }
 
    info('done');
}
    /*
    // View the settings on the printer setup form
    Args                            args;
    FormRun                         formRun;
    args = new Args()
    args.name(formStr(SRSPrintDestinationSettingsForm));
    args.caller(printSettings);
 
    formRun = classFactory::formRunClassOnClient(args);
    FormRun.init();
    formRun.run();
 
    if (!formRun.closed())
    {
        formRun.wait(true);
    }*/

By |2022-03-31T18:52:51+02:00March 31st, 2022|Categories: AX 2012|Tags: , , , |0 Comments
Go to Top