What is the preferred method of updating Money fields in Dynamics CRM 2013 via Web Services? - 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..

Related

Authorize.net Sandbox Accept page testing- always declined?

I’m attempting to test Accept Hosted payment page(redirect-method) with my sandbox. For this, I’m using the GetAnAcceptPaymentPage from the Java sample code application to generate a token string for a payment, specified the autoLoginId and transactionKey for my sandbox, and setting $1.00 as the amount. I’m then posting the returned token string to https://test.authorize.net/payment/payment with a “token” form element containing that string. This much appears to be working, and I do get a payment page showing the $1.00 amount. However, no matter what values I enter onto that payment page, pressing the “Pay” button just shows “The transaction has been declined.” in red text at the bottom of the form. I’ve confirmed that my sandbox is set to “Live” mode, and have looked at the following link to use what I believe should be valid values for testing: https://developer.authorize.net/hello_world/testing_guide/. I'm hoping someone can tell me why I can't get any result other than "The transaction has been declined".
public String getTokenValue() {
ApiOperationBase.setEnvironment(Environment.SANDBOX);
MerchantAuthenticationType merchantAuthenticationType = new MerchantAuthenticationType();
merchantAuthenticationType.setName("xxxxx");
merchantAuthenticationType.setTransactionKey("xxxxxx");
ApiOperationBase.setMerchantAuthentication(merchantAuthenticationType);
// Create the payment transaction request
TransactionRequestType txnRequest = new TransactionRequestType();
txnRequest.setTransactionType(TransactionTypeEnum.AUTH_CAPTURE_TRANSACTION.value());
txnRequest.setAmount(new BigDecimal(1.00).setScale(2, RoundingMode.CEILING));
OrderExType order = new OrderExType();
order.setInvoiceNumber("2");
txnRequest.setOrder(order);
CustomerProfilePaymentType cpp = new CustomerProfilePaymentType();
cpp.setCustomerProfileId("xxxx");
cpp.setCreateProfile(true);
txnRequest.setProfile(cpp);
SettingType setting1 = new SettingType();
setting1.setSettingName("hostedPaymentButtonOptions");
setting1.setSettingValue("{\"text\": \"Proceed\"}");
SettingType setting2 = new SettingType();
setting2.setSettingName("hostedPaymentOrderOptions");
setting2.setSettingValue("{\"show\": false}");
SettingType setting3 = new SettingType();
setting3.setSettingName("hostedPaymentPaymentOptions");
setting3.setSettingValue("{\"cardCodeRequired\": true}");
SettingType setting4 = new SettingType();
setting4.setSettingName("hostedPaymentIFrameCommunicatorUrl");
setting4.setSettingValue("{\"url\": \"http://example.com/abc\"}");
ArrayOfSetting alist = new ArrayOfSetting();
alist.getSetting().add(setting1);
alist.getSetting().add(setting2);
alist.getSetting().add(setting3);
alist.getSetting().add(setting4);
GetHostedPaymentPageRequest apiRequest = new GetHostedPaymentPageRequest();
apiRequest.setTransactionRequest(txnRequest);
apiRequest.setHostedPaymentSettings(alist);
GetHostedPaymentPageController controller = new GetHostedPaymentPageController(apiRequest);
controller.execute();
GetHostedPaymentPageResponse response = new GetHostedPaymentPageResponse();
response = controller.getApiResponse();
if (response != null) {
if (response.getMessages().getResultCode() == MessageTypeEnum.OK) {
System.out.println(response.getToken());
} else {
System.out.println("Failed to get hosted payment page: " + response.getMessages().getResultCode());
}
}
return response.getToken();
}
>>> order.setInvoiceNumber("2");
Set the invoice number to a different value than 2, this value is used in Sandbox to trigger decline for testing purposes.

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

MS CRM 2013 Attribute parentcustomeridtype must not be NULL if attribute parentcustomerid is not NULL

Dear smart developers out there,
I encounter a problem when I want to create a contact belonging to an organization in Microsoft Dynamics CRM 2013 via web services
client = new OrganizationServiceClient("CustomBinding_IOrganizationService");
var newContactProperties = new Dictionary<string, object> {
{ "lastname", "TestContact"},
{ "firstname", "A"},
{ "fullname", "A TestContact"}
};
/* organizationType is a CRM.CRMWebServices.OptionSetValue
* with ExtensionData null, PropertyChanged null and a valid Value
*
* orgReference is a CRM.CRMWebServices.EntityReference
* with a valid Id
*/
newContactProperties.Add("parentcustomeridtype", organizationType);
newContactProperties.Add("parentcustomerid", orgReference);
var entity = new Entity();
entity.LogicalName = "contact";
entity.Attributes = new AttributeCollection();
entity.Attributes.AddRange(newContactProperties);
client.Create(entity);
This gives me error 'Attribute parentcustomeridtype must not be NULL if attribute parentcustomerid is not NULL'
I am puzzled why this happens and how I can solve this problem. Please help me if you can.
Thank you,
AllWorkNoPlay
You don't need to set "parentcustomeridtype" attribute separately. It's a system field, will be set by platform and in parentcustomerid exists for legacy reason, when it was Customer type in earlier versions of Dynamics CRM.
You need to specify only EntityReference in lookup field.
newContactProperties.Add("parentcustomerid", new EntityReference("account", new Guid("{accountid guid}")));
Also it's not clear what type you use in "orgReference" field. For contact valid entity types should be "account" or "contact".
thank you for the answers, I did not manage to get it right using web services this way.
I tried using Early Bound access with success:
Generated proxy objects using https://xrmearlyboundgenerator.codeplex.com/
Added line [assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssemblyAttribute()] to assemblyInfo to have Intellisense available (even for customized fields)
Now I manage to create a contact and assign it to an organization (something like this):
var contact = new Contact()
{
FirstName = "Bob",
LastName = "Dobalina",
Address1_Line1 = "123 Strasse",
Address1_City = "Berlin",
Address1_PostalCode = "32254",
Telephone1 = "425-555-5678",
EMailAddress1 = "bob.dobalina#germany.de"
};
var account = new Account()
{
Name = "Siemens Germany",
};
context.AddObject(contact);
context.AddObject(account);
context.AddLink(account, "contact_customer_accounts", contact);
context.SaveChanges();
}

Invoking AX web service via C#

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.

Select new to create complex object with nested new throws cast exception

Has anyone had this issue? I am trying to get objects from the database and create a complex poco but I get a cast issue.
The Account property on the poco message type is a poco account type and it will tell me that whatever the first field type is can't be cast to PocoAccount, so in the example below, AccountID is an int so i'll get int cant' be cast to PocoAccount.
var result = (from a in DbAccount.All()
join m in DbMessage.All() on m.AccountID equals a.AccountID
select new PocoMessage {
Account = new PocoAccount {
AccountID = a.AccountID,
FirstName = a.FirstName,
LastName = a.LastName
},
MessageID = m.MessageID,
Subject = m.Subject,
Body = m.Body
});
If found a similar post that suggested using ToList() which seems to fix the issue however it doesn't feel quite right and I haven't checked out the sql consequence.
var result = (from a in DbAccount.All().ToList()
join m in DbMessage.All().ToList() on m.AccountID equals a.AccountID
select new PocoMessage {
Account = new PocoAccount {
AccountID = a.AccountID,
FirstName = a.FirstName,
LastName = a.LastName
},
MessageID = m.MessageID,
Subject = m.Subject,
Body = m.Body
});