@agnocon/piece-bokio 0.0.8
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/LICENSE.MIT-AP +24 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/actions/add-line-item-to-invoice.d.ts +15 -0
- package/dist/lib/actions/add-line-item-to-invoice.d.ts.map +1 -0
- package/dist/lib/actions/add-line-item-to-invoice.js +67 -0
- package/dist/lib/actions/add-line-item-to-invoice.js.map +1 -0
- package/dist/lib/actions/create-a-journal-entry.d.ts +9 -0
- package/dist/lib/actions/create-a-journal-entry.d.ts.map +1 -0
- package/dist/lib/actions/create-a-journal-entry.js +70 -0
- package/dist/lib/actions/create-a-journal-entry.js.map +1 -0
- package/dist/lib/actions/create-an-item.d.ts +11 -0
- package/dist/lib/actions/create-an-item.d.ts.map +1 -0
- package/dist/lib/actions/create-an-item.js +60 -0
- package/dist/lib/actions/create-an-item.js.map +1 -0
- package/dist/lib/actions/create-customer.d.ts +19 -0
- package/dist/lib/actions/create-customer.d.ts.map +1 -0
- package/dist/lib/actions/create-customer.js +145 -0
- package/dist/lib/actions/create-customer.js.map +1 -0
- package/dist/lib/actions/create-invoice.d.ts +18 -0
- package/dist/lib/actions/create-invoice.d.ts.map +1 -0
- package/dist/lib/actions/create-invoice.js +125 -0
- package/dist/lib/actions/create-invoice.js.map +1 -0
- package/dist/lib/actions/find-customer.d.ts +10 -0
- package/dist/lib/actions/find-customer.d.ts.map +1 -0
- package/dist/lib/actions/find-customer.js +78 -0
- package/dist/lib/actions/find-customer.js.map +1 -0
- package/dist/lib/actions/get-a-journal-entry.d.ts +7 -0
- package/dist/lib/actions/get-a-journal-entry.d.ts.map +1 -0
- package/dist/lib/actions/get-a-journal-entry.js +34 -0
- package/dist/lib/actions/get-a-journal-entry.js.map +1 -0
- package/dist/lib/actions/get-an-invoice.d.ts +7 -0
- package/dist/lib/actions/get-an-invoice.d.ts.map +1 -0
- package/dist/lib/actions/get-an-invoice.js +34 -0
- package/dist/lib/actions/get-an-invoice.js.map +1 -0
- package/dist/lib/actions/get-draft-invoice-by-customer-name.d.ts +9 -0
- package/dist/lib/actions/get-draft-invoice-by-customer-name.d.ts.map +1 -0
- package/dist/lib/actions/get-draft-invoice-by-customer-name.js +75 -0
- package/dist/lib/actions/get-draft-invoice-by-customer-name.js.map +1 -0
- package/dist/lib/actions/update-an-invoice.d.ts +18 -0
- package/dist/lib/actions/update-an-invoice.d.ts.map +1 -0
- package/dist/lib/actions/update-an-invoice.js +109 -0
- package/dist/lib/actions/update-an-invoice.js.map +1 -0
- package/dist/lib/common/auth.d.ts +5 -0
- package/dist/lib/common/auth.d.ts.map +1 -0
- package/dist/lib/common/auth.js +47 -0
- package/dist/lib/common/auth.js.map +1 -0
- package/dist/lib/common/client.d.ts +4 -0
- package/dist/lib/common/client.d.ts.map +1 -0
- package/dist/lib/common/client.js +29 -0
- package/dist/lib/common/client.js.map +1 -0
- package/dist/lib/common/props.d.ts +10 -0
- package/dist/lib/common/props.d.ts.map +1 -0
- package/dist/lib/common/props.js +128 -0
- package/dist/lib/common/props.js.map +1 -0
- package/package.json +47 -0
- package/src/index.ts +49 -0
- package/src/lib/actions/add-line-item-to-invoice.ts +74 -0
- package/src/lib/actions/create-a-journal-entry.ts +72 -0
- package/src/lib/actions/create-an-item.ts +68 -0
- package/src/lib/actions/create-customer.ts +163 -0
- package/src/lib/actions/create-invoice.ts +145 -0
- package/src/lib/actions/find-customer.ts +75 -0
- package/src/lib/actions/get-a-journal-entry.ts +32 -0
- package/src/lib/actions/get-an-invoice.ts +32 -0
- package/src/lib/actions/get-draft-invoice-by-customer-name.ts +78 -0
- package/src/lib/actions/update-an-invoice.ts +127 -0
- package/src/lib/common/auth.ts +44 -0
- package/src/lib/common/client.ts +25 -0
- package/src/lib/common/props.ts +136 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
import { lineItemsProps } from '../common/props';
|
|
6
|
+
|
|
7
|
+
export const createInvoice = createAction({
|
|
8
|
+
auth: bokioAuth,
|
|
9
|
+
name: 'createInvoice',
|
|
10
|
+
displayName: 'Create Invoice',
|
|
11
|
+
description: 'Creates a new draft invoice in Bokio',
|
|
12
|
+
audience: 'both',
|
|
13
|
+
aiMetadata: { description: 'Creates a new draft invoice in a Bokio company with a single line item. Use to start a new customer invoice; invoice date, due date, and item type are required, and the customer is linked by passing the customer UUID. Not idempotent — each call creates a separate draft invoice.', idempotent: false },
|
|
14
|
+
props: {
|
|
15
|
+
invoiceType: Property.StaticDropdown({
|
|
16
|
+
displayName: 'Invoice Type',
|
|
17
|
+
description: 'Type of invoice',
|
|
18
|
+
required: false,
|
|
19
|
+
options: {
|
|
20
|
+
options: [
|
|
21
|
+
{ label: 'Invoice', value: 'invoice' },
|
|
22
|
+
{ label: 'Cash Invoice', value: 'cashInvoice' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
customerRef: Property.ShortText({
|
|
27
|
+
displayName: 'Customer ID',
|
|
28
|
+
description: 'UUID of the customer',
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
invoiceDate: Property.ShortText({
|
|
32
|
+
displayName: 'Invoice Date',
|
|
33
|
+
description: 'Invoice date (YYYY-MM-DD)',
|
|
34
|
+
required: true,
|
|
35
|
+
}),
|
|
36
|
+
dueDate: Property.ShortText({
|
|
37
|
+
displayName: 'Due Date',
|
|
38
|
+
description: 'Due date (YYYY-MM-DD)',
|
|
39
|
+
required: true,
|
|
40
|
+
}),
|
|
41
|
+
orderNumberReference: Property.ShortText({
|
|
42
|
+
displayName: 'Order Number Reference',
|
|
43
|
+
description: 'The order number associated with the invoice',
|
|
44
|
+
required: false,
|
|
45
|
+
}),
|
|
46
|
+
currency: Property.ShortText({
|
|
47
|
+
displayName: 'Currency',
|
|
48
|
+
description: 'ISO 4217 currency code (e.g., SEK, EUR, USD)',
|
|
49
|
+
required: false,
|
|
50
|
+
}),
|
|
51
|
+
currencyRate: Property.Number({
|
|
52
|
+
displayName: 'Currency Rate',
|
|
53
|
+
description: 'Currency rate (defaults to 1)',
|
|
54
|
+
required: false,
|
|
55
|
+
}),
|
|
56
|
+
itemType: Property.StaticDropdown({
|
|
57
|
+
displayName: 'Item Type',
|
|
58
|
+
description: 'Type of the line item',
|
|
59
|
+
required: true,
|
|
60
|
+
options: {
|
|
61
|
+
options: [
|
|
62
|
+
{ label: 'Sales Item', value: 'salesItem' },
|
|
63
|
+
{ label: 'Description Only Item', value: 'descriptionOnlyItem' },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
dynamicProps: lineItemsProps,
|
|
68
|
+
},
|
|
69
|
+
async run(context) {
|
|
70
|
+
const {
|
|
71
|
+
invoiceType,
|
|
72
|
+
customerRef,
|
|
73
|
+
invoiceDate,
|
|
74
|
+
dueDate,
|
|
75
|
+
orderNumberReference,
|
|
76
|
+
currency,
|
|
77
|
+
currencyRate,
|
|
78
|
+
itemType,
|
|
79
|
+
dynamicProps,
|
|
80
|
+
} = context.propsValue;
|
|
81
|
+
const { api_key, companyId } = context.auth.props;
|
|
82
|
+
|
|
83
|
+
const body: any = {
|
|
84
|
+
type: invoiceType || 'invoice',
|
|
85
|
+
invoiceDate,
|
|
86
|
+
dueDate,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (customerRef) {
|
|
90
|
+
body.customerRef = {
|
|
91
|
+
id: customerRef,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (orderNumberReference) {
|
|
96
|
+
body.orderNumberReference = orderNumberReference;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (currency) {
|
|
100
|
+
body.currency = currency;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (currencyRate !== undefined && currencyRate !== null) {
|
|
104
|
+
body.currencyRate = currencyRate;
|
|
105
|
+
}
|
|
106
|
+
const lineItems: any = {};
|
|
107
|
+
if (itemType === 'salesItem' && dynamicProps) {
|
|
108
|
+
lineItems.itemType = itemType;
|
|
109
|
+
body.description = dynamicProps?.['description'];
|
|
110
|
+
lineItems.quantity = dynamicProps['quantity'];
|
|
111
|
+
lineItems.unitPrice = dynamicProps['unitPrice'];
|
|
112
|
+
|
|
113
|
+
if (dynamicProps['itemRefId']) {
|
|
114
|
+
lineItems.itemRef = {
|
|
115
|
+
id: dynamicProps['itemRefId'],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (dynamicProps['productType']) {
|
|
120
|
+
lineItems.productType = dynamicProps['productType'];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (dynamicProps['unitType']) {
|
|
124
|
+
lineItems.unitType = dynamicProps['unitType'];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
dynamicProps['taxRate'] !== undefined &&
|
|
129
|
+
dynamicProps['taxRate'] !== null
|
|
130
|
+
) {
|
|
131
|
+
lineItems.taxRate = dynamicProps['taxRate'];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
body.lineItems = [lineItems];
|
|
135
|
+
|
|
136
|
+
const response = await makeRequest(
|
|
137
|
+
api_key,
|
|
138
|
+
HttpMethod.POST,
|
|
139
|
+
`/companies/${companyId}/invoices`,
|
|
140
|
+
body
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return response;
|
|
144
|
+
},
|
|
145
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
|
|
6
|
+
export const findCustomer = createAction({
|
|
7
|
+
auth: bokioAuth,
|
|
8
|
+
name: 'findCustomer',
|
|
9
|
+
displayName: 'Find Customer',
|
|
10
|
+
description: 'Find customers in Bokio by filtering and pagination',
|
|
11
|
+
audience: 'both',
|
|
12
|
+
aiMetadata: { description: 'Lists customers in a Bokio company with pagination, optionally filtered by name, type, VAT number, or organization number. Leaving the filter empty returns all customers; supplying a filter type and value narrows to matching records. Use to look up a customer or their ID. Idempotent read-only lookup.', idempotent: true },
|
|
13
|
+
props: {
|
|
14
|
+
filterType: Property.StaticDropdown({
|
|
15
|
+
displayName: 'Filter Type',
|
|
16
|
+
description: 'Type of filter to use',
|
|
17
|
+
required: false,
|
|
18
|
+
options: {
|
|
19
|
+
options: [
|
|
20
|
+
{ label: 'Name', value: 'name' },
|
|
21
|
+
{ label: 'Type', value: 'type' },
|
|
22
|
+
{ label: 'VAT Number', value: 'vatNumber' },
|
|
23
|
+
{ label: 'Organization Number', value: 'orgNumber' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
filterValue: Property.ShortText({
|
|
28
|
+
displayName: 'Filter Value',
|
|
29
|
+
description: 'Value to filter by',
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
page: Property.Number({
|
|
33
|
+
displayName: 'Page',
|
|
34
|
+
description: 'Page number (defaults to 1)',
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
37
|
+
pageSize: Property.Number({
|
|
38
|
+
displayName: 'Page Size',
|
|
39
|
+
description: 'Number of items per page (max 100, defaults to 25)',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
async run(context) {
|
|
44
|
+
const { filterType, filterValue, page, pageSize } = context.propsValue;
|
|
45
|
+
const { api_key, companyId } = context.auth.props;
|
|
46
|
+
|
|
47
|
+
let query = '';
|
|
48
|
+
if (filterType && filterValue) {
|
|
49
|
+
query = `${filterType}=='${filterValue}'`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const params = new URLSearchParams();
|
|
53
|
+
if (page !== undefined && page !== null) {
|
|
54
|
+
params.append('page', String(page));
|
|
55
|
+
} else {
|
|
56
|
+
params.append('page', '1');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (pageSize !== undefined && pageSize !== null) {
|
|
60
|
+
params.append('pageSize', String(pageSize));
|
|
61
|
+
} else {
|
|
62
|
+
params.append('pageSize', '25');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (query) {
|
|
66
|
+
params.append('query', query);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const url = `/companies/${companyId}/customers?${params.toString()}`;
|
|
70
|
+
|
|
71
|
+
const response = await makeRequest(api_key, HttpMethod.GET, url);
|
|
72
|
+
|
|
73
|
+
return response;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
|
|
6
|
+
export const getAJournalEntry = createAction({
|
|
7
|
+
auth: bokioAuth,
|
|
8
|
+
name: 'getAJournalEntry',
|
|
9
|
+
displayName: 'Get a journal entry',
|
|
10
|
+
description: 'Retrieve a specific journal entry by its ID',
|
|
11
|
+
audience: 'both',
|
|
12
|
+
aiMetadata: { description: 'Retrieves a single journal entry from a Bokio company by its UUID. Use when you already have the journal entry ID and need its full bookkeeping details. Idempotent read-only lookup.', idempotent: true },
|
|
13
|
+
props: {
|
|
14
|
+
journalEntryId: Property.ShortText({
|
|
15
|
+
displayName: 'Journal Entry ID',
|
|
16
|
+
description: 'UUID of the journal entry',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
async run(context) {
|
|
21
|
+
const { journalEntryId } = context.propsValue;
|
|
22
|
+
const { api_key, companyId } = context.auth.props;
|
|
23
|
+
|
|
24
|
+
const response = await makeRequest(
|
|
25
|
+
api_key,
|
|
26
|
+
HttpMethod.GET,
|
|
27
|
+
`/companies/${companyId}/journal-entries/${journalEntryId}`
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return response;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
|
|
6
|
+
export const getAnInvoice = createAction({
|
|
7
|
+
auth: bokioAuth,
|
|
8
|
+
name: 'getAnInvoice',
|
|
9
|
+
displayName: 'Get an invoice',
|
|
10
|
+
description: 'Retrieve a specific invoice by its ID',
|
|
11
|
+
audience: 'both',
|
|
12
|
+
aiMetadata: { description: 'Retrieves a single invoice from a Bokio company by its invoice ID. Use when you already have the invoice ID and need its full details. Idempotent read-only lookup.', idempotent: true },
|
|
13
|
+
props: {
|
|
14
|
+
invoiceId: Property.ShortText({
|
|
15
|
+
displayName: 'Invoice ID',
|
|
16
|
+
description: 'The ID of the invoice to retrieve',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
async run(context) {
|
|
21
|
+
const { invoiceId } = context.propsValue;
|
|
22
|
+
const { api_key, companyId } = context.auth.props;
|
|
23
|
+
|
|
24
|
+
const response = await makeRequest(
|
|
25
|
+
api_key,
|
|
26
|
+
HttpMethod.GET,
|
|
27
|
+
`/companies/${companyId}/invoices/${invoiceId}`
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return response;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
|
|
6
|
+
export const getDraftInvoiceByCustomerName = createAction({
|
|
7
|
+
auth: bokioAuth,
|
|
8
|
+
name: 'getDraftInvoiceByCustomerName',
|
|
9
|
+
displayName: 'Get Draft Invoice by Customer Name',
|
|
10
|
+
description: 'Get all draft invoices for a customer by customer name',
|
|
11
|
+
audience: 'both',
|
|
12
|
+
aiMetadata: { description: 'Looks up a Bokio customer by exact name and returns their draft invoices with pagination. Use when you have a customer name (not an ID) and want their pending draft invoices; returns an empty result if no customer matches. Idempotent read-only lookup.', idempotent: true },
|
|
13
|
+
props: {
|
|
14
|
+
customerName: Property.ShortText({
|
|
15
|
+
displayName: 'Customer Name',
|
|
16
|
+
description: 'Name of the customer to find draft invoices for',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
page: Property.Number({
|
|
20
|
+
displayName: 'Page',
|
|
21
|
+
description: 'Page number (defaults to 1)',
|
|
22
|
+
required: false,
|
|
23
|
+
}),
|
|
24
|
+
pageSize: Property.Number({
|
|
25
|
+
displayName: 'Page Size',
|
|
26
|
+
description: 'Number of items per page (max 100, defaults to 25)',
|
|
27
|
+
required: false,
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
async run(context) {
|
|
31
|
+
const { customerName, page, pageSize } = context.propsValue;
|
|
32
|
+
const { api_key, companyId } = context.auth.props;
|
|
33
|
+
|
|
34
|
+
// First, find the customer by name
|
|
35
|
+
const customerSearchParams = new URLSearchParams();
|
|
36
|
+
customerSearchParams.append('page', '1');
|
|
37
|
+
customerSearchParams.append('pageSize', '100');
|
|
38
|
+
customerSearchParams.append('query', `name=='${customerName}'`);
|
|
39
|
+
|
|
40
|
+
const customerUrl = `/companies/${companyId}/customers?${customerSearchParams.toString()}`;
|
|
41
|
+
const customerResponse = await makeRequest(api_key, HttpMethod.GET, customerUrl);
|
|
42
|
+
|
|
43
|
+
if (!customerResponse.items || customerResponse.items.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
message: `No customer found with name: ${customerName}`,
|
|
46
|
+
items: [],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const customerId = customerResponse.items[0].id;
|
|
51
|
+
|
|
52
|
+
// Now fetch draft invoices for this customer
|
|
53
|
+
const invoiceParams = new URLSearchParams();
|
|
54
|
+
if (page !== undefined && page !== null) {
|
|
55
|
+
invoiceParams.append('page', String(page));
|
|
56
|
+
} else {
|
|
57
|
+
invoiceParams.append('page', '1');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (pageSize !== undefined && pageSize !== null) {
|
|
61
|
+
invoiceParams.append('pageSize', String(pageSize));
|
|
62
|
+
} else {
|
|
63
|
+
invoiceParams.append('pageSize', '25');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Filter for draft invoices with matching customer
|
|
67
|
+
invoiceParams.append('query', `status==draft&&customerRef==${customerId}`);
|
|
68
|
+
|
|
69
|
+
const invoiceUrl = `/companies/${companyId}/invoices?${invoiceParams.toString()}`;
|
|
70
|
+
const invoiceResponse = await makeRequest(api_key, HttpMethod.GET, invoiceUrl);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
customerName: customerResponse.items[0].name,
|
|
74
|
+
customerId,
|
|
75
|
+
...invoiceResponse,
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { bokioAuth } from '../common/auth';
|
|
3
|
+
import { makeRequest } from '../common/client';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
import { invoiceIdDropdown } from '../common/props';
|
|
6
|
+
|
|
7
|
+
export const updateAnInvoice = createAction({
|
|
8
|
+
auth: bokioAuth,
|
|
9
|
+
name: 'updateAnInvoice',
|
|
10
|
+
displayName: 'Update an invoice',
|
|
11
|
+
description: 'Updates an existing draft invoice in Bokio',
|
|
12
|
+
audience: 'both',
|
|
13
|
+
aiMetadata: { description: 'Updates an existing draft invoice in Bokio, identified by invoice ID, replacing fields such as type, customer, dates, currency, and the full line-items list (passed as a JSON array). Use to modify an invoice that already exists rather than create one. Idempotent — repeating the same update yields the same invoice state.', idempotent: true },
|
|
14
|
+
props: {
|
|
15
|
+
invoiceId: invoiceIdDropdown,
|
|
16
|
+
invoiceType: Property.StaticDropdown({
|
|
17
|
+
displayName: 'Invoice Type',
|
|
18
|
+
description: 'Type of invoice',
|
|
19
|
+
required: false,
|
|
20
|
+
options: {
|
|
21
|
+
options: [
|
|
22
|
+
{ label: 'Invoice', value: 'invoice' },
|
|
23
|
+
{ label: 'Cash Invoice', value: 'cashInvoice' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
customerRef: Property.ShortText({
|
|
28
|
+
displayName: 'Customer ID',
|
|
29
|
+
description: 'UUID of the customer',
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
invoiceDate: Property.ShortText({
|
|
33
|
+
displayName: 'Invoice Date',
|
|
34
|
+
description: 'Invoice date (YYYY-MM-DD)',
|
|
35
|
+
required: true,
|
|
36
|
+
}),
|
|
37
|
+
dueDate: Property.ShortText({
|
|
38
|
+
displayName: 'Due Date',
|
|
39
|
+
description: 'Due date (YYYY-MM-DD)',
|
|
40
|
+
required: true,
|
|
41
|
+
}),
|
|
42
|
+
orderNumberReference: Property.ShortText({
|
|
43
|
+
displayName: 'Order Number Reference',
|
|
44
|
+
description: 'The order number associated with the invoice',
|
|
45
|
+
required: false,
|
|
46
|
+
}),
|
|
47
|
+
currency: Property.ShortText({
|
|
48
|
+
displayName: 'Currency',
|
|
49
|
+
description: 'ISO 4217 currency code (e.g., SEK, EUR, USD)',
|
|
50
|
+
required: false,
|
|
51
|
+
}),
|
|
52
|
+
currencyRate: Property.Number({
|
|
53
|
+
displayName: 'Currency Rate',
|
|
54
|
+
description: 'Currency rate',
|
|
55
|
+
required: false,
|
|
56
|
+
}),
|
|
57
|
+
lineItemsJson: Property.LongText({
|
|
58
|
+
displayName: 'Line Items (JSON)',
|
|
59
|
+
description:
|
|
60
|
+
'Line items as JSON array. Each item should have: itemRef (object with id), description, quantity, unitPrice, unit, itemType (PRODUCT or SERVICE), vat',
|
|
61
|
+
required: false,
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
async run(context) {
|
|
65
|
+
const {
|
|
66
|
+
invoiceId,
|
|
67
|
+
invoiceType,
|
|
68
|
+
customerRef,
|
|
69
|
+
invoiceDate,
|
|
70
|
+
dueDate,
|
|
71
|
+
orderNumberReference,
|
|
72
|
+
currency,
|
|
73
|
+
currencyRate,
|
|
74
|
+
lineItemsJson,
|
|
75
|
+
} = context.propsValue;
|
|
76
|
+
const { api_key, companyId } = context.auth.props;
|
|
77
|
+
|
|
78
|
+
const body: any = {};
|
|
79
|
+
|
|
80
|
+
if (invoiceType) {
|
|
81
|
+
body.type = invoiceType;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (customerRef) {
|
|
85
|
+
body.customerRef = {
|
|
86
|
+
id: customerRef,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (invoiceDate) {
|
|
91
|
+
body.invoiceDate = invoiceDate;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (dueDate) {
|
|
95
|
+
body.dueDate = dueDate;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (orderNumberReference) {
|
|
99
|
+
body.orderNumberReference = orderNumberReference;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (currency) {
|
|
103
|
+
body.currency = currency;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (currencyRate !== undefined && currencyRate !== null) {
|
|
107
|
+
body.currencyRate = currencyRate;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (lineItemsJson) {
|
|
111
|
+
try {
|
|
112
|
+
body.lineItems = JSON.parse(lineItemsJson);
|
|
113
|
+
} catch {
|
|
114
|
+
throw new Error('Invalid JSON format for line items');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const response = await makeRequest(
|
|
119
|
+
api_key,
|
|
120
|
+
HttpMethod.PUT,
|
|
121
|
+
`/companies/${companyId}/invoices/${invoiceId}`,
|
|
122
|
+
body
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return response;
|
|
126
|
+
},
|
|
127
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PieceAuth, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { makeRequest } from './client';
|
|
3
|
+
import { httpClient, HttpMethod } from '@agnocon/pieces-common';
|
|
4
|
+
|
|
5
|
+
export const bokioAuth = PieceAuth.CustomAuth({
|
|
6
|
+
displayName: 'Bokio API Key',
|
|
7
|
+
description: 'Bokio API Key is required to authenticate requests to Bokio',
|
|
8
|
+
required: true,
|
|
9
|
+
props: {
|
|
10
|
+
companyId: Property.ShortText({
|
|
11
|
+
displayName: 'Company ID',
|
|
12
|
+
description: 'Enter your Bokio Company ID ',
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
api_key: Property.ShortText({
|
|
16
|
+
displayName: 'API Key',
|
|
17
|
+
description: 'Enter your Bokio API Key',
|
|
18
|
+
required: true,
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
validate: async ({ auth }) => {
|
|
22
|
+
if (auth) {
|
|
23
|
+
try {
|
|
24
|
+
await makeRequest(
|
|
25
|
+
auth.api_key,
|
|
26
|
+
HttpMethod.GET,
|
|
27
|
+
`/companies/${auth.companyId}/journal-entries`
|
|
28
|
+
);
|
|
29
|
+
return {
|
|
30
|
+
valid: true,
|
|
31
|
+
};
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return {
|
|
34
|
+
valid: false,
|
|
35
|
+
error: 'Invalid Api Key',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: 'Invalid Api Key',
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HttpMethod, httpClient } from '@agnocon/pieces-common';
|
|
2
|
+
|
|
3
|
+
export const BASE_URL = `https://api.bokio.se/v1`;
|
|
4
|
+
|
|
5
|
+
export async function makeRequest(
|
|
6
|
+
api_key: string,
|
|
7
|
+
method: HttpMethod,
|
|
8
|
+
path: string,
|
|
9
|
+
body?: unknown
|
|
10
|
+
) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await httpClient.sendRequest({
|
|
13
|
+
method,
|
|
14
|
+
url: `${BASE_URL}${path}`,
|
|
15
|
+
headers: {
|
|
16
|
+
Authorization: `Bearer ${api_key}`,
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
body,
|
|
20
|
+
});
|
|
21
|
+
return response.body;
|
|
22
|
+
} catch (error: any) {
|
|
23
|
+
throw new Error(`Unexpected error: ${error.message || String(error)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { makeRequest } from './client';
|
|
3
|
+
import { bokioAuth } from './auth';
|
|
4
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
5
|
+
|
|
6
|
+
export const invoiceIdDropdown = Property.Dropdown({
|
|
7
|
+
auth: bokioAuth,
|
|
8
|
+
displayName: 'Invoice',
|
|
9
|
+
description: 'Select the invoice',
|
|
10
|
+
required: true,
|
|
11
|
+
refreshers: [],
|
|
12
|
+
options: async ({ auth }) => {
|
|
13
|
+
if (!auth) {
|
|
14
|
+
return {
|
|
15
|
+
disabled: true,
|
|
16
|
+
options: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const response = await makeRequest(
|
|
21
|
+
auth.props.api_key,
|
|
22
|
+
HttpMethod.GET,
|
|
23
|
+
`/companies/${auth.props.companyId}/invoices?limit=100`
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const options = response.items.map((invoice: any) => ({
|
|
27
|
+
label: `#${invoice.invoiceNumber} - ${invoice.customerRef.customerName}`,
|
|
28
|
+
value: invoice.id,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
disabled: false,
|
|
33
|
+
options,
|
|
34
|
+
};
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
disabled: true,
|
|
38
|
+
options: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const unitTypeDropdown = Property.StaticDropdown({
|
|
45
|
+
displayName: 'Unit Type',
|
|
46
|
+
description: 'Unit type for the item',
|
|
47
|
+
required: false,
|
|
48
|
+
options: {
|
|
49
|
+
options: [
|
|
50
|
+
{ label: 'Unspecified', value: 'unspecified' },
|
|
51
|
+
{ label: 'Piece', value: 'piece' },
|
|
52
|
+
{ label: 'Hour', value: 'hour' },
|
|
53
|
+
{ label: 'Day', value: 'day' },
|
|
54
|
+
{ label: 'Month', value: 'month' },
|
|
55
|
+
{ label: 'Kilogram', value: 'kilogram' },
|
|
56
|
+
{ label: 'Gram', value: 'gram' },
|
|
57
|
+
{ label: 'Liter', value: 'liter' },
|
|
58
|
+
{ label: 'Meter', value: 'meter' },
|
|
59
|
+
{ label: 'Centimeter', value: 'centimeter' },
|
|
60
|
+
{ label: 'Millimeter', value: 'millimeter' },
|
|
61
|
+
{ label: 'Meter Squared', value: 'meterSquared' },
|
|
62
|
+
{ label: 'Meter Cubic', value: 'meterCubic' },
|
|
63
|
+
{ label: 'Mile', value: 'mile' },
|
|
64
|
+
{ label: 'Kilometer', value: 'kilometer' },
|
|
65
|
+
{ label: 'Gigabyte', value: 'gigabyte' },
|
|
66
|
+
{ label: 'Hectar', value: 'hectar' },
|
|
67
|
+
{ label: 'Words', value: 'words' },
|
|
68
|
+
{ label: 'Year', value: 'year' },
|
|
69
|
+
{ label: 'Week', value: 'week' },
|
|
70
|
+
{ label: 'Minute', value: 'minute' },
|
|
71
|
+
{ label: 'Megabyte', value: 'megabyte' },
|
|
72
|
+
{ label: 'Ton', value: 'ton' },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const lineItemsProps = Property.DynamicProperties({
|
|
78
|
+
auth: bokioAuth,
|
|
79
|
+
displayName: 'Item Details',
|
|
80
|
+
description: 'Configure item details based on item type',
|
|
81
|
+
required: false,
|
|
82
|
+
refreshers: ['itemType'],
|
|
83
|
+
props: async ({ itemType }) => {
|
|
84
|
+
const properties: Record<string, any> = {
|
|
85
|
+
description: Property.ShortText({
|
|
86
|
+
displayName: 'Description',
|
|
87
|
+
description: 'Description of the line item',
|
|
88
|
+
required: true,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Only show pricing fields for sales items
|
|
93
|
+
if (itemType === 'salesItem') {
|
|
94
|
+
properties['itemRefId'] = Property.ShortText({
|
|
95
|
+
displayName: 'Item Reference ID',
|
|
96
|
+
description: 'UUID of the item reference',
|
|
97
|
+
required: false,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
properties['productType'] = Property.StaticDropdown({
|
|
101
|
+
displayName: 'Product Type',
|
|
102
|
+
description: 'Type of product',
|
|
103
|
+
required: true,
|
|
104
|
+
options: {
|
|
105
|
+
options: [
|
|
106
|
+
{ label: 'Goods', value: 'goods' },
|
|
107
|
+
{ label: 'Service', value: 'service' },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
properties['unitType'] = unitTypeDropdown;
|
|
113
|
+
|
|
114
|
+
properties['quantity'] = Property.Number({
|
|
115
|
+
displayName: 'Quantity',
|
|
116
|
+
description: 'Quantity of the item',
|
|
117
|
+
required: true,
|
|
118
|
+
defaultValue: 1,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
properties['unitPrice'] = Property.Number({
|
|
122
|
+
displayName: 'Unit Price',
|
|
123
|
+
description: 'Price per unit',
|
|
124
|
+
required: true,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
properties['taxRate'] = Property.Number({
|
|
128
|
+
displayName: 'Tax Rate',
|
|
129
|
+
description: 'Tax rate in percentage (e.g., 25 for 25%)',
|
|
130
|
+
required: false,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return properties;
|
|
135
|
+
},
|
|
136
|
+
});
|