@paro.io/expert-shared-components 1.14.76 → 1.14.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/TaxAxis/TaxAxisShell.d.ts +1 -1
- package/lib/components/TaxAxis/TaxAxisShell.js +2 -2
- package/lib/components/TaxAxis/types.d.ts +1 -0
- package/lib/tax-axis/components/documents/DocumentReviewModal.d.ts +2 -1
- package/lib/tax-axis/components/documents/DocumentReviewModal.js +4 -3
- package/lib/tax-axis/components/documents/TaxAxisDocuments.d.ts +2 -1
- package/lib/tax-axis/components/documents/TaxAxisDocuments.js +21 -8
- package/lib/tax-axis/components/documents/qbo/types.d.ts +2 -0
- package/lib/tax-axis/components/documents/qbo/useQboFlow.d.ts +2 -2
- package/lib/tax-axis/components/documents/qbo/useQboFlow.js +31 -38
- package/lib/tax-axis/lib/documentFieldCatalog.js +64 -15
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { TaxAxisShellProps } from './types';
|
|
2
|
-
export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }: TaxAxisShellProps) => JSX.Element;
|
|
2
|
+
export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, qboRedirectUri, expertId, qboConnected, qboCompanyName, }: TaxAxisShellProps) => JSX.Element;
|
|
3
3
|
export default TaxAxisShell;
|
|
@@ -148,7 +148,7 @@ function ShellContainer({ children, fullWidth = false, }) {
|
|
|
148
148
|
return (react_1.default.createElement("div", { className: 'min-h-screen bg-tax-axis-navy text-white font-tax-axis-body' },
|
|
149
149
|
react_1.default.createElement("div", { className: 'max-w-[960px] mx-auto px-5 py-7' }, children)));
|
|
150
150
|
}
|
|
151
|
-
const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }) => {
|
|
151
|
+
const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, qboRedirectUri, expertId, qboConnected, qboCompanyName, }) => {
|
|
152
152
|
const [step, setStep] = (0, react_1.useState)('SESSION_SETUP');
|
|
153
153
|
const [profile, setProfile] = (0, react_1.useState)(initialProfile ? Object.assign({}, initialProfile) : null);
|
|
154
154
|
const [sessionId, setSessionId] = (0, react_1.useState)(initialSessionId || null);
|
|
@@ -556,7 +556,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
556
556
|
});
|
|
557
557
|
}), jobId: sessionId || undefined, onSaveReviewedField: (documentId, reviewedData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
558
558
|
yield taxAxisApi.saveReviewedData(documentId, reviewedData);
|
|
559
|
-
}), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP'), qboConnected: qboConnectedState, qboCompanyName: qboCompanyNameState, qboAuthorizeUrl: qboAuthorizeUrl, qboClientConfirmed: qboConnectedState, onImportQboReport: taxAxisApi.importQboReport ? handleImportQboReport : undefined, onQboConnected: (companyName) => {
|
|
559
|
+
}), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP'), qboConnected: qboConnectedState, qboCompanyName: qboCompanyNameState, qboAuthorizeUrl: qboAuthorizeUrl, qboRedirectUri: qboRedirectUri, qboClientConfirmed: qboConnectedState, onImportQboReport: taxAxisApi.importQboReport ? handleImportQboReport : undefined, onQboConnected: (companyName) => {
|
|
560
560
|
setQboConnectedState(true);
|
|
561
561
|
setQboCompanyNameState(companyName);
|
|
562
562
|
}, onQboDisconnected: () => {
|
|
@@ -6,8 +6,9 @@ export interface DocumentReviewModalProps {
|
|
|
6
6
|
documentName: string;
|
|
7
7
|
fileName: string;
|
|
8
8
|
jobId: string;
|
|
9
|
+
documentType?: string;
|
|
9
10
|
parsedData?: Record<string, unknown> | null;
|
|
10
11
|
onSaveReviewedData?: (fields: Record<string, string>) => Promise<void>;
|
|
11
12
|
onClose: () => void;
|
|
12
13
|
}
|
|
13
|
-
export declare function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, parsedData, onSaveReviewedData, onClose, }: DocumentReviewModalProps): React.JSX.Element;
|
|
14
|
+
export declare function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, documentType: documentTypeProp, parsedData, onSaveReviewedData, onClose, }: DocumentReviewModalProps): React.JSX.Element;
|
|
@@ -138,7 +138,7 @@ function getTotalFieldCount(sections) {
|
|
|
138
138
|
return sections.reduce((sum, s) => sum + s.fields.length, 0);
|
|
139
139
|
}
|
|
140
140
|
// ── Component ──
|
|
141
|
-
function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, parsedData, onSaveReviewedData, onClose, }) {
|
|
141
|
+
function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, documentType: documentTypeProp, parsedData, onSaveReviewedData, onClose, }) {
|
|
142
142
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
143
143
|
const [sections, setSections] = (0, react_1.useState)([]);
|
|
144
144
|
const [saveStatus, setSaveStatus] = (0, react_1.useState)("idle");
|
|
@@ -147,7 +147,8 @@ function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId
|
|
|
147
147
|
var _a;
|
|
148
148
|
if (parsedData && typeof parsedData === "object") {
|
|
149
149
|
const fields = ((_a = parsedData.fields) !== null && _a !== void 0 ? _a : parsedData);
|
|
150
|
-
|
|
150
|
+
// Prefer explicit prop, then parsedData.documentType, never fall back to documentId (UUID)
|
|
151
|
+
const documentType = documentTypeProp || parsedData.documentType || "";
|
|
151
152
|
setSections(buildSectionsFromCatalog(documentType, fields));
|
|
152
153
|
setLoading(false);
|
|
153
154
|
return;
|
|
@@ -158,7 +159,7 @@ function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId
|
|
|
158
159
|
setLoading(false);
|
|
159
160
|
}, 400);
|
|
160
161
|
return () => clearTimeout(t);
|
|
161
|
-
}, [documentId, parsedData]);
|
|
162
|
+
}, [documentId, documentTypeProp, parsedData]);
|
|
162
163
|
const handleFieldChange = (0, react_1.useCallback)((sectionIdx, fieldIdx, newValue) => {
|
|
163
164
|
setSections((prev) => prev.map((sec, si) => si === sectionIdx
|
|
164
165
|
? Object.assign(Object.assign({}, sec), { fields: sec.fields.map((f, fi) => fi === fieldIdx ? Object.assign(Object.assign({}, f), { value: newValue }) : f) }) : sec));
|
|
@@ -31,6 +31,7 @@ export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
|
|
|
31
31
|
qboConnected?: boolean;
|
|
32
32
|
qboCompanyName?: string | null;
|
|
33
33
|
qboAuthorizeUrl?: string;
|
|
34
|
+
qboRedirectUri?: string;
|
|
34
35
|
onImportQboReport?: (sessionId: string, realmId: string, reportType: string, accountingMethod: string) => Promise<any>;
|
|
35
36
|
onQboConnected?: (companyName: string) => void;
|
|
36
37
|
onQboDisconnected?: () => void;
|
|
@@ -40,4 +41,4 @@ export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
|
|
|
40
41
|
}[]) => void;
|
|
41
42
|
qboClientConfirmed?: boolean;
|
|
42
43
|
}
|
|
43
|
-
export declare function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId, onSaveReviewedField, userContext: _userContext, qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed, }: TaxAxisDocumentsProps): React.JSX.Element;
|
|
44
|
+
export declare function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId, onSaveReviewedField, userContext: _userContext, qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed: _qboClientConfirmed, }: TaxAxisDocumentsProps): React.JSX.Element;
|
|
@@ -77,7 +77,7 @@ const TIER_DEFS = [
|
|
|
77
77
|
ids: [],
|
|
78
78
|
},
|
|
79
79
|
];
|
|
80
|
-
function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId = "stub-job-id", onSaveReviewedField, userContext: _userContext = "expert", qboConnected = false, qboCompanyName, qboAuthorizeUrl, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed, }) {
|
|
80
|
+
function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId = "stub-job-id", onSaveReviewedField, userContext: _userContext = "expert", qboConnected = false, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed: _qboClientConfirmed, }) {
|
|
81
81
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
82
82
|
const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(entityType !== null && entityType !== void 0 ? entityType : undefined), [entityType]);
|
|
83
83
|
// Build tier defs dynamically from the doc spec list so they're always in sync.
|
|
@@ -124,6 +124,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
|
|
|
124
124
|
qboConnected,
|
|
125
125
|
qboCompanyName,
|
|
126
126
|
qboAuthorizeUrl,
|
|
127
|
+
qboRedirectUri,
|
|
127
128
|
onImportQboReport,
|
|
128
129
|
fetchUploadedDocuments,
|
|
129
130
|
sessionId: jobId,
|
|
@@ -134,7 +135,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
|
|
|
134
135
|
});
|
|
135
136
|
const showQboBanner = !!qboAuthorizeUrl || qboConnected;
|
|
136
137
|
// FIX B: gate document list behind QBO client confirmation or manual skip
|
|
137
|
-
const [clientConfirmed, setClientConfirmed] = (0, react_1.useState)(
|
|
138
|
+
const [clientConfirmed, setClientConfirmed] = (0, react_1.useState)(true);
|
|
138
139
|
// Sync when qboConnected prop updates (e.g. localStorage restore in parent)
|
|
139
140
|
(0, react_1.useEffect)(() => {
|
|
140
141
|
if (qboConnected)
|
|
@@ -147,6 +148,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
|
|
|
147
148
|
if (!fetchUploadedDocuments)
|
|
148
149
|
return;
|
|
149
150
|
fetchUploadedDocuments().then((uploaded) => {
|
|
151
|
+
var _a;
|
|
150
152
|
if (!uploaded.length)
|
|
151
153
|
return;
|
|
152
154
|
const parsedByType = {};
|
|
@@ -171,14 +173,25 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
|
|
|
171
173
|
}));
|
|
172
174
|
// Populate document ID map in one batch
|
|
173
175
|
setUploadedDocIds((prev) => (Object.assign(Object.assign({}, prev), nextDocIds)));
|
|
174
|
-
// Populate review modal data
|
|
175
|
-
|
|
176
|
+
// Populate review modal data — sort newest-first so the most recent doc wins
|
|
177
|
+
// when multiple docs share the same documentType (e.g. two profit_loss years).
|
|
178
|
+
const uploadedSorted = [...uploaded].sort((a, b) => new Date(String(b.updatedAt || 0)).getTime() - new Date(String(a.updatedAt || 0)).getTime());
|
|
179
|
+
for (const doc of uploadedSorted) {
|
|
176
180
|
if (!doc.documentType)
|
|
177
181
|
continue;
|
|
178
|
-
if
|
|
179
|
-
|
|
182
|
+
// Only store parsedData if it has non-empty fields, and don't overwrite
|
|
183
|
+
// a richer entry already stored for this documentType.
|
|
184
|
+
const pData = doc.parsedData;
|
|
185
|
+
const pFields = pData ? ((_a = pData.fields) !== null && _a !== void 0 ? _a : pData) : null;
|
|
186
|
+
if (pFields && Object.keys(pFields).filter((k) => k !== '_line_items').length > 0) {
|
|
187
|
+
if (!(doc.documentType in parsedByType)) {
|
|
188
|
+
parsedByType[doc.documentType] = pData;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
180
191
|
if (doc.reviewedData && Object.keys(doc.reviewedData).length > 0) {
|
|
181
|
-
|
|
192
|
+
if (!(doc.documentType in reviewedByType)) {
|
|
193
|
+
reviewedByType[doc.documentType] = doc.reviewedData;
|
|
194
|
+
}
|
|
182
195
|
}
|
|
183
196
|
}
|
|
184
197
|
if (Object.keys(parsedByType).length > 0)
|
|
@@ -416,7 +429,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
|
|
|
416
429
|
: failedCount > 0
|
|
417
430
|
? `Continue (${failedCount} failed)`
|
|
418
431
|
: "Continue")))),
|
|
419
|
-
reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: jobId, parsedData: reviewParsedData, onSaveReviewedData: onSaveReviewedField
|
|
432
|
+
reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: jobId, documentType: reviewDoc.documentType || undefined, parsedData: reviewParsedData, onSaveReviewedData: onSaveReviewedField
|
|
420
433
|
? (fields) => __awaiter(this, void 0, void 0, function* () {
|
|
421
434
|
const docId = uploadedDocIds[docs.indexOf(reviewDoc)];
|
|
422
435
|
if (docId)
|
|
@@ -29,6 +29,8 @@ export interface QboFlowProps {
|
|
|
29
29
|
qboCompanyName?: string | null;
|
|
30
30
|
qboAuthorizeUrl?: string;
|
|
31
31
|
qboGetClientsUrl?: string;
|
|
32
|
+
/** Override the OAuth redirect URI (e.g. /tax-axis/quickbooks for in-context connect) */
|
|
33
|
+
qboRedirectUri?: string;
|
|
32
34
|
/** Selected tax year from the session profile — pre-fills the year dropdown */
|
|
33
35
|
profileYear?: number;
|
|
34
36
|
onImportQboReport?: (sessionId: string, realmId: string, reportType: string, accountingMethod: string, year?: number) => Promise<any>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QboCompany, QboModalStep, QboFlowProps } from "./types";
|
|
2
|
-
export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }: QboFlowProps): {
|
|
2
|
+
export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }: QboFlowProps): {
|
|
3
3
|
modalStep: QboModalStep;
|
|
4
4
|
selectedCompany: QboCompany | null;
|
|
5
5
|
selectedReports: string[];
|
|
@@ -10,7 +10,7 @@ export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeU
|
|
|
10
10
|
companies: QboCompany[];
|
|
11
11
|
openPermissions: () => void;
|
|
12
12
|
openImport: () => void;
|
|
13
|
-
handlePermissionsContinue: () => void
|
|
13
|
+
handlePermissionsContinue: () => Promise<void>;
|
|
14
14
|
handleCompanySelected: (company: QboCompany) => void;
|
|
15
15
|
handleReportsConfirmed: (reportIds: string[]) => Promise<void>;
|
|
16
16
|
handleMappingConfirm: () => Promise<void>;
|
|
@@ -21,14 +21,13 @@ function buildCompany(companyName) {
|
|
|
21
21
|
.join("");
|
|
22
22
|
return { id: "connected", name, initials, color: "#2CA01C" };
|
|
23
23
|
}
|
|
24
|
-
function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }) {
|
|
24
|
+
function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }) {
|
|
25
25
|
const [modalStep, setModalStep] = (0, react_1.useState)("closed");
|
|
26
26
|
const [selectedCompany, setSelectedCompany] = (0, react_1.useState)(qboConnected ? buildCompany(qboCompanyName) : null);
|
|
27
27
|
const [selectedReports, setSelectedReports] = (0, react_1.useState)([]);
|
|
28
28
|
const [selectedYear, setSelectedYear] = (0, react_1.useState)(profileYear !== null && profileYear !== void 0 ? profileYear : new Date().getFullYear());
|
|
29
29
|
const [importStepIndex, setImportStepIndex] = (0, react_1.useState)(0);
|
|
30
30
|
const [error, setError] = (0, react_1.useState)(null);
|
|
31
|
-
const pollTimerRef = (0, react_1.useRef)(null);
|
|
32
31
|
// Keep selectedYear in sync if profileYear prop changes
|
|
33
32
|
const prevProfileYear = (0, react_1.useRef)(profileYear);
|
|
34
33
|
if (profileYear !== prevProfileYear.current) {
|
|
@@ -41,13 +40,6 @@ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQbo
|
|
|
41
40
|
if (qboConnected)
|
|
42
41
|
setSelectedCompany(buildCompany(qboCompanyName));
|
|
43
42
|
}, [qboConnected, qboCompanyName]);
|
|
44
|
-
// Cleanup poll timer on unmount
|
|
45
|
-
(0, react_1.useEffect)(() => {
|
|
46
|
-
return () => {
|
|
47
|
-
if (pollTimerRef.current)
|
|
48
|
-
clearInterval(pollTimerRef.current);
|
|
49
|
-
};
|
|
50
|
-
}, []);
|
|
51
43
|
// ── Already connected: skip straight to report selection ──
|
|
52
44
|
const openImport = (0, react_1.useCallback)(() => {
|
|
53
45
|
setError(null);
|
|
@@ -59,40 +51,41 @@ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQbo
|
|
|
59
51
|
setError(null);
|
|
60
52
|
setModalStep("permissions");
|
|
61
53
|
}, []);
|
|
62
|
-
// ── After permissions:
|
|
63
|
-
const handlePermissionsContinue = (0, react_1.useCallback)(() => {
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
if (isDemoMode) {
|
|
67
|
-
setSelectedCompany(buildCompany(qboCompanyName));
|
|
68
|
-
setModalStep("client_selector");
|
|
54
|
+
// ── After permissions: POST to authorize endpoint to get Intuit OAuth URL, then open popup ──
|
|
55
|
+
const handlePermissionsContinue = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
if (!qboAuthorizeUrl) {
|
|
57
|
+
setError("QuickBooks authorization URL is not configured.");
|
|
69
58
|
return;
|
|
70
59
|
}
|
|
71
60
|
setModalStep("oauth_pending");
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
elapsed += 500;
|
|
82
|
-
if (popup && popup.closed) {
|
|
83
|
-
if (pollTimerRef.current)
|
|
84
|
-
clearInterval(pollTimerRef.current);
|
|
85
|
-
// Parent prop update will trigger the useEffect above to set selectedCompany
|
|
86
|
-
setModalStep("client_selector");
|
|
61
|
+
try {
|
|
62
|
+
const redirectUri = qboRedirectUri !== null && qboRedirectUri !== void 0 ? qboRedirectUri : `${window.location.origin}/integrations-hub/callback`;
|
|
63
|
+
const res = yield fetch(qboAuthorizeUrl, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": "application/json" },
|
|
66
|
+
body: JSON.stringify({ redirectUri, environment: "sandbox" }),
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
throw new Error(`Authorization request failed: ${res.status}`);
|
|
87
70
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
setError("QuickBooks authorization timed out. Please try again.");
|
|
92
|
-
setModalStep("closed");
|
|
71
|
+
const data = yield res.json();
|
|
72
|
+
if (!data.authUri) {
|
|
73
|
+
throw new Error("No authorization URL returned.");
|
|
93
74
|
}
|
|
94
|
-
|
|
95
|
-
|
|
75
|
+
// Persist session context so the callback page can resume the correct session
|
|
76
|
+
if (sessionId) {
|
|
77
|
+
localStorage.setItem("taxaxis_pending_session", JSON.stringify({ sessionId, ts: Date.now() }));
|
|
78
|
+
}
|
|
79
|
+
localStorage.setItem("current_integration", "quickbooks");
|
|
80
|
+
// Full-page redirect — same as integrations hub. Intuit will redirect back
|
|
81
|
+
// to the registered redirectUri (/tax-axis/quickbooks) when complete.
|
|
82
|
+
window.location.href = data.authUri;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
setError(err.message || "Failed to start QuickBooks authorization.");
|
|
86
|
+
setModalStep("closed");
|
|
87
|
+
}
|
|
88
|
+
}), [qboAuthorizeUrl, qboRedirectUri, sessionId]);
|
|
96
89
|
// ── Company selected ──
|
|
97
90
|
const handleCompanySelected = (0, react_1.useCallback)((company) => {
|
|
98
91
|
setSelectedCompany(company);
|
|
@@ -39,58 +39,107 @@ exports.DOCUMENT_FIELD_CATALOG = {
|
|
|
39
39
|
sections: [
|
|
40
40
|
{
|
|
41
41
|
head: "Revenue",
|
|
42
|
-
fields: ["total_revenue", "
|
|
42
|
+
fields: ["total_revenue", "cost_of_goods_sold", "gross_profit"],
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
head: "Operating Expenses",
|
|
46
|
-
fields: [
|
|
46
|
+
fields: [
|
|
47
|
+
"total_operating_expenses",
|
|
48
|
+
"salaries_wages_staff",
|
|
49
|
+
"rent_expense",
|
|
50
|
+
"depreciation_expense",
|
|
51
|
+
"amortization_expense",
|
|
52
|
+
"advertising_marketing",
|
|
53
|
+
"professional_fees",
|
|
54
|
+
"insurance_nonhealth",
|
|
55
|
+
"business_meals",
|
|
56
|
+
"other_expenses",
|
|
57
|
+
],
|
|
47
58
|
},
|
|
48
59
|
{
|
|
49
60
|
head: "Net Income",
|
|
50
|
-
fields: ["
|
|
61
|
+
fields: ["net_operating_income", "net_income"],
|
|
51
62
|
},
|
|
52
63
|
],
|
|
53
64
|
fields: {
|
|
54
65
|
total_revenue: { label: "Total Revenue" },
|
|
55
|
-
gross_revenue: { label: "Gross Revenue" },
|
|
56
66
|
cost_of_goods_sold: { label: "Cost of Goods Sold (COGS)" },
|
|
57
67
|
gross_profit: { label: "Gross Profit" },
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
total_operating_expenses: { label: "Total Operating Expenses" },
|
|
69
|
+
salaries_wages_staff: { label: "Salaries & Wages" },
|
|
60
70
|
rent_expense: { label: "Rent / Lease" },
|
|
61
|
-
|
|
71
|
+
depreciation_expense: { label: "Depreciation" },
|
|
72
|
+
amortization_expense: { label: "Amortization" },
|
|
73
|
+
advertising_marketing: { label: "Advertising & Marketing" },
|
|
74
|
+
professional_fees: { label: "Professional Fees" },
|
|
75
|
+
insurance_nonhealth: { label: "Insurance" },
|
|
76
|
+
business_meals: { label: "Meals & Entertainment" },
|
|
62
77
|
other_expenses: { label: "Other Expenses" },
|
|
78
|
+
net_operating_income: { label: "Net Operating Income" },
|
|
63
79
|
net_income: { label: "Net Income" },
|
|
64
|
-
net_margin: { label: "Net Profit Margin" },
|
|
65
80
|
},
|
|
66
81
|
},
|
|
67
82
|
balance_sheet: {
|
|
68
83
|
sections: [
|
|
69
84
|
{
|
|
70
85
|
head: "Assets",
|
|
71
|
-
fields: [
|
|
86
|
+
fields: [
|
|
87
|
+
"total_current_assets",
|
|
88
|
+
"total_assets",
|
|
89
|
+
"cash_and_equivalents",
|
|
90
|
+
"accounts_receivable",
|
|
91
|
+
"inventory",
|
|
92
|
+
"prepaid_expenses",
|
|
93
|
+
"property_equipment_net",
|
|
94
|
+
"accumulated_depreciation",
|
|
95
|
+
],
|
|
72
96
|
},
|
|
73
97
|
{
|
|
74
98
|
head: "Liabilities",
|
|
75
|
-
fields: [
|
|
99
|
+
fields: [
|
|
100
|
+
"total_current_liabilities",
|
|
101
|
+
"total_liabilities",
|
|
102
|
+
"accounts_payable",
|
|
103
|
+
"accrued_liabilities",
|
|
104
|
+
"deferred_revenue",
|
|
105
|
+
"short_term_debt",
|
|
106
|
+
"long_term_debt",
|
|
107
|
+
"total_liabilities_and_equity",
|
|
108
|
+
],
|
|
76
109
|
},
|
|
77
110
|
{
|
|
78
111
|
head: "Equity",
|
|
79
|
-
fields: [
|
|
112
|
+
fields: [
|
|
113
|
+
"total_equity",
|
|
114
|
+
"retained_earnings",
|
|
115
|
+
"owners_equity",
|
|
116
|
+
"common_stock_par_value",
|
|
117
|
+
"additional_paid_in_capital",
|
|
118
|
+
],
|
|
80
119
|
},
|
|
81
120
|
],
|
|
82
121
|
fields: {
|
|
122
|
+
total_current_assets: { label: "Total Current Assets" },
|
|
83
123
|
total_assets: { label: "Total Assets" },
|
|
84
|
-
|
|
85
|
-
cash: { label: "Cash & Equivalents" },
|
|
124
|
+
cash_and_equivalents: { label: "Cash & Equivalents" },
|
|
86
125
|
accounts_receivable: { label: "Accounts Receivable" },
|
|
87
|
-
|
|
126
|
+
inventory: { label: "Inventory" },
|
|
127
|
+
prepaid_expenses: { label: "Prepaid Expenses" },
|
|
128
|
+
property_equipment_net: { label: "Fixed Assets (Net)" },
|
|
129
|
+
accumulated_depreciation: { label: "Accumulated Depreciation" },
|
|
130
|
+
total_current_liabilities: { label: "Total Current Liabilities" },
|
|
88
131
|
total_liabilities: { label: "Total Liabilities" },
|
|
89
|
-
current_liabilities: { label: "Current Liabilities" },
|
|
90
132
|
accounts_payable: { label: "Accounts Payable" },
|
|
133
|
+
accrued_liabilities: { label: "Accrued Liabilities" },
|
|
134
|
+
deferred_revenue: { label: "Deferred Revenue" },
|
|
135
|
+
short_term_debt: { label: "Short-Term Debt" },
|
|
91
136
|
long_term_debt: { label: "Long-Term Debt" },
|
|
137
|
+
total_liabilities_and_equity: { label: "Total Liabilities & Equity" },
|
|
92
138
|
total_equity: { label: "Total Equity" },
|
|
93
139
|
retained_earnings: { label: "Retained Earnings" },
|
|
140
|
+
owners_equity: { label: "Owner's Equity" },
|
|
141
|
+
common_stock_par_value: { label: "Common Stock" },
|
|
142
|
+
additional_paid_in_capital: { label: "Opening Balance Equity" },
|
|
94
143
|
},
|
|
95
144
|
},
|
|
96
145
|
schedule_k1_s_corp: {
|