node-autotask 0.28.0 → 0.29.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 +13 -0
  2. package/MCP_TOOL_CATALOG.json +27 -8
  3. package/MCP_TOOL_MANIFEST.md +12 -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 +1471 -1307
  8. package/dist/capabilities.cjs +2 -2
  9. package/dist/capabilities.js +1 -1
  10. package/dist/{chunk-GUY2KK7U.cjs → chunk-APXBBN3D.cjs} +143 -143
  11. package/dist/{chunk-WYKGD5UD.cjs → chunk-BNIOQEKP.cjs} +536 -491
  12. package/dist/{chunk-D5IVS6GY.cjs → chunk-IKWHRUQ5.cjs} +97 -6
  13. package/dist/{chunk-IRVZGW5K.js → chunk-KVT5V7SA.js} +93 -2
  14. package/dist/{chunk-ZEGMVEF3.cjs → chunk-PY45RBQ2.cjs} +1470 -1306
  15. package/dist/{chunk-SYX6EWMT.js → chunk-T4SZGOJB.js} +98 -53
  16. package/dist/{chunk-6EQSXYRW.js → chunk-WKP442HS.js} +1470 -1306
  17. package/dist/{chunk-O2D7KDDD.js → chunk-X4TUU2Y6.js} +2 -2
  18. package/dist/{index-DXuV04zM.d.ts → index-6_i-__sN.d.ts} +25 -2
  19. package/dist/{index-DQ9NgraW.d.cts → index-BGZ1Mhbh.d.cts} +105 -82
  20. package/dist/{index-DQ9NgraW.d.ts → index-BGZ1Mhbh.d.ts} +105 -82
  21. package/dist/{index-CORcZUNm.d.cts → index-BZkfUU59.d.cts} +25 -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-KVT5V7SA.js";
42
44
  import {
43
45
  CAPABILITIES
44
- } from "./chunk-6EQSXYRW.js";
46
+ } from "./chunk-WKP442HS.js";
45
47
 
46
48
  // src/resources/appointments.ts
47
49
  var AppointmentsResource = class extends BaseResource {
@@ -5299,7 +5301,7 @@ function appendWiresNote(wires, note) {
5299
5301
  wires.note = (wires.note ? wires.note + " | " : "") + note;
5300
5302
  }
5301
5303
  function makeFetchCtx(rawHttp, v, contractId, budget) {
5302
- const OP9 = PROFITABILITY_OP;
5304
+ const OP10 = PROFITABILITY_OP;
5303
5305
  const id = parseIdentifier(contractId, "contractId");
5304
5306
  const idNum = typeof id === "number" ? id : Number(id);
5305
5307
  const wires = { issued: 0, requests: 0, budget };
@@ -5311,19 +5313,19 @@ function makeFetchCtx(rawHttp, v, contractId, budget) {
5311
5313
  } };
5312
5314
  const readPart = (path, conditions, fields) => {
5313
5315
  wires.issued += 1;
5314
- return degradable(() => boundedPart(http, path, conditions, v.limit, OP9, fields));
5316
+ return degradable(() => boundedPart(http, path, conditions, v.limit, OP10, fields));
5315
5317
  };
5316
5318
  const truncatedMonths = {};
5317
5319
  const windowedPart = async (name, key, path, cond, fields, dateField) => {
5318
5320
  const first = await readPart(path, cond, fields);
5319
5321
  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));
5322
+ const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP10, fields, dateField, v.dateFrom, v.dateTo, wires));
5321
5323
  if (!("truncatedMonths" in wp)) return { part: stampPart(key, wp, cond), rows: [] };
5322
5324
  const { truncatedMonths: tm, ...part } = wp;
5323
5325
  truncatedMonths[name] = tm;
5324
5326
  return { part, rows: wp.items };
5325
5327
  };
