node-autotask 0.12.0 → 0.13.0
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/MCP_TOOL_CATALOG.json +4 -3
- package/MCP_TOOL_MANIFEST.md +5 -5
- package/N8N_MCP_MANIFEST.json +2 -2
- package/N8N_MCP_MANIFEST.md +3 -3
- package/capabilities.json +1238 -1237
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-SRJI5XUZ.cjs → chunk-CJA6L6NR.cjs} +135 -135
- package/dist/{chunk-JKZVCFRY.cjs → chunk-CXXNONRP.cjs} +1237 -1236
- package/dist/{chunk-3WIDDEHP.js → chunk-DDZXN6LN.js} +2 -2
- package/dist/{chunk-GD4JRPYF.js → chunk-IKJLIDK3.js} +1 -1
- package/dist/{chunk-5N6DYKXO.cjs → chunk-KB2ANVVR.cjs} +2 -2
- package/dist/{chunk-TELBX6HK.cjs → chunk-MTDCLVQQ.cjs} +797 -603
- package/dist/{chunk-4A6XLQHE.js → chunk-OZD5FQM4.js} +1237 -1236
- package/dist/{chunk-MY7ZSXY3.js → chunk-Y6O5VVQ4.js} +387 -193
- package/dist/{index-D8I6taEo.d.cts → index-DbMKWnEX.d.cts} +96 -35
- package/dist/{index-uAxAcRol.d.ts → index-TuqNq3Ro.d.ts} +96 -35
- package/dist/index.cjs +19 -19
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/operations/index.cjs +103 -103
- package/dist/operations/index.d.cts +2 -2
- package/dist/operations/index.d.ts +2 -2
- package/dist/operations/index.js +2 -2
- package/dist/pool.cjs +8 -8
- package/dist/pool.d.cts +1 -1
- package/dist/pool.d.ts +1 -1
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +3 -3
- package/docs/API.md +190 -51
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -2608,7 +2608,9 @@ the plan change — a new registry op — is a minor at publish). One call
|
|
|
2608
2608
|
returns the contract's reconciled P&L over a date window:
|
|
2609
2609
|
|
|
2610
2610
|
- **`contracts.profitability(contractId, opts)` — the closed opts bag.**
|
|
2611
|
-
`
|
|
2611
|
+
`opts` itself is required (0.13.0 — the signature and the registry arg map
|
|
2612
|
+
agree; a call without it is a pre-request `CONFIG_ERROR` naming the bag and
|
|
2613
|
+
its `dateFrom`/`dateTo`). `dateFrom`/`dateTo` are required (`YYYY-MM-DD`, `dateTo >= dateFrom`) and
|
|
2612
2614
|
the window is capped at 36 calendar months (the per-month windowing budget
|
|
2613
2615
|
stays bounded; OQ1). `labourCostMethod` (default
|
|
2614
2616
|
`billing_item_our_cost`; the closed 4-set
|
|
@@ -2625,34 +2627,62 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2625
2627
|
`ticketCategory` activity fold; `staleAfterDays` (default 30, integer `>=
|
|
2626
2628
|
1`) is the open-stale horizon; `maxTicketsForJoin` (default 500, integer
|
|
2627
2629
|
1..500) caps the ticket join set fed to the expenses + miscoding probe
|
|
2628
|
-
(sorted, first N, the `N of M` note)
|
|
2629
|
-
|
|
2630
|
+
(sorted, first N, the `N of M` note) and the time-referenced ticket ids;
|
|
2631
|
+
`limit` (default 50, clamped 500) is
|
|
2632
|
+
every part's `MaxRecords` + the capped lists; `includeRows` (default
|
|
2633
|
+
false, boolean) selects the output shape (below). Validation collects the
|
|
2630
2634
|
COMPLETE problem list and throws ONE typed `CONFIG_ERROR` pre-request —
|
|
2631
2635
|
zero wire on any malformed bag (the refusal table is house style: every
|
|
2632
2636
|
message names the key + the valid values).
|
|
2633
|
-
- **Wire plan: ≤
|
|
2637
|
+
- **Wire plan: ≤21 bounded reads, zero mutating, all pre-existing ops.**
|
|
2634
2638
|
Issue order: contract `get` → customer `GET /V1.0/Companies/{companyID}` →
|
|
2635
|
-
|
|
2636
|
-
|
|
2639
|
+
the Contracts `entityInformation/fields` read (the `contractType` LABEL —
|
|
2640
|
+
verified on the ww29 sandbox 2026-10-16: 1 Time & Materials, 3 Fixed
|
|
2641
|
+
Price, 4 Block Hours, 6 Retainer, 7 Recurring Service, 8 Per Ticket, 9
|
|
2642
|
+
Umbrella; the folds still branch on `/block\s*hours?/i` and
|
|
2643
|
+
`/time.*materials?/i` over the label, never an id; `contractCategory` is
|
|
2644
|
+
ignored — all its values are inactive; an absent/0 type skips the read; an absent type, an
|
|
2645
|
+
unlisted id or a catalog fault is `CONTRACT_TYPE_UNKNOWN`, surfaced in
|
|
2646
|
+
`contract_type { id, label, absent? }`) → `ContractServices` / `ContractServiceUnits` (windowed on the period
|
|
2647
|
+
`startDate`) / `ContractCharges` / `ContractRoleCosts` / `ContractRates`
|
|
2648
|
+
queries → `ContractBlocks` query (Block Hours contracts only, by label;
|
|
2649
|
+
skipped otherwise) → `BillingItems` query → invoices
|
|
2637
2650
|
(fetch-by-ids on the distinct billed `invoiceID`s; skipped on none) →
|
|
2638
|
-
`TimeEntries` query →
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
`
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2651
|
+
`TimeEntries` query → `Tickets` activity query (`or[ and[contractID,
|
|
2652
|
+
createDate in window], id in <the ticket ids the in-window time
|
|
2653
|
+
references> ]` — an older or closed ticket carrying this period's time is
|
|
2654
|
+
fetched, never reported as unresolvable) → the tickets
|
|
2655
|
+
`entityInformation/fields` metadata read (the open-status picklist) → open
|
|
2656
|
+
`Tickets` query (`status in` the open set) → resources (fetch-by-ids on the
|
|
2657
|
+
distinct time-row `resourceID`s; skipped on none) → `ExpenseItems` query +
|
|
2658
|
+
the ONE extra bounded miscoding probe (`TimeEntries` with `ticketID in
|
|
2659
|
+
join` + `or[contractID noteq, contractID notExist]`; the join is the
|
|
2660
|
+
fetched tickets whose record says THIS contract; skipped on an empty set)
|
|
2661
|
+
→ `ConfigurationItems` query (`contractID` = the CIs this contract covers,
|
|
2662
|
+
never the whole company; default-on) → `Subscriptions` query (CI join;
|
|
2663
|
+
skipped on no CIs) → the Subscriptions `entityInformation/fields` read
|
|
2664
|
+
(the `status` + `periodType` labels; only when subscriptions came back).
|
|
2665
|
+
Every window filter is `gte dateFrom` + `lt` the day
|
|
2666
|
+
after `dateTo` (a datetime field keeps the last day's later rows). All
|
|
2667
|
+
windows and month buckets are UTC days: date-only fields (`itemDate`,
|
|
2668
|
+
units `startDate`/`endDate`) come back as UTC midnight (`Z`), datetimes
|
|
2669
|
+
(ticket `createDate`) as UTC instants, and bucketing slices the ISO
|
|
2670
|
+
string — in a UTC+10/11 tenant a ticket created late local time buckets
|
|
2671
|
+
one UTC day (possibly one month) earlier than its local date. The
|
|
2646
2672
|
`time_entries.getPosted` / `getUnposted` endpoints are NEVER called (the
|
|
2647
2673
|
pinned vendor 500 on AU03); posted/unposted derives from the
|
|
2648
2674
|
`billingItems.timeEntryID` join (unposted = no billing item exists yet).
|
|
2649
2675
|
The structural exclusions (`/Services/`, `/ExpenseReports/`,
|
|
2650
2676
|
`/BillingCodes/` list reads, getPosted/getUnposted) are pinned by the
|
|
2651
2677
|
recorded-request test. Baseline = 12 (a resolvable contract); the
|
|
2652
|
-
typical busy contract =
|
|
2653
|
-
budget:
|
|
2654
|
-
|
|
2655
|
-
-
|
|
2678
|
+
typical busy contract = 18; the full fixture = 20 (21 on a Block Hours
|
|
2679
|
+
contract). `wires: { issued, requests, budget: 21, note? }` rides the
|
|
2680
|
+
result as the honesty stamp: `issued` counts logical part reads (a
|
|
2681
|
+
fetch-by-ids part counts once), `requests` the raw HTTP requests (its
|
|
2682
|
+
chunk queries + per-id gets counted individually; a read-cache-served
|
|
2683
|
+
contract `get` adds no request).
|
|
2684
|
+
- **Per-month windowing (parts `billingItems` + `timeEntries` +
|
|
2685
|
+
`contractServiceUnits`).** One
|
|
2656
2686
|
full-window page (`MaxRecords: limit`); when it comes back FULL with a
|
|
2657
2687
|
live next cursor, the part re-queries PER CALENDAR MONTH intersecting the
|
|
2658
2688
|
window (one page per month, the date field bounded to that month, in
|
|
@@ -2662,7 +2692,8 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2662
2692
|
higher limit`) — NO cursor walk (the house single-page rule; the composite
|
|
2663
2693
|
owns its own windowing so per-month figures stay correct by construction).
|
|
2664
2694
|
Every expansion read counts toward `wires.issued` (the note says why);
|
|
2665
|
-
the 36-month cap bounds the worst case.
|
|
2695
|
+
the 36-month cap bounds the worst case. A still-full month is also named
|
|
2696
|
+
in that month's `coverage_gaps` (`<part>: truncated at limit N`).
|
|
2666
2697
|
- **The cost-rate ladder (per time entry, zero extra wire).** Rung 1
|
|
2667
2698
|
`billing_item.ourCost`: a LINE cost, not a rate — Σ the finite `>= 0`
|
|
2668
2699
|
`ourCost` over the entry's billed lines (partial billing across months
|
|
@@ -2687,25 +2718,81 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2687
2718
|
is NOT a ladder rung (the customer billing rate — the wrong side of the
|
|
2688
2719
|
P&L).
|
|
2689
2720
|
- **Monthly P&L (one row per calendar month in the window).**
|
|
2690
|
-
`revenue_invoiced` (Σ
|
|
2691
|
-
non-voided invoices
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
`
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
`
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
`
|
|
2703
|
-
`
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
(
|
|
2721
|
+
`revenue_invoiced` (Σ the line revenue of the month's items on
|
|
2722
|
+
non-voided invoices; line revenue = `internalCurrencyExtendedPrice`,
|
|
2723
|
+
falling back to `extendedPrice`) · `revenue_by_type { recurring, labour,
|
|
2724
|
+
charges, expenses, other }` (the invoiced split, keyed on the verified
|
|
2725
|
+
`billingItemType` picklist: 1 Labor / 2 Labor Adjustment → labour, 3 Cost
|
|
2726
|
+
→ charges, 4 Expense → expenses, 5 Subscription → other, 6 Recurring
|
|
2727
|
+
Service/Bundle → recurring, 7 Recurring Contract Setup Fee → charges, 8
|
|
2728
|
+
Milestone → other; an absent/unknown type falls back to the line's FKs in
|
|
2729
|
+
the order `timeEntryID` → labour, `expenseItemID` → expenses, a charge FK
|
|
2730
|
+
→ charges, a contract service/bundle/period/adjustment FK → recurring —
|
|
2731
|
+
labour billed against a service carries `contractServiceID` too, verified)
|
|
2732
|
+
·
|
|
2733
|
+
`revenue_voided_excluded` (Σ on voided invoices) ·
|
|
2734
|
+
`revenue_posted_uninvoiced` (Σ on posted items with no invoice yet) ·
|
|
2735
|
+
`revenue_unresolved_invoice` (Σ on items whose invoice could not be read
|
|
2736
|
+
or was beyond the join cap — not recognized revenue, never silent: the
|
|
2737
|
+
month's `coverage_gaps` names `invoices: N line(s) on unresolved
|
|
2738
|
+
invoices`) · `revenue_expected` (Σ the period `internalCurrencyPrice`
|
|
2739
|
+
(fallback `price`) of the service units whose
|
|
2740
|
+
period STARTS in the month — a multi-month period is counted once, when
|
|
2741
|
+
it bills; `null` when the services/units basis is absent or the units
|
|
2742
|
+
part is `absent` — unknown, not 0. Unit `price`/`cost` are PERIOD totals
|
|
2743
|
+
(units × per-unit, prorated for a partial period — verified: the type-6
|
|
2744
|
+
recurring billing item has `itemDate` = the unit `startDate` and
|
|
2745
|
+
`extendedPrice` = the unit `price`). Units are normally one month but can
|
|
2746
|
+
span a year; the start-month attribution matches billing, so the monthly
|
|
2747
|
+
figures are a billing-period (cash) view, NOT accrual — an annual unit
|
|
2748
|
+
lands in its start month in one lump) ·
|
|
2749
|
+
`revenue_expected_unbilled = max(0, expected − the month's invoiced
|
|
2750
|
+
SERVICE lines)` (labour and charges never mask a missed recurring
|
|
2751
|
+
invoice) · `unposted_time { entries, hours, hoursAbsent?, value, absent? }`
|
|
2752
|
+
(billable time with no billing item yet — derived from the billing join,
|
|
2753
|
+
never getPosted/getUnposted; `hours` = Σ `max(0, hoursToBill)`, or `null`
|
|
2754
|
+
+ `hoursAbsent: 'HOURS_TO_BILL_MISSING'` when any of the month's unposted
|
|
2755
|
+
entries carries no numeric `hoursToBill` — unknown, never a summed 0; a
|
|
2756
|
+
deliberate `hoursToBill: 0` stays 0; `value` = Σ `hoursToBill ×
|
|
2757
|
+
internalCurrencyContractHourlyRate` (fallback `contractHourlyRate`) on
|
|
2758
|
+
Time & Materials contracts only, else `null` + `absent:
|
|
2759
|
+
'NOT_TIME_AND_MATERIALS_CONTRACT'`, `'CONTRACT_TYPE_UNKNOWN'`, or
|
|
2760
|
+
`'NO_CONTRACT_RATE'` when a role has no contract rate, or
|
|
2761
|
+
`'HOURS_TO_BILL_MISSING'` on a T&M contract when the hours are unknown) · `labour_hours`
|
|
2762
|
+
(Σ `hoursWorked` over positive entries — the same rule as the cost) ·
|
|
2763
|
+
`labour_cost { total, unresolvedHours, rateSources }` (the per-month
|
|
2764
|
+
mirror of the ladder audit) · `tooling_cost { vendor, caller_supplied }`
|
|
2765
|
+
(vendor = Σ `periodCost × the periodType monthly share` — by the tenant
|
|
2766
|
+
`periodType` LABEL (monthly 1, quarterly 1/3, semi-annual 1/6,
|
|
2767
|
+
annual/yearly 1/12); a One-Time subscription is never prorated: its
|
|
2768
|
+
`periodCost` is booked whole in its `effectiveDate` month; when the
|
|
2769
|
+
Subscriptions catalog read fails, the verified id maps apply (periodType
|
|
2770
|
+
1 One-Time, 2 Monthly, 3 Quarterly, 4 Semi-Annual, 5 Yearly; status 1
|
|
2771
|
+
Active, 2 Cancelled, 3 Inactive) and the note says so; an unrecognised
|
|
2772
|
+
periodType is excluded and named; only Active subscriptions count —
|
|
2773
|
+
Cancelled and Inactive are excluded — over the subscriptions on this
|
|
2774
|
+
contract's CIs active in the month, REPORTED ONLY:
|
|
2775
|
+
never folded into `total_cost`, since subscriptions bill outside the
|
|
2776
|
+
contract; `null` when the subscriptions part is skipped/absent;
|
|
2777
|
+
caller-supplied = the bag value, `0` when absent) · `recurring_cost`
|
|
2778
|
+
(charge COST — `extendedCost`, falling back to `unitCost × unitQuantity`
|
|
2779
|
+
when it is absent or 0 while a unit cost exists; never `billableAmount` —
|
|
2780
|
+
plus the `cost` of the units starting in the month, when included) ·
|
|
2781
|
+
`expense_cost` (Σ `internalCurrencyExpenseAmount`) · `total_cost` (labour
|
|
2782
|
+
+ caller-supplied tooling + recurring + expenses) · `profit =
|
|
2783
|
+
revenue_invoiced − total_cost` · `margin = profit / revenue_invoiced`
|
|
2784
|
+
when `> 0`, else `null` (the division is refused, not zeroed) ·
|
|
2785
|
+
`break_even_hours` (the labour hours the invoiced revenue absorbs at the
|
|
2786
|
+
average resolved cost rate before profit hits zero: `(revenue_invoiced −
|
|
2787
|
+
non-labour cost) / (Σcost / Σhours over resolved rows)`; `0` when the
|
|
2788
|
+
non-labour cost already exceeds revenue; `null` with no resolved hours or
|
|
2789
|
+
a zero rate — including an empty month) · `coverage_gaps` (why the
|
|
2790
|
+
month's figures may be incomplete: `<part>: absent (<code>)` /
|
|
2791
|
+
`truncated at limit N` / `capped at limit N` over units, charges, role
|
|
2792
|
+
costs, billing, time, expenses and subscriptions, plus the unresolved
|
|
2793
|
+
invoice lines; `[]` = complete). The P&L is single-currency in the
|
|
2794
|
+
INTERNAL currency (revenue and costs alike); money fields are emitted
|
|
2795
|
+
rounded to cents.
|
|
2709
2796
|
- **The 11 data-quality checks (always present, order-stable, `count: 0` =
|
|
2710
2797
|
a passed check).** Typed `{ code, count, sample_ids }` warnings
|
|
2711
2798
|
(`count` = the full in-memory/fetched-set count, never capped;
|
|
@@ -2721,11 +2808,13 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2721
2808
|
`missing_cost_rate` (the ladder's `missing` rows; the resource NOT_FOUND
|
|
2722
2809
|
misses ride the note) · `recurring_cost_drift` (month-over-month beyond
|
|
2723
2810
|
the 0.005 float dust; samples the charge/unit ids whose monthly
|
|
2724
|
-
contribution changed) · `expected_unbilled_revenue` (expected >
|
|
2725
|
-
|
|
2726
|
-
EMPTY sample — that is the assertion) ·
|
|
2811
|
+
contribution changed) · `expected_unbilled_revenue` (expected > the
|
|
2812
|
+
invoiced service lines + 0.005; the pure "expected with nothing billed"
|
|
2813
|
+
case carries the EMPTY sample — that is the assertion) ·
|
|
2814
|
+
`cross_contract_tickets` (the
|
|
2727
2815
|
ticket-side drift + the entry-side unresolvable set) ·
|
|
2728
|
-
`open_stale_tickets` (`now − lastActivityDate >
|
|
2816
|
+
`open_stale_tickets` (this contract's tickets; `now − lastActivityDate >
|
|
2817
|
+
staleAfterDays`, `now` =
|
|
2729
2818
|
the run's wall clock stamped `period.asOf`; absent `lastActivityDate` is
|
|
2730
2819
|
unknown, not zero) · `estimate_vs_actual` (both estimates prorated to the
|
|
2731
2820
|
window — `estimate × overlapDays / termDays` over the contract term — and
|
|
@@ -2738,12 +2827,26 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2738
2827
|
never silently dropped) · `possible_miscoding` (the probe rows + the
|
|
2739
2828
|
confirmed cross-contract entries, closed reason set
|
|
2740
2829
|
`no_contract_on_entry` / `ticket_of_other_contract` /
|
|
2741
|
-
`ticket_not_in_fetched_set`) · `open_renewal_items` (
|
|
2742
|
-
the stale-days detail, subscription expirations
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2830
|
+
`ticket_not_in_fetched_set`) · `open_renewal_items` (this contract's open
|
|
2831
|
+
tickets with the stale-days detail, subscription expirations with the
|
|
2832
|
+
prorated monthly cost, the contract end, one `included_hours_burn` row
|
|
2833
|
+
per purchased hour block) · `included_hours` (Block Hours contracts only:
|
|
2834
|
+
`{ value: { purchased, approved, remaining } }` from the `ContractBlocks`
|
|
2835
|
+
`hours` vs the vendor `hoursApproved`; any other contract type is `{
|
|
2836
|
+
value: null, absent: 'NOT_BLOCK_HOURS_CONTRACT' }`, an unresolvable type
|
|
2837
|
+
`'CONTRACT_TYPE_UNKNOWN'` — service units are a
|
|
2838
|
+
quantity, never read as hours, and retainers are dollar-denominated).
|
|
2839
|
+
The probe rows are EXCLUDED from the P&L folds (diagnostic — time not
|
|
2840
|
+
coded to this contract).
|
|
2841
|
+
- **Degradation (per part typed `ok` / `blind` / `skipped` / `absent`; the
|
|
2842
|
+
op NEVER fails the call on a degraded part).** Only the contract read is
|
|
2843
|
+
fatal (the root: `NOT_FOUND` — or any other fault on it — propagates).
|
|
2844
|
+
A vendor fault on any bounded list part degrades that part to `status:
|
|
2845
|
+
'absent'` + `absent: <the typed error code>` + the note (never a true
|
|
2846
|
+
zero — no `emptyReason`), and every monthly row it feeds names it in
|
|
2847
|
+
`coverage_gaps`; a non-404 fault on the customer read degrades to
|
|
2848
|
+
`customer: null` + `parts.customer: 'absent'` + `customerAbsent: <code>`.
|
|
2849
|
+
Every tickets call is
|
|
2747
2850
|
`contractID`-filtered, so the pinned `unfiltered-blind` tickets class
|
|
2748
2851
|
never stamps a filtered empty (a filtered 0 is a TRUE zero with the
|
|
2749
2852
|
honest note); the 0-row parts carry the pinned
|
|
@@ -2758,6 +2861,34 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2758
2861
|
rows with the divisions refused (`margin: null`, `break_even_hours:
|
|
2759
2862
|
null`, `tooling_cost.vendor: null`, `revenue_expected: null`), all 11
|
|
2760
2863
|
warnings at `count: 0`, zero tool failure.
|
|
2864
|
+
- **Summary-first output (0.13.0).** By default each of the 16 per-part
|
|
2865
|
+
containers (`contractServices` … `subscriptions`) is a
|
|
2866
|
+
`ProfitabilityPartDigest`: every stamp (`status`, `capped`, `hasMore`,
|
|
2867
|
+
`note`, `blindSurface`, `emptyReason`, `absent`) plus `rows_fetched` (the
|
|
2868
|
+
row count), WITHOUT the raw `items` array. Every computed figure is
|
|
2869
|
+
returned either way — `monthly` (incl. `coverage_gaps`), `contract_type`,
|
|
2870
|
+
`included_hours`, `currency`, the activity/costliest-ticket/miscoding/
|
|
2871
|
+
renewal folds, `data_quality_warnings`, `costRateResolution`, `parts`,
|
|
2872
|
+
`wires` — as are the `contract` and `customer` records. `includeRows: true`
|
|
2873
|
+
returns the full `ProfitabilityPart<T>` containers (the pre-0.13.0 shape,
|
|
2874
|
+
`items` included, no `rows_fetched`); the wire is identical in both modes.
|
|
2875
|
+
The part fields are typed `ProfitabilityPart<T> | ProfitabilityPartDigest`
|
|
2876
|
+
(both exported from the package root with the other option/result types):
|
|
2877
|
+
narrow with `'items' in part` before reading rows, e.g.
|
|
2878
|
+
`const rows = 'items' in res.billingItems ? res.billingItems.items : [];`.
|
|
2879
|
+
On the 2-month test fixture the serialised result is 8,657 bytes without
|
|
2880
|
+
rows vs 15,060 with them (−43%); the fixture rows are trimmed to a few
|
|
2881
|
+
fields, so real vendor rows over a 12-month window make the rows the bulk
|
|
2882
|
+
of the payload and the saving much larger.
|
|
2883
|
+
- **0.12.1 corrections.** The 0.12.0 folds counted the charge
|
|
2884
|
+
`billableAmount` (sell price) as recurring cost, read service `units` as
|
|
2885
|
+
included hours, joined every company CI's subscriptions and added a
|
|
2886
|
+
yearly `periodCost` every month, compared the expected recurring revenue
|
|
2887
|
+
against all billed lines (labour included) and priced it at today's
|
|
2888
|
+
`unitPrice`, read service units unwindowed, reported time on older
|
|
2889
|
+
tickets as unresolvable, defined break-even without revenue, folded
|
|
2890
|
+
expenses in their own currency, and threw on any bounded-part fault. The
|
|
2891
|
+
bullets above are the corrected semantics.
|
|
2761
2892
|
- **Registration surface.** The plan row (`capabilities.plan.json` →
|
|
2762
2893
|
re-derived hash) carries `kind: 'helper'`, `helperBasis: 'composite'`,
|
|
2763
2894
|
`effect: 'read'`, `dryRun: false`, the closed-bag `inputSchema` (the
|
|
@@ -2770,17 +2901,25 @@ returns the contract's reconciled P&L over a date window:
|
|
|
2770
2901
|
subset is ticket/contact/company/resource/time_entry only).
|
|
2771
2902
|
- **Test pins.** `test/resources/contracts.profitability.test.ts`: the
|
|
2772
2903
|
hand-computed 2-month synthetic fixture (every figure derived from the
|
|
2773
|
-
fixture rows; `wires.issued =
|
|
2904
|
+
fixture rows; `wires.issued = 20` with the exact queue; every wire pinned
|
|
2774
2905
|
in issue order; the structural exclusions + zero-mutating recorded paths;
|
|
2775
2906
|
the ladder audit incl. the per-month `rateSources`; the 11 checks with
|
|
2776
2907
|
exact counts/samples; the activity/highest-cost/miscoding/renewal folds;
|
|
2777
2908
|
the per-month windowing (exact extra month queries, id-dedup, the
|
|
2778
2909
|
still-full month note; `wires.issued` counts the expansion); the
|
|
2779
2910
|
join-set/`limit` caps; the malformed-bag refusal table (one complete
|
|
2780
|
-
problem list, zero wire); the contract NOT_FOUND
|
|
2911
|
+
problem list, zero wire); the contract NOT_FOUND; the summary-first pins
|
|
2912
|
+
(the default digest = the full container minus `items` plus the
|
|
2913
|
+
hand-counted `rows_fetched` per part, every computed figure equal across
|
|
2914
|
+
modes, a non-boolean `includeRows` and a missing opts bag refused with
|
|
2915
|
+
zero wire); the 0.12.1 pins: the
|
|
2916
|
+
charge cost/period-type/line-type classifiers, the Block Hours
|
|
2917
|
+
`contract_blocks` path, T&M unposted pricing + the time-referenced old
|
|
2918
|
+
ticket, the exclusive window ends, the month-level `coverage_gaps`).
|
|
2781
2919
|
`test/resources/profitability-degradation.test.ts`: the empty-tenant
|
|
2782
2920
|
13-wire suite (every part degrades, zero tool failure), the dangling
|
|
2783
|
-
customer FK, the
|
|
2921
|
+
customer FK, the non-404 customer fault (`absent`), a bounded-part fault
|
|
2922
|
+
(`absent` + `coverage_gaps`, the call resolves), the resources blind-list get-fallback (rung 3 from the
|
|
2784
2923
|
gets, the quirk note), the per-item invoice fault (retries off — the
|
|
2785
2924
|
fault semantics are the pin, not the transport's 5xx budget), the
|
|
2786
2925
|
fieldCatalog-fault open-pass skip, the status-helper sentinel
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-autotask",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Fully-typed TypeScript SDK for the Autotask (Datto) PSA REST API. Zero runtime dependencies. Built for building MCP servers and integrations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|