@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,165 @@
1
+ import { createTrigger, TriggerStrategy, PiecePropValueSchema, Property, AppConnectionValueForAuthProperty } from '@agnocon/pieces-framework';
2
+ import { DedupeStrategy, Polling, pollingHelper } from '@agnocon/pieces-common';
3
+ import { bexioAuth } from '../auth';
4
+ import { BexioClient } from '../common/client';
5
+ import { OAuth2PropertyValue } from '@agnocon/pieces-framework';
6
+
7
+ const polling: Polling<
8
+ AppConnectionValueForAuthProperty<typeof bexioAuth>,
9
+ { status_id?: number }
10
+ > = {
11
+ strategy: DedupeStrategy.LAST_ITEM,
12
+ items: async ({ auth, lastItemId, propsValue }) => {
13
+ const client = new BexioClient(auth);
14
+
15
+ const searchBody: Array<{ field: string; value: string; criteria: string }> = [
16
+ {
17
+ field: 'name',
18
+ value: '',
19
+ criteria: 'not_null',
20
+ },
21
+ ];
22
+
23
+ if (propsValue.status_id !== undefined && propsValue.status_id !== null) {
24
+ searchBody.push({
25
+ field: 'pr_state_id',
26
+ value: propsValue.status_id.toString(),
27
+ criteria: '=',
28
+ });
29
+ }
30
+
31
+ const queryParams: Record<string, string> = {
32
+ order_by: 'id_desc',
33
+ limit: '500',
34
+ };
35
+
36
+ const projects = await client.post<Array<{
37
+ id: number;
38
+ uuid: string;
39
+ nr: string;
40
+ name: string;
41
+ start_date: string | null;
42
+ end_date: string | null;
43
+ comment: string | null;
44
+ pr_state_id: number;
45
+ pr_project_type_id: number;
46
+ contact_id: number | null;
47
+ contact_sub_id: number | null;
48
+ pr_invoice_type_id: number | null;
49
+ pr_invoice_type_amount: string | null;
50
+ pr_budget_type_id: number | null;
51
+ pr_budget_type_amount: string | null;
52
+ user_id: number;
53
+ }>>('/2.0/pr_project/search', searchBody, queryParams);
54
+
55
+ return projects.map((project) => ({
56
+ id: project.id,
57
+ data: project,
58
+ }));
59
+ },
60
+ };
61
+
62
+ export const newProjectTrigger = createTrigger({
63
+ auth: bexioAuth,
64
+ name: 'new_project',
65
+ displayName: 'New Project',
66
+ description: 'Triggers when a Project is created or updated with the chosen status',
67
+ aiMetadata: {
68
+ description: 'Fires when a project is created or updated in Bexio, optionally filtered to a chosen project status. Emits each matching project with its number, name, dates, status, type, contact, invoicing and budget settings. Use to track new or changing projects.',
69
+ },
70
+ type: TriggerStrategy.POLLING,
71
+ props: {
72
+ status_id: Property.Dropdown({
73
+ auth: bexioAuth,
74
+ displayName: 'Status',
75
+ description: 'Filter projects by status (leave empty to trigger for all statuses)',
76
+ required: false,
77
+ refreshers: [],
78
+ options: async ({ auth }) => {
79
+ if (!auth) {
80
+ return {
81
+ disabled: true,
82
+ placeholder: 'Connect your Bexio account first',
83
+ options: [],
84
+ };
85
+ }
86
+
87
+ try {
88
+ const client = new BexioClient(auth);
89
+ const statuses = await client.get<Array<{ id: number; name: string }>>('/2.0/pr_project_state').catch(() => []);
90
+
91
+ if (statuses.length === 0) {
92
+ return {
93
+ disabled: false,
94
+ placeholder: 'Status filter not available - will trigger for all statuses',
95
+ options: [],
96
+ };
97
+ }
98
+
99
+ return {
100
+ disabled: false,
101
+ options: statuses.map((status) => ({
102
+ label: status.name,
103
+ value: status.id,
104
+ })),
105
+ };
106
+ } catch (error) {
107
+ return {
108
+ disabled: false,
109
+ placeholder: 'Enter status ID manually or leave empty for all statuses',
110
+ options: [],
111
+ };
112
+ }
113
+ },
114
+ }),
115
+ },
116
+ sampleData: {
117
+ id: 2,
118
+ uuid: '046b6c7f-0b8a-43b9-b35d-6489e6daee91',
119
+ nr: '000002',
120
+ name: 'Villa Kunterbunt',
121
+ start_date: '2019-07-12 00:00:00',
122
+ end_date: null,
123
+ comment: '',
124
+ pr_state_id: 2,
125
+ pr_project_type_id: 2,
126
+ contact_id: 2,
127
+ contact_sub_id: null,
128
+ pr_invoice_type_id: 3,
129
+ pr_invoice_type_amount: '230.00',
130
+ pr_budget_type_id: 1,
131
+ pr_budget_type_amount: '200.00',
132
+ user_id: 1,
133
+ },
134
+ onEnable: async (context) => {
135
+ await pollingHelper.onEnable(polling, {
136
+ auth: context.auth,
137
+ store: context.store,
138
+ propsValue: context.propsValue,
139
+ });
140
+ },
141
+ onDisable: async (context) => {
142
+ await pollingHelper.onDisable(polling, {
143
+ auth: context.auth,
144
+ store: context.store,
145
+ propsValue: context.propsValue,
146
+ });
147
+ },
148
+ test: async (context) => {
149
+ return await pollingHelper.test(polling, {
150
+ auth: context.auth,
151
+ store: context.store,
152
+ propsValue: context.propsValue,
153
+ files: context.files,
154
+ });
155
+ },
156
+ run: async (context) => {
157
+ return await pollingHelper.poll(polling, {
158
+ auth: context.auth,
159
+ store: context.store,
160
+ propsValue: context.propsValue,
161
+ files: context.files,
162
+ });
163
+ },
164
+ });
165
+
@@ -0,0 +1,186 @@
1
+ import { createTrigger, TriggerStrategy, Property, AppConnectionValueForAuthProperty } from '@agnocon/pieces-framework';
2
+ import { DedupeStrategy, Polling, pollingHelper } from '@agnocon/pieces-common';
3
+ import { bexioAuth } from '../auth';
4
+ import { BexioClient } from '../common/client';
5
+ import { OAuth2PropertyValue } from '@agnocon/pieces-framework';
6
+ import dayjs from 'dayjs';
7
+
8
+ const polling: Polling<
9
+ AppConnectionValueForAuthProperty<typeof bexioAuth>,
10
+ { status_id?: number }
11
+ > = {
12
+ strategy: DedupeStrategy.TIMEBASED,
13
+ items: async ({ auth, lastFetchEpochMS, propsValue }) => {
14
+ const client = new BexioClient(auth);
15
+
16
+ const isTest = lastFetchEpochMS === 0;
17
+
18
+ const lastFetchDate = isTest
19
+ ? dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss')
20
+ : dayjs(lastFetchEpochMS).format('YYYY-MM-DD HH:mm:ss');
21
+
22
+ const searchBody: Array<{ field: string; value: string; criteria: string }> = [
23
+ {
24
+ field: 'updated_at',
25
+ value: lastFetchDate,
26
+ criteria: '>=',
27
+ },
28
+ ];
29
+
30
+ if (propsValue.status_id !== undefined && propsValue.status_id !== null) {
31
+ searchBody.push({
32
+ field: 'kb_item_status_id',
33
+ value: propsValue.status_id.toString(),
34
+ criteria: '=',
35
+ });
36
+ }
37
+
38
+ const queryParams: Record<string, string> = {
39
+ order_by: 'updated_at_desc',
40
+ limit: isTest ? '5' : '500',
41
+ };
42
+
43
+ const quotes = await client.post<Array<{
44
+ id: number;
45
+ document_nr: string;
46
+ title: string | null;
47
+ contact_id: number | null;
48
+ contact_sub_id: number | null;
49
+ user_id: number;
50
+ project_id: number | null;
51
+ language_id: number;
52
+ bank_account_id: number;
53
+ currency_id: number;
54
+ payment_type_id: number;
55
+ total_gross: string;
56
+ total_net: string;
57
+ total_taxes: string;
58
+ total: string;
59
+ kb_item_status_id: number;
60
+ is_valid_from: string;
61
+ is_valid_until: string | null;
62
+ updated_at: string;
63
+ }>>('/2.0/kb_offer/search', searchBody, queryParams);
64
+
65
+ return quotes.map((quote) => {
66
+ const updatedAt = quote.updated_at || new Date().toISOString();
67
+ const epochMilliSeconds = dayjs(updatedAt).valueOf();
68
+
69
+ return {
70
+ epochMilliSeconds,
71
+ data: quote,
72
+ };
73
+ });
74
+ },
75
+ };
76
+
77
+ export const newQuoteTrigger = createTrigger({
78
+ auth: bexioAuth,
79
+ name: 'new_quote',
80
+ displayName: 'New Quotes',
81
+ description: 'Triggers when a quote is created or updated with the chosen status',
82
+ aiMetadata: {
83
+ description: 'Fires when a quote (offer) is created or its updated timestamp changes in Bexio, optionally filtered to a chosen quote status. Emits each matching quote with its document number, contact, totals (gross, net, taxes), validity dates, and status. Use to react to new or modified quotes.',
84
+ },
85
+ type: TriggerStrategy.POLLING,
86
+ props: {
87
+ status_id: Property.Dropdown({
88
+ auth: bexioAuth,
89
+ displayName: 'Status',
90
+ description: 'Filter quotes by status (leave empty to trigger for all statuses)',
91
+ required: false,
92
+ refreshers: [],
93
+ options: async ({ auth }) => {
94
+ if (!auth) {
95
+ return {
96
+ disabled: true,
97
+ placeholder: 'Connect your Bexio account first',
98
+ options: [],
99
+ };
100
+ }
101
+
102
+ try {
103
+ const client = new BexioClient(auth);
104
+ const statuses = await client.get<Array<{
105
+ id: number;
106
+ name: string;
107
+ }>>('/2.0/kb_offer_status').catch(() => []);
108
+
109
+ if (statuses.length === 0) {
110
+ return {
111
+ disabled: false,
112
+ placeholder: 'Status filter not available - will trigger for all statuses',
113
+ options: [],
114
+ };
115
+ }
116
+
117
+ return {
118
+ disabled: false,
119
+ options: statuses.map((status) => ({
120
+ label: status.name,
121
+ value: status.id,
122
+ })),
123
+ };
124
+ } catch (error) {
125
+ return {
126
+ disabled: false,
127
+ placeholder: 'Enter status ID manually or leave empty for all statuses',
128
+ options: [],
129
+ };
130
+ }
131
+ },
132
+ }),
133
+ },
134
+ sampleData: {
135
+ id: 4,
136
+ document_nr: 'AN-00001',
137
+ title: null,
138
+ contact_id: 14,
139
+ contact_sub_id: null,
140
+ user_id: 1,
141
+ project_id: null,
142
+ language_id: 1,
143
+ bank_account_id: 1,
144
+ currency_id: 1,
145
+ payment_type_id: 1,
146
+ total_gross: '17.800000',
147
+ total_net: '17.800000',
148
+ total_taxes: '1.3706',
149
+ total: '19.150000',
150
+ kb_item_status_id: 3,
151
+ is_valid_from: '2019-06-24',
152
+ is_valid_until: '2019-07-24',
153
+ updated_at: '2019-04-08 13:17:32',
154
+ },
155
+ onEnable: async (context) => {
156
+ await pollingHelper.onEnable(polling, {
157
+ auth: context.auth,
158
+ store: context.store,
159
+ propsValue: context.propsValue,
160
+ });
161
+ },
162
+ onDisable: async (context) => {
163
+ await pollingHelper.onDisable(polling, {
164
+ auth: context.auth,
165
+ store: context.store,
166
+ propsValue: context.propsValue,
167
+ });
168
+ },
169
+ test: async (context) => {
170
+ return await pollingHelper.test(polling, {
171
+ auth: context.auth,
172
+ store: context.store,
173
+ propsValue: context.propsValue,
174
+ files: context.files,
175
+ });
176
+ },
177
+ run: async (context) => {
178
+ return await pollingHelper.poll(polling, {
179
+ auth: context.auth,
180
+ store: context.store,
181
+ propsValue: context.propsValue,
182
+ files: context.files,
183
+ });
184
+ },
185
+ });
186
+
@@ -0,0 +1,186 @@
1
+ import { createTrigger, TriggerStrategy, PiecePropValueSchema, Property, AppConnectionValueForAuthProperty } from '@agnocon/pieces-framework';
2
+ import { DedupeStrategy, Polling, pollingHelper } from '@agnocon/pieces-common';
3
+ import { bexioAuth } from '../auth';
4
+ import { BexioClient } from '../common/client';
5
+ import { OAuth2PropertyValue } from '@agnocon/pieces-framework';
6
+ import dayjs from 'dayjs';
7
+
8
+ const polling: Polling<
9
+ AppConnectionValueForAuthProperty<typeof bexioAuth>,
10
+ { status_id?: number }
11
+ > = {
12
+ strategy: DedupeStrategy.TIMEBASED,
13
+ items: async ({ auth, lastFetchEpochMS, propsValue }) => {
14
+ const client = new BexioClient(auth);
15
+
16
+ const isTest = lastFetchEpochMS === 0;
17
+
18
+ const lastFetchDate = isTest
19
+ ? dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss')
20
+ : dayjs(lastFetchEpochMS).format('YYYY-MM-DD HH:mm:ss');
21
+
22
+ const searchBody: Array<{ field: string; value: string; criteria: string }> = [
23
+ {
24
+ field: 'updated_at',
25
+ value: lastFetchDate,
26
+ criteria: '>=',
27
+ },
28
+ ];
29
+
30
+ if (propsValue.status_id !== undefined && propsValue.status_id !== null) {
31
+ searchBody.push({
32
+ field: 'kb_item_status_id',
33
+ value: propsValue.status_id.toString(),
34
+ criteria: '=',
35
+ });
36
+ }
37
+
38
+ const queryParams: Record<string, string> = {
39
+ order_by: 'updated_at_desc',
40
+ limit: isTest ? '5' : '500',
41
+ };
42
+
43
+ const invoices = await client.post<Array<{
44
+ id: number;
45
+ document_nr: string;
46
+ title: string | null;
47
+ contact_id: number | null;
48
+ contact_sub_id: number | null;
49
+ user_id: number;
50
+ project_id: number | null;
51
+ language_id: number;
52
+ bank_account_id: number;
53
+ currency_id: number;
54
+ payment_type_id: number;
55
+ total_gross: string;
56
+ total_net: string;
57
+ total_taxes: string;
58
+ total: string;
59
+ kb_item_status_id: number;
60
+ is_valid_from: string;
61
+ is_valid_to: string | null;
62
+ updated_at: string;
63
+ }>>('/2.0/kb_invoice/search', searchBody, queryParams);
64
+
65
+ return invoices.map((invoice) => {
66
+ const updatedAt = invoice.updated_at || new Date().toISOString();
67
+ const epochMilliSeconds = dayjs(updatedAt).valueOf();
68
+
69
+ return {
70
+ epochMilliSeconds,
71
+ data: invoice,
72
+ };
73
+ });
74
+ },
75
+ };
76
+
77
+ export const newSalesInvoiceTrigger = createTrigger({
78
+ auth: bexioAuth,
79
+ name: 'new_sales_invoice',
80
+ displayName: 'New Sales-Invoice',
81
+ description: 'Triggers when a Sales-Invoice is created or updated with the chosen status',
82
+ aiMetadata: {
83
+ description: 'Fires when a sales invoice is created or its updated timestamp changes in Bexio, optionally filtered to a chosen invoice status. Emits each matching invoice with its document number, contact, totals (gross, net, taxes), validity dates, and status. Use to react to new or modified customer invoices.',
84
+ },
85
+ type: TriggerStrategy.POLLING,
86
+ props: {
87
+ status_id: Property.Dropdown({
88
+ auth: bexioAuth,
89
+ displayName: 'Status',
90
+ description: 'Filter invoices by status (leave empty to trigger for all statuses)',
91
+ required: false,
92
+ refreshers: [],
93
+ options: async ({ auth }) => {
94
+ if (!auth) {
95
+ return {
96
+ disabled: true,
97
+ placeholder: 'Connect your Bexio account first',
98
+ options: [],
99
+ };
100
+ }
101
+
102
+ try {
103
+ const client = new BexioClient(auth);
104
+ const statuses = await client.get<Array<{
105
+ id: number;
106
+ name: string;
107
+ }>>('/2.0/kb_invoice_status').catch(() => []);
108
+
109
+ if (statuses.length === 0) {
110
+ return {
111
+ disabled: false,
112
+ placeholder: 'Status filter not available - will trigger for all statuses',
113
+ options: [],
114
+ };
115
+ }
116
+
117
+ return {
118
+ disabled: false,
119
+ options: statuses.map((status) => ({
120
+ label: status.name,
121
+ value: status.id,
122
+ })),
123
+ };
124
+ } catch (error) {
125
+ return {
126
+ disabled: false,
127
+ placeholder: 'Enter status ID manually or leave empty for all statuses',
128
+ options: [],
129
+ };
130
+ }
131
+ },
132
+ }),
133
+ },
134
+ sampleData: {
135
+ id: 4,
136
+ document_nr: 'RE-00001',
137
+ title: null,
138
+ contact_id: 14,
139
+ contact_sub_id: null,
140
+ user_id: 1,
141
+ project_id: null,
142
+ language_id: 1,
143
+ bank_account_id: 1,
144
+ currency_id: 1,
145
+ payment_type_id: 1,
146
+ total_gross: '17.800000',
147
+ total_net: '17.800000',
148
+ total_taxes: '1.3706',
149
+ total: '19.150000',
150
+ kb_item_status_id: 3,
151
+ is_valid_from: '2019-06-24',
152
+ is_valid_to: '2019-07-24',
153
+ updated_at: '2019-04-08 13:17:32',
154
+ },
155
+ onEnable: async (context) => {
156
+ await pollingHelper.onEnable(polling, {
157
+ auth: context.auth,
158
+ store: context.store,
159
+ propsValue: context.propsValue,
160
+ });
161
+ },
162
+ onDisable: async (context) => {
163
+ await pollingHelper.onDisable(polling, {
164
+ auth: context.auth,
165
+ store: context.store,
166
+ propsValue: context.propsValue,
167
+ });
168
+ },
169
+ test: async (context) => {
170
+ return await pollingHelper.test(polling, {
171
+ auth: context.auth,
172
+ store: context.store,
173
+ propsValue: context.propsValue,
174
+ files: context.files,
175
+ });
176
+ },
177
+ run: async (context) => {
178
+ return await pollingHelper.poll(polling, {
179
+ auth: context.auth,
180
+ store: context.store,
181
+ propsValue: context.propsValue,
182
+ files: context.files,
183
+ });
184
+ },
185
+ });
186
+
@@ -0,0 +1,161 @@
1
+ import { createTrigger, TriggerStrategy, PiecePropValueSchema, AppConnectionValueForAuthProperty } from '@agnocon/pieces-framework';
2
+ import { DedupeStrategy, Polling, pollingHelper } from '@agnocon/pieces-common';
3
+ import { bexioAuth } from '../auth';
4
+ import { BexioClient } from '../common/client';
5
+ import dayjs from 'dayjs';
6
+
7
+ const polling: Polling<
8
+ AppConnectionValueForAuthProperty<typeof bexioAuth>,
9
+ Record<string, never>
10
+ > = {
11
+ strategy: DedupeStrategy.TIMEBASED,
12
+ items: async ({ auth, lastFetchEpochMS }) => {
13
+ const client = new BexioClient(auth);
14
+
15
+ const isTest = lastFetchEpochMS === 0;
16
+
17
+ const lastFetchDate = isTest
18
+ ? dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss')
19
+ : dayjs(lastFetchEpochMS).format('YYYY-MM-DD HH:mm:ss');
20
+
21
+ const searchBody = [
22
+ {
23
+ field: 'contact_type_id',
24
+ value: '1', // 1 = company
25
+ criteria: '=',
26
+ },
27
+ {
28
+ field: 'updated_at',
29
+ value: lastFetchDate,
30
+ criteria: '>=',
31
+ },
32
+ ];
33
+
34
+ const queryParams: Record<string, string> = {
35
+ order_by: 'updated_at_desc',
36
+ limit: isTest ? '5' : '500', // Limit to 5 for test, 500 for normal polling
37
+ };
38
+
39
+ const companies = await client.post<Array<{
40
+ id: number;
41
+ nr?: string | null;
42
+ contact_type_id: number;
43
+ name_1: string;
44
+ name_2?: string | null;
45
+ salutation_id?: number | null;
46
+ salutation_form?: string | null;
47
+ title_id?: number | null;
48
+ birthday?: string | null;
49
+ address?: string | null;
50
+ street_name?: string | null;
51
+ house_number?: string | null;
52
+ address_addition?: string | null;
53
+ postcode?: string | null;
54
+ city?: string | null;
55
+ country_id?: number | null;
56
+ mail?: string | null;
57
+ mail_second?: string | null;
58
+ phone_fixed?: string | null;
59
+ phone_fixed_second?: string | null;
60
+ phone_mobile?: string | null;
61
+ fax?: string | null;
62
+ url?: string | null;
63
+ skype_name?: string | null;
64
+ remarks?: string | null;
65
+ language_id?: number | null;
66
+ is_lead?: boolean;
67
+ contact_group_ids?: string | null;
68
+ contact_branch_ids?: string | null;
69
+ user_id?: number;
70
+ owner_id?: number;
71
+ updated_at?: string;
72
+ }>>('/2.0/contact/search', searchBody, queryParams);
73
+
74
+ return companies.map((company) => {
75
+ const updatedAt = company.updated_at || new Date().toISOString();
76
+ const epochMilliSeconds = dayjs(updatedAt).valueOf();
77
+
78
+ return {
79
+ epochMilliSeconds,
80
+ data: company,
81
+ };
82
+ });
83
+ },
84
+ };
85
+
86
+ export const newUpdatedCompanyTrigger = createTrigger({
87
+ auth: bexioAuth,
88
+ name: 'new_updated_company',
89
+ displayName: 'New/Updated Company',
90
+ description: 'Triggers when a company is added or updated',
91
+ aiMetadata: {
92
+ description: 'Fires when a company contact is created or its updated timestamp changes in Bexio. Emits each matching company with its name, address, contact details (email, phone), group memberships, and lead status. Use to react to new or modified organization contacts.',
93
+ },
94
+ type: TriggerStrategy.POLLING,
95
+ props: {},
96
+ sampleData: {
97
+ id: 4,
98
+ nr: null,
99
+ contact_type_id: 1,
100
+ name_1: 'Example Company',
101
+ name_2: null,
102
+ salutation_id: 2,
103
+ salutation_form: null,
104
+ title_id: null,
105
+ birthday: null,
106
+ address: 'Smith Street 22',
107
+ street_name: 'Smith Street',
108
+ house_number: '77',
109
+ address_addition: 'Building C',
110
+ postcode: '8004',
111
+ city: 'Zurich',
112
+ country_id: 1,
113
+ mail: 'contact@example.org',
114
+ mail_second: '',
115
+ phone_fixed: '',
116
+ phone_fixed_second: '',
117
+ phone_mobile: '',
118
+ fax: '',
119
+ url: '',
120
+ skype_name: '',
121
+ remarks: '',
122
+ language_id: null,
123
+ is_lead: false,
124
+ contact_group_ids: '1,2',
125
+ contact_branch_ids: null,
126
+ user_id: 1,
127
+ owner_id: 1,
128
+ updated_at: '2019-04-08 13:17:32',
129
+ },
130
+ onEnable: async (context) => {
131
+ await pollingHelper.onEnable(polling, {
132
+ auth: context.auth,
133
+ store: context.store,
134
+ propsValue: context.propsValue,
135
+ });
136
+ },
137
+ onDisable: async (context) => {
138
+ await pollingHelper.onDisable(polling, {
139
+ auth: context.auth,
140
+ store: context.store,
141
+ propsValue: context.propsValue,
142
+ });
143
+ },
144
+ test: async (context) => {
145
+ return await pollingHelper.test(polling, {
146
+ auth: context.auth,
147
+ store: context.store,
148
+ propsValue: context.propsValue,
149
+ files: context.files,
150
+ });
151
+ },
152
+ run: async (context) => {
153
+ return await pollingHelper.poll(polling, {
154
+ auth: context.auth,
155
+ store: context.store,
156
+ propsValue: context.propsValue,
157
+ files: context.files,
158
+ });
159
+ },
160
+ });
161
+