erp_products 3.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.
Files changed (38) hide show
  1. data/GPL-3-LICENSE +674 -0
  2. data/README.rdoc +2 -0
  3. data/Rakefile +30 -0
  4. data/app/assets/javascripts/erp_products/application.js +9 -0
  5. data/app/assets/stylesheets/erp_products/application.css +7 -0
  6. data/app/controllers/erp_products/erp_app/desktop/product_manager/base_controller.rb +233 -0
  7. data/app/helpers/erp_products/application_helper.rb +4 -0
  8. data/app/models/prod_availability_status_type.rb +5 -0
  9. data/app/models/prod_instance_reln.rb +14 -0
  10. data/app/models/prod_instance_reln_type.rb +4 -0
  11. data/app/models/prod_instance_role_type.rb +4 -0
  12. data/app/models/prod_type_reln.rb +15 -0
  13. data/app/models/prod_type_reln_type.rb +4 -0
  14. data/app/models/prod_type_role_type.rb +4 -0
  15. data/app/models/product_instance.rb +21 -0
  16. data/app/models/product_offer.rb +11 -0
  17. data/app/models/product_package.rb +7 -0
  18. data/app/models/product_type.rb +37 -0
  19. data/app/models/simple_product_offer.rb +4 -0
  20. data/app/views/layouts/erp_products/application.html.erb +14 -0
  21. data/config/routes.rb +4 -0
  22. data/db/data_migrations/20110324010232_product_role_types.rb +23 -0
  23. data/db/data_migrations/20110527160807_add_default_prod_avail_types.rb +26 -0
  24. data/db/data_migrations/20110728201730_create_desktop_app_product_manager.rb +25 -0
  25. data/db/migrate/20080805000040_base_products.rb +232 -0
  26. data/db/migrate/20080805000041_base_products_indexes.rb +58 -0
  27. data/lib/erp_products/engine.rb +16 -0
  28. data/lib/erp_products/extensions/active_record/acts_as_product_instance.rb +50 -0
  29. data/lib/erp_products/extensions/active_record/acts_as_product_offer.rb +52 -0
  30. data/lib/erp_products/extensions/active_record/acts_as_product_type.rb +59 -0
  31. data/lib/erp_products/extensions.rb +3 -0
  32. data/lib/erp_products/version.rb +3 -0
  33. data/lib/erp_products.rb +5 -0
  34. data/lib/tasks/erp_products_tasks.rake +4 -0
  35. data/public/javascripts/erp_app/desktop/applications/product_manager/module.js +799 -0
  36. data/public/javascripts/erp_app/desktop/applications/product_manager/product_list_panel.js +141 -0
  37. data/public/stylesheets/erp_app/desktop/applications/product_manager/style.css +71 -0
  38. metadata +119 -0
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ErpProducts'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require "rspec/core/rake_task"
29
+ RSpec::Core::RakeTask.new(:spec)
30
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require_tree .
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,233 @@
1
+ module ErpProducts
2
+ module ErpApp
3
+ module Desktop
4
+ module ProductManager
5
+ class BaseController < ::ErpApp::Desktop::BaseController
6
+ def index
7
+ products = ProductType.all.collect do |product_type|
8
+ {
9
+ :id => product_type.id,
10
+ :title => product_type.description,
11
+ :imageUrl => product_type.images.empty? ? '/images/img_blank.png' : product_type.images.first.data.url,
12
+ :price => product_type.get_current_simple_amount_with_currency.nil? ? 'no price set' : product_type.get_current_simple_amount_with_currency,
13
+ :available => product_type.inventory_entries.first.number_available,
14
+ :sold => product_type.inventory_entries.first.number_sold,
15
+ :sku => product_type.inventory_entries.first.sku.nil? ? '' : product_type.inventory_entries.first.sku
16
+ }
17
+ end
18
+
19
+ render :json => {:products => products}
20
+ end
21
+
22
+ def show
23
+ ProductType.class_eval do
24
+ def long_description
25
+ self.descriptions.find_by_internal_identifier('long_description').description
26
+ end
27
+ end
28
+ product_type = ProductType.find(params[:id])
29
+ render :json => {:id => product_type.id, :description => product_type.long_description, :title => product_type.description}
30
+ end
31
+
32
+ def update
33
+ product_type = ProductType.find(params[:id])
34
+ product_type.description = params[:title]
35
+ descriptive_asset = product_type.find_description_by_iid('long_description')
36
+ descriptive_asset.description = params[:description]
37
+ descriptive_asset.save
38
+
39
+ render :json => product_type.save ? {:success => true} : {:success => false}
40
+ end
41
+
42
+ def new
43
+ result = {}
44
+
45
+ title = params[:title]
46
+ description = params[:description]
47
+
48
+ product_type = ProductType.new(
49
+ :description => title
50
+ )
51
+
52
+ product_type.descriptions << DescriptiveAsset.create(
53
+ :description => description,
54
+ :internal_identifier => 'long_description'
55
+ )
56
+
57
+ if product_type.save
58
+ #create inventory
59
+ inventory_entry = InventoryEntry.new(
60
+ :product_type => product_type,
61
+ :number_available => 0,
62
+ :number_sold => 0,
63
+ :description => product_type.description
64
+ )
65
+
66
+ if inventory_entry.save
67
+ result[:success] = true
68
+ result[:id] = product_type.id
69
+ else
70
+ product_type.destroy
71
+ result[:success] = false
72
+ end
73
+
74
+ else
75
+ result[:success] = false
76
+ end
77
+
78
+ render :json => result
79
+ end
80
+
81
+ def delete
82
+ render :json => (ProductType.find(params[:id]).destroy) ? {:success => true} : {:success => false}
83
+ end
84
+
85
+ #
86
+ #Images
87
+ #
88
+
89
+ def images
90
+ data = {:images => []}
91
+ product_type = ProductType.find(params[:id])
92
+
93
+ product_type.images.each do |image|
94
+ data[:images] << {:id => image.id, :name => image.name, :shortName => image.name, :url => image.data.url}
95
+ end
96
+
97
+ render :json => data
98
+ end
99
+
100
+ def new_image
101
+ result = {}
102
+
103
+ begin
104
+ name = request.env['HTTP_X_FILE_NAME'].blank? ? params[:file_data].original_filename : request.env['HTTP_X_FILE_NAME']
105
+ data = request.env['HTTP_X_FILE_NAME'].blank? ? params[:file_data] : request.raw_post
106
+
107
+ product_type = ProductType.find(params[:product_type_id])
108
+ #build path
109
+ path = File.join(product_type.images_path,name)
110
+
111
+ product_type.add_file(data, path)
112
+ result = {:success => true}
113
+ rescue Exception=>ex
114
+ logger.error ex.message
115
+ logger.error ex.backtrace.join("\n")
116
+ result = {:success => false, :error => "Error uploading #{name}"}
117
+ end
118
+
119
+ #file uploader wants this inline
120
+ render :inline => result.to_json
121
+ end
122
+
123
+ def delete_image
124
+ render :json => (FileAsset.find(params[:id]).destroy) ? {:success => true} : {:success => false}
125
+ end
126
+
127
+ #
128
+ #Prices
129
+ #
130
+
131
+ def currencies
132
+ Currency.include_root_in_json = false
133
+ render :inline => "{currencies:#{Currency.all.to_json(:only => [:id, :internal_identifier])}}"
134
+ end
135
+
136
+ def prices
137
+ result = {:prices => []}
138
+
139
+ product_type = ProductType.find(params[:id])
140
+ product_type.pricing_plans.each do |pricing_plan|
141
+ result[:prices] << {
142
+ :pricing_plan_id => pricing_plan.id,
143
+ :price => pricing_plan.money_amount,
144
+ :currency => pricing_plan.currency.id,
145
+ :currency_display => pricing_plan.currency.internal_identifier,
146
+ :from_date => pricing_plan.from_date,
147
+ :thru_date => pricing_plan.thru_date,
148
+ :description => pricing_plan.description,
149
+ :comments => pricing_plan.comments
150
+ }
151
+ end
152
+
153
+ render :json => result
154
+ end
155
+
156
+
157
+ #pricing uses one form for new models and updates. So we use one action
158
+ def new_and_update_price
159
+ result = {}
160
+
161
+ if params[:pricing_plan_id].blank?
162
+ pricing_plan = PricingPlan.new(
163
+ :money_amount => params[:price],
164
+ :comments => params[:comments],
165
+ :currency => Currency.find(params[:currency]),
166
+ :from_date => Date.strptime(params[:from_date], '%m/%d/%Y').to_date,
167
+ :thru_date => Date.strptime(params[:thru_date], '%m/%d/%Y').to_date,
168
+ :description => params[:description],
169
+ :is_simple_amount => true
170
+ )
171
+
172
+ if pricing_plan.save
173
+ product_type = ProductType.find(params[:product_type_id])
174
+ product_type.pricing_plans << pricing_plan
175
+ if product_type.save
176
+ result[:success] = true
177
+ else
178
+ pricing_plan.destroy
179
+ result[:success] = false
180
+ end
181
+ else
182
+ result[:success] = false
183
+ end
184
+ else
185
+ pricing_plan = PricingPlan.find(params[:pricing_plan_id])
186
+ pricing_plan.money_amount = params[:price]
187
+ pricing_plan.currency = Currency.find(params[:currency])
188
+ pricing_plan.from_date = Date.strptime(params[:from_date], '%m/%d/%Y').to_date
189
+ pricing_plan.thru_date = Date.strptime(params[:thru_date], '%m/%d/%Y').to_date
190
+ pricing_plan.description = params[:description]
191
+ pricing_plan.comments = params[:comments]
192
+
193
+ if pricing_plan.save
194
+ result[:success] = true
195
+ else
196
+ result[:success] = false
197
+ end
198
+ end
199
+
200
+ render :json => result
201
+ end
202
+
203
+ def delete_price
204
+ render :json => (PricingPlan.find(params[:id]).destroy) ? {:success => true} : {:success => false}
205
+ end
206
+
207
+ #
208
+ #Inventory
209
+ #
210
+
211
+ def inventory
212
+ result = {}
213
+
214
+ inventory_entry = InventoryEntry.find_by_product_type_id(params[:id])
215
+ result[:number_available] = inventory_entry.number_available
216
+ result[:sku] = inventory_entry.sku
217
+
218
+ render :json => result
219
+ end
220
+
221
+ def update_inventory
222
+ inventory_entry = InventoryEntry.find_by_product_type_id(params[:product_type_id])
223
+ inventory_entry.sku = params[:sku]
224
+ inventory_entry.number_available = params[:number_available]
225
+
226
+ render :json => (inventory_entry.save) ? {:success => true} : {:success => false}
227
+ end
228
+
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,4 @@
1
+ module ErpProducts
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ class ProdAvailabilityStatusType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ acts_as_erp_type
5
+ end
@@ -0,0 +1,14 @@
1
+ class ProdInstanceReln < ActiveRecord::Base
2
+
3
+ belongs_to :prod_instance_from, :class_name => "ProductInstance", :foreign_key => "prod_instance_id_from"
4
+ belongs_to :prod_instance_to, :class_name => "ProductInstance", :foreign_key => "prod_instance_id_to"
5
+
6
+ belongs_to :from_role, :class_name => "ProdInstanceRoleType", :foreign_key => "role_type_id_from"
7
+ belongs_to :to_role, :class_name => "ProdInstanceRoleType", :foreign_key => "role_type_id_to"
8
+
9
+ belongs_to :prod_instance_reln_type
10
+
11
+ alias :from_item :prod_instance_from
12
+ alias :to_item :prod_instance_to
13
+
14
+ end
@@ -0,0 +1,4 @@
1
+ class ProdInstanceRelnType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ end
@@ -0,0 +1,4 @@
1
+ class ProdInstanceRoleType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ end
@@ -0,0 +1,15 @@
1
+ class ProdTypeReln < ActiveRecord::Base
2
+
3
+ belongs_to :prod_type_from, :class_name => "ProductType", :foreign_key => "prod_type_id_from"
4
+ belongs_to :prod_type_to, :class_name => "ProductType", :foreign_key => "prod_type_id_to"
5
+
6
+ belongs_to :from_role, :class_name => "ProdTypeRoleType", :foreign_key => "role_type_id_from"
7
+ belongs_to :to_role, :class_name => "ProdTypeRoleType", :foreign_key => "role_type_id_to"
8
+
9
+ belongs_to :prod_type_reln_type
10
+
11
+ alias :from_item :prod_type_from
12
+ alias :to_item :prod_type_to
13
+
14
+
15
+ end
@@ -0,0 +1,4 @@
1
+ class ProdTypeRelnType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ end
@@ -0,0 +1,4 @@
1
+ class ProdTypeRoleType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ end
@@ -0,0 +1,21 @@
1
+ class ProductInstance < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+ acts_as_priceable
5
+
6
+ belongs_to :product_type
7
+ belongs_to :product_instance_status_type
8
+ belongs_to :product_instance_record, :polymorphic => true
9
+ belongs_to :prod_availability_status_type
10
+
11
+ alias :status :product_instance_status_type
12
+
13
+ def prod_instance_relns_to
14
+ ProdInstanceReln.where('prod_instance_id_to = ?',id)
15
+ end
16
+
17
+ def prod_instance_relns_from
18
+ ProdInstanceReln.where('prod_instance_id_from = ?',id)
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ class ProductOffer < ActiveRecord::Base
2
+
3
+ belongs_to :product_offer_record, :polymorphic => true
4
+
5
+ def after_destroy
6
+ if self.product_offer_record && !self.product_offer_record.frozen?
7
+ self.product_offer_record.destroy
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ class ProductPackage < ProductInstance
2
+
3
+ def components
4
+ ProdInstanceReln.where('prod_instance_id_to = ?',self.id).collect{|reln| reln.prod_instance_from}
5
+ end
6
+
7
+ end
@@ -0,0 +1,37 @@
1
+ class ProductType < ActiveRecord::Base
2
+ acts_as_nested_set
3
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
4
+
5
+ acts_as_priceable
6
+ has_file_assets
7
+ is_describable
8
+
9
+ belongs_to :product_type_record, :polymorphic => true
10
+ has_one :product_instance
11
+
12
+ def prod_type_relns_to
13
+ ProdTypeReln.where('prod_type_id_to = ?',id)
14
+ end
15
+
16
+ def prod_type_relns_from
17
+ ProdTypeReln.where('prod_type_id_from = ?',id)
18
+ end
19
+
20
+ def to_label
21
+ "#{description}"
22
+ end
23
+
24
+ def to_s
25
+ "#{description}"
26
+ end
27
+
28
+ def self.count_by_status(product_type, prod_availability_status_type)
29
+ ProductInstance.count("product_type_id = #{product_type.id} and prod_availability_status_type_id = #{prod_availability_status_type.id}")
30
+ end
31
+
32
+ def images_path
33
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
34
+ File.join(file_support.root,'products/images',"#{self.description.underscore}_#{self.id}")
35
+ end
36
+
37
+ end
@@ -0,0 +1,4 @@
1
+ class SimpleProductOffer < ActiveRecord::Base
2
+ acts_as_product_offer
3
+ belongs_to :product
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ErpProducts</title>
5
+ <%= stylesheet_link_tag "erp_products/application" %>
6
+ <%= javascript_include_tag "erp_products/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ ErpProducts::Engine.routes.draw do
2
+ #product_manager
3
+ match '/erp_app/desktop/product_manager(/:action(/:id))' => 'erp_app/desktop/product_manager/base'
4
+ end
@@ -0,0 +1,23 @@
1
+ class ProductRoleTypes
2
+
3
+ def self.up
4
+ ProdTypeRelnType.create(:internal_identifier => 'product_type_package_reln', :description => 'Product Type Package Relationship')
5
+ ProdTypeRoleType.create(:internal_identifier => 'product_type_package', :description => 'Product Type Package')
6
+ ProdTypeRoleType.create(:internal_identifier => 'packaged_product_type', :description => 'Packaged Product Type')
7
+
8
+ ProdInstanceRelnType.create(:internal_identifier => 'product_instance_package_reln', :description => 'Product Instance Package Relantionship')
9
+ ProdInstanceRoleType.create(:internal_identifier => 'product_instance_package', :description => 'Product Instance Package')
10
+ ProdInstanceRoleType.create(:internal_identifier => 'packaged_product_instance', :description => 'Packaged Product Instance')
11
+ end
12
+
13
+ def self.down
14
+ ['product_type_package_reln','product_type_package','packaged_product_type'].each do |iid|
15
+ ProdTypeRelnType.iid(iid).destroy
16
+ end
17
+
18
+ ['product_instance_package_reln','product_instance_package','packaged_product_instance'].each do |iid|
19
+ ProdInstanceRoleType.iid(iid).destroy
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ class AddDefaultProdAvailTypes
2
+
3
+ def self.up
4
+ ProdAvailabilityStatusType.create(
5
+ :description => 'Available',
6
+ :internal_identifier => 'available'
7
+ )
8
+
9
+ ProdAvailabilityStatusType.create(
10
+ :description => 'Sold',
11
+ :internal_identifier => 'sold'
12
+ )
13
+
14
+ ProdAvailabilityStatusType.create(
15
+ :description => 'Shipped',
16
+ :internal_identifier => 'shipped'
17
+ )
18
+ end
19
+
20
+ def self.down
21
+ %w(available sold shipped).each do |iid|
22
+ ProdAvailabilityStatusType.find_by_internal_identifier(iid).destroy
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,25 @@
1
+ class CreateDesktopAppProductManager
2
+ def self.up
3
+ app = DesktopApplication.create(
4
+ :description => 'Products',
5
+ :icon => 'icon-product',
6
+ :javascript_class_name => 'Compass.ErpApp.Desktop.Applications.ProductManager',
7
+ :internal_identifier => 'product_manager',
8
+ :shortcut_id => 'product_manager-win'
9
+ )
10
+
11
+ pt1 = PreferenceType.iid('desktop_shortcut')
12
+ pt1.preferenced_records << app
13
+ pt1.save
14
+
15
+ pt2 = PreferenceType.iid('autoload_application')
16
+ pt2.preferenced_records << app
17
+ pt2.save
18
+
19
+ app.save
20
+ end
21
+
22
+ def self.down
23
+ DesktopApplication.destroy_all(:conditions => ['internal_identifier = ?','hello_world'])
24
+ end
25
+ end