erp_base_erp_svcs 4.0.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v1/base_controller.rb +17 -0
  3. data/app/controllers/api/v1/categories_controller.rb +165 -0
  4. data/app/controllers/api/v1/contact_purposes_controller.rb +24 -0
  5. data/app/controllers/api/v1/geo_zones_controller.rb +25 -0
  6. data/app/controllers/api/v1/note_types_controller.rb +27 -0
  7. data/app/controllers/api/v1/parties_controller.rb +182 -0
  8. data/app/controllers/api/v1/role_types_controller.rb +146 -0
  9. data/app/controllers/api/v1/status_applications_controller.rb +40 -0
  10. data/app/controllers/api/v1/tracked_status_types_controller.rb +146 -0
  11. data/app/controllers/api/v1/unit_of_measurements_controller.rb +16 -0
  12. data/app/models/category.rb +116 -4
  13. data/app/models/contact.rb +16 -2
  14. data/app/models/contact_purpose.rb +13 -0
  15. data/app/models/descriptive_asset.rb +17 -0
  16. data/app/models/email_address.rb +21 -3
  17. data/app/models/entity_party_role.rb +8 -0
  18. data/app/models/fixed_asset.rb +2 -0
  19. data/app/models/fixed_asset_type.rb +1 -0
  20. data/app/models/generated_item.rb +17 -0
  21. data/app/models/geo_country.rb +12 -1
  22. data/app/models/individual.rb +11 -20
  23. data/app/models/note.rb +9 -0
  24. data/app/models/note_type.rb +2 -0
  25. data/app/models/organization.rb +19 -16
  26. data/app/models/party.rb +134 -229
  27. data/app/models/party_unit_of_measurement.rb +17 -1
  28. data/app/models/phone_number.rb +32 -12
  29. data/app/models/postal_address.rb +42 -11
  30. data/app/models/relationship_type.rb +28 -1
  31. data/app/models/role_type.rb +25 -2
  32. data/app/models/status_application.rb +40 -0
  33. data/app/models/tracked_status_type.rb +12 -0
  34. data/app/models/unit_of_measurement.rb +46 -7
  35. data/app/models/view_type.rb +14 -0
  36. data/config/routes.rb +24 -0
  37. data/db/data_migrations/20110913145838_setup_compass_ae_instance.rb +22 -9
  38. data/db/migrate/20080805000020_base_erp_services.rb +235 -6
  39. data/db/migrate/20130713171653_create_party_unit_of_measurements.rb +28 -0
  40. data/db/migrate/20150716201200_add_record_to_contact.rb +42 -0
  41. data/db/migrate/20150814202823_add_party_id_to_status_application.rb +10 -0
  42. data/db/migrate/20160310163040_add_created_by_updated_by_to_erp_base_erp_svcs.rb +44 -0
  43. data/lib/erp_base_erp_svcs.rb +1 -0
  44. data/lib/erp_base_erp_svcs/config.rb +3 -2
  45. data/lib/erp_base_erp_svcs/engine.rb +8 -1
  46. data/lib/erp_base_erp_svcs/extensions.rb +7 -1
  47. data/lib/erp_base_erp_svcs/extensions/active_record/acts_as_category.rb +5 -4
  48. data/lib/erp_base_erp_svcs/extensions/active_record/acts_as_erp_type.rb +48 -9
  49. data/lib/erp_base_erp_svcs/extensions/active_record/can_be_generated.rb +54 -0
  50. data/lib/erp_base_erp_svcs/extensions/active_record/has_contacts.rb +383 -0
  51. data/lib/erp_base_erp_svcs/extensions/active_record/has_notes.rb +15 -4
  52. data/lib/erp_base_erp_svcs/extensions/active_record/has_party_roles.rb +94 -0
  53. data/lib/erp_base_erp_svcs/extensions/active_record/has_tracked_status.rb +118 -35
  54. data/lib/erp_base_erp_svcs/extensions/active_record/is_contact_mechanism.rb +94 -0
  55. data/lib/erp_base_erp_svcs/extensions/active_record/is_describable.rb +46 -42
  56. data/lib/erp_base_erp_svcs/extensions/active_record/is_tenantable.rb +40 -0
  57. data/lib/erp_base_erp_svcs/extensions/active_record/tracks_created_by_updated_by.rb +39 -0
  58. data/lib/erp_base_erp_svcs/extensions/core/numbers.rb +29 -18
  59. data/lib/erp_base_erp_svcs/extensions/core/object.rb +5 -0
  60. data/lib/erp_base_erp_svcs/extensions/core/string.rb +53 -0
  61. data/lib/erp_base_erp_svcs/non_escape_json_string.rb +1 -1
  62. data/lib/erp_base_erp_svcs/time_zone_helper.rb +57 -0
  63. data/lib/erp_base_erp_svcs/version.rb +1 -1
  64. data/lib/tasks/erp_base_erp_svcs_tasks.rake +20 -23
  65. data/spec/dummy/log/development.log +3 -0
  66. metadata +29 -22
  67. data/db/data_migrations/20110525001935_add_usd_currency.rb +0 -11
  68. data/db/data_migrations/20110609150135_add_iso_codes.rb +0 -18
  69. data/db/data_migrations/20130211555555_upgrade_compass_ae_instances_data.rb +0 -18
  70. data/db/data_migrations/20130404201756_add_guid_to_instances.rb +0 -9
  71. data/db/migrate/20120606183856_add_txn_status.rb +0 -36
  72. data/db/migrate/20130211444444_upgrade_compass_ae_instances.rb +0 -33
  73. data/db/migrate/20130404171435_add_uuid_compass_ae_instance.rb +0 -16
  74. data/db/migrate/20130411125210_add_long_lat_to_address.rb +0 -15
  75. data/db/migrate/20130522125404_create_facilities.rb +0 -114
  76. data/db/migrate/20130621182047_create_unit_of_measurements.rb +0 -18
  77. data/db/migrate/20130909163912_add_iid_index_to_role_types.rb +0 -9
  78. data/db/migrate/20130926023541_add_domain_to_unit_of_measure.rb +0 -13
  79. data/db/migrate/20130929025342_add_type_semantics_to_uom.rb +0 -54
  80. data/db/migrate/20131112013047_add_primary_to_contacts.rb +0 -13
  81. data/db/migrate/20131112013048_add_erp_base_erp_svcs_missing_indexes.rb +0 -33
  82. data/db/migrate/20131211180831_add_postal_address_to_facility.rb +0 -5
  83. data/db/migrate/20140102154311_create_fixed_asset_party_roles.rb +0 -12
  84. data/db/migrate/20140401072612_add_custom_fields_to_party.rb +0 -12
  85. data/lib/erp_base_erp_svcs/extensions/active_record/has_contact.rb +0 -69
