@paro.io/expert-shared-components 1.14.75 → 1.14.77

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.
@@ -128,6 +128,19 @@ function buildSessionInput(profile) {
128
128
  states: profile.states || [],
129
129
  };
130
130
  }
131
+ function enrichWithAllocations(p) {
132
+ var _a;
133
+ if (!((_a = p.partners) === null || _a === void 0 ? void 0 : _a.length))
134
+ return p;
135
+ const netIncome = toInt(p.netIncome);
136
+ const ownerComp = toInt(p.ownerComp);
137
+ return Object.assign(Object.assign({}, p), { per_owner_allocations: p.partners.map(partner => ({
138
+ owner_name: partner.name,
139
+ ownership_pct: partner.ownershipPct / 100,
140
+ qbi: Math.round(netIncome * (partner.ownershipPct / 100)),
141
+ w2_wages: Math.round(ownerComp * (partner.ownershipPct / 100)),
142
+ })) });
143
+ }
131
144
  function ShellContainer({ children, fullWidth = false, }) {
132
145
  if (fullWidth) {
133
146
  return react_1.default.createElement(react_1.default.Fragment, null, children);
@@ -293,7 +306,8 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
293
306
  updateSessionId(null);
294
307
  }, [updateSessionId]);
295
308
  const handleProspect = (nextProfile) => __awaiter(void 0, void 0, void 0, function* () {
296
- setProfile(nextProfile);
309
+ const enriched = enrichWithAllocations(nextProfile);
310
+ setProfile(enriched);
297
311
  setIsProspectFlow(true);
298
312
  setProspectData(null);
299
313
  setProspectLoading(true);
@@ -301,7 +315,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
301
315
  try {
302
316
  if (taxAxisApi.generateProspectReport) {
303
317
  const result = yield taxAxisApi.generateProspectReport({
304
- clientProfile: nextProfile,
318
+ clientProfile: enriched,
305
319
  });
306
320
  setProspectData({
307
321
  computePayload: result.computePayload,
@@ -317,7 +331,8 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
317
331
  }
318
332
  });
319
333
  const handleFullAnalysis = (0, react_1.useCallback)((nextProfile) => __awaiter(void 0, void 0, void 0, function* () {
320
- setProfile(nextProfile);
334
+ const enriched = enrichWithAllocations(nextProfile);
335
+ setProfile(enriched);
321
336
  setIsProspectFlow(false);
322
337
  setError(null);
323
338
  setBusyMessage('Creating Tax Axis session...');
@@ -52,7 +52,7 @@ function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, leadC
52
52
  " Savings estimates reflect your current revenue of ",
53
53
  rev >= 1000000 ? "$" + (rev / 1000000).toFixed(1) + "M" : "$" + Math.round(rev / 1000) + "K",
54
54
  ", an effective tax rate of ",
55
- effectiveTaxRate != null ? Math.round((effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate) * 10) / 10 : ((profile.entity === "C-Corporation" ? 21 : parseFloat(profile.federalRate || "24")) + parseFloat(profile.stateRate || "4.95")),
55
+ effectiveTaxRate != null ? Math.round((effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate) * 10) / 10 : ((profile.entity === "C-Corporation" ? 21 : parseFloat(profile.federalRate || "24")) + parseFloat(profile.stateRate || "0")),
56
56
  "%, and ",
57
57
  dataYearsLabel,
58
58
  " of financial data."),
@@ -60,7 +60,7 @@ function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, leadC
60
60
  // C-Corps have a flat 21% federal rate
61
61
  const isCCorp = profile.entity === "C-Corporation";
62
62
  const fedRate = isCCorp ? 21 : parseFloat(profile.federalRate || "24");
63
- const stateRate = parseFloat(profile.stateRate || "4.95");
63
+ const stateRate = parseFloat(profile.stateRate || "0");
64
64
  // If engine provided effective_tax_rate (as a decimal 0-1), convert to percentage
65
65
  const currentRate = effectiveTaxRate != null
66
66
  ? (effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate)
@@ -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
- const documentType = parsedData.documentType || documentId;
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));
@@ -147,6 +147,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
147
147
  if (!fetchUploadedDocuments)
148
148
  return;
149
149
  fetchUploadedDocuments().then((uploaded) => {
150
+ var _a;
150
151
  if (!uploaded.length)
151
152
  return;
152
153
  const parsedByType = {};
@@ -171,14 +172,25 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
171
172
  }));
172
173
  // Populate document ID map in one batch
173
174
  setUploadedDocIds((prev) => (Object.assign(Object.assign({}, prev), nextDocIds)));
174
- // Populate review modal data
175
- for (const doc of uploaded) {
175
+ // Populate review modal data — sort newest-first so the most recent doc wins
176
+ // when multiple docs share the same documentType (e.g. two profit_loss years).
177
+ const uploadedSorted = [...uploaded].sort((a, b) => new Date(String(b.updatedAt || 0)).getTime() - new Date(String(a.updatedAt || 0)).getTime());
178
+ for (const doc of uploadedSorted) {
176
179
  if (!doc.documentType)
177
180
  continue;
178
- if (doc.parsedData)
179
- parsedByType[doc.documentType] = doc.parsedData;
181
+ // Only store parsedData if it has non-empty fields, and don't overwrite
182
+ // a richer entry already stored for this documentType.
183
+ const pData = doc.parsedData;
184
+ const pFields = pData ? ((_a = pData.fields) !== null && _a !== void 0 ? _a : pData) : null;
185
+ if (pFields && Object.keys(pFields).filter((k) => k !== '_line_items').length > 0) {
186
+ if (!(doc.documentType in parsedByType)) {
187
+ parsedByType[doc.documentType] = pData;
188
+ }
189
+ }
180
190
  if (doc.reviewedData && Object.keys(doc.reviewedData).length > 0) {
181
- reviewedByType[doc.documentType] = doc.reviewedData;
191
+ if (!(doc.documentType in reviewedByType)) {
192
+ reviewedByType[doc.documentType] = doc.reviewedData;
193
+ }
182
194
  }
183
195
  }
184
196
  if (Object.keys(parsedByType).length > 0)
@@ -416,7 +428,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
416
428
  : failedCount > 0
417
429
  ? `Continue (${failedCount} failed)`
418
430
  : "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
431
+ 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
432
  ? (fields) => __awaiter(this, void 0, void 0, function* () {
421
433
  const docId = uploadedDocIds[docs.indexOf(reviewDoc)];
422
434
  if (docId)
@@ -60,6 +60,22 @@ function RefineAnalysisSection({ userContext: _userContext, }) {
60
60
  const [expanded, setExpanded] = (0, react_1.useState)(false);
61
61
  const { control, watch, formState: { errors }, } = (0, react_hook_form_1.useFormContext)();
62
62
  const ownsRealEstate = watch("ownsRealEstate");
63
+ const entity = watch("entity");
64
+ const singleOwner = watch("singleOwner");
65
+ const showRoster = (entity === "Partnership" || entity === "LLC") && singleOwner === "No";
66
+ const { fields, append, remove } = (0, react_hook_form_1.useFieldArray)({ control, name: "partners" });
67
+ const watchedPartners = watch("partners") || [];
68
+ const totalPct = watchedPartners.reduce((sum, p) => sum + (Number(p === null || p === void 0 ? void 0 : p.ownershipPct) || 0), 0);
69
+ const pctValid = Math.abs(totalPct - 100) < 0.01;
70
+ (0, react_1.useEffect)(() => {
71
+ if (showRoster && fields.length === 0) {
72
+ append([
73
+ { name: "", ownershipPct: 0, partnerType: "general" },
74
+ { name: "", ownershipPct: 0, partnerType: "general" },
75
+ ]);
76
+ }
77
+ // eslint-disable-next-line react-hooks/exhaustive-deps
78
+ }, [showRoster]);
63
79
  return (react_1.default.createElement(TaxAxisCard_1.TaxAxisCard, null,
64
80
  react_1.default.createElement("div", { onClick: () => setExpanded(prev => !prev), className: "flex items-center justify-between cursor-pointer", style: { marginBottom: expanded ? 10 : 0 } },
65
81
  react_1.default.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-tax-axis-text-3" }, "Refine Analysis"),
@@ -102,6 +118,34 @@ function RefineAnalysisSection({ userContext: _userContext, }) {
102
118
  react_1.default.createElement(react_hook_form_1.Controller, { name: "singleOwner", control: control, render: ({ field }) => (react_1.default.createElement("div", { className: "relative" },
103
119
  react_1.default.createElement("select", Object.assign({}, field, { className: selectCls }), SINGLE_OWNER_OPTIONS.map(o => (react_1.default.createElement("option", { key: o, value: o, className: "bg-tax-axis-surface-2" }, o)))),
104
120
  react_1.default.createElement(Chevron, null))) }))),
121
+ showRoster && (react_1.default.createElement("div", { className: "mt-3 mb-2.5" },
122
+ react_1.default.createElement("div", { className: subHeaderCls, style: { marginTop: 0 } }, "Partner / Member Roster"),
123
+ react_1.default.createElement("div", { className: "space-y-2" }, fields.map((field, idx) => (react_1.default.createElement("div", { key: field.id, className: "flex items-end gap-2" },
124
+ react_1.default.createElement("div", { className: "flex-1 min-w-0" },
125
+ idx === 0 && react_1.default.createElement("label", { className: labelCls }, "Name"),
126
+ react_1.default.createElement(react_hook_form_1.Controller, { name: `partners.${idx}.name`, control: control, render: ({ field: f }) => (react_1.default.createElement("input", Object.assign({}, f, { placeholder: "Partner name", className: inputCls }))) })),
127
+ react_1.default.createElement("div", { className: "w-[90px] shrink-0" },
128
+ idx === 0 && react_1.default.createElement("label", { className: labelCls }, "Ownership %"),
129
+ react_1.default.createElement(react_hook_form_1.Controller, { name: `partners.${idx}.ownershipPct`, control: control, render: ({ field: f }) => (react_1.default.createElement("div", { className: "relative" },
130
+ react_1.default.createElement("input", Object.assign({}, f, { type: "number", min: 0, max: 100, placeholder: "0", className: `${inputCls} pr-6`, onChange: (e) => f.onChange(Number(e.target.value) || 0) })),
131
+ react_1.default.createElement("span", { className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-tax-axis-text-3 text-[13px]" }, "%"))) })),
132
+ react_1.default.createElement("div", { className: "w-[130px] shrink-0" },
133
+ idx === 0 && react_1.default.createElement("label", { className: labelCls }, "Type"),
134
+ react_1.default.createElement(react_hook_form_1.Controller, { name: `partners.${idx}.partnerType`, control: control, render: ({ field: f }) => (react_1.default.createElement("div", { className: "relative" },
135
+ react_1.default.createElement("select", Object.assign({}, f, { className: selectCls }),
136
+ react_1.default.createElement("option", { value: "general", className: "bg-tax-axis-surface-2" }, "General"),
137
+ react_1.default.createElement("option", { value: "limited", className: "bg-tax-axis-surface-2" }, "Limited")),
138
+ react_1.default.createElement(Chevron, null))) })),
139
+ fields.length > 1 && (react_1.default.createElement("button", { type: "button", onClick: () => remove(idx), className: "w-7 h-[38px] shrink-0 flex items-center justify-center text-tax-axis-text-4 hover:text-tax-axis-red transition-colors", title: "Remove partner" },
140
+ react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" },
141
+ react_1.default.createElement("path", { d: "M2 2l8 8M10 2l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })))))))),
142
+ react_1.default.createElement("div", { className: "flex items-center justify-between mt-2 px-1" },
143
+ react_1.default.createElement("button", { type: "button", onClick: () => append({ name: "", ownershipPct: 0, partnerType: "general" }), className: "text-[11px] text-tax-axis-teal-light hover:text-white transition-colors font-tax-axis-body" }, "+ Add Partner"),
144
+ react_1.default.createElement("div", { className: `text-[11px] font-tax-axis-body ${pctValid ? "text-green-400" : "text-tax-axis-red"}` },
145
+ "Total: ",
146
+ totalPct,
147
+ "%",
148
+ !pctValid && " — must equal 100%")))),
105
149
  react_1.default.createElement("div", { className: subHeaderCls }, "Financial Data"),
106
150
  react_1.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3 mb-2.5" },
107
151
  react_1.default.createElement("div", null,
@@ -111,7 +111,7 @@ function StrategyRadar({ profile }) {
111
111
  const profileKey = JSON.stringify(profile);
112
112
  const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(safeProfile), [profileKey]);
113
113
  const fedRate = parseFloat((profile.federalRate || "24").replace("%", "")) / 100 || 0.24;
114
- const stateRate = parseFloat(profile.stateRate || "4.95") / 100;
114
+ const stateRate = parseFloat(profile.stateRate || "0") / 100;
115
115
  const netIncome = parseInt((profile.netIncome || "0").replace(/,/g, "")) || Math.round(rev * 0.3);
116
116
  let loSum = eligibleStrategies.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0);
117
117
  let hiSum = eligibleStrategies.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0);
@@ -17,6 +17,23 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
17
17
  ownsRealEstate: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
18
18
  itemizesDeductions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
19
19
  singleOwner: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
20
+ partners: import("yup/lib/array").RequiredArraySchema<Yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
21
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
22
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
23
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
24
+ }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
25
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
26
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
27
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
28
+ }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
29
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
30
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
31
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
32
+ }>>>, import("yup/lib/types").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
33
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
34
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
35
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
36
+ }>>[] | undefined>;
20
37
  equipmentPurchased: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
21
38
  realEstateValue: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
22
39
  capitalGains: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
@@ -49,6 +66,23 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
49
66
  ownsRealEstate: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
50
67
  itemizesDeductions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
51
68
  singleOwner: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
69
+ partners: import("yup/lib/array").RequiredArraySchema<Yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
70
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
71
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
72
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
73
+ }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
74
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
75
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
76
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
77
+ }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
78
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
79
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
80
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
81
+ }>>>, import("yup/lib/types").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
82
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
83
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
84
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
85
+ }>>[] | undefined>;
52
86
  equipmentPurchased: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
53
87
  realEstateValue: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
54
88
  capitalGains: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
@@ -81,6 +115,23 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
81
115
  ownsRealEstate: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
82
116
  itemizesDeductions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
83
117
  singleOwner: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
118
+ partners: import("yup/lib/array").RequiredArraySchema<Yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
119
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
120
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
121
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
122
+ }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
123
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
124
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
125
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
126
+ }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
127
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
128
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
129
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
130
+ }>>>, import("yup/lib/types").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
131
+ name: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
132
+ ownershipPct: Yup.NumberSchema<number, import("yup/lib/types").AnyObject, number>;
133
+ partnerType: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
134
+ }>>[] | undefined>;
84
135
  equipmentPurchased: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
85
136
  realEstateValue: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
86
137
  capitalGains: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
@@ -42,6 +42,11 @@ exports.intakeSchema = Yup.object().shape({
42
42
  ownsRealEstate: Yup.string().ensure(),
43
43
  itemizesDeductions: Yup.string().ensure(),
44
44
  singleOwner: Yup.string().ensure(),
45
+ partners: Yup.array().of(Yup.object().shape({
46
+ name: Yup.string().ensure(),
47
+ ownershipPct: Yup.number().min(0).max(100).default(0),
48
+ partnerType: Yup.string().oneOf(["general", "limited"]).default("general"),
49
+ })).ensure(),
45
50
  equipmentPurchased: Yup.string().ensure(),
46
51
  realEstateValue: Yup.string().ensure(),
47
52
  capitalGains: Yup.string().ensure(),
@@ -58,7 +63,10 @@ exports.intakeSchema = Yup.object().shape({
58
63
  familyEmployed: Yup.string().ensure(),
59
64
  period: Yup.string().ensure(),
60
65
  });
61
- exports.intakeDefaultValues = {
66
+ /* ── Blackridge Construction Partners, LP — Test Fixture ──
67
+ * Swap _BLACKRIDGE_FIXTURE for _EMPTY_DEFAULTS below to revert.
68
+ */
69
+ const _EMPTY_DEFAULTS = {
62
70
  bizName: "",
63
71
  cpaName: "",
64
72
  entity: "C-Corporation",
@@ -75,6 +83,7 @@ exports.intakeDefaultValues = {
75
83
  ownsRealEstate: "No",
76
84
  itemizesDeductions: "No",
77
85
  singleOwner: "Yes",
86
+ partners: [],
78
87
  equipmentPurchased: "0",
79
88
  realEstateValue: "0",
80
89
  capitalGains: "0",
@@ -91,3 +100,42 @@ exports.intakeDefaultValues = {
91
100
  familyEmployed: "No",
92
101
  period: "YTD",
93
102
  };
103
+ const _BLACKRIDGE_FIXTURE = {
104
+ bizName: "Blackridge Construction Partners, LP",
105
+ cpaName: "Amar",
106
+ entity: "Partnership",
107
+ industry: "Construction",
108
+ revenue: "4800000",
109
+ netIncome: "720000",
110
+ ownerComp: "412000",
111
+ employees: "28",
112
+ year: "2023",
113
+ states: ["TX", "CA"],
114
+ filingStatus: "MFJ",
115
+ age: "52",
116
+ sstb: "No",
117
+ ownsRealEstate: "Yes — Commercial",
118
+ itemizesDeductions: "No",
119
+ singleOwner: "No",
120
+ partners: [
121
+ { name: "Marcus D. Blackridge", ownershipPct: 50, partnerType: "general" },
122
+ { name: "David L. Chen", ownershipPct: 30, partnerType: "general" },
123
+ { name: "Sarah A. Okonkwo", ownershipPct: 20, partnerType: "general" },
124
+ ],
125
+ equipmentPurchased: "193000",
126
+ realEstateValue: "560000",
127
+ capitalGains: "0",
128
+ federalRate: "32%",
129
+ stateRate: "0",
130
+ taxDataYears: "3 years",
131
+ riskTolerance: "3",
132
+ overtimePremium: "34000",
133
+ tipIncomePct: "0",
134
+ retirementContributions: "42000",
135
+ hdhpEnrolled: "Yes",
136
+ hsaContributions: "0",
137
+ wotcHires: "Yes",
138
+ familyEmployed: "No",
139
+ period: "Full Year",
140
+ };
141
+ exports.intakeDefaultValues = _BLACKRIDGE_FIXTURE;
@@ -30,7 +30,7 @@ function EngagementHeader({ profile, palette }) {
30
30
  ["Filing", filingDisplay],
31
31
  ["SSTB", profile.sstb || "No"],
32
32
  ["Fed Rate", fedRateDisplay],
33
- ["State Rate", (profile.stateRate || "4.95") + "%"],
33
+ ["State Rate", (profile.stateRate || "0") + "%"],
34
34
  ["Revenue", "$" + (rev || 0).toLocaleString()],
35
35
  ["Net Income", "$" + (0, compute_1.parseNum)(profile.netIncome).toLocaleString()],
36
36
  ["Owner Comp", "$" + (0, compute_1.parseNum)(profile.ownerComp).toLocaleString()],
@@ -49,17 +49,28 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
49
49
  const remainingNames = eligible.filter(s => !top3.includes(s)).slice(0, 4).map(s => s.name);
50
50
  // ═══ SAVINGS RANGE — backend-authoritative when available ═══
51
51
  const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(profile), [profile]);
52
- const displayLo = backendComputePayload
52
+ let displayLo = backendComputePayload
53
53
  ? Math.round(backendComputePayload.savingsLo / 1000)
54
54
  : Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0) / 1000);
55
- const displayHi = backendComputePayload
55
+ let displayHi = backendComputePayload
56
56
  ? Math.round(backendComputePayload.savingsHi / 1000)
57
57
  : Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0) / 1000);
58
58
  // ═══ GAP BLOCK — backend-authoritative when available ═══
59
59
  const fedRatePct = (_a = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.fedRatePct) !== null && _a !== void 0 ? _a : (parseFloat((profile.federalRate || "24").replace("%", "")) || 24);
60
- const stateRatePct = (_b = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.stateRatePct) !== null && _b !== void 0 ? _b : (parseFloat(profile.stateRate || "4.95") || 4.95);
60
+ const stateRatePct = (_b = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.stateRatePct) !== null && _b !== void 0 ? _b : (parseFloat(profile.stateRate || "0"));
61
61
  const currentTax = (_c = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.currentTax) !== null && _c !== void 0 ? _c : Math.round(((0, compute_1.parseNum)(profile.netIncome) || Math.round((0, compute_1.parseNum)(profile.revenue) * 0.20))
62
62
  * (fedRatePct + stateRatePct) / 100);
63
+ // Cap savings range: hi ≤ currentTax, lo ≤ 60% of hi, K-rounding safety
64
+ if (!backendComputePayload) {
65
+ const currentTaxK = Math.round(currentTax / 1000);
66
+ if (displayHi > currentTaxK)
67
+ displayHi = currentTaxK;
68
+ const loFloor = Math.round(displayHi * 0.60);
69
+ if (displayLo > loFloor)
70
+ displayLo = loFloor;
71
+ if (displayLo >= displayHi && displayHi > 1)
72
+ displayLo = displayHi - 1;
73
+ }
63
74
  const realizedSavings = (_d = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedSavings) !== null && _d !== void 0 ? _d : Math.min(Math.round((displayLo + displayHi) / 2 * 1000 * 0.325), Math.round(currentTax * 0.6));
64
75
  const optimizedTax = (_e = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.optimizedTax) !== null && _e !== void 0 ? _e : (currentTax - realizedSavings);
65
76
  const realizedPctOfCurrent = (_f = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedPctOfCurrent) !== null && _f !== void 0 ? _f : (currentTax > 0 ? Math.round((realizedSavings / currentTax) * 100) : 0);
@@ -63,7 +63,7 @@ function computeAllStrategies(profile) {
63
63
  const hsaExisting = (0, exports.parseNum)(profile.hsaContributions);
64
64
  const retirementExisting = (0, exports.parseNum)(profile.retirementContributions);
65
65
  const fedRate = parseFloat((profile.federalRate || "24").replace("%", "")) / 100 || 0.24;
66
- const stateRate = parseFloat(profile.stateRate || "4.95") / 100;
66
+ const stateRate = parseFloat(profile.stateRate || "0") / 100;
67
67
  const combinedRate = fedRate + stateRate;
68
68
  const seTaxRate = 0.153;
69
69
  const sstb = profile.sstb || "No";
@@ -139,11 +139,19 @@ function computeAllStrategies(profile) {
139
139
  }
140
140
  /* #15 SALT/PTE Optimization */
141
141
  {
142
- const statesTaxPaid = Math.round(net * stateRate);
142
+ // When primary state rate is 0 (e.g. TX), use highest nexus state rate
143
+ let pteStateRate = stateRate;
144
+ if (pteStateRate === 0 && (profile.states || []).length > 1) {
145
+ const STATE_TOP_RATES = { CA: 13.30, NY: 10.90, NJ: 10.75, HI: 11.00, OR: 9.90, MN: 9.85, DC: 10.75, VT: 8.75, WI: 7.65, ME: 7.15, CT: 6.99, NE: 6.64, DE: 6.60, MT: 6.75, SC: 6.50, RI: 5.99, ID: 5.80, NM: 5.90, VA: 5.75, MD: 5.75, KS: 5.70, GA: 5.39, WV: 5.12, MA: 5.00, MS: 5.00, AL: 5.00, MO: 4.95, IL: 4.95, UT: 4.65, OK: 4.75, NC: 4.50, CO: 4.40, AR: 4.40, MI: 4.25, LA: 4.25, KY: 4.00, OH: 3.75, IN: 3.05, PA: 3.07, AZ: 2.50, ND: 1.95 };
146
+ const nexusRates = (profile.states || []).map(s => { var _a; return ((_a = STATE_TOP_RATES[s]) !== null && _a !== void 0 ? _a : 0) / 100; }).filter(r => r > 0);
147
+ if (nexusRates.length > 0)
148
+ pteStateRate = Math.max(...nexusRates);
149
+ }
150
+ const statesTaxPaid = Math.round(net * pteStateRate);
143
151
  const pteSavings = Math.round(statesTaxPaid * 0.70);
144
152
  results.set(15, { lo: Math.max(Math.round(pteSavings * 0.80), 0), hi: Math.max(Math.round(pteSavings * 1.10), 0),
145
- sources: [{ doc: "Intake", line: "Net Income", value: `$${net.toLocaleString()}`, field: "Pass-through Income" }, { doc: "Intake", line: "State Rate", value: `${(stateRate * 100).toFixed(2)}%`, field: "Marginal State Rate" }, { doc: "OBBBA \u00A770109", line: "SALT Cap", value: "$40,400", field: "2026 Cap" }],
146
- trace: [{ n: 1, step: "Entity-level state tax", formula: `${net.toLocaleString()} \u00D7 ${(stateRate * 100).toFixed(2)}%`, result: `$${statesTaxPaid.toLocaleString()}`, src: "Intake" }, { n: 2, step: "PTE deduction value", formula: "Entity-level deduction bypasses SALT cap", result: `$${statesTaxPaid.toLocaleString()}`, src: "Notice 2020-75" }, { n: 3, step: "Net savings (credit offset)", formula: `${statesTaxPaid.toLocaleString()} \u00D7 70% recovery`, result: `$${pteSavings.toLocaleString()}`, src: "State credit" }] });
153
+ sources: [{ doc: "Intake", line: "Net Income", value: `$${net.toLocaleString()}`, field: "Pass-through Income" }, { doc: "Intake", line: "State Rate", value: `${(pteStateRate * 100).toFixed(2)}%`, field: "Marginal State Rate" }, { doc: "OBBBA \u00A770109", line: "SALT Cap", value: "$40,400", field: "2026 Cap" }],
154
+ trace: [{ n: 1, step: "Entity-level state tax", formula: `${net.toLocaleString()} \u00D7 ${(pteStateRate * 100).toFixed(2)}%`, result: `$${statesTaxPaid.toLocaleString()}`, src: "Intake" }, { n: 2, step: "PTE deduction value", formula: "Entity-level deduction bypasses SALT cap", result: `$${statesTaxPaid.toLocaleString()}`, src: "Notice 2020-75" }, { n: 3, step: "Net savings (credit offset)", formula: `${statesTaxPaid.toLocaleString()} \u00D7 70% recovery`, result: `$${pteSavings.toLocaleString()}`, src: "State credit" }] });
147
155
  }
148
156
  /* #22 Overtime Pay Deduction (OBBBA) */
149
157
  if (overtime > 0) {
@@ -39,58 +39,107 @@ exports.DOCUMENT_FIELD_CATALOG = {
39
39
  sections: [
40
40
  {
41
41
  head: "Revenue",
42
- fields: ["total_revenue", "gross_revenue", "cost_of_goods_sold", "gross_profit"],
42
+ fields: ["total_revenue", "cost_of_goods_sold", "gross_profit"],
43
43
  },
44
44
  {
45
45
  head: "Operating Expenses",
46
- fields: ["total_expenses", "payroll_expense", "rent_expense", "depreciation", "other_expenses"],
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: ["net_income", "net_margin"],
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
- total_expenses: { label: "Total Operating Expenses" },
59
- payroll_expense: { label: "Payroll & Compensation" },
68
+ total_operating_expenses: { label: "Total Operating Expenses" },
69
+ salaries_wages_staff: { label: "Salaries & Wages" },
60
70
  rent_expense: { label: "Rent / Lease" },
61
- depreciation: { label: "Depreciation & Amortization" },
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: ["total_assets", "current_assets", "cash", "accounts_receivable", "fixed_assets"],
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: ["total_liabilities", "current_liabilities", "accounts_payable", "long_term_debt"],
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: ["total_equity", "retained_earnings"],
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
- current_assets: { label: "Current Assets" },
85
- cash: { label: "Cash & Equivalents" },
124
+ cash_and_equivalents: { label: "Cash & Equivalents" },
86
125
  accounts_receivable: { label: "Accounts Receivable" },
87
- fixed_assets: { label: "Fixed Assets (Net)" },
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: {
@@ -58,6 +58,11 @@ export interface Strategy {
58
58
  positionStrength?: string;
59
59
  specialistNote?: string;
60
60
  }
61
+ export interface PartnerInfo {
62
+ name: string;
63
+ ownershipPct: number;
64
+ partnerType: 'general' | 'limited';
65
+ }
61
66
  export interface ClientProfile {
62
67
  bizName: string;
63
68
  cpaName: string;
@@ -75,6 +80,13 @@ export interface ClientProfile {
75
80
  ownsRealEstate: "No" | "Yes — Commercial" | "Yes — Residential" | "Yes — Both";
76
81
  itemizesDeductions: "Yes" | "No" | "Unsure";
77
82
  singleOwner: "Yes" | "No";
83
+ partners?: PartnerInfo[];
84
+ per_owner_allocations?: Array<{
85
+ owner_name: string;
86
+ ownership_pct: number;
87
+ qbi?: number;
88
+ w2_wages?: number;
89
+ }>;
78
90
  equipmentPurchased: string;
79
91
  realEstateValue: string;
80
92
  capitalGains: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paro.io/expert-shared-components",
3
- "version": "1.14.75",
3
+ "version": "1.14.77",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {