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#L1404I 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-csharpThen 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.