Connecting nopCommerce to QuickBooks Desktop

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
3 年 前
Has anyone been successful in connecting nopCommerce to QuickBooks desktop? Since we cannot build a web service in .net core, is there an alternative approach?
3 年 前
You can use their SDK to create a standalone WPF app:
https://developer.intuit.com/app/developer/qbdesktop/docs/get-started

You can query your db with custom stored procedures to spit out XML or tap into nopCommerce services in a custom API plugin.  Or they have a Web Connector to listen for requests on your server:
https://developer.intuit.com/app/developer/qbdesktop/docs/get-started/get-started-with-quickbooks-web-connector

They have poorly coded examples in C# that you'll want to refactor immediately:
https://developer.intuit.com/app/developer/qbdesktop/docs/develop/sample-applications-and-code

Here's some interesting projects:
https://github.com/intuit/QuickBooks-V3-DotNET-SDK
https://github.com/pkpjpm/Zombie

I've never had any luck with QODBC or similar "SQL" drivers.  I've never figured out what kind of "database" tech QB pretends to use, but it's just the worst.

Expect a lot of pain and misery along the way, and make many many backups of your QB file.
3 年 前
The pain has already began since I can't find a lot of information out there with examples. QuickBooks online seems to offer better solutions, but I am stuck with the Desktop version. The QuickBooks-V3-DotNet-SDK looks to be just for online. Thanks for the information.
3 年 前
Yeah, the information out there is sparse since anyone who figures it out typically sells their solution instead of sharing code.  All my stuff is proprietary but I'm happy to help with code snippets or any specific questions.

