locked
Question about Opportunity Invoice, Sales Order Invoice and mappings RRS feed

  • Question

  • Hello,

    I'm using CRM Dynamics 4 and would like to know what are my possibilities of implementing a mapping between the Opportunity Invoice and the Sales Order Invoice.

    In other words, I noticed there's a Invoice tab in opportunities that gets the products from the oppotunity when created. Now, when I finally reach the sales order stage, I wanted all the information from the Opportunity invoice to pass on to the Sales Order Invoice with products and everything.

    Can this be achieved with simple mappings ( I have been able with a custom entity to make a record span across opportunity, quote, sales order but somehow with the invoice entity I can't and I fear this might be cause os system mappings) or do I have to resort to plug ins and if yes can anyone give me a clue please?

    In case of any doubts or misunderstanding please let me know and I will try my best to explain better.

    Thanks in advance.

    Tuesday, October 30, 2012 3:32 PM

Answers

  • it does look like invalid cast issue, did you tried following code? so replace your existing line of code with the following and see if you are getting same error?

    foreach (BusinessEntity cursor in bec.BusinessEntities)
    {
    
    DynamicEntity q = (DynamicEntity)cursor;
    try
    {
    DynamicEntity dynamicInvoice= new DynamicEntity(); 
    							      	
    dynamicInvoice.Properties = new PropertyCollection();
    
    dynamicInvoice.Name = EntityName.invoice.ToString();
    dynamicInvoice.Properties.Add(new KeyProperty("invoiceid", ((Key)q.Properties["invoiceid"])));
    dynamicInvoice.Properties.Add(new LookupProperty("salesorderid", crmTypes.CreateLookup(EntityName.salesorder.ToString(), 
    (Key)dynamicEntity["salesorderid"]).Value)));
    
    service.Update(dynamicInvoice);
    
    }
    catch (SoapException ex)
    {
    throw new InvalidPluginExecutionException("An error occurred in the SalesOrderAsyncPostCreateHandler plug-in.", ex.Detail.InnerText);
    }
    
    }


    MayankP
    My Blog
    Follow Me on Twitter

    • Marked as answer by Agenteusa Wednesday, October 31, 2012 6:00 PM
    Wednesday, October 31, 2012 4:21 PM
    Answerer

