Invoking AX web service via C# - web-services

I am trying to connect to the AX web services. What I will do it to fetch the right price given a product and a customer.
I realised the right webservice to use is PriceListServiceClient and I am able to log in to it using windows authentication, but I cannot retrieve any data from it.
Here is my code:
PriceListServiceClient priceListServiceClient = new PriceListServiceClient();
priceListServiceClient.ClientCredentials.Windows.ClientCredential.UserName = "yyy";
priceListServiceClient.ClientCredentials.Windows.ClientCredential.Password = "zzz!";
priceListServiceClient.ClientCredentials.Windows.ClientCredential.Domain = "xxx";
CriteriaElement[] criteriaElement = new CriteriaElement[1];
criteriaElement[0] = new CriteriaElement();
criteriaElement[0].DataSourceName = "SalesPrice";
criteriaElement[0].FieldName = "ItemId";
criteriaElement[0].Operator = Operator.NotEqual;
criteriaElement[0].Value1 = "5637153175";
QueryCriteria queryCriteria = new QueryCriteria();
queryCriteria.CriteriaElement = criteriaElement;
CallContext callContext = new CallContext();
var found = priceListServiceClient.find(callContext, queryCriteria);
Console.WriteLine(found.Currency);
priceListServiceClient.Close();
Any idea about why this is happening?

Try filling in the properties in the CallContext (company and language).
new CallContext { Company = "zzz", Language = "nl" };

I found the answer here: http://community.dynamics.com/ax/f/33/p/118741/246784.aspx
The Ax class for Price List document is AxPriceDiscTmpPrintout Class. This class wraps the TmpPriceDiscPrintout table, which is a TMP table. That's why you are not getting anything in return.

Related

JSON.NET causes System.TypeAccessException in Dynamics CRM

I get the below error when running a custom workflow action that uses JSON.NET to serialize a dynamics object to JSON.
Is there a restriction on using reflection in CRM Dynamics customer workflow activities / plugins?
Is it because I am using dynamic variables?
System.TypeAccessException: Attempt by method
'DynamicClass.CallSite.Target(System.Runtime.CompilerServices.Closure,
System.Runtime.CompilerServices.CallSite, System.Object,
System.String)' to access type
'Newtonsoft.Json.Linq.JObject+JObjectDynamicProxy' failed. at
CallSite.Target(Closure , CallSite , Object , String ) at
WSWA.CRM.Logic.MyobIntegrationLogic.CreateInvoice(Boolean retry) at
WSWA.CRM.Workflows.MyobJob.MyobIntegrationTester.Execute(CodeActivityContext
context)
dynamic account = new JObject();
account.UID = GetAccount("Undeposited Funds Account");
dynamic job = new JObject();
job.UID = GetJob("JFC Interiors");
dynamic gstTaxCode = new JObject();
gstTaxCode.UID = GetTaxUidByCode("GST");
dynamic customer = new JObject();
customer.UID = GetCustomerUid("Bar001.test");
dynamic line1 = new JObject();
line1.Total = 22.55;
line1.Account = account;
line1.Job = job;
line1.TaxCode = gstTaxCode;
dynamic line2 = new JObject();
line1.Total = 23.55;
line1.Account = account;
line1.Job = job;
line1.TaxCode = gstTaxCode;
var lines = new JArray();
lines.Add(line1);
lines.Add(line2);
dynamic invoice = new JObject();
invoice.Date = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
invoice.Customer = customer;
invoice.CustomerPurchaseOrderNumber = "PO Number";
invoice.Number = "INV-1000";
invoice.Lines = lines;
var content = new StringContent(contact.ToString());
content.Headers.ContentType = MediaTypeHeaderValue.Parse("text/json");
var responseTask = httpClient.PostAsync(url, content);
Task.WaitAll(responseTask);
You can't use dynamic types in Dynamics (ironic isn't it?) CRM Sandboxed plugins. You can use reflection, as long as whatever you're reflecting is publicly available. i.e. You can use reflection to get a list of public properties, but you can't get a list of private fields.
You can always farm your work out to an Azure service and do whatever you'd like there.
There are known issues with dynamic types and using NuGet packages like Microsoft.AspNet.WebApi.Client.
Could you try with a WebClient?
More info here

Dynamics GP Web Services: Changing policy behavior at runtime

