This code helps to import an XPO using X++
static void importXPO(Args _args)
{
    SysImportElements sysImportElements = new SysImportElements();
    ;
    sysImportElements.newFile("C:\\Form_KranthiTest.xpo");/// this is your XPO filename    
    sysImportElements.parmImportAot(true);
    sysImportElements.parmImportWithIds(false);///if you don't to import with IDvalues
    sysImportElements.import();
}
Friday, December 24, 2010
Monday, December 13, 2010
Sending mails through outlook
This code open an mail window form - to which you can send parameters like email id , subject and body
static void sendEmailThroughOutlook(Args args)
{
SmmOutlookEMail smmOutlookEMail = new SmmOutlookEMail();
Object smmSendEmail;
;
args = new Args();
args.name(formstr(smmSendEmail));
args.caller(smmOutlookEMail);
smmSendEmail = classfactory.formRunClass(args);
 
if (smmSendEmail)
{
smmSendEmail.init();
smmSendEmail.setEmailTos("aaa@gmail.com");
smmSendEmail.setEmailSubject("Kranthi");
smmSendEmail.setAttachments(["C:\AIF\kranthi.txt"]);
smmSendEmail.run();
smmSendEmail.refreshControl();
smmSendEmail.wait();
}
}
static void sendEmailThroughOutlook(Args args)
{
SmmOutlookEMail smmOutlookEMail = new SmmOutlookEMail();
Object smmSendEmail;
;
args = new Args();
args.name(formstr(smmSendEmail));
args.caller(smmOutlookEMail);
smmSendEmail = classfactory.formRunClass(args);
if (smmSendEmail)
{
smmSendEmail.init();
smmSendEmail.setEmailTos("aaa@gmail.com");
smmSendEmail.setEmailSubject("Kranthi");
smmSendEmail.setAttachments(["C:\AIF\kranthi.txt"]);
smmSendEmail.run();
smmSendEmail.refreshControl();
smmSendEmail.wait();
}
}
Tuesday, November 30, 2010
Which Report Template does the Auto Report Uses?
The Ax will use "FrontPage" report template for Auto Report.
Thursday, November 25, 2010
Download Project Time Management for AX 2009
Here is the link for downloading the Project Time Management for Microsoft Dynamics AX 2009 from partner source.
https://mbs.microsoft.com/partnersource/support/selfsupport/productreleases/ax2009projecttime.htm?printpage=false
https://mbs.microsoft.com/partnersource/support/selfsupport/productreleases/ax2009projecttime.htm?printpage=false
Monday, November 22, 2010
Changing Production Order Status to started
Here is a simple code which changes the production order status to started.
If your production module is setup-ed to post the PickingListJournal
automatically when the production is started - the code help you to post
even the picking list journal automatically.
prodTable = ProdTable::find("PRD_00000588");////pass your prodId
prodTable.autoUpdate(ProdStatus::StartedUp);
you cannot extend this to other status like ReportAsFinished and Ended
If your production module is setup-ed to post the PickingListJournal
automatically when the production is started - the code help you to post
even the picking list journal automatically.
prodTable = ProdTable::find("PRD_00000588");////pass your prodId
prodTable.autoUpdate(ProdStatus::StartedUp);
you cannot extend this to other status like ReportAsFinished and Ended
Saturday, November 20, 2010
Using DictClass
The below code is used to print number of methods in a Class ans even their names.
The same code can be extended to all other objects like DictTable class for tables,
dictEnum class for enums etc...
static void dictClass(Args _args)
{
int noOfMethods,i;
DictClass dictClass = new DictClass(ClassNum(InventMovement));
/// InventMovement is the Class
;
noOfMethods = dictClass.objectMethodCnt();
for(i = 1 ; i <= noOfMethods ; i++)
{
info(strfmt("%1. InvemntMovment - %2",i,dictClass.objectMethod(i)));
}
pause;
}
By using DictClass you can also print the number of Static methods and their names.
static void dictClassStatic(Args _args)
{
int noOfMethods,i;
DictClass dictClass = new DictClass(ClassNum(InventMovement));
;
noOfMethods = dictClass.staticMethodCnt();
for(i = 1 ; i <= noOfMethods ; i++)
{
info(strfmt("%1. InvemntMovment - %2",i,dictClass.staticMethod(i)));
}
pause;
}
The same code can be extended to all other objects like DictTable class for tables,
dictEnum class for enums etc...
static void dictClass(Args _args)
{
int noOfMethods,i;
DictClass dictClass = new DictClass(ClassNum(InventMovement));
/// InventMovement is the Class
;
noOfMethods = dictClass.objectMethodCnt();
for(i = 1 ; i <= noOfMethods ; i++)
{
info(strfmt("%1. InvemntMovment - %2",i,dictClass.objectMethod(i)));
}
pause;
}
By using DictClass you can also print the number of Static methods and their names.
static void dictClassStatic(Args _args)
{
int noOfMethods,i;
DictClass dictClass = new DictClass(ClassNum(InventMovement));
;
noOfMethods = dictClass.staticMethodCnt();
for(i = 1 ; i <= noOfMethods ; i++)
{
info(strfmt("%1. InvemntMovment - %2",i,dictClass.staticMethod(i)));
}
pause;
}
Wednesday, November 17, 2010
To create sales order from text file by using AxSalesTable and AxSalesLine Classes.
static void loadCSVFileSalesOrder(Args _args)
{
Dialog                   dialog;
DialogField              dialogFileName;
SysOperationProgress     simpleProgress;
Filename                 filename;
FileIOPermission         permission;
TextIO                   textIO;
NumberSeq                numSeq;
InventTable              inventTable;
str                      s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12;
Real                     r1,r2,r3,r4,r5,r6;
int                      i1,i2,i3,i4,i5,i6,i;
Container                c1,c2;
Container                filterCriteria;
AxSalesLine              axSalesLine  = new axSalesLine();
AxSalesTable             axsalesTable = new axSalesTable();
#File
#avifiles
;
    dialog            =  new Dialog("Importing Text File");
    dialogFileName    =  dialog.addField(typeid(Filenameopen), "File Name");
    filterCriteria  = ['*.txt'];
    filterCriteria  = dialog.filenameLookupFilter(filterCriteria);
    dialog.run();
    if (dialog.run())
     filename    =  dialogFileName.value();
    if(!filename)
    {
     info("Filename must be filled");
     throw("");
    }
    permission = new fileIOpermission(filename,#io_read);
    permission.assert();
    textIO = new TextIO(filename,#io_read);
    textIO.inFieldDelimiter(';');///Change the Delimeter if it is , or ; etc
    simpleProgress = SysOperationProgress::newGeneral(#aviUpdate, 'Importing sales data',100);
    if(textIO)
     {
       while(textIO.status() == IO_Status::Ok)
       {
           c1  =  textIO.read();
           s11 =  conpeek(c1,1);
           if(strlen(s11) > 1)///checks for the customer account
           {
            axsalesTable.parmSalesId();
            axsalesTable.parmCustAccount(Conpeek(c1,1));// Cust Account
            axsalesTable.save();
            axSalesLine.parmSalesId(axsalesTable.parmSalesId());
            axSalesLine.parmItemId(Conpeek(c1,2));//ItemId
            axSalesLine.axInventDim().parmInventSiteId(Conpeek(c1,3));//// InventSiteId
            axSalesLine.axInventDim().parmInventLocationId(Conpeek(c1,4));//// InventLocationId
            axSalesLine.parmSalesQty(conpeek(c1,5));//Sales Quantity
            axSalesline.parmSalesPrice(conpeek(c1,6));//Sales Price
            axSalesLine.save();
            i++;
            simpleProgress.incCount();
            simpleprogress.setText(strfmt("Lines imported: %1", i));
            info(strfmt("Sales Order : %1 has been created",axsalesTable.parmSalesId()));
            sleep(10);
           }
       }
     }
}
Importing data from text file in Axapta
The code will help you to import data from text file and create a sales order.
static void loadCSVFile(Args _args)
{
Dialog                   dialog;
DialogField              dialogFileName;
SysOperationProgress     simpleProgress;
Filename                 filename;
FileIOPermission         permission;
TextIO                   textIO;
NumberSeq                numSeq;
InventTable              inventTable;
str                      s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12;
Real                     r1,r2,r3,r4,r5,r6;
int                      i1,i2,i3,i4,i5,i6,i;
Addressing               a1,a2,a3;
TransDate                d1,d2,d3;
salesTable               salesTable,salesTable1,salesTable2;
salesLine                salesLine,salesLine1,salesLine2,salesLine3,salesLine4;
InventDim                inventDim,inventDim1,inventDim2;
Container                c1,c2;
CompanyInfo              companyInfoLoc = CompanyInfo::find();
Container                filterCriteria;
#File
#avifiles
;
    dialog            =  new Dialog("Importing Text File");
    dialogFileName    =  dialog.addField(typeid(Filenameopen), "File Name");
    filterCriteria  = ['*.txt'];
    filterCriteria  = dialog.filenameLookupFilter(filterCriteria);
    dialog.run();
    if (dialog.run())
     filename    =  dialogFileName.value();
    if(!filename)
    {
     info("Filename must be filled");
     throw("");
    }
    permission = new fileIOpermission(filename,#io_read);
    permission.assert();
    textIO = new TextIO(filename,#io_read);
    textIO.inFieldDelimiter(';');///Change the Delimeter if it is , or ; etc
    simpleProgress = SysOperationProgress::newGeneral(#aviUpdate, 'Importing sales data',100);
    if(textIO)
     {
       while(textIO.status() == IO_Status::Ok)
       {
           c1  =  textIO.read();
           s11 =  conpeek(c1,2);
           if(strlen(s11) > 1)
           {
            numSeq                           =  numberSeq::newGetNum(SalesParameters::numRefSalesId());
            salesTable.SalesId               =  numSeq.num();///SalesId
            salesTable.initValue();
            salestable.CustAccount           =  Conpeek(c1,1);// Cust Account
            salesTable.InvoiceAccount        =  Conpeek(c1,1);//Cust Account
            salestable.SalesId               =  salestable.SalesId;
            salesTable.initFromCustTable();
            salestable.ShippingDateRequested =  systemdateget();
            salesTable.ShippingDateConfirmed =  systemdateget();
            salesTable.insert();
            salesLine.SalesId = salesTable.SalesId;
            salesLine.initFromSalesTable(SalesTable);
            salesLine.initValue();
            salesLine.ItemId  = Conpeek(c1,2);//ItemId
            inventTable=salesLine.inventTable();
            salesLine.initFromInventTable(inventTable);
            inventDim1.InventSiteId = Conpeek(c1,3);//// InventSiteId
            inventDim1.InventLocationId = Conpeek(c1,4);//// InventLocationId
            salesLine.inventDimId = InventDim::findOrCreate(inventDim1).inventDimId;
            salesLine.initFromCustTable();
            salesLine.SalesQty           =   conpeek(c1,5);//Sales Quantity
            salesLine.QtyOrdered         =   conpeek(c1,5);// Sales Quantity
            salesLine.SalesPrice         =   conpeek(c1,6);//Sales Price
            salesLine.LineAmount         =   salesLine.SalesPrice * salesLine.SalesQty;//Sales Amount
            salesLine.AssessableValue_IN =   salesLine.LineAmount;
            salesLine.Address_IN       = companyInfoLoc.Address;
            salesLine.ExciseType_IN    = companyInfoLoc.ExciseType_IN;
            salesLine.TIN_IN           = companyInfoLoc.TIN_IN;
            salesLine.ECCNumber_IN     = companyInfoLoc.ECCNumber_IN;
            salesLine.IECNumber_IN     = companyInfoLoc.IECNumber_IN;
            salesLine.STCNumber_IN     = companyInfoLoc.STCNumber_IN;
            salesLine.SalesTaxRegistrationNumber_IN = companyInfoLoc.SalesTaxRegistrationNumber_IN;
            salesLine.TAN_IN           = companyInfoLoc.TAN_IN;
            salesLine.State_IN         = companyInfoLoc.State;
            salesLine.RemainSalesPhysical  =  salesLine.SalesQty;
            salesLine.RemainSalesFinancial =  0;
            salesLine.RemainInventPhysical =  salesLine.QtyOrdered;
            salesLine.LineNum = SalesLine::lastLineNum(salesLine.SalesId) + 1.0;
            salesLine.insert();
            i++;
            simpleProgress.incCount();
            simpleprogress.setText(strfmt("Lines imported: %1", i));
            info(strfmt("Sales Order : %1 has been created",salesTable.SalesId));
            sleep(10);
           }
       }
     }
}
Tuesday, November 16, 2010
To print comments related to workflow
This code helps you to print the comments related to a workflow. 
static void printWorkFlowComments(Args _args)
{
SmmLeadTable                  smmLeadTable;
WorkFlowTrackingTable         workFlowTrackingTable;
WorkflowTrackingCommentTable  workflowTrackingCommentTable;
;
smmLeadTable.RecId = 5637145827;////smmLeadTable buffer   //// Change the table where the workflow is enabled.
while select workFlowTrackingTable order by RecId asc where workFlowTrackingTable.ContextRecId == smmLeadTable.RecId
{
 select firstonly workflowTrackingCommentTable where workflowTrackingCommentTable.TrackingId == workFlowTrackingTable.TrackingId;
 if(workflowTrackingCommentTable)
 {
  print workflowTrackingCommentTable.Comment;
  print workflowTrackingCommentTable.TrackingMessage;
 }
}
pause;
}
To allow only alphabets to be entered into a string field
This code validates a string field. If you want only the alphabets need to be entered into it.
You can extend this numbers and combination of alphabets and numbers.
static void allowalpha(Args _args)
{
#define.alphabets('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
str k ="kranthi";;
;
 if((strkeep(substr(k, 1, strlen(k)), #alphabets)  != substr(k, 1, strlen(k)))) 
      print  "The string contains other than alphabets";
 else 
     print "The string contains only alphabets";
pause;
}
Creating Inventory journals through code in ax 2009 which mainly helps during the import of opening balances
Importing of inventory opening balances will be  some times a difficult task because of the Inventory dimensions. This code may help you.
The below code uses AxInventJournalTrans class for creating the inventory journal transactions.
it will automatically creates the InventDimId and InventTransId.
The code contains only 5 columns which should be order. if you have more columns change the code according to it.
static void createInventoryJournal(Args _args)///and also it will import the data from the Excel
{
InventJournalNameId          inventJournalNameId = "IMov";///Assign the journal Name
AxInventJournalTrans         axInventJournalTrans;
InventJournalTable           inventJournalTable;
Dialog                       dialog;
DialogField                  dialogField,dialogdate;
Filename                     filename;
COMVariant                   cOMVariant;
SysExcelApplication          sysExcelApp;
SysExcelWorkbooks            workbooks;
SysExcelWorkbook             workbook;
SysExcelWorksheets           worksheets;
SysExcelWorksheet            worksheet;
SysExcelCells                cells;
SysExcelCell                 rCell;
int                          i,j,k;
SysOperationProgress         simpleProgress;
Container                    filterCriteria;
#avifiles
;
sysExcelApp = SysExcelApplication::construct();///SysExcelApplication
workbooks   = sysExcelApp.workbooks();///Workbooks
Dialog = new dialog();
dialogField = dialog.addField(typeId(FileNameOpen),'File Name');
filterCriteria  = ['*.xls','*. xlsx'];//// To filter only Excel files
filterCriteria  = dialog.filenameLookupFilter(filterCriteria);
dialog.run();
if(dialog.run())
fileName = dialogField.value();
cOMVariant  = new COMVariant();
cOMVariant.bStr(fileName);
workBook    = workBooks.add(cOMVariant);///Workbook
worksheets  = Workbook.worksheets();///WorkSheets
worksheet   = worksheets.itemFromNum(1);///WorkSheet
Cells       = workSheet.cells();///Cells
i=2;
rCell       = Cells.item(i,1);///rCell
if(fileName)
{
 ttsBegin;
 inventJournalTable.JournalNameId =  inventJournalNameId;
 inventJournalTable.initFromInventJournalName(InventJournalName::find(inventJournalNameId));
 inventJournalTable.insert();
 simpleProgress =  SysOperationProgress::newGeneral(#aviUpdate,'Importing Transactions',100);///SysProgressOperation
 while(RCell.Value().bStr() != '')
 {
  j++;
  simpleProgress.incCount();
  simpleprogress.setText(strfmt("Transaction Imported: %1",i));
  sleep(10);
  simpleprogress.kill();
  axInventJournalTrans  = new AxInventJournalTrans();
  axInventJournalTrans.parmJournalId(inventJournalTable.JournalId);
  axInventJournalTrans.parmTransDate(systemdateget());
  axInventJournalTrans.parmLineNum(j);
  rCell = Cells.Item(i, 1);
  axInventJournalTrans.parmItemId(RCell.value().bStr());
  rCell = Cells.Item(i, 2);
  axInventJournalTrans.axInventDim().parmInventSiteId(rCell.value().bStr());
  rCell = Cells.Item(i, 3);
  axInventJournalTrans.axInventDim().parmInventLocationId(rCell.value().bStr());
  rCell = Cells.Item(i, 4);
  axInventJournalTrans.parmQty(rCell.value().double());
  rCell = Cells.Item(i, 5);
  axInventJournalTrans.parmCostPrice(rCell.value().double());
  axInventJournalTrans.save();
  i++;
  rCell = Cells.Item(i, 1);
 }
 ttsCommit;
}
}
Creating Purchase Orders Through code in ax 2009
You can create Purchase Orders through code by using AxPurchTable and AxPurchLine Classes
static void createPurchOrder(Args _args)
{
AxPurchLine      axPurchLine  = new axPurchLine();
AxPurchTable     axPurchTable = new axPurchTable();
;
axPurchTable.parmPurchId();///Creates new PurchId
axPurchTable.parmOrderAccount('1001');///Vendor Account
axPurchTable.save();
axPurchLine.parmPurchId(axPurchTable.parmPurchId());////Assigns PurchId
axPurchLine.parmItemId('PolyEthylene');///Item Number
axPurchLine.axInventDim().parmInventSiteId("Unit1");///Site Id
axPurchLine.axInventDim().parmInventLocationId("RM");/// Warehouse
axPurchLine.parmPurchQty(1000);///Quantity
axPurchLine.parmPurchPrice(20);///Purchase price per one quantity
axPurchLine.save();
}
Creating Sales Orders Through code in ax 2009
Creating a sales order using AxSalesTable, AxSalesLine classes.
static void createSalesOrder(Args _args)
{
AxSalesLine      axSalesLine  = new axSalesLine();
AxSalesTable     axsalesTable = new axSalesTable();
SalesId          salesId;
;
salesId = axsalesTable.parmSalesId();///Creates sales Id
axsalesTable.parmCustAccount('1104');
axsalesTable.save();
axSalesLine.parmSalesId(axsalesTable.parmSalesId());////assigns sales Id
axSalesLine.parmItemId('PolyEthylene');/// Item Id
axSalesLine.axInventDim().parmInventSiteId("Unit1");///Site
axSalesLine.axInventDim().parmInventLocationId("RM");///Warehouse
////if you have more dimensions enabled for item add here.
axSalesLine.parmSalesQty(1000);///Quantity
axSalesline.parmSalesPrice(20);///Sales Price per one quantity
axSalesLine.save();
}
Changing numerals to text in Indian Format
I have made some changes to the "numeralsToTxt_EN" method of the Global Class.
Add the below method to the Global class - which will convert the number into Indian format
static TempStr numeralsToTxt_IN(real _num)
{
    int     numOfPennies = (decround(frac(_num), 2) * 100) mod 100;
    real    test         = _num - frac(_num);
    int     numOfTenths;
    str 20  ones[19], tenths[9], hundreds, thousands, lakhs, crores;
    int64   temp;
    str 200 returntxt;
    str 200 pennytxt;
    int     penny;
    real modOperator(real a1, real a2)
    {
    int tmpi;
    real tmp1, tmp2;
    tmp1 = a1 / a2;
    tmpi = real2int(tmp1);
    tmp2 = tmpi;
    return (tmp1 - tmp2)*a2;
    }
    real checkPower(real  _test, int64 _power)
    {
        int64   numOfPower;
        if (_test >= _power)
        {
            numOfPower = _test div _power;
            if (numOfPower >= 100)
            {
                temp = numOfPower div 100;
                returntxt = returntxt + ' ' + ones[temp] + ' ' + hundreds;
                numOfPower = numOfPower mod 100;
            }
            if (numOfPower >= 20)
            {
                temp = numOfPower div 10;
                returntxt = returntxt + ' ' + tenths[temp];
                numOfPower = numOfPower mod 10;
            }
            if (numOfPower >= 1)
            {
                returntxt = returntxt + ' ' + ones[numOfPower];
                numOfPower = numOfPower mod 10;
            }
            switch(_power)
            {
                case 10000000 :
                {
                    returntxt = returntxt + ' ' + Crores;
                    _test = modOperator(_test, 10000000);
                    break;
                }
                case 100000 :
                {
                    returntxt = returntxt + ' ' + lakhs;
                    _test = modOperator(_test, 100000);
                    break;
                }
                case 1000 :
                {
                    returntxt = returntxt + ' ' + thousands;
                    _test = modOperator(_test, 1000);
                    break;
                }
                case 100 :
                {
                    returntxt = returntxt + ' ' + hundreds;
                    _test = modOperator(_test, 100);
                    break;
                }
            }
        }
        return _test;
    }
    #Define.text_1('One')
    #Define.text_2('Two')
    #Define.text_3('Three')
    #Define.text_4('Four')
    #Define.text_5('Five')
    #Define.text_6('Six')
    #Define.text_7('Seven')
    #Define.text_8('Eight')
    #Define.text_9('Nine')
    #Define.text_10('Ten')
    #Define.text_11('Eleven')
    #Define.text_12('Twelve')
    #Define.text_13('Thirteen')
    #Define.text_14('Fourteen')
    #Define.text_15('Fifteen')
    #Define.text_16('Sixteen')
    #Define.text_17('Seventeen')
    #Define.text_18('Eighteen')
    #Define.text_19('Nineteen')
    #Define.text_20('Twenty')
    #Define.text_30('Thirty')
    #Define.text_40('Forty')
    #Define.text_50('Fifty')
    #Define.text_60('Sixty')
    #Define.text_70('Seventy')
    #Define.text_80('Eighty')
    #Define.text_90('Ninety')
    #Define.text_100('Hundred')
    #Define.text_1000('Thousand')
    #Define.text_100000('Lakh')
    #Define.text_10000000('Crore')
    #Define.text_and('Rupees and')
    #Define.text_paise('Paise Only')
    #Define.text_ruppe('Rupees Only')
    ones[1] = #text_1;
    ones[2] = #text_2;
    ones[3] = #text_3;
    ones[4] = #text_4;
    ones[5] = #text_5;
    ones[6] = #text_6;
    ones[7] = #text_7;
    ones[8] = #text_8;
    ones[9] = #text_9;
    ones[10] = #text_10;
    ones[11] = #text_11;
    ones[12] = #text_12;
    ones[13] = #text_13;
    ones[14] = #text_14;
    ones[15] = #text_15;
    ones[16] = #text_16;
    ones[17] = #text_17;
    ones[18] = #text_18;
    ones[19] = #text_19;
    tenths[1] = 'Not used';
    tenths[2] = #text_20;
    tenths[3] = #text_30;
    tenths[4] = #text_40;
    tenths[5] = #text_50;
    tenths[6] = #text_60;
    tenths[7] = #text_70;
    tenths[8] = #text_80;
    tenths[9] = #text_90;
    hundreds    = #text_100;
    thousands   = #text_1000;
    lakhs       = #text_100000;
    crores      = #text_10000000;
    test = checkPower(test, 10000000);
    test = checkPower(test, 100000);
    test = checkPower(test, 1000);
    test = checkPower(test, 100);
    if (test >= 20)
    {
        numOfTenths = test div 10;
        returntxt = returntxt + ' ' + tenths[numofTenths];
        numOfTenths = numOfTenths mod 10;
        test = test mod 10;
    }
    if (test >= 1)
    {
        numOfTenths = real2int(test);
        returntxt = returntxt + ' ' + ones[numOfTenths];
    }
    if (numOfPennies)
    {
            if (numOfPennies >= 20)
            {
                penny = numOfPennies div 10;
                pennytxt = tenths[penny];
                numOfPennies = numOfPennies mod 10;
            }
            if (numOfPennies >= 1)
            {
                pennytxt = pennytxt + ' ' + ones[numOfPennies];
            }
        returntxt = returntxt + ' ' + #text_and + ' ' + pennytxt + ' ' +#text_paise;
    }
    else
    {
        returntxt = returntxt + ' ' + #text_ruppe;
    }
    return returntxt;
}
Subscribe to:
Comments (Atom)