twenty-migrate-espocrm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,261 @@
1
+ import { EspoCRMData, EspoCRMContact, EspoCRMAccount, EspoCRMOpportunity, EspoCRMNote } from './extract';
2
+
3
+ export interface TwentyPerson {
4
+ name?: {
5
+ firstName?: string;
6
+ lastName?: string;
7
+ };
8
+ email?: string;
9
+ phone?: string;
10
+ jobTitle?: string;
11
+ company?: string;
12
+ city?: string;
13
+ country?: string;
14
+ createdAt?: string;
15
+ updatedAt?: string;
16
+ source?: 'espocrm';
17
+ espocrmId?: string;
18
+ }
19
+
20
+ export interface TwentyCompany {
21
+ name?: string;
22
+ domainName?: string;
23
+ industry?: string;
24
+ description?: string;
25
+ phone?: string;
26
+ website?: string;
27
+ createdAt?: string;
28
+ updatedAt?: string;
29
+ source?: 'espocrm';
30
+ espocrmId?: string;
31
+ }
32
+
33
+ export interface TwentyOpportunity {
34
+ name?: string;
35
+ amount?: number;
36
+ closeDate?: string;
37
+ pipeline?: string;
38
+ stage?: string;
39
+ createdAt?: string;
40
+ updatedAt?: string;
41
+ source?: 'espocrm';
42
+ espocrmId?: string;
43
+ personId?: string;
44
+ companyId?: string;
45
+ }
46
+
47
+ export interface TwentyNote {
48
+ body?: string;
49
+ createdAt?: string;
50
+ updatedAt?: string;
51
+ source?: 'espocrm';
52
+ espocrmId?: string;
53
+ personId?: string;
54
+ companyId?: string;
55
+ opportunityId?: string;
56
+ }
57
+
58
+ export interface TransformedData {
59
+ people: TwentyPerson[];
60
+ companies: TwentyCompany[];
61
+ opportunities: TwentyOpportunity[];
62
+ notes: TwentyNote[];
63
+ }
64
+
65
+ export async function transformData(espocrmData: Partial<EspoCRMData>): Promise<TransformedData> {
66
+ const transformed: TransformedData = {
67
+ people: [],
68
+ companies: [],
69
+ opportunities: [],
70
+ notes: []
71
+ };
72
+
73
+ console.log('🔄 Transforming EspoCRM data to Twenty CRM format...');
74
+
75
+ // Transform contacts to people
76
+ if (espocrmData.contacts) {
77
+ console.log(`👥 Transforming ${espocrmData.contacts.length} contacts...`);
78
+ transformed.people = transformContactsToPeople(espocrmData.contacts);
79
+ }
80
+
81
+ // Transform accounts
82
+ if (espocrmData.accounts) {
83
+ console.log(`🏢 Transforming ${espocrmData.accounts.length} accounts...`);
84
+ transformed.companies = transformAccounts(espocrmData.accounts);
85
+ }
86
+
87
+ // Transform opportunities
88
+ if (espocrmData.opportunities) {
89
+ console.log(`💼 Transforming ${espocrmData.opportunities.length} opportunities...`);
90
+ transformed.opportunities = transformOpportunities(espocrmData.opportunities);
91
+ }
92
+
93
+ // Transform notes
94
+ if (espocrmData.notes) {
95
+ console.log(`📝 Transforming ${espocrmData.notes.length} notes...`);
96
+ transformed.notes = transformNotes(espocrmData.notes);
97
+ }
98
+
99
+ console.log('✅ Data transformation completed');
100
+ return transformed;
101
+ }
102
+
103
+ function transformContactsToPeople(contacts: EspoCRMContact[]): TwentyPerson[] {
104
+ return contacts.map(contact => {
105
+ const twentyPerson: TwentyPerson = {
106
+ source: 'espocrm',
107
+ espocrmId: contact.id,
108
+ createdAt: contact.createdAt,
109
+ updatedAt: contact.modifiedAt
110
+ };
111
+
112
+ // Transform name
113
+ if (contact.firstName || contact.lastName) {
114
+ twentyPerson.name = {
115
+ firstName: contact.firstName || '',
116
+ lastName: contact.lastName || ''
117
+ };
118
+ }
119
+
120
+ // Transform email
121
+ if (contact.email) {
122
+ twentyPerson.email = contact.email;
123
+ }
124
+
125
+ // Transform phone
126
+ if (contact.phone) {
127
+ twentyPerson.phone = contact.phone;
128
+ }
129
+
130
+ // Transform job title
131
+ if (contact.title) {
132
+ twentyPerson.jobTitle = contact.title;
133
+ }
134
+
135
+ // Link to account (stored as company reference)
136
+ if (contact.accountId) {
137
+ twentyPerson.company = contact.accountId;
138
+ }
139
+
140
+ return twentyPerson;
141
+ });
142
+ }
143
+
144
+ function transformAccounts(accounts: EspoCRMAccount[]): TwentyCompany[] {
145
+ return accounts.map(account => {
146
+ const twentyCompany: TwentyCompany = {
147
+ source: 'espocrm',
148
+ espocrmId: account.id,
149
+ createdAt: account.createdAt,
150
+ updatedAt: account.modifiedAt
151
+ };
152
+
153
+ // Transform name
154
+ if (account.name) {
155
+ twentyCompany.name = account.name;
156
+ }
157
+
158
+ // Transform website
159
+ if (account.website) {
160
+ twentyCompany.website = account.website;
161
+ }
162
+
163
+ // Transform industry
164
+ if (account.industry) {
165
+ twentyCompany.industry = account.industry;
166
+ }
167
+
168
+ // Transform description
169
+ if (account.description) {
170
+ twentyCompany.description = account.description;
171
+ }
172
+
173
+ // Transform phone
174
+ if (account.phone) {
175
+ twentyCompany.phone = account.phone;
176
+ }
177
+
178
+ return twentyCompany;
179
+ });
180
+ }
181
+
182
+ function transformOpportunities(opportunities: EspoCRMOpportunity[]): TwentyOpportunity[] {
183
+ return opportunities.map(opportunity => {
184
+ const twentyOpportunity: TwentyOpportunity = {
185
+ source: 'espocrm',
186
+ espocrmId: opportunity.id,
187
+ createdAt: opportunity.createdAt,
188
+ updatedAt: opportunity.modifiedAt
189
+ };
190
+
191
+ // Transform name
192
+ if (opportunity.name) {
193
+ twentyOpportunity.name = opportunity.name;
194
+ }
195
+
196
+ // Transform amount
197
+ if (opportunity.amount !== undefined && opportunity.amount !== null) {
198
+ twentyOpportunity.amount = opportunity.amount;
199
+ }
200
+
201
+ // Transform currency (store as pipeline for now)
202
+ if (opportunity.currency) {
203
+ twentyOpportunity.pipeline = opportunity.currency;
204
+ }
205
+
206
+ // Transform stage
207
+ if (opportunity.stage) {
208
+ twentyOpportunity.stage = opportunity.stage;
209
+ }
210
+
211
+ // Transform close date
212
+ if (opportunity.closeDate) {
213
+ twentyOpportunity.closeDate = opportunity.closeDate;
214
+ }
215
+
216
+ // Link to account
217
+ if (opportunity.accountId) {
218
+ twentyOpportunity.companyId = opportunity.accountId;
219
+ }
220
+
221
+ // Link to contact
222
+ if (opportunity.contactId) {
223
+ twentyOpportunity.personId = opportunity.contactId;
224
+ }
225
+
226
+ return twentyOpportunity;
227
+ });
228
+ }
229
+
230
+ function transformNotes(notes: EspoCRMNote[]): TwentyNote[] {
231
+ return notes.map(note => {
232
+ const twentyNote: TwentyNote = {
233
+ source: 'espocrm',
234
+ espocrmId: note.id,
235
+ createdAt: note.createdAt,
236
+ updatedAt: note.modifiedAt
237
+ };
238
+
239
+ // Transform note
240
+ if (note.note) {
241
+ twentyNote.body = note.note;
242
+ }
243
+
244
+ // Link to parent based on parentType
245
+ if (note.parentId && note.parentType) {
246
+ switch (note.parentType) {
247
+ case 'Contact':
248
+ twentyNote.personId = note.parentId;
249
+ break;
250
+ case 'Account':
251
+ twentyNote.companyId = note.parentId;
252
+ break;
253
+ case 'Opportunity':
254
+ twentyNote.opportunityId = note.parentId;
255
+ break;
256
+ }
257
+ }
258
+
259
+ return twentyNote;
260
+ });
261
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "removeComments": false,
16
+ "noImplicitAny": true,
17
+ "strictNullChecks": true,
18
+ "strictFunctionTypes": true,
19
+ "noImplicitReturns": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "moduleResolution": "node",
22
+ "baseUrl": "./",
23
+ "paths": {
24
+ "@/*": ["src/*"]
25
+ },
26
+ "allowSyntheticDefaultImports": true,
27
+ "experimentalDecorators": true,
28
+ "emitDecoratorMetadata": true
29
+ },
30
+ "include": [
31
+ "src/**/*"
32
+ ],
33
+ "exclude": [
34
+ "node_modules",
35
+ "dist",
36
+ "**/*.test.ts",
37
+ "**/*.spec.ts"
38
+ ]
39
+ }