@@ -0,0 +1,28 @@
1
+ class CreatePartyUnitOfMeasurements < ActiveRecord::Migration
2
+ def up
3
+ unless table_exists? :party_unit_of_measurements
4
+ create_table :party_unit_of_measurements do |t|
5
+
6
+ t.string :description
7
+ t.string :internal_identifier
8
+ t.string :scope_filter
9
+ t.references :party
10
+ t.references :unit_of_measurement
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+
16
+ unless index_exists? :party_unit_of_measurements, :party_id, name: 'party_uom_party_idx'
17
+ add_index :party_unit_of_measurements, :party_id, name: 'party_uom_party_idx'
18
+ end
19
+
20
+ unless index_exists? :party_unit_of_measurements, :unit_of_measurement_id, name: 'party_uom_uom_idx'
21
+ add_index :party_unit_of_measurements, :unit_of_measurement_id, name: 'party_uom_uom_idx'
22
+ end
23
+ end
24
+
25
+ def down
26
+ drop_table :party_unit_of_measurements
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ class AddRecordToContact < ActiveRecord::Migration
2
+ def up
3
+ add_column :contacts, :contact_record_type, :string unless column_exists? :contacts, :contact_record_type
4
+ add_column :contacts, :contact_record_id, :integer unless column_exists? :contacts, :contact_record_id
5
+
6
+ if column_exists? :contacts, :party_id
7
+ Contact.find_in_batches do |batch|
8
+ batch.each do |contact|
9
+ contact.contact_record = Party.find contact.party_id
10
+ contact.save
11
+ end
12
+ end
13
+ end
14
+
15
+ remove_column :contacts, :party_id if column_exists? :contacts, :party_id
16
+
17
+ add_index :contacts, [:contact_record_type, :contact_record_id], name: 'contacts_contact_record_idx' unless index_exists? :contacts, name: 'contacts_record_idx'
18
+ end
19
+
20
+ def down
21
+ remove_column :contacts, :contact_record_type if column_exists? :contacts, :contact_record_type
22
+ remove_column :contacts, :contact_record_id if column_exists? :contacts, :contact_record_id
23
+
24
+ add_column :contacts, :party_id, :integer unless column_exists? :contacts, :party_id
25
+
26
+ if column_exists? :contacts, :record_id
27
+
28
+ Contact.find_in_batches do |batch|
29
+ batch.each do |contact|
30
+ record = contact.contact_record
31
+ if record and record.is_a? Party
32
+ contact.party_id = record.id
33
+ contact.save
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+
40
+ remove_index :contacts, name: 'contacts_contact_record_idx' if index_exists? :contacts, name: 'contacts_contact_record_idx'
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ class AddPartyIdToStatusApplication < ActiveRecord::Migration
2
+ def up
3
+ add_column :status_applications, :party_id, :integer unless column_exists? :status_applications, :party_id
4
+ add_index :status_applications, :party_id, name: 'status_application_party_idx' unless index_exists? :status_applications, 'status_application_party_idx'
5
+ end
6
+
7
+ def down
8
+ remove_column :status_applications, :party_id if column_exists? :status_applications, :party_id
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ class AddCreatedByUpdatedByToErpBaseErpSvcs < ActiveRecord::Migration
2
+ def up
3
+ %w{parties
4
+ contacts
5
+ descriptive_assets
6
+ notes
7
+ unit_of_measurements
8
+ }.each do |table|
9
+
10
+ unless column_exists? table.to_sym, :created_by_party_id
11
+ add_column table.to_sym, :created_by_party_id, :integer
12
+
13
+ add_index table.to_sym, :created_by_party_id, name: "#{table}_created_by_pty_idx"
14
+ end
15
+
16
+ unless column_exists? table.to_sym, :updated_by_party_id
17
+ add_column table.to_sym, :updated_by_party_id, :integer
18
+
19
+ add_index table.to_sym, :updated_by_party_id, name: "#{table}_updated_by_pty_idx"
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ def down
27
+ %w{parties
28
+ contacts
29
+ descriptive_assets
30
+ notes
31
+ unit_of_measurements}.each do |table|
32
+
33
+ if column_exists? table.to_sym, :created_by_party_id
34
+ remove_column table.to_sym, :created_by_party_id
35
+ end
36
+
37
+ if column_exists? table.to_sym, :updated_by_party_id
38
+ remove_column table.to_sym, :updated_by_party_id
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -3,6 +3,7 @@ require "erp_base_erp_svcs/extensions"
3
3
  require "erp_base_erp_svcs/ar_fixtures"
