node-autotask 0.28.0 → 0.30.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 (41) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/MCP_TOOL_CATALOG.json +46 -8
  3. package/MCP_TOOL_MANIFEST.md +13 -11
  4. package/N8N_MCP_MANIFEST.json +34 -5
  5. package/N8N_MCP_MANIFEST.md +8 -7
  6. package/README.md +1 -2
  7. package/capabilities.json +1642 -1319
  8. package/dist/capabilities.cjs +2 -2
  9. package/dist/capabilities.js +1 -1
  10. package/dist/{chunk-O2D7KDDD.js → chunk-2ETR5GSO.js} +2 -2
  11. package/dist/{chunk-ZEGMVEF3.cjs → chunk-EDVSWL32.cjs} +1641 -1318
  12. package/dist/{chunk-GUY2KK7U.cjs → chunk-L4TDVUJM.cjs} +143 -143
  13. package/dist/{chunk-D5IVS6GY.cjs → chunk-LLIB4JGK.cjs} +140 -6
  14. package/dist/{chunk-SYX6EWMT.js → chunk-P4PC32RJ.js} +192 -70
  15. package/dist/{chunk-IRVZGW5K.js → chunk-QQPCJT2L.js} +136 -2
  16. package/dist/{chunk-WYKGD5UD.cjs → chunk-RS7QMPRO.cjs} +622 -500
  17. package/dist/{chunk-6EQSXYRW.js → chunk-TZC4ZAA6.js} +1641 -1318
  18. package/dist/{index-CORcZUNm.d.cts → index-B9VNv-EP.d.cts} +52 -2
  19. package/dist/{index-DQ9NgraW.d.cts → index-CJ36PwRG.d.cts} +110 -82
  20. package/dist/{index-DQ9NgraW.d.ts → index-CJ36PwRG.d.ts} +110 -82
  21. package/dist/{index-DXuV04zM.d.ts → index-DkpaFqyu.d.ts} +52 -2
  22. package/dist/index.cjs +22 -22
  23. package/dist/index.d.cts +4 -4
  24. package/dist/index.d.ts +4 -4
  25. package/dist/index.js +4 -4
  26. package/dist/operations/index.cjs +112 -112
  27. package/dist/operations/index.d.cts +4 -4
  28. package/dist/operations/index.d.ts +4 -4
  29. package/dist/operations/index.js +2 -2
  30. package/dist/pool.cjs +9 -9
  31. package/dist/pool.d.cts +2 -2
  32. package/dist/pool.d.ts +2 -2
  33. package/dist/pool.js +4 -4
  34. package/dist/resources/index.cjs +4 -4
  35. package/dist/resources/index.d.cts +2 -2
  36. package/dist/resources/index.d.ts +2 -2
  37. package/dist/resources/index.js +3 -3
  38. package/dist/types/index.d.cts +1 -1
  39. package/dist/types/index.d.ts +1 -1
  40. package/docs/API.md +1 -2
  41. package/package.json +1 -1
@@ -34,14 +34,16 @@ import {
34
34
  parseVendorErrors,
35
35
  resolveCompositeNames,
36
36
  resolveFieldsPreset,
37
+ resolveLineOfBusinessChain,
37
38
  truncateNoteChars,
38
39
  unwrapItem,
39
40
  unwrapItems,
40
- unwrapOperation
41
- } from "./chunk-IRVZGW5K.js";
41
+ unwrapOperation,
42
+ vendorId
43
+ } from "./chunk-QQPCJT2L.js";
42
44
  import {
43
45
  CAPABILITIES
44
- } from "./chunk-6EQSXYRW.js";
46
+ } from "./chunk-TZC4ZAA6.js";
45
47
 
46
48
  // src/resources/appointments.ts
47
49
  var AppointmentsResource = class extends BaseResource {
@@ -4830,6 +4832,42 @@ var ContractServiceUnitsResource = class extends BaseResource {
4830
4832
  }
4831
4833
  };
4832
4834
 
