erp_products 4.0.0 → 4.2.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v1/product_types_controller.rb +140 -0
  3. data/app/controllers/erp_products/shared/product_features_controller.rb +46 -0
  4. data/app/models/extensions/biz_txn_acct_root.rb +5 -0
  5. data/app/models/product_feature.rb +117 -0
  6. data/app/models/product_feature_applicability.rb +17 -0
  7. data/app/models/product_feature_interaction.rb +21 -0
  8. data/app/models/product_feature_interaction_type.rb +17 -0
  9. data/app/models/product_feature_type.rb +70 -0
  10. data/app/models/product_feature_type_product_feature_value.rb +16 -0
  11. data/app/models/product_feature_value.rb +42 -0
  12. data/app/models/product_instance.rb +4 -0
  13. data/app/models/product_offer.rb +25 -3
  14. data/app/models/product_type.rb +185 -21
  15. data/app/models/product_type_pty_role.rb +2 -0
  16. data/app/models/simple_product_offer.rb +3 -1
  17. data/config/routes.rb +15 -4
  18. data/db/data_migrations/20141222195727_add_product_type_view_types.rb +24 -0
  19. data/db/migrate/20080805000040_base_products.rb +284 -136
  20. data/db/migrate/20150304211841_add_shipping_cost_to_product_types.rb +7 -0
  21. data/db/migrate/20150507075057_add_dimensions_to_product_types.rb +12 -0
  22. data/db/migrate/20150622150625_add_taxable_to_product_types.rb +5 -0
  23. data/db/migrate/20150711194216_update_product_feature_interactions.rb +21 -0
  24. data/db/migrate/20151216235328_add_biz_txn_acct_root_to_products.rb +15 -0
  25. data/db/migrate/20160310163055_add_created_by_updated_by_to_erp_products.rb +43 -0
  26. data/lib/erp_products/engine.rb +0 -4
  27. data/lib/erp_products/extensions/active_record/acts_as_product_offer.rb +7 -4
  28. data/lib/erp_products/version.rb +1 -1
  29. metadata +24 -14
  30. data/app/controllers/erp_products/shared/product_types_controller.rb +0 -96
  31. data/app/views/erp_products/shared/product_types/show_details.html.erb +0 -8
  32. data/db/migrate/20080805000041_base_products_indexes.rb +0 -61
  33. data/db/migrate/20130131204335_add_product_instances_nested_set_indexes.rb +0 -9
  34. data/db/migrate/20130131204336_add_type_column_to_product_types.rb +0 -9
  35. data/db/migrate/20140124185720_add_product_party_roles.rb +0 -17
  36. data/db/migrate/20140130211433_add_sku_comment_uom_to_product_type.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd9373780450ef58a539f607556ee88ff417272e
4
- data.tar.gz: de281f11a15e17f9b5cc0914d31b104f67b8f5c9
3
+ metadata.gz: eb72e4e09cd0dec844882476e0cae80ecfb365f3
4
+ data.tar.gz: 30c5dde2888fd427d1cf601f5a1bab66bf9bafdc
5
5
  SHA512:
6
- metadata.gz: f86542e4a62072f0ac42a9def7addcaf76b592c5e8c2192e64671917d052c9bd54a6e90cf4cc71386a4d3c2efe25affa4d702c7204db8e73edda27f8752ea682
7
- data.tar.gz: 40763f3e853e92126be1fa46ecefe6b8fe5024c9452d1679d86c0d9f9eb293b3575245d9c29cfe7f7e87374b678e4bffe19cf5891ecbae55acfdf95f5a46fbd7
6
+ metadata.gz: 7257bfbed6ee33d738a5f9618a2a626bca9fe598fcfb86db4c215a0f39a2381df16ff9fe6199f41b4bdacf17693f0d213e4e0e99735cee97bc73258d18f40bcd
7
+ data.tar.gz: f2d0e65653701d0580ca1bf63648ed9cc5a13beceeb4473275fd8bd22a62bb5e393c9d42dbc28e8a085ceda1a71f6a3b7cea4c83a2a5d092dd6b5533f5c67fc3
@@ -0,0 +1,140 @@
1
+ module Api
2
+ module V1
3
+ class ProductTypesController < BaseController
4
+
5
+ def index
6
+ sort = nil
7
+ dir = nil
8
+ limit = nil
9
+ start = nil
10
+
11
+ unless params[:sort].blank?
12
+ sort_hash = params[:sort].blank? ? {} : Hash.symbolize_keys(JSON.parse(params[:sort]).first)
13
+ sort = sort_hash[:property] || 'description'
14
+ dir = sort_hash[:direction] || 'ASC'
15
+ limit = params[:limit] || 25
16
+ start = params[:start] || 0
17
+ end
18
+
19
+ query_filter = params[:query_filter].blank? ? {} : JSON.parse(params[:query_filter]).symbolize_keys
20
+
21
+ # hook method to apply any scopes passed via parameters to this api
22
+ product_types = ProductType.apply_filters(query_filter)
23
+
24
+ # scope by dba_organizations if there are no parties passed as filters
25
+ unless query_filter[:parties]
26
+ dba_organizations = [current_user.party.dba_organization]
27
+ dba_organizations = dba_organizations.concat(current_user.party.dba_organization.child_dba_organizations)
28
+ product_types = product_types.scope_by_dba_organization(dba_organizations)
29
+ end
30
+
31
+ if sort and dir
32
+ product_types = product_types.order("#{sort} #{dir}")
33
+ end
34
+
35
+ total_count = product_types.count
36
+
37
+ if start and limit
38
+ product_types = product_types.offset(start).limit(limit)
39
+ end
40
+
41
+ render :json => {success: true,
42
+ total_count: total_count,
43
+ product_types: product_types.collect { |product_type| product_type.to_data_hash }}
44
+
45
+ end
46
+
47
+ def show
48
+ product_type = ProductType.find(params[:id])
49
+
50
+ render :json => {success: true,
51
+ product_type: product_type.to_data_hash}
52
+ end
53
+
54
+ def create
55
+ begin
56
+ ActiveRecord::Base.transaction do
57
+ product_type = ProductType.new
58
+ product_type.description = params[:description]
59
+ product_type.sku = params[:sku]
60
+ product_type.unit_of_measurement_id = params[:unit_of_measurement]
61
+ product_type.comment = params[:comment]
62
+
63
+ product_type.created_by_party = current_user.party
64
+
65
+ product_type.save!
66
+
67
+ #
68
+ # For scoping by party, add party_id and role_type 'vendor' to product_party_roles table. However may want to override controller elsewhere
69
+ # so that default is no scoping in erp_products engine
70
+ #
71
+ party_role = params[:party_role]
72
+ party_id = params[:party_id]
73
+ unless party_role.blank? or party_id.blank?
74
+ product_type_party_role = ProductTypePtyRole.new
75
+ product_type_party_role.product_type = product_type
76
+ product_type_party_role.party_id = party_id
77
+ product_type_party_role.role_type = RoleType.iid(party_role)
78
+ product_type_party_role.save
79
+ end
80
+ end
81
+
82
+ render :json => {success: true,
83
+ product_type: product_type.to_data_hash}
84
+
85
+ rescue ActiveRecord::RecordInvalid => invalid
86
+
87
+ render :json => {success: false, message: invalid.record.errors.messages}
88
+
89
+ rescue => ex
90
+ Rails.logger.error ex.message
91
+ Rails.logger.error ex.backtrace.join("\n")
92
+
93
+ # email error
94
+ ExceptionNotifier.notify_exception(ex) if defined? ExceptionNotifier
95
+
96
+ render :json => {success: false, message: 'Could not create product type'}
97
+ end
98
+ end
99
+
100
+ def update
101
+ begin
102
+ ActiveRecord::Base.transaction do
103
+ product_type = ProductType.find(params[:id])
104
+
105
+ product_type.description = params[:description]
106
+ product_type.sku = params[:sku]
107
+ product_type.unit_of_measurement_id = params[:unit_of_measurement]
108
+ product_type.comment = params[:comment]
109
+
110
+ product_type.updated_by_party = current_user.party
111
+
112
+ product_type.save!
113
+
114
+ render :json => {success: true,
115
+ product_type: product_type.to_data_hash}
116
+ end
117
+ rescue ActiveRecord::RecordInvalid => invalid
118
+
119
+ render :json => {success: false, message: invalid.record.errors.messages}
120
+
121
+ rescue => ex
122
+ Rails.logger.error ex.message
123
+ Rails.logger.error ex.backtrace.join("\n")
124
+
125
+ # email error
126
+ ExceptionNotifier.notify_exception(ex) if defined? ExceptionNotifier
127
+
128
+ render :json => {success: false, message: 'Could not update product type'}
129
+ end
130
+ end
131
+
132
+ def destroy
133
+ ProductType.find(params[:id]).destroy
134
+
135
+ render :json => {:success => true}
136
+ end
137
+
138
+ end # ProductTypesController
139
+ end # V1
140
+ end # Api
@@ -0,0 +1,46 @@
1
+ module ErpProducts
2
+ module Shared
3
+ class ProductFeaturesController < ActionController::Base
4
+
5
+ def index
6
+ product_features = []
7
+ if params[:product_features]
8
+ params[:product_features].each do |product_feature|
9
+ found = ProductFeature.where(product_features: {product_feature_type_id: product_feature['type_id'].to_i, product_feature_value_id: product_feature['value_id'].to_i}).first
10
+ product_features << found if found
11
+ end
12
+ end
13
+
14
+ feature_type_arr = ProductFeature.get_feature_types(product_features)
15
+ render :json => {results: feature_type_arr}
16
+ end
17
+
18
+ def get_values
19
+ if params[:product_feature_type_id].present?
20
+ product_feature_type = ProductFeatureType.find(params[:product_feature_type_id])
21
+
22
+ if params[:product_features].present?
23
+ product_features = []
24
+ product_feature_params = params[:product_features].to_a.flatten.delete_if { |o| !o.is_a? Hash }
25
+ product_feature_params.each do |product_feature_hash|
26
+ product_features << ProductFeature.where('product_feature_type_id = ? and product_feature_value_id = ?',
27
+ product_feature_hash['product_feature_type_id'],
28
+ product_feature_hash['product_feature_value_id']).first
29
+ end
30
+
31
+ value_ids = ProductFeature.get_values(product_feature_type, product_features)
32
+ else
33
+ value_ids = ProductFeature.get_values(product_feature_type)
34
+ end
35
+
36
+ else
37
+ value_ids = []
38
+ end
39
+
40
+ render :json => {results: value_ids.map { |id| ProductFeatureValue.find(id) }}
41
+ end
42
+
43
+ end # ErpProducts
44
+ end # Shared
45
+ end # ProductFeaturesController
46
+
@@ -0,0 +1,5 @@
1
+ BizTxnAcctRoot.class_eval do
2
+
3
+ has_many :product_types
4
+
5
+ end
@@ -0,0 +1,117 @@
1
+ # create_table :product_features do |t|
2
+ # t.references :product_feature_type
3
+ # t.references :product_feature_value
4
+ #
5
+ # t.timestamps
6
+ # end
7
+ #
8
+ # add_index :product_features, :product_feature_type_id, :name => 'prod_feature_type_idx'
9
+ # add_index :product_features, :product_feature_value_id, :name => 'prod_feature_value_idx'
10
+
11
+ class ProductFeature < ActiveRecord::Base
12
+ attr_protected :created_at, :updated_at
13
+
14
+ tracks_created_by_updated_by
15
+
16
+ belongs_to :product_feature_type
17
+ belongs_to :product_feature_value
18
+ has_many :product_feature_applicabilities, dependent: :destroy
19
+
20
+ after_destroy :destroy_interactions
21
+
22
+ def self.find_or_create(product_feature_type, product_feature_value)
23
+ product_feature = ProductFeature.where(product_feature_type_id: product_feature_type.id,
24
+ product_feature_value_id: product_feature_value.id).first
25
+
26
+ unless product_feature
27
+ product_feature = ProductFeature.create(product_feature_type: product_feature_type,
28
+ product_feature_value: product_feature_value)
29
+ end
30
+
31
+ product_feature
32
+ end
33
+
34
+ def self.get_feature_types(product_features)
35
+ array = []
36
+ already_filtered_product_features = if product_features
37
+ product_features.map { |pf| pf.product_feature_type }
38
+ else
39
+ []
40
+ end
41
+ ProductFeatureType.each_with_level(ProductFeatureType.root.self_and_descendants) do |o, level|
42
+ if !already_filtered_product_features.include?(o) && level != 0
43
+ array << {feature_type: o, parent_id: o.parent_id, level: level}
44
+ end
45
+ end
46
+
47
+ block_given? ? yield(array) : array
48
+ end
49
+
50
+ def feature_of_records
51
+ product_feature_applicabilities.map { |o| o.feature_of_record_type.constantize.find(o.feature_of_record_id) }
52
+ end
53
+
54
+ def self.get_values(feature_type, product_features=[])
55
+ feature_value_ids = feature_type.product_feature_values.order('description').pluck(:id)
56
+ valid_feature_value_ids = feature_value_ids.dup
57
+
58
+ # if there are a product features passed then it is being scoped by that product feature and
59
+ # we only what valid interactions
60
+ unless product_features.empty?
61
+ product_features.each do |product_feature|
62
+
63
+ # check each possible feature type / feature value combination for the given feature_type
64
+ feature_value_ids.each do |value_id|
65
+
66
+ # Is there a product feature to support this feature type / feature value combination?
67
+ test_product_feature = ProductFeature.where(product_features: {product_feature_type_id: feature_type.id, product_feature_value_id: value_id}).last
68
+
69
+ valid_feature_value_ids.delete_at(valid_feature_value_ids.index(value_id)) unless test_product_feature
70
+ next unless test_product_feature
71
+
72
+ unless test_product_feature.find_interactions(:invalid).empty?
73
+ if test_product_feature.find_interactions(:invalid).where('product_feature_to_id = ?', product_feature.id).count == 1
74
+ index = valid_feature_value_ids.index(value_id)
75
+ if index
76
+ valid_feature_value_ids.delete_at(index)
77
+ end
78
+ end
79
+ end
80
+
81
+ end # feature_value_ids loop
82
+ end # product_features loop
83
+
84
+ end
85
+
86
+ valid_feature_value_ids.uniq
87
+ end
88
+
89
+ #
90
+ # Product Feature Interactions
91
+ #
92
+ def interactions
93
+ ProductFeatureInteraction.where('product_feature_from_id = ? or product_feature_to_id = ?', self.id, self.id)
94
+ end
95
+
96
+ def to_interactions
97
+ ProductFeatureInteraction.where('product_feature_to_id = ?', self.id)
98
+ end
99
+
100
+ def from_interactions
101
+ ProductFeatureInteraction.where('product_feature_from_id = ?', self.id)
102
+ end
103
+
104
+ # find product feature interactions by ProductFeatureInteractionsType
105
+ def find_interactions(type)
106
+ # look up type if iid is passed
107
+ type = ProductFeatureInteractionType.iid(type.to_s)
108
+
109
+ self.from_interactions.where('product_feature_interaction_type_id' => type.id)
110
+ end
111
+
112
+ # destroy all interactions this ProductFeature is part of
113
+ def destroy_interactions
114
+ self.interactions.destroy_all
115
+ end
116
+
117
+ end
@@ -0,0 +1,17 @@
1
+ # create_table :product_feature_applicabilities do |t|
2
+ # t.references :feature_of_record, :polymorphic => true
3
+ # t.references :product_feature
4
+ #
5
+ # t.boolean :is_mandatory
6
+ #
7
+ # t.timestamps
8
+ # end
9
+ #
10
+ # add_index :product_feature_applicabilities, [:feature_of_record_type, :feature_of_record_id], :name => 'prod_feature_record_idx'
11
+
12
+ class ProductFeatureApplicability < ActiveRecord::Base
13
+ attr_protected :created_at, :updated_at
14
+
15
+ belongs_to :feature_of_record, polymorphic: true
16
+ belongs_to :product_feature
17
+ end
@@ -0,0 +1,21 @@
1
+ # create_table :product_feature_interactions do |t|
2
+ # t.references :product_feature
3
+ # t.references :interacted_product_feature
4
+ # t.references :product_feature_interaction_type
5
+ #
6
+ # t.timestamps
7
+ # end
8
+ #
9
+ # add_index :product_feature_interactions, :product_feature_id, :name => 'prod_feature_int_feature_idx'
10
+ # add_index :product_feature_interactions, :interacted_product_feature_id, :name => 'prod_feature_int_interacted_feature_idx'
11
+ # add_index :product_feature_interactions, :product_feature_interaction_type_id, :name => 'prod_feature_int_interacted_feature_type_idx'
12
+
13
+ class ProductFeatureInteraction < ActiveRecord::Base
14
+ attr_protected :created_at, :updated_at
15
+
16
+ belongs_to :product_feature_from, class_name: "ProductFeature"
17
+ belongs_to :product_feature_to, class_name: "ProductFeature"
18
+ belongs_to :product_feature_interaction_type
19
+
20
+ is_json :custom_fields
21
+ end
@@ -0,0 +1,17 @@
1
+ # create_table :product_feature_interaction_types do |t|
2
+ # t.string :internal_identifier
3
+ # t.string :description
4
+ #
5
+ # t.timestamps
6
+ # end
7
+ #
8
+ # add_index :product_feature_interaction_types, :internal_identifier, name: 'product_ft_int_types_iid_idx'
9
+
10
+ class ProductFeatureInteractionType < ActiveRecord::Base
11
+ attr_protected :created_at, :updated_at
12
+
13
+ acts_as_erp_type
14
+
15
+ has_many :product_feature_interactions, dependent: :destroy
16
+
17
+ end
@@ -0,0 +1,70 @@
1
+ # create_table :product_feature_types do |t|
2
+ #
3
+ # t.integer :parent_id
4
+ # t.integer :lft
5
+ # t.integer :rgt
6
+ #
7
+ # t.string :description
8
+ # t.string :internal_identifier
9
+ # t.string :external_identifier
10
+ # t.string :external_id
11
+ #
12
+ # t.timestamps
13
+ # end
14
+ #
15
+ # add_index :product_feature_types, :internal_identifier, name: 'product_ft_types_iid_idx'
16
+ # add_index :product_feature_types, [:rgt, :lft, :parent_id], name: 'product_ft_types_nested_set_idx'
17
+
18
+ class ProductFeatureType < ActiveRecord::Base
19
+ attr_protected :created_at, :updated_at
20
+
21
+ tracks_created_by_updated_by
22
+
23
+ has_many :product_feature_type_product_feature_values, dependent: :destroy
24
+ has_many :product_feature_values, through: :product_feature_type_product_feature_values
25
+
26
+ has_many :product_features, dependent: :destroy
27
+
28
+ acts_as_nested_set
29
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods # acts_as_nested_set
30
+
31
+ def self.iid(description)
32
+ self.where("lower(description) = ?", description.downcase).first
33
+ end
34
+
35
+ def to_record_representation(root = ProductFeatureType.root)
36
+ # returns a string of category descriptions like
37
+ # 'main_category > sub_category n > ... > this category instance'
38
+ if root?
39
+ description
40
+ else
41
+ crawl_up_from(self, root).split('///').reverse.join(' > ')
42
+ end
43
+ end
44
+
45
+ def to_representation
46
+ # returns a string that consists of 1) a number of dashes equal to
47
+ # the category's level and 2) the category's description attr
48
+ rep = ''
49
+ level.times {rep << '- '}
50
+ rep << description
51
+ end
52
+
53
+ def self.to_all_representation(root = ProductFeatureType.root)
54
+ # returns an array of hashes which represent all categories in nested set order,
55
+ # each of which consists of the category's id and representation
56
+ container_arr = []
57
+ each_with_level(root.self_and_descendants) do |o, level|
58
+ container_arr << {:id => o.id, :description => o.to_representation}
59
+ end
60
+ container_arr
61
+ end
62
+
63
+ private
64
+
65
+ def crawl_up_from(product_feature_type, to_product_feature_type = ProductFeatureType.root)
66
+ # returns a string that is a '///'-separated list of categories
67
+ # from child category to root
68
+ "#{product_feature_type.description}///#{crawl_up_from(product_feature_type.parent, to_product_feature_type) if product_feature_type != to_product_feature_type}"
69
+ end
70
+ end