27 Aralık 2011 Salı

AXAPTA runbase dialog

void clicked()
{
    Dialog      dialog;
    DialogField DEmplId;
    DialogField DApp;
    DialogField DAppDesc;
    ;

    dialog = new Dialog("@SYS108667");
    DApp     = dialog.addField(typeId(B_Approval));
    DAppDesc = dialog.addField(typeId(Description));
    DEmplId  = dialog.addField(typeId(EmplId));

    DApp.value(B_RepairJournalPartsChangeLines.Approval);
    DAppDesc.value(B_RepairJournalPartsChangeLines.ApprovalDescription);
    DEmplId.value(SysCompanyUserInfo::find(CurUserId()).EmplId);

    dialog.run(); // show

    if (dialog.closedOK())
    {
        ttsbegin;
        B_RepairJournalPartsChangeLines.Approval            = DApp.value();
        B_RepairJournalPartsChangeLines.ApprovalDescription = DAppDesc.value();
        B_RepairJournalPartsChangeLines.ApproveEmplId       = DEmplId.value();
        element.redraw();
        ttscommit;
    }
}

AXAPTA post invent journal

    journalTableData        journalTabledata;
    inventJournalTable      inventJournalTable;
    InventJournalCheckPost  journalCheckPost;
    ;
    inventJournalTable = inventJournalTable::find(Line.InventJournalId);
    // Posting Journal
    journalTableData = JournalTableData::newTable(inventJournalTable);
    journalTableData.updateBlock(JournalBlockLevel::InUse,JournalBlockLevel::None);
    if (infolog.num(Exception::Error))
        return;
    infolog.clear(0);
    journalCheckPost =
    InventjournalCheckPost::newJournalCheckPost(JournalCheckPostType::Post,InventJournalTable);
    journalCheckPost.parmAutoBlock(true);
    journalCheckPost.run();
    if(journalTableData.hasErrors())
        return;

22 Aralık 2011 Perşembe

AXAPTA invent journal

When we were working with forms we just select InventJournalName and default parameters come. At working with code we have to put our parameters (or that is the way just I can found):

    InventJournalTrans  inventJournalTrans;
    InventJournalTable  inventJournalTable;
    InventJournalName   inventJournalName;
    InventDim toInventDim;
    InventDim tempInventDimTo; 
     ;
    // Process Journal Header
    inventJournalName   = "MyJournalName"
    inventJournalTable.initValue();
    inventJournalTable.JournalId        = NumberSeq::newGetNum(InventParameters::numRefInventJournalId()).num();
    inventJournalTable.Description      = InventJournalName.Description;
    inventJournalTable.Reservation      = ItemReservation::Automatic;
    inventJournalTable.JournalType      = inventJournalType::Transfer;

    inventJournalTable.BlockUserGroupId     = inventJournalName.BlockUserGroupId;
    inventJournalTable.JournalNameId        = inventJournalName.JournalNameId;
    inventJournalTable.ApprUserGroup        = inventJournalName.ApprUserGroup;
    inventJournalTable.ApprFromSiteId       = inventJournalName.ApprFromSiteId;
    inventJournalTable.ApprFromLocationId   = inventJournalName.ApprFromLocationId;
    inventJournalTable.ApprFromWMSLocationId= inventJournalName.ApprFromWMSLocationId;
    inventJournalTable.ApprFromBatchId      = inventJournalName.ApprFromBatchId;
    inventJournalTable.ApprToSiteId         = inventJournalName.ApprToSiteId;
    inventJournalTable.ApprToLocationId     = inventJournalName.ApprToLocationId;
    inventJournalTable.ApprToWMSLocationId  = inventJournalName.ApprToWMSLocationId;
    inventJournalTable.ApprToBatchId        = inventJournalName.ApprToBatchId;
    inventJournalTable.VoucherSeqId         = inventJournalName.VoucherSeqId;

    inventJournalTable.insert();