4835
+ // src/resources/special/contracts/lineOfBusiness.ts
4836
+ var OP3 = "contracts.lineOfBusiness";
4837
+ function positiveId(value, name) {
4838
+ const n = typeof value === "string" && /^[0-9]+$/.test(value.trim()) ? Number(value) : value;
4839
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
4840
+ throw new AutotaskConfigError(OP3 + ": " + name + " must be a positive integer id (got " + String(value) + ")", { operation: OP3 });
4841
+ }
4842
+ return n;
4843
+ }
4844
+ async function lineOfBusiness(resource, contractIdIn, http, associationIdIn) {
4845
+ const contractId = positiveId(contractIdIn, "contractId");
4846
+ let associationId;
4847
+ let associationSource;
4848
+ if (associationIdIn !== void 0) {
4849
+ associationId = positiveId(associationIdIn, "associationId");
4850
+ associationSource = "argument";
4851
+ } else {
4852
+ associationSource = "contract";
4853
+ const contract = await resource.get(contractId);
4854
+ associationId = vendorId(contract.organizationalLevelAssociationID);
4855
+ }
4856
+ if (associationId === void 0) {
4857
+ return {
4858
+ contractId,
4859
+ associationSource,
4860
+ association: null,
4861
+ organizationalLevel2: null,
4862
+ organizationalLevel1: null,
4863
+ markers: { association: "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
4864
+ };
4865
+ }
4866
+ const chain = await resolveLineOfBusinessChain(http, associationId, { budget: 3, operation: OP3, onError: "propagate" });
4867
+ const { association, organizationalLevel2, organizationalLevel1, markers } = chain.lineOfBusiness;
4868
+ return { contractId, associationSource, association, organizationalLevel2, organizationalLevel1, markers };
4869
+ }
4870
+
4833
4871
  // src/resources/special/contracts/profitability.constants.ts
4834
4872
  var PROFITABILITY_OPT_KEYS = [
4835
4873
  "dateFrom",
@@ -5299,7 +5337,7 @@ function appendWiresNote(wires, note) {
5299
5337
  wires.note = (wires.note ? wires.note + " | " : "") + note;
5300
5338
  }
5301
5339
  function makeFetchCtx(rawHttp, v, contractId, budget) {
5302
- const OP9 = PROFITABILITY_OP;
5340
+ const OP11 = PROFITABILITY_OP;
5303
5341
  const id = parseIdentifier(contractId, "contractId");
5304
5342
  const idNum = typeof id === "number" ? id : Number(id);
5305
5343
  const wires = { issued: 0, requests: 0, budget };
@@ -5311,19 +5349,19 @@ function makeFetchCtx(rawHttp, v, contractId, budget) {
5311
5349
  } };
5312
5350
  const readPart = (path, conditions, fields) => {
5313
5351
  wires.issued += 1;
5314
- return degradable(() => boundedPart(http, path, conditions, v.limit, OP9, fields));
5352
+ return degradable(() => boundedPart(http, path, conditions, v.limit, OP11, fields));
5315
5353
  };
5316
5354
  const truncatedMonths = {};
5317
5355
  const windowedPart = async (name, key, path, cond, fields, dateField) => {
5318
5356
  const first = await readPart(path, cond, fields);
5319
5357
  if (first.absent !== void 0 || !first.hasMore) return { part: stampPart(key, first, cond), rows: first.items };
5320
- const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP9, fields, dateField, v.dateFrom, v.dateTo, wires));
5358
+ const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP11, fields, dateField, v.dateFrom, v.dateTo, wires));
5321
5359
  if (!("truncatedMonths" in wp)) return { part: stampPart(key, wp, cond), rows: [] };
5322
5360
  const { truncatedMonths: tm, ...part } = wp;
5323
5361
  truncatedMonths[name] = tm;
5324
5362
  return { part, rows: wp.items };
5325
5363
  };
