2013年6月16日星期日

How to Create custom Alert for AX with Go to Source support [AX2012]


To create an Alert in AX, you can use the class EventNotification and its derivates

I got some problems using these standard classes, particularly in making the button "Go to source" work.

To avoid those problem, here is some code that may help:


 public static void CreateAlert(str message,  
               str subject,  
               UserId userId = curUserId(),  
               NoYes showPopup = NoYes::Yes,  
               NoYes sendEmail = NoYes::No,  
               Common record = null,  
               str dataSourcename = '',  
               MenuFunction menuFunction = null)  
 {  
   EventInbox inbox;  
   DictTable table;  
   EventContextInformation eci;  
   EventInboxData inboxData;  
   Args args = new Args();  
   List list;  
   EventInboxId inboxId = EventInbox::nextEventId();  
   FormRun formRun;  
   WorkflowRecordCaptionGenerator recordCaptionGenerator;  
   UserInfo userInfo;  
   inboxId = EventInbox::nextEventId();  
   inbox.initValue();  
   inbox.ShowPopup      = showPopup;  
   inbox.Subject       = subject;  
   inbox.Message       = message;  
   inbox.SendEmail      = sendEmail;  
   inbox.EmailRecipient    = SysUserInfo::find().Email;  
   inbox.UserId        = userId;  
   inbox.InboxId       = inboxId;  
   inbox.AlertCreatedDateTime = DateTimeUtil::getSystemDateTime();  
   if (record)  
   {  
     table = new DictTable(record.TableId);  
     eci = new EventContextInformation();  
     if (!menuFunction)  
     {  
       menuFunction = new MenuFunction(table.formRef(),MenuItemType::Display);  
       if (!menuFunction)  
         throw error(strFmt("@SYS104114",table.formRef()));  
     }  
     //Build the data to drill down to from the notification  
     args.menuItemName(menuFunction.name());  
     args.menuItemType(MenuItemType::Display);  
     args.name(menuFunction.object());  
     eci.parmPackedArgs(args);  
     eci.parmAlertBuffer(record);  
     eci.parmAlertFormDsName(dataSourceName);  
     //eci.parmDontUseFormRunFromMenuItem(true);  
     inboxData.InboxId = inboxId;  
     inboxData.DataType = EventInboxDataType::Context;  
     inboxData.Data = eci.pack();  
     inboxData.insert();  
     inbox.AlertTableId = table.id();  
     inbox.ParentTableId = table.id();  
     recordCaptionGenerator = WorkflowRecordCaptionGenerator::construct(record);  
     inbox.AlertedFor = recordCaptionGenerator.caption();  
     list = SysDictTable::getUniqueIndexFields(table.id());  
     if (list)  
     {  
       inbox.keyFieldList(list.pack());  
       inbox.keyFieldData(SysDictTable::mapFieldIds2Values(list,record).pack());  
     }  
     inbox.CompanyId = record.company();  
   }  
   inbox.insert();  
 }  

To use this code
 static void Job155(Args _args)  
 {  
   InventTable inventTable;  
   select firstOnly inventTable;  
   DEVUtils::CreateAlert("message", "subject", curUserId(), true, false, inventTable, "InventTable", new MenuFunction(menuitemDisplayStr(EcoResProductDetailsExtended), MenuItemType::Display));  
 }  

How to using x++ code to printing PDF format [AX2012]