toInventDim.clear();
toInventDim.initValue();
toInventDim.wMSLocationId = inventJournalName.ApprtoWMSLocationId;
toInventDim.InventLocationId = inventJournalName.ApprtoLocationId;
toInventDim.InventSiteId      = inventJournalName.ApprtoSiteId;


    // Process journal lines
    inventJournalTrans.initValue();
    inventjournaltrans.initFromInventJournalTable(inventJournalTable);
    inventJournalTrans.Voucher      = NumberSeq::newGetNum(InventParameters::numRefInventJournalVoucherId()).num();
    inventJournalTrans.ItemId       = "MyItem001";
    InventJournalTrans.initFromInventTable(Inventtable::find(InventJournalTrans.ItemId));
    inventJournalTrans.TransDate    = systemDateGet();
    inventJournalTrans.Qty          = -1;
    inventJournalTrans.InventDimId = tempInventDimto.inventDimId;
    // reservation check
    if (!InventMoveMent::setAutoReserving(InventJournalTrans))
    {
        inventJournalTable.delete();
        return;
    }
    inventJournalTrans.insert();

16 Aralık 2011 Cuma

AXAPTA Set empty date and datetime values

for Datetime values:

myDateTime=DateTimeUtil::minValue();

for Date values:
myDate =global::dateNull();

6 Aralık 2011 Salı

5 Aralık 2011 Pazartesi

AXAPTA sales price, cost price

Sales price:
    B_RepairJournalOrderLines.ProjSalesPrice = InventTable::find(B_RepairJournalOrderLines.ItemId).LastUserPrice;
Cost price:
    B_RepairJournalOrderLines.ProjCostPrice = InventTableModule::find(B_RepairJournalOrderLines.ItemId,ModuleInventPurchSales::Sales).price() ;

AXAPTA when there isn't go to main table form menu item?

You can see at right click "Go to main table form" if current control bounded a tables key field. But if it's just created with extended data type, you cannot see this menu item.

I used Fatih Demirci'nin this code:

public void jumpRef()
{
EmplTable emplTable;
Args args;
MenuFunction menuFunction;
;
emplTable = EmplTable::find(this.text());
if (!emplTable)
{
return;
}
args = new Args();
args.caller(element);
args.record(emplTable);
menuFunction = new MenuFunction( menuitemdisplaystr(EmplTable),
MenuItemType::Display);
menuFunction.run(args);
}

But actually this code behaves different from systems default "Go to main table" menu item. It just filter related record. Orijinal menu item just locate related record not filter. After a little search I wrote this code. It works just like original:

public void jumpRef()
{
CustTable custTable;
Args args;
MenuFunction menuFunction;
;
args = new Args();
args.caller(element);
args.lookupfield(FieldNum(CustTable,AccountNum));
args.lookupValue(this.text());
menuFunction = new MenuFunction(MenuItemDisplayStr(custTable),MenuItemType::Display);
menuFunction.run(args);
}

AXAPTA user tables

There are three tables about user relations:

UserInfo - There are active directory users at this table. User id at Id field and name at name field...

EmplTable - This is firms employee table. Employee code at  EmplId field.

SysCompanyUserInfo - This table ties UserInfo and EmplTable tables. UserId field at this table related with Id from UserInfo table, EmplId field at this table related with EmplId at EmplTable. You can call Administrator->User relations form for this relation.

CurUserId() gives you current users ID.
At this sample you created a table and you want to put default users employee id at create  new record:    MyTable.EmplId = SysCompanyUserInfo::find(CurUserId()).EmplId;

AXAPTA terminate AOS

This code doesn't give compile error. Actually code has mistake. It occurs error 174 and terminates AOS:

    select firstonly * from ord join rel join obj
        where ord.ServiceOrderId == rel.RelKeyId &&
            rel.RelTableId == ord.TableId &&
            obj.ServiceObjectId == rel.ServiceObjectId &&
            ( L_ItemSerialNum.valueStr()== obj.ItemSerialNum || (!L_ItemSerialNum)) &&
            ( L_SMAServiceObjectId.valueStr()== rel.ServiceObjectId || (!L_SMAServiceObjectId));

After this fix there is no terminate anymore:
    select firstonly * from ord join rel join obj
        where ord.ServiceOrderId == rel.RelKeyId &&
            rel.RelTableId == ord.TableId &&
            obj.ServiceObjectId == rel.ServiceObjectId &&
            ( L_ItemSerialNum.valueStr()== obj.ItemSerialNum || (!L_ItemSerialNum.valueStr())) &&
            ( L_SMAServiceObjectId.valueStr()== rel.ServiceObjectId || (!L_SMAServiceObjectId.valueStr()));

