@agnocon/piece-bexio 0.1.7

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.
Files changed (182) hide show
  1. package/LICENSE.MIT-AP +24 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +93 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/actions/create-company.d.ts +28 -0
  7. package/dist/lib/actions/create-company.d.ts.map +1 -0
  8. package/dist/lib/actions/create-company.js +461 -0
  9. package/dist/lib/actions/create-company.js.map +1 -0
  10. package/dist/lib/actions/create-file.d.ts +4 -0
  11. package/dist/lib/actions/create-file.d.ts.map +1 -0
  12. package/dist/lib/actions/create-file.js +48 -0
  13. package/dist/lib/actions/create-file.js.map +1 -0
  14. package/dist/lib/actions/create-manual-entry.d.ts +7 -0
  15. package/dist/lib/actions/create-manual-entry.d.ts.map +1 -0
  16. package/dist/lib/actions/create-manual-entry.js +426 -0
  17. package/dist/lib/actions/create-manual-entry.js.map +1 -0
  18. package/dist/lib/actions/create-person.d.ts +30 -0
  19. package/dist/lib/actions/create-person.d.ts.map +1 -0
  20. package/dist/lib/actions/create-person.js +477 -0
  21. package/dist/lib/actions/create-person.js.map +1 -0
  22. package/dist/lib/actions/create-product.d.ts +34 -0
  23. package/dist/lib/actions/create-product.d.ts.map +1 -0
  24. package/dist/lib/actions/create-product.js +527 -0
  25. package/dist/lib/actions/create-product.js.map +1 -0
  26. package/dist/lib/actions/create-project.d.ts +17 -0
  27. package/dist/lib/actions/create-project.d.ts.map +1 -0
  28. package/dist/lib/actions/create-project.js +316 -0
  29. package/dist/lib/actions/create-project.js.map +1 -0
  30. package/dist/lib/actions/create-sales-invoice.d.ts +25 -0
  31. package/dist/lib/actions/create-sales-invoice.d.ts.map +1 -0
  32. package/dist/lib/actions/create-sales-invoice.js +615 -0
  33. package/dist/lib/actions/create-sales-invoice.js.map +1 -0
  34. package/dist/lib/actions/create-sales-order.d.ts +25 -0
  35. package/dist/lib/actions/create-sales-order.d.ts.map +1 -0
  36. package/dist/lib/actions/create-sales-order.js +642 -0
  37. package/dist/lib/actions/create-sales-order.js.map +1 -0
  38. package/dist/lib/actions/create-sales-quote.d.ts +28 -0
  39. package/dist/lib/actions/create-sales-quote.d.ts.map +1 -0
  40. package/dist/lib/actions/create-sales-quote.js +666 -0
  41. package/dist/lib/actions/create-sales-quote.js.map +1 -0
  42. package/dist/lib/actions/create-time-tracking.d.ts +17 -0
  43. package/dist/lib/actions/create-time-tracking.d.ts.map +1 -0
  44. package/dist/lib/actions/create-time-tracking.js +380 -0
  45. package/dist/lib/actions/create-time-tracking.js.map +1 -0
  46. package/dist/lib/actions/export-invoice-pdf.d.ts +5 -0
  47. package/dist/lib/actions/export-invoice-pdf.d.ts.map +1 -0
  48. package/dist/lib/actions/export-invoice-pdf.js +94 -0
  49. package/dist/lib/actions/export-invoice-pdf.js.map +1 -0
  50. package/dist/lib/actions/find-account.d.ts +6 -0
  51. package/dist/lib/actions/find-account.d.ts.map +1 -0
  52. package/dist/lib/actions/find-account.js +107 -0
  53. package/dist/lib/actions/find-account.js.map +1 -0
  54. package/dist/lib/actions/find-company.d.ts +8 -0
  55. package/dist/lib/actions/find-company.d.ts.map +1 -0
  56. package/dist/lib/actions/find-company.js +160 -0
  57. package/dist/lib/actions/find-company.js.map +1 -0
  58. package/dist/lib/actions/find-country.d.ts +7 -0
  59. package/dist/lib/actions/find-country.d.ts.map +1 -0
  60. package/dist/lib/actions/find-country.js +125 -0
  61. package/dist/lib/actions/find-country.js.map +1 -0
  62. package/dist/lib/actions/find-person.d.ts +9 -0
  63. package/dist/lib/actions/find-person.d.ts.map +1 -0
  64. package/dist/lib/actions/find-person.js +175 -0
  65. package/dist/lib/actions/find-person.js.map +1 -0
  66. package/dist/lib/actions/find-product.d.ts +7 -0
  67. package/dist/lib/actions/find-product.d.ts.map +1 -0
  68. package/dist/lib/actions/find-product.js +124 -0
  69. package/dist/lib/actions/find-product.js.map +1 -0
  70. package/dist/lib/actions/search-invoice.d.ts +7 -0
  71. package/dist/lib/actions/search-invoice.d.ts.map +1 -0
  72. package/dist/lib/actions/search-invoice.js +140 -0
  73. package/dist/lib/actions/search-invoice.js.map +1 -0
  74. package/dist/lib/actions/search-order.d.ts +7 -0
  75. package/dist/lib/actions/search-order.d.ts.map +1 -0
  76. package/dist/lib/actions/search-order.js +139 -0
  77. package/dist/lib/actions/search-order.js.map +1 -0
  78. package/dist/lib/actions/search-quote.d.ts +7 -0
  79. package/dist/lib/actions/search-quote.d.ts.map +1 -0
  80. package/dist/lib/actions/search-quote.js +140 -0
  81. package/dist/lib/actions/search-quote.js.map +1 -0
  82. package/dist/lib/actions/send-sales-invoice.d.ts +9 -0
  83. package/dist/lib/actions/send-sales-invoice.d.ts.map +1 -0
  84. package/dist/lib/actions/send-sales-invoice.js +110 -0
  85. package/dist/lib/actions/send-sales-invoice.js.map +1 -0
  86. package/dist/lib/actions/update-company.d.ts +29 -0
  87. package/dist/lib/actions/update-company.d.ts.map +1 -0
  88. package/dist/lib/actions/update-company.js +506 -0
  89. package/dist/lib/actions/update-company.js.map +1 -0
  90. package/dist/lib/actions/update-person.d.ts +31 -0
  91. package/dist/lib/actions/update-person.d.ts.map +1 -0
  92. package/dist/lib/actions/update-person.js +524 -0
  93. package/dist/lib/actions/update-person.js.map +1 -0
  94. package/dist/lib/actions/update-product.d.ts +34 -0
  95. package/dist/lib/actions/update-product.d.ts.map +1 -0
  96. package/dist/lib/actions/update-product.js +556 -0
  97. package/dist/lib/actions/update-product.js.map +1 -0
  98. package/dist/lib/auth.d.ts +2 -0
  99. package/dist/lib/auth.d.ts.map +1 -0
  100. package/dist/lib/auth.js +51 -0
  101. package/dist/lib/auth.js.map +1 -0
  102. package/dist/lib/common/client.d.ts +13 -0
  103. package/dist/lib/common/client.d.ts.map +1 -0
  104. package/dist/lib/common/client.js +64 -0
  105. package/dist/lib/common/client.js.map +1 -0
  106. package/dist/lib/common/index.d.ts +5 -0
  107. package/dist/lib/common/index.d.ts.map +1 -0
  108. package/dist/lib/common/index.js +10 -0
  109. package/dist/lib/common/index.js.map +1 -0
  110. package/dist/lib/common/props.d.ts +14 -0
  111. package/dist/lib/common/props.d.ts.map +1 -0
  112. package/dist/lib/common/props.js +118 -0
  113. package/dist/lib/common/props.js.map +1 -0
  114. package/dist/lib/common/types.d.ts +89 -0
  115. package/dist/lib/common/types.d.ts.map +1 -0
  116. package/dist/lib/common/types.js +6 -0
  117. package/dist/lib/common/types.js.map +1 -0
  118. package/dist/lib/triggers/new-order.d.ts +11 -0
  119. package/dist/lib/triggers/new-order.d.ts.map +1 -0
  120. package/dist/lib/triggers/new-order.js +152 -0
  121. package/dist/lib/triggers/new-order.js.map +1 -0
  122. package/dist/lib/triggers/new-product.d.ts +3 -0
  123. package/dist/lib/triggers/new-product.d.ts.map +1 -0
  124. package/dist/lib/triggers/new-product.js +111 -0
  125. package/dist/lib/triggers/new-product.js.map +1 -0
  126. package/dist/lib/triggers/new-project.d.ts +11 -0
  127. package/dist/lib/triggers/new-project.d.ts.map +1 -0
  128. package/dist/lib/triggers/new-project.js +141 -0
  129. package/dist/lib/triggers/new-project.js.map +1 -0
  130. package/dist/lib/triggers/new-quote.d.ts +11 -0
  131. package/dist/lib/triggers/new-quote.d.ts.map +1 -0
  132. package/dist/lib/triggers/new-quote.js +153 -0
  133. package/dist/lib/triggers/new-quote.js.map +1 -0
  134. package/dist/lib/triggers/new-sales-invoice.d.ts +11 -0
  135. package/dist/lib/triggers/new-sales-invoice.d.ts.map +1 -0
  136. package/dist/lib/triggers/new-sales-invoice.js +153 -0
  137. package/dist/lib/triggers/new-sales-invoice.js.map +1 -0
  138. package/dist/lib/triggers/new-updated-company.d.ts +3 -0
  139. package/dist/lib/triggers/new-updated-company.d.ts.map +1 -0
  140. package/dist/lib/triggers/new-updated-company.js +122 -0
  141. package/dist/lib/triggers/new-updated-company.js.map +1 -0
  142. package/dist/lib/triggers/new-updated-person.d.ts +3 -0
  143. package/dist/lib/triggers/new-updated-person.d.ts.map +1 -0
  144. package/dist/lib/triggers/new-updated-person.js +122 -0
  145. package/dist/lib/triggers/new-updated-person.js.map +1 -0
  146. package/package.json +48 -0
  147. package/src/index.ts +89 -0
  148. package/src/lib/actions/create-company.ts +464 -0
  149. package/src/lib/actions/create-file.ts +62 -0
  150. package/src/lib/actions/create-manual-entry.ts +445 -0
  151. package/src/lib/actions/create-person.ts +481 -0
  152. package/src/lib/actions/create-product.ts +563 -0
  153. package/src/lib/actions/create-project.ts +344 -0
  154. package/src/lib/actions/create-sales-invoice.ts +656 -0
  155. package/src/lib/actions/create-sales-order.ts +689 -0
  156. package/src/lib/actions/create-sales-quote.ts +713 -0
  157. package/src/lib/actions/create-time-tracking.ts +418 -0
  158. package/src/lib/actions/export-invoice-pdf.ts +104 -0
  159. package/src/lib/actions/find-account.ts +114 -0
  160. package/src/lib/actions/find-company.ts +193 -0
  161. package/src/lib/actions/find-country.ts +127 -0
  162. package/src/lib/actions/find-person.ts +208 -0
  163. package/src/lib/actions/find-product.ts +161 -0
  164. package/src/lib/actions/search-invoice.ts +157 -0
  165. package/src/lib/actions/search-order.ts +155 -0
  166. package/src/lib/actions/search-quote.ts +157 -0
  167. package/src/lib/actions/send-sales-invoice.ts +118 -0
  168. package/src/lib/actions/update-company.ts +518 -0
  169. package/src/lib/actions/update-person.ts +536 -0
  170. package/src/lib/actions/update-product.ts +594 -0
  171. package/src/lib/auth.ts +44 -0
  172. package/src/lib/common/client.ts +65 -0
  173. package/src/lib/common/index.ts +5 -0
  174. package/src/lib/common/props.ts +127 -0
  175. package/src/lib/common/types.ts +99 -0
  176. package/src/lib/triggers/new-order.ts +184 -0
  177. package/src/lib/triggers/new-product.ts +154 -0
  178. package/src/lib/triggers/new-project.ts +165 -0
  179. package/src/lib/triggers/new-quote.ts +186 -0
  180. package/src/lib/triggers/new-sales-invoice.ts +186 -0
  181. package/src/lib/triggers/new-updated-company.ts +161 -0
  182. package/src/lib/triggers/new-updated-person.ts +161 -0
