@paro.io/expert-shared-components 1.14.69 → 1.14.71
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 +6 -2
- package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +10 -6
- package/lib/tax-axis/components/clientReport/ImplementationRoadmap.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/ImplementationRoadmap.js +11 -8
- package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.js +9 -3
- package/lib/tax-axis/components/clientReport/QuarterlyCashChart.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/QuarterlyCashChart.js +9 -3
- package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +9 -2
- package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +3 -1
- package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +5 -2
- package/lib/tax-axis/components/intake/ClientParametersSection.js +14 -4
- package/lib/tax-axis/components/intake/intakeSchema.js +1 -1
- package/lib/tax-axis/components/preparerWorkpaper/EngagementHeader.js +6 -2
- package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +3 -3
- package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +24 -1
- package/lib/tax-axis/lib/adapters/useEngineOutput.js +113 -5
- package/lib/tax-axis/lib/data/nextSteps.js +13 -13
- package/lib/tax-axis/lib/data/sourceDescriptions.js +6 -0
- package/lib/tax-axis/lib/types/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -155,8 +155,12 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
155
155
|
react_1.default.useEffect(() => { setQboCompanyNameState(qboCompanyName !== null && qboCompanyName !== void 0 ? qboCompanyName : null); }, [qboCompanyName]);
|
|
156
156
|
// Derive live strategies from engineOutput so CLIENT_REPORT and PREPARER_WORKPAPER
|
|
157
157
|
// render real engine data instead of the static STRATEGIES catalog.
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
// preComputed comes from the formula engine — only strategies with status=COMPLETE
|
|
159
|
+
// should appear in reports. Without preComputed, the hook falls back to
|
|
160
|
+
// _reconciledPreComputed flags on strategy_analysis entries.
|
|
161
|
+
const engineOutput = (0, react_1.useMemo)(() => { var _a, _b; return (_b = (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput) !== null && _a !== void 0 ? _a : llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) !== null && _b !== void 0 ? _b : null; }, [llmResult]);
|
|
162
|
+
const preComputed = (0, react_1.useMemo)(() => { var _a, _b; return (_b = (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.meta) === null || _a === void 0 ? void 0 : _a.preComputed) !== null && _b !== void 0 ? _b : null; }, [llmResult]);
|
|
163
|
+
const adapted = (0, useEngineOutput_1.useEngineOutput)(engineOutput, preComputed);
|
|
160
164
|
const fetchAndSetParsedDocuments = (0, react_1.useCallback)((sid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
165
|
try {
|
|
162
166
|
const docs = yield taxAxisApi.getDocuments(sid);
|
|
@@ -8,13 +8,15 @@ const react_1 = __importDefault(require("react"));
|
|
|
8
8
|
const compute_1 = require("../../lib/compute");
|
|
9
9
|
const SectionOpener_1 = require("./SectionOpener");
|
|
10
10
|
const ETRChart_1 = require("./ETRChart");
|
|
11
|
+
// Format K values: >= 1000K -> "$X.XM", otherwise "$XK"
|
|
12
|
+
const fmtKRange = (k) => k >= 1000 ? "$" + (k / 1000).toFixed(1) + "M" : "$" + k + "K";
|
|
11
13
|
function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCount, top3, palette, effectiveTaxRate, confidenceTier, dataYears }) {
|
|
12
14
|
const bizName = profile.bizName || "Client";
|
|
13
15
|
const rev = parseInt((profile.revenue || "0").replace(/,/g, "")) || 500000;
|
|
14
16
|
const dataYearsLabel = dataYears ? `${dataYears} year${dataYears > 1 ? "s" : ""}` : (profile.taxDataYears || "1 year");
|
|
15
17
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
16
18
|
react_1.default.createElement(SectionOpener_1.SectionOpener, { number: "01", eyebrow: "EXECUTIVE SUMMARY", headline: "Where " + bizName + "'s tax dollars are going \u2014 and where they don't have to.", bullets: [
|
|
17
|
-
eligible.length + " strategies apply to your current profile, with combined estimated savings of
|
|
19
|
+
eligible.length + " strategies apply to your current profile, with combined estimated savings of " + fmtKRange(totalLo) + "\u2013" + fmtKRange(totalHi) + " annually.",
|
|
18
20
|
nowCount > 0
|
|
19
21
|
? nowCount + " can be initiated this week without any structural changes to your business."
|
|
20
22
|
: "Implementation timeline spans the current tax year with no structural changes required.",
|
|
@@ -24,7 +26,7 @@ function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCo
|
|
|
24
26
|
react_1.default.createElement("div", { style: { background: palette.gray50, border: "1px solid " + palette.gray200, borderRadius: 10, padding: "24px 28px", marginBottom: 20 } },
|
|
25
27
|
react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.15em", color: palette.gray400, marginBottom: 16, fontFamily: palette.mono } }, "KEY METRICS"),
|
|
26
28
|
react_1.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 16 } }, [
|
|
27
|
-
{ v:
|
|
29
|
+
{ v: fmtKRange(totalLo) + "\u2013" + fmtKRange(totalHi), l: "Est. Annual Savings" },
|
|
28
30
|
{ v: String(eligible.length), l: "Strategies Identified" },
|
|
29
31
|
{ v: String(nowCount), l: "Immediate Actions" },
|
|
30
32
|
{ v: String(top3.length), l: "Priority Recommendations" },
|
|
@@ -39,21 +41,23 @@ function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCo
|
|
|
39
41
|
eligible.length,
|
|
40
42
|
" applicable strategies"),
|
|
41
43
|
" with combined estimated savings of ",
|
|
42
|
-
react_1.default.createElement("strong", { style: { color: palette.teal } },
|
|
44
|
+
react_1.default.createElement("strong", { style: { color: palette.teal } }, fmtKRange(totalLo) + "\u2013" + fmtKRange(totalHi) + " annually"),
|
|
43
45
|
"."),
|
|
44
46
|
react_1.default.createElement("div", { style: { fontSize: 14, color: palette.gray700, lineHeight: 1.8, fontFamily: palette.body, marginBottom: 16 } },
|
|
45
47
|
top3.length,
|
|
46
48
|
" strategies represent the highest-impact opportunities and are detailed below. ",
|
|
47
49
|
nowCount > 0 ? "Of these and the broader eligible set, " + nowCount + " can be initiated this week with no structural changes to your business." : "",
|
|
48
50
|
" Savings estimates reflect your current revenue of ",
|
|
49
|
-
"$" + Math.round(rev / 1000) + "K",
|
|
51
|
+
rev >= 1000000 ? "$" + (rev / 1000000).toFixed(1) + "M" : "$" + Math.round(rev / 1000) + "K",
|
|
50
52
|
", an effective tax rate of ",
|
|
51
|
-
effectiveTaxRate != null ? Math.round((effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate) * 10) / 10 : (parseFloat(profile.federalRate || "24") + parseFloat(profile.stateRate || "4.95")),
|
|
53
|
+
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")),
|
|
52
54
|
"%, and ",
|
|
53
55
|
dataYearsLabel,
|
|
54
56
|
" of financial data."),
|
|
55
57
|
(() => {
|
|
56
|
-
|
|
58
|
+
// C-Corps have a flat 21% federal rate
|
|
59
|
+
const isCCorp = profile.entity === "C-Corporation";
|
|
60
|
+
const fedRate = isCCorp ? 21 : parseFloat(profile.federalRate || "24");
|
|
57
61
|
const stateRate = parseFloat(profile.stateRate || "4.95");
|
|
58
62
|
// If engine provided effective_tax_rate (as a decimal 0-1), convert to percentage
|
|
59
63
|
const currentRate = effectiveTaxRate != null
|
|
@@ -21,6 +21,7 @@ interface ImplementationRoadmapProps {
|
|
|
21
21
|
bucketTotals: Record<string, BucketTotal>;
|
|
22
22
|
activeBuckets: BucketDef[];
|
|
23
23
|
palette: Palette;
|
|
24
|
+
taxYear?: string;
|
|
24
25
|
}
|
|
25
|
-
export declare function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bucketDefs, bucketTotals, activeBuckets, palette, }: ImplementationRoadmapProps): React.JSX.Element;
|
|
26
|
+
export declare function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bucketDefs, bucketTotals, activeBuckets, palette, taxYear, }: ImplementationRoadmapProps): React.JSX.Element;
|
|
26
27
|
export {};
|
|
@@ -9,7 +9,7 @@ const data_1 = require("../../lib/data");
|
|
|
9
9
|
const SectionOpener_1 = require("./SectionOpener");
|
|
10
10
|
const QuarterlyCashChart_1 = require("./QuarterlyCashChart");
|
|
11
11
|
const ImplementationTimelineChart_1 = require("./ImplementationTimelineChart");
|
|
12
|
-
function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bucketDefs, bucketTotals, activeBuckets, palette, }) {
|
|
12
|
+
function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bucketDefs, bucketTotals, activeBuckets, palette, taxYear, }) {
|
|
13
13
|
const card = {
|
|
14
14
|
background: palette.white,
|
|
15
15
|
border: "1px solid " + palette.gray200,
|
|
@@ -31,8 +31,8 @@ function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bu
|
|
|
31
31
|
"Time-sensitive elections and filings have firm deadlines noted in each strategy detail above.",
|
|
32
32
|
"Year-end planning items will be reviewed in a coordinated session before December 31.",
|
|
33
33
|
], palette: palette }),
|
|
34
|
-
react_1.default.createElement(QuarterlyCashChart_1.QuarterlyCashChart, { eligible: eligible, computed: computed, palette: palette }),
|
|
35
|
-
react_1.default.createElement(ImplementationTimelineChart_1.ImplementationTimelineChart, { top3: top3, computed: computed, palette: palette }),
|
|
34
|
+
react_1.default.createElement(QuarterlyCashChart_1.QuarterlyCashChart, { eligible: eligible, computed: computed, palette: palette, taxYear: taxYear }),
|
|
35
|
+
react_1.default.createElement(ImplementationTimelineChart_1.ImplementationTimelineChart, { top3: top3, computed: computed, palette: palette, taxYear: taxYear }),
|
|
36
36
|
react_1.default.createElement("div", { style: { marginBottom: 28 } }, activeBuckets.map(b => {
|
|
37
37
|
const bt = bucketTotals[b.key];
|
|
38
38
|
const midK = Math.round((bt.lo + bt.hi) / 2 / 1000);
|
|
@@ -42,10 +42,13 @@ function ImplementationRoadmap({ eligible, computed, top3, nowCount, nowMidK, bu
|
|
|
42
42
|
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: b.color, fontFamily: palette.mono } }, b.label),
|
|
43
43
|
react_1.default.createElement("div", { style: { fontSize: 11, color: palette.gray400, fontFamily: palette.mono } }, bt.strategies.length + " " + (bt.strategies.length === 1 ? "strategy" : "strategies"))),
|
|
44
44
|
react_1.default.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: palette.teal, fontFamily: palette.mono } }, "~$" + midK + "K midpoint")),
|
|
45
|
-
bt.strategies.map(s =>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
react_1.default.createElement("div", { style: {
|
|
49
|
-
|
|
45
|
+
bt.strategies.map(s => {
|
|
46
|
+
var _a;
|
|
47
|
+
return (react_1.default.createElement("div", { key: s.rank, style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", padding: "10px 0", borderBottom: "1px solid " + palette.gray100 } },
|
|
48
|
+
react_1.default.createElement("div", { style: { flex: 1 } },
|
|
49
|
+
react_1.default.createElement("div", { style: { fontSize: 14, fontWeight: 600, color: palette.gray800, fontFamily: palette.head } }, s.name),
|
|
50
|
+
react_1.default.createElement("div", { style: { fontSize: 12, color: palette.gray500, marginTop: 2, fontFamily: palette.body } }, data_1.NEXT_STEPS[(_a = s.strategyNumber) !== null && _a !== void 0 ? _a : s.rank] || "Discuss with your preparer.")),
|
|
51
|
+
react_1.default.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: palette.teal, fontFamily: palette.mono, flexShrink: 0, marginLeft: 12 } }, stSavings(s))));
|
|
52
|
+
})));
|
|
50
53
|
}))));
|
|
51
54
|
}
|
|
@@ -5,6 +5,7 @@ interface ImplementationTimelineChartProps {
|
|
|
5
5
|
top3: Strategy[];
|
|
6
6
|
computed: ComputedMap;
|
|
7
7
|
palette: Palette;
|
|
8
|
+
taxYear?: string;
|
|
8
9
|
}
|
|
9
|
-
export declare function ImplementationTimelineChart({ top3, computed, palette }: ImplementationTimelineChartProps): React.JSX.Element;
|
|
10
|
+
export declare function ImplementationTimelineChart({ top3, computed, palette, taxYear }: ImplementationTimelineChartProps): React.JSX.Element;
|
|
10
11
|
export {};
|
|
@@ -5,17 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ImplementationTimelineChart = ImplementationTimelineChart;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
-
function ImplementationTimelineChart({ top3, computed, palette }) {
|
|
8
|
+
function ImplementationTimelineChart({ top3, computed, palette, taxYear }) {
|
|
9
9
|
const W = 640, H = Math.max(180, 60 + top3.length * 44);
|
|
10
10
|
const padL = 180, padR = 80, padT = 44, padB = 24;
|
|
11
11
|
const plotW = W - padL - padR, plotH = H - padT - padB;
|
|
12
12
|
const BUCKET_RANGE = { now: [0, 2], "30d": [0, 4], "90d": [2, 12], filing: [36, 52] };
|
|
13
13
|
const weekScale = (w) => padL + (w / 52) * plotW;
|
|
14
14
|
const qDividers = [13, 26, 39];
|
|
15
|
+
const currentYear = new Date().getFullYear();
|
|
16
|
+
const year = taxYear || String(currentYear);
|
|
17
|
+
const yearNum = parseInt(year, 10);
|
|
18
|
+
const isHistorical = yearNum < currentYear;
|
|
19
|
+
const chartTitle = isHistorical ? "IMPLEMENTATION SEQUENCE" : "IMPLEMENTATION TIMELINE";
|
|
15
20
|
const qLabels = ["Q1", "Q2", "Q3", "Q4"];
|
|
16
21
|
return (react_1.default.createElement("div", { className: "chart-block", style: { background: palette.white, border: "1px solid " + palette.gray200, borderRadius: 10, padding: "24px 28px", marginBottom: 24 } },
|
|
17
22
|
react_1.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 18 } },
|
|
18
|
-
react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.15em", color: palette.gray400, fontFamily: palette.mono } },
|
|
23
|
+
react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.15em", color: palette.gray400, fontFamily: palette.mono } }, chartTitle),
|
|
19
24
|
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, color: palette.teal, fontFamily: palette.mono } }, "top " + top3.length + " priorities")),
|
|
20
25
|
react_1.default.createElement("svg", { viewBox: "0 0 " + W + " " + H, style: { width: "100%", height: "auto", display: "block" } },
|
|
21
26
|
[0, 1, 2, 3].map(qi => {
|
|
@@ -27,7 +32,8 @@ function ImplementationTimelineChart({ top3, computed, palette }) {
|
|
|
27
32
|
const cx = weekScale(qi * 13 + 6.5);
|
|
28
33
|
return react_1.default.createElement("text", { key: q, x: cx, y: padT - 18, fontSize: "10", fontWeight: "700", fill: palette.gray400, fontFamily: palette.mono, textAnchor: "middle" },
|
|
29
34
|
q,
|
|
30
|
-
"
|
|
35
|
+
" ",
|
|
36
|
+
year);
|
|
31
37
|
}),
|
|
32
38
|
qDividers.map(w => (react_1.default.createElement("line", { key: w, x1: weekScale(w), y1: padT - 12, x2: weekScale(w), y2: padT + plotH + 4, stroke: palette.gray200, strokeWidth: "1", strokeDasharray: "2,3" }))),
|
|
33
39
|
top3.map((s, i) => {
|
|
@@ -5,6 +5,7 @@ interface QuarterlyCashChartProps {
|
|
|
5
5
|
eligible: Strategy[];
|
|
6
6
|
computed: ComputedMap;
|
|
7
7
|
palette: Palette;
|
|
8
|
+
taxYear?: string;
|
|
8
9
|
}
|
|
9
|
-
export declare function QuarterlyCashChart({ eligible, computed, palette }: QuarterlyCashChartProps): React.JSX.Element;
|
|
10
|
+
export declare function QuarterlyCashChart({ eligible, computed, palette, taxYear }: QuarterlyCashChartProps): React.JSX.Element;
|
|
10
11
|
export {};
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.QuarterlyCashChart = QuarterlyCashChart;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
-
function QuarterlyCashChart({ eligible, computed, palette }) {
|
|
8
|
+
function QuarterlyCashChart({ eligible, computed, palette, taxYear }) {
|
|
9
9
|
const W = 600, H = 240;
|
|
10
10
|
const padL = 56, padR = 24, padT = 32, padB = 52;
|
|
11
11
|
const plotW = W - padL - padR, plotH = H - padT - padB;
|
|
@@ -25,8 +25,14 @@ function QuarterlyCashChart({ eligible, computed, palette }) {
|
|
|
25
25
|
const yScale = (v) => padT + plotH - (v / maxVal) * plotH;
|
|
26
26
|
const barW = Math.min(72, (plotW - 60) / 4);
|
|
27
27
|
const slot = plotW / 4;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
28
|
+
const currentYear = new Date().getFullYear();
|
|
29
|
+
const year = taxYear || String(currentYear);
|
|
30
|
+
const yearNum = parseInt(year, 10);
|
|
31
|
+
const isHistorical = yearNum < currentYear;
|
|
32
|
+
const labels = [`Q1 ${year}`, `Q2 ${year}`, `Q3 ${year}`, `Q4 ${year}`];
|
|
33
|
+
const sublabels = isHistorical
|
|
34
|
+
? ["Q1 actions", "Q2 actions", "Q3 actions", "Q4 actions"]
|
|
35
|
+
: ["Now / 30 days", "Within 90 days", "Mid-year", "At filing"];
|
|
30
36
|
const tickStep = maxVal > 200000 ? 100000 : maxVal > 100000 ? 50000 : 25000;
|
|
31
37
|
const yTicks = [];
|
|
32
38
|
for (let v = 0; v <= maxVal * 1.05; v += tickStep)
|
|
@@ -55,7 +55,14 @@ function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategi
|
|
|
55
55
|
];
|
|
56
56
|
const bucketTotals = {};
|
|
57
57
|
bucketDefs.forEach(b => {
|
|
58
|
-
const strategies = eligible.filter(s =>
|
|
58
|
+
const strategies = eligible.filter(s => {
|
|
59
|
+
var _a, _b, _c, _d;
|
|
60
|
+
if (s.timelineBucket !== b.key)
|
|
61
|
+
return false;
|
|
62
|
+
const clo = (_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo;
|
|
63
|
+
const chi = (_d = (_c = computed.get(s.rank)) === null || _c === void 0 ? void 0 : _c.hi) !== null && _d !== void 0 ? _d : s.hi;
|
|
64
|
+
return clo > 0 || chi > 0;
|
|
65
|
+
}).sort((a, c) => c.score - a.score);
|
|
59
66
|
const lo = strategies.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);
|
|
60
67
|
const hi = strategies.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);
|
|
61
68
|
bucketTotals[b.key] = { strategies, lo, hi };
|
|
@@ -115,7 +122,7 @@ function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategi
|
|
|
115
122
|
"This summary was prepared using TaxAxis, Paro's AI-powered tax analysis engine. Savings estimates are based on financial data provided for tax year " + profile.year + " (" + (profile.taxDataYears || "1 year") + " of data). All figures are estimates. Your tax preparer has reviewed these recommendations.",
|
|
116
123
|
hasOBBBA && " Some strategies reference provisions from the One Big Beautiful Bill Act (OBBBA), signed July 4, 2025, with limited IRS guidance."),
|
|
117
124
|
react_1.default.createElement(RecommendedStrategies_1.RecommendedStrategies, { profile: profile, eligible: eligible, computed: computed, top3: top3, hasOBBBA: hasOBBBA, palette: palette_1.P, interactionWarnings: engineRawOutput === null || engineRawOutput === void 0 ? void 0 : engineRawOutput.strategy_interaction_warnings }),
|
|
118
|
-
react_1.default.createElement(ImplementationRoadmap_1.ImplementationRoadmap, { eligible: eligible, computed: computed, top3: top3, nowCount: nowCount, nowMidK: nowMidK, bucketDefs: bucketDefs, bucketTotals: bucketTotals, activeBuckets: activeBuckets, palette: palette_1.P }),
|
|
125
|
+
react_1.default.createElement(ImplementationRoadmap_1.ImplementationRoadmap, { eligible: eligible, computed: computed, top3: top3, nowCount: nowCount, nowMidK: nowMidK, bucketDefs: bucketDefs, bucketTotals: bucketTotals, activeBuckets: activeBuckets, palette: palette_1.P, taxYear: profile.year }),
|
|
119
126
|
react_1.default.createElement(Methodology_1.Methodology, { profile: profile, eligible: eligible, palette: palette_1.P }),
|
|
120
127
|
react_1.default.createElement("div", { style: { padding: "16px 20px", marginBottom: 16, fontSize: 10, color: palette_1.P.gray400, lineHeight: 1.7, fontFamily: palette_1.P.body } }, "This summary was generated by TaxAxis, an AI-powered analysis tool by Paro. Recommendations are based on financial data provided and current tax law as of April 2026, including OBBBA provisions. These are estimates, not guarantees. Consult your tax preparer before implementing any strategy. This is not legal or financial advice."),
|
|
121
128
|
react_1.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "12px 0", borderTop: "1px solid " + palette_1.P.gray200, fontSize: 10, color: palette_1.P.gray400, fontFamily: palette_1.P.mono } },
|
|
@@ -11,7 +11,9 @@ function Sec({ title, children, titleColor }) {
|
|
|
11
11
|
react_1.default.createElement("div", { className: "text-[11px] font-bold uppercase tracking-widest mb-2.5 font-tax-axis-mono", style: { color: titleColor || "#A1E5E6" } }, title),
|
|
12
12
|
children));
|
|
13
13
|
}
|
|
14
|
-
const fmtK = (n) =>
|
|
14
|
+
const fmtK = (n) => n >= 1000000
|
|
15
|
+
? `$${(n / 1000000).toFixed(1)}M`
|
|
16
|
+
: `$${(n / 1000).toFixed(n % 1000 ? 1 : 0)}K`;
|
|
15
17
|
function positionStrengthLabel(positionStrength) {
|
|
16
18
|
if (!positionStrength)
|
|
17
19
|
return { label: "Position Under Review", color: "#9498B8", dots: 2 };
|
|
@@ -235,7 +235,9 @@ function buildLlmComputed(strategies) {
|
|
|
235
235
|
return map;
|
|
236
236
|
}
|
|
237
237
|
// ─── Formatting helper ──────────────────────────────────────────
|
|
238
|
-
const fmtK = (n) =>
|
|
238
|
+
const fmtK = (n) => n >= 1000000
|
|
239
|
+
? `$${(n / 1000000).toFixed(1)}M`
|
|
240
|
+
: `$${(n / 1000).toFixed(n % 1000 ? 1 : 0)}K`;
|
|
239
241
|
// ─── Timeline buckets for Client Summary ────────────────────────
|
|
240
242
|
const BUCKETS = [
|
|
241
243
|
{ key: "now", label: "Act Now", desc: "Claim on your return or adjust this pay period" },
|
|
@@ -264,7 +266,8 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
|
|
|
264
266
|
// clientBrief from why_it_applies, etc.) over the thin mapLlmToStrategies fallback.
|
|
265
267
|
// engineOutput is at llmResult.engineOutput or llmResult.rawOutput (both present).
|
|
266
268
|
const engineOutputForAdapter = (0, react_1.useMemo)(() => { var _a, _b; return (_b = (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput) !== null && _a !== void 0 ? _a : llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) !== null && _b !== void 0 ? _b : null; }, [llmResult]);
|
|
267
|
-
const
|
|
269
|
+
const preComputedForAdapter = (0, react_1.useMemo)(() => { var _a, _b; return (_b = (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.meta) === null || _a === void 0 ? void 0 : _a.preComputed) !== null && _b !== void 0 ? _b : null; }, [llmResult]);
|
|
270
|
+
const adapted = (0, useEngineOutput_1.useEngineOutput)(engineOutputForAdapter, preComputedForAdapter);
|
|
268
271
|
const llmStrategies = (0, react_1.useMemo)(() => {
|
|
269
272
|
var _a;
|
|
270
273
|
if (!hasLlm)
|
|
@@ -66,10 +66,20 @@ const INDUSTRY_OPTIONS = [
|
|
|
66
66
|
"Real Estate",
|
|
67
67
|
"Other",
|
|
68
68
|
];
|
|
69
|
-
const
|
|
70
|
-
const YEAR_OPTIONS = ["2024", "2025", "2026"];
|
|
69
|
+
const CURRENT_YEAR = new Date().getFullYear();
|
|
70
|
+
const YEAR_OPTIONS = ["2023", "2024", "2025", "2026"];
|
|
71
71
|
function ClientParametersSection({ userContext = "expert", }) {
|
|
72
|
-
const { control, formState: { errors }, } = (0, react_hook_form_1.useFormContext)();
|
|
72
|
+
const { control, formState: { errors }, watch, setValue, } = (0, react_hook_form_1.useFormContext)();
|
|
73
|
+
const selectedYear = watch("year");
|
|
74
|
+
const yearNum = parseInt(selectedYear || "2026", 10);
|
|
75
|
+
const isCurrentOrFuture = yearNum >= CURRENT_YEAR;
|
|
76
|
+
const periodOptions = isCurrentOrFuture ? ["YTD"] : ["Full Year", "YTD"];
|
|
77
|
+
// Auto-switch period to "YTD" when user selects a current/future tax year
|
|
78
|
+
react_1.default.useEffect(() => {
|
|
79
|
+
if (isCurrentOrFuture) {
|
|
80
|
+
setValue("period", "YTD");
|
|
81
|
+
}
|
|
82
|
+
}, [isCurrentOrFuture, setValue]);
|
|
73
83
|
const [expanded, setExpanded] = (0, react_1.useState)(true);
|
|
74
84
|
return (react_1.default.createElement(TaxAxisCard_1.TaxAxisCard, null,
|
|
75
85
|
react_1.default.createElement("div", { onClick: () => setExpanded((prev) => !prev), className: "flex items-center justify-between cursor-pointer", style: { marginBottom: expanded ? 14 : 0 } },
|
|
@@ -103,7 +113,7 @@ function ClientParametersSection({ userContext = "expert", }) {
|
|
|
103
113
|
react_1.default.createElement(react_hook_form_1.Controller, { name: "period", control: control, render: ({ field }) => {
|
|
104
114
|
var _a;
|
|
105
115
|
return (react_1.default.createElement("div", { className: "relative" },
|
|
106
|
-
react_1.default.createElement("select", Object.assign({}, field, { value: (_a = field.value) !== null && _a !== void 0 ? _a : "Full Year", className: selectCls }),
|
|
116
|
+
react_1.default.createElement("select", Object.assign({}, field, { value: (_a = field.value) !== null && _a !== void 0 ? _a : (isCurrentOrFuture ? "YTD" : "Full Year"), className: selectCls }), periodOptions.map(o => (react_1.default.createElement("option", { key: o, value: o, className: "bg-tax-axis-surface-2" }, o)))),
|
|
107
117
|
react_1.default.createElement(Chevron, null)));
|
|
108
118
|
} })),
|
|
109
119
|
react_1.default.createElement("div", null,
|
|
@@ -18,14 +18,18 @@ function EngagementHeader({ profile, palette }) {
|
|
|
18
18
|
marginBottom: 16,
|
|
19
19
|
boxShadow: "0 1px 3px rgba(0,0,0,0.04)",
|
|
20
20
|
};
|
|
21
|
+
// C-Corps have a flat 21% federal rate and no filing status
|
|
22
|
+
const isCCorp = profile.entity === "C-Corporation";
|
|
23
|
+
const fedRateDisplay = isCCorp ? "21%" : (profile.federalRate || "24%");
|
|
24
|
+
const filingDisplay = isCCorp ? "\u2014" : (profile.filingStatus || "MFJ");
|
|
21
25
|
const rows = [
|
|
22
26
|
["Entity", profile.entity],
|
|
23
27
|
["Industry", profile.industry],
|
|
24
28
|
["States", states.join(", ") || "\u2014"],
|
|
25
29
|
["Tax Year", profile.year],
|
|
26
|
-
["Filing",
|
|
30
|
+
["Filing", filingDisplay],
|
|
27
31
|
["SSTB", profile.sstb || "No"],
|
|
28
|
-
["Fed Rate",
|
|
32
|
+
["Fed Rate", fedRateDisplay],
|
|
29
33
|
["State Rate", (profile.stateRate || "4.95") + "%"],
|
|
30
34
|
["Revenue", "$" + (rev || 0).toLocaleString()],
|
|
31
35
|
["Net Income", "$" + (0, compute_1.parseNum)(profile.netIncome).toLocaleString()],
|
|
@@ -50,9 +50,9 @@ function TaxAxisPreparerWorkpaper({ profile, onBack, onToggleToClient, liveStrat
|
|
|
50
50
|
const grouped = priorityOrder
|
|
51
51
|
.map(p => ({ priority: p, items: eligible.filter(s => s.priority === p).sort((a, b) => b.score - a.score) }))
|
|
52
52
|
.filter(g => g.items.length > 0);
|
|
53
|
-
// Interaction pairs
|
|
54
|
-
const
|
|
55
|
-
const activeInteractions = data_1.WORKPAPER_INTERACTION_PAIRS.filter(([a, b]) =>
|
|
53
|
+
// Interaction pairs — match on original catalog strategy number, not re-indexed rank
|
|
54
|
+
const eligibleStrategyNumbers = new Set(eligible.map(s => { var _a; return (_a = s.strategyNumber) !== null && _a !== void 0 ? _a : s.rank; }));
|
|
55
|
+
const activeInteractions = data_1.WORKPAPER_INTERACTION_PAIRS.filter(([a, b]) => eligibleStrategyNumbers.has(a) && eligibleStrategyNumbers.has(b));
|
|
56
56
|
// Savings summary
|
|
57
57
|
const sortedByMid = [...eligible].sort((a, b) => {
|
|
58
58
|
var _a, _b, _c, _d;
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import type { Strategy, ComputedMap } from "../types";
|
|
2
2
|
/** Raw engine output blob from the LLM run. */
|
|
3
3
|
export type EngineOutput = Record<string, unknown> | null;
|
|
4
|
+
/** Pre-computed formula engine results from outputPayload.meta.preComputed. */
|
|
5
|
+
export interface PreComputedResults {
|
|
6
|
+
results: Record<string, PreComputedStrategyResult>;
|
|
7
|
+
completedCount?: number;
|
|
8
|
+
excludedCount?: number;
|
|
9
|
+
failedCount?: number;
|
|
10
|
+
totalSavingsLow?: number;
|
|
11
|
+
totalSavingsHigh?: number;
|
|
12
|
+
computedAt?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PreComputedStrategyResult {
|
|
15
|
+
strategy_id: string;
|
|
16
|
+
status: "COMPLETE" | "EXCLUDED" | "FAILED";
|
|
17
|
+
result_gross: number | null;
|
|
18
|
+
confidence_interval?: {
|
|
19
|
+
low: number;
|
|
20
|
+
high: number;
|
|
21
|
+
basis?: string;
|
|
22
|
+
} | null;
|
|
23
|
+
trace?: Record<string, unknown>;
|
|
24
|
+
}
|
|
4
25
|
/** Adapter result passed to client-report / preparer-workpaper components. */
|
|
5
26
|
export interface EngineOutputAdapterResult {
|
|
6
27
|
strategies: Strategy[];
|
|
@@ -14,7 +35,9 @@ export interface EngineStrategyAnalysis {
|
|
|
14
35
|
weighted_score: number;
|
|
15
36
|
quick_win: boolean;
|
|
16
37
|
engagement_recommendation: string;
|
|
38
|
+
risk_disclosure?: string | null;
|
|
17
39
|
calculation_trace?: Record<string, unknown>;
|
|
40
|
+
_reconciledPreComputed?: boolean;
|
|
18
41
|
[key: string]: unknown;
|
|
19
42
|
}
|
|
20
43
|
/** CPA workflow section from the engine. */
|
|
@@ -23,4 +46,4 @@ export interface EngineCpaWorkflow {
|
|
|
23
46
|
engagement_recommendations: string[];
|
|
24
47
|
[key: string]: unknown;
|
|
25
48
|
}
|
|
26
|
-
export declare function useEngineOutput(
|
|
49
|
+
export declare function useEngineOutput(engineOutput: EngineOutput, preComputed?: PreComputedResults | null): EngineOutputAdapterResult | null;
|
|
@@ -1,12 +1,120 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// useEngineOutput —
|
|
3
|
-
//
|
|
2
|
+
// useEngineOutput — maps raw engine output + formula pre-computed results
|
|
3
|
+
// into the canonical Strategy[] + ComputedMap consumed by report components.
|
|
4
|
+
//
|
|
5
|
+
// Only strategies validated by the deterministic formula engine are included.
|
|
6
|
+
// This prevents LLM-hallucinated strategies and dollar amounts from reaching
|
|
7
|
+
// the Client Report or Preparer Workpaper.
|
|
4
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
9
|
exports.useEngineOutput = useEngineOutput;
|
|
6
10
|
const react_1 = require("react");
|
|
11
|
+
const strategyNarrative_1 = require("../data/strategyNarrative");
|
|
12
|
+
const data_1 = require("../data");
|
|
7
13
|
// ---------------------------------------------------------------------------
|
|
8
|
-
//
|
|
14
|
+
// Helpers
|
|
9
15
|
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
/** Parse "S10" -> 10. Returns null if strategyId doesn't match the SN pattern. */
|
|
17
|
+
function parseStrategyNumber(strategyId) {
|
|
18
|
+
const match = strategyId.match(/^S(\d+)$/);
|
|
19
|
+
return match ? parseInt(match[1], 10) : null;
|
|
20
|
+
}
|
|
21
|
+
// Static catalog lookups keyed by original strategy rank (1-25).
|
|
22
|
+
const CATEGORY_BY_RANK = new Map();
|
|
23
|
+
const CODE_BY_RANK = new Map();
|
|
24
|
+
for (const s of data_1.STRATEGIES) {
|
|
25
|
+
CATEGORY_BY_RANK.set(s.rank, s.cat);
|
|
26
|
+
CODE_BY_RANK.set(s.rank, s.code);
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Core mapping — engine strategy_analysis entry -> canonical Strategy
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
function mapAnalysisToStrategy(sa, idx, pc) {
|
|
32
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
33
|
+
const ct = ((_a = sa.calculation_trace) !== null && _a !== void 0 ? _a : {});
|
|
34
|
+
const ci = pc === null || pc === void 0 ? void 0 : pc.confidence_interval;
|
|
35
|
+
// Dollar amounts: prefer preComputed CI, then calculation_trace CI
|
|
36
|
+
const lo = ci ? Math.round(ci.low) : Math.round((_c = (_b = ct.confidence_interval) === null || _b === void 0 ? void 0 : _b.low) !== null && _c !== void 0 ? _c : 0);
|
|
37
|
+
const hi = ci ? Math.round(ci.high) : Math.round((_e = (_d = ct.confidence_interval) === null || _d === void 0 ? void 0 : _d.high) !== null && _e !== void 0 ? _e : 0);
|
|
38
|
+
const strategyNumber = parseStrategyNumber(sa.strategy_id);
|
|
39
|
+
const cat = strategyNumber !== null ? ((_f = CATEGORY_BY_RANK.get(strategyNumber)) !== null && _f !== void 0 ? _f : "income") : "income";
|
|
40
|
+
const staticNarrative = strategyNumber !== null ? strategyNarrative_1.STRATEGY_NARRATIVE[strategyNumber] : undefined;
|
|
41
|
+
// Source documents from calculation_trace
|
|
42
|
+
const sourceDocuments = Array.isArray(ct.source_documents) ? ct.source_documents : undefined;
|
|
43
|
+
const specialistNote = typeof ct.specialist_note === "string" && ct.specialist_note.trim() !== ""
|
|
44
|
+
? ct.specialist_note
|
|
45
|
+
: ((_g = staticNarrative === null || staticNarrative === void 0 ? void 0 : staticNarrative.whySpecialist) !== null && _g !== void 0 ? _g : undefined);
|
|
46
|
+
const positionStrength = typeof ct.position_strength === "string" ? ct.position_strength : undefined;
|
|
47
|
+
const authority = typeof ct.irs_cite === "string" ? ct.irs_cite : undefined;
|
|
48
|
+
const formsFromTrace = Array.isArray(ct.forms_required)
|
|
49
|
+
? ct.forms_required.join(", ")
|
|
50
|
+
: undefined;
|
|
51
|
+
// Timeline bucket: derive from priority as a reasonable default
|
|
52
|
+
const priority = (sa.priority_tier || "MEDIUM").toUpperCase();
|
|
53
|
+
const timelineBucket = sa.quick_win ? "now" : priority === "HIGH" ? "now" : priority === "MEDIUM" ? "30d" : "90d";
|
|
54
|
+
return {
|
|
55
|
+
rank: idx + 1,
|
|
56
|
+
strategyNumber: strategyNumber !== null && strategyNumber !== void 0 ? strategyNumber : undefined,
|
|
57
|
+
code: (_h = (strategyNumber !== null ? CODE_BY_RANK.get(strategyNumber) : undefined)) !== null && _h !== void 0 ? _h : sa.strategy_id,
|
|
58
|
+
name: sa.strategy_name,
|
|
59
|
+
cat,
|
|
60
|
+
priority,
|
|
61
|
+
score: sa.weighted_score != null
|
|
62
|
+
? Math.min(Math.round((sa.weighted_score / 25) * 100), 100)
|
|
63
|
+
: (priority === "HIGH" ? 85 : priority === "MEDIUM" ? 70 : 55),
|
|
64
|
+
entities: [],
|
|
65
|
+
lo,
|
|
66
|
+
hi,
|
|
67
|
+
timeline: timelineBucket === "now" ? "Act now" : timelineBucket === "30d" ? "Within 30 days" : "Within 90 days",
|
|
68
|
+
timelineBucket,
|
|
69
|
+
abstract: sa.engagement_recommendation || "",
|
|
70
|
+
warning: (_j = sa.risk_disclosure) !== null && _j !== void 0 ? _j : null,
|
|
71
|
+
forms: formsFromTrace,
|
|
72
|
+
authority,
|
|
73
|
+
sourceDocuments,
|
|
74
|
+
specialistNote,
|
|
75
|
+
positionStrength,
|
|
76
|
+
quickWin: sa.quick_win,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Hook
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
function useEngineOutput(engineOutput, preComputed) {
|
|
83
|
+
return (0, react_1.useMemo)(() => {
|
|
84
|
+
var _a;
|
|
85
|
+
if (!engineOutput)
|
|
86
|
+
return null;
|
|
87
|
+
const strategyAnalysis = engineOutput.strategy_analysis;
|
|
88
|
+
if (!Array.isArray(strategyAnalysis) || strategyAnalysis.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
const pcResults = (_a = preComputed === null || preComputed === void 0 ? void 0 : preComputed.results) !== null && _a !== void 0 ? _a : {};
|
|
91
|
+
// Filter: only include strategies where the formula engine returned COMPLETE
|
|
92
|
+
// OR the reconciler tagged _reconciledPreComputed: true.
|
|
93
|
+
const validated = strategyAnalysis.filter((sa) => {
|
|
94
|
+
const pc = pcResults[sa.strategy_id];
|
|
95
|
+
if ((pc === null || pc === void 0 ? void 0 : pc.status) === "COMPLETE")
|
|
96
|
+
return true;
|
|
97
|
+
if (sa._reconciledPreComputed === true)
|
|
98
|
+
return true;
|
|
99
|
+
return false;
|
|
100
|
+
});
|
|
101
|
+
if (validated.length === 0)
|
|
102
|
+
return null;
|
|
103
|
+
// Map to canonical Strategy[]
|
|
104
|
+
const strategies = validated.map((sa, idx) => {
|
|
105
|
+
const pc = pcResults[sa.strategy_id];
|
|
106
|
+
return mapAnalysisToStrategy(sa, idx, pc);
|
|
107
|
+
});
|
|
108
|
+
// Build ComputedMap keyed by rank (1-based index matching strategies array)
|
|
109
|
+
const computedMap = new Map();
|
|
110
|
+
for (const s of strategies) {
|
|
111
|
+
computedMap.set(s.rank, {
|
|
112
|
+
lo: s.lo,
|
|
113
|
+
hi: s.hi,
|
|
114
|
+
sources: s.sources,
|
|
115
|
+
trace: s.trace,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return { strategies, computedMap };
|
|
119
|
+
}, [engineOutput, preComputed]);
|
|
12
120
|
}
|
|
@@ -16,20 +16,20 @@ exports.NEXT_STEPS = {
|
|
|
16
16
|
8: "Provide your property appraisal or purchase records. A cost segregation study will identify reclassifiable assets.",
|
|
17
17
|
9: "Increase your payroll HSA deduction this pay period. Your preparer will confirm your remaining contribution room.",
|
|
18
18
|
10: "Start logging business meals with date, attendees, and business purpose. 50% of qualifying meals are deductible.",
|
|
19
|
-
11: "
|
|
20
|
-
12: "
|
|
21
|
-
13: "
|
|
22
|
-
14: "
|
|
23
|
-
15: "Your preparer will
|
|
24
|
-
16: "Your preparer will
|
|
25
|
-
17: "
|
|
26
|
-
18: "
|
|
27
|
-
19: "
|
|
28
|
-
20: "Your preparer will
|
|
29
|
-
21: "
|
|
30
|
-
22: "
|
|
19
|
+
11: "Photograph your dedicated workspace and collect mortgage, insurance, and utility bills for the year. Your preparer will compare actual vs. simplified methods.",
|
|
20
|
+
12: "Your preparer will draft an accountable plan document and board resolution for tax-free reimbursement of qualifying employee business expenses.",
|
|
21
|
+
13: "Document fair market rental rates for your home and schedule qualifying business use days. Your preparer will prepare the rental agreement and FMV documentation.",
|
|
22
|
+
14: "Document bona fide job duties and market-rate wages for family members working in the business. Your preparer will set up proper payroll documentation.",
|
|
23
|
+
15: "Your preparer will evaluate your state's pass-through entity election and calculate the federal deduction benefit versus your current SALT position.",
|
|
24
|
+
16: "Your preparer will assess qualifying energy-efficient commercial building improvements and determine applicable deduction or credit tiers.",
|
|
25
|
+
17: "Your preparer will compare standard mileage rate versus actual expense methods and evaluate heavy SUV §179 eligibility for your business vehicles.",
|
|
26
|
+
18: "Your preparer will review group-term life insurance, disability, and other employer-provided coverage for tax-efficient structuring under IRC employer benefit rules.",
|
|
27
|
+
19: "Your preparer will evaluate qualifying real property exchanges and coordinate identification and exchange period deadlines with a qualified intermediary.",
|
|
28
|
+
20: "Your preparer will verify qualified small business stock eligibility, holding period requirements, and the $10M or 10x-basis exclusion limit for capital gains.",
|
|
29
|
+
21: "Your preparer will calculate the OBBBA qualified tip income deduction for eligible tipped employees and verify documentation requirements.",
|
|
30
|
+
22: "Your preparer will evaluate OBBBA overtime pay deduction eligibility and calculate the incremental tax deduction for qualifying overtime wages.",
|
|
31
31
|
23: "Your preparer will review prior-year R&E amortization for catch-up eligibility.",
|
|
32
|
-
24: "
|
|
32
|
+
24: "Your preparer will evaluate employer-provided childcare facility or referral service expenditures and calculate the 25% qualified expenditure credit.",
|
|
33
33
|
25: "Your preparer will evaluate Trump Account eligibility and optimal contribution levels.",
|
|
34
34
|
};
|
|
35
35
|
exports.INTERACTION_PAIRS = [[1, 4], [1, 7], [2, 3]];
|
|
@@ -17,7 +17,10 @@ exports.SOURCE_DESC = {
|
|
|
17
17
|
"§223": "Health Savings Account contributions for HDHP-covered taxpayers",
|
|
18
18
|
"§274": "Business meal deductions, generally limited to 50% of qualifying expenses",
|
|
19
19
|
"§162": "Ordinary and necessary business expenses",
|
|
20
|
+
"§62": "Accountable plan reimbursements for qualifying employee business expenses under an employer-maintained plan",
|
|
20
21
|
"§280A": "Home office deduction for the business use of a residence",
|
|
22
|
+
"§280A(g)": "Augusta Rule: exclusion of rental income for personal residence rented 14 days or fewer per year",
|
|
23
|
+
"§73": "Employment of family members; wage deduction and FICA/FUTA savings for qualifying family employees",
|
|
21
24
|
"§45R": "Small employer health insurance credit",
|
|
22
25
|
"§170": "Charitable contribution deductions",
|
|
23
26
|
"OBBBA §70109": "Pass-through entity tax election workaround for the SALT cap",
|
|
@@ -26,6 +29,9 @@ exports.SOURCE_DESC = {
|
|
|
26
29
|
"§1400Z": "Qualified Opportunity Zone capital gains deferral",
|
|
27
30
|
"§179D": "Energy efficient commercial buildings deduction",
|
|
28
31
|
"§446": "Permissible accounting methods and changes",
|
|
32
|
+
"§79, §264": "Group-term life insurance exclusion and employer-owned life insurance tax treatment",
|
|
33
|
+
"§1031": "Like-kind exchange deferral of recognized gain on qualifying real property dispositions",
|
|
34
|
+
"§1202": "Qualified Small Business Stock gain exclusion: up to $10M or 10x adjusted basis for qualifying C-Corp stock held 5+ years",
|
|
29
35
|
"OBBBA": "OBBBA-introduced deductions and credits (Qualified Tips, Overtime Pay, Trump Accounts)",
|
|
30
36
|
"§174A": "OBBBA-restored full expensing for domestic research and experimental expenditures",
|
|
31
37
|
"§45F": "Employer-provided childcare facilities and services credit",
|