5326
- return { v, OP: OP9, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5328
+ return { v, OP: OP10, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5327
5329
  }
5328
5330
 
5329
5331
  // src/resources/special/contracts/profitability.stages.ts
@@ -12061,6 +12063,7 @@ async function addCompositeNames(http, ctx, entriesPart, result, opts) {
12061
12063
  builder.addFk("contract", t["contractID"]);
12062
12064
  builder.addFk("billingCode", t["billingCodeID"]);
12063
12065
  builder.addFk("role", t["assignedResourceRoleID"]);
12066
+ builder.addLineOfBusiness(t["organizationalLevelAssociationID"]);
12064
12067
  for (const e of entriesPart.items) {
12065
12068
  builder.addResource(e["resourceID"]);
12066
12069
  builder.addFk("role", e["roleID"]);
@@ -12084,6 +12087,12 @@ async function addCompositeNames(http, ctx, entriesPart, result, opts) {
12084
12087
  return row;
12085
12088
  });
12086
12089
  result.names = names.section;
12090
+ result.lineOfBusiness = names.lineOfBusiness ?? {
12091
+ association: null,
12092
+ organizationalLevel2: null,
12093
+ organizationalLevel1: null,
12094
+ markers: { association: "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
12095
+ };
12087
12096
  result.truncated = result.truncated || names.section.truncated;
12088
12097
  }
12089
12098
  async function fullContext(resource, id, opts = {}, http) {
@@ -12257,6 +12266,38 @@ async function forResource(_resource, resourceIDIn, http, opts = {}) {
12257
12266
  };
12258
12267
  }
12259
12268
 
12269
+ // src/resources/special/tickets/lineOfBusiness.ts
12270
+ var OP5 = "tickets.lineOfBusiness";
12271
+ function positiveId2(value, name) {
12272
+ const n = typeof value === "string" && /^[0-9]+$/.test(value.trim()) ? Number(value) : value;
12273
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
12274
+ throw new AutotaskConfigError(OP5 + ": " + name + " must be a positive integer id (got " + String(value) + ")", { operation: OP5 });
12275
+ }
12276
+ return n;
12277
+ }
12278
+ async function lineOfBusiness(resource, ticketIdIn, http, associationIdIn) {
12279
+ const ticketId = positiveId2(ticketIdIn, "ticketId");
12280
+ let associationId;
12281
+ if (associationIdIn !== void 0) {
12282
+ associationId = positiveId2(associationIdIn, "associationId");
12283
+ } else {
12284
+ const ticket = await resource.get(ticketId);
12285
+ associationId = vendorId(ticket.organizationalLevelAssociationID);
12286
+ }
12287
+ if (associationId === void 0) {
12288
+ return {
12289
+ ticketId,
12290
+ association: null,
12291
+ organizationalLevel2: null,
12292
+ organizationalLevel1: null,
12293
+ markers: { association: "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
12294
+ };
12295
+ }
12296
+ const chain = await resolveLineOfBusinessChain(http, associationId, { budget: 3, operation: OP5, onError: "propagate" });
12297
+ const { association, organizationalLevel2, organizationalLevel1, markers } = chain.lineOfBusiness;
12298
+ return { ticketId, association, organizationalLevel2, organizationalLevel1, markers };
12299
+ }
12300
+
12260
12301
  // src/resources/special/tickets/statusSemantics.ts
12261
12302
  async function statusSemantics(_resource, opts = {}, http) {
12262
12303
  return resolveTicketStatusSemantics(http, opts);
@@ -12375,6 +12416,10 @@ var TicketsResource = class extends BaseResource {
12375
12416
  forResource(resourceID, opts) {
12376
12417
  return forResource(this, resourceID, this.http, opts);
12377
12418
  }
12419
+ /** Node-parity special op (hand-written in src/resources/special/tickets.ts). */
12420
+ lineOfBusiness(ticketId, associationId) {
12421
+ return lineOfBusiness(this, ticketId, this.http, associationId);
12422
+ }
12378
12423
  };
12379
12424
 
12380
12425
  // src/resources/ticket_webhooks.ts
@@ -12503,12 +12548,12 @@ async function postedDateStatusQuery(_resource, mode, http, opts) {
12503
12548
  return items;
12504
12549
  }
12505
12550
  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);
12551
+ const OP10 = opLabel;
12552
+ if (opts?.tz !== void 0) validateZone(opts.tz, OP10);
12553
+ const fromB = customRangeDay(from, "from", void 0, OP10);
12554
+ const toB = customRangeDay(to, "to", void 0, OP10);
12510
12555
  if (fromB.ms > toB.ms) {
12511
- throw new AutotaskConfigError(`${OP9}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP9 });
12556
+ throw new AutotaskConfigError(`${OP10}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP10 });
12512
12557
  }
12513
12558
  const windowToMs = toB.ms + DAY_MS2 - 1;
12514
12559
  const filter = [
@@ -12519,7 +12564,7 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
12519
12564
  else if (opts?.posted === false) filter.push({ field: "postedDate", op: "notExist" });
12520
12565
  if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
12521
12566
  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);
12567
+ const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP10, http);
12523
12568
  let postedCount = 0;
