пятница, 10 июня 2011 г.

AIF exception message

Hello colleagues.

My new article is about error messages which AIF sends to web service consumer application. If exception appeared inside AX business logic while web service performs a request then a client will get a message like "Request Failed. See the Exception Log for details.". It is not the transparent approach, is it. I was requested to resolve this issue in order to provide Web service consumer with detailed information about an error. After short investigation I have figured out that modification should be done in the handleProcessingException() method of the AifRequestProcessor class.

static private AifXml handleProcessingException(AifMessage message, AifInfoLog aifInfoLog)
{
SysExceptionLog exceptionLog;
AifXml faultXml;
AifMessage originalMessage;
;
// We have to get the faultXml first, otherwise doing a infolog.cut() will clear
// the fault object from infolog.
/* I have commented this block of code
if( message && AifEndpoint::propagateErrors(message.sourceEndpointId()) )
{

faultXml = AifFault::faultXml(aifInfoLog.getLastMessage());
}
else
{
faultXml = AifFault::faultXml("@SYS94277");
}*/

faultXml = AifFault::faultXml(infoLogStr(aifInfoLog.getInfoLogData())); // I have add this record

//Write entries to the Exception Log
exceptionLog = new SysExceptionLog();
exceptionLog.writeInfoLogData(strfmt("@SYS95172"), aifInfoLog.getInfoLogData());
aifInfoLog.clearLast();

// Set message status to Error if not processed yet
// The message will exist only if the message has been created, if
// any exception was thrown from processInboundMessage() before the message was created,
// the below call (AifMessageManager::update()) will fail, so we have additional check here.
if( message
&& message.messageId()
&& (!AifMessageManager::isNewMessage(message.messageId())))
{
originalMessage = AifMessageManager::read(message.messageId());
// If message in already in processed or error state, then leave it as is.
if ((originalMessage.status() != AifMessageStatus::Processed) && (originalMessage.status() != AifMessageStatus::Error))
{
originalMessage.status(AifMessageStatus::Error);
AifMessageManager::update(originalMessage, classstr(AifRequestProcessor), true);
}
}

return faultXml;
}

Here is the code of infoLogStr() method:

public static str infoLogStr(InfologData _infologData)
{
str ret;
SysInfoLogEnumerator infoLogEnum = SysInfoLogEnumerator::newData(_infologData);
SysInfologMessageStruct infoMessageStruct;
;
while(infoLogEnum.moveNext())
{

infoMessageStruct = SysInfologMessageStruct::construct(infoLogEnum.currentMessage());

if (ret)
ret += '\r\n';

ret += strFmt('%1: %2',
infoMessageStruct.preFixTextElement(infoMessageStruct.prefixDepth()),
infoMessageStruct.message());
}

return ret;
}

I hope this post will be usefull.
Best regards.

вторник, 13 мая 2008 г.

Be careful whith Collections and Maps in Axapta

