Thursday, July 18, 2024

Label D365FO cloud environments using extensions

In D365FO Developers, Testers, key users, functional consultants will work on multiple environments. It is very important to them to differentiate and which environments they are working on.

Using below Extensions, you can enter the URL of specific cloud environments, for example to separate PROD environments from TEST environments like in the attached image.

Chrome: https://chromewebstore.google.com/detail/environment-marker/ahjhdebcnlgmojdmjnhikhakkghcchkk

Edge: https://microsoftedge.microsoft.com/addons/detail/environment-marker/iggchogopggbmjgplniemfaghmdkhlip






Wednesday, July 3, 2024

Copy args value to variable

CopyArgs() method will copy args value from one args to another variable 


 private void run(Args _args)

{

    Args salesArgs  = Args::copyArgs(_args, new Args());

    Args projArgs   = Args::copyArgs(salesArgs, new Args());

    Map  dataSourceValidRecs = new Map(extendedTypeId2Type(extendedTypeNum(RecId)), Types::Record);

    

    if (FormDataUtil::getFormDataSource(salesArgs.record()))

    {

        FormDataSource formDataSource_ds = FormDataUtil::getFormDataSource(salesArgs.record());


        for (Common common = formDataSource_ds.getFirst(true) ? formDataSource_ds.getFirst(true) : formDataSource_ds.cursor();

            common;

            common = formDataSource_ds.getNext())

        {

            WHSPostPackingSlipAction postPackingSlipAction = this.constructPostPackingSlipAction(common, salesArgs);


            if (postPackingSlipAction.validateCanPackingSlipBePosted())

            {

                this.determinePackingSlipTypes(postPackingSlipAction.whsPackingSlipType());


                dataSourceValidRecs.insert(common.RecId, common);

            }

        }

    }

}

Monday, June 24, 2024

Check Security Privilege is assigned or not for that User iusing X++

 

Below method will help to check security Privileges assigned for that user.

public static boolean hasUserSecurityPrivilege(SecurityPrivilegeName _securityPrivilege, UserId _userId = curUserId())

{

  SecurityRolePrivilegeExplodedGraph securityRolePrivilegeExplodedGraph;

  SecurityUserRole securityUserRole;

  SecurityPrivilege securityPrivilege;

  select firstonly securityPrivilege

    where securityPrivilege.Identifier == _securityPrivilege

  exists join securityRolePrivilegeExplodedGraph

    where securityRolePrivilegeExplodedGraph.SecurityPrivilege == securityPrivilege.RecId

  exists join securityUserRole

    where securityUserRole.SecurityRole == securityRolePrivilegeExplodedGraph.SecurityRole

       && securityUserRole.User == _userId

       && securityUserRole.AssignmentStatus == RoleAssignmentStatus::Enabled

       && (securityUserRole.ValidFrom < DateTimeUtil::utcNow() || securityUserRole.ValidFrom == utcDateTimeNull())

       && (securityUserRole.ValidTo > DateTimeUtil::utcNow() || securityUserRole.ValidTo == utcDateTimeNull());

  return securityPrivilege.RecId != 0;

}


Orelse you can use below method also with dummy control in form

Logic to implement user has specific privilege through code:

Created one dummy form control with visibility false and need permission manual and created privilege using below logic we can verify the access.

SecurityRights securityRights = SecurityRights::construct();

        maxAccess = securityRights.formControlAccessRight(formStr(SalesTable), formControlStr(SalesTable, QTQSalesStatus));

=============

verify user has specific role assigned or not.

if (xUserInfo::checkUserRole(roleStr(InventMaterialsManager))

            || xUserInfo::checkUserRole(roleStr(InventQualityControlManager)) 

            || xUserInfo::checkUserRole(roleStr(WMSWarehouseManager)) 

            || Global::isSystemAdministrator()) 


Wednesday, October 18, 2023

Shrink log D365FO log file

 Shrink log D365FO log file 

Select DB -> Right click -> Tasks -> Shrink -> Files

select file type as "Log".

Note down the value of file name.