5326
- return { v, OP: OP9, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5364
+ return { v, OP: OP11, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5327
5365
  }
5328
5366
 
5329
5367
  // src/resources/special/contracts/profitability.stages.ts
@@ -6329,6 +6367,10 @@ var ContractsResource = class extends BaseResource {
6329
6367
  profitability(id, opts) {
6330
6368
  return profitability(this, id, opts, this.http);
6331
6369
  }
6370
+ /** Node-parity special op (hand-written in src/resources/special/contracts.ts). */
6371
+ lineOfBusiness(contractId, associationId) {
6372
+ return lineOfBusiness(this, contractId, this.http, associationId);
6373
+ }
6332
6374
  };
6333
6375
 
6334
6376
  // src/resources/contract_ticket_purchases.ts
@@ -10691,22 +10733,22 @@ function uiLinkFor(http, entity, id) {
10691
10733
  }
10692
10734
 
10693
10735
  // src/resources/special/ticket_charges.ts
10694
- var OP3 = "ticket_charges.moveToTicket";
10736
+ var OP4 = "ticket_charges.moveToTicket";
10695
10737
  var COPY = ["name", "description", "chargeType", "billingCodeID", "productID", "unitQuantity", "unitCost", "unitPrice", "datePurchased", "isBillableToCompany", "notes", "purchaseOrderNumber", "internalPurchaseOrderNumber"];
10696
10738
  var CONTRACT_SCOPED = ["contractServiceID", "contractServiceBundleID"];
10697
10739
  async function readCharge(http, id, retries) {
10698
- const rows = await queryRows(http, "TicketCharges", [{ field: "id", op: "eq", value: id }], OP3, retries);
10740
+ const rows = await queryRows(http, "TicketCharges", [{ field: "id", op: "eq", value: id }], OP4, retries);
10699
10741
  return rows[0] ?? null;
10700
10742
  }
10701
10743
  async function checkSource({ http, source, sourceId, sourceTicketId, toTicketId }) {
10702
10744
  if (source.isBilled === true) {
10703
- throw refuse("MOVE_BLOCKED_BILLED", `${OP3}: ticket charge ${sourceId} is billed \u2014 move it in the Autotask UI`, { sourceId, toTicketId }, OP3, {
10745
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP4}: ticket charge ${sourceId} is billed \u2014 move it in the Autotask UI`, { sourceId, toTicketId }, OP4, {
10704
10746
  manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "tickets", sourceTicketId), reason: "billed charges are not moved by the API (the ticket link opens its charges)" }
10705
10747
  });
10706
10748
  }
10707
10749
  }
10708
10750
  var SPEC = {
10709
- operation: OP3,
10751
+ operation: OP4,
10710
10752
  entity: "ticket_charge",
10711
10753
  readSource: readCharge,
10712
10754
  sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
@@ -12061,6 +12103,12 @@ async function addCompositeNames(http, ctx, entriesPart, result, opts) {
12061
12103
  builder.addFk("contract", t["contractID"]);
12062
12104
  builder.addFk("billingCode", t["billingCodeID"]);
12063
12105
  builder.addFk("role", t["assignedResourceRoleID"]);
12106
+ const ticketLobPointer = vendorId(t["organizationalLevelAssociationID"]);
12107
+ if (ticketLobPointer !== void 0) {
12108
+ builder.addLineOfBusiness(ticketLobPointer, { source: "ticket" });
12109
+ } else {
12110
+ builder.addLineOfBusinessFromContract(t["contractID"]);
12111
+ }
12064
12112
  for (const e of entriesPart.items) {
12065
12113
  builder.addResource(e["resourceID"]);
12066
12114
  builder.addFk("role", e["roleID"]);
@@ -12084,6 +12132,12 @@ async function addCompositeNames(http, ctx, entriesPart, result, opts) {
12084
12132
  return row;
12085
12133
  });
12086
12134
  result.names = names.section;
12135
+ result.lineOfBusiness = names.lineOfBusiness ?? {
12136
+ association: null,
12137
+ organizationalLevel2: null,
12138
+ organizationalLevel1: null,
12139
+ markers: { association: "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
12140
+ };
12087
12141
  result.truncated = result.truncated || names.section.truncated;
12088
12142
  }
12089
12143
  async function fullContext(resource, id, opts = {}, http) {
@@ -12148,16 +12202,16 @@ async function fullContext(resource, id, opts = {}, http) {
12148
12202
  }
12149
12203
 
12150
12204
  // src/resources/special/tickets/forResource.ts
12151
- var OP4 = "tickets.forResource";
12205
+ var OP5 = "tickets.forResource";
12152
12206
  var TICKET_CHUNK = 100;
12153
12207
  var LIMIT_DEFAULT = 100;
12154
12208
  var LIMIT_MAX = 500;
12155
12209
  var LINK_READ_CAP = 500;
12156
12210
  var TICKET_FIELDS2 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
12157
- function positiveId(value, name) {
12211
+ function positiveId2(value, name) {
12158
12212
  const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
12159
12213
  if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
12160
- throw new AutotaskConfigError(`${OP4}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP4 });
12214
+ throw new AutotaskConfigError(`${OP5}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP5 });
12161
12215
  }
12162
12216
  return n;
12163
12217
  }
@@ -12167,28 +12221,28 @@ async function ticketPage(http, filter, maxRecords, fields) {
12167
12221
  idempotent: true,
12168
12222
  path: "/V1.0/Tickets/query",
12169
12223
  body: { MaxRecords: maxRecords, filter, IncludeFields: fields },
12170
- operation: OP4
12224
+ operation: OP5
12171
12225
  });
12172
- const page = unwrapItems(res, OP4);
12226
+ const page = unwrapItems(res, OP5);
12173
12227
  return { rows: page.items, more: page.pageDetails.nextPageUrl !== null };
12174
12228
  }
12175
12229
  async function forResource(_resource, resourceIDIn, http, opts = {}) {
12176
- if (opts === null || typeof opts !== "object") throw new AutotaskConfigError(`${OP4}: opts must be an object`, { operation: OP4 });
12177
- const resourceID = positiveId(resourceIDIn, "resourceID");
12230
+ if (opts === null || typeof opts !== "object") throw new AutotaskConfigError(`${OP5}: opts must be an object`, { operation: OP5 });
12231
+ const resourceID = positiveId2(resourceIDIn, "resourceID");
12178
12232
  const scope = opts.scope ?? "both";
12179
12233
  if (scope !== "primary" && scope !== "secondary" && scope !== "both") {
12180
- throw new AutotaskConfigError(`${OP4}: scope must be 'primary', 'secondary' or 'both' (got ${JSON.stringify(opts.scope)})`, { operation: OP4 });
12234
+ throw new AutotaskConfigError(`${OP5}: scope must be 'primary', 'secondary' or 'both' (got ${JSON.stringify(opts.scope)})`, { operation: OP5 });
12181
12235
  }
12182
12236
  if (opts.openOnly !== void 0 && typeof opts.openOnly !== "boolean") {
12183
- throw new AutotaskConfigError(`${OP4}: openOnly must be a boolean`, { operation: OP4 });
12237
+ throw new AutotaskConfigError(`${OP5}: openOnly must be a boolean`, { operation: OP5 });
12184
12238
  }
12185
12239
  const limit = opts.limit === void 0 ? LIMIT_DEFAULT : opts.limit;
12186
12240
  if (!Number.isInteger(limit) || limit < 1 || limit > LIMIT_MAX) {
12187
- throw new AutotaskConfigError(`${OP4}: limit must be an integer 1-${LIMIT_MAX} (got ${String(opts.limit)})`, { operation: OP4 });
12241
+ throw new AutotaskConfigError(`${OP5}: limit must be an integer 1-${LIMIT_MAX} (got ${String(opts.limit)})`, { operation: OP5 });
12188
12242
  }
12189
12243
  const includeFields = opts.includeFields ?? [];
12190
12244
  if (!Array.isArray(includeFields) || includeFields.some((f) => typeof f !== "string" || f.length === 0)) {
12191
- throw new AutotaskConfigError(`${OP4}: includeFields must be a list of field names`, { operation: OP4 });
12245
+ throw new AutotaskConfigError(`${OP5}: includeFields must be a list of field names`, { operation: OP5 });
12192
12246
  }
12193
12247
  const openOnly = opts.openOnly === true;
12194
12248
  const fields = [.../* @__PURE__ */ new Set([...TICKET_FIELDS2, ...includeFields])];
@@ -12203,9 +12257,9 @@ async function forResource(_resource, resourceIDIn, http, opts = {}) {
12203
12257
  idempotent: true,
12204
12258
  path: "/V1.0/TicketSecondaryResources/query",
12205
12259
  body: { MaxRecords: LINK_READ_CAP, filter: [{ field: "resourceID", op: "eq", value: resourceID }], IncludeFields: ["ticketID"] },
12206
- operation: OP4
12260
+ operation: OP5
12207
12261
  });
12208
- const page = unwrapItems(res, OP4);
12262
+ const page = unwrapItems(res, OP5);
12209
12263
  for (const row of page.items) if (typeof row.ticketID === "number" && !linkIds.includes(row.ticketID)) linkIds.push(row.ticketID);
12210
12264
  if (page.items.length >= LINK_READ_CAP || page.pageDetails.nextPageUrl !== null) capped = true;
12211
12265
  }
@@ -12257,6 +12311,70 @@ async function forResource(_resource, resourceIDIn, http, opts = {}) {
12257
12311
  };
12258
12312
  }
12259
12313
 
12314
+ // src/resources/special/tickets/lineOfBusiness.ts
12315
+ var OP6 = "tickets.lineOfBusiness";
12316
+ function positiveId3(value, name) {
12317
+ const n = typeof value === "string" && /^[0-9]+$/.test(value.trim()) ? Number(value) : value;
12318
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
12319
+ throw new AutotaskConfigError(OP6 + ": " + name + " must be a positive integer id (got " + String(value) + ")", { operation: OP6 });
12320
+ }
12321
+ return n;
12322
+ }
12323
+ async function lineOfBusiness2(resource, ticketIdIn, http, associationIdIn) {
12324
+ const ticketId = positiveId3(ticketIdIn, "ticketId");
12325
+ let associationId;
12326
+ let associationSource = null;
12327
+ let contractId = null;
12328
+ let contractUnread = false;
12329
+ if (associationIdIn !== void 0) {
12330
+ associationId = positiveId3(associationIdIn, "associationId");
12331
+ associationSource = "argument";
12332
+ } else {
12333
+ const ticket = await resource.get(ticketId);
12334
+ associationId = vendorId(ticket.organizationalLevelAssociationID);
12335
+ if (associationId !== void 0) {
12336
+ associationSource = "ticket";
12337
+ } else {
12338
+ const contractRef = vendorId(ticket.contractID);
12339
+ if (contractRef !== void 0) {
12340
+ const client = http.client;
12341
+ if (client === void 0) {
12342
+ throw new AutotaskConfigError(OP6 + ": the transport is not wired to an AutotaskClient (the composite must run through the client)", { operation: OP6 });
12343
+ }
12344
+ try {
12345
+ const contract = await client.contracts.get(contractRef);
12346
+ contractId = contractRef;
12347
+ associationId = vendorId(contract.organizationalLevelAssociationID);
12348
+ if (associationId !== void 0) associationSource = "contract";
12349
+ } catch (e) {
12350
+ if (e instanceof NotFoundError) {
12351
+ contractId = contractRef;
12352
+ } else if (isAutotaskError(e) && e.code !== "ABORTED") {
12353
+ contractId = contractRef;
12354
+ contractUnread = true;
12355
+ } else {
12356
+ throw e;
12357
+ }
12358
+ }
12359
+ }
12360
+ }
12361
+ }
12362
+ if (associationId === void 0) {
12363
+ return {
12364
+ ticketId,
12365
+ associationSource,
12366
+ contractId,
12367
+ association: null,
12368
+ organizationalLevel2: null,
12369
+ organizationalLevel1: null,
12370
+ markers: { association: contractUnread ? "unresolved" : "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
12371
+ };
12372
+ }
12373
+ const chain = await resolveLineOfBusinessChain(http, associationId, { budget: 3, operation: OP6, onError: "propagate" });
12374
+ const { association, organizationalLevel2, organizationalLevel1, markers } = chain.lineOfBusiness;
12375
+ return { ticketId, associationSource, contractId, association, organizationalLevel2, organizationalLevel1, markers };
12376
+ }
12377
+
12260
12378
  // src/resources/special/tickets/statusSemantics.ts
12261
12379
  async function statusSemantics(_resource, opts = {}, http) {
12262
12380
  return resolveTicketStatusSemantics(http, opts);
@@ -12375,6 +12493,10 @@ var TicketsResource = class extends BaseResource {
12375
12493
  forResource(resourceID, opts) {
12376
12494
  return forResource(this, resourceID, this.http, opts);
12377
12495
  }
12496
+ /** Node-parity special op (hand-written in src/resources/special/tickets.ts). */
12497
+ lineOfBusiness(ticketId, associationId) {
12498
+ return lineOfBusiness2(this, ticketId, this.http, associationId);
12499
+ }
12378
12500
  };
12379
12501
 
12380
12502
  // src/resources/ticket_webhooks.ts
@@ -12503,12 +12625,12 @@ async function postedDateStatusQuery(_resource, mode, http, opts) {
12503
12625
  return items;
12504
12626
  }
12505
12627
  async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
12506
- const OP9 = opLabel;
12507
- if (opts?.tz !== void 0) validateZone(opts.tz, OP9);
12508
- const fromB = customRangeDay(from, "from", void 0, OP9);
12509
- const toB = customRangeDay(to, "to", void 0, OP9);
12628
+ const OP11 = opLabel;
12629
+ if (opts?.tz !== void 0) validateZone(opts.tz, OP11);
12630
+ const fromB = customRangeDay(from, "from", void 0, OP11);
12631
+ const toB = customRangeDay(to, "to", void 0, OP11);
12510
12632
  if (fromB.ms > toB.ms) {
12511
- throw new AutotaskConfigError(`${OP9}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP9 });
12633
+ throw new AutotaskConfigError(`${OP11}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP11 });
12512
12634
  }
12513
12635
  const windowToMs = toB.ms + DAY_MS2 - 1;
12514
12636
  const filter = [
@@ -12519,7 +12641,7 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
12519
12641
  else if (opts?.posted === false) filter.push({ field: "postedDate", op: "notExist" });
12520
12642
  if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
12521
12643
  if (opts?.resourceID !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
12522
- const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP9, http);
12644
+ const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP11, http);
12523
12645
  let postedCount = 0;
12524
12646
  for (const item of items) {
12525
12647
  const postedDate = item.postedDate;
@@ -12729,7 +12851,7 @@ async function resolveCode(http, family, familyLabel2, pinnedId, departments, op
12729
12851
  }
12730
12852
 
12731
12853
  // src/resources/special/time_entries/createForResource.ts
12732
- var OP5 = "time_entries.createForResource";
12854
+ var OP7 = "time_entries.createForResource";
12733
12855
  var PARAM_KEYS = /* @__PURE__ */ new Set([
12734
12856
  "internallAllocationCodeID",
12735
12857
  "ticketID",
@@ -12765,30 +12887,30 @@ function validateCreateParams(params) {
12765
12887
  if (pinCount > 1) {
12766
12888
  throw new AutotaskConfigError(
12767
12889
  "createForResource: exactly one linkage pin is honoured (internallAllocationCodeID | ticketID | taskID) \u2014 the vendor gate requires exactly one; pass one pin (or none for the default internal linkage)",
12768
- { operation: OP5 }
12890
+ { operation: OP7 }
12769
12891
  );
12770
12892
  }
12771
12893
  if (pins.role !== void 0 && pins.code !== void 0) {
12772
12894
  throw new AutotaskConfigError(
12773
12895
  "createForResource: a pinned roleID is only meaningful with the task/ticket linkage (internallAllocationCodeID pins the internal linkage and owns the code) \u2014 drop one",
12774
- { operation: OP5 }
12896
+ { operation: OP7 }
12775
12897
  );
12776
12898
  }
12777
12899
  if (params.hoursWorked !== void 0 && (typeof params.hoursWorked !== "number" || !Number.isFinite(params.hoursWorked) || params.hoursWorked <= 0)) {
12778
12900
  throw new AutotaskConfigError(
12779
12901
  `createForResource: hoursWorked must be a positive finite number (got ${String(params.hoursWorked)})`,
12780
- { operation: OP5 }
12902
+ { operation: OP7 }
12781
12903
  );
12782
12904
  }
12783
12905
  return pins;
12784
12906
  }
12785
12907
  async function assertResourceActive(http, rid) {
12786
- const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP5);
12908
+ const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP7);
12787
12909
  if (rec === null) {
12788
12910
  throw new ResourceInvalidError(`Resource ${rid} was not found \u2014 a time entry cannot attach to a missing resource`, {
12789
12911
  resourceIds: [rid],
12790
12912
  details: { idRefs: [{ entity: "resource", id: rid }] },
12791
- operation: OP5,
12913
+ operation: OP7,
12792
12914
  category: "not_found",
12793
12915
  suggestedAction: "Resolve the resource id (resources.get / resources.search) and retry."
12794
12916
  });
@@ -12797,7 +12919,7 @@ async function assertResourceActive(http, rid) {
12797
12919
  throw new ResourceInvalidError(`Resource ${rid} is not active (isActive !== true) \u2014 the vendor would reject a time entry for an inactive resource`, {
12798
12920
  resourceIds: [rid],
12799
12921
  details: { idRefs: [{ entity: "resource", id: rid }] },
12800
- operation: OP5,
12922
+ operation: OP7,
12801
12923
  suggestedAction: "Activate the resource or pick an active one."
12802
12924
  });
12803
12925
  }
@@ -12807,11 +12929,11 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12807
12929
  const pinnedForCode = pins.code !== void 0 ? pins.code : pins.role;
12808
12930
  let code = null;
12809
12931
  const familiesTried = [];
12810
- code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP5);
12932
+ code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP7);
12811
12933
  familiesTried.push({ family: familyOf(linkage), label: familyLabel(familyOf(linkage)), filters: [{ field: "useType", op: "eq", value: familyOf(linkage) }, { field: "isActive", op: "eq", value: true }] });
12812
12934
  if (code === null && linkage === "task" && pins.role === void 0) {
12813
12935
  usedLinkage = "internal";
12814
- code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP5);
12936
+ code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP7);
12815
12937
  familiesTried.push({ family: USE_TYPE_INTERNAL, label: familyLabel(USE_TYPE_INTERNAL), filters: [{ field: "useType", op: "eq", value: USE_TYPE_INTERNAL }, { field: "isActive", op: "eq", value: true }] });
12816
12938
  }
12817
12939
  if (code === null) {
@@ -12821,7 +12943,7 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12821
12943
  families: familiesTried,
12822
12944
  resourceDepartments: [...departments].sort((a, b) => a - b)
12823
12945
  },
12824
- { operation: OP5 }
12946
+ { operation: OP7 }
12825
12947
  );
12826
12948
  }
12827
12949
  const resolvedCode = codeInfo(code);
@@ -12860,7 +12982,7 @@ function createDryRun2(http, rid, body, linkage, usedLinkage, resolvedCode) {
12860
12982
  }
12861
12983
  return buildDryRunResult(
12862
12984
  {
12863
- operation: OP5,
12985
+ operation: OP7,
12864
12986
  method: "POST",
12865
12987
  path: "/V1.0/TimeEntries",
12866
12988
  ids: [rid],
@@ -12902,7 +13024,7 @@ async function createWithAttempts(resource, http, opts, body, defaults, pins, us
12902
13024
  const gate3 = ROLE_REJECTION_PATTERN.test(text);
12903
13025
  if (usedLinkage !== "internal" && gate3) {
12904
13026
  if (!taskRoleFetched && usedLinkage === "task" && pins.task !== void 0) {
12905
- const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP5);
13027
+ const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP7);
12906
13028
  taskRoleFetched = true;
12907
13029
  if (task !== null) pushLadder(task.assignedResourceRoleID);
12908
13030
  }
@@ -12928,7 +13050,7 @@ async function createForResource(resource, resourceID, params, opts = {}, http)
12928
13050
  const pins = validateCreateParams(params);
12929
13051
  await assertResourceActive(http, rid);
12930
13052
  const linkage = pins.code !== void 0 ? "internal" : pins.ticket !== void 0 ? "ticket" : pins.task !== void 0 ? "task" : "internal";
12931
- const roleRows = await resourceRoleRows(http, rid, OP5);
13053
+ const roleRows = await resourceRoleRows(http, rid, OP7);
12932
13054
  const departments = roleRows.departments;
12933
13055
  const { usedLinkage, resolvedCode } = await resolveAllocationCode(http, rid, linkage, pins, departments);
12934
13056
  const defaults = windowDefaults(params);
@@ -12951,12 +13073,12 @@ function discoveryField(err, body, defaults) {
12951
13073
  }
12952
13074
 
12953
13075
  // src/resources/special/time_entries/move.ts
12954
- var OP6 = "time_entries.moveToTicket";
13076
+ var OP8 = "time_entries.moveToTicket";
12955
13077
  var COPY2 = ["resourceID", "roleID", "billingCodeID", "dateWorked", "startDateTime", "endDateTime", "hoursWorked", "offsetHours", "summaryNotes", "internalNotes", "isNonBillable", "showOnInvoice"];
12956
13078
  var CONTRACT_SCOPED2 = ["contractID", "contractServiceID", "contractServiceBundleID"];
12957
13079
  async function readTimeEntry(http, id, retries) {
12958
13080
  try {
12959
- const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP6, maxRetries: retries });
13081
+ const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP8, maxRetries: retries });
12960
13082
  return res?.item ?? null;
12961
13083
  } catch (e) {
12962
13084
  if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
@@ -12966,27 +13088,27 @@ async function readTimeEntry(http, id, retries) {
12966
13088
  async function checkSource2({ http, source, sourceId, toTicketId, retries }) {
12967
13089
  const ids2 = { sourceId, toTicketId };
12968
13090
  if (source.taskID !== null && source.taskID !== void 0) {
12969
- throw refuse("MOVE_SOURCE_UNSUPPORTED", `${OP6}: time entry ${sourceId} is on a task \u2014 only ticket time entries are moved`, ids2, OP6);
13091
+ throw refuse("MOVE_SOURCE_UNSUPPORTED", `${OP8}: time entry ${sourceId} is on a task \u2014 only ticket time entries are moved`, ids2, OP8);
12970
13092
  }
12971
13093
  if (source.billingApprovalDateTime || source.billingApprovalResourceID || source.billingApprovalLevelMostRecent) {
12972
- throw refuse("MOVE_BLOCKED_APPROVED", `${OP6}: time entry ${sourceId} has billing approval set \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "approved entries are not moved by the API" } });
13094
+ throw refuse("MOVE_BLOCKED_APPROVED", `${OP8}: time entry ${sourceId} has billing approval set \u2014 move it in the Autotask UI`, ids2, OP8, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "approved entries are not moved by the API" } });
12973
13095
  }
12974
13096
  let billing;
12975
13097
  try {
12976
- billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
13098
+ billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP8, retries);
12977
13099
  } catch {
12978
- throw refuse("MOVE_BLOCKED_BILLED", `${OP6}: the billing items of time entry ${sourceId} could not be read \u2014 refused (fails closed)`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billing state unreadable" } });
13100
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP8}: the billing items of time entry ${sourceId} could not be read \u2014 refused (fails closed)`, ids2, OP8, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billing state unreadable" } });
12979
13101
  }
12980
13102
  if (billing.some((b) => b.invoiceID || b.postedDate)) {
12981
- throw refuse("MOVE_BLOCKED_BILLED", `${OP6}: time entry ${sourceId} is billed or posted \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billed entries are not moved by the API" } });
13103
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP8}: time entry ${sourceId} is billed or posted \u2014 move it in the Autotask UI`, ids2, OP8, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billed entries are not moved by the API" } });
12982
13104
  }
12983
- const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
13105
+ const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP8, retries);
12984
13106
  if (attachments.length > 0) {
12985
- throw refuse("MOVE_BLOCKED_ATTACHMENTS", `${OP6}: time entry ${sourceId} has attachments, which a delete would destroy \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "entries with attachments are not moved by the API" } });
13107
+ throw refuse("MOVE_BLOCKED_ATTACHMENTS", `${OP8}: time entry ${sourceId} has attachments, which a delete would destroy \u2014 move it in the Autotask UI`, ids2, OP8, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "entries with attachments are not moved by the API" } });
12986
13108
  }