12524
12569
  for (const item of items) {
12525
12570
  const postedDate = item.postedDate;
@@ -12729,7 +12774,7 @@ async function resolveCode(http, family, familyLabel2, pinnedId, departments, op
12729
12774
  }
12730
12775
 
12731
12776
  // src/resources/special/time_entries/createForResource.ts
12732
- var OP5 = "time_entries.createForResource";
12777
+ var OP6 = "time_entries.createForResource";
12733
12778
  var PARAM_KEYS = /* @__PURE__ */ new Set([
12734
12779
  "internallAllocationCodeID",
12735
12780
  "ticketID",
@@ -12765,30 +12810,30 @@ function validateCreateParams(params) {
12765
12810
  if (pinCount > 1) {
12766
12811
  throw new AutotaskConfigError(
12767
12812
  "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 }
12813
+ { operation: OP6 }
12769
12814
  );
12770
12815
  }
12771
12816
  if (pins.role !== void 0 && pins.code !== void 0) {
12772
12817
  throw new AutotaskConfigError(
12773
12818
  "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 }
12819
+ { operation: OP6 }
12775
12820
  );
12776
12821
  }
12777
12822
  if (params.hoursWorked !== void 0 && (typeof params.hoursWorked !== "number" || !Number.isFinite(params.hoursWorked) || params.hoursWorked <= 0)) {
12778
12823
  throw new AutotaskConfigError(
12779
12824
  `createForResource: hoursWorked must be a positive finite number (got ${String(params.hoursWorked)})`,
12780
- { operation: OP5 }
12825
+ { operation: OP6 }
12781
12826
  );
12782
12827
  }
12783
12828
  return pins;
12784
12829
  }
12785
12830
  async function assertResourceActive(http, rid) {
12786
- const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP5);
12831
+ const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP6);
12787
12832
  if (rec === null) {
12788
12833
  throw new ResourceInvalidError(`Resource ${rid} was not found \u2014 a time entry cannot attach to a missing resource`, {
12789
12834
  resourceIds: [rid],
12790
12835
  details: { idRefs: [{ entity: "resource", id: rid }] },
12791
- operation: OP5,
12836
+ operation: OP6,
12792
12837
  category: "not_found",
12793
12838
  suggestedAction: "Resolve the resource id (resources.get / resources.search) and retry."
12794
12839
  });
@@ -12797,7 +12842,7 @@ async function assertResourceActive(http, rid) {
12797
12842
  throw new ResourceInvalidError(`Resource ${rid} is not active (isActive !== true) \u2014 the vendor would reject a time entry for an inactive resource`, {
12798
12843
  resourceIds: [rid],
12799
12844
  details: { idRefs: [{ entity: "resource", id: rid }] },
12800
- operation: OP5,
12845
+ operation: OP6,
12801
12846
  suggestedAction: "Activate the resource or pick an active one."
12802
12847
  });
12803
12848
  }
@@ -12807,11 +12852,11 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12807
12852
  const pinnedForCode = pins.code !== void 0 ? pins.code : pins.role;
12808
12853
  let code = null;
12809
12854
  const familiesTried = [];
12810
- code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP5);
12855
+ code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP6);
12811
12856
  familiesTried.push({ family: familyOf(linkage), label: familyLabel(familyOf(linkage)), filters: [{ field: "useType", op: "eq", value: familyOf(linkage) }, { field: "isActive", op: "eq", value: true }] });
12812
12857
  if (code === null && linkage === "task" && pins.role === void 0) {
12813
12858
  usedLinkage = "internal";
12814
- code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP5);
12859
+ code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP6);
12815
12860
  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
12861
  }
12817
12862
  if (code === null) {
@@ -12821,7 +12866,7 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12821
12866
  families: familiesTried,
12822
12867
  resourceDepartments: [...departments].sort((a, b) => a - b)
12823
12868
  },
12824
- { operation: OP5 }
12869
+ { operation: OP6 }
12825
12870
  );
12826
12871
  }
12827
12872
  const resolvedCode = codeInfo(code);
@@ -12860,7 +12905,7 @@ function createDryRun2(http, rid, body, linkage, usedLinkage, resolvedCode) {
12860
12905
  }
