How to add contacts to customer - microsoft-dynamics

I try to add contacts to a customer, like this:
WHILE (LoopCount <> 0) DO
BEGIN
KlantContact.GET(FirstContact);
KlantContact.VALIDATE(KlantContact."Company No.", BussinesContactName."Contact No."); //'44241';
KlantContact.MODIFY;
MESSAGE(KlantContact.Name);
LoopCount := LoopCount - 1;
FirstContact := INCSTR(FirstContact);
END;
But the problem is that the contacts are not connected with the customer.

You have to use the Contact Business Relation table to link them together. Just check the table structure.
Otherwise, if you set up the Bus. Relation Codes in the Marketing Setup NAV will create a new Contact in the background for Customers, Vendors and Bank Accounts.

If you look in the OnInsert trigger of the Customer Table you can see, that a Method UpdateContFromCust.OnInsert(Rec); is called. Within this Method a new Contact Bus. Relation is inserted.
From Method InsertNewContact (Codeunit 5056), with Cont beeing the contact and RMSetup beeing Marketing Setup
WITH ContBusRel DO BEGIN
INIT;
"Contact No." := Cont."No.";
"Business Relation Code" := RMSetup."Bus. Rel. Code for Customers";
"Link to Table" := "Link to Table"::Customer;
"No." := Cust."No.";
INSERT(TRUE);
END;

Related

Netsuite suiteql how to get all available tables to query?

I am using Postman and Netsuite's SuiteQL to query some tables. I would like to write two queries. One is to return all items (fulfillment items) for a given sales order. Two is to return all sales orders that contain a given item. I am not sure what tables to use.
The sales order I can return from something like this.
"q": "SELECT * FROM transaction WHERE Type = 'SalesOrd' and id = '12345'"
The item I can get from this.
"q": "SELECT * FROM item WHERE id = 1122"
I can join transactions and transactionline for the sale order, but no items.
"q": "SELECT * from transactionline tl join transaction t on tl.transaction = t.id where t.id in ('12345')"
The best reference I have found is the Analytics Browser, https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2021_1/analytics/record/transaction.html, but it does not show relationships like an ERD diagram.
What tables do I need to join to say, given this item id 1122, return me all sales orders (transactions) that have this item?
You are looking for TransactionLine.item. That will allow you to query transaction lines whose item is whatever internal id you specify.
{
"q": "SELECT Transaction.ID FROM Transaction INNER JOIN TransactionLine ON TransactionLine.Transaction = Transaction.ID WHERE type = 'SalesOrd' AND TransactionLine.item = 1122"
}
If you are serious about getting all available tables to query take a look at the metadata catalog. It's not technically meant to be used for learning SuiteQL (supposed to make the normal API Calls easier to navigate), but I've found the catalog endpoints are the same as the SuiteQL tables for the most part.
https://{{YOUR_ACCOUNT_ID}}.suitetalk.api.netsuite.com/services/rest/record/v1/metadata-catalog/
Headers:
Accept application/schema+json
You can review all the available records, fields and joins in the Record Catalog page (Customization > Record Catalog).

Assign a value from Gen Business Posting Group Table

I have got Gen Bus. Posting Group field showing on the subform of Sales Order. This Subform is also showing Location Code for the Line.
I am attempting to write a logic as such that if the User selects Location Code as DROPSHIP, the Gen Bus. Posting Group should be LOCAL-DROPSHIP.
LOCAL-DROPSHIP is a record in the Gen. Bus. Posting Group. I have created it before writing this code. However, it would not assign to my rec.Gen Bus. Posting Group.
Here is the code on the Subform;
modify("Location Code")
{
trigger OnAfterValidate()
var
recLocation: Record Location;
recCustomer: Record Customer;
recSalesLine: Record "Sales Line";
recGenPosting: Record "Gen. Business Posting Group";
begin
recGenPosting.Get('LOCAL-DS');
if rec."Location Code" = 'DROPSHIP' then begin
Message(recGenPosting.Code);
// Validate("Gen. Bus. Posting Group", recGenPosting.Code);
Rec."Gen. Bus. Posting Group" := recGenPosting.Code;
CurrPage.Update();
end;
end;
}
You should do this as Table Extension of Table Sales Line instead of Page Extension. Validate is important in this case.
Extended Sales Line table, on the OnModify trigger, wrote;
if rec."Location Code" = 'DROPSHIP' then begin
If recCustomer.Get(REC."Sell-to Customer No.") THEN begin
if recCustomer."Country/Region Code" = 'CITY' then begin
rec."Gen. Bus. Posting Group" := 'DS';
rec.Modify();
end
end

Item Journal Line Table's Records are getting deleted by itself