12987
13109
  }
12988
13110
  var SPEC2 = {
12989
- operation: OP6,
13111
+ operation: OP8,
12990
13112
  entity: "time_entry",
12991
13113
  readSource: readTimeEntry,
12992
13114
  sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
@@ -13021,7 +13143,7 @@ function moveToTicket2(_resource, sourceId, toTicketId, opts, http) {
13021
13143
  }
13022
13144
 
13023
13145
  // src/resources/special/time_entries/approve.ts
13024
- var OP7 = "time_entries.approve";
13146
+ var OP9 = "time_entries.approve";
13025
13147
  var APPROVAL_LEVELS_PATH = "/V1.0/BillingItemApprovalLevels";
13026
13148
  function toIso(value) {
13027
13149
  if (value instanceof Date) {
@@ -13059,7 +13181,7 @@ async function approve(resource, id, opts = {}, http) {
13059
13181
  if (opts.dryRun === true) {
13060
13182
  return buildDryRunResult(
13061
13183
  {
13062
- operation: OP7,
13184
+ operation: OP9,
13063
13185
  method: "POST",
13064
13186
  path: APPROVAL_LEVELS_PATH,
13065
13187
  ids: [numericId],
@@ -13082,10 +13204,10 @@ async function approve(resource, id, opts = {}, http) {
13082
13204
  method: "POST",
13083
13205
  path: APPROVAL_LEVELS_PATH,
13084
13206
  body,
13085
- operation: OP7,
13207
+ operation: OP9,
13086
13208
  maxRetries: opts.maxRetries
13087
13209
  });
13088
- const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP7).id, "billingItemApprovalLevelID"));
13210
+ const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP9).id, "billingItemApprovalLevelID"));
13089
13211
  await resource.update(
13090
13212
  numericId,
13091
13213
  { billingApprovalDateTime: approvalDateTime, billingApprovalResourceID: approvalResourceID },
@@ -13108,28 +13230,28 @@ async function approve(resource, id, opts = {}, http) {
13108
13230
  }
13109
13231
 
13110
13232
  // src/resources/special/time_entries/recentTickets.ts
13111
- var OP8 = "time_entries.recentTickets";
13233
+ var OP10 = "time_entries.recentTickets";
13112
13234
  var DAY_MS3 = 864e5;
13113
13235
  var TICKET_CHUNK2 = 100;
13114
13236
  var TICKET_FIELDS3 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
13115
- function positiveId2(value, name) {
13237
+ function positiveId4(value, name) {
13116
13238
  const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
13117
13239
  if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
13118
- throw new AutotaskConfigError(`${OP8}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP8 });
13240
+ throw new AutotaskConfigError(`${OP10}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP10 });
13119
13241
  }
13120
13242
  return n;
13121
13243
  }
