getjobber-cli 1.0.0__py3-none-any.whl
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.
- getjobber_cli/__init__.py +5 -0
- getjobber_cli/__main__.py +6 -0
- getjobber_cli/api/__init__.py +0 -0
- getjobber_cli/api/client.py +149 -0
- getjobber_cli/api/mutations.py +251 -0
- getjobber_cli/api/queries.py +264 -0
- getjobber_cli/auth/__init__.py +0 -0
- getjobber_cli/auth/callback_server.py +187 -0
- getjobber_cli/auth/oauth.py +211 -0
- getjobber_cli/auth/token_manager.py +226 -0
- getjobber_cli/cli.py +115 -0
- getjobber_cli/commands/__init__.py +0 -0
- getjobber_cli/commands/auth_commands.py +184 -0
- getjobber_cli/commands/client_commands.py +335 -0
- getjobber_cli/commands/config_commands.py +87 -0
- getjobber_cli/commands/invoice_commands.py +192 -0
- getjobber_cli/commands/job_commands.py +250 -0
- getjobber_cli/commands/query_commands.py +70 -0
- getjobber_cli/commands/quote_commands.py +209 -0
- getjobber_cli/constants.py +56 -0
- getjobber_cli/utils/__init__.py +0 -0
- getjobber_cli/utils/config.py +147 -0
- getjobber_cli/utils/errors.py +112 -0
- getjobber_cli/utils/formatters.py +238 -0
- getjobber_cli-1.0.0.dist-info/METADATA +427 -0
- getjobber_cli-1.0.0.dist-info/RECORD +30 -0
- getjobber_cli-1.0.0.dist-info/WHEEL +5 -0
- getjobber_cli-1.0.0.dist-info/entry_points.txt +2 -0
- getjobber_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- getjobber_cli-1.0.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""GraphQL client for GetJobber API."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from gql import Client, gql
|
|
6
|
+
from gql.transport.requests import RequestsHTTPTransport
|
|
7
|
+
|
|
8
|
+
from getjobber_cli.constants import API_BASE_URL, DEFAULT_TIMEOUT
|
|
9
|
+
from getjobber_cli.utils.errors import GraphQLError, JobberAPIError, NotAuthenticatedError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_client(access_token: str, api_url: str = API_BASE_URL) -> Client:
|
|
13
|
+
"""Create configured GraphQL client.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
access_token: OAuth access token for authentication.
|
|
17
|
+
api_url: API endpoint URL.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Configured GQL Client instance.
|
|
21
|
+
|
|
22
|
+
Raises:
|
|
23
|
+
NotAuthenticatedError: If access token is not provided.
|
|
24
|
+
"""
|
|
25
|
+
if not access_token:
|
|
26
|
+
raise NotAuthenticatedError()
|
|
27
|
+
|
|
28
|
+
# Configure transport with authentication
|
|
29
|
+
# Jobber requires X-JOBBER-GRAPHQL-VERSION; pin to a known stable schema date
|
|
30
|
+
transport = RequestsHTTPTransport(
|
|
31
|
+
url=api_url,
|
|
32
|
+
headers={
|
|
33
|
+
"Authorization": f"Bearer {access_token}",
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
"X-JOBBER-GRAPHQL-VERSION": "2025-04-16",
|
|
36
|
+
},
|
|
37
|
+
timeout=DEFAULT_TIMEOUT,
|
|
38
|
+
verify=True,
|
|
39
|
+
retries=3,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Create client with schema introspection
|
|
43
|
+
client = Client(
|
|
44
|
+
transport=transport,
|
|
45
|
+
fetch_schema_from_transport=False, # Disable for now, can be enabled later
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return client
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def execute_query(client: Client, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict:
|
|
52
|
+
"""Execute a GraphQL query.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
client: GQL Client instance.
|
|
56
|
+
query: GraphQL query string.
|
|
57
|
+
variables: Optional query variables.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Query result dictionary.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
GraphQLError: If query execution fails.
|
|
64
|
+
JobberAPIError: If API returns an error.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
# Parse query
|
|
68
|
+
parsed_query = gql(query)
|
|
69
|
+
|
|
70
|
+
# Execute query
|
|
71
|
+
result = client.execute(parsed_query, variable_values=variables)
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
error_message = str(e)
|
|
76
|
+
|
|
77
|
+
# Check if it's a GraphQL error with structured errors
|
|
78
|
+
if hasattr(e, "errors") and e.errors:
|
|
79
|
+
raise GraphQLError("GraphQL query failed", errors=e.errors)
|
|
80
|
+
|
|
81
|
+
# Check for authentication errors
|
|
82
|
+
if "401" in error_message or "Unauthorized" in error_message:
|
|
83
|
+
raise NotAuthenticatedError("Authentication failed. Please login again.")
|
|
84
|
+
|
|
85
|
+
# Check for rate limiting
|
|
86
|
+
if "429" in error_message or "rate limit" in error_message.lower():
|
|
87
|
+
from getjobber_cli.utils.errors import RateLimitError
|
|
88
|
+
|
|
89
|
+
raise RateLimitError()
|
|
90
|
+
|
|
91
|
+
# Generic GraphQL error
|
|
92
|
+
raise GraphQLError(error_message)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def execute_mutation(
|
|
96
|
+
client: Client, mutation: str, variables: Optional[Dict[str, Any]] = None
|
|
97
|
+
) -> Dict:
|
|
98
|
+
"""Execute a GraphQL mutation.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
client: GQL Client instance.
|
|
102
|
+
mutation: GraphQL mutation string.
|
|
103
|
+
variables: Optional mutation variables.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Mutation result dictionary.
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
GraphQLError: If mutation execution fails.
|
|
110
|
+
JobberAPIError: If API returns an error.
|
|
111
|
+
"""
|
|
112
|
+
# Mutations use the same execution as queries
|
|
113
|
+
return execute_query(client, mutation, variables)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class GraphQLClient:
|
|
117
|
+
"""Wrapper class for GraphQL client operations."""
|
|
118
|
+
|
|
119
|
+
def __init__(self, access_token: str):
|
|
120
|
+
"""Initialize GraphQL client wrapper.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
access_token: OAuth access token.
|
|
124
|
+
"""
|
|
125
|
+
self.client = create_client(access_token)
|
|
126
|
+
|
|
127
|
+
def query(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict:
|
|
128
|
+
"""Execute a GraphQL query.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
query: GraphQL query string.
|
|
132
|
+
variables: Optional query variables.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Query result dictionary.
|
|
136
|
+
"""
|
|
137
|
+
return execute_query(self.client, query, variables)
|
|
138
|
+
|
|
139
|
+
def mutate(self, mutation: str, variables: Optional[Dict[str, Any]] = None) -> Dict:
|
|
140
|
+
"""Execute a GraphQL mutation.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
mutation: GraphQL mutation string.
|
|
144
|
+
variables: Optional mutation variables.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Mutation result dictionary.
|
|
148
|
+
"""
|
|
149
|
+
return execute_mutation(self.client, mutation, variables)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Pre-built GraphQL mutations for GetJobber API."""
|
|
2
|
+
|
|
3
|
+
# Client Mutations
|
|
4
|
+
CREATE_CLIENT = """
|
|
5
|
+
mutation CreateClient($input: ClientInput!) {
|
|
6
|
+
clientCreate(input: $input) {
|
|
7
|
+
client {
|
|
8
|
+
id
|
|
9
|
+
firstName
|
|
10
|
+
lastName
|
|
11
|
+
companyName
|
|
12
|
+
email
|
|
13
|
+
phoneNumber
|
|
14
|
+
createdAt
|
|
15
|
+
}
|
|
16
|
+
userErrors {
|
|
17
|
+
message
|
|
18
|
+
path
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
UPDATE_CLIENT = """
|
|
25
|
+
mutation UpdateClient($id: ID!, $input: ClientInput!) {
|
|
26
|
+
clientUpdate(id: $id, input: $input) {
|
|
27
|
+
client {
|
|
28
|
+
id
|
|
29
|
+
firstName
|
|
30
|
+
lastName
|
|
31
|
+
companyName
|
|
32
|
+
email
|
|
33
|
+
phoneNumber
|
|
34
|
+
updatedAt
|
|
35
|
+
}
|
|
36
|
+
userErrors {
|
|
37
|
+
message
|
|
38
|
+
path
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
DELETE_CLIENT = """
|
|
45
|
+
mutation DeleteClient($id: ID!) {
|
|
46
|
+
clientArchive(id: $id) {
|
|
47
|
+
client {
|
|
48
|
+
id
|
|
49
|
+
}
|
|
50
|
+
userErrors {
|
|
51
|
+
message
|
|
52
|
+
path
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
# Job Mutations
|
|
59
|
+
CREATE_JOB = """
|
|
60
|
+
mutation CreateJob($input: JobInput!) {
|
|
61
|
+
jobCreate(input: $input) {
|
|
62
|
+
job {
|
|
63
|
+
id
|
|
64
|
+
title
|
|
65
|
+
jobNumber
|
|
66
|
+
status
|
|
67
|
+
client {
|
|
68
|
+
id
|
|
69
|
+
firstName
|
|
70
|
+
lastName
|
|
71
|
+
}
|
|
72
|
+
createdAt
|
|
73
|
+
}
|
|
74
|
+
userErrors {
|
|
75
|
+
message
|
|
76
|
+
path
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
UPDATE_JOB = """
|
|
83
|
+
mutation UpdateJob($id: ID!, $input: JobInput!) {
|
|
84
|
+
jobUpdate(id: $id, input: $input) {
|
|
85
|
+
job {
|
|
86
|
+
id
|
|
87
|
+
title
|
|
88
|
+
jobNumber
|
|
89
|
+
status
|
|
90
|
+
updatedAt
|
|
91
|
+
}
|
|
92
|
+
userErrors {
|
|
93
|
+
message
|
|
94
|
+
path
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
COMPLETE_JOB = """
|
|
101
|
+
mutation CompleteJob($id: ID!) {
|
|
102
|
+
jobComplete(id: $id) {
|
|
103
|
+
job {
|
|
104
|
+
id
|
|
105
|
+
title
|
|
106
|
+
status
|
|
107
|
+
completedAt
|
|
108
|
+
}
|
|
109
|
+
userErrors {
|
|
110
|
+
message
|
|
111
|
+
path
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
# Quote Mutations
|
|
118
|
+
CREATE_QUOTE = """
|
|
119
|
+
mutation CreateQuote($input: QuoteInput!) {
|
|
120
|
+
quoteCreate(input: $input) {
|
|
121
|
+
quote {
|
|
122
|
+
id
|
|
123
|
+
quoteNumber
|
|
124
|
+
title
|
|
125
|
+
status
|
|
126
|
+
client {
|
|
127
|
+
id
|
|
128
|
+
firstName
|
|
129
|
+
lastName
|
|
130
|
+
}
|
|
131
|
+
totalAmount
|
|
132
|
+
createdAt
|
|
133
|
+
}
|
|
134
|
+
userErrors {
|
|
135
|
+
message
|
|
136
|
+
path
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
UPDATE_QUOTE = """
|
|
143
|
+
mutation UpdateQuote($id: ID!, $input: QuoteInput!) {
|
|
144
|
+
quoteUpdate(id: $id, input: $input) {
|
|
145
|
+
quote {
|
|
146
|
+
id
|
|
147
|
+
quoteNumber
|
|
148
|
+
title
|
|
149
|
+
status
|
|
150
|
+
updatedAt
|
|
151
|
+
}
|
|
152
|
+
userErrors {
|
|
153
|
+
message
|
|
154
|
+
path
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
SEND_QUOTE = """
|
|
161
|
+
mutation SendQuote($id: ID!) {
|
|
162
|
+
quoteSend(id: $id) {
|
|
163
|
+
quote {
|
|
164
|
+
id
|
|
165
|
+
quoteNumber
|
|
166
|
+
status
|
|
167
|
+
sentAt
|
|
168
|
+
}
|
|
169
|
+
userErrors {
|
|
170
|
+
message
|
|
171
|
+
path
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
APPROVE_QUOTE = """
|
|
178
|
+
mutation ApproveQuote($id: ID!) {
|
|
179
|
+
quoteApprove(id: $id) {
|
|
180
|
+
quote {
|
|
181
|
+
id
|
|
182
|
+
quoteNumber
|
|
183
|
+
status
|
|
184
|
+
}
|
|
185
|
+
userErrors {
|
|
186
|
+
message
|
|
187
|
+
path
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
# Invoice Mutations
|
|
194
|
+
CREATE_INVOICE = """
|
|
195
|
+
mutation CreateInvoice($input: InvoiceInput!) {
|
|
196
|
+
invoiceCreate(input: $input) {
|
|
197
|
+
invoice {
|
|
198
|
+
id
|
|
199
|
+
invoiceNumber
|
|
200
|
+
subject
|
|
201
|
+
status
|
|
202
|
+
client {
|
|
203
|
+
id
|
|
204
|
+
firstName
|
|
205
|
+
lastName
|
|
206
|
+
}
|
|
207
|
+
totalAmount
|
|
208
|
+
createdAt
|
|
209
|
+
}
|
|
210
|
+
userErrors {
|
|
211
|
+
message
|
|
212
|
+
path
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
UPDATE_INVOICE = """
|
|
219
|
+
mutation UpdateInvoice($id: ID!, $input: InvoiceInput!) {
|
|
220
|
+
invoiceUpdate(id: $id, input: $input) {
|
|
221
|
+
invoice {
|
|
222
|
+
id
|
|
223
|
+
invoiceNumber
|
|
224
|
+
subject
|
|
225
|
+
status
|
|
226
|
+
updatedAt
|
|
227
|
+
}
|
|
228
|
+
userErrors {
|
|
229
|
+
message
|
|
230
|
+
path
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
SEND_INVOICE = """
|
|
237
|
+
mutation SendInvoice($id: ID!) {
|
|
238
|
+
invoiceSend(id: $id) {
|
|
239
|
+
invoice {
|
|
240
|
+
id
|
|
241
|
+
invoiceNumber
|
|
242
|
+
status
|
|
243
|
+
sentAt
|
|
244
|
+
}
|
|
245
|
+
userErrors {
|
|
246
|
+
message
|
|
247
|
+
path
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
"""
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Pre-built GraphQL queries for GetJobber API."""
|
|
2
|
+
|
|
3
|
+
# Client Queries
|
|
4
|
+
LIST_CLIENTS = """
|
|
5
|
+
query ListClients($first: Int, $after: String) {
|
|
6
|
+
clients(first: $first, after: $after) {
|
|
7
|
+
nodes {
|
|
8
|
+
id
|
|
9
|
+
firstName
|
|
10
|
+
lastName
|
|
11
|
+
companyName
|
|
12
|
+
email
|
|
13
|
+
phone
|
|
14
|
+
createdAt
|
|
15
|
+
updatedAt
|
|
16
|
+
}
|
|
17
|
+
pageInfo {
|
|
18
|
+
hasNextPage
|
|
19
|
+
endCursor
|
|
20
|
+
}
|
|
21
|
+
totalCount
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
GET_CLIENT = """
|
|
27
|
+
query GetClient($id: ID!) {
|
|
28
|
+
client(id: $id) {
|
|
29
|
+
id
|
|
30
|
+
firstName
|
|
31
|
+
lastName
|
|
32
|
+
companyName
|
|
33
|
+
email
|
|
34
|
+
phone
|
|
35
|
+
phones { nodes { number smsAllowed } }
|
|
36
|
+
billingAddress {
|
|
37
|
+
street1
|
|
38
|
+
street2
|
|
39
|
+
city
|
|
40
|
+
province
|
|
41
|
+
postalCode
|
|
42
|
+
country
|
|
43
|
+
}
|
|
44
|
+
tags
|
|
45
|
+
createdAt
|
|
46
|
+
updatedAt
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
SEARCH_CLIENTS = """
|
|
52
|
+
query SearchClients($query: String!, $first: Int) {
|
|
53
|
+
clients(first: $first, filter: {search: $query}) {
|
|
54
|
+
nodes {
|
|
55
|
+
id
|
|
56
|
+
firstName
|
|
57
|
+
lastName
|
|
58
|
+
companyName
|
|
59
|
+
email
|
|
60
|
+
phone
|
|
61
|
+
}
|
|
62
|
+
totalCount
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# Job Queries
|
|
68
|
+
LIST_JOBS = """
|
|
69
|
+
query ListJobs($first: Int, $after: String, $status: String) {
|
|
70
|
+
jobs(first: $first, after: $after, filter: {status: $status}) {
|
|
71
|
+
nodes {
|
|
72
|
+
id
|
|
73
|
+
title
|
|
74
|
+
jobNumber
|
|
75
|
+
status
|
|
76
|
+
client {
|
|
77
|
+
id
|
|
78
|
+
firstName
|
|
79
|
+
lastName
|
|
80
|
+
companyName
|
|
81
|
+
}
|
|
82
|
+
startAt
|
|
83
|
+
endAt
|
|
84
|
+
createdAt
|
|
85
|
+
updatedAt
|
|
86
|
+
}
|
|
87
|
+
pageInfo {
|
|
88
|
+
hasNextPage
|
|
89
|
+
endCursor
|
|
90
|
+
}
|
|
91
|
+
totalCount
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
GET_JOB = """
|
|
97
|
+
query GetJob($id: ID!) {
|
|
98
|
+
job(id: $id) {
|
|
99
|
+
id
|
|
100
|
+
title
|
|
101
|
+
jobNumber
|
|
102
|
+
status
|
|
103
|
+
description
|
|
104
|
+
client {
|
|
105
|
+
id
|
|
106
|
+
firstName
|
|
107
|
+
lastName
|
|
108
|
+
companyName
|
|
109
|
+
email
|
|
110
|
+
}
|
|
111
|
+
property {
|
|
112
|
+
id
|
|
113
|
+
address {
|
|
114
|
+
street1
|
|
115
|
+
street2
|
|
116
|
+
city
|
|
117
|
+
province
|
|
118
|
+
postalCode
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
startAt
|
|
122
|
+
endAt
|
|
123
|
+
totalAmount
|
|
124
|
+
createdAt
|
|
125
|
+
updatedAt
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
# Quote Queries
|
|
131
|
+
LIST_QUOTES = """
|
|
132
|
+
query ListQuotes($first: Int, $after: String, $status: String) {
|
|
133
|
+
quotes(first: $first, after: $after, filter: {status: $status}) {
|
|
134
|
+
nodes {
|
|
135
|
+
id
|
|
136
|
+
quoteNumber
|
|
137
|
+
title
|
|
138
|
+
status
|
|
139
|
+
client {
|
|
140
|
+
id
|
|
141
|
+
firstName
|
|
142
|
+
lastName
|
|
143
|
+
companyName
|
|
144
|
+
}
|
|
145
|
+
totalAmount
|
|
146
|
+
sentAt
|
|
147
|
+
createdAt
|
|
148
|
+
updatedAt
|
|
149
|
+
}
|
|
150
|
+
pageInfo {
|
|
151
|
+
hasNextPage
|
|
152
|
+
endCursor
|
|
153
|
+
}
|
|
154
|
+
totalCount
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
GET_QUOTE = """
|
|
160
|
+
query GetQuote($id: ID!) {
|
|
161
|
+
quote(id: $id) {
|
|
162
|
+
id
|
|
163
|
+
quoteNumber
|
|
164
|
+
title
|
|
165
|
+
status
|
|
166
|
+
message
|
|
167
|
+
client {
|
|
168
|
+
id
|
|
169
|
+
firstName
|
|
170
|
+
lastName
|
|
171
|
+
companyName
|
|
172
|
+
email
|
|
173
|
+
}
|
|
174
|
+
lineItems {
|
|
175
|
+
id
|
|
176
|
+
name
|
|
177
|
+
description
|
|
178
|
+
quantity
|
|
179
|
+
unitPrice
|
|
180
|
+
total
|
|
181
|
+
}
|
|
182
|
+
subtotal
|
|
183
|
+
taxAmount
|
|
184
|
+
totalAmount
|
|
185
|
+
sentAt
|
|
186
|
+
createdAt
|
|
187
|
+
updatedAt
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
# Invoice Queries
|
|
193
|
+
LIST_INVOICES = """
|
|
194
|
+
query ListInvoices($first: Int, $after: String, $status: String) {
|
|
195
|
+
invoices(first: $first, after: $after, filter: {status: $status}) {
|
|
196
|
+
nodes {
|
|
197
|
+
id
|
|
198
|
+
invoiceNumber
|
|
199
|
+
subject
|
|
200
|
+
status
|
|
201
|
+
client {
|
|
202
|
+
id
|
|
203
|
+
firstName
|
|
204
|
+
lastName
|
|
205
|
+
companyName
|
|
206
|
+
}
|
|
207
|
+
totalAmount
|
|
208
|
+
amountPaid
|
|
209
|
+
balance
|
|
210
|
+
dueDate
|
|
211
|
+
sentAt
|
|
212
|
+
createdAt
|
|
213
|
+
updatedAt
|
|
214
|
+
}
|
|
215
|
+
pageInfo {
|
|
216
|
+
hasNextPage
|
|
217
|
+
endCursor
|
|
218
|
+
}
|
|
219
|
+
totalCount
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
GET_INVOICE = """
|
|
225
|
+
query GetInvoice($id: ID!) {
|
|
226
|
+
invoice(id: $id) {
|
|
227
|
+
id
|
|
228
|
+
invoiceNumber
|
|
229
|
+
subject
|
|
230
|
+
status
|
|
231
|
+
message
|
|
232
|
+
client {
|
|
233
|
+
id
|
|
234
|
+
firstName
|
|
235
|
+
lastName
|
|
236
|
+
companyName
|
|
237
|
+
email
|
|
238
|
+
}
|
|
239
|
+
job {
|
|
240
|
+
id
|
|
241
|
+
title
|
|
242
|
+
jobNumber
|
|
243
|
+
}
|
|
244
|
+
lineItems {
|
|
245
|
+
id
|
|
246
|
+
name
|
|
247
|
+
description
|
|
248
|
+
quantity
|
|
249
|
+
unitPrice
|
|
250
|
+
total
|
|
251
|
+
}
|
|
252
|
+
subtotal
|
|
253
|
+
taxAmount
|
|
254
|
+
totalAmount
|
|
255
|
+
amountPaid
|
|
256
|
+
balance
|
|
257
|
+
dueDate
|
|
258
|
+
sentAt
|
|
259
|
+
paidAt
|
|
260
|
+
createdAt
|
|
261
|
+
updatedAt
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
"""
|
|
File without changes
|