go_import 3.0.25 → 3.0.26

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,523 @@
1
+ # encoding: iso-8859-1
2
+
3
+ require 'go_import'
4
+ require 'tiny_tds'
5
+ require_relative("../converter")
6
+
7
+
8
+ def convert_source
9
+ puts "Trying to convert LIME Pro source to LIME Go..."
10
+
11
+ if !defined?(SQL_SERVER) || SQL_SERVER.empty?
12
+ raise "SQL_SERVER must be set in converter.rb"
13
+ end
14
+
15
+ if !defined?(SQL_SERVER_DATABASE) || SQL_SERVER_DATABASE.empty?
16
+ raise "SQL_SERVER_DATABASE must be set in converter.rb"
17
+ end
18
+
19
+ if !defined?(LIME_SERVER) || LIME_SERVER.empty?
20
+ raise "LIME_SERVER must be set in converter.rb"
21
+ end
22
+
23
+ if !defined?(LIME_DATABASE) || LIME_DATABASE.empty?
24
+ raise "LIME_DATABASE must be set in converter.rb"
25
+ end
26
+
27
+ if !defined?(LIME_LANGUAGE) || LIME_LANGUAGE.empty?
28
+ raise "LIME_LANGUAGE must be set in converter.rb"
29
+ end
30
+
31
+ windows_authentication = false
32
+ if defined?(SQL_SERVER_USER) && !SQL_SERVER_USER.empty?
33
+ begin
34
+ print "Password for #{SQL_SERVER_USER}: "
35
+ # We hide the entered characters before to ask for the password
36
+ system 'stty -echo'
37
+ sql_server_password = $stdin.gets.chomp
38
+ system 'stty echo'
39
+ puts ""
40
+ rescue NoMethodError, Interrupt
41
+ # When the process is exited, we display the characters
42
+ # again And we exit
43
+ system 'stty echo'
44
+ exit
45
+ end
46
+ else
47
+ puts "No user defined, using Windows authentication to connect to SQL Server. We will connect as the user that is running go-import. Set a value for SQL_SERVER_USER in converter.rb to change."
48
+ windows_authentication = true
49
+ end
50
+
51
+ begin
52
+ if windows_authentication
53
+ db_con = TinyTds::Client.new(dataserver: SQL_SERVER,
54
+ database: SQL_SERVER_DATABASE)
55
+ else
56
+ db_con = TinyTds::Client.new(username: SQL_SERVER_USER,
57
+ password: sql_server_password,
58
+ dataserver: SQL_SERVER,
59
+ database: SQL_SERVER_DATABASE)
60
+ end
61
+
62
+ puts "Connected to SQL Server."
63
+ rescue Exception => e
64
+ puts "ERROR: Failed to connect to SQL-server"
65
+ puts e.message
66
+ exit
67
+ end
68
+
69
+ con = LIMEProConnection.new db_con
70
+ converter = Converter.new
71
+ rootmodel = GoImport::RootModel.new
72
+
73
+
74
+ # coworker_class = con.get_class_by_name("coworker")
75
+ # puts "Coworker class: #{coworker_class}"
76
+ # name_field = coworker_class.get_field_by_label(FieldLabel::Name)
77
+
78
+ # puts "name field #{name_field}"
79
+
80
+
81
+ # exit
82
+
83
+
84
+ converter.configure rootmodel
85
+
86
+ #Add custom fields for LIME-links
87
+ rootmodel.settings.with_person do |person|
88
+ person.set_custom_field( { :integration_id => 'limelink', :title => 'Länk till LIME Pro', :type => :Link} )
89
+ end
90
+
91
+ rootmodel.settings.with_organization do |org|
92
+ org.set_custom_field( { :integration_id => 'limelink', :title => 'Länk till LIME Pro', :type => :Link} )
93
+ end
94
+
95
+ rootmodel.settings.with_deal do |deal|
96
+ deal.set_custom_field( { :integration_id => 'limelink', :title => 'Länk till LIME Pro', :type => :Link} )
97
+ end
98
+
99
+ # coworkers
100
+ # start with these since they are referenced
101
+ # from everywhere....
102
+ con.fetch_data "coworker" do |row|
103
+ coworker = init_coworker(row, con.get_class_by_name('coworker'))
104
+ rootmodel.add_coworker(converter.to_coworker(coworker, row))
105
+ end
106
+
107
+ # organizations
108
+ con.fetch_data "company" do |row|
109
+ organization = init_organization(row, con.get_class_by_name('company'), rootmodel)
110
+ rootmodel.add_organization(converter.to_organization(organization, row))
111
+ end
112
+
113
+ # persons
114
+ # depends on organizations
115
+ con.fetch_data "person" do |row|
116
+ # init method also adds the person to the employer
117
+ person = init_person(row, con.get_class_by_name('person'), rootmodel)
118
+ converter.to_person(person, row)
119
+ end
120
+
121
+ # deals
122
+ # deals can reference coworkers (responsible), organizations
123
+ # and persons (contact)
124
+ if defined?(IMPORT_DEALS) && IMPORT_DEALS == true
125
+ puts "Trying to import deals..."
126
+ con.fetch_data 'business' do |row|
127
+ deal = init_deal(row, con.get_class_by_name('business'), rootmodel)
128
+ rootmodel.add_deal(converter.to_deal(deal, row))
129
+ end
130
+ else
131
+ puts "Deals are not imported. To enable set IMPORT_DEALS = true in converter.rb."
132
+ end
133
+
134
+ if defined?(IMPORT_NOTES) && IMPORT_NOTES == true
135
+ con.fetch_data 'history' do |row|
136
+ note = converter.to_note(init_note(row, con.get_class_by_name('history'), rootmodel), row)
137
+ #if !note.organization.nil? || !note.person.nil?
138
+ rootmodel.add_note(note)
139
+ #end
140
+ end
141
+ else
142
+ puts "Notes/history is not imported. To enable set IMPORT_NOTES = true in converter.rb."
143
+ end
144
+
145
+ """
146
+ # documents
147
+ if defined?(IMPORT_DOCUMENTS) && !IMPORT_DOCUMENTS.nil? && IMPORT_DOCUMENTS
148
+ process_rows ORGANIZATION_DOCUMENT_FILE do |row|
149
+ rootmodel.add_file(to_organization_document(row, rootmodel))
150
+ end
151
+
152
+ process_rows PROJECT_DOCUMENT_FILE do |row|
153
+ rootmodel.add_file(to_deal_document(row, rootmodel))
154
+ end
155
+ end
156
+ """
157
+
158
+ return rootmodel
159
+ end
160
+
161
+
162
+ def init_coworker(row, table)
163
+ coworker = GoImport::Coworker.new
164
+ # integration_id is typically the idcoworker in Pro
165
+ # Must be set to be able to import the same file more
166
+ # than once without creating duplicates
167
+ coworker.integration_id = row['idcoworker'].to_s
168
+
169
+ coworker.parse_name_to_firstname_lastname_se table.get_value_for_field_with_label(row, FieldLabel::Name)
170
+ coworker.email = table.get_value_for_field_with_label(row, FieldLabel::PrimaryEmailAddress)
171
+ coworker.direct_phone_number = table.get_value_for_field_with_label(row, FieldLabel::BusinessTelephoneNumber)
172
+ coworker.mobile_phone_number = table.get_value_for_field_with_label(row, FieldLabel::MobileTelephoneNumber)
173
+
174
+ return coworker
175
+ end
176
+
177
+ def init_organization(row, table, rootmodel)
178
+ organization = GoImport::Organization.new
179
+ # integration_id is typically the company Id in Easy
180
+ # Must be set to be able to import the same file more
181
+ # than once without creating duplicates
182
+ organization.integration_id = row['idcompany'].to_s
183
+ organization.set_custom_value("limelink", build_lime_link("company", row['idcompany']))
184
+
185
+ organization.name = table.get_value_for_field_with_label(row, FieldLabel::Name)
186
+
187
+ organization.organization_number = table.get_value_for_field_with_label(row, FieldLabel::CompanyNumber)
188
+ organization.email = table.get_value_for_field_with_label(row, FieldLabel::PrimaryEmailAddress)
189
+ organization.web_site = table.get_value_for_field_with_label(row, FieldLabel::BusinessHomepage)
190
+ organization.central_phone_number = table.get_value_for_field_with_label(row, FieldLabel::BusinessTelephoneNumber)
191
+
192
+ organization.with_postal_address do |address|
193
+ address.street = table.get_value_for_field_with_label(row, FieldLabel::StreetAddress) + " " +
194
+ table.get_value_for_field_with_label(row, FieldLabel::StreetAddress2)
195
+ address.zip_code = table.get_value_for_field_with_label(row, FieldLabel::ZipCode)
196
+ address.city = table.get_value_for_field_with_label(row, FieldLabel::City)
197
+ address.country_name = table.get_value_for_field_with_label(row, FieldLabel::Country)
198
+ end
199
+
200
+ organization.with_visit_address do |address|
201
+ address.street = table.get_value_for_field_with_label(row, FieldLabel::VisitingAddress_StreetAddress) + " " +
202
+ table.get_value_for_field_with_label(row, FieldLabel::VisitingAddress_StreetAddress2)
203
+ address.zip_code = table.get_value_for_field_with_label(row, FieldLabel::VisitingAddress_ZipCode)
204
+ address.city = table.get_value_for_field_with_label(row, FieldLabel::VisitingAddress_City)
205
+ address.country_name = table.get_value_for_field_with_label(row, FieldLabel::VisitingAddress_Country)
206
+ end
207
+
208
+ if defined?(ORGANIZATION_RESPONSIBLE_FIELD) && !ORGANIZATION_RESPONSIBLE_FIELD.nil? && !ORGANIZATION_RESPONSIBLE_FIELD.empty?
209
+ # Responsible coworker for the organization.
210
+ # For instance responsible sales rep.
211
+ coworker_id = row[ORGANIZATION_RESPONSIBLE_FIELD].to_s
212
+ organization.responsible_coworker = rootmodel.find_coworker_by_integration_id(coworker_id)
213
+ end
214
+
215
+ return organization
216
+ end
217
+
218
+ def init_person(row, table, rootmodel)
219
+ person = GoImport::Person.new
220
+
221
+ person.integration_id = row['idperson'].to_s
222
+ person.set_custom_value("limelink", build_lime_link("person", row['idperson']))
223
+
224
+ # set employer connection
225
+ employer = rootmodel.find_organization_by_integration_id(row['company'].to_s)
226
+ employer.add_employee(person) if employer
227
+
228
+ person.parse_name_to_firstname_lastname_se table.get_value_for_field_with_label(row, FieldLabel::Name)
229
+ person.direct_phone_number = table.get_value_for_field_with_label(row, FieldLabel::BusinessTelephoneNumber)
230
+ person.mobile_phone_number = table.get_value_for_field_with_label(row, FieldLabel::MobileTelephoneNumber)
231
+ person.position = table.get_value_for_field_with_label(row, FieldLabel::JobTitle)
232
+ person.email = table.get_value_for_field_with_label(row, FieldLabel::PrimaryEmailAddress)
233
+
234
+ return person
235
+ end
236
+
237
+ def init_deal(row, table, rootmodel)
238
+ deal = GoImport::Deal.new
239
+
240
+ deal.integration_id = row['idbusiness'].to_s
241
+ deal.set_custom_value("limelink", build_lime_link("person", row['idbusiness']))
242
+
243
+ coworker = rootmodel.find_coworker_by_integration_id(row[DEAL_RESPONSIBLE_FIELD].to_s)
244
+ deal.responsible_coworker = coworker if coworker
245
+
246
+ organization = rootmodel.find_organization_by_integration_id(row[DEAL_COMPANY_FIELD].to_s)
247
+ deal.customer = organization if organization
248
+
249
+ deal.name = table.get_value_for_field_with_name(row, "name")
250
+ deal.description = table.get_value_for_field_with_name(row, "wonlostreason")
251
+ deal.value = table.get_value_for_field_with_name(row, "businessvalue")
252
+
253
+ if (deal.name.nil? || deal.name.empty?) && !organization.nil?
254
+ deal.name = organization.name
255
+ end
256
+
257
+ return deal
258
+ end
259
+
260
+ def init_note(row, table, rootmodel)
261
+ note = GoImport::Note.new
262
+
263
+ note.integration_id = row['idhistory'].to_s
264
+
265
+ coworker = rootmodel.find_coworker_by_integration_id(row[NOTE_COWORKER_FIELD].to_s)
266
+ note.created_by = coworker if coworker
267
+
268
+ organization = rootmodel.find_organization_by_integration_id(row[NOTE_COMPANY_FIELD].to_s)
269
+ note.organization = organization if organization
270
+
271
+ person = rootmodel.find_person_by_integration_id(row[NOTE_PERSON_FIELD].to_s)
272
+ note.person = person if person
273
+
274
+ deal = rootmodel.find_deal_by_integration_id(row[NOTE_DEAL_FIELD].to_s)
275
+ note.deal = deal if deal
276
+
277
+ note.text = table.get_value_for_field_with_label(row, FieldLabel::Notes)
278
+ note.date = table.get_value_for_field_with_label(row, FieldLabel::StartDate)
279
+
280
+ return note
281
+ end
282
+
283
+
284
+ ############################################################################
285
+ ## Helper functions and classes
286
+ ############################################################################
287
+
288
+ module FieldLabel
289
+ None = 0
290
+ Name = 1
291
+ Key = 2
292
+ Description = 3
293
+ StartDate = 4
294
+ DueDate = 5
295
+ Category = 6
296
+ Completed = 7
297
+ Notes = 8
298
+ Priority = 9
299
+ ResponsibleCoworker = 10
300
+ HomeTelephoneNumber = 13
301
+ BusinessTelephoneNumber = 14
302
+ MobileTelephoneNumber = 15
303
+ HomeFaxNumber = 16
304
+ BusinessFaxNumber = 17
305
+ Birthday = 18
306
+ HomeAddress = 19
307
+ BusinessAddress = 20
308
+ BusinessHomepage = 21
309
+ PersonalHomepage = 22
310
+ PrimaryEmailAddress = 23
311
+ SecondaryEmailAddress = 24
312
+ JobTitle = 25
313
+ Nickname = 26
314
+ ReceivedTime = 27
315
+ SentTime = 28
316
+ Location = 29
317
+ FirstName = 30
318
+ LastName = 31
319
+ Table = 11
320
+ IdDecord = 12
321
+ Inactive = 32
322
+ CompanyNumber = 33
323
+ VisitingAddress = 34
324
+ RecordImage = 35
325
+ Signature = 36
326
+ Screenshot = 37
327
+ StreetAddress = 38
328
+ ZipCode = 39
329
+ City = 40
330
+ Country = 41
331
+ CustomerNumber = 42
332
+ Geography = 43
333
+ StreetAddress2 = 44
334
+ VisitingAddress_StreetAddress = 45
335
+ VisitingAddress_StreetAddress2 = 46
336
+ VisitingAddress_ZipCode = 47
337
+ VisitingAddress_City = 48
338
+ VisitingAddress_Country = 49
339
+
340
+ end
341
+
342
+ class LIMEProConnection
343
+ def initialize(db_con)
344
+ @db_con = db_con
345
+ @tablestructure = get_table_structure().map{|proClass| proClass}
346
+ end
347
+
348
+ def fetch_data(table_name)
349
+ table = @tablestructure.find{|tbl| tbl.name == table_name}
350
+ sql = build_sql_query(table)
351
+ # puts sql
352
+ dataQuery = @db_con.execute sql
353
+
354
+ dataQuery.each do |row|
355
+ yield row
356
+ end
357
+ end
358
+
359
+ def get_class_by_name(name)
360
+ table = @tablestructure.find{|tbl| tbl.name == name}
361
+
362
+ return table
363
+ end
364
+
365
+ private
366
+ def db_con
367
+ @db_con
368
+ end
369
+
370
+ private
371
+ def tablestructure
372
+ @tablestructure
373
+ end
374
+
375
+ private
376
+ def get_table_structure()
377
+ tablesQuery = @db_con.execute("SELECT * FROM [table]")
378
+ avaiblableProClasses = tablesQuery.map{|table| table}
379
+
380
+ return avaiblableProClasses.map {|proClass| LIMEProClass.new(proClass["name"], proClass["idtable"], @db_con)}
381
+ end
382
+
383
+ private
384
+ def build_sql_query(table)
385
+ sqlForFields = table.fields.map{|field|
386
+ case field.fieldtype
387
+ when "relation"
388
+ desc = @tablestructure.find{|tbl| tbl.name == field.relatedTable}.descriptive
389
+ next "[#{table.name}].[#{field.name}],(SELECT #{desc} from [#{field.relatedTable}] WHERE [#{table.name}].[#{field.name}] = [#{field.relatedTable}].[id#{field.relatedTable}]) as #{field.name}_descriptive"
390
+ when "set"
391
+ next "dbo.lfn_getfieldsettext2([#{field.name}],';','#{LIME_LANGUAGE}')"
392
+ when "option"
393
+ next "(SELECT #{LIME_LANGUAGE} FROM string WHERE idstring = #{field.name}) as #{field.name}"
394
+ else
395
+ next "[#{table.name}].[#{field.name}]"
396
+ end
397
+ }.join(",")
398
+
399
+ sql = "SELECT #{sqlForFields} FROM [#{table.name}]"
400
+ return sql
401
+ end
402
+
403
+ private
404
+ class LIMEProClass
405
+ attr_reader :name, :id, :descriptive, :fields
406
+ def initialize(name, id, db_con)
407
+ @name = name
408
+ @id = id
409
+ @db_con = db_con
410
+ @fields = get_fields()
411
+ @descriptive = get_desc().first
412
+ end
413
+
414
+ def get_field_by_label(label)
415
+ @fields.find{|field| field.label == label}
416
+ end
417
+
418
+ def get_field_by_name(name)
419
+ @fields.find{|field| field.name == name}
420
+ end
421
+
422
+ def get_value_for_field_with_label(row, label)
423
+ field = get_field_by_label(label)
424
+
425
+ if field.nil?
426
+ return nil
427
+ end
428
+
429
+ return row[field.name]
430
+ end
431
+
432
+ def get_value_for_field_with_name(row, name)
433
+ field = get_field_by_name(name)
434
+
435
+ if field.nil?
436
+ return nil
437
+ end
438
+
439
+ return row[field.name]
440
+ end
441
+
442
+ private
443
+ def get_desc()
444
+ descriptive = @db_con.execute("SELECT dbo.lfn_getdescriptive(#{@id})")
445
+ descriptive.each(:as => :array) {|desc| return desc}
446
+ end
447
+
448
+ private
449
+ def get_fields()
450
+ metadataForRelationFieldsQuery = @db_con.execute(
451
+ """
452
+ SELECT * from relationfieldview
453
+ WHERE relationsingle = 1 AND relationintable = 1 AND idtable = #{@id}
454
+ """
455
+ )
456
+ metadataForRelationFields = metadataForRelationFieldsQuery.map {|relationField| relationField }
457
+
458
+ avaialableFieldsQuery = @db_con.execute(
459
+ """
460
+ SELECT field.idtable, field.name, field.idfield, fieldtype.name as 'fieldtypename',
461
+ cast(isnull(ad.value, 0) as int) as 'fieldlabel'
462
+ FROM field
463
+ INNER JOIN fieldtype ON field.fieldtype = fieldtype.idfieldtype
464
+ LEFT OUTER JOIN attributedata ad on ad.idrecord = field.idfield and ad.owner = 'field' and ad.name = 'label'
465
+ WHERE field.idtable = #{@id}
466
+ """
467
+ )
468
+
469
+ fields = avaialableFieldsQuery.map{ |field|
470
+ if field["fieldtypename"] != "relation"
471
+ LIMEProField.new(field["name"], field["fieldtypename"], field['fieldlabel'])
472
+ else
473
+ relationFieldMetadata = metadataForRelationFields.find {|relField| relField["idfield"] == field["idfield"]}
474
+ if relationFieldMetadata
475
+ LIMEProRelationField.new(field["name"], field["fieldtypename"], relationFieldMetadata)
476
+ end
477
+ end
478
+ }.compact
479
+
480
+ # Add hardcoded fields
481
+ fields.push LIMEProField.new "id#{@name}", "int", FieldLabel::None
482
+ fields.push LIMEProField.new "status", "int", FieldLabel::None
483
+ fields.push LIMEProField.new "createdtime", "datetime", FieldLabel::None
484
+ fields.push LIMEProField.new "createduser", "int", FieldLabel::None
485
+ fields.push LIMEProField.new "updateduser", "int", FieldLabel::None
486
+ fields.push LIMEProField.new "timestamp", "datetime", FieldLabel::None
487
+
488
+ # puts "Field for table: #{name}"
489
+ # fields.each{|f| puts "Field: #{f.name}, type: #{f.fieldtype}, label: #{f.label} "}
490
+
491
+ return fields
492
+ end
493
+ end
494
+
495
+ private
496
+ class LIMEProField
497
+ attr_reader :name, :fieldtype, :label
498
+
499
+ def initialize(name, fieldtype, label)
500
+ @name = name
501
+ @fieldtype = fieldtype
502
+ @label = label
503
+ end
504
+ end
505
+
506
+ class LIMEProRelationField < LIMEProField
507
+ def initialize(name, fieldtype, relationFieldMetadata)
508
+ super(name, fieldtype, FieldLabel::None)
509
+ @relatedTable = relationFieldMetadata["relatedtable"]
510
+ end
511
+
512
+ def relatedTable
513
+ @relatedTable
514
+ end
515
+ end
516
+ end
517
+
518
+ def build_lime_link(limeClassName, id)
519
+ return "limecrm:#{limeClassName}.#{LIME_DATABASE}.#{LIME_SERVER}?idrecord=#{id}"
520
+ end
521
+
522
+
523
+
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'thor'
4
+ gem 'go_import'
5
+ gem 'rspec'
6
+ gem 'tiny_tds'