Right click the Database -> New query -> Execute below query.

       ALTER DATABASE AXDB

        SET RECOVERY SIMPLE

        GO

      -- DBCC SHRINKFILE (filename , 1)

        DBCC SHRINKFILE ('AXDB-17Mar_log', 1)

        GO

        ALTER DATABASE AXDB

        SET RECOVERY FULL


** replace your file name

Tuesday, September 26, 2023

Restore/move D365 database

 Restore data base from same tenant (company)


1. Open as Administrator PowerShell
Install if not installed d365fo tools in power shell
- Install-Module -Name d365fo.tools
- Invoke-D365InstallSqlPackage

2. Restore database
if BACPAC file
in powershell
- Import-D365Bacpac -ImportModeTier1 -BacpacFile "C:\temp\CLI TEST1backup.bacpac" -NewDatabaseName "AxDB_20211104" -ShowOriginalProgress

if BAK file go to Microsoft SQL Server Management Studio
Databases node / Right click / Restore Database...
General / Source / Device / click ... button
Click add button select BAK file and click OK and then again OK
Destination / Database change to AxDB_20211104 (date)
Files page (at right)
make sure that Restore As columnt has different files from Original File Name, change from AXDB_FromProdRep1 to AXDB_20211104_FromProdRep1, both lines
Click OK to start process

3. Stop environment
powershell
- Stop-D365Environment
Open IIS manager / Application Pools / AOSService / stop
Close all visual studio. Re open visual studio. Check tray for IISExpress. Right click in tray and Exit

4. Switch databases
In powershell
- Switch-D365ActiveDatabase -SourceDatabaseName "AxDB_20211104" -DestinationSuffix "_old"
Where _old is postfix for currently used AxDB, it will be called AxDB_old

5. Start environment
In powershell
- Start-D365Environment
Open IIS manager / Application Pools / AOSService / start
Close all visual studio. Re open visual studio.
Dynamics 365 / Synchronize database...

For tier2 environments like UAT it is possible to do it from LCS:
Open full environment details
Maintain / Move database
Export database - fill create BACPAC file and upload it to LCS
Import database - will import database from BACPAC file from LCS

To take backups in Microsoft SQL Server Management Studio
Righr click on data base Tasks / Back Up...
Back up type : Full
Destination / Back up to: Disk
Files - change the file name of back up for example AxDB_20211103.bak
Backup Options: Compression can be enabled - Set backup compression: Compress backup. LCS has limit of 14 Gb to upload. Therefore might be usefull.
Also shrink DB might be used: https://daxonline.org/1734-axdb-shrink.html

D365FO documentation:
https://github.com/d365collaborative/d365fo.tools/tree/development/docs

BAK/BACKBAK difference:
https://www.sqlservercentral.com/forums/topic/diff-between-bak-and-backpac-files

Monday, August 28, 2023

Synchornize the D365FO database using command prompt

 K:\AosService\PackagesLocalDirectory\Bin\SyncEngine.exe -syncmode=fullall -metadatabinaries=K:\AosService\PackagesLocalDirectory -connect="Data Source=localhost;Initial Catalog=AxDB;Integrated Security=True;Enlist=True;Application Name=SyncEngine" -fallbacktonative=False -verbosity=Diagnostic

Monday, April 3, 2023

From date and To date range in Dynamic Query

  private Query buidDynamicsQuery(Query _query)

    {

        this.retrieveQueryFilters(_query);

        QueryBuildDataSource qbdsPriceDiscAdmTrans = _query.dataSourceTable(tableNum(PriceDiscAdmTrans));

        qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, AccountCode)).value(queryValue(PriceDiscProductCodeType::All));

        qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, relation)).value(queryValue(PriceType::PriceSales));


        if (currencyCode)

        {

            qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, Currency)).value(queryValue(currencyCode));

        }


        if (toDate)

        {

            qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, FromDate)).value(queryRange(null, toDate));

        }


        if (fromDate)

        {

            qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, ToDate)).value(queryRange(fromDate, null));

            qbdsPriceDiscAdmTrans.addRange(fieldNum(PriceDiscAdmTrans, ToDate)).value(queryValue(dateNull()));

        }


        return _query;

    }