I wrote this procedure what will take lines and add them to the Item Journal Line table at the click of a button called "Issue Material"
Here is the procedure;
procedure IssueMaterial(IncomingLine: Record "Purchase Requisition Line")
var
ItemCont: Record "Item Journal Line";
begin
IF ItemCont.FindLast then begin
ItemCont."Line No." := ItemCont."Line No." + 10000;
end;
with ItemCont do begin
ItemCont.Init();
ItemCont."Journal Template Name" := 'Item';
Validate("Item No.", IncomingLine."No.");
ItemCont."Posting Date" := WorkDate();
Validate("Location Code", IncomingLine.Location);
ItemCont."Entry Type" := ItemCont."Entry Type"::"Negative Adjmt.";
ItemCont."Journal Batch Name" := 'Default';
ItemCont."Line No." := IncomingLine."Line No.";
Validate(Quantity, IncomingLine.Quantity);
Validate(Amount, IncomingLine.Amount);
Validate("Unit Cost", IncomingLine."Unit Cost");
ItemCont.Insert();
end;
Message('%1 has been successfully created', IncomingLine."No.");
end;
Here is the code at the Action (Button) I wrote;
trigger OnAction()
var
codeunitcont: Codeunit "Purchase Req. Mgmt.";
PurchReqLine: Record "Purchase Requisition Line";
begin
CurrPage.SetSelectionFilter(PurchReqLine);
IF PurchReqLine.FindSet then
repeat
PurchReqLine.TestField(PurchReqLine.Location);
codeunitcont.IssueMaterial(PurchReqLine);
until PurchReqLine.Next = 0;
end;
It works fine. I get my lines moved to the Item Journal Table, however, once I restart the Tenant or Rebuild and Republish an extension my Item Journal Table loses its records!
Thank you for your advice on what could be the issue
Had code written on the OnOpenPage that was deleting all records of Item Journal Line! Ops.

How to setup automatically increase my primary key in Microsoft Dynamics NAV

I am beginner in Microsoft Dynamic NAV.
So, I want to find out how to make my Primary Key in my table to be auto increment.
For example I have table with next Line: "Field No.= 10" "Field name = Code" -"Type = Code" "Lenght = 10";
I want that every time someone puts a new user, that my code to increase by 10. Can someone help me?
Normally you would create a no. series.
In your code you should use the standard NAV codeunit NoSeriesManagement (396).
This code would look something like:
NewNo := gCuNoSeriesMngmnt.GetNextNo('No series code',WORKDATE,TRUE);
If you want your table to always increment with 10 you will have to add some code in your table's OnInsert trigger.
I'm assuming that Field No. is your Primary Key
// if there is no "Field No." assigned yet
IF "Field No." = '' THEN BEGIN
// Get the last entry in the table and assign the next number to Field No.
<RecordVariableOfTable>.FINDLAST;
"Field No." := <RecordVariableOfTable>."Field No." + 10;
END;
RecordVariableOfTable has to be defined as a Record instance of your table in your C/AL global variables for the table.
If this is your master table and Field No. is your Primary Key
Then you should implement No. Series functionality on your table and assign a No. Series which increments by 10.

Dynamics NAV Sales Invoice Report: Modifying the SalesShipmentBuffer and findPostedShipmentDate()

