How to set InventLocation address by code? - axapta

I have to set the InventLocation Address by code.
By code I know how to find the InventLocation address, but if it isn't there I want to create it.
My code is this :
InventLocation invLocationTable;
LogisticsPostalAddress logisticsPostalAddress;
invLocationTable = InventLocation::find(MYinvLocationId);
logisticsPostalAddress = LogisticsLocationEntity::findPostalAddress(invLocationTable, LogisticsLocationRoleType::None);
if (!logisticsPostalAddress )
{
// Here I want to create/set the InventLocation adress, but I don't know how to do it.
}
Is it possible to create it?

Your InventLocation.RecId will need to be referenced in InventLocationLogisticsLocation.Location, so obviously this table will need to have a record inserted. The next reference will be in InventLocationLogisticsLocationRole.LocationLogisticsLocation, again you will need a record. Finally, the reference in LogisticsPostalAddress.Location.
So first insert into InventLocationLogisticsLocation, then into InventLocationLogisticsLocationRole, finally into LogisticsPostalAddress.
Also make sure that you have a Role Type defined as None, otherwise you might be missing the record in LocationPostalAddress.

Related

Datasource Paging Issue (Revised Again)

See Datasource Paging Issue (Revised)
for the original question.
Markus, you were kind enough to help with out with the issue of incorporating a record count into a query using a calculated datasource. I have a search form with 15 widgets - a mix of date ranges, dropdowns, text values and ._contains, ._equals, ._greaterThanOrEquals, ._lessThanOrEquals, etc.
I have tested this extensively against mySQL SQL code and it works fine.
I have now added a 16th parameter PropertyNames, which is a list with binding #datasource.query.filters.Property.PropertyName._in and Options blank. The widget on the form is hidden because it is only used for additional filtering.
Logic such as the following is used, such that a particular logged-in user can only view their own properties. So if they perform a search and the Property is not specified we do:-
if (params.param_Property === null && canViewAllRecords === false) {
console.log(params.param_PropertyNames); // correct output
ds.filters.Property.PropertyName._in = params.param_PropertyNames;
}
The record count (records.length) is correct, and if I for loop through the array of records the record set is correct.
However, on the results page the table displays a larger resultset which omits the PropertyNames filter. So if I was to search on Status 'Open' (mySQL results 50) and then I add a single value ['Property Name London SW45'] for params.param_PropertyNames the record count is 6, the records array is 6 but the datasource display is 50. So the datasource is not filtering on the property array.
Initially I tried without adding the additional parameter and form widget and just using code such as
if (params.param_Property === null && canViewAllRecords === false) {
console.log(params.param_PropertyNames); // correct output
ds.filters.Property.PropertyName._in = properties; // an array of
properties to filter out
}
But this didn't work, hence the idea of adding a form widget and an additional parameter to the calculated recordcount datasource.
If I inspect at query.parameters then I see:-
"param_Status": "Open",
"param_PropertyNames": ["Property Name London SW45"],
If I inspect query.filters:-
name=param_Status, value=Open
name=param_PropertyNames, value=[]}]}
It looks as though the filter isn't set. Even hard coding
ds.filters.Property.PropertyName._in = ['Property Name London SW45'],
I get the same reuslt.
Have you got any idea what would be causing this issue and what I can do for a workaround ?
Using a server side solution I would suggest editing both your SQL datasource query script (server side) that is supposed to filter by this property list and including the same code in your server side script for your calculated Count datasource. The code would look something like this, not knowing your exact details:
var subquery = app.models.Directory.newQuery();
subquery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
subquery.prefetch.Property._add();
var results = subquery.run();
if(!results[0].CanViewAllRecords) {
query.filters.Property.PropertyName._in = results[0].Property.map(function(i) {return i.PropertyName;});
}
By adding this code you are filtering your directory by your current user and prefetching the Property relation table, then you set the filter only if your user canviewallRecords is false and use JS map function to create an array of the PropertyName field in the Property table. As I stated, your code may not be exactly the same depending on how you have to retrieve your user canviewallrecords property and then of course I don't know your relation between user and Property table either, is it one-to-many or other. But this should give you an idea how to implement this on server side.

How to check if the data already exists on the table?

