WIP quickbooks testing.

This commit is contained in:
Patrick Fic
2025-03-27 11:57:57 -07:00
parent e2ccbf7007
commit 7d59796f49
10 changed files with 755 additions and 1040 deletions

1597
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,8 @@
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"electron-log": "^5.3.2", "electron-log": "^5.3.2",
"electron-store": "^8.2.0", "electron-store": "^8.2.0",
"electron-updater": "^6.3.9" "electron-updater": "^6.3.9",
"winax": "^3.6.1"
}, },
"devDependencies": { "devDependencies": {
"@ant-design/v5-patch-for-react-19": "^1.0.3", "@ant-design/v5-patch-for-react-19": "^1.0.3",

Binary file not shown.

Binary file not shown.

BIN
resources/QBFC16.dll Normal file

Binary file not shown.

View File

@@ -2,6 +2,8 @@ import cors from "cors";
import { app } from "electron"; import { app } from "electron";
import log from "electron-log/main"; import log from "electron-log/main";
import express from "express"; import express from "express";
import { handleQuickBookRequest } from "../quickbooks-desktop/quickbooks-desktop";
import errorTypeCheck from "../../util/errorTypeCheck";
export default class LocalServer { export default class LocalServer {
private app: express.Application; private app: express.Application;
@@ -94,7 +96,7 @@ export default class LocalServer {
private configureRoutes(): void { private configureRoutes(): void {
// Basic health check endpoint // Basic health check endpoint
this.app.get("/health", (req, res) => { this.app.get("/health", (req: express.Request, res: express.Response) => {
res.status(200).json({ status: "ok" }); res.status(200).json({ status: "ok" });
}); });
this.app.post("/ping", (req, res) => { this.app.post("/ping", (req, res) => {
@@ -103,13 +105,25 @@ export default class LocalServer {
qbPath: app.getPath("userData"), //TODO: Resolve to actual QB file path. qbPath: app.getPath("userData"), //TODO: Resolve to actual QB file path.
}); });
}); });
this.app.post("/qb", handleQuickBookRequest);
// Add more routes as needed // Add more routes as needed
} }
public start(): void { public start(): void {
this.server = this.app.listen(this.PORT, () => { try {
log.info(`[HTTP Server] Local HTTP server running on port ${this.PORT}`); this.server = this.app.listen(this.PORT, (error: Error) => {
if (error) {
log.error(`[HTTP Server] Error starting server: ${error}`);
} else {
log.info(
`[HTTP Server] Local HTTP server running on port ${this.PORT}`,
);
}
}); });
} catch (error: unknown) {
log.error("[HTTP Server] Error starting server", errorTypeCheck(error));
}
} }
public stop(): void { public stop(): void {

View File

@@ -1,16 +1,19 @@
import { electronApp, is, optimizer } from "@electron-toolkit/utils"; import { electronApp, is, optimizer } from "@electron-toolkit/utils";
import { app, BrowserWindow, Menu, shell, nativeImage, Tray } from "electron"; import { app, BrowserWindow, Menu, nativeImage, shell, Tray } from "electron";
import log from "electron-log/main"; import log from "electron-log/main";
import { autoUpdater } from "electron-updater"; import { autoUpdater } from "electron-updater";
import path, { join } from "path"; import path, { join } from "path";
import appIcon from "../../resources/diamond.png?asset";
import icon from "../../resources/icon.png?asset"; import icon from "../../resources/icon.png?asset";
import ErrorTypeCheck from "../util/errorTypeCheck"; import {
default as ErrorTypeCheck,
default as errorTypeCheck,
} from "../util/errorTypeCheck";
import ipcTypes from "../util/ipcTypes.json"; import ipcTypes from "../util/ipcTypes.json";
import client from "./graphql/graphql-client"; import client from "./graphql/graphql-client";
import store from "./store/store";
import appIcon from "../../resources/diamond.png?asset";
import LocalServer from "./http-server/http-server"; import LocalServer from "./http-server/http-server";
import errorTypeCheck from "../util/errorTypeCheck"; import store from "./store/store";
import { TestQB } from "./quickbooks-desktop/quickbooks-desktop";
log.initialize(); log.initialize();
const isMac = process.platform === "darwin"; const isMac = process.platform === "darwin";
@@ -171,20 +174,9 @@ function createWindow(): void {
type: "separator", type: "separator",
}, },
{ {
label: "Temp Test Action - Get Token from Renderer", label: "Temp Test Action",
click: (): void => { click: (): void => {
client TestQB();
.request(
`
query jobs{
jobs
{
id}}
`,
)
.then((data) => {
log.info("Data from graffle", data);
});
}, },
}, },
], ],

View File

@@ -0,0 +1,30 @@
using System;
using Interop.QBFC16; // Ensure this matches your DLL version
public class QuickBooksConnector
{
public string ProcessQBXML(string qbxmlRequest)
{
try
{
QBSessionManager sessionManager = new QBSessionManager();
sessionManager.OpenConnection("", "YourAppName");
sessionManager.BeginSession("", ENOpenMode.omDontCare);
IMsgSetRequest requestMsgSet = sessionManager.CreateMsgSetRequest("US", 13, 0);
requestMsgSet.AppendXML(qbxmlRequest);
IMsgSetResponse responseMsgSet = sessionManager.DoRequests(requestMsgSet);
string qbxmlResponse = responseMsgSet.ToXMLString();
sessionManager.EndSession();
sessionManager.CloseConnection();
return qbxmlResponse;
}
catch (Exception ex)
{
return $"Error: {ex.Message}";
}
}
}

View File

@@ -0,0 +1,114 @@
import * as Winax from "winax";
import log from "electron-log/main";
import { Request, Response } from "express";
import { UUID } from "crypto";
import errorTypeCheck from "../../util/errorTypeCheck";
import _ from "lodash";
import store from "../store/store";
export async function handleQuickBookRequest(
req: Request,
res: Response,
): Promise<void> {
//TODO: Add OS sysstem checking. This can only happen on windows machines.
const QbFilePath: string = `C:\\Users\\PatrickFic\\Development\\FRODO COLLISION.QBW`;
// ||
// (store.get("settings.qbFilePath") as string) F
if (_.isEmpty(QbFilePath)) {
res.status(400).json({ error: "Quickbooks file path not set" });
return;
}
const qbxmlRequestList = req.body as Array<{
id: UUID;
okStatusCodes: Array<string>;
qbxml: string;
}>;
const returnResponse: Array<{
Id: UUID;
Success: boolean;
ErrorMessage: string;
}> = [];
//Connect to the QuickBooks File
let requestProcessor;
try {
requestProcessor = new Winax.Object("QBXMLRP2.RequestProcessor.2");
requestProcessor.OpenConnection(QbFilePath, "ShopPartnerActualRequest");
} catch (error) {
log.error(
"Error instnatiating QuickBooks Request Processor",
QbFilePath,
errorTypeCheck(error),
);
res.status(500).json({ error: "Error connecting to QuickBooks" });
return;
}
const ticket = requestProcessor.BeginSession(QbFilePath, 2); //2 indicated qbFileOpenModeDoNotCare
log.info("Quickbooks Ticket", ticket);
for (const qbxmlRequest of qbxmlRequestList) {
try {
//TODO: Refactor to not create a new connection every time.
const QuickBooksResponse = requestProcessor.ProcessRequest(
ticket,
qbxmlRequest.qbxml,
);
log.info("QuickBooks Raw Response: ", QuickBooksResponse);
returnResponse.push({
Id: qbxmlRequest.id,
Success:
QuickBooksResponse.StatusCode === "0" ||
qbxmlRequest.okStatusCodes.includes(QuickBooksResponse.StatusCode),
ErrorMessage: QuickBooksResponse,
});
} catch (error) {
log.error(
"Error running transaction",
ticket,
qbxmlRequest,
errorTypeCheck(error),
);
}
}
requestProcessor.EndSession(ticket);
requestProcessor.CloseConnection();
res.json(qbxmlRequestList);
}
//This set of functions works.
export function TestQB(): void {
let requestProcessor, ticket;
try {
requestProcessor = new Winax.Object("QBXMLRP.RequestProcessor.1");
const connection = requestProcessor.OpenConnection("", "ShopPartnerOneoFf");
ticket = requestProcessor.BeginSession("", 2); //2 indicated qbFileOFpenModeDoNotCare
const qbre = requestProcessor.ProcessRequest(
ticket,
`<?qbxml version="16.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<AccountQueryRq requestID="1"> </AccountQueryRq>
</QBXMLMsgsRq>
</QBXML>`,
);
} catch (error) {
log.error(
"Error instnatiating QuickBooks Request Processor",
errorTypeCheck(error),
);
return;
}
log.log("Ticket", ticket);
requestProcessor.EndSession(ticket);
requestProcessor.CloseConnection();
return;
}

View File

@@ -4,6 +4,7 @@ const store = new Store({
defaults: { defaults: {
settings: { settings: {
filepaths: [], filepaths: [],
qbFilePath: "",
runWatcherOnStartup: true, runWatcherOnStartup: true,
polling: { polling: {
enabled: false, enabled: false,