4
4
  require "erp_base_erp_svcs/config"
5
5
  require "erp_base_erp_svcs/non_escape_json_string"
6
+ require "erp_base_erp_svcs/time_zone_helper"
6
7
 
7
8
  module ErpBaseErpSvcs
8
9
  class << self
@@ -1,11 +1,12 @@
1
1
  module ErpBaseErpSvcs
2
2
  module Config
3
3
  class << self
4
- attr_accessor :compass_ae_engines
4
+ attr_accessor :compass_ae_engines, :encryption_key
5
5
 
6
6
  def init!
7
7
  @defaults = {
8
- :@compass_ae_engines => []
8
+ :@compass_ae_engines => [],
9
+ :@encryption_key => '314465fe-9c2b-11e4-89d3-123b93f75cba'
9
10
  }
10
11
  end
11
12
 
@@ -7,6 +7,8 @@ module ErpBaseErpSvcs
7
7
  class Engine < Rails::Engine
8
8
  isolate_namespace ErpBaseErpSvcs
9
9
 
10
+ Mime::Type.register "tree", :tree
11
+
10
12
  config.erp_base_erp_svcs = ErpBaseErpSvcs::Config
11
13
 
12
14
  ActiveSupport.on_load(:active_record) do
@@ -15,9 +17,14 @@ module ErpBaseErpSvcs
15
17
  include ErpBaseErpSvcs::Extensions::ActiveRecord::ActsAsNoteType
16
18
  include ErpBaseErpSvcs::Extensions::ActiveRecord::ActsAsErpType
17
19
  include ErpBaseErpSvcs::Extensions::ActiveRecord::ActsAsCategory
18
- include ErpBaseErpSvcs::Extensions::ActiveRecord::HasContact
20
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::IsContactMechanism
19
21
  include ErpBaseErpSvcs::Extensions::ActiveRecord::ActsAsFixedAsset
20
22
  include ErpBaseErpSvcs::Extensions::ActiveRecord::ActsAsFacility
23
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::CanBeGenerated
24
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::HasPartyRoles
25
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::HasContacts
26
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::TracksCreatedByUpdatedBy
27
+ include ErpBaseErpSvcs::Extensions::ActiveRecord::IsTenantable
21
28
  extend ErpBaseErpSvcs::Extensions::ActiveRecord::StiInstantiation::ActMacro
22
29
  end
23
30
 
@@ -3,6 +3,7 @@ require 'erp_base_erp_svcs/extensions/core/array'
3
3
  require 'erp_base_erp_svcs/extensions/core/hash'
4
4
  require 'erp_base_erp_svcs/extensions/core/numbers'
