Dekryptid
Dekryptid

Reputation: 1110

How can I specify Product/Service for an Invoice Line Item for QBO IPP .NET SDK V3?

I'm trying to specify a Product/Service list item for invoice line items of invoices that I am importing to QuickBooks Online (QBO) company file and I am getting an error.

Error I receive is:

Intuit.Ipp.Exception.IdsException: InternalServerError ---> Intuit.Ipp.Exception.EndpointNotFoundException: Ids service endpoint was not found.

The exception doesn't give any further breakdown as to if what I'm doing is valid or not.

My unit test method:

    [TestMethod()]
    public void CreateTest()
    {
        Entities.Invoice invoice = new Entities.Invoice();
        invoice.ReferenceId = Guid.NewGuid().ToString("N").Substring(0, 10);
        invoice.CreatedDate = DateTime.Now;
        invoice.CustomerId = 1;
        invoice.LineItems.Add(new InvoiceLine() { ItemName = "Initial Funding", Description = "Initial Funding", Amount = 5500 });
        invoice.LineItems.Add(new InvoiceLine() { ItemName = "Lien Fee", Description = "Lien Fee", Amount = 100 });


        IPPRestProfile restProfile = new IPPRestProfile(realmId, accessToken, accessTokenSecret, Intuit.Ipp.Core.IntuitServicesType.QBO, consumerKey, consumerSecret);
        IPP.Invoices target = new IPP.Invoices(restProfile);
        Intuit.Ipp.Data.Invoice actual = target.Create(invoice);

        if (actual != null)
        {
            Console.WriteLine("QB Invoice ID: {0}", actual.Id);
            Console.WriteLine("QB Sync Token: {0}", actual.SyncToken);
            Console.WriteLine("================================================");
            ObjectDumper.Write(actual, 4);
        }
    }

The method that the unit test calls:

        public Intuit.Ipp.Data.Invoice Create(Entities.Invoice invoice)
    {
        // Check pre-conditions
        if (invoice == null) { throw new ArgumentException("Invoice object is required.", "invoice"); }

        var qboInvoice = new Intuit.Ipp.Data.Invoice();
        BuildInvoiceEntity(qboInvoice, invoice);

        return _Service.Add(qboInvoice) as Intuit.Ipp.Data.Invoice;
    }

And finally the build invoice method:

    private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
    {
        if (qboInvoice != null && invoice != null)
        {
            IQuickBooksHeader header = invoice as IQuickBooksHeader;

            if (String.IsNullOrEmpty(header.Id))
            {
                qboInvoice.DocNumber = invoice.ReferenceId;
                qboInvoice.TxnDate = invoice.CreatedDate;
                qboInvoice.TxnDateSpecified = true;

                // Customer
                qboInvoice.CustomerRef = new ReferenceType()
                {
                    type = objectNameEnumType.Customer.ToString(),
                    Value = invoice.CustomerId.ToString()
                };

                // AR Account
                qboInvoice.ARAccountRef = new ReferenceType()
                {
                    type = objectNameEnumType.Account.ToString(),
                    name = "Accounts Receivable"
                };
            }

            if (invoice.LineItems.Count > 0)
            {
                Intuit.Ipp.Data.Line[] invoiceLineCollection = new Intuit.Ipp.Data.Line[invoice.LineItems.Count];
                for (int i = 0; i < invoice.LineItems.Count; i++)
                {
                    var line = invoice.LineItems[i];
                    var qboInvoiceLine = new Intuit.Ipp.Data.Line()
                    {
                        Amount = line.Amount,
                        AmountSpecified = true,
                        Description = line.Description,
                        DetailType = LineDetailTypeEnum.SalesItemLineDetail,
                        DetailTypeSpecified = true,
                        AnyIntuitObject = new SalesItemLineDetail()
                        {
                            ItemRef = new ReferenceType()
                            {
                                name = line.ItemName,
                            },
                            ItemElementName = ItemChoiceType.UnitPrice,
                            AnyIntuitObject = line.Amount
                        }
                    };
                    invoiceLineCollection[i] = qboInvoiceLine;
                }
                qboInvoice.Line = invoiceLineCollection;
            }
        }
    }

If I remove this piece of code from my build method:

      ItemRef = new ReferenceType()
      {
          name = line.ItemName,
      },

the invoice is successfully added with the default "Services" list item for the Product/Service of the invoice line items.

The online documentation for the IPP .NET SDK V3 is vague on what to specify for ReferenceType. What is wrong about just specifying the name of the list item? If I'm wrong about how I'm trying to specify a Product/Service list item for invoice line items, what is the correct way?

Upvotes: 2

Views: 2387

Answers (1)

Dekryptid
Dekryptid

Reputation: 1110

After days of researching, I never did find an answer as to why I can't just use the name like I wanted to, even though it works that way when specifying an AccountRef. But I digress, here is my solution:

// Hold a collection of QBO items
private ReadOnlyCollection<Item> _Items;

// I set the collection in the constructor only once
public Invoices(Entities.IPPRestProfile restProfile)
{
    if (restProfile == null)
        throw new ArgumentException("IPPRestProfile object is required.", "restProfile");

    OAuthRequestValidator oAuthValidator = new OAuthRequestValidator(restProfile.OAuthAccessToken, restProfile.OAuthAccessTokenSecret,
            restProfile.ConsumerKey, restProfile.ConsumerSecret);
    ServiceContext context = new ServiceContext(restProfile.RealmId, restProfile.DataSource, oAuthValidator);
    _Service = new DataService(context);
    _Items = (new QueryService<Item>(context)).ExecuteIdsQuery("SELECT * FROM Item", QueryOperationType.query);
}

Whenever I build my invoice I query the collection for the Id of the item by name:

private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
{
    ...
    // Get the Id value of the item by name
    string itemTypeId = _Items.Where(o => o.Name == line.ItemName).FirstOrDefault().Id;

    // Specify the Id value in the item reference of the SalesItemLineDetail
    var qboInvoiceLine = new Intuit.Ipp.Data.Line()
    {
        Amount = (decimal)amount,
        AmountSpecified = true,
        Description = line.Description,
        DetailType = LineDetailTypeEnum.SalesItemLineDetail,
        DetailTypeSpecified = true,
        AnyIntuitObject = new SalesItemLineDetail()
        {
            ItemRef = new ReferenceType() { Value = itemTypeId },
            AnyIntuitObject = (decimal)line.Rate,
            ItemElementName = ItemChoiceType.UnitPrice
        }
    };
    ...
}

Hopefully this helps point someone in the right direction to a possible better solution.

Upvotes: 5

Related Questions