I made a bit search at internet and learned error 174 occurs even with perfect code parts...

2 Aralık 2011 Cuma

AXAPTA another way to get number sequence at create record with code

    ExtendedTypeId              id  = TypeID2ExtendedTypeId(TypeId(SMAServiceObjectId));
    NumberSeq                   num = NumberSeq::newGetNum(NumberSequenceReference::find(id));
    ;
...
...
    SMAServiceObjectTable.ServiceObjectId     = num.num();

AXAPTA control with code a field of datasource at form.

It's so easy. You have to use object array for that:    

SMAObjectGroup_ds.object(fieldnum(SMAServiceObjectGroup,
        Barebone)).enabled(SMAObjectGroup.ServiceItemType == B_ServiceItemType::Item);

AXAPTA Create new record at another form and return from

Sometimes you can want to or have to create new record  at another form. There are some Axapta forms do it with complex ways. I couldn't understand Axapta's ways.
This is mine simple way:

Main forms data source create method:


public void create(boolean _append = false)
{
    Args                args;
    FormRun             formCreate;
    ;

        args = new Args();
        args.name(formstr(B_ExpressServiceNew));
        args.caller(element);
        formCreate = classfactory.formRunClass(args);
        formCreate.init();
        formCreate.run();
        formCreate.wait();
}

B_ExpressServiceNew, is my new record forms name.
This part should write at new record forms new record method:


    if (element.args() && element.args().caller() && element.args().caller().name() == formstr(B_ExpressService))

    {
     mainForm = element.args().caller();
     mainForm.SetRecord(oTable);
    }


B_ExpressService is my main form, oTable is my new record.
Setrecord method at main form for locate new record:

void SetRecord(SMAServiceOrderTable _order)
{
    ;
    SMAServiceOrderTable.reread();
    SMAServiceOrderTable.data(_order);
    SMAServiceOrderTable_DS.setCurrent();

} 

AXAPTA edit method sample

Edit method let us show a field of another table and tie it with a field of our table. In this sample ServiceObjectId fields value changes related with ItemId updates. We get ServiceObjectId fields value, put ItemId which has this ServiceObjectId value. When ItemId changed we update ServiceObjectId value.


edit ItemId EditItemId(boolean set, ItemId _ItemId)
{
    ItemId ret;
    ;

    if (set)
    {
        this.ServiceObjectId =
            SMAServiceObjectTable::findItem(_ItemId).ServiceObjectId;
    }
    else
    {
        ret = SMAServiceObjectTable::find(this.ServiceObjectId).ItemId;
    }
    return ret;
}

Set boolean value selects if editmethod will work for update or just display. If set true it's mean second parameter (in this case _ItemId ) filled from user. Else editmethod value cames from related field value.

AXAPTA splitter control

There isn't a true splitter control in Axapta. There is a class made by group panels. There are a lot of forms use this control.

Write these at ClassDeclaration:

    SysFormSplitter_X               _VerticalSplitter;
    SysFormSplitter_Y               _HorizontalSplitter;



First was for vertical splitter, second was horizontal. Put this code part to forms init method:

   _VerticalSplitter = new SysFormSplitter_X(VerticalSplitter,groupLeft,element);
   _HorizontalSplitter = new SysFormSplitter_Y(HorizontalSplitter,groupTop,element);

Arrangements for vertical splitter:

VerticalSplitter at up is group control for use vertical splitter control. It parameters are:
AlignChild = No
AlignControl = Yes
AutoDeclaration = Yes
BackgroundColor = Window background
FrameType = Raised 3D
HideifEmpty = No
Height = Column height
Width = 5
We put two group controls of vertical splitter one is left and one is right and it's parents column value should 3. This is important for a correct screening.Make Autodeclaration = Yes for left group. (It's name is groupLeft at upper). It's Height value must be Column Height, Width may be like 100-200, width value will be change and save after we move the splitter. Right groups height value should be column height and width value should be column width.
Vertical splitters methods are should be like:

int mouseUp(int x, int y, int button, boolean ctrl, boolean shift)
{
    int ret;

    ret = super(x, y, button, ctrl, shift);

    Return _VerticalSplitter.mouseUp(x, y, button, ctrl, shift);
}