Let`s review block of code listed below.

List list = new List(Types::String);

ListIterator iterator;
;
list.addEnd("a");
list.addEnd("b");
list.addEnd("c");
list.addEnd("d");

iterator = new ListIterator(list);

while (iterator.more())
{
if (iterator.value() == "b")
list.addEnd("e");

iterator.next();
}

info(list.toString());

There is instance of List class retaining arbitrary string values. ListIterator class is instantiated for traversing through elements in the list. What will happen when we try to add a new element in the list inside iteration block? New element will be added to collection! In Java for iterators implemented other behavior. If an iterator is obtained, modifying the underlying collection will throw a ConcurrentModificationException. Pay attantion to this difference.

четверг, 8 мая 2008 г.

Warehouse attributes

The interesting solution was implemented on current project. One from our customer requirements is more deeply analysis of goods flows for effective management. There are many criterions for review and one of them is the warehouse properties. It can be region where warehouse is situated or belonging warehouse to company attribute, for instance. Inventory dimensions technology is powerful facility in Axapta. And our developer team took these ideas as basis. For all attributes was created tables as they are created for all inventory dimensions. And fields referenced to corresponding tables, was added to InventLocation as inventory dimensions fields in InventDim. Then functionality of grouping and rendering different views for warehouse attributes was extended in OnHand form. Users can quickly get used to warehouse attributes as to inventory dimensions. This solution is sutable for OLAP analysis as well.

понедельник, 5 мая 2008 г.

Inventory closing make corrections for transactions in closed periods

It was on previous project where I first took part as a manager. The project was completed successfully, but some questions have no answers till now. One of them is why inventory closing functionality makes corrections for transactions in closed periods. It is huge headache for all users. Fortunately it happens rarely, that is why solution of this issue was suspended. When I visited masterclass at December 2007 arranged by Microsoft in Moscow, I asked this question, but got no answer. I am going to investigate inventory closing algorithm more deeply, but if anybody has decision, be welcome.

среда, 30 апреля 2008 г.

Write InfoLog to database

There are several batch tasks running on the schedule in our company. They send many messages to InfoLog. Usually I am interesting messages of some type. It is not convenient scroll all messages in SysInfologBrowser form in search of required. I decided to implement the method for writing InfoLog to the database. After investigation of the Info class I wrote code listed below.

protected Log logTxt()

{

#resAppl

#Define.UserTab('\t')

Integer fromIdx = transLogPoint + 1;

Integer toIdx = infoLog.num();

SysInfoLogEnumerator enum = SysInfologEnumerator::newData(infoLog.copy(fromIdx, toIdx));

Log logTxt;

DictEnum dictEnum = new DictEnum(enumNum(Exception));
;
while (enum.moveNext())
{

logTxt += strFmt("%1 \t %2\r\n",

dictEnum.index2Name(enum.currentException()),
strRem(enum.currentMessage(), #UserTab));
}

transLogPoint = toIdx;

return logTxt;

}

Variable transLogPoint keep the value of the last message index before start batch task.

Why debit and credit turnovers different in Ledger and Inventory modules?

I want to share my thoughts about inventory closing functionality. I had made this research since time I took part in Axapta implementation projects. For all customers our developer team extended functionality of the inventory turnover report. After report modification users could receive detailed financial data in the inventory module. But there is one small issue. Financial managers and accountants asked me why turnovers were different in Ledger and Inventory modules. After investigation I found the cause of problem. There is no field Correct in InventTrans table like in LedgerTrans. That is why transactions are different for credit note in Ledger and Inventory modules. And inventory closing doesn’t create ledger transactions with Correct sign. We suggested correcting turnovers in Ledger module after inventory closing by manually creating additional correctional transactions. What do you think about it?

вторник, 29 апреля 2008 г.

Null value for ADO command parameter

Now I whith my colleagues work on integration Axapta with the other business application named M&F (Management and Finance). Our company uses M&F for more than eight years and now it is time for new ERP system. This application has 2-tier architecture, and its business logic is on the server side. We use stored procedure API for document exchange from Axapta to M&F. Some days ago I tried to call stored procedure on MS SQL Server from X++. For several parameters it was necessary to set null value. At first I instantiated COMVariant like this:

COM parameter = new COM("ADODB.Parameter");

COMVariant nullParam = new COMVarian(ComVariantInOut::In, ComVariantType::VT_NULL);

;

parameter.name("@paramName");

parameter.type(#adInteger);

parameter.value(nullParam); // This method throw exception!

After several hours research I found decision of this problem:

COM parameter = new COM("ADODB.Parameter");

nullParam = new COMVariant();

;

parameter.name("@paramName");

parameter.type(#adInteger);

nullParam.variantType(ComVariantType::VT_NULL);

parameter.value(nullParam); // This method doesn`t throw exception now!

I think problem is in constructor of the COMVariant class. What is your opinion?