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.
- package/CHANGELOG.md +31 -0
- package/MCP_TOOL_CATALOG.json +46 -8
- package/MCP_TOOL_MANIFEST.md +13 -11
- package/N8N_MCP_MANIFEST.json +34 -5
- package/N8N_MCP_MANIFEST.md +8 -7
- package/README.md +1 -2
- package/capabilities.json +1642 -1319
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-O2D7KDDD.js → chunk-2ETR5GSO.js} +2 -2
- package/dist/{chunk-ZEGMVEF3.cjs → chunk-EDVSWL32.cjs} +1641 -1318
- package/dist/{chunk-GUY2KK7U.cjs → chunk-L4TDVUJM.cjs} +143 -143
- package/dist/{chunk-D5IVS6GY.cjs → chunk-LLIB4JGK.cjs} +140 -6
- package/dist/{chunk-SYX6EWMT.js → chunk-P4PC32RJ.js} +192 -70
- package/dist/{chunk-IRVZGW5K.js → chunk-QQPCJT2L.js} +136 -2
- package/dist/{chunk-WYKGD5UD.cjs → chunk-RS7QMPRO.cjs} +622 -500
- package/dist/{chunk-6EQSXYRW.js → chunk-TZC4ZAA6.js} +1641 -1318
- package/dist/{index-CORcZUNm.d.cts → index-B9VNv-EP.d.cts} +52 -2
- package/dist/{index-DQ9NgraW.d.cts → index-CJ36PwRG.d.cts} +110 -82
- package/dist/{index-DQ9NgraW.d.ts → index-CJ36PwRG.d.ts} +110 -82
- package/dist/{index-DXuV04zM.d.ts → index-DkpaFqyu.d.ts} +52 -2
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/operations/index.cjs +112 -112
- package/dist/operations/index.d.cts +4 -4
- package/dist/operations/index.d.ts +4 -4
- package/dist/operations/index.js +2 -2
- package/dist/pool.cjs +9 -9
- package/dist/pool.d.cts +2 -2
- package/dist/pool.d.ts +2 -2
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +2 -2
- package/dist/resources/index.d.ts +2 -2
- package/dist/resources/index.js +3 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/docs/API.md +1 -2
- 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
|
-
|
|
41
|
+
unwrapOperation,
|
|
42
|
+
vendorId
|
|
43
|
+
} from "./chunk-QQPCJT2L.js";
|
|
42
44
|
import {
|
|
43
45
|
CAPABILITIES
|
|
44
|
-
} from "./chunk-
|
|
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
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
|
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 }],
|
|
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", `${
|
|
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:
|
|
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
|
|
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
|
|
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(`${
|
|
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:
|
|
12224
|
+
operation: OP5
|
|
12171
12225
|
});
|
|
12172
|
-
const page = unwrapItems(res,
|
|
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(`${
|
|
12177
|
-
const 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(`${
|
|
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(`${
|
|
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(`${
|
|
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(`${
|
|
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:
|
|
12260
|
+
operation: OP5
|
|
12207
12261
|
});
|
|
12208
|
-
const page = unwrapItems(res,
|
|
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
|
|
12507
|
-
if (opts?.tz !== void 0) validateZone(opts.tz,
|
|
12508
|
-
const fromB = customRangeDay(from, "from", void 0,
|
|
12509
|
-
const toB = customRangeDay(to, "to", void 0,
|
|
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(`${
|
|
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),
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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}`,
|
|
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:
|
|
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:
|
|
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,
|
|
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,
|
|
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:
|
|
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:
|
|
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}`,
|
|
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,
|
|
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
|
|
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:
|
|
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", `${
|
|
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", `${
|
|
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 }],
|
|
13098
|
+
billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP8, retries);
|
|
12977
13099
|
} catch {
|
|
12978
|
-
throw refuse("MOVE_BLOCKED_BILLED", `${
|
|
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", `${
|
|
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 }],
|
|
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", `${
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
13207
|
+
operation: OP9,
|
|
13086
13208
|
maxRetries: opts.maxRetries
|
|
13087
13209
|
});
|
|
13088
|
-
const levelId = toNumber(parseIdentifier(unwrapOperation(created,
|
|
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
|
|
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
|
|
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(`${
|
|
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(`${
|
|
13246
|
+
throw new AutotaskConfigError(`${OP10}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP10 });
|
|
13125
13247
|
}
|
|
13126
|
-
const resourceID =
|
|
13127
|
-
const companyID = opts.companyID === void 0 ? void 0 :
|
|
13128
|
-
if (opts.tz !== void 0) validateZone(opts.tz,
|
|
13129
|
-
const fromB = customRangeDay(from, "from", void 0,
|
|
13130
|
-
const toB = customRangeDay(to, "to", void 0,
|
|
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(`${
|
|
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
|
-
|
|
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:
|
|
13291
|
+
operation: OP10
|
|
13170
13292
|
});
|
|
13171
|
-
for (const t of unwrapItems(res,
|
|
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 = [];
|