12861
12906
  return buildDryRunResult(
12862
12907
  {
12863
- operation: OP5,
12908
+ operation: OP6,
12864
12909
  method: "POST",
12865
12910
  path: "/V1.0/TimeEntries",
12866
12911
  ids: [rid],
@@ -12902,7 +12947,7 @@ async function createWithAttempts(resource, http, opts, body, defaults, pins, us
12902
12947
  const gate3 = ROLE_REJECTION_PATTERN.test(text);
12903
12948
  if (usedLinkage !== "internal" && gate3) {
12904
12949
  if (!taskRoleFetched && usedLinkage === "task" && pins.task !== void 0) {
12905
- const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP5);
12950
+ const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP6);
12906
12951
  taskRoleFetched = true;
12907
12952
  if (task !== null) pushLadder(task.assignedResourceRoleID);
12908
12953
  }
@@ -12928,7 +12973,7 @@ async function createForResource(resource, resourceID, params, opts = {}, http)
12928
12973
  const pins = validateCreateParams(params);
12929
12974
  await assertResourceActive(http, rid);
12930
12975
  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);
12976
+ const roleRows = await resourceRoleRows(http, rid, OP6);
12932
12977
  const departments = roleRows.departments;
12933
12978
  const { usedLinkage, resolvedCode } = await resolveAllocationCode(http, rid, linkage, pins, departments);
12934
12979
  const defaults = windowDefaults(params);
@@ -12951,12 +12996,12 @@ function discoveryField(err, body, defaults) {
12951
12996
  }
12952
12997
 
12953
12998
  // src/resources/special/time_entries/move.ts
12954
- var OP6 = "time_entries.moveToTicket";
12999
+ var OP7 = "time_entries.moveToTicket";
12955
13000
  var COPY2 = ["resourceID", "roleID", "billingCodeID", "dateWorked", "startDateTime", "endDateTime", "hoursWorked", "offsetHours", "summaryNotes", "internalNotes", "isNonBillable", "showOnInvoice"];
12956
13001
  var CONTRACT_SCOPED2 = ["contractID", "contractServiceID", "contractServiceBundleID"];
12957
13002
  async function readTimeEntry(http, id, retries) {
12958
13003
  try {
12959
- const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP6, maxRetries: retries });
13004
+ const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP7, maxRetries: retries });
12960
13005
  return res?.item ?? null;
12961
13006
  } catch (e) {
12962
13007
  if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
@@ -12966,27 +13011,27 @@ async function readTimeEntry(http, id, retries) {
12966
13011
  async function checkSource2({ http, source, sourceId, toTicketId, retries }) {
12967
13012
  const ids2 = { sourceId, toTicketId };
12968
13013
  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);
13014
+ throw refuse("MOVE_SOURCE_UNSUPPORTED", `${OP7}: time entry ${sourceId} is on a task \u2014 only ticket time entries are moved`, ids2, OP7);
12970
13015
  }
12971
13016
  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" } });
13017
+ throw refuse("MOVE_BLOCKED_APPROVED", `${OP7}: time entry ${sourceId} has billing approval set \u2014 move it in the Autotask UI`, ids2, OP7, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "approved entries are not moved by the API" } });
12973
13018
  }
12974
13019
  let billing;
12975
13020
  try {
12976
- billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
13021
+ billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP7, retries);
12977
13022
  } 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" } });
13023
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP7}: the billing items of time entry ${sourceId} could not be read \u2014 refused (fails closed)`, ids2, OP7, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billing state unreadable" } });
12979
13024
  }
12980
13025
  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" } });
13026
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP7}: time entry ${sourceId} is billed or posted \u2014 move it in the Autotask UI`, ids2, OP7, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billed entries are not moved by the API" } });
12982
13027
  }
12983
- const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
13028
+ const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP7, retries);
12984
13029
  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" } });
13030
+ throw refuse("MOVE_BLOCKED_ATTACHMENTS", `${OP7}: time entry ${sourceId} has attachments, which a delete would destroy \u2014 move it in the Autotask UI`, ids2, OP7, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "entries with attachments are not moved by the API" } });
12986
13031
  }
12987
13032
  }