13122
13244
  async function recentTickets(_resource, from, to, http, opts) {
13123
13245
  if (opts === void 0 || opts === null || typeof opts !== "object") {
13124
- throw new AutotaskConfigError(`${OP8}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP8 });
13246
+ throw new AutotaskConfigError(`${OP10}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP10 });
13125
13247
  }
13126
- const resourceID = positiveId2(opts.resourceID, "resourceID");
13127
- const companyID = opts.companyID === void 0 ? void 0 : positiveId2(opts.companyID, "companyID");
13128
- if (opts.tz !== void 0) validateZone(opts.tz, OP8);
13129
- const fromB = customRangeDay(from, "from", void 0, OP8);
13130
- const toB = customRangeDay(to, "to", void 0, OP8);
13248
+ const resourceID = positiveId4(opts.resourceID, "resourceID");
13249
+ const companyID = opts.companyID === void 0 ? void 0 : positiveId4(opts.companyID, "companyID");
13250
+ if (opts.tz !== void 0) validateZone(opts.tz, OP10);
13251
+ const fromB = customRangeDay(from, "from", void 0, OP10);
13252
+ const toB = customRangeDay(to, "to", void 0, OP10);
13131
13253
  if (fromB.ms > toB.ms) {
13132
- throw new AutotaskConfigError(`${OP8}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP8 });
13254
+ throw new AutotaskConfigError(`${OP10}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP10 });
13133
13255
  }