All replies

  • Thanks for the reply.

    I read those posts but it didn't help much. The question is the invoice entity is already mapped to both salesorder and to opportunity.

    The issue is if I create an invoice in an opportunity, let's suppose the opportunity has 2 products, the invoice is created with teh 2 products which is correct and the field opportunityid gets populated in the invoice with the said opportunity like it should.

    I continue my sales flow, activate the quote and then create a sales order and no invoice gets passed. That invoice I created in the opportunity should be populated with the salesorderid that was created since it comes from that opportunity.

    If this is still answered on one of your links please direct me to a less broad choice since I'm not that experienced with mappings :S

    Thanks again.

    PS I tried using SQL Select to get the mapping guid and used it to try to access the mapping window but it gave me an error saying the object didn't exist or I didn't have permissions. I have Admin rights and the guid showed up on the database so I'm not sure about what happened there as well.

    Tuesday, October 30, 2012 6:41 PM
  • Ok, as per this mapping and sales flow. if you go from opportunity -> quote -> order -> invoice, it will work correctly..

    since you are going directly from opportunity -> Invoice first and then opportunity -> quote -> order, system will not able to automatically put this order id in to Invoice.

    I guess  the reason for this is that at the time of Order creation, system can not go and check all invoices present against quote/opportunity and update them becuase there could be mutiple invoice present on quote/opportunity, Also what if those invoices are paid at that time? so I guess that's why it is logical for system to not to update them out of the box..

    however if this your requirement, then you can write plug in on order creation to check relevant invoice and update them, but again question would be what if user create two order against opportunity/quote, which one will be updated on Invoice..

    so personally I suggest you implement sales flow from opportunity -> quote -> order -> invoice. please let me know if you got any further queries regarding the same..

    PS : the mapping field (links provided above) are generally used to add any new fields in to mapping since your requirement is different please ignore those links, I thought you were looking to add new fields in to mapping so provided these links so apologies for the same..


    MayankP
    My Blog
    Follow Me on Twitter

    Wednesday, October 31, 2012 9:18 AM
    Answerer
  • Again thanks for the reply.

    Yes you are absolutely correct and made a very accurate post about what I need. Sorry if I wasn´t clear on the first 2 posts.

    I also understand your warning but the CRM I'm working with is heavily customized and as so one of the changes is it doesn't allow an opportunity to have more than one sales order. So it won't affect me as I know every invoice added to the opportunity is going to belong to that sales order.

    Also the invoice is only allowed tto be set as paid on the sales order else it will fire a handled exception.

    As so I have already developed tthe code to do this but I'm getting a strange error on the Service.Update( ) method .

    Message=Server was unable to process request.

    It's odd since the inner exception shows as null so I can't really figure out what's going on. I did several updates on other customizations and never had a problem, somehow here it's giving me one.

    I can get the invoice, salesorder and opportunity ID's just fine and checked them against the DB and they are all correct.

    The code is below if it helps and it's an asynchronous plugin with a create message against the salesorder entity.

    Any clues?

    Thanks again for the help.

    public class SalesOrderAsyncPostCreateHandler : IPlugin
        {
            ICrmService service;
            public void Execute(IPluginExecutionContext context)
            {
                service = context.CreateCrmService(true);
    
                DynamicEntity target = null;
    
                if (context.InputParameters.Properties.Contains("Target"))
                {
                    target = (DynamicEntity)context.InputParameters.Properties["Target"];
                }
                else
                {
                    return;
                }
    
                UpdateInvoiceSetSalesOrderID(target);
    
            }
    
    private void UpdateInvoiceSetSalesOrderID(DynamicEntity target)
            {
    
                BusinessEntityCollection bec = new BusinessEntityCollection();
    
                if (target.Properties.Contains("opportunityid"))
                    bec = CrmAccess.getInvoicesOpportunity(((Lookup)target.Properties["opportunityid"]).Value, service);
    
                if (bec != null)
                {
                    if (bec.BusinessEntities.Count > 0)
                    {
                        DynamicEntity q = new DynamicEntity();
                        foreach (BusinessEntity cursor in bec.BusinessEntities)
                        {
                            q = (DynamicEntity)cursor;
                            q.Name = EntityName.invoice.ToString();
                            q.Properties.Add(new KeyProperty("invoiceid", ((Key)q.Properties["invoiceid"])));
                            q.Properties.Add(new LookupProperty("salesorderid", ((Lookup)target.Properties["salesorderid"])));
                            
                            try
                            {
                                service.Update(q);
                            }
                            catch (SoapException ex)
                            {
                                throw new InvalidPluginExecutionException(
                                      "An error occurred in the SalesOrderAsyncPostCreateHandler plug-in.", ex);
                            }
                        }
                    }
                }
            }

    Wednesday, October 31, 2012 10:15 AM
  • Hi,

    you need to check ex.Detail.InnerText (exception's inner details) to see what's the issue here..

    anyways, I suggest changing your code block as mentioned below and try to see if this works..

    foreach (BusinessEntity cursor in bec.BusinessEntities)
    {
    
    DynamicEntity q = (DynamicEntity)cursor;
    try
    {
    DynamicEntity dynamicInvoice= new DynamicEntity(); 
    							      	
    dynamicInvoice.Properties = new PropertyCollection();
    
    dynamicInvoice.Name = EntityName.invoice.ToString();
    dynamicInvoice.Properties.Add(new KeyProperty("invoiceid", ((Key)q.Properties["invoiceid"])));
    dynamicInvoice.Properties.Add(new LookupProperty("salesorderid", ((Lookup)target.Properties["salesorderid"])));
    
    service.Update(dynamicInvoice);
    
    }
    catch (SoapException ex)
    {
    throw new InvalidPluginExecutionException("An error occurred in the SalesOrderAsyncPostCreateHandler plug-in.", ex.Detail.InnerText);
    }
    
    }


    MayankP
    My Blog
    Follow Me on Twitter

    Wednesday, October 31, 2012 10:40 AM
    Answerer
  • Still got the same error. The inner exception returns:

    ""\n  0x80040216\n  An unexpected error occurred.\n  Platform\n"

    Wednesday, October 31, 2012 11:58 AM
  • can you please check one thing (while debugging your code) to see what kind of type is  " target.Properties["salesorderid"] ", my guess is that it could be key becuase for target salesorderid is Key property.

    so if this is the case try getting replacing this line

    dynamicInvoice.Properties.Add(new LookupProperty("salesorderid", ((Lookup)target.Properties["salesorderid"])));

    with

    dynamicInvoice.Properties.Add(new LookupProperty("salesorderid", crmTypes.CreateLookup(EntityName.salesorder.ToString(), 
    (Key)dynamicEntity["salesorderid"]).Value)));

    if this still does not work after doing above changes then I suggest check event log on server to see if any more details regarding this error can be found on this error otherwise you need to enable tracing on CRM server to get more information on this error, see following blog article for the same.

    http://mayankp.wordpress.com/2010/09/11/dynamic-crm-troubleshooting-methods-for-any-crm-issue/


    MayankP
    My Blog
    Follow Me on Twitter

    Wednesday, October 31, 2012 2:10 PM
    Answerer
  • Yes, my original code had the property as Key instead of Lookup. I changed it to lookup to test if the error would be gone but to no avail.

    Yes, salesorderid is a Key and not a Lookup but the error persists.

    I'm going to enable tracing to see if I can figure out something more and hopefully to mark as answer. :)

    Wednesday, October 31, 2012 2:23 PM
  • Ok, 2 logs were generated and the first one named "CRMDEV-CrmAsyncService-bin-20121031-1" had this info as an error(opened with CRM Trace Log Viewer):

    at AsyncOperationCommand.Execute(AsyncEvent asyncEvent)
    at PoolHandler.ProcessAsyncEvent(AsyncEvent asyncEvent)
    at PoolHandler.InvokeHandlerInPool(Object state)
    at ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at _ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
    at _ThreadPoolWaitCallback.PerformWaitCallback(Object state)
    >Exception while trying to execute AsyncOperationId: {F1800B37-7223-E211-A683-00155DC85A04} AsyncOperationType: 1 - Microsoft.Crm.Sdk.InvalidPluginExecutionException: An error occurred in the SalesOrderDetailAsyncPostCreateHandler plug-in. ---> System.Web.Services.Protocols.SoapException: Server was unable to process request.
    at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
    at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
    at Microsoft.Crm.SdkTypeProxy.CrmService.Update(BusinessEntity entity)
    at Microsoft.Crm.Asynchronous.SdkTypeProxyCrmServiceWrapper.Update(BusinessEntity entity)
    at ESRI.CRM.Application.Plugins.SalesOrderAsyncPostCreateHandler.UpdateInvoiceSetSalesOrderID(DynamicEntity target) in C:\IntegracaoEPM\ESRI.CRM.Application\ESRI.CRM.Application.Plugins\SalesOrderAsyncPostCreateHandler.cs:line 173
    --- End of inner exception stack trace ---
    at ESRI.CRM.Application.Plugins.SalesOrderAsyncPostCreateHandler.UpdateInvoiceSetSalesOrderID(DynamicEntity target) in C:\IntegracaoEPM\ESRI.CRM.Application\ESRI.CRM.Application.Plugins\SalesOrderAsyncPostCreateHandler.cs:line 183
    at ESRI.CRM.Application.Plugins.SalesOrderAsyncPostCreateHandler.Execute(IPluginExecutionContext context) in C:\IntegracaoEPM\ESRI.CRM.Application\ESRI.CRM.Application.Plugins\SalesOrderAsyncPostCreateHandler.cs:line 32
    at Microsoft.Crm.Asynchronous.EventOperation.InternalExecute(AsyncEvent asyncEvent)
    at Microsoft.Crm.Asynchronous.AsyncOperationCommand.Execute(AsyncEvent asyncEvent)

    Strange it´s mentioning an error wit salesorderdetail plugin since i have unregistered it.

    The second had 2 error messages and is called "CRMDEV-w3wp-wwwroot-20121031-1":

    at CompositeSoapExtensionExceptionHandler.Handle(Stream to, Stream from, Exception exception)
    at CrmAuthenticationSoapExtensionBase.ProcessMessage(SoapMessage message)
    at SoapMessage.RunExtensions(SoapExtension[] extensions, Boolean throwOnException)
    at SoapServerProtocol.WriteException(Exception e, Stream outputStream)
    at WebServiceHandler.WriteException(Exception e)
    at WebServiceHandler.Invoke()
    at WebServiceHandler.CoreProcessRequest()
    at SyncSessionlessHandler.ProcessRequest(HttpContext context)
    at CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
    at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
    at ApplicationStepManager.ResumeSteps(Exception error)
    at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
    at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
    at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
    at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)
    >CrmSoapExtension detected non-CrmException - report will be sent to Watson:
    System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.InvalidCastException: Unable to cast object of type 'Microsoft.Crm.Sdk.KeyProperty' to type 'Microsoft.Crm.Sdk.LookupProperty'.
    at Microsoft.Crm.BusinessEntities.LookupPropertyConverter.GetPropertyValue(Property property)
    at Microsoft.Crm.BusinessEntities.CrmReferencePropertyConverter.InternalToBusinessEntity(ICrmConversionContext conversionContext, AttributeMetadata attributeMetadata, Property property, BusinessEntity businessEntity, Dictionary`2 abbrvToChildAttributeMetadata, Guid organizationId)
    at Microsoft.Crm.BusinessEntities.PropertyConverterBase.ToBusinessEntity(ICrmConversionContext conversionContext, AttributeMetadata attributeMetadata, Property property, BusinessEntity businessEntity)
    at Microsoft.Crm.BusinessEntities.DynamicEntityToBusinessEntityConverter.Convert(ICrmConversionContext conversionContext, DynamicEntity dynamicEntity)
    at Microsoft.Crm.BusinessEntities.BusinessEntity.Converter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
    at Microsoft.Crm.BusinessEntities.ConversionHelpers.Convert(ICrmConversionContext conversionContext, Object source, Type destinationType)
    at Microsoft.Crm.Sdk.DictionaryMapper.Map(PropertyBag inputs, ICrmConversionContext context)
    at Microsoft.Crm.Extensibility.InternalOperationPlugin.Execute(IPluginExecutionContext context)
    at Microsoft.Crm.Extensibility.PluginStep.Execute(PipelineExecutionContext context)
    at Microsoft.Crm.Extensibility.Pipeline.Execute(PipelineExecutionContext context)
    at Microsoft.Crm.Extensibility.MessageProcessor.Execute(PipelineExecutionContext context)
    at Microsoft.Crm.Extensibility.InternalMessageDispatcher.Execute(PipelineExecutionContext context)
    at Microsoft.Crm.Extensibility.ExternalMessageDispatcher.Execute(String messageName, Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, PropertyBag fields, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId)
    at Microsoft.Crm.Sdk.CrmServiceInternal.Update(String namespaceName, BusinessEntityBase entity, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId)
    at Microsoft.Crm.Sdk.Crm2007.CrmService.Update(BusinessEntity entity)
    --- End of inner exception stack trace ---
    at MessageProcessor.Execute(PipelineExecutionContext context)
    at InternalMessageDispatcher.Execute(PipelineExecutionContext context)
    at ExternalMessageDispatcher.Execute(String messageName, Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, PropertyBag fields, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId)
    at CrmServiceInternal.Update(String namespaceName, BusinessEntityBase entity, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId)
    at CrmService.Update(BusinessEntity entity)
    at RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
    at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
    at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
    at LogicalMethodInfo.Invoke(Object target, Object[] values)
    at WebServiceHandler.Invoke()
    at WebServiceHandler.CoreProcessRequest()
    at SyncSessionlessHandler.ProcessRequest(HttpContext context)
    at CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
    at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
    at ApplicationStepManager.ResumeSteps(Exception error)
    at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
    at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
    at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
    at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)
    >MessageProcessor fail to process message 'Update' for 'invoice'.

    It's kind of odd it's throwing an invalid cast since both casts are now set to keyproperty and it will give an earlier error if they are not this way.

    Can you make something out of this since I'm really not getting what the problem might be :S

    Also checked the event viewer and has a message tagged with "Information" and it seems to be pointing again at an invalid cast:

    Fault bucket , type 0
    Event Name: CRMmanaged
    Response: Not available
    Cab Id: 0
    
    Problem signature:
    P1: 4.0.7333.3018
    P2: w3wp
    P3: Microsoft.Crm
    P4: Unable to cast object of type 'Microsoft.Crm.Sdk.KeyProperty' to type 'Microsoft.Crm.Sdk.LookupProperty'.;Hash='311085001'
    P5: 
    P6: 
    P7: 
    P8: 
    P9: 
    P10: 
    
    Attached files:
    C:\Windows\Temp\tmpE6E6.tmp
    
    These files may be available here:
    C:\ProgramData\Microsoft\Windows\WER\ReportQueue\NonCritical_4.0.7333.3018_dfd434eb696bc56bc1e514c04a823988666888_cab_0d910eac
    
    Analysis symbol: 
    Rechecking for solution: 0
    Report Id: 4f40e5ae-2372-11e2-a683-00155dc85a04
    Report Status: 4

    Wednesday, October 31, 2012 4:05 PM
  • it does look like invalid cast issue, did you tried following code? so replace your existing line of code with the following and see if you are getting same error?

    foreach (BusinessEntity cursor in bec.BusinessEntities)
    {
    
    DynamicEntity q = (DynamicEntity)cursor;
    try
    {
    DynamicEntity dynamicInvoice= new DynamicEntity(); 
    							      	
    dynamicInvoice.Properties = new PropertyCollection();
    
    dynamicInvoice.Name = EntityName.invoice.ToString();
    dynamicInvoice.Properties.Add(new KeyProperty("invoiceid", ((Key)q.Properties["invoiceid"])));
    dynamicInvoice.Properties.Add(new LookupProperty("salesorderid", crmTypes.CreateLookup(EntityName.salesorder.ToString(), 
    (Key)dynamicEntity["salesorderid"]).Value)));
    
    service.Update(dynamicInvoice);
    
    }
    catch (SoapException ex)
    {
    throw new InvalidPluginExecutionException("An error occurred in the SalesOrderAsyncPostCreateHandler plug-in.", ex.Detail.InnerText);
    }
    
    }


    MayankP
    My Blog
    Follow Me on Twitter

    • Marked as answer by Agenteusa Wednesday, October 31, 2012 6:00 PM
    Wednesday, October 31, 2012 4:21 PM
    Answerer
  • Yes, I think it has something to do with the type as well. Just to test I created a TargetUpdateInvoice to check what the datatypes were for invoiceid and for salesorderid.

    Turns out invoiceid is in fact a Key but salesorderid is a Lookup. I have to try that method you provided me to pass it as a lookup to see if it fixes my issue.

    Will get back to you soon.

    Yhanks for the help again.

    Wednesday, October 31, 2012 4:55 PM
  • Yep, that CreateLookup method did it for me. :)

    Thanks for your time, I appreciate the help.

    Wednesday, October 31, 2012 5:59 PM
  • you are welcome

    MayankP
    My Blog
    Follow Me on Twitter

    Thursday, November 1, 2012 9:15 AM
    Answerer