int mouseMove(int x, int y, int button, boolean ctrl, boolean shift)
{
    int ret;

    ret = super(x, y, button, ctrl, shift);

    Return _VerticalSplitter.mouseMove(x,y,button,ctrl,shift);
}

int mouseDown(int x, int y, int button, boolean ctrl, boolean shift)
{
    int ret;

    ret = super(x, y, button, ctrl, shift);

    Return _VerticalSplitter.mouseDown(x, y, button, ctrl, shift);
}

Arrangements for horizontal splitter:
I'll just tell differences with vertical.Different parameters:
Columns = 1 for splitters parent control.
Height = 5
Width =Column Width

Upper groups name is groupTOP for our sample. Height value may be like 100-200 and Width value is Column width. Methods names different; instead of _VerticalSplitter, _HorizontalSplitter. Lower groups Width value is Column Width and Height value is Column Height.

1 Aralık 2011 Perşembe

AXAPTA Edit data group dropped to form from table?

For add new field you have to do  AutoDataGroup = NO. For change or delete make DataGroup to empty.

AXAPTA work with address tables

We have to join some tables. In my sample we will try to get a vendors address:

    DirPartyAddressRelationShip         dirPartyAddressRelationShip;
    DirPartyAddressRelationShipMapping  dirPartyAddressRelationShipMapping;
    Address                             address;
    ;
                    select firstonly * from DirPartyAddressRelationShip join
                        dirPartyAddressRelationShipMapping join address
                        order by
                            DirPartyAddressRelationShip.Shared desc,
                            DirPartyAddressRelationShip.IsPrimary desc
                        where DirPartyAddressRelationShip.PartyId == VendTable::find(vendPAckingSlipJour.OrderAccount).PartyId &&
                              dirPartyAddressRelationShipMapping.PartyAddressRelationshipRecId == dirPartyAddressRelationShip.RecId &&
                              address.RecId == dirPartyAddressRelationShipMapping.AddressRecId &&
                              address.type == AddressType::Service;

DirPartyAddressRelationShip table ties source table with (source table in this sample is VendTable) addresses. In this sample VendTable tables PartyId field join with  DirPartyAddressRelationShip table.
Address tables name is  Address. DirPartyAddressRelationShipMapping table is a bridge between DirPartyAddressRelationShip and Address tables. DirPartyAddressRelationShipMapping tables PartyAddressRelationshipRecId field related with DirPartyAddressRelationShip tables RecId field. DirPartyAddressRelationShipMapping tables AddressRecId field related with Address tables RecId field.

DirPartyAddressRelationShip tables Shared field is shown in vendor form addres tab->general field, IsPrimary alanı da Birincil alanına denk geliyor. Type field is address types combobox. I wanted to get service address so selected as AddressType::Service enum value.

AXAPTA icase equivalent in axapta and today

b = a > SystemDateGet() ? "büyük" : "küçük"

AXAPTA TC Id field

This is just about turkish users:

There is an extended data type for TC id: IdentityNum_TR . But it's lenght incorrect (12). Should be 11.

AXAPTA refreshing records

    repairjournal_ds.research();
    repairjournal_ds.refresh();

If you want to stay at current record:

    repairjournal_ds.research(true);
    repairjournal_ds.refresh();

30 Kasım 2011 Çarşamba

AXAPTA empty InventDimId

InventDimId is a mandatory field for some tables. If you don't use it, you can left it blank:

SMAServiceBOMTable.InventDimId  = InventDim::inventDimIdBlank();

AXAPTA Query: empty records, not empty records, use enum at query value

We have to put parameters when we want to do a complex addrange  at query. But it's not necessary with some situations:

Not empty records:
    qbds.addRange(fieldnum(custtable,name)).value(sysquery::valueNotEmptyString()) ;
Empty records:
    qbds.addRange(fieldnum(custtable,name)).value(sysquery::valueEmptyString()) ;
Records are not equal 10000:
    qbds.addRange(fieldnum(custtable,accountnum)).value(sysquery::valueNot("10000") ) ;
Ranges:
    qbds.addRange(fieldnum(custtable,accountnum)).value(queryrange("1","10000") ) ;