13134
13256
  const windowToMs = toB.ms + DAY_MS3 - 1;
13135
13257
  const { items: entries, hasMore } = await queryTimeEntries(
@@ -13140,7 +13262,7 @@ async function recentTickets(_resource, from, to, http, opts) {
13140
13262
  { field: "resourceID", op: "eq", value: resourceID }
13141
13263
  ],
13142
13264
  clampWindowLimit(opts.limit),
13143
- OP8,
13265
+ OP10,
13144
13266
  http
13145
13267
  );
13146
13268
  const groups = /* @__PURE__ */ new Map();
@@ -13166,9 +13288,9 @@ async function recentTickets(_resource, from, to, http, opts) {
13166
13288
  idempotent: true,
13167
13289
  path: "/V1.0/Tickets/query",
13168
13290
  body: { MaxRecords: TICKET_CHUNK2, filter, IncludeFields: TICKET_FIELDS3 },
13169
- operation: OP8
13291
+ operation: OP10
13170
13292
  });
13171
- for (const t of unwrapItems(res, OP8).items) if (typeof t.id === "number") tickets.set(t.id, t);
13293
+ for (const t of unwrapItems(res, OP10).items) if (typeof t.id === "number") tickets.set(t.id, t);
13172
13294
  }
13173
13295
  const items = [];
13174
13296
  const unresolvedTicketIDs = [];