@pipedream/harvest 0.0.6 → 1.0.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/README.md +7 -12
- package/actions/create-invoice/create-invoice.mjs +116 -0
- package/actions/create-project/create-project.mjs +138 -0
- package/actions/create-task-assignment/create-task-assignment.mjs +77 -0
- package/actions/create-timesheet-entry/create-timesheet-entry.mjs +39 -38
- package/actions/create-user/create-user.mjs +109 -0
- package/actions/create-user-assignment/create-user-assignment.mjs +85 -0
- package/actions/delete-invoice/delete-invoice.mjs +38 -0
- package/actions/delete-project/delete-project.mjs +38 -0
- package/actions/delete-task-assignment/delete-task-assignment.mjs +45 -0
- package/actions/delete-time-entry/delete-time-entry.mjs +38 -0
- package/actions/delete-user/delete-user.mjs +39 -0
- package/actions/delete-user-assignment/delete-user-assignment.mjs +45 -0
- package/actions/get-invoice/get-invoice.mjs +39 -0
- package/actions/get-me/get-me.mjs +32 -0
- package/actions/get-project/get-project.mjs +39 -0
- package/actions/get-projects/get-projects.mjs +20 -12
- package/actions/get-task-assignment/get-task-assignment.mjs +46 -0
- package/actions/get-time-entry/get-time-entry.mjs +39 -0
- package/actions/get-time-report/get-time-report.mjs +80 -0
- package/actions/get-user/get-user.mjs +40 -0
- package/actions/get-user-assignment/get-user-assignment.mjs +46 -0
- package/actions/list-account-id-options/list-account-id-options.mjs +33 -0
- package/actions/list-clients/list-clients.mjs +57 -0
- package/actions/list-invoices/list-invoices.mjs +86 -0
- package/actions/list-task-assignments/list-task-assignments.mjs +66 -0
- package/actions/list-tasks/list-tasks.mjs +57 -0
- package/actions/list-time-entries/list-time-entries.mjs +150 -0
- package/actions/list-user-assignments/list-user-assignments.mjs +74 -0
- package/actions/list-users/list-users.mjs +57 -0
- package/actions/start-timer/start-timer.mjs +9 -6
- package/actions/stop-timer/stop-timer.mjs +9 -6
- package/actions/update-invoice/update-invoice.mjs +114 -0
- package/actions/update-project/update-project.mjs +140 -0
- package/actions/update-task-assignment/update-task-assignment.mjs +77 -0
- package/actions/update-time-entry/update-time-entry.mjs +92 -0
- package/actions/update-user/update-user.mjs +106 -0
- package/actions/update-user-assignment/update-user-assignment.mjs +84 -0
- package/common/constants.mjs +51 -1
- package/common/utils.mjs +50 -1
- package/harvest.app.mjs +683 -110
- package/package.json +3 -3
- package/sources/new-invoice-entry/new-invoice-entry.mjs +1 -1
- package/sources/new-timesheet-entry/new-timesheet-entry.mjs +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
in Harvest, a web-based time tracking application. With the API, developers can
|
|
5
|
-
create applications that submit and retrieve time tracking data, as well as
|
|
6
|
-
automate various aspects of the Harvest user experience.
|
|
3
|
+
Harvest is a time tracking and invoicing tool that can streamline the way freelancers and businesses record time for various projects and tasks. By leveraging the Harvest API on Pipedream, you can automate complex workflows that integrate time tracking data with other business tools. Generate reports, sync project data, and manage invoices with minimal manual intervention. Pipedream's serverless platform lets you create these automations using simple, code-driven components, enabling a seamless connection between Harvest and a multitude of other apps.
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
# Example Use Cases
|
|
9
6
|
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
in time tracking data from Harvest to provide a more complete picture of
|
|
16
|
-
project progress
|
|
7
|
+
- **Automated Invoicing Workflow**: Create an automation that triggers at the end of each billing cycle. It compiles timesheet data from Harvest and generates invoices automatically. Then, it sends these invoices to clients via email using a service like SendGrid or directly through accounting software like QuickBooks.
|
|
8
|
+
|
|
9
|
+
- **Slack Time Tracking Reminders**: Set up a Pipedream workflow that sends reminders to a Slack channel or directly to team members. Reminders prompt users to submit their timesheets if they haven't been completed by a certain time each day or week. The Harvest API checks for unsubmitted timesheets and triggers the Slack notifications accordingly.
|
|
10
|
+
|
|
11
|
+
- **Project Management Sync**: Build a workflow that synchronizes project and task updates between Harvest and a project management tool such as Trello or Asana. When a new project is created or a task is updated in Harvest, the corresponding card or task in the project management app is created or updated, keeping both systems in sync and up-to-date.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import harvest from "../../harvest.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "harvest-create-invoice",
|
|
5
|
+
name: "Create Invoice",
|
|
6
|
+
description: "Create a new free-form invoice for a client. Provide Line Items as a JSON array to itemize the invoice. Example: call with clientId set to InGen Corp's client ID, subject=\"Consulting Services\", lineItems=`[{\"kind\":\"Service\",\"description\":\"Genetics consulting\",\"unit_price\":2000,\"quantity\":1}]`. [See the documentation](https://help.getharvest.com/api-v2/invoices-api/invoices/invoices/#create-a-free-form-invoice).",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
ai: "optimized",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
harvest,
|
|
17
|
+
accountId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
harvest,
|
|
20
|
+
"accountId",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
clientId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
harvest,
|
|
26
|
+
"clientId",
|
|
27
|
+
],
|
|
28
|
+
optional: false,
|
|
29
|
+
},
|
|
30
|
+
subject: {
|
|
31
|
+
propDefinition: [
|
|
32
|
+
harvest,
|
|
33
|
+
"subject",
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
number: {
|
|
37
|
+
propDefinition: [
|
|
38
|
+
harvest,
|
|
39
|
+
"invoiceNumber",
|
|
40
|
+
],
|
|
41
|
+
description: "The invoice number, e.g. `1000`. Defaults to the next sequential number if omitted.",
|
|
42
|
+
},
|
|
43
|
+
purchaseOrder: {
|
|
44
|
+
propDefinition: [
|
|
45
|
+
harvest,
|
|
46
|
+
"purchaseOrder",
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
tax: {
|
|
50
|
+
propDefinition: [
|
|
51
|
+
harvest,
|
|
52
|
+
"tax",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
discount: {
|
|
56
|
+
propDefinition: [
|
|
57
|
+
harvest,
|
|
58
|
+
"discount",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
notes: {
|
|
62
|
+
propDefinition: [
|
|
63
|
+
harvest,
|
|
64
|
+
"invoiceNotes",
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
currency: {
|
|
68
|
+
type: "string",
|
|
69
|
+
label: "Currency",
|
|
70
|
+
description: "The currency code, e.g. `USD`.",
|
|
71
|
+
optional: true,
|
|
72
|
+
},
|
|
73
|
+
issueDate: {
|
|
74
|
+
propDefinition: [
|
|
75
|
+
harvest,
|
|
76
|
+
"issueDate",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
dueDate: {
|
|
80
|
+
propDefinition: [
|
|
81
|
+
harvest,
|
|
82
|
+
"dueDate",
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
lineItems: {
|
|
86
|
+
type: "string",
|
|
87
|
+
label: "Line Items",
|
|
88
|
+
description: "JSON array of line item objects. Example: `[{\"kind\":\"Service\",\"description\":\"Consulting\",\"unit_price\":100,\"quantity\":5}]`. Each item requires `kind` and `unit_price`.",
|
|
89
|
+
optional: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
async run({ $ }) {
|
|
93
|
+
const lineItems = this.lineItems
|
|
94
|
+
? JSON.parse(this.lineItems)
|
|
95
|
+
: undefined;
|
|
96
|
+
const response = await this.harvest.createInvoice({
|
|
97
|
+
$,
|
|
98
|
+
accountId: this.accountId,
|
|
99
|
+
data: {
|
|
100
|
+
client_id: this.clientId,
|
|
101
|
+
subject: this.subject,
|
|
102
|
+
number: this.number,
|
|
103
|
+
purchase_order: this.purchaseOrder,
|
|
104
|
+
tax: this.tax,
|
|
105
|
+
discount: this.discount,
|
|
106
|
+
notes: this.notes,
|
|
107
|
+
currency: this.currency,
|
|
108
|
+
issue_date: this.issueDate,
|
|
109
|
+
due_date: this.dueDate,
|
|
110
|
+
line_items: lineItems,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
$.export("$summary", `Successfully created invoice ${response.id}`);
|
|
114
|
+
return response;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import harvest from "../../harvest.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "harvest-create-project",
|
|
5
|
+
name: "Create Project",
|
|
6
|
+
description: "Create a new project. Example: call with clientId set to InGen Corp's client ID, projectName=\"Compound Perimeter Fence Upgrade\", isBillable=true, billBy=\"Project\", budgetBy=\"none\". [See the documentation](https://help.getharvest.com/api-v2/projects-api/projects/projects/#create-a-project).",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
ai: "optimized",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
harvest,
|
|
17
|
+
accountId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
harvest,
|
|
20
|
+
"accountId",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
clientId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
harvest,
|
|
26
|
+
"clientId",
|
|
27
|
+
],
|
|
28
|
+
optional: false,
|
|
29
|
+
},
|
|
30
|
+
projectName: {
|
|
31
|
+
propDefinition: [
|
|
32
|
+
harvest,
|
|
33
|
+
"projectName",
|
|
34
|
+
],
|
|
35
|
+
optional: false,
|
|
36
|
+
},
|
|
37
|
+
isBillable: {
|
|
38
|
+
propDefinition: [
|
|
39
|
+
harvest,
|
|
40
|
+
"isBillable",
|
|
41
|
+
],
|
|
42
|
+
optional: false,
|
|
43
|
+
},
|
|
44
|
+
billBy: {
|
|
45
|
+
propDefinition: [
|
|
46
|
+
harvest,
|
|
47
|
+
"billBy",
|
|
48
|
+
],
|
|
49
|
+
optional: false,
|
|
50
|
+
},
|
|
51
|
+
budgetBy: {
|
|
52
|
+
propDefinition: [
|
|
53
|
+
harvest,
|
|
54
|
+
"budgetBy",
|
|
55
|
+
],
|
|
56
|
+
optional: false,
|
|
57
|
+
},
|
|
58
|
+
code: {
|
|
59
|
+
propDefinition: [
|
|
60
|
+
harvest,
|
|
61
|
+
"code",
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
isActive: {
|
|
65
|
+
propDefinition: [
|
|
66
|
+
harvest,
|
|
67
|
+
"isActive",
|
|
68
|
+
],
|
|
69
|
+
description: "Whether the project is active.",
|
|
70
|
+
},
|
|
71
|
+
isFixedFee: {
|
|
72
|
+
propDefinition: [
|
|
73
|
+
harvest,
|
|
74
|
+
"isFixedFee",
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
hourlyRate: {
|
|
78
|
+
propDefinition: [
|
|
79
|
+
harvest,
|
|
80
|
+
"projectHourlyRate",
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
budget: {
|
|
84
|
+
propDefinition: [
|
|
85
|
+
harvest,
|
|
86
|
+
"projectBudget",
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
costBudget: {
|
|
90
|
+
propDefinition: [
|
|
91
|
+
harvest,
|
|
92
|
+
"costBudget",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
notes: {
|
|
96
|
+
propDefinition: [
|
|
97
|
+
harvest,
|
|
98
|
+
"projectNotes",
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
startsOn: {
|
|
102
|
+
propDefinition: [
|
|
103
|
+
harvest,
|
|
104
|
+
"startsOn",
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
endsOn: {
|
|
108
|
+
propDefinition: [
|
|
109
|
+
harvest,
|
|
110
|
+
"endsOn",
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
async run({ $ }) {
|
|
115
|
+
const response = await this.harvest.createProject({
|
|
116
|
+
$,
|
|
117
|
+
accountId: this.accountId,
|
|
118
|
+
data: {
|
|
119
|
+
client_id: this.clientId,
|
|
120
|
+
name: this.projectName,
|
|
121
|
+
is_billable: this.isBillable,
|
|
122
|
+
bill_by: this.billBy,
|
|
123
|
+
budget_by: this.budgetBy,
|
|
124
|
+
code: this.code,
|
|
125
|
+
is_active: this.isActive,
|
|
126
|
+
is_fixed_fee: this.isFixedFee,
|
|
127
|
+
hourly_rate: this.hourlyRate,
|
|
128
|
+
budget: this.budget,
|
|
129
|
+
cost_budget: this.costBudget,
|
|
130
|
+
notes: this.notes,
|
|
131
|
+
starts_on: this.startsOn,
|
|
132
|
+
ends_on: this.endsOn,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
$.export("$summary", `Successfully created project ${response.id}: ${response.name}`);
|
|
136
|
+
return response;
|
|
137
|
+
},
|
|
138
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import harvest from "../../harvest.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "harvest-create-task-assignment",
|
|
5
|
+
name: "Create Task Assignment",
|
|
6
|
+
description: "Assign a task to a project. Use **Get Projects** to find a project ID and **List Tasks** to find a task ID. Example: call with projectId set to the Jurassic Park Construction project's ID and taskId set to the Genetics Research task's ID to put that task on the project. [See the documentation](https://help.getharvest.com/api-v2/projects-api/projects/task-assignments/#create-a-task-assignment).",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
ai: "optimized",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
harvest,
|
|
17
|
+
accountId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
harvest,
|
|
20
|
+
"accountId",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
projectId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
harvest,
|
|
26
|
+
"projectId",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
taskId: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
harvest,
|
|
32
|
+
"taskId",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
isActive: {
|
|
36
|
+
propDefinition: [
|
|
37
|
+
harvest,
|
|
38
|
+
"isActive",
|
|
39
|
+
],
|
|
40
|
+
description: "Whether the task assignment is active.",
|
|
41
|
+
},
|
|
42
|
+
billable: {
|
|
43
|
+
propDefinition: [
|
|
44
|
+
harvest,
|
|
45
|
+
"billable",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
hourlyRate: {
|
|
49
|
+
propDefinition: [
|
|
50
|
+
harvest,
|
|
51
|
+
"taskAssignmentHourlyRate",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
budget: {
|
|
55
|
+
propDefinition: [
|
|
56
|
+
harvest,
|
|
57
|
+
"taskAssignmentBudget",
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
async run({ $ }) {
|
|
62
|
+
const response = await this.harvest.createTaskAssignment({
|
|
63
|
+
$,
|
|
64
|
+
projectId: this.projectId,
|
|
65
|
+
accountId: this.accountId,
|
|
66
|
+
data: {
|
|
67
|
+
task_id: this.taskId,
|
|
68
|
+
is_active: this.isActive,
|
|
69
|
+
billable: this.billable,
|
|
70
|
+
hourly_rate: this.hourlyRate,
|
|
71
|
+
budget: this.budget,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
$.export("$summary", `Successfully created task assignment ${response.id}`);
|
|
75
|
+
return response;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -7,11 +7,15 @@ import {
|
|
|
7
7
|
export default {
|
|
8
8
|
key: "harvest-create-timesheet-entry",
|
|
9
9
|
name: "Create Timesheet Entry",
|
|
10
|
-
description:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
description: "Create a new time entry. Two ways to log time, matching the account's Company setting: on **duration-based** accounts (`wants_timestamp_timers` is `false` — the common case) set **Hours** to log a completed entry directly. On **timestamp-based** accounts (`wants_timestamp_timers` is `true`) leave **Hours** blank and use **Started Time**/**Ended Time** instead — leave both time fields blank too to start a running timer now, or set both to log a completed entry with explicit start/end times. Passing Started/Ended Time on a duration-based account is silently ignored by Harvest (it starts a running timer instead) — if that happens, switch to **Hours**. Use **Get Projects** to find a Project ID, **List Tasks** to find a Task ID, and **List Users** to find a User ID. Example (duration-based): call with projectId, taskId, spentDate=\"2026-09-17\", hours=3.5. Example (timestamp-based): the same call with hours omitted and startedTime/endedTime set instead. [See the documentation](https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#create-a-time-entry-via-duration) and [start/end time variant](https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#create-a-time-entry-via-start-and-end-time).",
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
annotations: {
|
|
13
|
+
destructiveHint: false,
|
|
14
|
+
openWorldHint: true,
|
|
15
|
+
readOnlyHint: false,
|
|
16
|
+
},
|
|
14
17
|
type: "action",
|
|
18
|
+
ai: "optimized",
|
|
15
19
|
props: {
|
|
16
20
|
harvest,
|
|
17
21
|
accountId: {
|
|
@@ -24,57 +28,43 @@ export default {
|
|
|
24
28
|
propDefinition: [
|
|
25
29
|
harvest,
|
|
26
30
|
"projectId",
|
|
27
|
-
(c) => ({
|
|
28
|
-
accountId: c.accountId,
|
|
29
|
-
}),
|
|
30
31
|
],
|
|
31
32
|
},
|
|
32
33
|
taskId: {
|
|
33
34
|
propDefinition: [
|
|
34
35
|
harvest,
|
|
35
36
|
"taskId",
|
|
36
|
-
(c) => ({
|
|
37
|
-
accountId: c.accountId,
|
|
38
|
-
}),
|
|
39
37
|
],
|
|
40
38
|
},
|
|
41
39
|
spentDate: {
|
|
42
40
|
type: "string",
|
|
43
41
|
label: "Spent date (YYYY-MM-DD)",
|
|
44
|
-
description: "The ISO 8601 formatted date on which the time entry was spent.
|
|
42
|
+
description: "The ISO 8601 formatted date on which the time entry was spent, e.g. `2019-07-26`.",
|
|
45
43
|
},
|
|
46
44
|
userId: {
|
|
47
45
|
propDefinition: [
|
|
48
46
|
harvest,
|
|
49
47
|
"userId",
|
|
50
|
-
(c) => ({
|
|
51
|
-
accountId: c.accountId,
|
|
52
|
-
}),
|
|
53
48
|
],
|
|
54
49
|
},
|
|
55
|
-
|
|
56
|
-
type: "
|
|
57
|
-
label: "
|
|
58
|
-
description: "
|
|
59
|
-
|
|
50
|
+
hours: {
|
|
51
|
+
type: "string",
|
|
52
|
+
label: "Hours",
|
|
53
|
+
description: "Duration in hours, e.g. `3.5`. Use this on duration-based accounts (the common case) to log a completed entry in one call. Leave blank if using **Started Time**/**Ended Time** instead.",
|
|
54
|
+
optional: true,
|
|
55
|
+
},
|
|
56
|
+
startedTime: {
|
|
57
|
+
type: "string",
|
|
58
|
+
label: "Started time (H:MM am/pm)",
|
|
59
|
+
description: "Timestamp-based accounts only. The time the entry started, e.g. `8:00am`. Leave this and **Ended Time** blank to start a running timer now instead of logging a completed entry.",
|
|
60
|
+
optional: true,
|
|
61
|
+
},
|
|
62
|
+
endedTime: {
|
|
63
|
+
type: "string",
|
|
64
|
+
label: "Ended time (H:MM am/pm)",
|
|
65
|
+
description: "Timestamp-based accounts only. The time the entry ended, e.g. `5:00pm`. Only used when **Started Time** is also set.",
|
|
66
|
+
optional: true,
|
|
60
67
|
},
|
|
61
|
-
},
|
|
62
|
-
async additionalProps() {
|
|
63
|
-
const props = {};
|
|
64
|
-
if (this.specifyStartEndTime === true) {
|
|
65
|
-
props.startedTime = {
|
|
66
|
-
type: "string",
|
|
67
|
-
label: "Start time(H:MM am/pm)",
|
|
68
|
-
description: "The time the entry started. Defaults to the current time. Example: 8:00am.",
|
|
69
|
-
};
|
|
70
|
-
props.endedTime = {
|
|
71
|
-
type: "string",
|
|
72
|
-
label: "End time(H:MM am/pm)",
|
|
73
|
-
description: "The time the entry ended. Defaults to the current time. Example: 8:00am.",
|
|
74
|
-
optional: true,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
return props;
|
|
78
68
|
},
|
|
79
69
|
async run({ $ }) {
|
|
80
70
|
|
|
@@ -90,17 +80,28 @@ export default {
|
|
|
90
80
|
throw new ConfigurationError("Invalid end time. Ensure format is (H:MM am/pm)");
|
|
91
81
|
}
|
|
92
82
|
|
|
93
|
-
|
|
83
|
+
if (this.endedTime && !this.startedTime) {
|
|
84
|
+
throw new ConfigurationError("Started Time is required when Ended Time is set");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.hours && (this.startedTime || this.endedTime)) {
|
|
88
|
+
throw new ConfigurationError("Set either Hours (duration-based accounts) or Started/Ended Time (timestamp-based accounts), not both");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const data = removeNullEntries({
|
|
94
92
|
project_id: this.projectId,
|
|
95
93
|
task_id: this.taskId,
|
|
96
94
|
user_id: this.userId,
|
|
97
95
|
spent_date: this.spentDate,
|
|
96
|
+
hours: this.hours !== undefined && this.hours !== null && this.hours !== ""
|
|
97
|
+
? Number(this.hours)
|
|
98
|
+
: undefined,
|
|
98
99
|
started_time: this.startedTime?.replace(/\s/g, ""),
|
|
99
100
|
ended_time: this.endedTime?.replace(/\s/g, ""),
|
|
100
101
|
});
|
|
101
102
|
const response = await this.harvest.createTimeEntry({
|
|
102
103
|
$,
|
|
103
|
-
|
|
104
|
+
data,
|
|
104
105
|
accountId: this.accountId,
|
|
105
106
|
});
|
|
106
107
|
response && $.export("$summary", "Successfully created time entry");
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import harvest from "../../harvest.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "harvest-create-user",
|
|
5
|
+
name: "Create User",
|
|
6
|
+
description: "Create a new user in the Harvest account. Example: call with firstName=\"Ian\", lastName=\"Malcolm\", email=\"ian.malcolm@example.com\" to add a new team member. [See the documentation](https://help.getharvest.com/api-v2/users-api/users/users/#create-a-user).",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
ai: "optimized",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
harvest,
|
|
17
|
+
accountId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
harvest,
|
|
20
|
+
"accountId",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
firstName: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
harvest,
|
|
26
|
+
"firstName",
|
|
27
|
+
],
|
|
28
|
+
optional: false,
|
|
29
|
+
},
|
|
30
|
+
lastName: {
|
|
31
|
+
propDefinition: [
|
|
32
|
+
harvest,
|
|
33
|
+
"lastName",
|
|
34
|
+
],
|
|
35
|
+
optional: false,
|
|
36
|
+
},
|
|
37
|
+
email: {
|
|
38
|
+
propDefinition: [
|
|
39
|
+
harvest,
|
|
40
|
+
"email",
|
|
41
|
+
],
|
|
42
|
+
optional: false,
|
|
43
|
+
},
|
|
44
|
+
timezone: {
|
|
45
|
+
propDefinition: [
|
|
46
|
+
harvest,
|
|
47
|
+
"timezone",
|
|
48
|
+
],
|
|
49
|
+
description: "The user's IANA timezone name, e.g. `America/Chicago`. Defaults to the company timezone if omitted.",
|
|
50
|
+
},
|
|
51
|
+
isContractor: {
|
|
52
|
+
propDefinition: [
|
|
53
|
+
harvest,
|
|
54
|
+
"isContractor",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
isActive: {
|
|
58
|
+
propDefinition: [
|
|
59
|
+
harvest,
|
|
60
|
+
"isActive",
|
|
61
|
+
],
|
|
62
|
+
description: "Whether the user is active.",
|
|
63
|
+
},
|
|
64
|
+
weeklyCapacity: {
|
|
65
|
+
propDefinition: [
|
|
66
|
+
harvest,
|
|
67
|
+
"weeklyCapacity",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
defaultHourlyRate: {
|
|
71
|
+
propDefinition: [
|
|
72
|
+
harvest,
|
|
73
|
+
"defaultHourlyRate",
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
costRate: {
|
|
77
|
+
type: "string",
|
|
78
|
+
label: "Cost Rate",
|
|
79
|
+
description: "Cost rate, decimal, e.g. `100` or `100.00`.",
|
|
80
|
+
optional: true,
|
|
81
|
+
},
|
|
82
|
+
accessRoles: {
|
|
83
|
+
propDefinition: [
|
|
84
|
+
harvest,
|
|
85
|
+
"accessRoles",
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
async run({ $ }) {
|
|
90
|
+
const response = await this.harvest.createUser({
|
|
91
|
+
$,
|
|
92
|
+
accountId: this.accountId,
|
|
93
|
+
data: {
|
|
94
|
+
first_name: this.firstName,
|
|
95
|
+
last_name: this.lastName,
|
|
96
|
+
email: this.email,
|
|
97
|
+
timezone: this.timezone,
|
|
98
|
+
is_contractor: this.isContractor,
|
|
99
|
+
is_active: this.isActive,
|
|
100
|
+
weekly_capacity: this.weeklyCapacity,
|
|
101
|
+
default_hourly_rate: this.defaultHourlyRate,
|
|
102
|
+
cost_rate: this.costRate,
|
|
103
|
+
access_roles: this.accessRoles,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
$.export("$summary", `Successfully created user ${response.id}: ${response.first_name} ${response.last_name}`);
|
|
107
|
+
return response;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import harvest from "../../harvest.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "harvest-create-user-assignment",
|
|
5
|
+
name: "Create User Assignment",
|
|
6
|
+
description: "Assign a user to a project. Use **Get Projects** to find a project ID, and **List Users** to find user IDs. Example: call with projectId and userId set to staff that person on the project. [See the documentation](https://help.getharvest.com/api-v2/projects-api/projects/user-assignments/#create-a-user-assignment).",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
ai: "optimized",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
harvest,
|
|
17
|
+
accountId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
harvest,
|
|
20
|
+
"accountId",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
projectId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
harvest,
|
|
26
|
+
"projectId",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
userId: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
harvest,
|
|
32
|
+
"userId",
|
|
33
|
+
],
|
|
34
|
+
optional: false,
|
|
35
|
+
},
|
|
36
|
+
isActive: {
|
|
37
|
+
propDefinition: [
|
|
38
|
+
harvest,
|
|
39
|
+
"isActive",
|
|
40
|
+
],
|
|
41
|
+
description: "Whether the user assignment is active.",
|
|
42
|
+
},
|
|
43
|
+
isProjectManager: {
|
|
44
|
+
propDefinition: [
|
|
45
|
+
harvest,
|
|
46
|
+
"isProjectManager",
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
useDefaultRates: {
|
|
50
|
+
propDefinition: [
|
|
51
|
+
harvest,
|
|
52
|
+
"useDefaultRates",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
hourlyRate: {
|
|
56
|
+
propDefinition: [
|
|
57
|
+
harvest,
|
|
58
|
+
"userAssignmentHourlyRate",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
budget: {
|
|
62
|
+
propDefinition: [
|
|
63
|
+
harvest,
|
|
64
|
+
"userAssignmentBudget",
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
async run({ $ }) {
|
|
69
|
+
const response = await this.harvest.createUserAssignment({
|
|
70
|
+
$,
|
|
71
|
+
projectId: this.projectId,
|
|
72
|
+
accountId: this.accountId,
|
|
73
|
+
data: {
|
|
74
|
+
user_id: this.userId,
|
|
75
|
+
is_active: this.isActive,
|
|
76
|
+
is_project_manager: this.isProjectManager,
|
|
77
|
+
use_default_rates: this.useDefaultRates,
|
|
78
|
+
hourly_rate: this.hourlyRate,
|
|
79
|
+
budget: this.budget,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
$.export("$summary", `Successfully created user assignment ${response.id}`);
|
|
83
|
+
return response;
|
|
84
|
+
},
|
|
85
|
+
};
|