Replaces the value of the specified dimension attribute from source to target =>
LedgerDimensionDefaultFacade::serviceReplaceAttributeValue(<source dimension>, <target dimension>, DimensionAttribute::findByName("<Dimension name>").RecId);
Blog on Microsoft Dynamics AX/ D365
Replaces the value of the specified dimension attribute from source to target =>
LedgerDimensionDefaultFacade::serviceReplaceAttributeValue(<source dimension>, <target dimension>, DimensionAttribute::findByName("<Dimension name>").RecId);
/// <summary>
/// To check if user can get access to the Parameter form
/// </summary>
class SAN_ParamFormsAccessCtrl
{
protected void new()
{
super();
}
static SAN_ParamFormsAccessCtrl construct()
{
return new SAN_ParamFormsAccessCtrl();
}
public boolean validateUserRoleAccess()
{
#characters
UserId userId = curUserId();
container tmpValues;
SecurityRole securityRole;
SecurityUserRole securityUserRole;
int idx;
boolean isValidated = false;
Str secRoleList;
//Created new custom parameter to have which roles alone should have EDIT access, apart from this any roles would have only READ access even System admin
secRoleList = SAN_IntegrationParameters::find().ParametersAccessSecurityRole;
if(secRoleList)
{
tmpValues = str2con(secRoleList, #semicolon);
for(idx=1; idx<=conLen(tmpValues); idx++)
{
securityRole.clear();
select firstonly securityRole
where securityRole.Name == conPeek(tmpValues, idx);
if(securityRole.RecId && !isValidated)
{
securityUserRole.clear();
select firstonly RecId from securityUserRole
where securityUserRole.User == userId
&& securityUserRole.SecurityRole == securityRole.RecId;
if (securityUserRole.RecId)
{
isValidated = true;
}
}
}
}
return isValidated;
}
public void disableFormDataSources(FormRun _callingForm)
{
FormBuildDataSource frmBuildDS;
int i;
for(i = 1; i<= _callingForm.form().dataSourceCount(); i++)
{
frmBuildDS = _callingForm.form().dataSource(i);
frmBuildDS.allowEdit(false);
}
}
public boolean checkRoleCtrlAccess(FormRun _callingForm)
{
boolean userHasAccess = this.validateUserRoleAccess();
if(!userHasAccess)
{
this.disableFormDataSources(_callingForm);
}
return userHasAccess;
}
}
[ExtensionOf(formStr(LedgerParameters))]
final class LedgerParametersFrm_SAN_Extension
{
public void init()
{
next init();
SAN_ParamFormsAccessCtrl paramFormsAccessCtrl;
paramFormsAccessCtrl = SAN_ParamFormsAccessCtrl::construct();
if(!paramFormsAccessCtrl.checkRoleCtrlAccess(this))
{
this.san_lockControls();
}
}
void san_lockControls()
{
FormBuildControl formBuildControl;
int i;
for (i=1;i<=this.form().design().controlCount(); i++)
{
formBuildControl = this.form().design().controlNum(i);
this.control(formBuildControl.id()).enabled(false);
}
}
}
Requirement:
1. Override custom dimension value on posting sales invoice
2. Ledger posting type as "Cost of goods, invoiced" & "Sales revenue"
3. Reason: Requirement is to default for certain orders type, these account has to be changed.
4. Why can't we acheive in Customer or item level dimension. [Reason: Currently it was managed in item level, but requirement is to for same item if order type met certain criteria on posting, this need to defaulted with this dimension value for ledger reporting]
Class 1:
/// <summary>
/// The <c>LedgerVoucherTransObject</c> class represents a single transaction in an individual voucher. COC
/// </summary>
/// <remarks>
/// The transaction is stored in a temporary instance of a <see cref="T:LedgerTrans" /> record buffer.
/// The temporary transaction is inserted into the database during posting and made a permanent record.
/// </remarks>
[ExtensionOf(ClassStr(LedgerVoucherTransObject))]
final class LedgerVoucherTransObjectCls_SAN_Extension
{
/// <summary>
/// Initializes a new instance of the <c>LedgerVoucherTransObject</c> class by using a transaction COC POL
/// currency amount and a ledger posting reference for defaulting.
/// </summary>
/// <param name="_defaultLedgerPostingReference">
/// The ledger posting reference to use for defaulting.
/// </param>
/// <param name="_postingType">
/// The posting type of the general journal entry.
/// </param>
/// <param name="_ledgerDimensionId">
/// The dimension attribute value combination of the general journal entry.
/// </param>
/// <param name="_transactionCurrencyCode">
/// The currency code of the general journal entry.
/// </param>
/// <param name="_transactionCurrencyAmount">
/// The amount in the transaction currency.
/// </param>
/// <param name="_exchangeRateHelper">
/// The accounting currency amount and secondary currency amount exchange rates.
/// </param>
/// <returns>
/// A new instance of the <c>LedgerVoucherTransObject</c> class.
/// </returns>
/// <remarks>
/// The default ledger posting reference is used to set the transaction type and exchange rate date.
/// </remarks>
public static LedgerVoucherTransObject newTransactionAmountDefault(
LedgerVoucherObject _defaultLedgerPostingReference,
LedgerPostingType _postingType,
LedgerDimensionAccount _ledgerDimensionId,
CurrencyCode _transactionCurrencyCode,
Money _transactionCurrencyAmount,
CurrencyExchangeHelper _exchangeRateHelper)
{
LedgerVoucherTransObject polpostingTrans;
LedgerDimensionAccount ledgerDimensionAccount, tempLedgerDimenionAcc;
ledgerDimensionAccount = _ledgerDimensionId;
if(ledgerDimensionAccount && (_postingType == LedgerPostingType::SalesConsump
|| _postingType == LedgerPostingType::SalesRevenue))
{
SAN_SalesInvoiceDimensionMockContext contextCur = SAN_SalesInvoiceDimensionMockContext::current();
if(contextCur != null)
{
if(contextCur.isParameterActive && contextCur.isValidToOverride && contextCur.polproductLineToOverride)
{
tempLedgerDimenionAcc = _ledgerDimensionId;
ledgerDimensionAccount = SalesInvoiceJournalPost::SAN_buildDefaultAndLedgerDimension(tempLedgerDimenionAcc,contextCur.polproductLineToOverride);
}
}
}
polpostingTrans = next newTransactionAmountDefault(_defaultLedgerPostingReference, _postingType, ledgerDimensionAccount, _transactionCurrencyCode, _transactionCurrencyAmount, _exchangeRateHelper);
return polpostingTrans;
}
}
Class 2:
/// <summary>
/// To hold the value of SAN_SalesInvoiceDimensionMockContext through out the runtime.
/// </summary>
class SAN_SalesInvoiceDimensionMockContext implements System.IDisposable
{
public boolean isParameterActive;
public boolean isValidToOverride;
public SalesTable polsalesTable;
public str polsmmSegmentId;
public str polproductLineToOverride;
static SAN_SalesInvoiceDimensionMockContext instance;
protected void new ()
{
if (instance)
{
throw Error('Nesting of SAN_SalesInvoiceDimensionMockContext is not supported');
}
instance = this;
}
/// <summary>
/// To Create the instance wherever the method is called during the run time.
/// </summary>
/// <returns>current new instance</returns>
Public static SAN_SalesInvoiceDimensionMockContext createInstance()
{
SAN_SalesInvoiceDimensionMockContext newInstance = new SAN_SalesInvoiceDimensionMockContext();
instance = newInstance;
return instance;
}
/// <summary>
/// Dispose the instance once the Aging snapshot batch is run.
/// </summary>
public void dispose()
{
instance = null;
}
/// <summary>
/// To get the instance wherever the method is called during the run time.
/// </summary>
/// <returns>Current instance</returns>
public static SAN_SalesInvoiceDimensionMockContext current()
{
return instance;
}
}
Class 3:
/// <summary>
/// Extension of class SalesInvoiceJournalPost
/// </summary>
[Extensionof (classstr(SalesInvoiceJournalPost))]
Public Final class SalesInvoiceJournalPostCls_SAN_Extension
{
/// <summary>
/// Posts to inventory.
/// </summary>
protected void postInventory()
{
using(SAN_SalesInvoiceDimensionMockContext contextSet = SAN_SalesInvoiceDimensionMockContext::createInstance())
{
if(SalesParameters::find().SAN_OverrideDimensionValue)
{
contextSet.isParameterActive = true;
SalesTable salesTable;
salesTable = salesLine.SalesTable();
if(salesTable && salesLine)
{
contextSet.polsalesTable = salesTable;
contextSet.isValidToOverride = true;
contextSet.polproductLineToOverride = '09';
}
}
next postInventory();
}
}
/// <summary>
/// Posts one journal line.
/// </summary>
protected void postLine()
{
using(SAN_SalesInvoiceDimensionMockContext contextSet = SAN_SalesInvoiceDimensionMockContext::createInstance())
{
if(SalesParameters::find().SAN_OverrideDimensionValue)
{
contextSet.isParameterActive = true;
SalesTable salesTable;
salesTable = salesParmLine.SalesLine().SalesTable();
if(salesTable && salesParmLine )
{
contextSet.polsalesTable = salesTable;
contextSet.isValidToOverride = true;
contextSet.polproductLineToOverride = '09';
}
}
next postLine();
}
}
/// <summary>
/// buildDefaultAndLedgerDimension for govt and non-govt segment
/// </summary>
/// <param name = "_ledger">LedgerDimensionAccount</param>
/// <param name = "_overridePL">overridePL</param>
/// <returns>ledgerDimensionAccount</returns>
Static ledgerDimensionAccount SAN_buildDefaultAndLedgerDimension(LedgerDimensionAccount _ledger, str _overridePL)
{
DimensionAttributeValueSetStorage dimensionAttributeValueSetStorage;
DimensionAttributeValue dimensionAttributeValue;
DimensionDefault dimensionDefault;
LedgerDimensionAccount ledgerDimensionAccount;
DimensionAttributeLevelValueAllView dimAttrValueallview;
dimensionAttributeValueSetStorage = new DimensionAttributeValueSetStorage();
RefRecId mainAccountRecId, LedgerDimensionAcc;
mainAccountRecId = LedgerDimensionFacade::getMainAccountRecIdFromLedgerDimension(_ledger);
LedgerDimensionAcc = LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountRecId(mainAccountRecId);
while select dimAttrValueallview
where dimAttrValueallview.ValueCombinationRecId == _ledger
{
if(DimensionAttribute::find(dimAttrValueallview.DimensionAttribute).Name != "MainAccount")
{
if(DimensionAttribute::find(dimAttrValueallview.DimensionAttribute).Name == "ProductLine")
{
dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(
DimensionAttribute::find(dimAttrValueallview.DimensionAttribute),
_overridePL, false, true);
}
else
{
dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(
DimensionAttribute::find(dimAttrValueallview.DimensionAttribute),
dimAttrValueallview.DisplayValue, false, true);
}
dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);
dimensionDefault = dimensionAttributeValueSetStorage.save();
}
}
ledgerDimensionAccount = LedgerDimensionFacade::serviceCreateLedgerDimension(LedgerDimensionAcc,dimensionDefault);
return ledgerDimensionAccount;
}
}
Details:
In case, extensible support for class instance to reinstance with different class to execute on Extension layer.
public static SANSyncInventTable construct(Common _record, Common _record_orig, boolean _isUpdate)
{
SANSyncInventTable syncRecordInventTable;
syncRecordInventTable = new SANSyncInventTable(_record, _record_orig, _isUpdate);
return syncRecordInventTable;
}
[PostHandlerFor(classStr(SANSyncInventTable), staticMethodStr(SANSyncInventTable, construct))]
public static void SANSyncInventTable_Post_construct(XppPrePostArgs args)
{
args.setReturnValue(SAN_SANSyncInventTable::construct(args.getArg(identifierStr(_record))
,args.getArg(identifierStr(_record_orig))
,args.getArg(identifierStr(_isUpdate))));
}
Base class for extract
/// <summary>
/// Class for extract data
/// </summary>
class SAN_SalesReportDailyService
{
Filename filename;
int recordCnt;
/// <summary>
/// Import warranty claims
/// </summary>
/// <param name = "_contract">Contract of type <b>SAN_SalesReportDailyContract</b></param>
[SysEntryPointAttribute(false)]
public void processImport(SAN_SalesReportDailyContract _contract)
{
contract = _contract;
try
{
this.exportSalesInvoiceData();
if (recordCnt)
{
info(strFmt("@SANaris:FileExportInfo", filename));
}
}
catch
{
error("@SYP4861341");
}
}
public void exportSalesInvoiceData()
{
#DMF
SharedServiceUnitFileID fileId;
SAN_DataEntityExporter exporter = new SAN_DataEntityExporter();
DMFDefinitionGroupName definitionGroupName = SalesParameters::find().SAN_SalesDailyReportDefinitionGroup; //TODO create new parameter field to get DMF execution
DMFDefinitionGroupEntity dmfDefinitionGroupEntity = SAN_SalesReportDailyService::findDMFDefinitionGroupEntity(definitionGroupName);
if (dmfDefinitionGroupEntity)
{
this.updateQueryData(dmfDefinitionGroupEntity);
this.setFileName();
exporter.parmFilename(filename);
exporter.exportDataEntity(definitionGroupName, dmfDefinitionGroupEntity.Entity);
}
else
{
throw error(strFmt("@SANaris:DataEntityMapError", definitionGroupName));
}
}
public str setFileName()
{
#File
str priFix;
priFix = 'SalesReportSAN_';
filename = priFix +
date2str(systemDateGet(),123,DateDay::Digits2,DateSeparator::None,DateMonth::Digits2,DateSeparator::None,DateYear::Digits4) + #xlsx;
return filename;
}
private void updateQueryData(DmfDefinitionGroupEntity _dmfDefinitionGroupEntity)
{
container queryData = _dmfDefinitionGroupEntity.QueryData;
if (queryData == connull())
{
queryData = DMFUtil::getDefaultQueryForEntityV3(_dmfDefinitionGroupEntity.Entity, _dmfDefinitionGroupEntity.DefinitionGroup);
}
QueryRun queryRun = new QueryRun(queryData);
Query query = queryRun.query();
QueryBuildDataSource qbds = query.dataSourceTable(tableNum(SAN_SalesReportDailyEntity));
SysQuery::findOrCreateRange(qbds, fieldNum(SAN_SalesReportDailyEntity, InvoiceDate)).value(queryRange(fromDate, toDate));
if (warehouseId)
{
SysQuery::findOrCreateRange(qbds, fieldNum(SAN_SalesReportDailyEntity, Warehouse)).value(queryValue(warehouseId));
}
queryRun = new QueryRun(query);
queryData = queryRun.pack();
recordCnt = SysQuery::countTotal(queryRun);
if (recordCnt < 1)
{
throw error("@SYS4205");
}
ttsbegin;
_dmfDefinitionGroupEntity.reread();
_dmfDefinitionGroupEntity.selectForUpdate(true);
_dmfDefinitionGroupEntity.QueryData = queryData;
_dmfDefinitionGroupEntity.update();
ttscommit;
}
/// <summary>
/// Finds the <c>DMFDefinitionGroupEntity</c> record in the given definition group for the Line active view record entity.
/// </summary>
/// <param name = "_definitionGroupName">The name if the definition group</param>
/// <returns>the <c>DMFDefinitionGroupEntity</c> record if found, otherwise null</returns>
public static DMFDefinitionGroupEntity findDMFDefinitionGroupEntity(DMFDefinitionGroupName _definitionGroupName)
{
DMFDefinitionGroupEntity definitionGroupEntity;
DMFEntity dmfEntity;
str targetEntityName;
targetEntityName = 'SAN_SalesReport';
select firstonly RecId, Entity, DefinitionGroup from definitionGroupEntity
exists join dmfEntity
where definitionGroupEntity.DefinitionGroup == _definitionGroupName
&& dmfEntity.EntityName == definitionGroupEntity.Entity
&& dmfEntity.TargetEntity == targetEntityName;
return definitionGroupEntity;
}
}
Helper class:
/// <summary>
/// Data entity data export helper class.
/// </summary>
public class SAN_DataEntityExporter
{
Filename filename;
/// <summary>
/// set file name
/// </summary>
/// <param name = "_filename">file name to export data</param>
/// <returns>file name</returns>
public Filename parmFilename(Filename _filename = filename)
{
filename = _filename;
return filename;
}
/// <summary>
/// Export an entity to file
/// </summary>
/// <param name = "_definitionGroupName">Definition group to reuse</param>
/// <param name = "_entityName">Entity label</param>
public void exportDataEntity(DMFDefinitionGroupName _definitionGroupName, DMFEntityName _entityName)
{
#DMF
SharedServiceUnitFileID fileId;
try
{
DMFEntityExporter exporter = new DMFEntityExporter();
fileId = exporter.exportToFile(_entityName,
_definitionGroupName,
'',
BudgetPlanningConstants::Excel,
#FieldGroupName_AllFields,
conNull(),
curExt()
);
if (fileId != '')
{
this.sendFileToDestination(fileId);
}
else
{
// DMF execution failed and details were written to the execution log.
throw error("@CashManagement:DMFExportCallFailedToExecutionLog");
}
}
catch
{
error("@SYP4861341");
}
}
/// <summary>
/// Creates a stream, puts it into temp storage (like SendFileToTempStore), and redirects the user to that file to cause it to be downloaded by the user's browser.
/// </summary>
/// <param name = "_fileId">The file name of the stream.</param>
public client void sendFileToDestination(SharedServiceUnitFileID _fileId)
{
str downloadUrl = DMFDataPopulation::getAzureBlobReadUrl(str2Guid(_fileId));
System.IO.Stream stream = File::UseFileFromURL(downloadUrl);
File::SendFileToUser(stream, filename);
}
}
VendTable localVendTable = VendTable::find(<VendAccount>, false);
VendCustTransportPointCalculateInput input = VendCustTransportPointCalculateInput::construct();
input.ShippingAddress = localVendTable.postalAddress();
input.ReceivingAddress = <Current logictics delivery address>;
input.DeliveryMode = <Current mode of delivery>;
input.PurchaseCalendarId = localVendTable.PurchCalendarId;
input.VendorShipCalendarId = <Current Ship calendar Id (from PO line)>;
input.CalculateDirection = <Forward or bacward>;
input.LeadTime = _leadtime;
input.UseCalendarDays = _useCalendarDays;
input.RequestedShipDateInPast = PurchParameters::find().RequestedShipDateInPast;
input.VendorShipCalendarId = '';
input.ConfirmedShipDate = _confirmedShipDate;
input.ConfirmedReceiptDate = _confirmedReceiptDate;
VendCustTransportPointCalculateOutput output = VendCustTransportPointLine::calculateConfirmedShipAndReceiptDate(input);
Open command prompt:
//Enable maintenance mode
cd K:\AosService\PackagesLocalDirectory\Bin\
Microsoft.Dynamics.AX.Deployment.Setup.exe --metadatadir K:\AosService\PackagesLocalDirectory --bindir K:\AosService\PackagesLocalDirectory\Bin --sqlserver . --sqldatabase axdb --sqluser axdbadmin --sqlpwd "password from LCS" --setupmode maintenancemode --isinmaintenancemode true
//Restart IIS
iisreset
//Import new license file comman
Microsoft.Dynamics.AX.Deployment.Setup.exe --setupmode importlicensefile --metadatadir K:\AOSService\PackagesLocalDirectory --bindir K:\AOSService\PackagesLocalDirectory --sqlserver . --sqldatabase AxDB --sqluser axdbadmin --sqlpwd "password from LCS" --licensefilename C:\Temp\<"license file name">.txt
//Disbale maintenance mode
Microsoft.Dynamics.AX.Deployment.Setup.exe --metadatadir K:\AosService\PackagesLocalDirectory --bindir K:\AosService\PackagesLocalDirectory\Bin --sqlserver . --sqldatabase axdb --sqluser axdbadmin --sqlpwd "password from LCS" --setupmode maintenancemode --isinmaintenancemode false
//Restart IIS
iisreset
Build ISV model and custom model (If any).
DB Sync
[SubscribesTo(classstr(SysMenuNavigationObjectFactory), staticdelegatestr(SysMenuNavigationObjectFactory, checkAddSubMenuDelegate))]
public static void menuItemVisibilityHandler(SysDictMenu _rootMenu, SysDictMenu _subMenu, SysBoxedBoolean _subMenuVisibility)
{
if (_subMenu.isMenuItem())
{
var metadataElement = _subMenu.GetMenuItemMetaElement();
if (metadataElement != null)
{
SAN_HelperCls::santst_setVisibilityMenuItems(metadataElement.Name, _subMenuVisibility);
}
}
}
public class SAN_HelperCls
{
private static void santst_setVisibilityMenuItems(str _menuItemName, SysBoxedBoolean _subMenuVisibility)
{
const str menuItemsPrefix = "SAN_TEST";
if (strScan(_menuItemName, menuItemsPrefix , 0, strLen(_menuItemName)))
{
_subMenuVisibility.value = SAN_HelperCls::isEnabled();
}
}
public static boolean isEnabled()
{
return isConfigurationkeyEnabled(configurationkeynum(Trade)) &&
FeatureStateProvider::isFeatureEnabled(SAN_DevFeature::instance());
}
}
Parameter control (Yes/No)
or
Feature class:
using System.ComponentModel.Composition;
using Microsoft.Dynamics.ApplicationPlatform.FeatureExposure;
/// <summary>
/// The <c>SAN_DevFeature</c> class defines the enable advanced order hold feature.
/// </summary>
[ExportAttribute(identifierstr(Microsoft.Dynamics.ApplicationPlatform.FeatureExposure.IFeatureMetadata))]
[Microsoft.Dynamics.BusinessPlatform.SharedTypes.InternalUseOnlyAttribute]
public final class SAN_DevFeature implements IFeatureMetadata, IFeatureLifecycle
{
private static SAN_DevFeature instance;
private void new()
{
}
[Hookable(false)]
public WebSiteURL learnMoreUrl()
{
return 'https://learn.microsoft.com/en-us/';
}
private static void TypeNew()
{
instance = new SAN_DevFeature();
}
/// <summary>
/// Obtains the singleton object instance.
/// </summary>
/// <returns>The <c>SAN_DevFeature</c> instance.</returns>
[Hookable(false)]
public static SAN_DevFeature instance()
{
return SAN_DevFeature::instance;
}
[Hookable(false)]
public FeatureLabelId label()
{
return literalStr("SAN DEV tool features");
}
[Hookable(false)]
public int module()
{
return FeatureModuleV0::SalesAndMarketing;
}
[Hookable(false)]
public FeatureLabelId summary()
{
return literalStr("SAN DEV tool features");
}
[Hookable(false)]
public boolean isEnabledByDefault()
{
return false;
}
[Hookable(false)]
public boolean canDisable()
{
return true;
}
[Hookable(false)]
public FeatureLifecycleStage FeatureStage()
{
return FeatureLifecycleStage::Released;
}
}
Replaces the value of the specified dimension attribute from source to target => LedgerDimensionDefaultFacade::serviceReplaceAttributeVal...