Enum:
            qbds.addRange(fieldnum(B_RMARequestTable, RefType)).value(queryvalue(B_RMAReqRefType::RepairJournal));

AXAPTA use number sequence at insert record with code

B_RepairJournalPartsChange.ChangeNo               = NumberSeq::newGetNum(SMAParameters:: numRefRMARequestNo()).num();

AXAPTA create number sequence

First we have to find table and class for our module. Mine is SMAParameters .

My table is SMAParameters (It can find from modules parameters screen, setup tab). Add this method to table:

public server static NumberSequenceReference numRefRMARequestNo()
{
    return NumberSeqReference::findReference(
            typeid2extendedtypeid(typeid(B_RMARequestNo)));
}


Update for AX 2012:

Instead of

typeid2extendedtypeid(typeid(B_RMARequestNo))

write

extendedTypeNum(SNBmasterNum) 


"B_RMARequestNo" at here is our tables key field's extended data type.


After that we have to go number sequence class. Related class name can be find from parameter table's numberSeqModule method. Write this code part at loadModule method:

    /* RMA Request No */
    numRef.DataTypeId              = typeId2ExtendedTypeId(typeid(B_RMARequestNo));
    numRef.ConfigurationKeyId      = configurationkeynum(SMAManagement);
    numRef.ReferenceHelp           = literalstr("My Number Seq...");
    numRef.WizardContinuous        = false;
    numRef.WizardFetchAheadQty     = 20;
    numRef.WizardManual            = NoYes::No;
    numRef.WizardAllowChangeDown   = NoYes::No;
    numRef.WizardAllowChangeUp     = NoYes::No;
    numRef.SortField               = 11;
    this.create(numRef);

Put upper numberseqs sortfield value +1 to Sortfield field.

Update for AX 2012:
Before launch wizard run this command

NumberSeqApplicationModule::loadAll();

Then Basic->Setup->Number sequences->Number sequences. Run Wizard.

Look service parameters->number sequence tab at related modules menu for check.

Now we can use this number sequence at forms...

Add this line at forms classDeclaration method:

NumberSeqFormHandler numberSeqFormHandler;

Add this method to form:

NumberSeqFormHandler numberSeqFormHandler()
{
    ;
    if (!numberSeqFormHandler)
    {

        numberSeqFormHandler = NumberSeqFormHandler::newForm(SMAParameters::numRefRMARequestNo().NumberSequence,
                                                             element,
                                                             B_RMARequestTable_DS,
                                                             fieldnum(B_RMARequestTable,RequestNo));
    }
    return numberSeqFormHandler;
}

Update: Upper line should be like below for AX 2012:

numRefRMARequestNo().NumberSequenceId 
instead of
numRefRMARequestNo().NumberSequence


B_RMARequestTable_DS is my tables datasource. RequestNo is key field.

Form close method:

public void close()
{
    if (numberSeqFormHandler)
    {
        numberSeqFormHandler.formMethodClose();
    }
    super();
}

Data source methods:

public void linkActive()
{
    ;
    element.numberSeqFormHandler().formMethodDataSourceLinkActive();
    super();
}

public boolean validateWrite()
{
    boolean ret;

    ret = super();
    ret = element.numberSeqFormHandler().formMethodDataSourceValidateWrite(ret) && ret;

    return ret;
}

public void write()
{
    super();
    element.numberSeqFormHandler().formMethodDataSourceWrite();
}

public void delete()
{
    ttsbegin;
    element.numberSeqFormHandler().formMethodDataSourceDelete();
    super();
    ttscommit;
}

public void create(boolean _append = false,boolean extern = false)
{
    element.numberSeqFormHandler().formMethodDataSourceCreatePre();
    super(_append);
    if (!extern)
    {
        element.numberSeqFormHandler().formMethodDataSourceCreate();
    }
}

That's all..

AXAPTA get unit id for an item

Unit id for items aren't saving item table. There is a table named InventTableModule for unit id:


MyTable.UnitID  = InventTableModule::find(Sobj.ItemId,ModuleInventPurchSales::Invent).UnitId;

Second parameter is which module you looking for.

AXAPTA create sequential number

There is "Number Sequence class" for creating numbers for Axapta tables. But if you just want to create a "next number" function for just a string? Like "AB0001"->"AB0002". There isn't any built-in function in Axapta for that or I couldn't find. This is my number generator function:

Static str AutoNum(str Num)
{
    int i;
    int bas;
    str b;
    ;
    for (i=StrLen(num);i>=1;i--)
    {
        b=SubStr(num,i,1);
        if (b<"0" || b>"9")
        {
            bas=i+1;
            Break;
        }
        if (i==1)
            bas=1;
    }
    num=StrReplace(SubStr(num,1,bas-1)+
    Num2Str(Str2Num( SubStr(num,bas,StrLen(num) - bas + 1) )+1,
        StrLen(num) - bas + 1,0,1,0)," ","0");
    Return num;
}

AXAPTA modified method sometimes works twice

If combobox value selected with lookup and there is a data source refresh in method modified, modified method works twice. Another person faced this situation and unfortunately had not found a solution.
public void modified()
{
    ;
    super();
    info("111");
    salesLine_DS.research(true);
    salesLine_DS.refresh();
    info("222");
}
I found a solution for that and applined mine. I put a variable at class declaration method and:

public void modified()
{
    ;
    super();
    if (hasChanged) //for twice call bugfix.
        return;
    hasChanged=true;
    info("111");
    salesLine_DS.research(true);
    salesLine_DS.refresh();
    hasChanged=false;
    info("222");
}

AXAPTA multitable lookup

We can write a lookup and join two tables. So if we want  to show fields from second table at lookup list? Unfortunately Axapta's SysTableLookUp class cannot provide this feature. There is another class derived from SysTableLookUp named  SysMultiTableLookUp.

It's easy download and install it from this link. Sample is below:


public void Agreementlookup(FormControl _formControl, str _filterStr)
{
SMAAgreementTable SMAagreementTable;
Query query = new Query();
SysMultiTableLookup sysTableLookup;
QueryBuildDataSource queryBuildDataSource = query.addDataSource(tablenum(SMAAgreementTable));
QueryBuildDataSource qbds2;
QueryBuildRange qbr;
;

qbds2=queryBuildDataSource.addDataSource(tablenum(B_AgreementCustomerTable));
qbds2.relations(true);

qbr=qbds2.addRange(fieldnum(B_AgreementCustomerTable, num));

qbr.value(strfmt('((Num == "%1") && (AccountCode == TableGroupAll::Table)) || (AccountCode == TableGroupAll::All)',
QueryValue(salesTable.CustAccount)));

sysTableLookup = SysMultiTableLookup::newParameters(_formControl,query);
sysTableLookup.addLookupfield(fieldnum(SMAAgreementTable, AgreementId),1);
sysTableLookup.addLookupfield(fieldnum(SMAAgreementTable, AgreementDescription),1);
sysTableLookup.addLookupfield(fieldnum(SMAAgreementTable, ProjId),1);
sysTableLookup.addLookupfield(fieldnum(B_AgreementCustomerTable, AccountCode),2);
sysTableLookup.addLookupfield(fieldnum(B_AgreementCustomerTable, Num),2);

sysTableLookup.parmQuery(query);
sysTableLookup.performFormLookup();
}

AXAPTA first bug

Today I discovered my first bug:

When classDeclaration method like below occurs that error:
public class FormRun extends ObjectRun{
    FormStringControl       lineAccountField;
    FormStringControl       lineAccountField2;
    NumberSeqFormHandler    numberSeqFormHandler;
    SMAAgreementId          agreementId;
}



When write like this, no error:
public class FormRun extends ObjectRun
{
    FormStringControl       lineAccountField;
    NumberSeqFormHandler    numberSeqFormHandler;
    SMAAgreementId          agreementId;
    FormStringControl       lineAccountField2;
}

29 Kasım 2011 Salı

AXAPTA get value sent by "Goto Main Table Form" button.


This blog copy of my turkish blog in english. I created this because unfortunately Google translator is so terrible with turkish translates even mines... :)
This value accessible by args().record() method, but in case of calling from different table fields extended by, it's a bit hard. You have to put case for every caller table or:


FormStringControl   callerControl   = SysTableLookup::getCallerStringControl(element.args());
;
info(callercontrol.text());



I found this code part from Yakup Kirisci's blog. It just already from standart forms, but I didn't know until see his blog. It worked good for me. Thanks to him...