erp_products 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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