public class THK_MinutesTableController extends SrsReportRunController
{
}
----------------------------------------------------------------------
static void main(Args _args)
{
    THK_MinutesTableController controller;
    THK_MinutesTableContract   contract;
;
    controller = new THK_MinutesTableController();
    controller.parmReportName("THK_MinutesTable.Design");
    controller.parmDialogCaption("@THK422");
    controller.parmArgs(_args);
    controller.setRange(_args, controller.parmReportContract().parmQueryContracts().lookup(controller.getFirstQueryContractKey()));
    contract = controller.parmReportContract().parmRdpContract() as THK_MinutesTableContract;
    if (controller.prompt())
    {
        if (contract.parmExportPDF())
        {
            controller.parmReportContract().parmPrintSettings().printMediumType(SRSPrintMediumType::File);
            controller.parmReportContract().parmPrintSettings().overwriteFile(true);
            controller.parmReportContract().parmPrintSettings().fileFormat(SRSReportFileFormat::PDF);
            controller.parmReportContract().parmPrintSettings().fileName(controller.getFileNameSave());
            controller.run();
        }
        else
        {
            controller.parmReportContract().parmPrintSettings().printMediumType(SRSPrintMediumType::Screen);
            controller.run();
        }
    }
}
------------------------
public FilenameSave getFileNameSave()
{
    FilenameSave                fileNameSave;
    str                         directory;
    str                         filename;
    str dateTime2Str2(utcDateTime _dateTime)
    {
        utcDateTime     dateTime;
        ;
        dateTime = DateTimeUtil::applyTimeZoneOffset(_dateTime, DateTimeUtil::getCompanyTimeZone());
        return (strFmt("%1%2%3%4%5%6", DateTimeUtil::year(dateTime),
                                       DateTimeUtil::month(dateTime),
                                       DateTimeUtil::day(dateTime),
                                       DateTimeUtil::hour(dateTime),
                                       DateTimeUtil::minute(dateTime),
                                       DateTimeUtil::second(dateTime)));
    }
    ;
    directory = EcoResProductParameters::find().THK_TendorReportExportPath;
    filename = strFmt("%1_%2.pdf", "TenderMinutes", dateTime2Str2(DateTimeUtil::utcNow()));
    return (strFmt("%1\\%2", directory, filename));
}

How to update Financial dimension [AX2012]

 http://daxldsoft.blogspot.hk/2012/11/ax-2012-financial-dimension-update.html?showComment=1358080214915
Here are 2 ways to update a financial dimension, while keeping the other dims. The result should be the same, just use whatever you prefer

To use those two method proceed like so:

  purchTable = purchTable::find('PURCHID', true);  
  purchTable.DefaultDimension = getNewDefaultDimension(purchTable.DefaultDimension, "COSTCENTER", "YOURVALUE");  
  purchTable.update();  

METHOD NUMBER 1:

 static RecId getNewDefaultDimension(RecId defaultDimension, Name dimName, str 255 dimValue)  
 {  
   DimensionAttributeValueSetStorage  dimStorage;  
   Counter               i;  
   DimensionAttribute         dimAttributeCostCenter;  
   DimensionAttributeValue       dimAttributeValue;  
   dimStorage = DimensionAttributeValueSetStorage::find(defaultDimension);  
   dimAttributeCostCenter = DimensionAttribute::findByName(dimName);  
   if(dimValue)  
   {  
     dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttributeCostCenter, dimValue, true, true);  
     dimStorage.addItem(dimAttributeValue);  
   }  
   else  
     dimStorage.removeDimensionAttribute(DimensionAttribute::findByName(dimName).RecId);  
   return dimStorage.save();  
 }  


METHOD NUMBER 2:

 static RecId getNewDefaultDimension(RecId defaultDimension, Name dimName, str 255 dimValue)  
 {  
   container c;  
   RecId   newdefaultDimension;  
   int    i;  
   c = AxdDimensionUtil::getDimensionAttributeValueSetValue(defaultDimension);  
   i = conFind(c, dimName);      
   if(!i && !dimValue)  
     return defaultDimension;  
   if(i)  
   {      
     c = conDel(c, i+1, 1);   
     c = conDel(c, i, 1);    
   }  
   if(dimValue)  
   {  
     c += dimName;  
     c += dimValue;  
   }  
   c = conDel(c, 1, 1);  
   c = conIns(c, 1, conLen(c) / 2);  
   newdefaultDimension = AxdDimensionUtil::getDimensionAttributeValueSetId(c);  
   return newdefaultDimension;  
 }  

To get the current value of a financial dimension:

 static str 255 getDimensionValue(RecId defaultDimension, Name dimName)  
 {  
   DimensionAttributeValueSetStorage  dimStorage;  
   Counter               i;  
   DimensionAttribute         dimAttributeCostCenter;  
   DimensionAttributeValue       dimAttributeValue;  
    dimStorage = DimensionAttributeValueSetStorage::find(defaultDimension);  
   return dimStorage.getDisplayValueByDimensionAttribute(DimensionAttribute::findByName(dimName).RecId);  
 }  
 