5
5
  require 'erp_base_erp_svcs/extensions/core/object'
6
+ require 'erp_base_erp_svcs/extensions/core/string'
6
7
  require 'erp_base_erp_svcs/extensions/core/acts_as_aspector_on'
7
8
 
8
9
  #active record extensions
@@ -12,10 +13,15 @@ require 'erp_base_erp_svcs/extensions/active_record/has_tracked_status'
12
13
  require 'erp_base_erp_svcs/extensions/active_record/has_notes'
13
14
  require 'erp_base_erp_svcs/extensions/active_record/acts_as_note_type'
14
15
  require 'erp_base_erp_svcs/extensions/active_record/is_describable'
15
- require 'erp_base_erp_svcs/extensions/active_record/has_contact'
16
+ require 'erp_base_erp_svcs/extensions/active_record/is_contact_mechanism'
16
17
  require 'erp_base_erp_svcs/extensions/active_record/sti_instantiation'
17
18
  require 'erp_base_erp_svcs/extensions/active_record/to_hash'
18
19
  require 'erp_base_erp_svcs/extensions/active_record/migration'
19
20
  require 'erp_base_erp_svcs/extensions/active_record/acts_as_fixed_asset'
20
21
  require 'erp_base_erp_svcs/extensions/active_record/acts_as_facility'
22
+ require 'erp_base_erp_svcs/extensions/active_record/can_be_generated'
23
+ require 'erp_base_erp_svcs/extensions/active_record/has_party_roles'
24
+ require 'erp_base_erp_svcs/extensions/active_record/has_contacts'
25
+ require 'erp_base_erp_svcs/extensions/active_record/tracks_created_by_updated_by'
26
+ require 'erp_base_erp_svcs/extensions/active_record/is_tenantable'
21
27
 
@@ -44,7 +44,8 @@ module ErpBaseErpSvcs
44
44
  end
45
45
  end
46
46
  end
47
- end
48
- end
49
- end
50
- end
47
+ end # ActsAsCategory
48
+ end # ActiveRecord
49
+ end # Extensions
50
+ end # ErpBaseErpSvcs
51
+
@@ -2,9 +2,16 @@ module ErpBaseErpSvcs
2
2
  module Extensions
3
3
  module ActiveRecord
4
4
  module ActsAsErpType
5
+
6
+ @@models = []
7
+
5
8
  def self.included(base)
6
9
  base.extend(ClassMethods)
7
- end
10
+ end
11
+
12
+ def self.models
13
+ @@models
14
+ end
8
15
 
9
16
  # declare the class level helper methods which
10
17
  # will load the relevant instance methods
@@ -20,7 +27,7 @@ module ErpBaseErpSvcs
20
27
 
21
28
  if ::ActiveRecord::Base.connection.tables.include?(self.table_name)
22
29
  # find each valid value for the domain type (erp_type) in question
23
- # we will then create a class method with the name of the internal idenfifier
30
+ # we will then create a class method with the name of the internal indentifier
24
31
  # for that type
25
32
  valid_values = self.all
26
33
 
@@ -29,6 +36,9 @@ module ErpBaseErpSvcs
29
36
  (class << self; self; end).instance_eval { define_method vv.internal_identifier, Proc.new{vv} } unless vv.internal_identifier.nil?
30
37
  end
31
38
  end
39
+
40
+ ActsAsErpType.models.push(self.name)
41
+ ActsAsErpType.models.uniq!
32
42
  end
33
43
 
34
44
  def belongs_to_erp_type(model_id = nil, options = {})
@@ -60,7 +70,26 @@ module ErpBaseErpSvcs
60
70
 
61
71
  def iid( internal_identifier_string )
62
72
  where('internal_identifier = ?', internal_identifier_string.to_s).first
63
- end
73
+ end
74
+
75
+ def generate_unique_iid(name)
76
+ iid = name.to_iid
77
+
78
+ iid_exists = true
79
+ iid_test = iid
80
+ iid_counter = 1
81
+ while iid_exists
82
+ if self.where(internal_identifier: iid_test).first
83
+ iid_test = "#{iid}_#{iid_counter}"
84
+ iid_counter += 1
85
+ else
86
+ iid_exists = false
87
+ iid = iid_test
88
+ end
89
+ end
90
+
91
+ iid
92
+ end
64
93
 
65
94
  end
66
95
 
@@ -120,16 +149,26 @@ module ErpBaseErpSvcs
120
149
 
121
150
  where(fk_str + ' in (?)', in_clause_array)
122
151
 
123
- end
124
- end
152
+ end
153
+
154
+ end
125
155
 
126
156
 
127
157
  # Adds instance methods.
128
158
  module ActsAsInstanceMethods