I have a table named users two of it's fields are login and name.
These fields can not be duplicate.
When the user are going to create a new registration, I already made a method to check if that name/login already exists or not.
But the user can also edit his login/name.
When the user enters on the page to edit his registration data, it already fills the fields with the current data.
I have 3 fields [NAME] [EMAIL] [LOGIN].
The user can edit only 1 of this or all of them at once ...
How may I create a method to check if that name/loginalready exists or not when he try to edit it ? Maybe a Query ? A select count on the login then on the name field ?
--UPDATE--
here's my solution
You should leave this up to the database system you are using and handle any errors it may throw. All database systems (Access, Oracle, MS SQL, etc) allow you to mark a table field as being Unique. This means that the table can only hold one records with a field(s) with that value. If you try to add more than one record with a same field, you will be thrown an error. Your application should catch that error and alert the user. If you post what kind of database system you are using I can show you how to do this.
Edit:
Heres is an example. This uses the SqlClient.SqlException exception class. I'm not sure what the error code is for unique constraints but I added a variable in the catch that you can place a break point on to get. Just change the if statement to match that error code:
Try
'your database insert attempt here
Catch ex As SqlClient.SqlException
Dim sqlErrorNumber = ex.ErrorCode
If (sqlErrorNumber = 1) Then
Me.lblWarning.Text = "Please select a unique ID"
Me.lblWarning.Visible = True
Me.lblWarning.ForeColor = Drawing.Color.Red
Me.lblWarning.Font.Bold = True
End If
End Try
In the aspx page:
<asp:Label ID="lblWarning" runat="server" Visible="false"></asp:Label>
What is your primary key here?
I suggest, Adding new identity column to your table will solve the issue.
You can edit the row based on the identity column.
For Ex: Add [UserID] field and made it unique identity column. then perform update operations based on userid in where clause.
You should use unique fields in your database, try catch in php or asp and ajax technique to get sure that the fields are filled or not with the informations that are you typing now.
if you don't have a column ID or if username column is not set like UNIQUE you can use this function :
function user_exists($username){
$username = sanitize($username); // sanitize
return(mysql_result(mysql_query("SELECT COUNT(user_name) FROM user_table WHERE username = '$username'"), 0) == 1) ? true : false;
}
For New Members / Or if current User wants to edit his username
this function return false if username is not registered and true if it is

vb.net alternative to select case when dealing with object values