2013年6月14日星期五

How to replace a Financial Dimension in Default Dimensions–Condensed [AX 2012]

In my previous post on Replacing default dimensions, I had provided a job to replace one financial dimension within a default dimension. The job was pretty big and I always thought that Microsoft should have provided some way to do these operations easily. Luckily I found a class that has helped me to condense that job and make it pretty small. I am sharing that job here:
The dimensions for Customer record looks like this before running the job:


Here is the job to change the values. We will change the values for Business Unit, Department and Worker all with this simple job:
static void replaceDefaultDimensionsCondense(Args _args)
{
    /*
     * In this job, we will replace the Business Unit Value from BU-001 to BU-002
     * and fill in the values for Department as Dep-001 and Worker as 114
     */
    CustTable                       custTable = CustTable::find(‘CUS-00004′); //Customer Record containing Financial Dimension
    Struct                          struct = new Struct(); //Structure to hold the dimension values to replace
    container                       defDimensionCon; //Container to prepare the required values and dimension attribute combination
    DimensionDefault                dimensionDefault; //Get the replaced dimension recid
    DimensionAttributeSetItem       dimAttrSetItem; //Table to get active dimensions for the legal entity
    DimensionAttribute              dimAttribute; //Table to get the Financial dimensions
    int i; //For looping

    //Loop for required dimensions
    while select Name, BackingEntityType from dimAttribute
        where dimAttribute.BackingEntityType == tableNum(DimAttributeOMBusinessUnit) ||
              dimAttribute.BackingEntityType == tableNum(DimAttributeOMDepartment) ||
              dimAttribute.BackingEntityType == tableNum(DimAttributeHcmWorker) &&
              dimAttribute.Type              != DimensionAttributeType::DynamicAccount
              join dimAttrSetItem
                where dimAttrSetItem.DimensionAttribute == dimAttribute.RecId &&
                      dimAttrSetItem.DimensionAttributeSet == DimensionCache::getDimensionAttributeSetForLedger()
    {
        //Add the Dimension name and display value to struct
        if (dimAttribute.BackingEntityType == tableNum(DimAttributeOMBusinessUnit))
        {
            struct.add(dimAttribute.Name, ‘BU-002′);
        }
        else if (dimAttribute.BackingEntityType == tableNum(DimAttributeOMDepartment))
        {
            struct.add(dimAttribute.Name, ‘DEP-002′);
        }
        else if (dimAttribute.BackingEntityType == tableNum(DimAttributeHcmWorker))
        {
            struct.add(dimAttribute.Name, ’114′);
        }
    }

    //Prepare the container
    defDimensionCon += struct.fields();

    for (i = 1; i <= struct.fields(); i++)
    {
        defDimensionCon += struct.fieldName(i);
        defDimensionCon += struct.valueIndex(i);
    }

    //if there are fields in struct
    if (struct.fields())
    {
        //Get the DimensionAttributeValueSet table’s Record ID
        dimensionDefault = AxdDimensionUtil::getDimensionAttributeValueSetId(defDimensionCon);
      
        //Update to Customer
        ttsBegin;
        custTable.selectForUpdate(true);
        if (custTable.DefaultDimension)
        {
            custTable.DefaultDimension = DimensionDefaultingService::serviceMergeDefaultDimensions(dimensionDefault, custTable.DefaultDimension);
        }
        else
        {
            custTable.DefaultDimension = dimensionDefault;
        }
        custTable.doUpdate();
        ttsCommit;
    }
}

Dimensions after running the job:


Pretty Neat!
The class AxdDimensionUtil is pretty handy to do all these stuff.

How to remove a Financial Dimension Value in AX2012

Here is a job that can help you remove or blank out a financial dimension.
Note the Dimensions before executing this job


