@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.
- package/lib/components/TaxAxis/TaxAxisShell.js +18 -3
- package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +2 -2
- package/lib/tax-axis/components/intake/RefineAnalysisSection.js +44 -0
- package/lib/tax-axis/components/intake/StrategyRadar.js +1 -1
- package/lib/tax-axis/components/intake/intakeSchema.d.ts +51 -0
- package/lib/tax-axis/components/intake/intakeSchema.js +49 -1
- package/lib/tax-axis/components/preparerWorkpaper/EngagementHeader.js +1 -1
- package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.js +14 -3
- package/lib/tax-axis/lib/compute/index.js +12 -4
- package/lib/tax-axis/lib/types/index.d.ts +12 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
|
|
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 || "
|
|
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 || "
|
|
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 || "
|
|
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
|
-
|
|
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 || "
|
|
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
|
-
|
|
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
|
-
|
|
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 || "
|
|
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 || "
|
|
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
|
-
|
|
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: `${(
|
|
146
|
-
trace: [{ n: 1, step: "Entity-level state tax", formula: `${net.toLocaleString()} \u00D7 ${(
|
|
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;
|