go_import 3.0.25 → 3.0.26

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,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'