QB has some code samples here (but it's not going to get you very far):  https://github.com/IntuitDeveloper/QBXML_SDK_Samples/tree/master/qbdt/c-sharp

But at the end of the day depending on your budget, available time, and/or coding skills it may make more sense to opt for a 3rd party paid solution.  These guys turn up in a search:
https://www.cartspan.com/
https://www.nopcommerce.com/en/quickbooks-intuit-accounting-pluginby-nopcommerceplus

Or google "quickbooks shopping cart integration" since nearly all of those companies will offer a service to map their solution to a new database schema if they don't already have it for nopCommerce.  Of course then you're at their mercy for support and updates, and in all likelihood an expensive subscription service.
3 年 前
I found this on github for asp.net core
https://github.com/jsgoupil/quickbooks-sync

I am running the test application am successful connecting to QuickBooks and running queries. Still a long way to go though.
3 年 前
Nice!

Once you get up to speed and before you do a deep dive tailoring everything to meet your needs, I recommend making the switch from qbXML over to QBFC (the QB Foundation Classes).  That way you interact with strongly typed classes for your QB objects instead of parsing XML strings.  

For any task you want to tackle, this is a good place to get scaffolding (select your task in the "Message" dropdown, E.G. SalesReceiptAdd, then the C# tab for a code example):
https://static.developer.intuit.com/qbSDK-current/common/newosr/index.html

The "Request" and "Response" tabs are invaluable too for figuring out the data types and limitations, so you don't waste something like an hour figuring out why a Nop Order won't shove itself into QB because you've mapped the GUID to QB's RefNumber, which only supports 11 characters.
3 年 前
I think the QB Foundation Classes are only available to QuickBooks Online. I unfortunately on working on Desktop.

Thanks for the tips.
3 年 前
they work for Desktop too, it's what I use.  

I started out with qbXML too, but I found it very time consuming to parse and bug chase.   Once I switched over to QBFC my development time decreased exponentially.  Now if I want to add a new feature, like most recently importing a nopCommerce Return Request into QB as a Credit Memo, it only takes a few days rather than several weeks to work out.

What are you looking to tackle first, import a nopCommerce Order as a QB Sales Receipt?
3 年 前
That is great news. Are you working with the Interop.QBFC13 package?

Yes, importing a nopCommerce Order is my first item on the list to do.
3 年 前
yes, QBFC13

Here is my basic workflow:

Custom API plugin that taps into Nop.Services/ExportImport:
https://github.com/nopSolutions/nopCommerce/blob/79cd562b38c0b9c60642306e74da5b6f9814c815/src/Libraries/Nop.Services/ExportImport/ExportManager.cs#L1404

I have a bit field in my Order table so I know whether an Order has been imported or not.  Outside of that, use  _orderService.SearchOrders() with your conditions to generate the Order list, or override that method with your own to get the orders you need (whether paid, shipped, whatever your business rules are).

I save that XML output locally on the user's computer in something like MyDocuments, or equivalent, for debugging and later use.  

Copy/paste an example of that output into this website to generate the Order classes you can use to map Nop to QB:
https://json2csharp.com/xml-to-csharp

Then you can load your OrdersToImport.XML file into a IList<Order>:

        private IList<Order> LoadOrderXMLfile(string fileName)
        {
            using (var reader = new StreamReader(savePath + fileName))
            {
                var deserializer = new XmlSerializer(typeof(List<Order>),
                    new XmlRootAttribute("Orders"));
                var orders = (List<Order>)deserializer.Deserialize(reader);

                return orders;
            }
        }


When I have a List ready I connect to my QB file:
(note that I do not feed a file location to BeginSession so it uses the QB instance open on that computer, there are several other ways to go about this including feeding it a direct network connection and not requiring any instance of QB to be open)
(also note that I have logging and session references particular to WPF):

        //Fields
        private static readonly string appID = "yourAppId";
        private static readonly string appName = "yourAppName";
        private static readonly string savePath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + Properties.Settings.Default.SavePath;
        
        //Quickbooks properties
        private static string ticket;
        private static string maxVersion;
        private static RequestProcessor2 rp;
        private static readonly QBFileMode mode = QBFileMode.qbFileOpenDoNotCare;
        public static QBSessionManager sessionManager = new QBSessionManager();
        private static bool sessionBegun;
        private static bool connectionOpen;
        //CTOR

        //Methods
        public async Task ConnectToQuickbooks()
        {
            try
            {
                //Connect to QuickBooks and begin a session //SetFilePath(_logger.testing)
                if (!connectionOpen) await Task.Run(() => sessionManager.OpenConnection(appID, appName));
                connectionOpen = true;
                if (!sessionBegun) await Task.Run(() => sessionManager.BeginSession("", ENOpenMode.omDontCare));
                //if (!sessionBegun) await Task.Run(() => sessionManager.BeginSession(SetFilePath(_logger.testing), ENOpenMode.omDontCare));
                var filePath = sessionManager.GetCurrentCompanyFileName();
                _logger.Info($"Connected to: {filePath}", "Debug");
                sessionBegun = true;
                Application.Current.Properties["sessionBegun"] = sessionBegun;
            }
            catch (Exception e)
            {
                _logger.Info(e.Message, "Error");
                MessageBox.Show(e.Message, "Error");
                if ((bool)Application.Current.Properties["sessionBegun"])
                {
                    sessionManager.EndSession();
                    Application.Current.Properties["sessionBegun"] = false;
                }
                if (connectionOpen)
                {
                    sessionManager.CloseConnection();
                }
            }
        }


Then it's a matter of looping through your IList<Order> to shovel each into your SalesReceiptAdd class, noting the following:

Each Customer entity must already exist in QB, otherwise SalesReceiptAdd will error out.  I simply do a CustomerQuery on the FullName first, which will return the FullName if they exist, otherwise I do a quick CustomerAdd.

Each nopCommerce Product must already exist in QB, otherwise SalesReceiptAdd will error out.  That includes QB's Inventory Items, Grouped Items, and Assemblies.  I have about 100k products, so instead of checking every single item during every single import I first try to shove the Sales Receipt in and just catch any errors.  For any error I just confirm that Order's products exist with ItemInventoryAdd and ItemGroupAdd (we don't use Assemblies), which conveniently will return the QB Item's FullName if they exist, and adds them otherwise.   Then try the SalesReceiptAdd again.

Each Vendor associated with a QB Item must already exist in QB, otherwise ItemInventoryAdd and ItemGroupAdd will error out.  I think you can see where I'm going with this.

Lastly, once I get a successful SalesReceiptAdd then I do a quick check that the QB total matches Nop's total (because of QB group pricing, discounts, coupons, or any number of other pain points the totals may not match), modify the receipt as necessary (which is it's own special joy), and then do a quick SQL query to update the Order.Imported bit field in the Nop database to 1 (true).

Don't forget to disconnect from QB when done:

        public void DisconnectFromQuickbooks()
        {
            //End the session and close the connection to QuickBooks
            sessionManager.EndSession();
            sessionBegun = false;
            Application.Current.Properties["sessionBegun"] = sessionBegun;
            sessionManager.CloseConnection();
            connectionOpen = false;
        }


That's the basic outline, but of course there are many other intricacies and pitfalls.  I've probably hit most of them so see how far you can get and we'll go from there.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.