Hey all, I was able to do this via a SELECT CASE statement, however I'm always trying to improve my code writing and was wondering if there was a better approach. Here's the scenario:
Each document has x custom fields on it.
There's y number of documents
However there's only 21 distinct custom fields, but they can obviously have n different combinations of them depending on the form.
So here's what I did, I created an object called CustomFields like so:
Private Class CustomFields
Public agentaddress As String
Public agentattorney As String
Public agentcity As String
Public agentname As String
Public agentnumber As String
Public agentstate As String
Public agentzip As String
... more fields here ....
End Class`
Then I went ahead and assigned the values I get from the user to each of those fields like so:
Set All of Our Custom Fields Accordingly
Dim pcc As New CustomFields()
pcc.agentaddress = agent.address1
pcc.agentattorney = cplinfo.attorneyname
pcc.agentcity = agent.city
pcc.agentname = agent.agencyName
pcc.agentnumber = agent.agentNumber
pcc.agentstate = agent.state
pcc.agentzip = agent.zip ....other values set to fields etc.
Now the idea is based upon what combo of fields come back based upon the document, we need to assign the value which matches up with that custom field's value. So if the form only needed agentaddress and agentcity:
'Now Let's Loop Through the Custom Fields for This Document
For Each cf As vCustomField In cc
Dim customs As New tblCustomValue()
Select Case cf.fieldname
Case "agentaddress"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
Case "agentcity"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
End Select
_db.tblCustomValues.InsertOnSubmit(customs)
_db.SubmitChanges()
This works, however we may end up having 100's of fields in the future so there a way to somehow "EVAL" (yes I know that doesn't exist in vb.net) the cf.fieldname and find it's corresponding value in the CustomFields object?
Just trying to write more efficient code and looking for some brainstorming here. Hopefully my code and description makes sense. If it doesn't let me know and I'll go hit my head up against the wall and try writing it again.
If I am reading your question correctly, you are trying to avoid setting the value of fields, when the field isn't used. If so, I would recommend you just go ahead and set the field to nothing in that case.

Dynamics Ax: Alert when any record changes

I want to send an alert in Ax, when any field in the vendor table changes (and on create/delete of a record).
In the alert, I would like to include the previous and current value.
But, it appears that you can't set alerts for when any field in a table changes, but need to set one up for EVERY FIELD?! I hope I am mistaken.
And how can I send this notification to a group of people
I have created a new class with a static method that I can easily call from any .update() method to alert me when a record changes, and what changed in the record.
It uses the built in email templates of Ax as well.
static void CompareAndEmail(str emailTemplateName, str nameField, str recipient, Common original, Common modified)
{
UserInfo userInfo;
Map emailParameterMap = new Map(Types::String, Types::String);
str changes;
int i, fieldId;
DictTable dictTable = new DictTable(original.TableId);
DictField dictField;
;
for (i=1; i<=dictTable.fieldCnt(); i++)
{
fieldId = dictTable.fieldCnt2Id(i);
dictField = dictTable.fieldObject(fieldId);
if (dictField.isSystem())
continue;
if (original.(fieldId) != modified.(fieldId))
{
changes += strfmt("%1: %2 -> %3 \n\r",
dictField.name(),
original.(fieldId),
modified.(fieldId)
);
}
}
//Send Notification Email
select Name from UserInfo where userInfo.id == curUserId();
emailParameterMap.insert("modifiedBy", userInfo.Name);
emailParameterMap.insert("tableName", dictTable.name());
emailParameterMap.insert("recordName", original.(dictTable.fieldName2Id(nameField)));
emailParameterMap.insert("recordChanges", changes);
SysEmailTable::sendMail(emailTemplateName, "en-us", recipient, emailParameterMap);
}
Then in the .update() method I just add this one line
//Compare and email differences
RecordChangeNotification::CompareAndEmail(
"RecChange", //Template to use
"Name", //Name field of the record (MUST BE VALID)
"user#domain.com", //Recipient email
this_Orig, //Original record
this //Modified record
);
The only things I want to improve upon are:
moving the template name and recipient into a table, for easier maintenance
better formatting for the change list, I don't know how to template that (see: here)
As you have observed the alert system is not designed for "any" field changes, only specific field changes.
This is a bogus request anyway as it would generate many alarts. The right thing to do is to enable database logging of the VendTable table, then send a daily report (in batch) to those interested.
This is done in Administration\Setup\Database logging. There is a report in Administration\Reports. You will need to know the table number to select the table.
This solution requires a "Database logging" license key.
If you really need this feature, then you can create a class that sends a message/email with the footprint of the old record vs the new record. Then simply add some code in the table method "write"/"update"/"save" to make sure you class is run whenever vendtable gets edited.
But I have to agree with Jan. This will generate a lot of alerts. I'd spend some energy checking if the modifications done in vendtable are according to the business needs, and prohibit illegal modifications. That includes making sure only the right people have enough access.
Good luck!
I do agree with suggestion of Skaue.you just write and class to send the mail of changes in vend table.
and execute this class on update method of vendtable.
thanks and Regards,
Deepak Kumar

How to find a table name by ID in Dynamics AX

Each table in the AOT has an ID, how can I discover the table name given an ID?
Looking at the SQL dictironary is really the correct method.
Search for the line with the FieldId equal to 0.
Using TSQL this will tell the name of the table for tableid 505
select NAME
from SQLDICTIONARY
where TABLEID = 505
and FIELDID = 0
From X++, use the tableId2Name function.
From the GUI, choose Tools/Development tools/Application objects/Application objects and filter for a recordType of TableInternalHeader and a parentId of the table id you are looking for.
Or in the AOT, right click on Tables and choose Find. On the Name & Location tab, set Search to All nodes. On the Properties tab click Selected next to ID and fill in the table id in the Range field.
I dont'know if this is your answer,
if you want to give the TableName with his ID, you can use
the method:
str tableId2Name(int _tableid)
For example:
If YourTable has ID :123456 ;
use method
tableId2PName(123456)
will return the str name YourTable.
info(strFmt("%1" , tableId2PName(123456))); -> VideoStamp the name.
I used the info in
https://msdn.microsoft.com/en-us/library/aa498759.aspx
I hope to useful , greetings!
If you need AX system table name you can use tableId2name or DictTable.name method.
If you need SQL table name you shoud use DictTable.name method with first argument of DbBackend::Sql
Example:
print new DictTable(tableNum(DirPartyTable)).name(DbBackend::Sql);
print new DictTable(tableNum(OMOperatingUnit)).name(DbBackend::Sql);
pause;
// result:
// DIRPARTYTABLE
// DIRPARTYTABLE
Or you can try:
select Name, AxId
from MicrosoftDynamicsAX_model.dbo.ModelElement (nolock)
where ElementType = 44
order by AxId
In the AOT, go to the System Documentation node.
In the Tables node, find SqlDictionary and open it with the table browser.
Filter the column TabId with your ID.
In the AOT, go to the System Documentation node. In the Tables node, find SqlDictionary and right click and open it with the table browser. Filter the column TabId with your ID and fieldid == 0, his will give you the name of the table.
Easiest way:
Create a project (not necessary but easier to delete later)
Add a new view to your project
Add the data source SqlSyncInfo
Drag the fields ID, MessageType, SyncTable, TableName, etc to field
Open the view
It provides all the table names and their respective IDs. Just filter what you want. If you know the table ID, search for that. If you know the table name, search for that.

Resources