I have recently modified the default report 207 Sales Invoice and created a custom report. One request I had, was to display Shipment information on this invoice for each shipment that the current invoice covers. Particularly I needed to show the following values:
Shipment No. from the Delivery Note
Quantity of the Shipment
The standard report only shows the posted shipment date and the quantity and that only for one shipment (I believe its the last one). It does not show the "Shipment No.".
When making the required changes I had several problems and it was difficult to find any information about the findPostedShipmentDate() function, so I have decided to post my solution here.
The question would be:
How can I show multiple shipments on the sales invoice?
How can I show the "Shipmente No." and the individual quantities or each shipment?
In the function "Sales Invoice Line"::OnAfterGetRecord() the function findPostedShipmentDate() is called. This function performs several checks to find the posting date and if necessary calls the function GenerateBufferFromValueEntry() which fills the table "SalesShipmentBuffer" which temporary stores all the shipment information for the current "Sales Invoice Line" record:
CASE "Sales Invoice Line".Type OF
"Sales Invoice Line".Type::Item:
GenerateBufferFromValueEntry("Sales Invoice Line");
...
Afterwards it performs several checks on the buffer table and deletes the entries again (more on this later).
In the GenerateBufferFromValueEntry the following happens. The actual shipment information is stored in the ItemLedgerEntry (32) table, in order to find the correct rows, the ValueEntry (5802) table is used, which contains the right key (ValueEntry."Item Ledger Entry No.").
Additionally to the existing range filters I needed to add one line to limit the range to the Item No. and I had to remove the range filter for the Entry No.. I also modified the function AddBufferEntry to additionally take the ItemLedgerEntry."Document No." which contains the Shipment No. from the delivery note. Here is the complete code:
TotalQuantity := SalesInvoiceLine2."Quantity (Base)";
ValueEntry.SETCURRENTKEY("Document No.");
ValueEntry.SETRANGE("Document No.",SalesInvoiceLine2."Document No.");
ValueEntry.SETRANGE("Posting Date","Sales Invoice Header"."Posting Date");
ValueEntry.SETRANGE(ValueEntry."Item No.", "Sales Invoice Line"."No."); //Added
ValueEntry.SETRANGE("Item Charge No.",'');
//ValueEntry.SETFILTER("Entry No.",'%1..',FirstValueEntryNo); //Removed
IF ValueEntry.FIND('-') THEN BEGIN
REPEAT
IF ItemLedgerEntry.GET(ValueEntry."Item Ledger Entry No.") THEN BEGIN
IF SalesInvoiceLine2."Qty. per Unit of Measure" <> 0 THEN
Quantity := ValueEntry."Invoiced Quantity" /
SalesInvoiceLine2."Qty. per Unit of Measure"
ELSE
Quantity := ValueEntry."Invoiced Quantity";
AddBufferEntry(
SalesInvoiceLine2,
-Quantity,
ItemLedgerEntry."Posting Date",
ItemLedgerEntry."Document No."); //Added
TotalQuantity := TotalQuantity + ValueEntry."Invoiced Quantity";
END;
FirstValueEntryNo := ValueEntry."Entry No." + 1;
UNTIL (ValueEntry.NEXT = 0) OR (TotalQuantity = 0);
END;
In the FindPostedShipmentDate function I also deleted the first couple of lines to prevent the function from exiting before the SalesShipmentBuffer had been filled:
IF "Sales Invoice Line"."Shipment No." <> '' THEN
IF SalesShipmentHeader.GET("Sales Invoice Line"."Shipment No.") THEN
EXIT(SalesShipmentHeader."Posting Date");
IF "Sales Invoice Header"."Order No."='' THEN
EXIT("Sales Invoice Header"."Posting Date");
In the last part of FindPostedShipmentDate the records of SalesShipmentBuffer are deleted again, which might seem a bit strange at first (it did to me). The reason is that even after the DELETE and DELETEALL functions are called, the record variable retains the values that were stored in the record before calling these functions. So the result is that the SalesShipmentBuffer tables is cleared, such that its empty for the next run of the findPostedShipmentDate() function, but the values can still be used in the report. The other confusing part is that actually does a very simple thing which is made to look very complicated. If the buffer contains only one line it deletes the line, otherwise if it contains more than one line it first calculates the sum of all quantity fields and then deletes all lines.
Anyway, I only had to make one change, which was removing the following line since the Document No. field now contains the Shipment No. and not the Invoice No.
SalesShipmentBuffer.SETRANGE("Document No.","Sales Invoice Line"."Document No.");
Here is the remaining code:
SalesShipmentBuffer.SETRANGE("Line No." ,"Sales Invoice Line"."Line No.");
SalesShipmentBuffer.SETRANGE("No." ,"Sales Invoice Line"."No.");
IF SalesShipmentBuffer.FIND('-') THEN BEGIN
SalesShipmentBuffer2 := SalesShipmentBuffer;
IF SalesShipmentBuffer.NEXT = 0 THEN BEGIN
SalesShipmentBuffer.GET(
SalesShipmentBuffer2."Document No.",
SalesShipmentBuffer2."Line No.",
SalesShipmentBuffer2."Entry No."
);
SalesShipmentBuffer.DELETE;
EXIT(SalesShipmentBuffer2."Posting Date");
END ;
SalesShipmentBuffer.CALCSUMS(Quantity);
IF SalesShipmentBuffer.Quantity <> "Sales Invoice Line".Quantity THEN BEGIN
SalesShipmentBuffer.DELETEALL;
EXIT("Sales Invoice Header"."Posting Date");
END;
END ELSE
EXIT("Sales Invoice Header"."Posting Date");
That's it. Now I just had to modify the DataItem source entries to show the necessary fields on the report.
The changes I had to make are actually not so many, but the code is not documented at all and I could not find much information when I googled for this problem or the functions involved. So I hope my post is helpful, in case someone has a similar problem than I had.
On a side not, I do not understand why the above code segment is written in such a wired why, I belive (though I have not verified this), that it could be simplified into the following code, which is much more readable. Please leave comments if you feel that my analysis is wrong.
SalesShipmentBuffer.SETRANGE("Line No." ,"Sales Invoice Line"."Line No.");
SalesShipmentBuffer.SETRANGE("No." ,"Sales Invoice Line"."No.");
SalesShipmentBuffer.FINDFIRST;
IF SalesShipmentBuffer.COUNT > 0 THEN BEGIN
IF SalesShipmentBuffer.COUNT = 1 THEN BEGIN
SalesShipmentBuffer.DELETE;
EXIT(SalesShipmentBuffer2."Posting Date");
END ELSE BEGIN
SalesShipmentBuffer.CALCSUMS(Quantity);
SalesShipmentBuffer.DELETEALL;
EXIT("Sales Invoice Header"."Posting Date");
END;
END ELSE
EXIT("Sales Invoice Header"."Posting Date");