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

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)
@@ -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) {
@@ -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.76",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {