untrap-mcp 0.1.4 → 0.2.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 (74) hide show
  1. package/dist/db/filters.d.ts +49 -0
  2. package/dist/db/filters.d.ts.map +1 -0
  3. package/dist/db/filters.js +97 -0
  4. package/dist/db/filters.js.map +1 -0
  5. package/dist/tools/analytics/billing.d.ts.map +1 -1
  6. package/dist/tools/analytics/billing.js +4 -1
  7. package/dist/tools/analytics/billing.js.map +1 -1
  8. package/dist/tools/analytics/qc-violations.d.ts.map +1 -1
  9. package/dist/tools/analytics/qc-violations.js +6 -0
  10. package/dist/tools/analytics/qc-violations.js.map +1 -1
  11. package/dist/tools/analytics/sla-breach-tickets.d.ts.map +1 -1
  12. package/dist/tools/analytics/sla-breach-tickets.js +4 -1
  13. package/dist/tools/analytics/sla-breach-tickets.js.map +1 -1
  14. package/dist/tools/analytics/sla-breaches.d.ts.map +1 -1
  15. package/dist/tools/analytics/sla-breaches.js +5 -2
  16. package/dist/tools/analytics/sla-breaches.js.map +1 -1
  17. package/dist/tools/analytics/technician-perf.d.ts.map +1 -1
  18. package/dist/tools/analytics/technician-perf.js +8 -0
  19. package/dist/tools/analytics/technician-perf.js.map +1 -1
  20. package/dist/tools/cfo/client-profitability.d.ts.map +1 -1
  21. package/dist/tools/cfo/client-profitability.js +18 -16
  22. package/dist/tools/cfo/client-profitability.js.map +1 -1
  23. package/dist/tools/cfo/financial-overview.d.ts.map +1 -1
  24. package/dist/tools/cfo/financial-overview.js +6 -4
  25. package/dist/tools/cfo/financial-overview.js.map +1 -1
  26. package/dist/tools/cfo/labor-cost-trend.d.ts.map +1 -1
  27. package/dist/tools/cfo/labor-cost-trend.js +33 -31
  28. package/dist/tools/cfo/labor-cost-trend.js.map +1 -1
  29. package/dist/tools/cfo/revenue-leakage.d.ts.map +1 -1
  30. package/dist/tools/cfo/revenue-leakage.js +20 -18
  31. package/dist/tools/cfo/revenue-leakage.js.map +1 -1
  32. package/dist/tools/cfo/service-cost-analysis.d.ts.map +1 -1
  33. package/dist/tools/cfo/service-cost-analysis.js +4 -2
  34. package/dist/tools/cfo/service-cost-analysis.js.map +1 -1
  35. package/dist/tools/cfo/technician-utilization.d.ts.map +1 -1
  36. package/dist/tools/cfo/technician-utilization.js +19 -17
  37. package/dist/tools/cfo/technician-utilization.js.map +1 -1
  38. package/dist/tools/composite/client-360.d.ts.map +1 -1
  39. package/dist/tools/composite/client-360.js +12 -9
  40. package/dist/tools/composite/client-360.js.map +1 -1
  41. package/dist/tools/composite/compare-periods.d.ts.map +1 -1
  42. package/dist/tools/composite/compare-periods.js +17 -13
  43. package/dist/tools/composite/compare-periods.js.map +1 -1
  44. package/dist/tools/composite/draft-email.d.ts.map +1 -1
  45. package/dist/tools/composite/draft-email.js +7 -4
  46. package/dist/tools/composite/draft-email.js.map +1 -1
  47. package/dist/tools/composite/morning-briefing.d.ts.map +1 -1
  48. package/dist/tools/composite/morning-briefing.js +10 -7
  49. package/dist/tools/composite/morning-briefing.js.map +1 -1
  50. package/dist/tools/composite/technician-360.d.ts.map +1 -1
  51. package/dist/tools/composite/technician-360.js +10 -6
  52. package/dist/tools/composite/technician-360.js.map +1 -1
  53. package/dist/tools/qbr/client-detail.d.ts.map +1 -1
  54. package/dist/tools/qbr/client-detail.js +13 -9
  55. package/dist/tools/qbr/client-detail.js.map +1 -1
  56. package/dist/tools/qbr/executive-summary.d.ts.map +1 -1
  57. package/dist/tools/qbr/executive-summary.js +20 -13
  58. package/dist/tools/qbr/executive-summary.js.map +1 -1
  59. package/dist/tools/qbr/financial-summary.d.ts.map +1 -1
  60. package/dist/tools/qbr/financial-summary.js +13 -8
  61. package/dist/tools/qbr/financial-summary.js.map +1 -1
  62. package/dist/tools/qbr/sla-performance.d.ts.map +1 -1
  63. package/dist/tools/qbr/sla-performance.js +11 -8
  64. package/dist/tools/qbr/sla-performance.js.map +1 -1
  65. package/dist/tools/qbr/technician-scorecard.d.ts.map +1 -1
  66. package/dist/tools/qbr/technician-scorecard.js +35 -26
  67. package/dist/tools/qbr/technician-scorecard.js.map +1 -1
  68. package/dist/tools/qbr/ticket-trends.d.ts.map +1 -1
  69. package/dist/tools/qbr/ticket-trends.js +18 -15
  70. package/dist/tools/qbr/ticket-trends.js.map +1 -1
  71. package/dist/tools/query/data-query.d.ts.map +1 -1
  72. package/dist/tools/query/data-query.js +24 -1
  73. package/dist/tools/query/data-query.js.map +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Cached filter configuration for an MSP.
3
+ * Fetched once on first use, reused for all subsequent tool calls.
4
+ */
5
+ interface MspFilters {
6
+ boardNames: string[];
7
+ enabledTechnicians: string[];
8
+ mspCompanyName: string;
9
+ }
10
+ /**
11
+ * Fetch and cache the user-configured filters for an MSP.
12
+ * These are READ-ONLY user preferences — never modify them.
13
+ */
14
+ export declare function getFilters(mspId: string): Promise<MspFilters>;
15
+ /**
16
+ * Build SQL WHERE fragments for ticket_lifecycle queries.
17
+ *
18
+ * Returns a string like:
19
+ * AND tl.board_name IN ('Board A', 'Board B')
20
+ * AND tl.owner_name IN ('Tech 1', 'Tech 2')
21
+ * AND LOWER(tl.company_name) NOT LIKE '%test%'
22
+ * ...
23
+ *
24
+ * Pass tableAlias to match your query (default 'tl').
25
+ */
26
+ export declare function buildTicketFilter(filters: MspFilters, opts?: {
27
+ tableAlias?: string;
28
+ includeBoardFilter?: boolean;
29
+ includeTechnicianFilter?: boolean;
30
+ includeCompanyExclusions?: boolean;
31
+ }): string;
32
+ /**
33
+ * Build SQL WHERE fragments for time_entries queries.
34
+ * time_entries uses member_name instead of owner_name.
35
+ */
36
+ export declare function buildTimeEntriesFilter(filters: MspFilters, opts?: {
37
+ tableAlias?: string;
38
+ includeTechnicianFilter?: boolean;
39
+ }): string;
40
+ /**
41
+ * Build SQL WHERE fragments for queries that use company_name column
42
+ * but don't have board_name (e.g., billing_classifications, MVs).
43
+ */
44
+ export declare function buildCompanyExclusionFilter(filters: MspFilters, opts?: {
45
+ tableAlias?: string;
46
+ companyColumn?: string;
47
+ }): string;
48
+ export {};
49
+ //# sourceMappingURL=filters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../../src/db/filters.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;CACxB;AAKD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+BnE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC/B,GACL,MAAM,CAsCR;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC9B,GACL,MAAM,CAaR;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,MAAM,CAYR"}
@@ -0,0 +1,97 @@
1
+ import { queryRLS } from "./gateway-client.js";
2
+ // In-memory cache keyed by mspId (lives for the process lifetime = one MCP session)
3
+ const filterCache = new Map();
4
+ /**
5
+ * Fetch and cache the user-configured filters for an MSP.
6
+ * These are READ-ONLY user preferences — never modify them.
7
+ */
8
+ export async function getFilters(mspId) {
9
+ const cached = filterCache.get(mspId);
10
+ if (cached)
11
+ return cached;
12
+ // Fetch all three configs in parallel
13
+ const [boardRows, techRows, mspRows] = await Promise.all([
14
+ queryRLS(`SELECT board_name FROM msp_board_names WHERE msp_id = $1`, [mspId], mspId),
15
+ queryRLS(`SELECT technician_name FROM msp_technician_preferences WHERE msp_id = $1 AND is_enabled = true`, [mspId], mspId),
16
+ queryRLS(`SELECT name FROM msp_table WHERE id = $1`, [mspId], mspId),
17
+ ]);
18
+ const filters = {
19
+ boardNames: boardRows.map((r) => r.board_name),
20
+ enabledTechnicians: techRows.map((r) => r.technician_name),
21
+ mspCompanyName: mspRows[0]?.name || "",
22
+ };
23
+ filterCache.set(mspId, filters);
24
+ return filters;
25
+ }
26
+ function escapeSQL(val) {
27
+ return val.replace(/'/g, "''");
28
+ }
29
+ /**
30
+ * Build SQL WHERE fragments for ticket_lifecycle queries.
31
+ *
32
+ * Returns a string like:
33
+ * AND tl.board_name IN ('Board A', 'Board B')
34
+ * AND tl.owner_name IN ('Tech 1', 'Tech 2')
35
+ * AND LOWER(tl.company_name) NOT LIKE '%test%'
36
+ * ...
37
+ *
38
+ * Pass tableAlias to match your query (default 'tl').
39
+ */
40
+ export function buildTicketFilter(filters, opts = {}) {
41
+ const { tableAlias: t = "tl", includeBoardFilter = true, includeTechnicianFilter = true, includeCompanyExclusions = true, } = opts;
42
+ let sql = "";
43
+ // Board filter
44
+ if (includeBoardFilter && filters.boardNames.length > 0) {
45
+ const list = filters.boardNames.map((b) => `'${escapeSQL(b)}'`).join(", ");
46
+ sql += `\n AND ${t}.board_name IN (${list})`;
47
+ }
48
+ // Technician filter (only when query involves owner_name)
49
+ if (includeTechnicianFilter && filters.enabledTechnicians.length > 0) {
50
+ const list = filters.enabledTechnicians
51
+ .map((n) => `'${escapeSQL(n)}'`)
52
+ .join(", ");
53
+ sql += `\n AND ${t}.owner_name IN (${list})`;
54
+ }
55
+ // Company exclusions
56
+ if (includeCompanyExclusions) {
57
+ sql += `
58
+ AND LOWER(${t}.company_name) NOT LIKE '%test%'
59
+ AND LOWER(${t}.company_name) NOT LIKE '%catchall%'
60
+ AND ${t}.company_name IS NOT NULL
61
+ AND ${t}.company_name != ''`;
62
+ if (filters.mspCompanyName) {
63
+ sql += `\n AND LOWER(${t}.company_name) NOT LIKE LOWER('%${escapeSQL(filters.mspCompanyName)}%')`;
64
+ }
65
+ }
66
+ return sql;
67
+ }
68
+ /**
69
+ * Build SQL WHERE fragments for time_entries queries.
70
+ * time_entries uses member_name instead of owner_name.
71
+ */
72
+ export function buildTimeEntriesFilter(filters, opts = {}) {
73
+ const { tableAlias: t = "te", includeTechnicianFilter = true } = opts;
74
+ let sql = "";
75
+ if (includeTechnicianFilter && filters.enabledTechnicians.length > 0) {
76
+ const list = filters.enabledTechnicians
77
+ .map((n) => `'${escapeSQL(n)}'`)
78
+ .join(", ");
79
+ sql += `\n AND ${t}.member_name IN (${list})`;
80
+ }
81
+ return sql;
82
+ }
83
+ /**
84
+ * Build SQL WHERE fragments for queries that use company_name column
85
+ * but don't have board_name (e.g., billing_classifications, MVs).
86
+ */
87
+ export function buildCompanyExclusionFilter(filters, opts = {}) {
88
+ const { tableAlias: t = "bc", companyColumn = "company_name" } = opts;
89
+ let sql = `
90
+ AND LOWER(${t}.${companyColumn}) NOT LIKE '%test%'
91
+ AND LOWER(${t}.${companyColumn}) NOT LIKE '%catchall%'`;
92
+ if (filters.mspCompanyName) {
93
+ sql += `\n AND LOWER(${t}.${companyColumn}) NOT LIKE LOWER('%${escapeSQL(filters.mspCompanyName)}%')`;
94
+ }
95
+ return sql;
96
+ }
97
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.js","sourceRoot":"","sources":["../../src/db/filters.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/billing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa;;;;;;;GA6BtB"}
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/billing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa;;;;;;;GAgCtB"}
@@ -1,7 +1,10 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildCompanyExclusionFilter } from "../../db/filters.js";
2
3
  export async function getBillingOpportunities(mspId, params) {
3
4
  const fromDate = params.fromDate ?? new Date(Date.now() - 30 * 86400000).toISOString().split("T")[0];
4
5
  const toDate = params.toDate ?? new Date().toISOString().split("T")[0];
6
+ const filters = await getFilters(mspId);
7
+ const companyFilter = buildCompanyExclusionFilter(filters, { tableAlias: "bc" });
5
8
  const rows = await queryRLS(`SELECT bc.ticket_id, bc.company_name, bc.classification,
6
9
  bc.billable_amount, bc.resolved_date::text
7
10
  FROM agents.billing_classifications bc
@@ -9,7 +12,7 @@ export async function getBillingOpportunities(mspId, params) {
9
12
  AND bc.classification = 'out_of_scope'
10
13
  AND bc.resolved_date >= $2::date
11
14
  AND bc.resolved_date <= $3::date
12
- AND bc.billable_amount > 0
15
+ AND bc.billable_amount > 0${companyFilter}
13
16
  ORDER BY bc.billable_amount DESC`, [mspId, fromDate, toDate], mspId);
14
17
  const totalDollars = rows.reduce((sum, r) => sum + (r.billable_amount || 0), 0);
15
18
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../../src/tools/analytics/billing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAQtD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAa,EACb,MAAqB;IAErB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;;;;sCAQkC,EAClC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EACzB,KAAK,CACN,CAAC;IAEF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhF,OAAO;QACL,OAAO,EAAE;YACP,aAAa,EAAE,IAAI,CAAC,MAAM;YAC1B,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;YACnD,cAAc,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;SAC3F;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../../src/tools/analytics/billing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAQ9E,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAa,EACb,MAAqB;IAErB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,aAAa,GAAG,2BAA2B,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjF,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;;;mCAO+B,aAAa;sCACV,EAClC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EACzB,KAAK,CACN,CAAC;IAEF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhF,OAAO;QACL,OAAO,EAAE;YACP,aAAa,EAAE,IAAI,CAAC,MAAM;YAC1B,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;YACnD,cAAc,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;SAC3F;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"qc-violations.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/qc-violations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,UAAU,iBAAiB;IACzB,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClD,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;CACzD;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,iBAAiB;;;;;;;;;;;;;GAmE1B"}
1
+ {"version":3,"file":"qc-violations.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/qc-violations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,UAAU,iBAAiB;IACzB,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClD,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;CACzD;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,iBAAiB;;;;;;;;;;;;;GA0E1B"}
@@ -1,5 +1,7 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters } from "../../db/filters.js";
2
3
  export async function getQCViolations(mspId, params) {
4
+ const filters = await getFilters(mspId);
3
5
  let whereClause = "WHERE v.msp_id = $1";
4
6
  const queryParams = [mspId];
5
7
  let paramIndex = 2;
@@ -13,6 +15,10 @@ export async function getQCViolations(mspId, params) {
13
15
  queryParams.push(params.status);
14
16
  paramIndex++;
15
17
  }
18
+ if (filters.enabledTechnicians.length > 0) {
19
+ const techList = filters.enabledTechnicians.map(n => `'${n.replace(/'/g, "''")}'`).join(", ");
20
+ whereClause += ` AND v.technician_name IN (${techList})`;
21
+ }
16
22
  const rows = await queryRLS(`SELECT v.violation_id, v.ticket_id, v.ticket_summary,
17
23
  v.rule_id, v.rule_name, v.severity,
18
24
  v.description, v.technician_name, v.client_name,
@@ -1 +1 @@
1
- {"version":3,"file":"qc-violations.js","sourceRoot":"","sources":["../../../src/tools/analytics/qc-violations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAQtD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,MAAyB;IAEzB,IAAI,WAAW,GAAG,qBAAqB,CAAC;IACxC,MAAM,WAAW,GAAc,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,WAAW,IAAI,sBAAsB,UAAU,EAAE,CAAC;QAClD,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,WAAW,IAAI,2BAA2B,UAAU,EAAE,CAAC;QACvD,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;OAKG,WAAW;;eAEH,EACX,WAAW,EACX,KAAK,CACN,CAAC;IAEF,MAAM,OAAO,GAAG;QACd,gBAAgB,EAAE,IAAI,CAAC,MAAM;QAC7B,WAAW,EAAE;YACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;YAC9D,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;YACtD,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM;YAC1D,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM;SACrD;KACF,CAAC;IAEF,yCAAyC;IACzC,MAAM,YAAY,GAAG,MAAM,QAAQ,CACjC;;;;;;;;;;;aAWS,EACT,CAAC,KAAK,CAAC,EACP,KAAK,CACN,CAAC;IAEF,OAAO;QACL,OAAO,EAAE;YACP,GAAG,OAAO;YACV,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,gBAAgB,IAAI,CAAC;YACxD,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC;SAC3C;QACD,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"qc-violations.js","sourceRoot":"","sources":["../../../src/tools/analytics/qc-violations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,MAAyB;IAEzB,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAExC,IAAI,WAAW,GAAG,qBAAqB,CAAC;IACxC,MAAM,WAAW,GAAc,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,WAAW,IAAI,sBAAsB,UAAU,EAAE,CAAC;QAClD,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,WAAW,IAAI,2BAA2B,UAAU,EAAE,CAAC;QACvD,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,OAAO,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,WAAW,IAAI,8BAA8B,QAAQ,GAAG,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;OAKG,WAAW;;eAEH,EACX,WAAW,EACX,KAAK,CACN,CAAC;IAEF,MAAM,OAAO,GAAG;QACd,gBAAgB,EAAE,IAAI,CAAC,MAAM;QAC7B,WAAW,EAAE;YACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;YAC9D,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;YACtD,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM;YAC1D,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM;SACrD;KACF,CAAC;IAEF,yCAAyC;IACzC,MAAM,YAAY,GAAG,MAAM,QAAQ,CACjC;;;;;;;;;;;aAWS,EACT,CAAC,KAAK,CAAC,EACP,KAAK,CACN,CAAC;IAEF,OAAO;QACL,OAAO,EAAE;YACP,GAAG,OAAO;YACV,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,gBAAgB,IAAI,CAAC;YACxD,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC;SAC3C;QACD,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sla-breach-tickets.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breach-tickets.ts"],"names":[],"mappings":"AAEA,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,GAAG,KAAK,CAAC;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,sBAAsB;;;;;;;;;;GA8E/B"}
1
+ {"version":3,"file":"sla-breach-tickets.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breach-tickets.ts"],"names":[],"mappings":"AAGA,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,GAAG,KAAK,CAAC;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,sBAAsB;;;;;;;;;;GAiF/B"}
@@ -1,9 +1,12 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildTicketFilter } from "../../db/filters.js";
2
3
  export async function getSlaBreachTickets(mspId, params) {
3
4
  const fromDate = params.fromDate || new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10);
4
5
  const toDate = params.toDate || new Date().toISOString().slice(0, 10);
5
6
  const limit = params.limit || 25;
6
7
  const breachType = params.breachType || "any";
8
+ const mspFilters = await getFilters(mspId);
9
+ const ticketFilter = buildTicketFilter(mspFilters);
7
10
  const filters = [];
8
11
  const queryParams = [mspId, fromDate, toDate];
9
12
  let paramIndex = 4;
@@ -54,7 +57,7 @@ export async function getSlaBreachTickets(mspId, params) {
54
57
  AND tl.responded_date >= $2::date AND tl.responded_date <= $3::date
55
58
  AND tl.owner_name IS NOT NULL AND tl.owner_name != ''
56
59
  ${breachFilter}
57
- ${filters.join(" ")}
60
+ ${filters.join(" ")}${ticketFilter}
58
61
  ORDER BY GREATEST(
59
62
  COALESCE(tl.response_minutes - sla.triage_minutes, 0),
60
63
  COALESCE(tl.resolution_minutes - sla.resolve_minutes, 0)
@@ -1 +1 @@
1
- {"version":3,"file":"sla-breach-tickets.js","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breach-tickets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAWtD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,MAA8B;IAE9B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC;IAE9C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzD,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,wCAAwC,UAAU,GAAG,CAAC,CAAC;QACpE,WAAW,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC;QAC/C,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,0CAA0C,UAAU,GAAG,CAAC,CAAC;QACtE,WAAW,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;QAC3C,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,YAAY,GAAG,8CAA8C,CAAC;IAChE,CAAC;SAAM,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;QACvC,YAAY,GAAG,iDAAiD,CAAC;IACnE,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,+FAA+F,CAAC;IACjH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;SA0BK,YAAY;SACZ,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;;;;;aAKb,KAAK,EAAE,EAChB,WAAW,EACX,KAAK,CACN,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE;QACtC,WAAW,EAAE,UAAU;QACvB,iBAAiB,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI;QAChD,aAAa,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACxC,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,OAAO;KACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sla-breach-tickets.js","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breach-tickets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAWpE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,MAA8B;IAE9B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC;IAE9C,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzD,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,wCAAwC,UAAU,GAAG,CAAC,CAAC;QACpE,WAAW,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC;QAC/C,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,0CAA0C,UAAU,GAAG,CAAC,CAAC;QACtE,WAAW,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;QAC3C,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,YAAY,GAAG,8CAA8C,CAAC;IAChE,CAAC;SAAM,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;QACvC,YAAY,GAAG,iDAAiD,CAAC;IACnE,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,+FAA+F,CAAC;IACjH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;SA0BK,YAAY;SACZ,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY;;;;;aAK5B,KAAK,EAAE,EAChB,WAAW,EACX,KAAK,CACN,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE;QACtC,WAAW,EAAE,UAAU;QACvB,iBAAiB,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI;QAChD,aAAa,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACxC,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sla-breaches.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breaches.ts"],"names":[],"mappings":"AAEA,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,eAAe;;;;;;;;;GA2ExB"}
1
+ {"version":3,"file":"sla-breaches.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breaches.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,eAAe;;;;;;;;;GA8ExB"}
@@ -1,7 +1,10 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildTicketFilter } from "../../db/filters.js";
2
3
  export async function getSlaBreachesByTechnician(mspId, params) {
3
4
  const fromDate = params.fromDate || new Date(Date.now() - 90 * 86400000).toISOString().slice(0, 10);
4
5
  const toDate = params.toDate || new Date().toISOString().slice(0, 10);
6
+ const mspFilters = await getFilters(mspId);
7
+ const ticketFilter = buildTicketFilter(mspFilters);
5
8
  const clientFilter = params.clientName
6
9
  ? "AND LOWER(tl.company_name) LIKE LOWER($4)"
7
10
  : "";
@@ -24,7 +27,7 @@ export async function getSlaBreachesByTechnician(mspId, params) {
24
27
  WHERE tl.msp_id = $1
25
28
  AND tl.responded_date >= $2::date AND tl.responded_date <= $3::date
26
29
  AND tl.owner_name IS NOT NULL AND tl.owner_name != ''
27
- ${clientFilter}
30
+ ${clientFilter}${ticketFilter}
28
31
  GROUP BY tl.owner_name
29
32
  ORDER BY any_breach DESC
30
33
  LIMIT 20`, [mspId, fromDate, toDate, ...clientParam], mspId),
@@ -47,7 +50,7 @@ export async function getSlaBreachesByTechnician(mspId, params) {
47
50
  WHERE tl.msp_id = $1
48
51
  AND tl.responded_date >= $2::date AND tl.responded_date <= $3::date
49
52
  AND tl.owner_name IS NOT NULL AND tl.owner_name != ''
50
- ${clientFilter}`, [mspId, fromDate, toDate, ...clientParam], mspId),
53
+ ${clientFilter}${ticketFilter}`, [mspId, fromDate, toDate, ...clientParam], mspId),
51
54
  ]);