@@ -0,0 +1,713 @@
1
+ import { createAction, Property, OAuth2PropertyValue } from '@agnocon/pieces-framework';
2
+ import { bexioAuth } from '../auth';
3
+ import { BexioClient } from '../common/client';
4
+
5
+ export const createSalesQuoteAction = createAction({
6
+ auth: bexioAuth,
7
+ name: 'create_sales_quote',
8
+ displayName: 'Create Sales Quote',
9
+ description: 'Create a new product-based sales quote',
10
+ audience: 'both',
11
+ aiMetadata: { description: 'Create a new Bexio sales quote (kb_offer) with required user, language, currency, payment type, bank account, tax handling, validity dates, and at least one line-item position (custom, article, text, subtotal, discount, or page-break). Not idempotent: each call creates a separate quote, so guard against duplicates. Requires resolved IDs for contact, currency, taxes, units, and accounts; look these up first if not known.', idempotent: false },
12
+ props: {
13
+ document_nr: Property.ShortText({
14
+ displayName: 'Document Number',
15
+ description: 'Quote number (required if automatic numbering is disabled)',
16
+ required: false,
17
+ }),
18
+ title: Property.ShortText({
19
+ displayName: 'Title',
20
+ description: 'Quote title',
21
+ required: false,
22
+ }),
23
+ contact_id: Property.Dropdown({
24
+ auth: bexioAuth,
25
+ displayName: 'Contact',
26
+ description: 'The contact for this quote',
27
+ required: false,
28
+ refreshers: [],
29
+ options: async ({ auth }) => {
30
+ if (!auth) {
31
+ return {
32
+ disabled: true,
33
+ placeholder: 'Connect your Bexio account first',
34
+ options: [],
35
+ };
36
+ }
37
+
38
+ try {
39
+ const client = new BexioClient(auth);
40
+ const contacts = await client.get<Array<{
41
+ id: number;
42
+ contact_type_id: number;
43
+ name_1: string;
44
+ name_2?: string | null;
45
+ nr?: string | null;
46
+ }>>('/2.0/contact');
47
+
48
+ return {
49
+ disabled: false,
50
+ options: contacts.map((contact) => {
51
+ const name = contact.name_2
52
+ ? `${contact.name_2} ${contact.name_1}`
53
+ : contact.name_1;
54
+ const label = contact.nr ? `${name} (#${contact.nr})` : name;
55
+ return {
56
+ label,
57
+ value: contact.id,
58
+ };
59
+ }),
60
+ };
61
+ } catch (error) {
62
+ return {
63
+ disabled: true,
64
+ placeholder: 'Failed to load contacts',
65
+ options: [],
66
+ };
67
+ }
68
+ },
69
+ }),
70
+ contact_sub_id: Property.Dropdown({
71
+ auth: bexioAuth,
72
+ displayName: 'Contact Sub',
73
+ description: 'Contact sub-address (optional)',
74
+ required: false,
75
+ refreshers: [],
76
+ options: async ({ auth }) => {
77
+ if (!auth) {
78
+ return {
79
+ disabled: true,
80
+ placeholder: 'Connect your Bexio account first',
81
+ options: [],
82
+ };
83
+ }
84
+
85
+ try {
86
+ const client = new BexioClient(auth);
87
+ const contacts = await client.get<Array<{
88
+ id: number;
89
+ contact_type_id: number;
90
+ name_1: string;
91
+ name_2?: string | null;
92
+ nr?: string | null;
93
+ }>>('/2.0/contact');
94
+
95
+ return {
96
+ disabled: false,
97
+ options: contacts.map((contact) => {
98
+ const name = contact.name_2
99
+ ? `${contact.name_2} ${contact.name_1}`
100
+ : contact.name_1;
101
+ const label = contact.nr ? `${name} (#${contact.nr})` : name;
102
+ return {
103
+ label,
104
+ value: contact.id,
105
+ };
106
+ }),
107
+ };
108
+ } catch (error) {
109
+ return {
110
+ disabled: true,
111
+ placeholder: 'Failed to load contacts',
112
+ options: [],
113
+ };
114
+ }
115
+ },
116
+ }),
117
+ user_id: Property.Dropdown({
118
+ auth: bexioAuth,
119
+ displayName: 'User',
120
+ description: 'User responsible for this quote',
121
+ required: true,
122
+ refreshers: [],
123
+ options: async ({ auth }) => {
124
+ if (!auth) {
125
+ return {
126
+ disabled: true,
127
+ placeholder: 'Connect your Bexio account first',
128
+ options: [],
129
+ };
130
+ }
131
+
132
+ try {
133
+ const client = new BexioClient(auth);
134
+ const users = await client.get<Array<{
135
+ id: number;
136
+ firstname?: string | null;
137
+ lastname?: string | null;
138
+ email: string;
139
+ }>>('/3.0/users');
140
+
141
+ return {
142
+ disabled: false,
143
+ options: users.map((user) => {
144
+ const name = user.firstname && user.lastname
145
+ ? `${user.firstname} ${user.lastname}`
146
+ : user.email;
147
+ return {
148
+ label: name,
149
+ value: user.id,
150
+ };
151
+ }),
152
+ };
153
+ } catch (error) {
154
+ return {
155
+ disabled: true,
156
+ placeholder: 'Failed to load users',
157
+ options: [],
158
+ };
159
+ }
160
+ },
161
+ }),
162
+ pr_project_id: Property.Dropdown({
163
+ auth: bexioAuth,
164
+ displayName: 'Project',
165
+ description: 'Project associated with this quote',
166
+ required: false,
167
+ refreshers: [],
168
+ options: async ({ auth }) => {
169
+ if (!auth) {
170
+ return {
171
+ disabled: true,
172
+ placeholder: 'Connect your Bexio account first',
173
+ options: [],
174
+ };
175
+ }
176
+
177
+ try {
178
+ const client = new BexioClient(auth);
179
+ const projects = await client.get<Array<{
180
+ id: number;
181
+ name: string;
182
+ nr?: string;
183
+ }>>('/2.0/pr_project');
184
+
185
+ return {
186
+ disabled: false,
187
+ options: projects.map((project) => ({
188
+ label: project.nr ? `${project.name} (#${project.nr})` : project.name,
189
+ value: project.id,
190
+ })),
191
+ };
192
+ } catch (error) {
193
+ return {
194
+ disabled: true,
195
+ placeholder: 'Failed to load projects',
196
+ options: [],
197
+ };
198
+ }
199
+ },
200
+ }),
201
+ language_id: Property.Dropdown({
202
+ auth: bexioAuth,
203
+ displayName: 'Language',
204
+ description: 'Language for the quote',
205
+ required: true,
206
+ refreshers: [],
207
+ options: async ({ auth }) => {
208
+ if (!auth) {
209
+ return {
210
+ disabled: true,
211
+ placeholder: 'Connect your Bexio account first',
212
+ options: [],
213
+ };
214
+ }
215
+
216
+ try {
217
+ const client = new BexioClient(auth);
218
+ const languages = await client.get<Array<{ id: number; name: string }>>('/2.0/language');
219
+
220
+ return {
221
+ disabled: false,
222
+ options: languages.map((lang) => ({
223
+ label: lang.name,
224
+ value: lang.id,
225
+ })),
226
+ };
227
+ } catch (error) {
228
+ return {
229
+ disabled: true,
230
+ placeholder: 'Failed to load languages',
231
+ options: [],
232
+ };
233
+ }
234
+ },
235
+ }),
236
+ bank_account_id: Property.Dropdown({
237
+ auth: bexioAuth,
238
+ displayName: 'Bank Account',
239
+ description: 'Bank account for payment',
240
+ required: true,
241
+ refreshers: [],
242
+ options: async ({ auth }) => {
243
+ if (!auth) {
244
+ return {
245
+ disabled: true,
246
+ placeholder: 'Connect your Bexio account first',
247
+ options: [],
248
+ };
249
+ }
250
+
251
+ try {
252
+ const client = new BexioClient(auth);
253
+ const bankAccounts = await client.get<Array<{
254
+ id: number;
255
+ name: string;
256
+ iban_nr?: string;
257
+ bank_account_nr?: string;
258
+ }>>('/3.0/banking/accounts');
259
+
260
+ return {
261
+ disabled: false,
262
+ options: bankAccounts.map((account) => {
263
+ const iban = account.iban_nr || account.bank_account_nr;
264
+ return {
265
+ label: iban ? `${account.name} (${iban})` : account.name,
266
+ value: account.id,
267
+ };
268
+ }),
269
+ };
270
+ } catch (error) {
271
+ return {
272
+ disabled: true,
273
+ placeholder: 'Failed to load bank accounts',
274
+ options: [],
275
+ };
276
+ }
277
+ },
278
+ }),
279
+ currency_id: Property.Dropdown({
280
+ auth: bexioAuth,
281
+ displayName: 'Currency',
282
+ description: 'Currency for this quote',
283
+ required: true,
284
+ refreshers: [],
285
+ options: async ({ auth }) => {
286
+ if (!auth) {
287
+ return {
288
+ disabled: true,
289
+ placeholder: 'Connect your Bexio account first',
290
+ options: [],
291
+ };
292
+ }
293
+
294
+ try {
295
+ const client = new BexioClient(auth);
296
+ const currencies = await client.get<Array<{ id: number; name: string }>>('/currencies');
297
+
298
+ return {
299
+ disabled: false,
300
+ options: currencies.map((currency) => ({
301
+ label: currency.name,
302
+ value: currency.id,
303
+ })),
304
+ };
305
+ } catch (error) {
306
+ return {
307
+ disabled: true,
308
+ placeholder: 'Failed to load currencies',
309
+ options: [],
310
+ };
311
+ }
312
+ },
313
+ }),
314
+ payment_type_id: Property.Dropdown({
315
+ auth: bexioAuth,
316
+ displayName: 'Payment Type',
317
+ description: 'Payment type for this quote',
318
+ required: true,
319
+ refreshers: [],
320
+ options: async ({ auth }) => {
321
+ if (!auth) {
322
+ return {
323
+ disabled: true,
324
+ placeholder: 'Connect your Bexio account first',
325
+ options: [],
326
+ };
327
+ }
328
+
329
+ try {
330
+ const client = new BexioClient(auth);
331
+ const paymentTypes = await client.get<Array<{ id: number; name: string }>>('/2.0/payment_type');
332
+
333
+ return {
334
+ disabled: false,
335
+ options: paymentTypes.map((type) => ({
336
+ label: type.name,
337
+ value: type.id,
338
+ })),
339
+ };
340
+ } catch (error) {
341
+ return {
342
+ disabled: true,
343
+ placeholder: 'Failed to load payment types',
344
+ options: [],
345
+ };
346
+ }
347
+ },
348
+ }),
349
+ header: Property.LongText({
350
+ displayName: 'Header',
351
+ description: 'Header text for the quote',
352
+ required: false,
353
+ }),
354
+ footer: Property.LongText({
355
+ displayName: 'Footer',
356
+ description: 'Footer text for the quote',
357
+ required: false,
358
+ }),
359
+ mwst_type: Property.StaticDropdown({
360
+ displayName: 'Tax Type',
361
+ description: 'How taxes are handled',
362
+ required: true,
363
+ defaultValue: 0,
364
+ options: {
365
+ disabled: false,
366
+ options: [
367
+ { label: 'Including taxes', value: 0 },
368
+ { label: 'Excluding taxes', value: 1 },
369
+ { label: 'Exempt from taxes', value: 2 },
370
+ ],
371
+ },
372
+ }),
373
+ mwst_is_net: Property.Checkbox({
374
+ displayName: 'Tax is Net',
375
+ description: 'If taxes are included, set to true to add taxes to total, false to include in total',
376
+ required: false,
377
+ defaultValue: false,
378
+ }),
379
+ show_position_taxes: Property.Checkbox({
380
+ displayName: 'Show Position Taxes',
381
+ description: 'Show taxes for each position',
382
+ required: false,
383
+ defaultValue: false,
384
+ }),
385
+ is_valid_from: Property.ShortText({
386
+ displayName: 'Valid From',
387
+ description: 'Quote valid from date (YYYY-MM-DD)',
388
+ required: true,
389
+ }),
390
+ is_valid_until: Property.ShortText({
391
+ displayName: 'Valid Until',
392
+ description: 'Quote valid until date (YYYY-MM-DD)',
393
+ required: true,
394
+ }),
395
+ contact_address_manual: Property.LongText({
396
+ displayName: 'Manual Contact Address',
397
+ description: 'Override contact address (leave empty to use contact address)',
398
+ required: false,
399
+ }),
400
+ delivery_address_type: Property.StaticDropdown({
401
+ displayName: 'Delivery Address Type',
402
+ description: 'Type of delivery address',
403
+ required: false,
404
+ defaultValue: 0,
405
+ options: {
406
+ disabled: false,
407
+ options: [
408
+ { label: 'Use Invoice Address', value: 0 },
409
+ { label: 'Use Custom Address', value: 1 },
410
+ ],
411
+ },
412
+ }),
413
+ delivery_address_manual_text: Property.LongText({
414
+ displayName: 'Manual Delivery Address',
415
+ description: 'Custom delivery address (only used if delivery address type is set to custom)',
416
+ required: false,
417
+ }),
418
+ api_reference: Property.ShortText({
419
+ displayName: 'API Reference',
420
+ description: 'Reference for API use (can only be edited via API)',
421
+ required: false,
422
+ }),
423
+ viewed_by_client_at: Property.DateTime({
424
+ displayName: 'Viewed By Client At',
425
+ description: 'Date when the quote was viewed by the client',
426
+ required: false,
427
+ }),
428
+ kb_terms_of_payment_template_id: Property.Number({
429
+ displayName: 'Terms of Payment Template ID',
430
+ description: 'Terms of payment template ID',
431
+ required: false,
432
+ }),
433
+ template_slug: Property.Dropdown({
434
+ auth: bexioAuth,
435
+ displayName: 'Document Template',
436
+ description: 'Document template for the quote',
437
+ required: false,
438
+ refreshers: [],
439
+ options: async ({ auth }) => {
440
+ if (!auth) {
441
+ return {
442
+ disabled: true,
443
+ placeholder: 'Connect your Bexio account first',
444
+ options: [],
445
+ };
446
+ }
447
+
448
+ try {
449
+ const client = new BexioClient(auth);
450
+ const templates = await client.get<Array<{
451
+ template_slug: string;
452
+ name: string;
453
+ is_default: boolean;
454
+ default_for_document_types: string[];
455
+ }>>('/3.0/document_templates');
456
+
457
+ return {
458
+ disabled: false,
459
+ options: templates.map((template) => ({
460
+ label: template.is_default ? `${template.name} (Default)` : template.name,
461
+ value: template.template_slug,
462
+ })),
463
+ };
464
+ } catch (error) {
465
+ return {
466
+ disabled: true,
467
+ placeholder: 'Failed to load templates',
468
+ options: [],
469
+ };
470
+ }
471
+ },
472
+ }),
473
+ positionFields: Property.DynamicProperties({
474
+ auth: bexioAuth,
475
+ displayName: 'Quote Positions',
476
+ description: 'Configure quote line items',
477
+ required: true,
478
+ refreshers: ['auth'],
479
+ props: async ({ auth }) => {
480
+ let units: Array<{ id: number; name: string }> = [];
481
+ let accounts: Array<{ id: number; account_no: string; name: string }> = [];
482
+ let taxes: Array<{ id: number; name: string; percentage: string }> = [];
483
+
484
+ if (auth) {
485
+ try {
486
+ const client = new BexioClient(auth);
487
+ units = await client.get<Array<{ id: number; name: string }>>('/2.0/unit').catch(() => []);
488
+ accounts = await client.get<Array<{ id: number; account_no: string; name: string }>>('/accounts').catch(() => []);
489
+ const taxesResponse = await client.get<Array<{
490
+ id: number;
491
+ name: string;
492
+ value: number;
493
+ display_name?: string;
494
+ }>>('/3.0/taxes?types=sales_tax&scope=active').catch(() => []);
495
+ taxes = taxesResponse.map((tax) => ({
496
+ id: tax.id,
497
+ name: tax.display_name || tax.name,
498
+ percentage: tax.value.toString(),
499
+ }));
500
+ } catch (error) {
501
+ // Ignore error, use empty array as fallback
502
+ }
503
+ }
504
+
505
+ const unitOptions = units.map((unit) => ({ label: unit.name, value: unit.id }));
506
+ const accountOptions = accounts.map((acc) => ({ label: `${acc.account_no} - ${acc.name}`, value: acc.id }));
507
+ const taxOptions = taxes.map((tax) => ({ label: `${tax.name} (${tax.percentage}%)`, value: tax.id }));
508
+
509
+ return {
510
+ positions: Property.Array({
511
+ displayName: 'Positions',
512
+ description: 'Quote line items',
513
+ required: true,
514
+ properties: {
515
+ type: Property.StaticDropdown({
516
+ displayName: 'Position Type',
517
+ description: 'Type of quote position',
518
+ required: true,
519
+ defaultValue: 'KbPositionCustom',
520
+ options: {
521
+ disabled: false,
522
+ options: [
523
+ { label: 'Custom Position', value: 'KbPositionCustom' },
524
+ { label: 'Article Position', value: 'KbPositionArticle' },
525
+ { label: 'Text Position', value: 'KbPositionText' },
526
+ { label: 'Subtotal Position', value: 'KbPositionSubtotal' },
527
+ { label: 'Page Break Position', value: 'KbPositionPagebreak' },
528
+ { label: 'Discount Position', value: 'KbPositionDiscount' },
529
+ ],
530
+ },
531
+ }),
532
+ amount: Property.ShortText({
533
+ displayName: 'Amount',
534
+ description: 'Quantity/amount',
535
+ required: true,
536
+ }),
537
+ unit_id: unitOptions.length > 0
538
+ ? Property.StaticDropdown({
539
+ displayName: 'Unit',
540
+ description: 'Unit of measurement',
541
+ required: false,
542
+ options: {
543
+ disabled: false,
544
+ options: unitOptions,
545
+ },
546
+ })
547
+ : Property.Number({
548
+ displayName: 'Unit ID',
549
+ description: 'Unit ID (use List Units action to find IDs)',
550
+ required: false,
551
+ }),
552
+ account_id: accountOptions.length > 0
553
+ ? Property.StaticDropdown({
554
+ displayName: 'Account',
555
+ description: 'Account for this position',
556
+ required: false,
557
+ options: {
558
+ disabled: false,
559
+ options: accountOptions,
560
+ },
561
+ })
562
+ : Property.Number({
563
+ displayName: 'Account ID',
564
+ description: 'Account ID (use List Accounts action to find IDs)',
565
+ required: false,
566
+ }),
567
+ tax_id: taxOptions.length > 0
568
+ ? Property.StaticDropdown({
569
+ displayName: 'Tax',
570
+ description: 'Tax rate (only active sales taxes)',
571
+ required: false,
572
+ options: {
573
+ disabled: false,
574
+ options: taxOptions,
575
+ },
576
+ })
577
+ : Property.Number({
578
+ displayName: 'Tax ID',
579
+ description: 'Tax ID (use List Taxes action to find IDs)',
580
+ required: false,
581
+ }),
582
+ text: Property.LongText({
583
+ displayName: 'Description',
584
+ description: 'Position description/text',
585
+ required: false,
586
+ }),
587
+ unit_price: Property.ShortText({
588
+ displayName: 'Unit Price',
589
+ description: 'Price per unit (max 6 decimals)',
590
+ required: false,
591
+ }),
592
+ discount_in_percent: Property.ShortText({
593
+ displayName: 'Discount (%)',
594
+ description: 'Discount percentage (max 6 decimals)',
595
+ required: false,
596
+ }),
597
+ parent_id: Property.Number({
598
+ displayName: 'Parent ID',
599
+ description: 'Parent position ID (for grouped positions)',
600
+ required: false,
601
+ }),
602
+ },
603
+ }),
604
+ };
605
+ },
606
+ }),
607
+ },
608
+ async run(context) {
609
+ const client = new BexioClient(context.auth);
610
+ const props = context.propsValue;
611
+
612
+ const requestBody: Record<string, unknown> = {
613
+ user_id: props['user_id'],
614
+ language_id: props['language_id'],
615
+ bank_account_id: props['bank_account_id'],
616
+ currency_id: props['currency_id'],
617
+ payment_type_id: props['payment_type_id'],
618
+ mwst_type: props['mwst_type'],
619
+ is_valid_from: props['is_valid_from'],
620
+ is_valid_until: props['is_valid_until'],
621
+ };
622
+
623
+ if (props['document_nr']) {
624
+ requestBody['document_nr'] = props['document_nr'];
625
+ }
626
+ if (props['title'] !== undefined) {
627
+ requestBody['title'] = props['title'] || null;
628
+ }
629
+ if (props['contact_id']) {
630
+ requestBody['contact_id'] = props['contact_id'];
631
+ }
632
+ if (props['contact_sub_id']) {
633
+ requestBody['contact_sub_id'] = props['contact_sub_id'];
634
+ }
635
+ if (props['pr_project_id']) {
636
+ requestBody['pr_project_id'] = props['pr_project_id'];
637
+ }
638
+ if (props['header']) {
639
+ requestBody['header'] = props['header'];
640
+ }
641
+ if (props['footer']) {
642
+ requestBody['footer'] = props['footer'];
643
+ }
644
+ if (props['mwst_is_net'] !== undefined) {
645
+ requestBody['mwst_is_net'] = props['mwst_is_net'];
646
+ }
647
+ if (props['show_position_taxes'] !== undefined) {
648
+ requestBody['show_position_taxes'] = props['show_position_taxes'];
649
+ }
650
+ if (props['contact_address_manual']) {
651
+ requestBody['contact_address_manual'] = props['contact_address_manual'];
652
+ }
653
+ if (props['delivery_address_type'] !== undefined) {
654
+ requestBody['delivery_address_type'] = props['delivery_address_type'];
655
+ }
656
+ if (props['delivery_address_manual_text']) {
657
+ requestBody['delivery_address_manual'] = props['delivery_address_manual_text'];
658
+ }
659
+ if (props['api_reference']) {
660
+ requestBody['api_reference'] = props['api_reference'];
661
+ }
662
+ if (props['viewed_by_client_at']) {
663
+ requestBody['viewed_by_client_at'] = props['viewed_by_client_at'];
664
+ }
665
+ if (props['kb_terms_of_payment_template_id']) {
666
+ requestBody['kb_terms_of_payment_template_id'] = props['kb_terms_of_payment_template_id'];
667
+ }
668
+ if (props['template_slug']) {
669
+ requestBody['template_slug'] = props['template_slug'];
670
+ }
671
+
672
+ const positionFields = props['positionFields'] as Record<string, unknown>;
673
+ if (positionFields && positionFields['positions'] && Array.isArray(positionFields['positions'])) {
674
+ requestBody['positions'] = (positionFields['positions'] as Array<Record<string, unknown>>).map((position) => {
675
+ const pos: Record<string, unknown> = {
676
+ type: position['type'] || 'KbPositionCustom',
677
+ };
678
+
679
+ if (position['amount']) {
680
+ pos['amount'] = position['amount'];
681
+ }
682
+ if (position['unit_id']) {
683
+ pos['unit_id'] = position['unit_id'];
684
+ }
685
+ if (position['account_id']) {
686
+ pos['account_id'] = position['account_id'];
687
+ }
688
+ if (position['tax_id']) {
689
+ pos['tax_id'] = position['tax_id'];
690
+ }
691
+ if (position['text']) {
692
+ pos['text'] = position['text'];
693
+ }
694
+ if (position['unit_price']) {
695
+ pos['unit_price'] = position['unit_price'];
696
+ }
697
+ if (position['discount_in_percent']) {
698
+ pos['discount_in_percent'] = position['discount_in_percent'];
699
+ }
700
+ if (position['parent_id']) {
701
+ pos['parent_id'] = position['parent_id'];
702
+ }
703
+
704
+ return pos;
705
+ });
706
+ }
707
+
708
+ const response = await client.post('/2.0/kb_offer', requestBody);
709
+
710
+ return response;
711
+ },
712
+ });
713
+