Create Sales Order

NavDaveNavDave Member Posts: 13
edited 2013-01-23 in NAV Three Tier
Hi all,

Does anyone here know where I can find example or good documentation on how to create
a sales order using C/AL?

Thank you and Happy Holidays ! :)

Dave

Comments

  • bbrownbbrown Member Posts: 3,268
    Look at some of the standard functions, such as "Create Order" from Quote. There's others.
    There are no bugs - only undocumented features.
  • MarkHamblinMarkHamblin Member Posts: 118
    If you're in 2013, you could also look at the unit test library provided by MS - has wrapper functions for common tasks like creating sales orders. See http://blogs.msdn.com/b/nav/archive/2012/11/07/application-test-toolset-for-microsoft-dynamics-nav-2013.aspx

    (To be clear, not recommending you deploy the library with your solution just so you can create sales orders, I'm suggesting you look at the code to see how they do it).
  • NavDaveNavDave Member Posts: 13
    That would have been handy but i'm working on nav 2009.

    As the previous post suggested, I'm following the code found in the code unit for creating a sales order from a sales quotes. But the issue now is the roll back thing. I can't do a full rollback if my sales order routine are scattered into many code units because of the COMMIT called by Navy after a transaction...
  • deV.chdeV.ch Member Posts: 543
    Do NOT create sales order like in the test unit framework! If you read the documentation about it, it states that the test unit framework does only create the data necessary for a certain test, therfore you can not be sure you get a proper order. I would recommend look at productive code if you don't know how to do create certain data in NAV.

    Here the best practices http://blogs.msdn.com/b/nav/archive/2012/12/03/writing-unit-tests-in-c-al.aspx?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+MicrosoftDynamicsNavTeamBlog+%28Microsoft+Dynamics+NAV+Team+Blog%29
    The PRE-CONDITIONS step:
    2.Create only data (records and fields in the records) that is needed for the test.
    a.Setup data should be created using direct assignments only (":="). Using VALIDATEs and other C/AL procedures to set up data may go through unnecessary paths unrelated to the test.
    b.Avoid using any functions or triggers in the production code, as that results in "usually" doing more than what the unit test requires. For example, in a case where items need to be sold from inventory, the items may be added by two approaches:

    Avoid
    Making a posting through item journals

    Prefer
    Create item ledger entries directly

    c.It may not be necessary to fill in all primary key fields, as they may not be relevant to the test. For example, in a test that checks the OnValidate trigger on the Location Code field of the Sales Line table, it may not be necessary to put in the Document No. field.
    d.Because of the above reasons, the use of libraries to create business data should be limited, as they tend to make "complete" business entities. Instead, use LOCAL helper functions to promote reuse of code. As an example, some unit tests may only need (as part of their setup data) an Item record, with the Base Unit of Measure field filled in. Although the base unit of measure for any item should always have Qty. per Unit of Measure set to 1, this requirement may be irrelevant in case of such unit tests.

    LOCAL PROCEDURE CreateItemWithBaseUOM@1(VAR Item@1000 : Record 27);
    VAR
    ItemUnitOfMeasure@1001 : Record 5404;
    BEGIN
    Item.INIT;
    Item.INSERT;
    ItemUnitOfMeasure.Code := 'NewCode';
    ItemUnitOfMeasure.INSERT;
    Item."Base Unit of Measure" := ItemUnitOfMeasure.Code;
    Item.MODIFY;
    END;

    e.Improving the performance:
    A.Database changes should be limited to only INSERTs. MODIFY calls should be avoided. This limits the number of database calls and speeds up the test. For example, in order to create an item with a base unit of measure:

    Avoid
    The helper function to create an item record in the previous example.
    Prefer

    LOCAL PROCEDURE CreateItemWithBaseUOM@1(VAR Item@1000 : Record 27);
    VAR
    ItemUnitOfMeasure@1001 : Record 5404;
    BEGIN
    Item.INIT;
    ItemUnitOfMeasure.Code := 'NewCode';
    ItemUnitOfMeasure.INSERT;
    Item."Base Unit of Measure" := ItemUnitOfMeasure.Code;
    Item.INSERT;
    END;

    B.Taking the approach of minimal data creation, calls to INSERT(TRUE) should be avoided. Plain INSERTs should be used as in the above example.
    C.In case record variables are to be passed to helper functions, it is recommended to pass them as VARs even though the records themselves are not going to be altered. This is faster than passing the record variables as
    values.
  • MarkHamblinMarkHamblin Member Posts: 118
    Nobody said anything about using the exact code from the unit tests. The unit tests are an excellent way to learn what logic needs to be employed to achieve a certain result. The actual code may be of limited use depending on how the test is implemented, but it beats scrolling through a ton of system code to figure out the minimum required fields and/or the logic of an operation.

    In addition to being invaluable for doing regression testing on customizations, the unit test framework can also be a great learning tool for people trying to figure NAV out.
  • David_CoxDavid_Cox Member Posts: 509
    Worst advice I have seen, as the OP is looking for a simple example to create as Sales Order not a unit test, they should be using example code that is complete and uses standard validation routines only ](*,)
    Avoid
    Making a posting through item journals

    Prefer
    Create item ledger entries directly

    How could you unit test say "Item Application" without using the posting routines, these will create many needed database records, insert an 'item ledger entry' directly would be no good, as you have lost all the validation and supporting data for a true unit test?

    In my own personal opinion, developers should never Create item ledger entries directly (Item Ledgers have 'Value Entries', "Ledger Entry Dimensions" as well), it takes less time to populate and post a journal than write the code, and validation makes sure all support data including dimensions and tables are in place, this will reduce the chances of errors later when posting.

    As the OP wants to create Sales Orders, all they need to do is to create a manual order write down the fields they populate, then do the same in code.

    Example:
    SalesHeader.INIT;
    SalesHeader."Document Type" := SalesHeader."Document Type"::Order;
    SalesHeader."No." := ''; 
    SalesHeader.INSERT(TRUE);
    SalesHeader.VALIDATE("Sell-to Customer No.",CustNo);
    SalesHeader.VALIDATE("Location Code",LocCode);
    SalesHeader.MODIFY;
    
    SalesLine.INIT;
    SalesLine."Document Type" := SalesHeader."Document Type";
    SalesLine."Document No." := SalesHeader."No.";
    NextLineNo := NextLineNo + 10000;
    SalesLine."Line No." := NextLineNo; 
    SalesLine.INSERT(TRUE); //Run Trigger Code
    SalesLine.VALIDATE(Type,SalesLine.Type::Item);
    SalesLine.VALIDATE("No.",ItemNo);
    SalesLine.VALIDATE("Location Code",LocCode);
    SalesLine.VALIDATE("Unit Price",UnitPrice);
    SalesLine.VALIDATE(Quantity,Qty);
    SalesLine.MODIFY(True);
    

    Looking at the code in the sample code function, not a good example for a novice, there is no validation for 'NewCode', nothing to check that 'NewCode' is in the "Unit of Measure" table.

    Confusion is caused here between "Item Unit of Measure" and "Unit of Measure" both are required for the Item record, the "Item No." is not populated in example [-X !

    Table 5404 "Item Unit of Measure" Fields:
    1 Item No. Code 20
    2 Code Code 10
    3 Qty. per Unit of Measure Decimal
    7300 Length Decimal
    7301 Width Decimal
    7302 Height Decimal
    7303 Cubage Decimal
    7304 Weight Decimal

    I would suggest never to 'hard code' not even for testing, if you do add a check in code, like this!

    UOM = Table 204 "Unit of Measure"
    IF NOT UOM.GET('EACH')THEN BEGIN
      UOM.INIT;
      UOM.Code := 'EACH';
      UOM.Description := 'Each';  
      UOM.INSERT;
    END;
    

    This would then be the structure of the sample code
    //Init the item clear the "No." field
    Item.INIT; //The "No." field will retain the last value and error on insert
    Item."No." := ''; //So clear the "No." field to stop the error
    Item.INSERT(TRUE); //Run trigger code to set the item "No."
    
    ItemUnitOfMeasure.INIT;
    ItemUnitOfMeasure."Item No.":= Item."No.";
    ItemUnitOfMeasure.Code := UOM.Code;
    ItemUnitOfMeasure."Qty. per Unit of Measure" := 1;
    ItemUnitOfMeasure.INSERT;
    Item."Base Unit of Measure" := ItemUnitOfMeasure.Code;
    Item.MODIFY;
    

    HTH

    David
    Analyst Developer with over 17 years Navision, Contract Status - Busy
    Mobile: +44(0)7854 842801
    Email: david.cox@adeptris.com
    Twitter: https://twitter.com/Adeptris
    Website: http://www.adeptris.com
  • deV.chdeV.ch Member Posts: 543
    Nobody said anything about using the exact code from the unit tests. The unit tests are an excellent way to learn what logic needs to be employed to achieve a certain result. The actual code may be of limited use depending on how the test is implemented, but it beats scrolling through a ton of system code to figure out the minimum required fields and/or the logic of an operation.

    In addition to being invaluable for doing regression testing on customizations, the unit test framework can also be a great learning tool for people trying to figure NAV out.

    Well but this is the second time i read answers to questions like "how i can i make this in nav" and the answer is have a look at the test framework.
    I think if you recommand this you have to warn the people about the limitations of that code in the test framework. Since these are realy basic questions you have to assume you answer to a beginner so therefore i think it is definitly recommended to tell the whole story...

    just my 2 cents...

    @David Cox: :thumbsup: I totaly agree with you.
  • NavDaveNavDave Member Posts: 13
    Hi all,

    Thanks for the help. I appreciate it.

    I found the solution to the rollback problem which is simply handling the whole called codeunit as such:
    IF NOT Codeunit.RUN THEN...

    This way it will suppress the error message as well as doing a full Rollback to the previous state.

    However, my Sales Order creation routine needs to write to a LOG table which I DON'T want to be affected by the rollback
    in case of an error. Is there a way to COMMIT an "isolate" transaction (e.g. while inserting a log line to a table) ?

    David
Sign In or Register to comment.