@goxtechnologies/connectwise-psa-mcp 1.1.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.
Files changed (147) hide show
  1. package/data/connectwise_api.db +0 -0
  2. package/data/manage.json +298179 -0
  3. package/dist/index.d.ts +10 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +116 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/operations/analytics-extended.d.ts +6 -0
  8. package/dist/operations/analytics-extended.d.ts.map +1 -0
  9. package/dist/operations/analytics-extended.js +825 -0
  10. package/dist/operations/analytics-extended.js.map +1 -0
  11. package/dist/operations/analytics-msp-assets.d.ts +3 -0
  12. package/dist/operations/analytics-msp-assets.d.ts.map +1 -0
  13. package/dist/operations/analytics-msp-assets.js +180 -0
  14. package/dist/operations/analytics-msp-assets.js.map +1 -0
  15. package/dist/operations/analytics-msp-clients.d.ts +3 -0
  16. package/dist/operations/analytics-msp-clients.d.ts.map +1 -0
  17. package/dist/operations/analytics-msp-clients.js +198 -0
  18. package/dist/operations/analytics-msp-clients.js.map +1 -0
  19. package/dist/operations/analytics-msp-comms.d.ts +3 -0
  20. package/dist/operations/analytics-msp-comms.d.ts.map +1 -0
  21. package/dist/operations/analytics-msp-comms.js +127 -0
  22. package/dist/operations/analytics-msp-comms.js.map +1 -0
  23. package/dist/operations/analytics-msp-contracts.d.ts +3 -0
  24. package/dist/operations/analytics-msp-contracts.d.ts.map +1 -0
  25. package/dist/operations/analytics-msp-contracts.js +91 -0
  26. package/dist/operations/analytics-msp-contracts.js.map +1 -0
  27. package/dist/operations/analytics-msp-financial.d.ts +3 -0
  28. package/dist/operations/analytics-msp-financial.d.ts.map +1 -0
  29. package/dist/operations/analytics-msp-financial.js +300 -0
  30. package/dist/operations/analytics-msp-financial.js.map +1 -0
  31. package/dist/operations/analytics-msp-procurement.d.ts +3 -0
  32. package/dist/operations/analytics-msp-procurement.d.ts.map +1 -0
  33. package/dist/operations/analytics-msp-procurement.js +78 -0
  34. package/dist/operations/analytics-msp-procurement.js.map +1 -0
  35. package/dist/operations/analytics-msp-projects.d.ts +3 -0
  36. package/dist/operations/analytics-msp-projects.d.ts.map +1 -0
  37. package/dist/operations/analytics-msp-projects.js +190 -0
  38. package/dist/operations/analytics-msp-projects.js.map +1 -0
  39. package/dist/operations/analytics-msp-sales.d.ts +3 -0
  40. package/dist/operations/analytics-msp-sales.d.ts.map +1 -0
  41. package/dist/operations/analytics-msp-sales.js +99 -0
  42. package/dist/operations/analytics-msp-sales.js.map +1 -0
  43. package/dist/operations/analytics-msp-schedule.d.ts +3 -0
  44. package/dist/operations/analytics-msp-schedule.d.ts.map +1 -0
  45. package/dist/operations/analytics-msp-schedule.js +339 -0
  46. package/dist/operations/analytics-msp-schedule.js.map +1 -0
  47. package/dist/operations/analytics-msp-team.d.ts +3 -0
  48. package/dist/operations/analytics-msp-team.d.ts.map +1 -0
  49. package/dist/operations/analytics-msp-team.js +195 -0
  50. package/dist/operations/analytics-msp-team.js.map +1 -0
  51. package/dist/operations/analytics-msp-tickets.d.ts +3 -0
  52. package/dist/operations/analytics-msp-tickets.d.ts.map +1 -0
  53. package/dist/operations/analytics-msp-tickets.js +578 -0
  54. package/dist/operations/analytics-msp-tickets.js.map +1 -0
  55. package/dist/operations/analytics-msp-time.d.ts +3 -0
  56. package/dist/operations/analytics-msp-time.d.ts.map +1 -0
  57. package/dist/operations/analytics-msp-time.js +485 -0
  58. package/dist/operations/analytics-msp-time.js.map +1 -0
  59. package/dist/operations/analytics-msp-utils.d.ts +49 -0
  60. package/dist/operations/analytics-msp-utils.d.ts.map +1 -0
  61. package/dist/operations/analytics-msp-utils.js +157 -0
  62. package/dist/operations/analytics-msp-utils.js.map +1 -0
  63. package/dist/operations/analytics.d.ts +9 -0
  64. package/dist/operations/analytics.d.ts.map +1 -0
  65. package/dist/operations/analytics.js +742 -0
  66. package/dist/operations/analytics.js.map +1 -0
  67. package/dist/operations/executor.d.ts +10 -0
  68. package/dist/operations/executor.d.ts.map +1 -0
  69. package/dist/operations/executor.js +243 -0
  70. package/dist/operations/executor.js.map +1 -0
  71. package/dist/operations/registry.d.ts +16 -0
  72. package/dist/operations/registry.d.ts.map +1 -0
  73. package/dist/operations/registry.js +847 -0
  74. package/dist/operations/registry.js.map +1 -0
  75. package/dist/services/api-database.d.ts +38 -0
  76. package/dist/services/api-database.d.ts.map +1 -0
  77. package/dist/services/api-database.js +191 -0
  78. package/dist/services/api-database.js.map +1 -0
  79. package/dist/services/cache.d.ts +12 -0
  80. package/dist/services/cache.d.ts.map +1 -0
  81. package/dist/services/cache.js +32 -0
  82. package/dist/services/cache.js.map +1 -0
  83. package/dist/services/connectwise-api.d.ts +43 -0
  84. package/dist/services/connectwise-api.d.ts.map +1 -0
  85. package/dist/services/connectwise-api.js +198 -0
  86. package/dist/services/connectwise-api.js.map +1 -0
  87. package/dist/services/db-builder.d.ts +11 -0
  88. package/dist/services/db-builder.d.ts.map +1 -0
  89. package/dist/services/db-builder.js +237 -0
  90. package/dist/services/db-builder.js.map +1 -0
  91. package/dist/services/fast-memory.d.ts +39 -0
  92. package/dist/services/fast-memory.d.ts.map +1 -0
  93. package/dist/services/fast-memory.js +147 -0
  94. package/dist/services/fast-memory.js.map +1 -0
  95. package/dist/services/load-env.d.ts +15 -0
  96. package/dist/services/load-env.d.ts.map +1 -0
  97. package/dist/services/load-env.js +59 -0
  98. package/dist/services/load-env.js.map +1 -0
  99. package/dist/tools/batch.d.ts +9 -0
  100. package/dist/tools/batch.d.ts.map +1 -0
  101. package/dist/tools/batch.js +159 -0
  102. package/dist/tools/batch.js.map +1 -0
  103. package/dist/tools/composite.d.ts +9 -0
  104. package/dist/tools/composite.d.ts.map +1 -0
  105. package/dist/tools/composite.js +353 -0
  106. package/dist/tools/composite.js.map +1 -0
  107. package/dist/tools/discovery.d.ts +9 -0
  108. package/dist/tools/discovery.d.ts.map +1 -0
  109. package/dist/tools/discovery.js +245 -0
  110. package/dist/tools/discovery.js.map +1 -0
  111. package/dist/tools/execution.d.ts +9 -0
  112. package/dist/tools/execution.d.ts.map +1 -0
  113. package/dist/tools/execution.js +130 -0
  114. package/dist/tools/execution.js.map +1 -0
  115. package/dist/tools/memory.d.ts +9 -0
  116. package/dist/tools/memory.d.ts.map +1 -0
  117. package/dist/tools/memory.js +152 -0
  118. package/dist/tools/memory.js.map +1 -0
  119. package/dist/tools/operations.d.ts +9 -0
  120. package/dist/tools/operations.d.ts.map +1 -0
  121. package/dist/tools/operations.js +214 -0
  122. package/dist/tools/operations.js.map +1 -0
  123. package/dist/tools/pagination.d.ts +9 -0
  124. package/dist/tools/pagination.d.ts.map +1 -0
  125. package/dist/tools/pagination.js +133 -0
  126. package/dist/tools/pagination.js.map +1 -0
  127. package/dist/tools/validation.d.ts +9 -0
  128. package/dist/tools/validation.d.ts.map +1 -0
  129. package/dist/tools/validation.js +705 -0
  130. package/dist/tools/validation.js.map +1 -0
  131. package/dist/types/index.d.ts +145 -0
  132. package/dist/types/index.d.ts.map +1 -0
  133. package/dist/types/index.js +3 -0
  134. package/dist/types/index.js.map +1 -0
  135. package/dist/types/operations.d.ts +30 -0
  136. package/dist/types/operations.d.ts.map +1 -0
  137. package/dist/types/operations.js +3 -0
  138. package/dist/types/operations.js.map +1 -0
  139. package/dist/utils/conditions.d.ts +20 -0
  140. package/dist/utils/conditions.d.ts.map +1 -0
  141. package/dist/utils/conditions.js +78 -0
  142. package/dist/utils/conditions.js.map +1 -0
  143. package/dist/utils/formatters.d.ts +35 -0
  144. package/dist/utils/formatters.d.ts.map +1 -0
  145. package/dist/utils/formatters.js +337 -0
  146. package/dist/utils/formatters.js.map +1 -0
  147. package/package.json +46 -0