I am creating a payables invoices using Web Services on GP2013. Optionally, my users can provide line item distributions. I can create invoices, but unless I modify the CreatePayablesInvoice policy to "Distributions Will Be Provided" in the dynamics security console, the invoice gets both the system provided distributions as well as the distribution lines I am creating. I want to have the ability to provide distributions if necessary, otherwise I want the system to handle it.
The documentation suggests I should be able to alter the policy in code, but when I get the policy object back from GetPolicyByOperation, the Behaviors array is empty. I have tried creating the behavior manually in code and it doesn't alter what happens when the invoice is created. The only thing that impacts the result is editing the property in the security console.
My code for altering the the policy is below:
payablesInvoiceCreatePolicy = wsDynamicsGP.GetPolicyByOperation("CreatePayablesInvoice", context);
BehaviorKey bk = new BehaviorKey();
bk.Id = new Guid("e476a157-ecf0-4dae-8cef-317dd2cfbe41");
Behavior b = new Behavior();
b.Key = bk;
BehaviorOption opt0 = new BehaviorOption();
opt0.Key = new BehaviorOptionKey();
opt0.Key.Id = 0;
opt0.Name = "Distributions Will Be Provided";
BehaviorOption opt1 = new BehaviorOption();
opt1.Key = new BehaviorOptionKey();
opt1.Key.Id = 1;
opt1.Name = "Automatically Create Distributions";
b.Options = new BehaviorOption[] { opt0, opt1 };
b.SelectedOption = b.Options[1];
payablesInvoiceCreatePolicy.Behaviors = new Behavior[]{b};
wsDynamicsGP.CreatePayablesInvoice(payablesInvoice, context, payablesInvoiceCreatePolicy);
Documentation seems to be sparse on what should or shouldn't work here. I have to assume I should be able to update the policy as I see fit at runtime based on whether or not my user has decided to provide line item distributions.
Does anyone know what I am missing?
Yes. It took me 2 days to fix this myself. After adding the behaviour to the policy object UpdatePolicy before calling the create invoice as below:
wsDynamicsGP.UpdatePolicy(payablesInvoiceCreatePolicy, new RoleKey { Id = "00000000-0000-0000-0000-000000000000" }, context)
wsDynamicsGP.CreatePayablesInvoice(payablesInvoice, context, payablesInvoiceCreatePolicy);
Note that you selected the behavior that automatically creates distribution lines on invoice creation. The behavior's Internal property also must be set to true.
Here is a working example also utilizing FlowerKing's answer:
BehaviorKey bk = new BehaviorKey();
bk.Id = new Guid("e476a157-ecf0-4dae-8cef-317dd2cfbe41");
bk.PolicyKey = payablesInvoiceCreatePolicy.Key;
Behavior b = new Behavior();
b.Key = bk;
b.Internal = true;
BehaviorOption opt0 = new BehaviorOption();
opt0.Key = new BehaviorOptionKey();
opt0.Key.Id = 0;
opt0.Name = "Distributions Will Be Provided";
BehaviorOption opt1 = new BehaviorOption();
opt1.Key = new BehaviorOptionKey();
opt1.Key.Id = 1;
opt1.Name = "Automatically Create Distributions";
b.Options = new BehaviorOption[] { opt0, opt1 };
b.SelectedOption = b.Options[0];
policy.Behaviors = new Behavior[] { b };
client.UpdatePolicy(payablesInvoiceCreatePolicy, new RoleKey { Id = "00000000-0000-0000-0000-000000000000" }, context);
client.CreatePayablesInvoice(payablesInvoice, context, payablesInvoiceCreatePolicy);

What is the preferred method of updating Money fields in Dynamics CRM 2013 via Web Services?

I have an entity in CRM that has some Money fields on them. At the time of the creation of the entity there is no value for those fields, so the entity is created and saved. However if I then have values and go to update them (either through the UI or Web Services) I basically get an error stating "The currency cannot be null".
In the UI:
I then get a red circle with an X through it if I go to update the value.
In the code (web service call):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
I then get the "The currency cannot be null" on the .SaveChanges() call.
In the code (second attempt):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
var crmCurrency = crmService.TransactionCurrencySet.Where(c => c.ISOCurrencyCode == "AUD").First();
crmService.SetLink(crmAccount, "TransactionCurrency_Account", crmCurrency);
//crmService.SaveChanges() /* tried with this uncommented as well to try and "set currency" before setting value */
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
So this attempt fails in the same way, however if I run it a second time (without the currency and SetLink) so that the currency was saved before then it does work - hence the atempt to do a second .SaveChanges() but really need it to sort of work the first time through.
In the code (third attempt):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.athos_MoneyValueItem= new CrmServiceReference.Money() { Value = 10.0m };
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
This doesn't appear to work either
When you create a new record containing a Money field (or updating an existing one where no Money fields are filled before) , you need to specify the currency, the right field to set is TransactionCurrencyId (logical name transactioncurrencyid), it's a lookup (so inside the code is an EntityReference) to the currency entity.
assuming you have the Guid of your currency stored inside the currencyId variable:
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.TransactionCurrencyId = new EntityReference(TransactionCurrency.LogicalName, currencyId);
crmAccount.myitems_MoneyValueItem = new Money(10.0m); //better to update the money field with this syntax
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
CRM 2013 expects a Money object for a currency field.
crmAccount.myitems_MoneyValueItem = new Money(10.0m);
You shouldn't have to explicitly declare a currency unless your CRM organization doesn't have a default currency set (and since you need to set this when creating an organization, it should always be set).
It looks like if I do a lookup on currency (shame as an extra data call) and then use that to set the TransactionCurrencyId it then "works" ... seems like I shouldn't have to do this as I do have a default set for the organisation though. But this does appear to be working in that it results in being able to update those fields.
var crmCurrency = crmService.TransactionCurrencySet.Where(c => c.ISOCurrencyCode == currencyCode).First();
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.TransactionCurrencyId = new CrmServiceReference.EntityReference() { Id = crmCurrency.TransactionCurrencyId, LogicalName = "transactioncurrency", Name = crmCurrency.CurrencyName };
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges();
i am doing the same thing as you but unable to set the money field value during service.create.
I have already put in
entity.Attributes["transactioncurrencyid"] = currencyGuid;
entity.Attributes["new_moneyfield"] = new Money(123.45M);
but in the end, the record is created with the transactioncurrency field populated, while the money field is blank.
Its a custom workflow on CRM online 2016 by the way..