Job
static void sgxRemoveWorkerDimension(Args _args)
{
/*
* We will Remove a worker dimension in this example
*/
CustTable custTable = CustTable::find(‘CUS-00004′); //Customer Record containing Financial Dimension
DimensionSHA1Hash hash; //To store the calculated hash for DimensionAttributeValueSet
DimensionAttribute dimAttr; // Contains the financial dimensions records
DimensionAttributeValue dimAttrValue; // Contains used financial dimension values
DimensionAttributeValueSet dimAttrValueSet; //Contains default dimension records
DimensionAttributeValueSetItem dimAttrValueSetItem; //Contains individual records for default dimensions
DimensionAttributeSetItem dimAttrSetItem; // Contains the number of dimensions active for a account structure ledger

HashKey valueKeyHashArray[]; //To store the has key of dimension in question
Map dimAttrRecId, dimAttrStr; //To store the dimension attribute recid and dimension attribute value display value
Set dimAttrValueRecId;
SetEnumerator setEnum;
int dimAttrCount, i;


//Initialize the map to store the backing entity types
dimAttrRecId = new Map(Types::Int64, Types::Integer);
dimAttrValueRecId = new Set(Types::Int64);
dimAttrStr = new Map(Types::Int64, Types::String);

//Find all the active dimensions for current ledger except main account and store there
//backing entity type in the map
while select RecId from dimAttr
order by Name
where dimAttr.Type != DimensionAttributeType::MainAccount
join RecId from dimAttrSetItem
where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&
dimAttrSetItem.DimensionAttributeSet == DimensionCache::getDimensionAttributeSetForLedger()
{
dimAttrCount++;
dimAttrRecId.insert(dimAttr.RecId, dimAttrCount);
}

//initialize hash key array to null
for (i = 1; i<= dimAttrCount; i++)
valueKeyHashArray[i] = emptyGuid();

//Get individual dimension attribute records and display values except worker dimension
//Store them in sets
while select DisplayValue, DimensionAttributeValue from dimAttrValueSetItem
where dimAttrValueSetItem.DimensionAttributeValueSet == custTable.DefaultDimension
join DimensionAttribute, HashKey from dimAttrValue
where dimAttrValue.RecId == dimAttrValueSetItem.DimensionAttributeValue
join RecId from dimAttr
where dimAttr.RecId == dimAttrValue.DimensionAttribute
&& dimAttr.BackingEntityType != tableNum(DimAttributeHcmWorker) //As we ignore worker dimension, its hash key remains blank
{
dimAttrValueRecId.add(dimAttrValueSetItem.DimensionAttributeValue);
dimAttrStr.insert(dimAttrValueSetItem.DimensionAttributeValue, dimAttrValueSetItem.DisplayValue);
valueKeyHashArray[dimAttrRecId.lookup(dimAttrValue.DimensionAttribute)] = dimAttrValue.HashKey;
}

//Calculate the hash for the current values
hash = DimensionAttributeValueSetStorage::getHashFromArray(valueKeyHashArray, dimAttrCount);

//Null hash indicates no values exist, which may occur if the user entered an invalid value for one dimension attribute
if (hash == conNull())
{
throw error(“Wrong value for Dimensions”);
}

// Search for existing value set
dimAttrValueSet = DimensionAttributeValueSet::findByHash(hash);

// This value set does not exist, so it must be persisted
if (!dimAttrValueSet)
{
ttsbegin;
// Insert the value set with appropriate hash
dimAttrValueSet.Hash = hash;
dimAttrValueSet.insert();

// Insert only specified set items use this
setEnum = dimAttrValueRecId.getEnumerator();

while (setEnum.moveNext())
{
dimAttrValueSetItem.clear();
dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;
dimAttrValueSetItem.DimensionAttributeValue = setEnum.current();
dimAttrValueSetItem.DisplayValue = dimAttrStr.lookup(setEnum.current());
dimAttrValueSetItem.insert();
}
ttscommit;
}

ttsBegin;
custTable.selectForUpdate(true);
custTable.DefaultDimension = dimAttrValueSet.RecId;
custTable.doUpdate();
ttsCommit;
}
Result After Job Run
image

How to enable or disable any report Section (header, Footer, Page Header, Page Footer etc.) by X++ in Ax 2009

As we know in report Section (header, Footer, Page Header, Page Footer etc) property does not exist visible node so we can do that from following code by writing this in fetch method according to the logic.
reportSection = this.design().sectionName('PageFooter');
this.enablePageFooter(); // this is used to enable page footer
this.disableSection(PageFooter);// used for Report section
this.enableSection(reportSection);