129
-
130
- # def instance_method_for_acts_as
131
- # puts "Instance with ID #{self.id}"
132
- # end
159
+
160
+ def to_s
161
+ self.try(:description) ? self.try(:description) : self.try(:id)
162
+ end
163
+
164
+ # Alias for to_s
165
+ def to_label
166
+ to_s
167
+ end
168
+
169
+ def to_data_hash
170
+ to_hash(only: [:id, :description, :internal_identifier, :created_at, :updated_at])
171
+ end
133
172
 
134
173
  end
135
174
 
@@ -0,0 +1,54 @@
1
+ module ErpBaseErpSvcs
2
+ module Extensions
3
+ module ActiveRecord
4
+ module CanBeGenerated
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def can_be_generated
11
+ extend CanBeGenerated::SingletonMethods
12
+ include CanBeGenerated::InstanceMethods
13
+
14
+ has_many :generated_items, :as => :generated_record
15
+ end
16
+ end # ClassMethods
17
+
18
+ module SingletonMethods
19
+
20
+ def items_generated_by(record)
21
+ entity_record_type = (record.class.superclass == ::ActiveRecord::Base) ? record.class.name.to_s : record.class.superclass.to_s
22
+
23
+ joins(:generated_items)
24
+ .where('generated_items.generated_by_type = ?', entity_record_type)
25
+ .where('generated_items.generated_by_id = ?', record.id)
26
+ end
27
+
28
+ end # SingletonMethods
29
+
30
+ module InstanceMethods
31
+ def generated_by
32
+ if generated_items.length == 1
33
+ generated_items.first.generated_by
34
+ else
35
+ generated_items.collect(&:generated_by)
36
+ end
37
+ end
38
+
39
+ def generated_by=(record)
40
+ if record.is_a?(Array)
41
+ record.each do |item|
42
+ GeneratedItem.create(generated_record: self, generated_by: item)
43
+ end
44
+ else
45
+ GeneratedItem.create(generated_record: self, generated_by: record)
46
+ end
47
+ end
48
+
49
+ end # InstanceMethods
50
+
51
+ end # CanBeGenerated
52
+ end # ActiveRecord
53
+ end # Extensions
54
+ end # ErpBaseErpSvcs
@@ -0,0 +1,383 @@
1
+ module ErpBaseErpSvcs
2
+ module Extensions
3
+ module ActiveRecord
4
+ module HasContacts
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def has_contacts
12
+ extend HasContacts::SingletonMethods
13
+ include HasContacts::InstanceMethods
14
+
15
+ after_initialize :build_contact_methods
16
+
17
+ has_many :contacts, :as => :contact_record, :dependent => :destroy
18
+
19
+ end
20
+ end
21
+
22
+ module SingletonMethods
23
+
24
+ def find_by_email(email, contact_purpose=nil)
25
+ if contact_purpose
26
+ self.joins(:contacts => [:contact_purposes])
27
+ .joins("INNER JOIN email_addresses on email_addresses.id = contacts.contact_mechanism_id
28
+ and contacts.contact_mechanism_type = 'EmailAddress'")
29
+ .where('contact_mechanism_type = ?', 'EmailAddress')
30
+ .where('contact_purposes.internal_identifier = ?', contact_purpose)
31
+ .where('email_address = ?', email).readonly(false).first
32
+ else
33
+ self.joins(:contacts)
34
+ .joins("INNER JOIN email_addresses on email_addresses.id = contacts.contact_mechanism_id
35
+ and contacts.contact_mechanism_type = 'EmailAddress'")
36
+ .where('contact_mechanism_type = ?', 'EmailAddress')
37
+ .where('email_address = ?', email).readonly(false).first
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ module InstanceMethods
44
+
45
+ def postal_addresses
46
+ find_all_contacts_by_contact_mechanism(PostalAddress)
47
+ end
48
+
49
+ def emails
50
+ find_all_contacts_by_contact_mechanism(EmailAddress)
51
+ end
52
+
53
+ def phone_numbers
54
+ find_all_contacts_by_contact_mechanism(PhoneNumber)
55
+ end
56
+
57
+ def has_phone_number?(phone_number)
58
+ result = nil
59
+ self.contacts.each do |c|
60
+ if c.contact_mechanism_type == 'PhoneNumber'
61
+ if c.contact_mechanism.phone_number == phone_number
62
+ result = true
63
+ end
64
+ end
65
+ end
66
+ result
67
+ end
68
+
69
+ def has_zip_code?(zip)
70
+ result = nil
71
+ self.contacts.each do |c|
72
+ if c.contact_mechanism_type == 'PostalAddress'
73
+ if c.contact_mechanism.zip == zip
74
+ result = true
75
+ end
76
+ end
77
+ end
78
+ result
79
+ end
80
+
81
+ # Check if record has contact with purpose
82
+ #
83
+ # @param contact_mechanism_klass [Class] the contact mechanism class (Email, PhoneNumber, PostalAddress)
84
+ # the passed contact purposes)
85
+ #
86
+ # @return result [Boolean] True if record has contact false if not
87
+ def has_contact?(contact_mechanism_klass, contact_purpose)
88
+ !contact_mechanisms_to_hash(contact_mechanism_klass, [contact_purpose]).empty?
89
+ end
90
+
91
+ # Converts PhoneNumber contact mechanisms related to this record to an array of hashes
92
+ # containing the contact records data
93
+ #
94
+ # @param contact_purposes [Array] an array of contact purposes to filter by (only return contacts with
95
+ # the passed contact purposes)
96
+ #
97
+ # @return contact_mechanisms_data [Array] an Array of hashes containing contact data
98
+ def phone_numbers_to_hash(contact_purposes=nil)
99
+ contact_mechanisms_to_hash(PhoneNumber, contact_purposes)
100
+ end
101
+
102
+ # Converts EmailAddress contact mechanisms related to this record to an array of hashes
103
+ # containing the contact records data
104
+ #
105
+ # @param contact_purposes [Array] an array of contact purposes to filter by (only return contacts with
106
+ # the passed contact purposes)
107
+ #
108
+ # @return contact_mechanisms_data [Array] an Array of hashes containing contact data
109
+ def email_addresses_to_hash(contact_purposes=nil)
110
+ contact_mechanisms_to_hash(EmailAddress, contact_purposes)
111
+ end
112
+
113
+ # Converts PostalAddress contact mechanisms related to this record to an array of hashes
114
+ # containing the contact records data
115
+ #
116
+ # @param contact_purposes [Array] an array of contact purposes to filter by (only return contacts with
117
+ # the passed contact purposes)
118
+ #
119
+ # @return contact_mechanisms_data [Array] an Array of hashes containing contact data
120
+ def postal_addresses_to_hash(contact_purposes=nil)
121
+ contact_mechanisms_to_hash(PostalAddress, contact_purposes)
122
+ end
123
+
124
+ # Converts contact mechanisms related to this record to an array of hashes
125
+ # containing the contact records data
126
+ #
127
+ # @param contact_mechanism_klass [Class] the contact mechanism class (Email, PhoneNumber, PostalAddress)
128
+ # @param contact_purposes [Array] an array of contact purposes to filter by (only return contacts with
129
+ # the passed contact purposes)
130
+ #
131
+ # @return contact_mechanisms_data [Array] an Array of hashes containing contact data
132
+ def contact_mechanisms_to_hash(contact_mechanism_klass, contact_purposes=nil)
133
+ contact_mechanisms_data = []
134
+
135
+ # if the passed contact purpose is a string convert to an Array
136
+ if contact_purposes && contact_purposes.is_a?(String)
137
+ contact_purposes = [contact_purposes]
138
+ end
139
+
140
+ if contact_purposes
141
+ contact_purposes.each do |contact_purpose|
142
+ contact_mechanisms = find_contact_mechanisms_with_purpose(contact_mechanism_klass, contact_purpose)
143
+
144
+ unless contact_mechanisms.empty?
145
+ contact_mechanisms.collect do |item|
146
+ data = item.to_data_hash
147
+ data[:contact_purpose] = contact_purpose
148
+
149
+ contact_mechanisms_data.push(data)
150
+ end
151
+ end
152
+ end
153
+ else
154
+ contact_mechanisms = find_all_contacts_by_contact_mechanism(contact_mechanism_klass)
155
+ contact_mechanisms.each do |contact_mechanism|
156
+ data = contact_mechanism.to_data_hash
157
+ data[:contact_purpose] = contact_mechanism.contact.contact_purpose.first.internal_identifier
158
+
159
+ contact_mechanisms_data.push(data)
160
+ end
161
+ end
162
+
163
+ contact_mechanisms_data
164
+ end
165
+
166
+ def primary_phone_number
167
+ contact_mechanism = nil
168
+
169
+ contact = self.get_primary_contact(PhoneNumber)
170
+ contact_mechanism = contact.contact_mechanism unless contact.nil?
171
+
172
+ contact_mechanism
173
+ end
174
+
175
+ alias primary_phone primary_phone_number
176
+
177
+ def primary_phone_number=(phone_number)
178
+ self.set_primary_contact(PhoneNumber, phone_number)
179
+ end
180
+
181
+ alias primary_phone= primary_phone_number=
182
+
183
+ def primary_email_address
184
+ contact_mechanism = nil
185
+
186
+ contact = self.get_primary_contact(EmailAddress)
187
+ contact_mechanism = contact.contact_mechanism unless contact.nil?
188
+
189
+ contact_mechanism
190
+ end
191
+
192
+ alias primary_email primary_email_address
193
+
194
+ def primary_email_address=(email_address)
195
+ self.set_primary_contact(EmailAddress, email_address)
196
+ end
197
+
198
+ alias primary_email= primary_email_address=
199
+
200
+ def primary_postal_address
201
+ contact_mechanism = nil
202
+
203
+ contact = self.get_primary_contact(PostalAddress)
204
+ contact_mechanism = contact.contact_mechanism unless contact.nil?
205
+
206
+ contact_mechanism
207
+ end
208
+
209
+ alias primary_address primary_postal_address
210
+
211
+ def primary_postal_address=(postal_address)
212
+ self.set_primary_contact(PostalAddress, postal_address)
213
+ end
214
+
215
+ alias primary_address= primary_postal_address=
216
+
217
+ def set_primary_contact(contact_mechanism_class, contact_mechanism_instance)
218
+ # set is_primary to false for any current primary contacts of this type
219
+ primary_contact_mechanism = get_primary_contact(contact_mechanism_class)
220
+ if primary_contact_mechanism
221
+ primary_contact_mechanism.is_primary = false
222
+ primary_contact_mechanism.save!
223
+ end
224
+
225
+ contact_mechanism_instance.is_primary = true
226
+ contact_mechanism_instance.save!
227
+
228
+ contact_mechanism_instance
229
+ end
230
+
231
+ def get_primary_contact(contact_mechanism_class)
232
+ table_name = contact_mechanism_class.name.tableize
233
+
234
+ self.contacts.joins("inner join #{table_name} on #{table_name}.id = contact_mechanism_id and contact_mechanism_type = '#{contact_mechanism_class.name}'")
235
+ .where('contacts.is_primary = ?', true).readonly(false).first
236
+ end
237
+
238
+ # find first contact mechanism with purpose
239
+ def find_contact_mechanism_with_purpose(contact_mechanism_class, contact_purpose)
240
+ contact = self.find_contact_with_purpose(contact_mechanism_class, contact_purpose)
241
+
242
+ contact.contact_mechanism unless contact.nil?
243
+ end
244
+
245
+ # find all contact mechanisms with purpose
246
+ def find_contact_mechanisms_with_purpose(contact_mechanism_class, contact_purpose)
247
+ contacts = self.find_contacts_with_purpose(contact_mechanism_class, contact_purpose)
248
+
249
+ contacts.empty? ? [] : contacts.collect(&:contact_mechanism)
250
+ end
251
+
252
+ # find first contact with purpose
253
+ def find_contact_with_purpose(contact_mechanism_class, contact_purpose)
254
+ #if a symbol or string was passed get the model
255
+ unless contact_purpose.is_a? ContactPurpose
256
+ contact_purpose = ContactPurpose.find_by_internal_identifier(contact_purpose.to_s)
257
+ end
258
+
259
+ self.find_contact(contact_mechanism_class, nil, [contact_purpose])
260
+ end
261
+
262
+ def has_contact_with_purpose?(contact_mechanism_class, contact_purpose)
263
+ !find_contact_with_purpose(contact_mechanism_class, contact_purpose).nil?
264
+ end
265
+
266
+ # find all contacts with purpose
267
+ def find_contacts_with_purpose(contact_mechanism_class, contact_purpose)
268
+ #if a symbol or string was passed get the model
269
+ unless contact_purpose.is_a? ContactPurpose
270
+ contact_purpose = ContactPurpose.find_by_internal_identifier(contact_purpose.to_s)
271
+ end
272
+
273
+ self.find_contacts(contact_mechanism_class, nil, [contact_purpose])
274
+ end
275
+
276
+ # find all contacts by contact mechanism
277
+ def find_all_contacts_by_contact_mechanism(contact_mechanism_class)
278
+ table_name = contact_mechanism_class.name.tableize
279
+
280
+ contacts = self.contacts.joins("inner join #{table_name} on #{table_name}.id = contact_mechanism_id and contact_mechanism_type = '#{contact_mechanism_class.name}'")
281
+
282
+ contacts.collect(&:contact_mechanism)
283
+ end
284
+
285
+ # find first contact
286
+ def find_contact(contact_mechanism_class, contact_mechanism_args={}, contact_purposes=[])
287
+ find_contacts(contact_mechanism_class, contact_mechanism_args, contact_purposes).first
288
+ end
289
+
290
+ # find all contacts
291
+ def find_contacts(contact_mechanism_class, contact_mechanism_args={}, contact_purposes=[])
292
+ table_name = contact_mechanism_class.name.tableize
293
+
294
+ query = self.contacts.joins("inner join #{table_name} on #{table_name}.id = contact_mechanism_id and contact_mechanism_type = '#{contact_mechanism_class.name}'
295
+ inner join contact_purposes_contacts on contact_purposes_contacts.contact_id = contacts.id
296
+ and contact_purposes_contacts.contact_purpose_id in (#{contact_purposes.collect { |item| item.attributes["id"] }.join(',')})")
297
+
298
+ contact_mechanism_args.each do |key, value|
299
+ next if key == 'updated_at' or key == 'created_at' or key == 'id' or key == 'is_primary'
300
+ query = query.where("#{table_name}.#{key} = ?", value) unless value.nil?
301
+ end unless contact_mechanism_args.nil?
302
+
303
+ query
304
+ end
305
+
306
+ # Adds contact
307
+ def add_contact(contact_mechanism_class, contact_mechanism_args={}, contact_purposes=[])
308
+ is_primary = contact_mechanism_args['is_primary']
309
+ contact_purposes = [contact_purposes] if !contact_purposes.kind_of?(Array) # gracefully handle a single purpose not in an array
310
+
311
+ contact_mechanism_args.delete_if { |k, v| ['created_at', 'updated_at', 'is_primary'].include? k.to_s }
312
+ contact_mechanism = contact_mechanism_class.new(contact_mechanism_args)
313
+ contact_mechanism.contact.contact_record = self
314
+ contact_purposes.each do |contact_purpose|
315
+ if contact_purpose.is_a?(String)
316
+ contact_mechanism.contact.contact_purposes << ContactPurpose.iid(contact_purpose)
317
+ else
318
+ contact_mechanism.contact.contact_purposes << contact_purpose
319
+ end
320
+ end
321
+ contact_mechanism.contact.save!
322
+ contact_mechanism.save!
323
+
324
+ set_primary_contact(contact_mechanism_class, contact_mechanism) if is_primary
325
+
326
+ contact_mechanism
327
+ end
328
+
329
+ # tries to update contact by purpose
330
+ # if contact doesn't exist, it adds it
331
+ def update_or_add_contact_with_purpose(contact_mechanism_class, contact_purpose, contact_mechanism_args)
332
+ contact_mechanism = update_contact_with_purpose(contact_mechanism_class, contact_purpose, contact_mechanism_args)
333
+
334
+ unless contact_mechanism
335
+ contact_mechanism = add_contact(contact_mechanism_class, contact_mechanism_args, [contact_purpose])
336
+ end
337
+
338
+ contact_mechanism
339
+ end
340
+
341
+ # looks for a contact matching on purpose
342
+ # if it exists, it updates it, if not returns false
343
+ def update_contact_with_purpose(contact_mechanism_class, contact_purpose, contact_mechanism_args)
344
+ contact = find_contact_with_purpose(contact_mechanism_class, contact_purpose)
345
+ contact.nil? ? false : update_contact(contact_mechanism_class, contact, contact_mechanism_args)
346
+ end
347
+
348
+ def update_contact(contact_mechanism_class, contact, contact_mechanism_args)
349
+ set_primary_contact(contact_mechanism_class, contact.contact_mechanism) if contact_mechanism_args[:is_primary] == true
350
+
351
+ contact_mechanism_args.delete_if { |k, v| ['client_utc_offset'].include? k.to_s }
352
+ contact.contact_mechanism.update_attributes!(contact_mechanism_args)
353
+
354
+ contact.contact_mechanism
355
+ end
356
+
357
+ #
358
+ # Builds methods based on contacts and contact purposes associated to this record
359
+ # For example if there is a PhoneNumber with a contact purpose of Home associated it would
360
+ # create a method like home_phone_number
361
+ #
362
+ def build_contact_methods
363
+ self.contacts.each do |contact|
364
+ contact.contact_purposes.each do |contact_purpose|
365
+ klass = contact.contact_mechanism.class.name
366
+
367
+ self.class.send 'define_method', "#{contact_purpose.internal_identifier}_#{contact.contact_mechanism.class.name.underscore}" do
368
+ _klass_const = klass.camelize.constantize
369
+ _contact_purpose = contact_purpose
370
+ find_contact_mechanism_with_purpose(_klass_const, _contact_purpose)
371
+ end
372
+
373
+ end
374
+
375
+ end
376
+ end # build_contact_methods
377
+
378
+ end
379
+
380
+ end # HasContacts
381
+ end # ActiveRecord
382
+ end # Extensions
383
+ end # ErpBaseErpSvcs