node-autotask 0.29.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 +18 -0
- package/MCP_TOOL_CATALOG.json +28 -9
- package/MCP_TOOL_MANIFEST.md +12 -11
- package/N8N_MCP_MANIFEST.json +3 -3
- package/N8N_MCP_MANIFEST.md +4 -4
- package/README.md +1 -1
- package/capabilities.json +1462 -1303
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-X4TUU2Y6.js → chunk-2ETR5GSO.js} +2 -2
- package/dist/{chunk-PY45RBQ2.cjs → chunk-EDVSWL32.cjs} +1461 -1302
- package/dist/{chunk-APXBBN3D.cjs → chunk-L4TDVUJM.cjs} +143 -143
- package/dist/{chunk-IKWHRUQ5.cjs → chunk-LLIB4JGK.cjs} +52 -9
- package/dist/{chunk-T4SZGOJB.js → chunk-P4PC32RJ.js} +157 -80
- package/dist/{chunk-KVT5V7SA.js → chunk-QQPCJT2L.js} +49 -6
- package/dist/{chunk-BNIOQEKP.cjs → chunk-RS7QMPRO.cjs} +589 -512
- package/dist/{chunk-WKP442HS.js → chunk-TZC4ZAA6.js} +1461 -1302
- package/dist/{index-BZkfUU59.d.cts → index-B9VNv-EP.d.cts} +30 -3
- package/dist/{index-BGZ1Mhbh.d.cts → index-CJ36PwRG.d.cts} +6 -1
- package/dist/{index-BGZ1Mhbh.d.ts → index-CJ36PwRG.d.ts} +6 -1
- package/dist/{index-6_i-__sN.d.ts → index-DkpaFqyu.d.ts} +30 -3
- 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 -1
- package/package.json +1 -1
|
@@ -40,10 +40,10 @@ import {
|
|
|
40
40
|
unwrapItems,
|
|
41
41
|
unwrapOperation,
|
|
42
42
|
vendorId
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-QQPCJT2L.js";
|
|
44
44
|
import {
|
|
45
45
|
CAPABILITIES
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-TZC4ZAA6.js";
|
|
47
47
|
|
|
48
48
|
// src/resources/appointments.ts
|
|
49
49
|
var AppointmentsResource = class extends BaseResource {
|
|
@@ -4832,6 +4832,42 @@ var ContractServiceUnitsResource = class extends BaseResource {
|
|
|
4832
4832
|
}
|
|
4833
4833
|
};
|
|
4834
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
|
+
|
|
4835
4871
|
// src/resources/special/contracts/profitability.constants.ts
|
|
4836
4872
|
var PROFITABILITY_OPT_KEYS = [
|
|
4837
4873
|
"dateFrom",
|
|
@@ -5301,7 +5337,7 @@ function appendWiresNote(wires, note) {
|
|
|
5301
5337
|
wires.note = (wires.note ? wires.note + " | " : "") + note;
|
|
5302
5338
|
}
|
|
5303
5339
|
function makeFetchCtx(rawHttp, v, contractId, budget) {
|
|
5304
|
-
const
|
|
5340
|
+
const OP11 = PROFITABILITY_OP;
|
|
5305
5341
|
const id = parseIdentifier(contractId, "contractId");
|
|
5306
5342
|
const idNum = typeof id === "number" ? id : Number(id);
|
|
5307
5343
|
const wires = { issued: 0, requests: 0, budget };
|
|
@@ -5313,19 +5349,19 @@ function makeFetchCtx(rawHttp, v, contractId, budget) {
|
|
|
5313
5349
|
} };
|
|
5314
5350
|
const readPart = (path, conditions, fields) => {
|
|
5315
5351
|
wires.issued += 1;
|
|
5316
|
-
return degradable(() => boundedPart(http, path, conditions, v.limit,
|
|
5352
|
+
return degradable(() => boundedPart(http, path, conditions, v.limit, OP11, fields));
|
|
5317
5353
|
};
|
|
5318
5354
|
const truncatedMonths = {};
|
|
5319
5355
|
const windowedPart = async (name, key, path, cond, fields, dateField) => {
|
|
5320
5356
|
const first = await readPart(path, cond, fields);
|
|
5321
5357
|
if (first.absent !== void 0 || !first.hasMore) return { part: stampPart(key, first, cond), rows: first.items };
|
|
5322
|
-
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));
|
|
5323
5359
|
if (!("truncatedMonths" in wp)) return { part: stampPart(key, wp, cond), rows: [] };
|
|
5324
5360
|
const { truncatedMonths: tm, ...part } = wp;
|
|
5325
5361
|
truncatedMonths[name] = tm;
|
|
5326
5362
|
return { part, rows: wp.items };
|
|
5327
5363
|
};
|
|
5328
|
-
return { v, OP:
|
|
5364
|
+
return { v, OP: OP11, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
|
|
5329
5365
|
}
|
|
5330
5366
|
|
|
5331
5367
|
// src/resources/special/contracts/profitability.stages.ts
|
|
@@ -6331,6 +6367,10 @@ var ContractsResource = class extends BaseResource {
|
|
|
6331
6367
|
profitability(id, opts) {
|
|
6332
6368
|
return profitability(this, id, opts, this.http);
|
|
6333
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
|
+
}
|
|
6334
6374
|
};
|
|
6335
6375
|
|
|
6336
6376
|
// src/resources/contract_ticket_purchases.ts
|
|
@@ -10693,22 +10733,22 @@ function uiLinkFor(http, entity, id) {
|
|
|
10693
10733
|
}
|
|
10694
10734
|
|
|
10695
10735
|
// src/resources/special/ticket_charges.ts
|
|
10696
|
-
var
|
|
10736
|
+
var OP4 = "ticket_charges.moveToTicket";
|
|
10697
10737
|
var COPY = ["name", "description", "chargeType", "billingCodeID", "productID", "unitQuantity", "unitCost", "unitPrice", "datePurchased", "isBillableToCompany", "notes", "purchaseOrderNumber", "internalPurchaseOrderNumber"];
|
|
10698
10738
|
var CONTRACT_SCOPED = ["contractServiceID", "contractServiceBundleID"];
|
|
10699
10739
|
async function readCharge(http, id, retries) {
|
|
10700
|
-
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);
|
|
10701
10741
|
return rows[0] ?? null;
|
|
10702
10742
|
}
|
|
10703
10743
|
async function checkSource({ http, source, sourceId, sourceTicketId, toTicketId }) {
|
|
10704
10744
|
if (source.isBilled === true) {
|
|
10705
|
-
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, {
|
|
10706
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)" }
|
|
10707
10747
|
});
|
|
10708
10748
|
}
|
|
10709
10749
|
}
|
|
10710
10750
|
var SPEC = {
|
|
10711
|
-
operation:
|
|
10751
|
+
operation: OP4,
|
|
10712
10752
|
entity: "ticket_charge",
|
|
10713
10753
|
readSource: readCharge,
|
|
10714
10754
|
sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
|
|
@@ -12063,7 +12103,12 @@ async function addCompositeNames(http, ctx, entriesPart, result, opts) {
|
|
|
12063
12103
|
builder.addFk("contract", t["contractID"]);
|
|
12064
12104
|
builder.addFk("billingCode", t["billingCodeID"]);
|
|
12065
12105
|
builder.addFk("role", t["assignedResourceRoleID"]);
|
|
12066
|
-
|
|
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
|
+
}
|
|
12067
12112
|
for (const e of entriesPart.items) {
|
|
12068
12113
|
builder.addResource(e["resourceID"]);
|
|
12069
12114
|
builder.addFk("role", e["roleID"]);
|
|
@@ -12157,16 +12202,16 @@ async function fullContext(resource, id, opts = {}, http) {
|
|
|
12157
12202
|
}
|
|
12158
12203
|
|
|
12159
12204
|
// src/resources/special/tickets/forResource.ts
|
|
12160
|
-
var
|
|
12205
|
+
var OP5 = "tickets.forResource";
|
|
12161
12206
|
var TICKET_CHUNK = 100;
|
|
12162
12207
|
var LIMIT_DEFAULT = 100;
|
|
12163
12208
|
var LIMIT_MAX = 500;
|
|
12164
12209
|
var LINK_READ_CAP = 500;
|
|
12165
12210
|
var TICKET_FIELDS2 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
|
|
12166
|
-
function
|
|
12211
|
+
function positiveId2(value, name) {
|
|
12167
12212
|
const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
|
|
12168
12213
|
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
|
|
12169
|
-
throw new AutotaskConfigError(`${
|
|
12214
|
+
throw new AutotaskConfigError(`${OP5}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP5 });
|
|
12170
12215
|
}
|
|
12171
12216
|
return n;
|
|
12172
12217
|
}
|
|
@@ -12176,28 +12221,28 @@ async function ticketPage(http, filter, maxRecords, fields) {
|
|
|
12176
12221
|
idempotent: true,
|
|
12177
12222
|
path: "/V1.0/Tickets/query",
|
|
12178
12223
|
body: { MaxRecords: maxRecords, filter, IncludeFields: fields },
|
|
12179
|
-
operation:
|
|
12224
|
+
operation: OP5
|
|
12180
12225
|
});
|
|
12181
|
-
const page = unwrapItems(res,
|
|
12226
|
+
const page = unwrapItems(res, OP5);
|
|
12182
12227
|
return { rows: page.items, more: page.pageDetails.nextPageUrl !== null };
|
|
12183
12228
|
}
|
|
12184
12229
|
async function forResource(_resource, resourceIDIn, http, opts = {}) {
|
|
12185
|
-
if (opts === null || typeof opts !== "object") throw new AutotaskConfigError(`${
|
|
12186
|
-
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");
|
|
12187
12232
|
const scope = opts.scope ?? "both";
|
|
12188
12233
|
if (scope !== "primary" && scope !== "secondary" && scope !== "both") {
|
|
12189
|
-
throw new AutotaskConfigError(`${
|
|
12234
|
+
throw new AutotaskConfigError(`${OP5}: scope must be 'primary', 'secondary' or 'both' (got ${JSON.stringify(opts.scope)})`, { operation: OP5 });
|
|
12190
12235
|
}
|
|
12191
12236
|
if (opts.openOnly !== void 0 && typeof opts.openOnly !== "boolean") {
|
|
12192
|
-
throw new AutotaskConfigError(`${
|
|
12237
|
+
throw new AutotaskConfigError(`${OP5}: openOnly must be a boolean`, { operation: OP5 });
|
|
12193
12238
|
}
|
|
12194
12239
|
const limit = opts.limit === void 0 ? LIMIT_DEFAULT : opts.limit;
|
|
12195
12240
|
if (!Number.isInteger(limit) || limit < 1 || limit > LIMIT_MAX) {
|
|
12196
|
-
throw new AutotaskConfigError(`${
|
|
12241
|
+
throw new AutotaskConfigError(`${OP5}: limit must be an integer 1-${LIMIT_MAX} (got ${String(opts.limit)})`, { operation: OP5 });
|
|
12197
12242
|
}
|
|
12198
12243
|
const includeFields = opts.includeFields ?? [];
|
|
12199
12244
|
if (!Array.isArray(includeFields) || includeFields.some((f) => typeof f !== "string" || f.length === 0)) {
|
|
12200
|
-
throw new AutotaskConfigError(`${
|
|
12245
|
+
throw new AutotaskConfigError(`${OP5}: includeFields must be a list of field names`, { operation: OP5 });
|
|
12201
12246
|
}
|
|
12202
12247
|
const openOnly = opts.openOnly === true;
|
|
12203
12248
|
const fields = [.../* @__PURE__ */ new Set([...TICKET_FIELDS2, ...includeFields])];
|
|
@@ -12212,9 +12257,9 @@ async function forResource(_resource, resourceIDIn, http, opts = {}) {
|
|
|
12212
12257
|
idempotent: true,
|
|
12213
12258
|
path: "/V1.0/TicketSecondaryResources/query",
|
|
12214
12259
|
body: { MaxRecords: LINK_READ_CAP, filter: [{ field: "resourceID", op: "eq", value: resourceID }], IncludeFields: ["ticketID"] },
|
|
12215
|
-
operation:
|
|
12260
|
+
operation: OP5
|
|
12216
12261
|
});
|
|
12217
|
-
const page = unwrapItems(res,
|
|
12262
|
+
const page = unwrapItems(res, OP5);
|
|
12218
12263
|
for (const row of page.items) if (typeof row.ticketID === "number" && !linkIds.includes(row.ticketID)) linkIds.push(row.ticketID);
|
|
12219
12264
|
if (page.items.length >= LINK_READ_CAP || page.pageDetails.nextPageUrl !== null) capped = true;
|
|
12220
12265
|
}
|
|
@@ -12267,35 +12312,67 @@ async function forResource(_resource, resourceIDIn, http, opts = {}) {
|
|
|
12267
12312
|
}
|
|
12268
12313
|
|
|
12269
12314
|
// src/resources/special/tickets/lineOfBusiness.ts
|
|
12270
|
-
var
|
|
12271
|
-
function
|
|
12315
|
+
var OP6 = "tickets.lineOfBusiness";
|
|
12316
|
+
function positiveId3(value, name) {
|
|
12272
12317
|
const n = typeof value === "string" && /^[0-9]+$/.test(value.trim()) ? Number(value) : value;
|
|
12273
12318
|
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
|
|
12274
|
-
throw new AutotaskConfigError(
|
|
12319
|
+
throw new AutotaskConfigError(OP6 + ": " + name + " must be a positive integer id (got " + String(value) + ")", { operation: OP6 });
|
|
12275
12320
|
}
|
|
12276
12321
|
return n;
|
|
12277
12322
|
}
|
|
12278
|
-
async function
|
|
12279
|
-
const ticketId =
|
|
12323
|
+
async function lineOfBusiness2(resource, ticketIdIn, http, associationIdIn) {
|
|
12324
|
+
const ticketId = positiveId3(ticketIdIn, "ticketId");
|
|
12280
12325
|
let associationId;
|
|
12326
|
+
let associationSource = null;
|
|
12327
|
+
let contractId = null;
|
|
12328
|
+
let contractUnread = false;
|
|
12281
12329
|
if (associationIdIn !== void 0) {
|
|
12282
|
-
associationId =
|
|
12330
|
+
associationId = positiveId3(associationIdIn, "associationId");
|
|
12331
|
+
associationSource = "argument";
|
|
12283
12332
|
} else {
|
|
12284
12333
|
const ticket = await resource.get(ticketId);
|
|
12285
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
|
+
}
|
|
12286
12361
|
}
|
|
12287
12362
|
if (associationId === void 0) {
|
|
12288
12363
|
return {
|
|
12289
12364
|
ticketId,
|
|
12365
|
+
associationSource,
|
|
12366
|
+
contractId,
|
|
12290
12367
|
association: null,
|
|
12291
12368
|
organizationalLevel2: null,
|
|
12292
12369
|
organizationalLevel1: null,
|
|
12293
|
-
markers: { association: "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
|
|
12370
|
+
markers: { association: contractUnread ? "unresolved" : "missing", organizationalLevel2: "missing", organizationalLevel1: "missing" }
|
|
12294
12371
|
};
|
|
12295
12372
|
}
|
|
12296
|
-
const chain = await resolveLineOfBusinessChain(http, associationId, { budget: 3, operation:
|
|
12373
|
+
const chain = await resolveLineOfBusinessChain(http, associationId, { budget: 3, operation: OP6, onError: "propagate" });
|
|
12297
12374
|
const { association, organizationalLevel2, organizationalLevel1, markers } = chain.lineOfBusiness;
|
|
12298
|
-
return { ticketId, association, organizationalLevel2, organizationalLevel1, markers };
|
|
12375
|
+
return { ticketId, associationSource, contractId, association, organizationalLevel2, organizationalLevel1, markers };
|
|
12299
12376
|
}
|
|
12300
12377
|
|
|
12301
12378
|
// src/resources/special/tickets/statusSemantics.ts
|
|
@@ -12418,7 +12495,7 @@ var TicketsResource = class extends BaseResource {
|
|
|
12418
12495
|
}
|
|
12419
12496
|
/** Node-parity special op (hand-written in src/resources/special/tickets.ts). */
|
|
12420
12497
|
lineOfBusiness(ticketId, associationId) {
|
|
12421
|
-
return
|
|
12498
|
+
return lineOfBusiness2(this, ticketId, this.http, associationId);
|
|
12422
12499
|
}
|
|
12423
12500
|
};
|
|
12424
12501
|
|
|
@@ -12548,12 +12625,12 @@ async function postedDateStatusQuery(_resource, mode, http, opts) {
|
|
|
12548
12625
|
return items;
|
|
12549
12626
|
}
|
|
12550
12627
|
async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
|
|
12551
|
-
const
|
|
12552
|
-
if (opts?.tz !== void 0) validateZone(opts.tz,
|
|
12553
|
-
const fromB = customRangeDay(from, "from", void 0,
|
|
12554
|
-
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);
|
|
12555
12632
|
if (fromB.ms > toB.ms) {
|
|
12556
|
-
throw new AutotaskConfigError(`${
|
|
12633
|
+
throw new AutotaskConfigError(`${OP11}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP11 });
|
|
12557
12634
|
}
|
|
12558
12635
|
const windowToMs = toB.ms + DAY_MS2 - 1;
|
|
12559
12636
|
const filter = [
|
|
@@ -12564,7 +12641,7 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
|
|
|
12564
12641
|
else if (opts?.posted === false) filter.push({ field: "postedDate", op: "notExist" });
|
|
12565
12642
|
if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
|
|
12566
12643
|
if (opts?.resourceID !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
|
|
12567
|
-
const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit),
|
|
12644
|
+
const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP11, http);
|
|
12568
12645
|
let postedCount = 0;
|
|
12569
12646
|
for (const item of items) {
|
|
12570
12647
|
const postedDate = item.postedDate;
|
|
@@ -12774,7 +12851,7 @@ async function resolveCode(http, family, familyLabel2, pinnedId, departments, op
|
|
|
12774
12851
|
}
|
|
12775
12852
|
|
|
12776
12853
|
// src/resources/special/time_entries/createForResource.ts
|
|
12777
|
-
var
|
|
12854
|
+
var OP7 = "time_entries.createForResource";
|
|
12778
12855
|
var PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
12779
12856
|
"internallAllocationCodeID",
|
|
12780
12857
|
"ticketID",
|
|
@@ -12810,30 +12887,30 @@ function validateCreateParams(params) {
|
|
|
12810
12887
|
if (pinCount > 1) {
|
|
12811
12888
|
throw new AutotaskConfigError(
|
|
12812
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)",
|
|
12813
|
-
{ operation:
|
|
12890
|
+
{ operation: OP7 }
|
|
12814
12891
|
);
|
|
12815
12892
|
}
|
|
12816
12893
|
if (pins.role !== void 0 && pins.code !== void 0) {
|
|
12817
12894
|
throw new AutotaskConfigError(
|
|
12818
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",
|
|
12819
|
-
{ operation:
|
|
12896
|
+
{ operation: OP7 }
|
|
12820
12897
|
);
|
|
12821
12898
|
}
|
|
12822
12899
|
if (params.hoursWorked !== void 0 && (typeof params.hoursWorked !== "number" || !Number.isFinite(params.hoursWorked) || params.hoursWorked <= 0)) {
|
|
12823
12900
|
throw new AutotaskConfigError(
|
|
12824
12901
|
`createForResource: hoursWorked must be a positive finite number (got ${String(params.hoursWorked)})`,
|
|
12825
|
-
{ operation:
|
|
12902
|
+
{ operation: OP7 }
|
|
12826
12903
|
);
|
|
12827
12904
|
}
|
|
12828
12905
|
return pins;
|
|
12829
12906
|
}
|
|
12830
12907
|
async function assertResourceActive(http, rid) {
|
|
12831
|
-
const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`,
|
|
12908
|
+
const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP7);
|
|
12832
12909
|
if (rec === null) {
|
|
12833
12910
|
throw new ResourceInvalidError(`Resource ${rid} was not found \u2014 a time entry cannot attach to a missing resource`, {
|
|
12834
12911
|
resourceIds: [rid],
|
|
12835
12912
|
details: { idRefs: [{ entity: "resource", id: rid }] },
|
|
12836
|
-
operation:
|
|
12913
|
+
operation: OP7,
|
|
12837
12914
|
category: "not_found",
|
|
12838
12915
|
suggestedAction: "Resolve the resource id (resources.get / resources.search) and retry."
|
|
12839
12916
|
});
|
|
@@ -12842,7 +12919,7 @@ async function assertResourceActive(http, rid) {
|
|
|
12842
12919
|
throw new ResourceInvalidError(`Resource ${rid} is not active (isActive !== true) \u2014 the vendor would reject a time entry for an inactive resource`, {
|
|
12843
12920
|
resourceIds: [rid],
|
|
12844
12921
|
details: { idRefs: [{ entity: "resource", id: rid }] },
|
|
12845
|
-
operation:
|
|
12922
|
+
operation: OP7,
|
|
12846
12923
|
suggestedAction: "Activate the resource or pick an active one."
|
|
12847
12924
|
});
|
|
12848
12925
|
}
|
|
@@ -12852,11 +12929,11 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
|
|
|
12852
12929
|
const pinnedForCode = pins.code !== void 0 ? pins.code : pins.role;
|
|
12853
12930
|
let code = null;
|
|
12854
12931
|
const familiesTried = [];
|
|
12855
|
-
code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments,
|
|
12932
|
+
code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP7);
|
|
12856
12933
|
familiesTried.push({ family: familyOf(linkage), label: familyLabel(familyOf(linkage)), filters: [{ field: "useType", op: "eq", value: familyOf(linkage) }, { field: "isActive", op: "eq", value: true }] });
|
|
12857
12934
|
if (code === null && linkage === "task" && pins.role === void 0) {
|
|
12858
12935
|
usedLinkage = "internal";
|
|
12859
|
-
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);
|
|
12860
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 }] });
|
|
12861
12938
|
}
|
|
12862
12939
|
if (code === null) {
|
|
@@ -12866,7 +12943,7 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
|
|
|
12866
12943
|
families: familiesTried,
|
|
12867
12944
|
resourceDepartments: [...departments].sort((a, b) => a - b)
|
|
12868
12945
|
},
|
|
12869
|
-
{ operation:
|
|
12946
|
+
{ operation: OP7 }
|
|
12870
12947
|
);
|
|
12871
12948
|
}
|
|
12872
12949
|
const resolvedCode = codeInfo(code);
|
|
@@ -12905,7 +12982,7 @@ function createDryRun2(http, rid, body, linkage, usedLinkage, resolvedCode) {
|
|
|
12905
12982
|
}
|
|
12906
12983
|
return buildDryRunResult(
|
|
12907
12984
|
{
|
|
12908
|
-
operation:
|
|
12985
|
+
operation: OP7,
|
|
12909
12986
|
method: "POST",
|
|
12910
12987
|
path: "/V1.0/TimeEntries",
|
|
12911
12988
|
ids: [rid],
|
|
@@ -12947,7 +13024,7 @@ async function createWithAttempts(resource, http, opts, body, defaults, pins, us
|
|
|
12947
13024
|
const gate3 = ROLE_REJECTION_PATTERN.test(text);
|
|
12948
13025
|
if (usedLinkage !== "internal" && gate3) {
|
|
12949
13026
|
if (!taskRoleFetched && usedLinkage === "task" && pins.task !== void 0) {
|
|
12950
|
-
const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`,
|
|
13027
|
+
const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP7);
|
|
12951
13028
|
taskRoleFetched = true;
|
|
12952
13029
|
if (task !== null) pushLadder(task.assignedResourceRoleID);
|
|
12953
13030
|
}
|
|
@@ -12973,7 +13050,7 @@ async function createForResource(resource, resourceID, params, opts = {}, http)
|
|
|
12973
13050
|
const pins = validateCreateParams(params);
|
|
12974
13051
|
await assertResourceActive(http, rid);
|
|
12975
13052
|
const linkage = pins.code !== void 0 ? "internal" : pins.ticket !== void 0 ? "ticket" : pins.task !== void 0 ? "task" : "internal";
|
|
12976
|
-
const roleRows = await resourceRoleRows(http, rid,
|
|
13053
|
+
const roleRows = await resourceRoleRows(http, rid, OP7);
|
|
12977
13054
|
const departments = roleRows.departments;
|
|
12978
13055
|
const { usedLinkage, resolvedCode } = await resolveAllocationCode(http, rid, linkage, pins, departments);
|
|
12979
13056
|
const defaults = windowDefaults(params);
|
|
@@ -12996,12 +13073,12 @@ function discoveryField(err, body, defaults) {
|
|
|
12996
13073
|
}
|
|
12997
13074
|
|
|
12998
13075
|
// src/resources/special/time_entries/move.ts
|
|
12999
|
-
var
|
|
13076
|
+
var OP8 = "time_entries.moveToTicket";
|
|
13000
13077
|
var COPY2 = ["resourceID", "roleID", "billingCodeID", "dateWorked", "startDateTime", "endDateTime", "hoursWorked", "offsetHours", "summaryNotes", "internalNotes", "isNonBillable", "showOnInvoice"];
|
|
13001
13078
|
var CONTRACT_SCOPED2 = ["contractID", "contractServiceID", "contractServiceBundleID"];
|
|
13002
13079
|
async function readTimeEntry(http, id, retries) {
|
|
13003
13080
|
try {
|
|
13004
|
-
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 });
|
|
13005
13082
|
return res?.item ?? null;
|
|
13006
13083
|
} catch (e) {
|
|
13007
13084
|
if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
|
|
@@ -13011,27 +13088,27 @@ async function readTimeEntry(http, id, retries) {
|
|
|
13011
13088
|
async function checkSource2({ http, source, sourceId, toTicketId, retries }) {
|
|
13012
13089
|
const ids2 = { sourceId, toTicketId };
|
|
13013
13090
|
if (source.taskID !== null && source.taskID !== void 0) {
|
|
13014
|
-
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);
|
|
13015
13092
|
}
|
|
13016
13093
|
if (source.billingApprovalDateTime || source.billingApprovalResourceID || source.billingApprovalLevelMostRecent) {
|
|
13017
|
-
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" } });
|
|
13018
13095
|
}
|
|
13019
13096
|
let billing;
|
|
13020
13097
|
try {
|
|
13021
|
-
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);
|
|
13022
13099
|
} catch {
|
|
13023
|
-
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" } });
|
|
13024
13101
|
}
|
|
13025
13102
|
if (billing.some((b) => b.invoiceID || b.postedDate)) {
|
|
13026
|
-
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" } });
|
|
13027
13104
|
}
|
|
13028
|
-
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);
|
|
13029
13106
|
if (attachments.length > 0) {
|
|
13030
|
-
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" } });
|
|
13031
13108
|
}
|
|
13032
13109
|
}
|
|
13033
13110
|
var SPEC2 = {
|
|
13034
|
-
operation:
|
|
13111
|
+
operation: OP8,
|
|
13035
13112
|
entity: "time_entry",
|
|
13036
13113
|
readSource: readTimeEntry,
|
|
13037
13114
|
sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
|
|
@@ -13066,7 +13143,7 @@ function moveToTicket2(_resource, sourceId, toTicketId, opts, http) {
|
|
|
13066
13143
|
}
|
|
13067
13144
|
|
|
13068
13145
|
// src/resources/special/time_entries/approve.ts
|
|
13069
|
-
var
|
|
13146
|
+
var OP9 = "time_entries.approve";
|
|
13070
13147
|
var APPROVAL_LEVELS_PATH = "/V1.0/BillingItemApprovalLevels";
|
|
13071
13148
|
function toIso(value) {
|
|
13072
13149
|
if (value instanceof Date) {
|
|
@@ -13104,7 +13181,7 @@ async function approve(resource, id, opts = {}, http) {
|
|
|
13104
13181
|
if (opts.dryRun === true) {
|
|
13105
13182
|
return buildDryRunResult(
|
|
13106
13183
|
{
|
|
13107
|
-
operation:
|
|
13184
|
+
operation: OP9,
|
|
13108
13185
|
method: "POST",
|
|
13109
13186
|
path: APPROVAL_LEVELS_PATH,
|
|
13110
13187
|
ids: [numericId],
|
|
@@ -13127,10 +13204,10 @@ async function approve(resource, id, opts = {}, http) {
|
|
|
13127
13204
|
method: "POST",
|
|
13128
13205
|
path: APPROVAL_LEVELS_PATH,
|
|
13129
13206
|
body,
|
|
13130
|
-
operation:
|
|
13207
|
+
operation: OP9,
|
|
13131
13208
|
maxRetries: opts.maxRetries
|
|
13132
13209
|
});
|
|
13133
|
-
const levelId = toNumber(parseIdentifier(unwrapOperation(created,
|
|
13210
|
+
const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP9).id, "billingItemApprovalLevelID"));
|
|
13134
13211
|
await resource.update(
|
|
13135
13212
|
numericId,
|
|
13136
13213
|
{ billingApprovalDateTime: approvalDateTime, billingApprovalResourceID: approvalResourceID },
|
|
@@ -13153,28 +13230,28 @@ async function approve(resource, id, opts = {}, http) {
|
|
|
13153
13230
|
}
|
|
13154
13231
|
|
|
13155
13232
|
// src/resources/special/time_entries/recentTickets.ts
|
|
13156
|
-
var
|
|
13233
|
+
var OP10 = "time_entries.recentTickets";
|
|
13157
13234
|
var DAY_MS3 = 864e5;
|
|
13158
13235
|
var TICKET_CHUNK2 = 100;
|
|
13159
13236
|
var TICKET_FIELDS3 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
|
|
13160
|
-
function
|
|
13237
|
+
function positiveId4(value, name) {
|
|
13161
13238
|
const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
|
|
13162
13239
|
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
|
|
13163
|
-
throw new AutotaskConfigError(`${
|
|
13240
|
+
throw new AutotaskConfigError(`${OP10}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP10 });
|
|
13164
13241
|
}
|
|
13165
13242
|
return n;
|
|
13166
13243
|
}
|
|
13167
13244
|
async function recentTickets(_resource, from, to, http, opts) {
|
|
13168
13245
|
if (opts === void 0 || opts === null || typeof opts !== "object") {
|
|
13169
|
-
throw new AutotaskConfigError(`${
|
|
13246
|
+
throw new AutotaskConfigError(`${OP10}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP10 });
|
|
13170
13247
|
}
|
|
13171
|
-
const resourceID =
|
|
13172
|
-
const companyID = opts.companyID === void 0 ? void 0 :
|
|
13173
|
-
if (opts.tz !== void 0) validateZone(opts.tz,
|
|
13174
|
-
const fromB = customRangeDay(from, "from", void 0,
|
|
13175
|
-
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);
|
|
13176
13253
|
if (fromB.ms > toB.ms) {
|
|
13177
|
-
throw new AutotaskConfigError(`${
|
|
13254
|
+
throw new AutotaskConfigError(`${OP10}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP10 });
|
|
13178
13255
|
}
|
|
13179
13256
|
const windowToMs = toB.ms + DAY_MS3 - 1;
|
|
13180
13257
|
const { items: entries, hasMore } = await queryTimeEntries(
|
|
@@ -13185,7 +13262,7 @@ async function recentTickets(_resource, from, to, http, opts) {
|
|
|
13185
13262
|
{ field: "resourceID", op: "eq", value: resourceID }
|
|
13186
13263
|
],
|
|
13187
13264
|
clampWindowLimit(opts.limit),
|
|
13188
|
-
|
|
13265
|
+
OP10,
|
|
13189
13266
|
http
|
|
13190
13267
|
);
|
|
13191
13268
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -13211,9 +13288,9 @@ async function recentTickets(_resource, from, to, http, opts) {
|
|
|
13211
13288
|
idempotent: true,
|
|
13212
13289
|
path: "/V1.0/Tickets/query",
|
|
13213
13290
|
body: { MaxRecords: TICKET_CHUNK2, filter, IncludeFields: TICKET_FIELDS3 },
|
|
13214
|
-
operation:
|
|
13291
|
+
operation: OP10
|
|
13215
13292
|
});
|
|
13216
|
-
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);
|
|
13217
13294
|
}
|
|
13218
13295
|
const items = [];
|
|
13219
13296
|
const unresolvedTicketIDs = [];
|