@@ -0,0 +1,127 @@
1
+ // ConnectWise PSA MCP Server — MSP Communications Analytics Handlers
2
+ import { getAPI } from '../services/connectwise-api.js';
3
+ import { num, nested, daysAgo, round2, mdTable, settledWarning } from './analytics-msp-utils.js';
4
+ export const mspCommsHandlers = {};
5
+ function reg(name, handler) { mspCommsHandlers[name] = handler; }
6
+ /** Compute median of a numeric array. Returns 0 for empty arrays. */
7
+ function medianOf(values) {
8
+ if (values.length === 0)
9
+ return 0;
10
+ const sorted = [...values].sort((a, b) => a - b);
11
+ const mid = Math.floor(sorted.length / 2);
12
+ return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
13
+ }
14
+ /** Format minutes into a human-readable string (e.g. "2h 15m" or "45m"). */
15
+ function fmtMinutes(minutes) {
16
+ const h = Math.floor(minutes / 60);
17
+ const m = Math.round(minutes % 60);
18
+ return h > 0 ? `${h}h ${m}m` : `${m}m`;
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // first_contact_response_time
22
+ // For tickets created in period, fetch first note and compute response delta.
23
+ // Groups by priority.
24
+ // ---------------------------------------------------------------------------
25
+ reg('first_contact_response_time', async (params) => {
26
+ const api = getAPI();
27
+ const days = num(params['days'], 30);
28
+ const since = daysAgo(days);
29
+ const tickets = await api.paginatedFetch('/service/tickets', `dateEntered>=[${since}]`, 'id,priority/name,dateEntered', 500);
30
+ if (tickets.items.length === 0)
31
+ return `No tickets found in the last ${days} days.`;
32
+ // For each ticket, fetch the first note. Limit to 50 tickets to avoid excessive API load.
33
+ const sample = tickets.items.slice(0, 50);
34
+ const fcrNoteResults = await Promise.allSettled(sample.map(async (t) => {
35
+ const notes = await api.paginatedFetch(`/service/tickets/${t['id']}/notes`, '', 'id,dateCreated,createdBy', 1);
36
+ return { ticket: t, firstNote: notes.items[0] ?? null };
37
+ }));
38
+ // Bucket response times (in minutes) by priority
39
+ const byPriority = new Map();
40
+ for (const result of fcrNoteResults) {
41
+ if (result.status !== 'fulfilled')
42
+ continue;
43
+ const { ticket, firstNote } = result.value;
44
+ if (!firstNote)
45
+ continue;
46
+ const entered = new Date(String(ticket['dateEntered'] ?? '')).getTime();
47
+ const noted = new Date(String(firstNote['dateCreated'] ?? '')).getTime();
48
+ if (!entered || !noted || noted < entered)
49
+ continue;
50
+ const minutesDelta = (noted - entered) / 60000;
51
+ const priority = nested(ticket, 'priority', 'name');
52
+ const bucket = byPriority.get(priority);
53
+ if (bucket)
54
+ bucket.push(minutesDelta);
55
+ else
56
+ byPriority.set(priority, [minutesDelta]);
57
+ }
58
+ if (byPriority.size === 0)
59
+ return `No response time data available for the last ${days} days.`;
60
+ const rows = [];
61
+ for (const [priority, times] of [...byPriority.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
62
+ const avg = times.reduce((acc, t) => acc + t, 0) / times.length;
63
+ const med = medianOf(times);
64
+ rows.push([priority, fmtMinutes(round2(avg)), fmtMinutes(round2(med)), String(times.length)]);
65
+ }
66
+ return [
67
+ `## First Contact Response Time (last ${days} days, sample of ${sample.length} tickets)\n`,
68
+ mdTable(['Priority', 'Avg Response', 'Median', 'Count'], rows),
69
+ ].join('\n') + settledWarning(fcrNoteResults, 'sub-requests');
70
+ });
71
+ // ---------------------------------------------------------------------------
72
+ // communication_frequency_by_client
73
+ // Fetch ticket notes in period, group by ticket's company. Count notes/ticket.
74
+ // ---------------------------------------------------------------------------
75
+ reg('communication_frequency_by_client', async (params) => {
76
+ const api = getAPI();
77
+ const days = num(params['days'], 30);
78
+ const since = daysAgo(days);
79
+ // Fetch recent tickets with company info to build a ticket->company map
80
+ const tickets = await api.paginatedFetch('/service/tickets', `dateEntered>=[${since}]`, 'id,company/name', 1000);
81
+ if (tickets.items.length === 0)
82
+ return `No tickets found in the last ${days} days.`;
83
+ const ticketToCompany = new Map();
84
+ for (const t of tickets.items) {
85
+ ticketToCompany.set(String(t['id']), nested(t, 'company', 'name'));
86
+ }
87
+ // Fetch notes for those tickets. Limit sample to 100 tickets.
88
+ const sampleTickets = tickets.items.slice(0, 100);
89
+ const commNoteResults = await Promise.allSettled(sampleTickets.map(async (t) => {
90
+ const notes = await api.paginatedFetch(`/service/tickets/${t['id']}/notes`, '', 'id,dateCreated', 50);
91
+ return { ticketId: String(t['id']), noteCount: notes.items.length, lastNote: notes.items[0] ?? null };
92
+ }));
93
+ // Aggregate by company
94
+ const byCompany = new Map();
95
+ for (const result of commNoteResults) {
96
+ if (result.status !== 'fulfilled')
97
+ continue;
98
+ const { ticketId, noteCount, lastNote } = result.value;
99
+ const company = ticketToCompany.get(ticketId) ?? 'Unknown';
100
+ const lastDate = String(lastNote?.['dateCreated'] ?? '').substring(0, 10);
101
+ const existing = byCompany.get(company);
102
+ if (existing) {
103
+ existing.notes += noteCount;
104
+ existing.ticketIds.add(ticketId);
105
+ if (lastDate && lastDate > existing.lastContact)
106
+ existing.lastContact = lastDate;
107
+ }
108
+ else {
109
+ byCompany.set(company, { notes: noteCount, ticketIds: new Set([ticketId]), lastContact: lastDate });
110
+ }
111
+ }
112
+ if (byCompany.size === 0)
113
+ return `No communication data available for the last ${days} days.`;
114
+ const rows = [...byCompany.entries()]
115
+ .sort((a, b) => b[1].notes - a[1].notes)
116
+ .slice(0, 30)
117
+ .map(([company, d]) => {
118
+ const ticketCount = d.ticketIds.size;
119
+ const notesPerTicket = ticketCount > 0 ? round2(d.notes / ticketCount) : 0;
120
+ return [company, String(d.notes), String(ticketCount), String(notesPerTicket), d.lastContact || 'N/A'];
121
+ });
122
+ return [
123
+ `## Communication Frequency by Client (last ${days} days, sample of ${sampleTickets.length} tickets)\n`,
124
+ mdTable(['Company', 'Notes', 'Tickets', 'Notes/Ticket', 'Last Contact'], rows),
125
+ ].join('\n') + settledWarning(commNoteResults, 'sub-requests');
126
+ });
127
+ //# sourceMappingURL=analytics-msp-comms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-comms.js","sourceRoot":"","sources":["../../src/operations/analytics-msp-comms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type MspHandler } from './analytics-msp-utils.js';
2
+ export declare const mspContractHandlers: Record<string, MspHandler>;
3
+ //# sourceMappingURL=analytics-msp-contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-contracts.d.ts","sourceRoot":"","sources":["../../src/operations/analytics-msp-contracts.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,UAAU,EAAiD,MAAM,0BAA0B,CAAC;AAEnI,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM,CAAC"}
@@ -0,0 +1,91 @@
1
+ // ConnectWise PSA MCP Server — MSP Contract & Agreement Analytics Handlers
2
+ import { getAPI } from '../services/connectwise-api.js';
3
+ import { num, nested, round2, currencyDisplay, mdTable } from './analytics-msp-utils.js';
4
+ export const mspContractHandlers = {};
5
+ function reg(name, handler) { mspContractHandlers[name] = handler; }
6
+ /** Returns today's ISO date string at midnight UTC. */
7
+ function today() { return new Date().toISOString().split('T')[0] + 'T00:00:00Z'; }
8
+ /** Returns ISO date string N days in the future at midnight UTC. */
9
+ function daysFromNow(n) {
10
+ const d = new Date();
11
+ d.setUTCDate(d.getUTCDate() + n);
12
+ return d.toISOString().split('T')[0] + 'T00:00:00Z';
13
+ }
14
+ /** Returns the number of days from today until the given ISO date. Negative if in the past. */
15
+ function daysUntil(iso) {
16
+ if (!iso)
17
+ return 0;
18
+ return Math.floor((new Date(iso).getTime() - Date.now()) / 86400000);
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // expiring_agreements_forecast
22
+ // Fetch agreements ending within N days. Show revenue at risk.
23
+ // ---------------------------------------------------------------------------
24
+ reg('expiring_agreements_forecast', async (params) => {
25
+ const api = getAPI();
26
+ const daysAhead = num(params['days_ahead'], 90);
27
+ const cutoff = daysFromNow(daysAhead);
28
+ const r = await api.paginatedFetch('/finance/agreements', `cancelledFlag=false AND endDate>=[${today()}] AND endDate<=[${cutoff}]`, 'id,name,company/name,endDate,billAmount', 500);
29
+ if (r.items.length === 0)
30
+ return `No agreements expiring in the next ${daysAhead} days.`;
31
+ const sorted = [...r.items].sort((a, b) => String(a['endDate'] ?? '').localeCompare(String(b['endDate'] ?? '')));
32
+ const totalMRR = sorted.reduce((acc, a) => acc + num(a['billAmount']), 0);
33
+ const rows = sorted.map(a => {
34
+ const endDate = String(a['endDate'] ?? '').substring(0, 10);
35
+ const daysLeft = daysUntil(String(a['endDate'] ?? ''));
36
+ const monthlyRev = num(a['billAmount']);
37
+ const revenueAtRisk = round2(monthlyRev * Math.max(1, Math.ceil(daysLeft / 30)));
38
+ return [
39
+ String(a['name'] ?? 'N/A'),
40
+ nested(a, 'company', 'name'),
41
+ endDate,
42
+ `${daysLeft}d`,
43
+ currencyDisplay(monthlyRev),
44
+ currencyDisplay(revenueAtRisk),
45
+ ];
46
+ });
47
+ return [
48
+ `## Expiring Agreements Forecast (next ${daysAhead} days)\n`,
49
+ `**Agreements at Risk:** ${r.items.length} | **Total MRR at Risk:** ${currencyDisplay(round2(totalMRR))}\n`,
50
+ mdTable(['Agreement', 'Company', 'End Date', 'Days Left', 'Monthly Revenue', 'Revenue at Risk'], rows),
51
+ ].join('\n');
52
+ });
53
+ // ---------------------------------------------------------------------------
54
+ // renewal_pipeline_value
55
+ // Fetch agreements expiring in next 12 months, grouped by month.
56
+ // ---------------------------------------------------------------------------
57
+ reg('renewal_pipeline_value', async (_params) => {
58
+ const api = getAPI();
59
+ const cutoff = daysFromNow(365);
60
+ const r = await api.paginatedFetch('/finance/agreements', `cancelledFlag=false AND endDate>=[${today()}] AND endDate<=[${cutoff}]`, 'id,name,company/name,endDate,billAmount', 500);
61
+ if (r.items.length === 0)
62
+ return 'No agreements expiring in the next 12 months.';
63
+ // Group by YYYY-MM
64
+ const byMonth = new Map();
65
+ for (const a of r.items) {
66
+ const month = String(a['endDate'] ?? '').substring(0, 7);
67
+ if (!month)
68
+ continue;
69
+ const bucket = byMonth.get(month);
70
+ if (bucket)
71
+ bucket.push(a);
72
+ else
73
+ byMonth.set(month, [a]);
74
+ }
75
+ const sortedMonths = [...byMonth.keys()].sort();
76
+ let cumulative = 0;
77
+ const rows = [];
78
+ for (const month of sortedMonths) {
79
+ const agreements = byMonth.get(month) ?? [];
80
+ const mrr = agreements.reduce((acc, a) => acc + num(a['billAmount']), 0);
81
+ cumulative += mrr;
82
+ rows.push([month, String(agreements.length), currencyDisplay(round2(mrr)), currencyDisplay(round2(cumulative))]);
83
+ }
84
+ const totalMRR = r.items.reduce((acc, a) => acc + num(a['billAmount']), 0);
85
+ return [
86
+ `## Renewal Pipeline Value (next 12 months)\n`,
87
+ `**Total Agreements:** ${r.items.length} | **Total MRR at Risk:** ${currencyDisplay(round2(totalMRR))}\n`,
88
+ mdTable(['Month', 'Count', 'Total MRR', 'Cumulative at Risk'], rows),
89
+ ].join('\n');
90
+ });
91
+ //# sourceMappingURL=analytics-msp-contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-contracts.js","sourceRoot":"","sources":["../../src/operations/analytics-msp-contracts.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAA4C,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnI,MAAM,CAAC,MAAM,mBAAmB,GAA+B,EAAE,CAAC;AAClE,SAAS,GAAG,CAAC,IAAY,EAAE,OAAmB,IAAU,mBAAmB,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AAE9F,uDAAuD;AACvD,SAAS,KAAK,KAAa,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;AAE1F,oEAAoE;AACpE,SAAS,WAAW,CAAC,CAAS;IAC5B,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IACrB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;AACtD,CAAC;AAED,+FAA+F;AAC/F,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvE,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,+DAA+D;AAC/D,8EAA8E;AAE9E,GAAG,CAAC,8BAA8B,EAAE,KAAK,EAAE,MAAc,EAAE,EAAE;IAC3D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEtC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,cAAc,CAChC,qBAAqB,EACrB,qCAAqC,KAAK,EAAE,mBAAmB,MAAM,GAAG,EACxE,yCAAyC,EACzC,GAAG,CACJ,CAAC;IAEF,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,sCAAsC,SAAS,QAAQ,CAAC;IAEzF,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACjH,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1E,MAAM,IAAI,GAAe,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACtC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;QACxC,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO;YACL,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;YAC1B,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC;YAC5B,OAAO;YACP,GAAG,QAAQ,GAAG;YACd,eAAe,CAAC,UAAU,CAAC;YAC3B,eAAe,CAAC,aAAa,CAAC;SAC/B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,yCAAyC,SAAS,UAAU;QAC5D,2BAA2B,CAAC,CAAC,KAAK,CAAC,MAAM,6BAA6B,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI;QAC3G,OAAO,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC;KACvG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,yBAAyB;AACzB,iEAAiE;AACjE,8EAA8E;AAE9E,GAAG,CAAC,wBAAwB,EAAE,KAAK,EAAE,OAAe,EAAE,EAAE;IACtD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAEhC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,cAAc,CAChC,qBAAqB,EACrB,qCAAqC,KAAK,EAAE,mBAAmB,MAAM,GAAG,EACxE,yCAAyC,EACzC,GAAG,CACJ,CAAC;IAEF,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,+CAA+C,CAAC;IAEjF,mBAAmB;IACnB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,UAAU,IAAI,GAAG,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACnH,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3E,OAAO;QACL,8CAA8C;QAC9C,yBAAyB,CAAC,CAAC,KAAK,CAAC,MAAM,6BAA6B,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI;QACzG,OAAO,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,oBAAoB,CAAC,EAAE,IAAI,CAAC;KACrE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type MspHandler } from './analytics-msp-utils.js';
2
+ export declare const mspFinancialHandlers: Record<string, MspHandler>;
3
+ //# sourceMappingURL=analytics-msp-financial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-financial.d.ts","sourceRoot":"","sources":["../../src/operations/analytics-msp-financial.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,UAAU,EAYhB,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM,CAAC"}
@@ -0,0 +1,300 @@
1
+ // ConnectWise PSA MCP Server — MSP Analytics: Financial & Profitability (7 handlers)
2
+ import { getAPI } from '../services/connectwise-api.js';
3
+ import { num, nested, daysAgo, round2, groupByField, sum, pct, hoursDisplay, currencyDisplay, mdTable, settledWarning, } from './analytics-msp-utils.js';
4
+ export const mspFinancialHandlers = {};
5
+ function reg(name, handler) { mspFinancialHandlers[name] = handler; }
6
+ // ===========================================================================
7
+ // 1. revenue_per_company
8
+ // Billable time entries grouped by company, summing actualHours * hourlyRate.
9
+ // ===========================================================================
10
+ reg('revenue_per_company', async (params) => {
11
+ const api = getAPI();
12
+ const days = num(params['days'], 30);
13
+ const entries = await api.paginatedFetch('/time/entries', `billableOption='Billable' AND dateEntered>=[${daysAgo(days)}]`, 'id,actualHours,hourlyRate,company/name', 5000);
14
+ if (entries.items.length === 0)
15
+ return `No billable time entries found in the last ${days} days.`;
16
+ const byCompany = groupByField(entries.items, 'company/name');
17
+ const stats = new Map();
18
+ for (const [company, items] of byCompany) {
19
+ const hours = sum(items, 'actualHours');
20
+ const revenue = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyRate']), 0);
21
+ stats.set(company, { hours, revenue });
22
+ }
23
+ const sorted = [...stats.entries()].sort((a, b) => b[1].revenue - a[1].revenue);
24
+ const rows = sorted.map(([company, s]) => {
25
+ const effectiveRate = s.hours > 0 ? round2(s.revenue / s.hours) : 0;
26
+ return [company, hoursDisplay(s.hours), currencyDisplay(s.revenue), currencyDisplay(effectiveRate)];
27
+ });
28
+ const totalHours = [...stats.values()].reduce((a, s) => a + s.hours, 0);
29
+ const totalRevenue = [...stats.values()].reduce((a, s) => a + s.revenue, 0);
30
+ return [
31
+ `## Revenue per Company (last ${days} days)`,
32
+ ``,
33
+ `**Total Hours:** ${hoursDisplay(totalHours)} | **Total Revenue:** ${currencyDisplay(totalRevenue)}`,
34
+ ``,
35
+ mdTable(['Company', 'Hours', 'Revenue', 'Effective Rate'], rows),
36
+ ].join('\n');
37
+ });
38
+ // ===========================================================================
39
+ // 2. agreement_utilization_summary
40
+ // Active agreements vs. time entries consumed, with at-risk status.
41
+ // ===========================================================================
42
+ reg('agreement_utilization_summary', async (_params) => {
43
+ const api = getAPI();
44
+ const agreements = await api.paginatedFetch('/finance/agreements', 'cancelledFlag=false', 'id,name,company/name,budgetHours', 500);
45
+ if (agreements.items.length === 0)
46
+ return 'No active agreements found.';
47
+ const batch = agreements.items.slice(0, 40);
48
+ const agrResults = await Promise.allSettled(batch.map(async (a) => {
49
+ const timeEntries = await api.paginatedFetch('/time/entries', `agreement/id=${a['id']}`, 'id,actualHours', 2000);
50
+ const usedHours = timeEntries.items.reduce((acc, e) => acc + num(e['actualHours']), 0);
51
+ return { agreement: a, usedHours };
52
+ }));
53
+ const rows = [];
54
+ for (const result of agrResults) {
55
+ if (result.status !== 'fulfilled')
56
+ continue;
57
+ const { agreement: a, usedHours } = result.value;
58
+ const budget = num(a['budgetHours']);
59
+ const usedPct = budget > 0 ? round2((usedHours / budget) * 100) : 0;
60
+ const status = budget === 0 ? 'N/A' : usedPct > 100 ? 'OVER' : usedPct >= 80 ? 'At Risk' : 'OK';
61
+ rows.push([
62
+ String(a['name'] ?? 'N/A'),
63
+ nested(a, 'company', 'name'),
64
+ budget > 0 ? hoursDisplay(budget) : 'N/A',
65
+ hoursDisplay(usedHours),
66
+ budget > 0 ? `${usedPct}%` : 'N/A',
67
+ status,
68
+ ]);
69
+ }
70
+ if (rows.length === 0)
71
+ return 'No agreement utilization data available.';
72
+ const overCount = rows.filter(r => r[5] === 'OVER').length;
73
+ const atRiskCount = rows.filter(r => r[5] === 'At Risk').length;
74
+ return [
75
+ `## Agreement Utilization Summary`,
76
+ ``,
77
+ `**Agreements:** ${rows.length} | **Over Budget:** ${overCount} | **At Risk:** ${atRiskCount}`,
78
+ ``,
79
+ mdTable(['Agreement', 'Company', 'Budget', 'Used', '%', 'Status'], rows),
80
+ ].join('\n') + settledWarning(agrResults, 'sub-requests');
81
+ });
82
+ // ===========================================================================
83
+ // 3. agreement_burn_rate
84
+ // Monthly consumption rate and projected exhaustion date per agreement.
85
+ // ===========================================================================
86
+ reg('agreement_burn_rate', async (params) => {
87
+ const api = getAPI();
88
+ const agreementId = params['agreement_id'] !== undefined ? num(params['agreement_id']) : null;
89
+ const condition = agreementId !== null
90
+ ? `cancelledFlag=false AND id=${agreementId}`
91
+ : 'cancelledFlag=false';
92
+ const agreements = await api.paginatedFetch('/finance/agreements', condition, 'id,name,company/name,budgetHours,startDate', agreementId !== null ? 1 : 200);
93
+ if (agreements.items.length === 0)
94
+ return 'No matching agreements found.';
95
+ const batch = agreements.items.slice(0, 30);
96
+ const burnResults = await Promise.allSettled(batch.map(async (a) => {
97
+ const timeEntries = await api.paginatedFetch('/time/entries', `agreement/id=${a['id']}`, 'id,actualHours,dateEntered', 2000);
98
+ return { agreement: a, entries: timeEntries.items };
99
+ }));
100
+ const rows = [];
101
+ for (const result of burnResults) {
102
+ if (result.status !== 'fulfilled')
103
+ continue;
104
+ const { agreement: a, entries } = result.value;
105
+ const budget = num(a['budgetHours']);
106
+ const totalUsed = entries.reduce((acc, e) => acc + num(e['actualHours']), 0);
107
+ // Determine the date span of entries to compute monthly burn rate
108
+ const startDate = a['startDate'] ? new Date(String(a['startDate'])) : null;
109
+ const now = new Date();
110
+ let monthsElapsed = 1;
111
+ if (startDate !== null && startDate < now) {
112
+ const msPerMonth = 30.44 * 24 * 3600 * 1000;
113
+ monthsElapsed = Math.max(1, (now.getTime() - startDate.getTime()) / msPerMonth);
114
+ }
115
+ const monthlyBurn = round2(totalUsed / monthsElapsed);
116
+ const remaining = round2(budget - totalUsed);
117
+ let projectedExhaustion = 'N/A';
118
+ if (monthlyBurn > 0 && remaining > 0) {
119
+ const monthsLeft = remaining / monthlyBurn;
120
+ const exhaustDate = new Date();
121
+ exhaustDate.setDate(exhaustDate.getDate() + Math.round(monthsLeft * 30.44));
122
+ projectedExhaustion = exhaustDate.toISOString().substring(0, 10);
123
+ }
124
+ else if (remaining <= 0) {
125
+ projectedExhaustion = 'Already exhausted';
126
+ }
127
+ rows.push([
128
+ String(a['name'] ?? 'N/A'),
129
+ nested(a, 'company', 'name'),
130
+ `${monthlyBurn}h/mo`,
131
+ hoursDisplay(remaining),
132
+ projectedExhaustion,
133
+ ]);
134
+ }
135
+ if (rows.length === 0)
136
+ return 'No burn rate data available.';
137
+ return [
138
+ `## Agreement Burn Rate`,
139
+ ``,
140
+ mdTable(['Agreement', 'Company', 'Monthly Burn', 'Remaining', 'Projected Exhaustion Date'], rows),
141
+ ].join('\n') + settledWarning(burnResults, 'sub-requests');
142
+ });
143
+ // ===========================================================================
144
+ // 4. effective_hourly_rate
145
+ // Revenue divided by hours, grouped by company or member.
146
+ // ===========================================================================
147
+ reg('effective_hourly_rate_msp', async (params) => {
148
+ const api = getAPI();
149
+ const days = num(params['days'], 30);
150
+ const groupBy = String(params['group_by'] ?? 'company');
151
+ const isGroupByMember = groupBy === 'member';
152
+ const groupField = isGroupByMember ? 'member/identifier' : 'company/name';
153
+ const fields = isGroupByMember
154
+ ? 'id,actualHours,hourlyRate,member/identifier,member/firstName,member/lastName'
155
+ : 'id,actualHours,hourlyRate,company/name';
156
+ const entries = await api.paginatedFetch('/time/entries', `billableOption='Billable' AND dateEntered>=[${daysAgo(days)}]`, fields, 5000);
157
+ if (entries.items.length === 0)
158
+ return `No billable time entries found in the last ${days} days.`;
159
+ const byEntity = groupByField(entries.items, groupField);
160
+ const stats = new Map();
161
+ for (const [entity, items] of byEntity) {
162
+ const hours = sum(items, 'actualHours');
163
+ const revenue = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyRate']), 0);
164
+ const listRate = items.length > 0
165
+ ? items.reduce((acc, e) => acc + num(e['hourlyRate']), 0) / items.length
166
+ : 0;
167
+ stats.set(entity, { hours, revenue, listRate, count: items.length });
168
+ }
169
+ const sorted = [...stats.entries()].sort((a, b) => b[1].revenue - a[1].revenue);
170
+ const rows = sorted.map(([entity, s]) => {
171
+ const effectiveRate = s.hours > 0 ? round2(s.revenue / s.hours) : 0;
172
+ const vsListRate = s.listRate > 0 ? `${round2(((effectiveRate - s.listRate) / s.listRate) * 100)}%` : 'N/A';
173
+ return [entity, hoursDisplay(s.hours), currencyDisplay(s.revenue), currencyDisplay(effectiveRate), vsListRate];
174
+ });
175
+ const entityLabel = isGroupByMember ? 'Member' : 'Company';
176
+ return [
177
+ `## Effective Hourly Rate by ${entityLabel} (last ${days} days)`,
178
+ ``,
179
+ mdTable([entityLabel, 'Hours', 'Revenue', 'Effective Rate', 'vs List Rate'], rows),
180
+ ].join('\n');
181
+ });
182
+ // ===========================================================================
183
+ // 5. profitability_by_company
184
+ // Revenue (billable hours × rate) minus cost (hours × hourlyCost) per company.
185
+ // ===========================================================================
186
+ reg('profitability_by_company', async (params) => {
187
+ const api = getAPI();
188
+ const days = num(params['days'], 30);
189
+ const entries = await api.paginatedFetch('/time/entries', `billableOption='Billable' AND dateEntered>=[${daysAgo(days)}]`, 'id,actualHours,hourlyRate,hourlyCost,company/name', 5000);
190
+ if (entries.items.length === 0)
191
+ return `No billable time entries found in the last ${days} days.`;
192
+ const byCompany = groupByField(entries.items, 'company/name');
193
+ const stats = new Map();
194
+ for (const [company, items] of byCompany) {
195
+ const revenue = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyRate']), 0);
196
+ const cost = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyCost']), 0);
197
+ const hours = sum(items, 'actualHours');
198
+ stats.set(company, { revenue, cost, hours });
199
+ }
200
+ const sorted = [...stats.entries()].sort((a, b) => (b[1].revenue - b[1].cost) - (a[1].revenue - a[1].cost));
201
+ const rows = sorted.map(([company, s]) => {
202
+ const margin = round2(s.revenue - s.cost);
203
+ const marginPct = pct(s.revenue - s.cost, s.revenue);
204
+ return [company, currencyDisplay(s.revenue), currencyDisplay(s.cost), currencyDisplay(margin), marginPct];
205
+ });
206
+ const totalRevenue = [...stats.values()].reduce((a, s) => a + s.revenue, 0);
207
+ const totalCost = [...stats.values()].reduce((a, s) => a + s.cost, 0);
208
+ const totalMargin = round2(totalRevenue - totalCost);
209
+ return [
210
+ `## Profitability by Company (last ${days} days)`,
211
+ ``,
212
+ `**Total Revenue:** ${currencyDisplay(totalRevenue)} | **Total Cost:** ${currencyDisplay(totalCost)} | **Total Margin:** ${currencyDisplay(totalMargin)} (${pct(totalMargin, totalRevenue)})`,
213
+ ``,
214
+ mdTable(['Company', 'Revenue', 'Cost', 'Margin', 'Margin %'], rows),
215
+ ].join('\n');
216
+ });
217
+ // ===========================================================================
218
+ // 6. profitability_by_member
219
+ // Revenue minus cost per member (technician-level P&L).
220
+ // ===========================================================================
221
+ reg('profitability_by_member', async (params) => {
222
+ const api = getAPI();
223
+ const days = num(params['days'], 30);
224
+ const entries = await api.paginatedFetch('/time/entries', `billableOption='Billable' AND dateEntered>=[${daysAgo(days)}]`, 'id,actualHours,hourlyRate,hourlyCost,member/identifier', 5000);
225
+ if (entries.items.length === 0)
226
+ return `No billable time entries found in the last ${days} days.`;
227
+ const byMember = groupByField(entries.items, 'member/identifier');
228
+ const stats = new Map();
229
+ for (const [member, items] of byMember) {
230
+ const hours = sum(items, 'actualHours');
231
+ const revenue = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyRate']), 0);
232
+ const cost = items.reduce((acc, e) => acc + num(e['actualHours']) * num(e['hourlyCost']), 0);
233
+ stats.set(member, { hours, revenue, cost });
234
+ }
235
+ const sorted = [...stats.entries()].sort((a, b) => (b[1].revenue - b[1].cost) - (a[1].revenue - a[1].cost));
236
+ const rows = sorted.map(([member, s]) => {
237
+ const margin = round2(s.revenue - s.cost);
238
+ return [member, hoursDisplay(s.hours), currencyDisplay(s.revenue), currencyDisplay(s.cost), currencyDisplay(margin)];
239
+ });
240
+ const totalRevenue = [...stats.values()].reduce((a, s) => a + s.revenue, 0);
241
+ const totalCost = [...stats.values()].reduce((a, s) => a + s.cost, 0);
242
+ const totalMargin = round2(totalRevenue - totalCost);
243
+ return [
244
+ `## Profitability by Member (last ${days} days)`,
245
+ ``,
246
+ `**Total Revenue:** ${currencyDisplay(totalRevenue)} | **Total Cost:** ${currencyDisplay(totalCost)} | **Total Margin:** ${currencyDisplay(totalMargin)} (${pct(totalMargin, totalRevenue)})`,
247
+ ``,
248
+ mdTable(['Member', 'Billable Hours', 'Revenue', 'Cost', 'Margin'], rows),
249
+ ].join('\n');
250
+ });
251
+ // ===========================================================================
252
+ // 7. invoice_readiness
253
+ // Billable time split by invoiceFlag (ready vs. pending) grouped by company.
254
+ // ===========================================================================
255
+ reg('invoice_readiness', async (_params) => {
256
+ const api = getAPI();
257
+ const entries = await api.paginatedFetch('/time/entries', `billableOption='Billable'`, 'id,actualHours,hourlyRate,invoiceFlag,company/name', 5000);
258
+ if (entries.items.length === 0)
259
+ return 'No billable time entries found.';
260
+ const byCompany = new Map();
261
+ for (const e of entries.items) {
262
+ const company = nested(e, 'company', 'name');
263
+ if (!byCompany.has(company)) {
264
+ byCompany.set(company, { readyHours: 0, readyRevenue: 0, pendingHours: 0, pendingRevenue: 0 });
265
+ }
266
+ const stat = byCompany.get(company);
267
+ const hours = num(e['actualHours']);
268
+ const revenue = hours * num(e['hourlyRate']);
269
+ const isReady = e['invoiceFlag'] === true;
270
+ if (isReady) {
271
+ stat.readyHours += hours;
272
+ stat.readyRevenue += revenue;
273
+ }
274
+ else {
275
+ stat.pendingHours += hours;
276
+ stat.pendingRevenue += revenue;
277
+ }
278
+ }
279
+ const sorted = [...byCompany.entries()].sort((a, b) => (b[1].readyRevenue + b[1].pendingRevenue) - (a[1].readyRevenue + a[1].pendingRevenue));
280
+ const rows = sorted.map(([company, s]) => [
281
+ company,
282
+ hoursDisplay(s.readyHours),
283
+ currencyDisplay(s.readyRevenue),
284
+ hoursDisplay(s.pendingHours),
285
+ currencyDisplay(s.pendingRevenue),
286
+ ]);
287
+ const totals = [...byCompany.values()];
288
+ const totalReadyHours = totals.reduce((a, s) => a + s.readyHours, 0);
289
+ const totalReadyRevenue = totals.reduce((a, s) => a + s.readyRevenue, 0);
290
+ const totalPendingHours = totals.reduce((a, s) => a + s.pendingHours, 0);
291
+ const totalPendingRevenue = totals.reduce((a, s) => a + s.pendingRevenue, 0);
292
+ return [
293
+ `## Invoice Readiness`,
294
+ ``,
295
+ `**Ready:** ${hoursDisplay(totalReadyHours)} / ${currencyDisplay(totalReadyRevenue)} | **Pending:** ${hoursDisplay(totalPendingHours)} / ${currencyDisplay(totalPendingRevenue)}`,
296
+ ``,
297
+ mdTable(['Company', 'Ready Hours', 'Ready $', 'Pending Hours', 'Pending $'], rows),
298
+ ].join('\n');
299
+ });
300
+ //# sourceMappingURL=analytics-msp-financial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-financial.js","sourceRoot":"","sources":["../../src/operations/analytics-msp-financial.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type MspHandler } from './analytics-msp-utils.js';
2
+ export declare const mspProcurementHandlers: Record<string, MspHandler>;
3
+ //# sourceMappingURL=analytics-msp-procurement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-msp-procurement.d.ts","sourceRoot":"","sources":["../../src/operations/analytics-msp-procurement.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,UAAU,EAA6F,MAAM,0BAA0B,CAAC;AAE/K,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM,CAAC"}