52
55
  return {
53
56
  period: { from: fromDate, to: toDate },
@@ -1 +1 @@
1
- {"version":3,"file":"sla-breaches.js","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breaches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAQtD,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAAa,EACb,MAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtE,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU;QACpC,CAAC,CAAC,2CAA2C;QAC7C,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,MAAM,CAAC,YAAY,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5D,yBAAyB;QACzB,QAAQ,CACN;;;;;;;;;;;;;;;;WAgBK,YAAY;;;gBAGP,EACV,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EACzC,KAAK,CACN;QAED,4BAA4B;QAC5B,QAAQ,CACN;;;yBAGmB,EACnB,CAAC,KAAK,CAAC,EACP,KAAK,CACN;QAED,sBAAsB;QACtB,QAAQ,CACN;;;;;;;;;;;;;WAaK,YAAY,EAAE,EACnB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EACzC,KAAK,CACN;KACF,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE;QACtC,aAAa,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACxC,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE;QACvF,aAAa,EAAE,YAAY;KAC5B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sla-breaches.js","sourceRoot":"","sources":["../../../src/tools/analytics/sla-breaches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAQpE,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAAa,EACb,MAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtE,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU;QACpC,CAAC,CAAC,2CAA2C;QAC7C,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,MAAM,CAAC,YAAY,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5D,yBAAyB;QACzB,QAAQ,CACN;;;;;;;;;;;;;;;;WAgBK,YAAY,GAAG,YAAY;;;gBAGtB,EACV,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EACzC,KAAK,CACN;QAED,4BAA4B;QAC5B,QAAQ,CACN;;;yBAGmB,EACnB,CAAC,KAAK,CAAC,EACP,KAAK,CACN;QAED,sBAAsB;QACtB,QAAQ,CACN;;;;;;;;;;;;;WAaK,YAAY,GAAG,YAAY,EAAE,EAClC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EACzC,KAAK,CACN;KACF,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE;QACtC,aAAa,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACxC,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE;QACvF,aAAa,EAAE,YAAY;KAC5B,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"technician-perf.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/technician-perf.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,oBAAoB;;GAiB7B"}
1
+ {"version":3,"file":"technician-perf.d.ts","sourceRoot":"","sources":["../../../src/tools/analytics/technician-perf.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,oBAAoB;;GA0B7B"}
@@ -1,5 +1,12 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters } from "../../db/filters.js";
2
3
  export async function getTechnicianPerformance(mspId, params) {
4
+ const filters = await getFilters(mspId);
5
+ let techFilter = "";
6
+ if (filters.enabledTechnicians.length > 0) {
7
+ const techList = filters.enabledTechnicians.map(n => `'${n.replace(/'/g, "''")}'`).join(", ");
8
+ techFilter = `AND technician_name IN (${techList})`;
9
+ }
3
10
  const rows = await queryRLS(`SELECT technician_name, calculation_date::text,
4
11
  unique_tickets, total_notes, avg_note_length,
5
12
  quality_points, completeness_points, accuracy_points,
@@ -8,6 +15,7 @@ export async function getTechnicianPerformance(mspId, params) {
8
15
  WHERE msp_id = $1
9
16
  AND calculation_date >= $2::date
10
17
  AND calculation_date <= $3::date
18
+ ${techFilter}
11
19
  ORDER BY quality_points DESC`, [mspId, params.fromDate, params.toDate], mspId);
12
20
  return { technicians: rows };
13
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"technician-perf.js","sourceRoot":"","sources":["../../../src/tools/analytics/technician-perf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAQtD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAa,EACb,MAA4B;IAE5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;;;;kCAQ8B,EAC9B,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EACvC,KAAK,CACN,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"technician-perf.js","sourceRoot":"","sources":["../../../src/tools/analytics/technician-perf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAa,EACb,MAA4B;IAE5B,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAExC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,OAAO,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,UAAU,GAAG,2BAA2B,QAAQ,GAAG,CAAC;IACtD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB;;;;;;;;SAQK,UAAU;kCACe,EAC9B,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EACvC,KAAK,CACN,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"client-profitability.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/client-profitability.ts"],"names":[],"mappings":"AAEA,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;;;;;;;;;;;;;;;;;GAsFtF"}
1
+ {"version":3,"file":"client-profitability.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/client-profitability.ts"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;;;;;;;;;;;;;;;;;GAuFtF"}
@@ -1,33 +1,35 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildTimeEntriesFilter } from "../../db/filters.js";
2
3
  export async function cfoClientProfitability(mspId, params) {
3
4
  const fromDate = params.fromDate ?? new Date(Date.now() - 90 * 86400000).toISOString().split("T")[0];
4
5
  const toDate = params.toDate ?? new Date().toISOString().split("T")[0];
6
+ const filters = await getFilters(mspId);
5
7
  let clientFilter = "";
6
8
  const queryParams = [mspId, fromDate, toDate];
7
9
  if (params.clientName) {
8
- clientFilter = ` AND LOWER(company) LIKE LOWER($4)`;
10
+ clientFilter = ` AND LOWER(te.company) LIKE LOWER($4)`;
9
11
  queryParams.push(`%${params.clientName}%`);
10
12
  }
11
13
  // Cost-to-serve per client
12
- const clients = await queryRLS(`SELECT company as client_name,
13
- COUNT(DISTINCT ticket_id)::int as tickets,
14
- SUM(actual_hours)::numeric as total_hours,
15
- SUM(billable_hours)::numeric as billable_hours,
16
- SUM(total_cost)::numeric as total_cost,
17
- CASE WHEN COUNT(DISTINCT ticket_id) > 0
18
- THEN ROUND(SUM(total_cost) / COUNT(DISTINCT ticket_id), 2)
14
+ const clients = await queryRLS(`SELECT te.company as client_name,
15
+ COUNT(DISTINCT te.ticket_id)::int as tickets,
16
+ SUM(te.actual_hours)::numeric as total_hours,
17
+ SUM(te.billable_hours)::numeric as billable_hours,
18
+ SUM(te.total_cost)::numeric as total_cost,
19
+ CASE WHEN COUNT(DISTINCT te.ticket_id) > 0
20
+ THEN ROUND(SUM(te.total_cost) / COUNT(DISTINCT te.ticket_id), 2)
19
21
  ELSE 0
20
22
  END as cost_per_ticket,
21
- CASE WHEN SUM(actual_hours) > 0
22
- THEN ROUND((SUM(billable_hours) / SUM(actual_hours)) * 100, 1)
23
+ CASE WHEN SUM(te.actual_hours) > 0
24
+ THEN ROUND((SUM(te.billable_hours) / SUM(te.actual_hours)) * 100, 1)
23
25
  ELSE 0
24
26
  END as billable_pct
25
- FROM time_entries
26
- WHERE msp_id = $1
27
- AND time_start >= $2::date AND time_start <= $3::date
28
- ${clientFilter}
29
- GROUP BY company
30
- HAVING SUM(actual_hours) > 0
27
+ FROM time_entries te
28
+ WHERE te.msp_id = $1
29
+ AND te.time_start >= $2::date AND te.time_start <= $3::date
30
+ ${clientFilter}${buildTimeEntriesFilter(filters)}
31
+ GROUP BY te.company
32
+ HAVING SUM(te.actual_hours) > 0
31
33
  ORDER BY total_cost DESC
32
34
  LIMIT 25`, queryParams, mspId);
33
35
  // Agreement coverage
@@ -1 +1 @@
1
- {"version":3,"file":"client-profitability.js","sourceRoot":"","sources":["../../../src/tools/cfo/client-profitability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client-profitability.js","sourceRoot":"","sources":["../../../src/tools/cfo/client-profitability.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"financial-overview.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/financial-overview.ts"],"names":[],"mappings":"AAEA,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgF/E"}
1
+ {"version":3,"file":"financial-overview.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/financial-overview.ts"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiF/E"}
@@ -1,7 +1,9 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildTimeEntriesFilter, buildCompanyExclusionFilter } from "../../db/filters.js";
2
3
  export async function cfoFinancialOverview(mspId, params) {
3
4
  const fromDate = params.fromDate ?? new Date(Date.now() - 90 * 86400000).toISOString().split("T")[0];
4
5
  const toDate = params.toDate ?? new Date().toISOString().split("T")[0];
6
+ const filters = await getFilters(mspId);
5
7
  const laborCost = await queryRLS(`SELECT SUM(actual_hours)::numeric as total_hours,
6
8
  SUM(billable_hours)::numeric as billable_hours,
7
9
  SUM(non_billable_hours)::numeric as non_billable_hours,
@@ -13,18 +15,18 @@ export async function cfoFinancialOverview(mspId, params) {
13
15
  THEN ROUND((SUM(billable_hours) / SUM(actual_hours)) * 100, 1)
14
16
  ELSE 0
15
17
  END as utilization_pct
16
- FROM time_entries
18
+ FROM time_entries te
17
19
  WHERE msp_id = $1
18
- AND time_start >= $2::date AND time_start <= $3::date`, [mspId, fromDate, toDate], mspId);
20
+ AND time_start >= $2::date AND time_start <= $3::date${buildTimeEntriesFilter(filters)}`, [mspId, fromDate, toDate], mspId);
19
21
  let outOfScope = { tickets: 0, dollars: 0 };
20
22
  try {
21
23
  const oos = await queryRLS(`SELECT COUNT(DISTINCT ticket_id)::int as tickets,
22
24
  COALESCE(SUM(billable_amount), 0)::numeric as dollars
23
- FROM agents.billing_classifications
25
+ FROM agents.billing_classifications bc
24
26
  WHERE msp_id::uuid = $1::uuid
25
27
  AND classification = 'out_of_scope'
26
28
  AND billable_amount > 0
27
- AND resolved_date >= $2::date AND resolved_date <= $3::date`, [mspId, fromDate, toDate], mspId);
29
+ AND resolved_date >= $2::date AND resolved_date <= $3::date${buildCompanyExclusionFilter(filters)}`, [mspId, fromDate, toDate], mspId);
28
30
  outOfScope = {
29
31
  tickets: Number(oos[0]?.tickets) || 0,
30
32
  dollars: Number(oos[0]?.dollars) || 0,
@@ -1 +1 @@
1
- {"version":3,"file":"financial-overview.js","sourceRoot":"","sources":["../../../src/tools/cfo/financial-overview.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"financial-overview.js","sourceRoot":"","sources":["../../../src/tools/cfo/financial-overview.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"labor-cost-trend.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/labor-cost-trend.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;;;;;;;;GA+EzE"}
1
+ {"version":3,"file":"labor-cost-trend.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/labor-cost-trend.ts"],"names":[],"mappings":"AAGA,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;;;;;;;;GAgFzE"}
@@ -1,45 +1,47 @@
1
1
  import { queryRLS } from "../../db/gateway-client.js";
2
+ import { getFilters, buildTimeEntriesFilter } from "../../db/filters.js";
2
3
  export async function cfoLaborCostTrend(mspId, params) {
3
4
  const months = params.months ?? 6;
4
5
  const fromDate = new Date(Date.now() - months * 30 * 86400000).toISOString().split("T")[0];
6
+ const filters = await getFilters(mspId);
5
7
  // Monthly cost and hours trend
6
- const monthly = await queryRLS(`SELECT TO_CHAR(time_start, 'YYYY-MM') as month,
7
- SUM(actual_hours)::numeric as total_hours,
8
- SUM(billable_hours)::numeric as billable_hours,
9
- SUM(total_cost)::numeric as total_cost,
10
- COUNT(DISTINCT ticket_id)::int as tickets,
11
- COUNT(DISTINCT member_name)::int as active_techs,
12
- COUNT(DISTINCT company)::int as active_clients,
13
- CASE WHEN SUM(actual_hours) > 0
14
- THEN ROUND((SUM(billable_hours) / SUM(actual_hours)) * 100, 1)
8
+ const monthly = await queryRLS(`SELECT TO_CHAR(te.time_start, 'YYYY-MM') as month,
9
+ SUM(te.actual_hours)::numeric as total_hours,
10
+ SUM(te.billable_hours)::numeric as billable_hours,
11
+ SUM(te.total_cost)::numeric as total_cost,
12
+ COUNT(DISTINCT te.ticket_id)::int as tickets,
13
+ COUNT(DISTINCT te.member_name)::int as active_techs,
14
+ COUNT(DISTINCT te.company)::int as active_clients,
15
+ CASE WHEN SUM(te.actual_hours) > 0
16
+ THEN ROUND((SUM(te.billable_hours) / SUM(te.actual_hours)) * 100, 1)
15
17
  ELSE 0
16
18
  END as utilization_pct
17
- FROM time_entries
18
- WHERE msp_id = $1
19
- AND time_start >= $2::date
20
- GROUP BY TO_CHAR(time_start, 'YYYY-MM')
19
+ FROM time_entries te
20
+ WHERE te.msp_id = $1
21
+ AND te.time_start >= $2::date${buildTimeEntriesFilter(filters)}
22
+ GROUP BY TO_CHAR(te.time_start, 'YYYY-MM')
21
23
  ORDER BY month`, [mspId, fromDate], mspId);
22
24
  // Cost by department trend
23
- const byDepartment = await queryRLS(`SELECT department,
24
- SUM(actual_hours)::numeric as hours,
25
- SUM(total_cost)::numeric as cost,
26
- COUNT(DISTINCT ticket_id)::int as tickets
27
- FROM time_entries
28
- WHERE msp_id = $1
29
- AND time_start >= $2::date
30
- AND department IS NOT NULL
31
- GROUP BY department
25
+ const byDepartment = await queryRLS(`SELECT te.department,
26
+ SUM(te.actual_hours)::numeric as hours,
27
+ SUM(te.total_cost)::numeric as cost,
28
+ COUNT(DISTINCT te.ticket_id)::int as tickets
29
+ FROM time_entries te
30
+ WHERE te.msp_id = $1
31
+ AND te.time_start >= $2::date
32
+ AND te.department IS NOT NULL${buildTimeEntriesFilter(filters)}
33
+ GROUP BY te.department
32
34
  ORDER BY cost DESC`, [mspId, fromDate], mspId);
33
35
  // Cost by work type (top categories)
34
- const byWorkType = await queryRLS(`SELECT work_type,
35
- SUM(actual_hours)::numeric as hours,
36
- SUM(total_cost)::numeric as cost,
37
- COUNT(DISTINCT ticket_id)::int as tickets
38
- FROM time_entries
39
- WHERE msp_id = $1
40
- AND time_start >= $2::date
41
- AND work_type IS NOT NULL
42
- GROUP BY work_type
36
+ const byWorkType = await queryRLS(`SELECT te.work_type,
37
+ SUM(te.actual_hours)::numeric as hours,
38
+ SUM(te.total_cost)::numeric as cost,
39
+ COUNT(DISTINCT te.ticket_id)::int as tickets
40
+ FROM time_entries te
41
+ WHERE te.msp_id = $1
42
+ AND te.time_start >= $2::date
43
+ AND te.work_type IS NOT NULL${buildTimeEntriesFilter(filters)}
44
+ GROUP BY te.work_type
43
45
  ORDER BY cost DESC
44
46
  LIMIT 10`, [mspId, fromDate], mspId);
45
47
  // Calculate month-over-month changes
@@ -1 +1 @@
1
- {"version":3,"file":"labor-cost-trend.js","sourceRoot":"","sources":["../../../src/tools/cfo/labor-cost-trend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAMtD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAa,EAAE,MAAmB;IACxE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3F,+BAA+B;IAC/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC5B;;;;;;;;;;;;;;;oBAegB,EAChB,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,2BAA2B;IAC3B,MAAM,YAAY,GAAG,MAAM,QAAQ,CACjC;;;;;;;;;wBASoB,EACpB,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,qCAAqC;IACrC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAC/B;;;;;;;;;;cAUU,EACV,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,qCAAqC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,OAAO;YACL,GAAG,CAAC;YACJ,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG;YACxC,cAAc,EAAE,IAAI,IAAI,QAAQ,GAAG,CAAC;gBAClC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE;gBACxD,CAAC,CAAC,IAAI;SACT,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,eAAe,EAAE,MAAM;QACvB,aAAa,EAAE,KAAK;QACpB,aAAa,EAAE,YAAY;QAC3B,YAAY,EAAE,UAAU;KACzB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"labor-cost-trend.js","sourceRoot":"","sources":["../../../src/tools/cfo/labor-cost-trend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAMzE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAa,EAAE,MAAmB;IACxE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAExC,+BAA+B;IAC/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC5B;;;;;;;;;;;;;sCAakC,sBAAsB,CAAC,OAAO,CAAC;;oBAEjD,EAChB,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,2BAA2B;IAC3B,MAAM,YAAY,GAAG,MAAM,QAAQ,CACjC;;;;;;;sCAOkC,sBAAsB,CAAC,OAAO,CAAC;;wBAE7C,EACpB,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,qCAAqC;IACrC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAC/B;;;;;;;qCAOiC,sBAAsB,CAAC,OAAO,CAAC;;;cAGtD,EACV,CAAC,KAAK,EAAE,QAAQ,CAAC,EACjB,KAAK,CACN,CAAC;IAEF,qCAAqC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,OAAO;YACL,GAAG,CAAC;YACJ,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG;YACxC,cAAc,EAAE,IAAI,IAAI,QAAQ,GAAG,CAAC;gBAClC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE;gBACxD,CAAC,CAAC,IAAI;SACT,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,eAAe,EAAE,MAAM;QACvB,aAAa,EAAE,KAAK;QACpB,aAAa,EAAE,YAAY;QAC3B,YAAY,EAAE,UAAU;KACzB,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"revenue-leakage.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/revenue-leakage.ts"],"names":[],"mappings":"AAEA,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;;;;;;;;;;;;;;;GA+E1E"}
1
+ {"version":3,"file":"revenue-leakage.d.ts","sourceRoot":"","sources":["../../../src/tools/cfo/revenue-leakage.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;;;;;;;;;;;;;;;GAgF1E"}