How to specify language, location and city with facebook graph api

I am using FacebookGraphAPIDesktop.swc api to create unpublished post.
How can I assign language, location and city parameter with it?
Currently I am creating it like below :-
var params:Object = new Object();
params.access_token = PhotoToken;
params.published = 0;
params.message = "Test unpublished post";
FacebookDesktop.api("/" + PageID +"/feed", StatusPosted, params,"POST");
thanks in advance
I found the solution and posting here if anyone needs it :-
We need to pass it as a json string :-
var targeting:String = "{'countries':['US','IN']}";
_params.access_token = token;
_params.published = 0;
_params.message = "This is status message 5";
_params.targeting = targeting;
FacebookDesktop.api("/202387076493582/feed", messagePosted1, _params,"POST");

VMWare VIM SDK. List all VM's working, but list all datasources does not. Am I missing something?

My goal is to get a listing of all the DataStores in a specific datacenter. I'm able to list all of the Hosts, and VM's, but not the Datastores, and I don't understand why (I'm still learning the API's). Any insight would be appreciated.
Here's the code for grabbing all of the VM's (this works as expected):
public List<VM> getVMsInDatacenter(String datacenter, IEnumerable<String> properties)
{
List<VM> VMs = null;
this.joinConnection((appUtil) =>
{
var svcUtil = appUtil.getServiceUtil();
var dcMoRef = svcUtil.GetDecendentMoRef(null, "Datacenter", datacenter);
var typeinfo = buildTypeInfo("VirtualMachine", properties.ToList());
VMs = buildVMsFromObjectContent(svcUtil.GetContentsRecursively(null, dcMoRef, typeinfo, true));
});
return VMs;
}
Here is the analogous code for the Datastore (which does not work as expected):
public List<DataStore> getDataStoresInDatacenter(String datacenter, IEnumerable<String> properties)
{
List<DataStore> DataStores = null;
this.joinConnection((appUtil) =>
{
var svcUtil = appUtil.getServiceUtil();
var dcMoRef = svcUtil.GetDecendentMoRef(null, "Datacenter", datacenter);
var typeinfo = buildTypeInfo("Datastore", properties.ToList());
DataStores = buildDataStoresFromObjectContent(svcUtil.GetContentsRecursively(null, dcMoRef, typeinfo, true));
});
return DataStores;
}
appUtil is an instantiation of the AppUtil class that came with the VIM SDK samples. It houses functionality for connecting, querying, etc.
joinConnection is a method for connecting, or re-using a connection if we've already connected.
If there are any other questions about the code, please let me know.
Also, if there's a better way, I'd like to know that too :)
Found the problem. The method getContentsRecursively is calling a method called 'buildFullTraversal' that builds a traversal/selection spec. This method was not adding a traversal for the datastore. I added one that like so:
TraversalSpec vmToDs = new TraversalSpec();
vmToDs.name = "vmToDs";
vmToDs.type = "VirtualMachine";
vmToDs.path = "datastore";
HToVm.skip = false;
HToVm.skipSpecified = true;
And then I modified the visitFolders traversal like so:
// Recurse through the folders
TraversalSpec visitFolders = new TraversalSpec();
visitFolders.name = "visitFolders";
visitFolders.type = "Folder";
visitFolders.path = "childEntity";
visitFolders.skip = false;
visitFolders.skipSpecified = true;
visitFolders.selectSet = new SelectionSpec[] { new SelectionSpec(), new SelectionSpec(), new SelectionSpec(), new SelectionSpec(), new SelectionSpec(), new SelectionSpec(), new SelectionSpec(), new SelectionSpec() };
visitFolders.selectSet[0].name = "visitFolders";
visitFolders.selectSet[1].name = "dcToHf";
visitFolders.selectSet[2].name = "dcToVmf";
visitFolders.selectSet[3].name = "crToH";
visitFolders.selectSet[4].name = "crToRp";
visitFolders.selectSet[5].name = "HToVm";
visitFolders.selectSet[6].name = "rpToVm";
visitFolders.selectSet[7].name = "vmToDs";
return new SelectionSpec[] { visitFolders, dcToVmf, dcToHf, crToH, crToRp, rpToRp, HToVm, rpToVm, vmToDs };
Now, calls to getContentsRecursively will also include the datastores that belong to a VM, so the method in the question will works as expected.