12988
13033
  var SPEC2 = {
12989
- operation: OP6,
13034
+ operation: OP7,
12990
13035
  entity: "time_entry",
12991
13036
  readSource: readTimeEntry,
12992
13037
  sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
@@ -13021,7 +13066,7 @@ function moveToTicket2(_resource, sourceId, toTicketId, opts, http) {
13021
13066
  }
13022
13067
 
13023
13068
  // src/resources/special/time_entries/approve.ts
13024
- var OP7 = "time_entries.approve";
13069
+ var OP8 = "time_entries.approve";
13025
13070
  var APPROVAL_LEVELS_PATH = "/V1.0/BillingItemApprovalLevels";
13026
13071
  function toIso(value) {
13027
13072
  if (value instanceof Date) {
@@ -13059,7 +13104,7 @@ async function approve(resource, id, opts = {}, http) {
13059
13104
  if (opts.dryRun === true) {
13060
13105
  return buildDryRunResult(
13061
13106
  {
13062
- operation: OP7,
13107
+ operation: OP8,
13063
13108
  method: "POST",
13064
13109
  path: APPROVAL_LEVELS_PATH,
13065
13110
  ids: [numericId],
@@ -13082,10 +13127,10 @@ async function approve(resource, id, opts = {}, http) {
13082
13127
  method: "POST",
13083
13128
  path: APPROVAL_LEVELS_PATH,
13084
13129
  body,
13085
- operation: OP7,
13130
+ operation: OP8,
13086
13131
  maxRetries: opts.maxRetries
13087
13132
  });
13088
- const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP7).id, "billingItemApprovalLevelID"));
13133
+ const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP8).id, "billingItemApprovalLevelID"));
13089
13134
  await resource.update(
13090
13135
  numericId,
13091
13136
  { billingApprovalDateTime: approvalDateTime, billingApprovalResourceID: approvalResourceID },
@@ -13108,28 +13153,28 @@ async function approve(resource, id, opts = {}, http) {
13108
13153
  }
13109
13154
 
13110
13155
  // src/resources/special/time_entries/recentTickets.ts
13111
- var OP8 = "time_entries.recentTickets";
13156
+ var OP9 = "time_entries.recentTickets";
13112
13157
  var DAY_MS3 = 864e5;
13113
13158
  var TICKET_CHUNK2 = 100;
13114
13159
  var TICKET_FIELDS3 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
13115
- function positiveId2(value, name) {
13160
+ function positiveId3(value, name) {
13116
13161
  const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
13117
13162
  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 });
13163
+ throw new AutotaskConfigError(`${OP9}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP9 });
13119
13164
  }
13120
13165
  return n;
13121
13166
  }
13122
13167
  async function recentTickets(_resource, from, to, http, opts) {
13123
13168
  if (opts === void 0 || opts === null || typeof opts !== "object") {
13124
- throw new AutotaskConfigError(`${OP8}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP8 });
13169
+ throw new AutotaskConfigError(`${OP9}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP9 });
13125
13170
  }
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);
13171
+ const resourceID = positiveId3(opts.resourceID, "resourceID");
13172
+ const companyID = opts.companyID === void 0 ? void 0 : positiveId3(opts.companyID, "companyID");
13173
+ if (opts.tz !== void 0) validateZone(opts.tz, OP9);
13174
+ const fromB = customRangeDay(from, "from", void 0, OP9);
13175
+ const toB = customRangeDay(to, "to", void 0, OP9);
13131
13176
  if (fromB.ms > toB.ms) {
13132
- throw new AutotaskConfigError(`${OP8}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP8 });
13177
+ throw new AutotaskConfigError(`${OP9}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP9 });
13133
13178
  }
13134
13179
  const windowToMs = toB.ms + DAY_MS3 - 1;
13135
13180
  const { items: entries, hasMore } = await queryTimeEntries(
@@ -13140,7 +13185,7 @@ async function recentTickets(_resource, from, to, http, opts) {
13140
13185
  { field: "resourceID", op: "eq", value: resourceID }
13141
13186
  ],
13142
13187
  clampWindowLimit(opts.limit),
13143
- OP8,
13188
+ OP9,
13144
13189
  http
13145
13190
  );
13146
13191
  const groups = /* @__PURE__ */ new Map();
@@ -13166,9 +13211,9 @@ async function recentTickets(_resource, from, to, http, opts) {
13166
13211
  idempotent: true,
13167
13212
  path: "/V1.0/Tickets/query",
13168
13213
  body: { MaxRecords: TICKET_CHUNK2, filter, IncludeFields: TICKET_FIELDS3 },
13169
- operation: OP8
13214
+ operation: OP9
13170
13215
  });
13171
- for (const t of unwrapItems(res, OP8).items) if (typeof t.id === "number") tickets.set(t.id, t);
13216
+ for (const t of unwrapItems(res, OP9).items) if (typeof t.id === "number") tickets.set(t.id, t);
13172
13217
  }
13173
13218
  const items = [];
13174
13219
  const unresolvedTicketIDs = [];