synergy_inventory_management 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in synergy_inventory_management.gemspec
4
+ gemspec
@@ -0,0 +1,88 @@
1
+ Synergy Inventory Management Interface (IMI)
2
+ ============================================
3
+
4
+ An interface where administrator can manage products easily on one page and quickly perform the common products actions.
5
+
6
+
7
+ Installation
8
+ ------------
9
+
10
+ Install synergy_inventory_management from git:
11
+
12
+ gem 'synergy_inventory_management', :git => 'git@github.com:secoint/synergy_inventory_management.git'
13
+
14
+ Bundle up with:
15
+
16
+ bundle install
17
+
18
+ Run the rake task that adds the assets to your project:
19
+
20
+ rails g synergy_inventory_management:install
21
+
22
+ Now you should be able to boot up your server with:
23
+
24
+ rails s
25
+
26
+ Enjoy!
27
+
28
+
29
+ Compatibility
30
+ -------------
31
+
32
+ Current version (0.2) compatible with Spree/Synergy 0.60.
33
+
34
+
35
+ Usage
36
+ -----
37
+
38
+ You can access the interface in Products -> Inventory management menu.
39
+
40
+ From the left there is a tree of taxonomies, where admin can manage nodes just like he does it in /admin/taxonomies now (create, move, rename etc.). After each taxon there is a number of products, having this taxon. By clicking on a taxon or taxonomy admin can see products having taxons in a selected node.
41
+
42
+ Products are shown in Products list. Products list contains the following columns:
43
+
44
+ * Selecting checkbox (helps to select a set of products for group processing).
45
+
46
+ * SKU
47
+
48
+ * Product name (also a link to edit product)
49
+
50
+ * Main price
51
+
52
+ * Indicator if a product has image
53
+
54
+ * Quick actions
55
+
56
+ Products list have pagination and "products per page" selector (10/50/100/All).
57
+
58
+ Each product has the following quick actions (from left to right):
59
+
60
+ * Indicator if the product is shown in a shop. If an indicator is dark, the product is hidden. By clicking on a dark indicator product becomes available from now. Light indicator tells that the product is active; admin can hide the product from now by clicking it.
61
+
62
+ * Preview the product on site (opens in a new tab).
63
+
64
+ * Edit product.
65
+
66
+ * Clone product.
67
+
68
+ * Delete product.
69
+
70
+ Above the Products list there a button "New product in this category". This button opens "new product" dialog with pre-defined taxon (current selected taxon).
71
+
72
+ Quick group products processing controls from the right perform just the most common actions with selected products:
73
+
74
+ * Publish from (select date).
75
+
76
+ * Quick hide from now.
77
+
78
+ * Add a taxon to selected products (select taxon from combo-box).
79
+
80
+ * Delete selected products.
81
+
82
+
83
+ Any questions?
84
+ --------------
85
+
86
+ Feel free to ask: synergy@secoint.ru
87
+
88
+ Russian-speaking community: http://groups.google.com/group/synergy-user
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ "1.3.x" => { :branch => "master" }
2
+ "0.70.x" => { :branch => "0-70" }
3
+ "0.60.x" => { :branch => "0-60" }
@@ -0,0 +1,33 @@
1
+ #synergy_imi #taxonomy_tree {
2
+ overflow: auto;
3
+ width: 100%;
4
+ }
5
+
6
+ #synergy_imi #products_actions{
7
+ margin-bottom: 20px;
8
+ }
9
+
10
+ #synergy_imi #current_path{
11
+ margin-bottom: 15px;
12
+ }
13
+
14
+ #synergy_imi .pagination {
15
+ padding-top: 0;
16
+ float: right;
17
+ }
18
+
19
+ #synergy_imi #per_page_form {
20
+ float: left;
21
+ }
22
+
23
+ #synergy_imi table.index td.actions {
24
+ white-space: nowrap;
25
+ }
26
+
27
+ #synergy_imi table.index td.has_image {
28
+ text-align: center;
29
+ }
30
+
31
+ #synergy_imi .actions a {
32
+ margin: 0 0 0 5px;
33
+ }
@@ -0,0 +1,55 @@
1
+ module Spree
2
+ Admin::ProductsController.class_eval do
3
+ skip_before_filter :load_resource, :only => [:edit_multiple, :update_multiple, :destroy_multiple]
4
+ before_filter :load_multiple, :only => [:update_multiple, :destroy_multiple]
5
+
6
+
7
+ def edit_multiple
8
+ session[:im_per_page] = params[:per_page].to_i if !params[:per_page].nil?
9
+ @taxon = params[:id].present? ? Taxon.find(params[:id]) : Taxon.root
10
+ product_ids = @taxon.present? ? @taxon.self_and_descendants.map { |tax| tax.product_ids }.flatten.uniq : []
11
+
12
+ params[:q] ||= {}
13
+ params[:q][:deleted_at_null] = '1' if params[:q][:deleted_at_null].nil?
14
+ params[:q][:s] ||= 'name asc'
15
+
16
+ @search = Product.where(:id => product_ids).ransack(params[:q])
17
+ # pagination_options = {:per_page => (session[:im_per_page] || 10), :page => params[:page]}
18
+ @collection = @search.result.group_by_products_id.page(params[:page]).per(session[:im_per_page] || 10)
19
+
20
+ respond_with(@collection) do |format|
21
+ format.html
22
+ format.json { render :json => json_data }
23
+ end
24
+ end
25
+
26
+ def update_multiple
27
+ @collection.each { |pr| pr.update_attributes!(params[:product]) }
28
+ flash[:notice] = I18n.t('sim.products_successfully_updated')
29
+ respond_with(@collection) do |format|
30
+ format.html { redirect_to admin_edit_multiple_products_url(:id => params[:id]) }
31
+ format.json { render :json => json_data }
32
+ end
33
+ end
34
+
35
+ def destroy_multiple
36
+ authorize! :destroy, Product
37
+ @collection.each do |pr|
38
+ pr.update_attributes!(:deleted_at => Time.zone.now)
39
+ pr.variants.each { |v| v.update_attributes!(:deleted_at => Time.zone.now) }
40
+ end
41
+ flash[:notice] = I18n.t('sim.products_successfully_deleted')
42
+ respond_with(@collection) do |format|
43
+ format.html { redirect_to admin_edit_multiple_products_url(:id => params[:id]) }
44
+ format.json { render :json => json_data }
45
+ end
46
+ end
47
+
48
+ def load_multiple
49
+ @collection = Product.where(:id => params[:product_ids])
50
+ end
51
+
52
+ private :load_multiple
53
+
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ Product.class_eval do
3
+ attr_accessible :add_taxon
4
+ attr_accessor :add_taxon
5
+ after_save :add_taxon_save
6
+
7
+ def active?
8
+ self.deleted_at.nil? and available?
9
+ end
10
+
11
+ def available?
12
+ available_on.nil? ? false : available_on < Time.zone.now
13
+ end
14
+
15
+ def add_taxon_save
16
+ return true unless add_taxon.present?
17
+ taxon = Taxon.find(add_taxon)
18
+ self.taxons << taxon if !self.taxons.include? taxon
19
+ true
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module Spree
2
+ Taxon.class_eval do
3
+ after_save :update_children_taxonomy, :if => :taxonomy_id_changed?
4
+
5
+ def not_deleted_products_count
6
+ self.self_and_descendants.map { |tax| tax.products.select(:id).not_deleted }.flatten.uniq.size
7
+ end
8
+
9
+ def update_children_taxonomy
10
+ self.children.each { |ch| ch.update_attributes(:taxonomy_id => self.taxonomy.id) }
11
+ true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(
2
+ :virtual_path => "spree/admin/shared/_product_sub_menu",
3
+ :insert_bottom => "[data-hook='admin_product_sub_tabs']",
4
+ :text => "<li class='<%= 'selected' if params[:action].to_s == 'edit_multiple' %>'><%= link_to I18n.t('sim.edit_multiple_products'), admin_edit_multiple_products_start_path %></li>",
5
+ :name => "sim_admin_product_sub_tabs")
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(
2
+ :virtual_path => "spree/admin/products/new",
3
+ :insert_bottom => '[data-hook="new_product"]',
4
+ :text => "<%= f.hidden_field :add_taxon %>",
5
+ :name => "sim_new_product_taxon_field")
@@ -0,0 +1,297 @@
1
+ <% content_for :head do %>
2
+ <%#= javascript_include_tag 'jquery.cookie.js', 'jsTree/jquery.jstree.js' %>
3
+ <%= stylesheet_link_tag 'admin/synergy_imi' %>
4
+ <script type="text/javascript">
5
+
6
+ // var taxonomy_id = '3';
7
+ var loading = '<%= escape_javascript t('loading') %>';
8
+ var new_taxon = '<%=escape_javascript t('new_taxon') %>';
9
+ var server_error = '<%=escape_javascript t('server_error') %>';
10
+ var taxonomy_tree_error = '<%=escape_javascript t('taxonomy_tree_error') %>';
11
+ var is_cut = false;
12
+ var last_rollback = null;
13
+
14
+ var handle_ajax_error = function(XMLHttpRequest, textStatus, errorThrown) {
15
+ jQuery.jstree.rollback(last_rollback);
16
+ jQuery("#ajax_error").show().html("<strong>" + server_error + "</strong><br/>" + taxonomy_tree_error);
17
+ };
18
+
19
+ var node_taxonomy = function (node) {
20
+ return node.attr('data-taxonomy') || node.parents('li[rel="root"]').attr('data-taxonomy');
21
+ }
22
+
23
+ var handle_move = function(e, data) {
24
+ last_rollback = data.rlbk;
25
+ var position = data.rslt.cp;
26
+ var node = data.rslt.o;
27
+ var new_parent = data.rslt.np;
28
+ var old_parent = data.rslt.op;
29
+
30
+ jQuery.ajax({
31
+ type: "POST",
32
+ dataType: "json",
33
+ //url: "/admin/taxonomies/" + node.attr('data-taxonomy') + "/taxons/" + node.attr("id"),
34
+ url: "/admin/taxonomies/" + node_taxonomy(old_parent) + "/taxons/" + node.attr("id"),
35
+ data: ({_method: "put", "taxon[taxonomy_id]": node_taxonomy(new_parent),"taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position, authenticity_token: AUTH_TOKEN}),
36
+ error: handle_ajax_error
37
+ });
38
+
39
+ return true
40
+ };
41
+
42
+ var handle_create = function(e, data) {
43
+ last_rollback = data.rlbk;
44
+ var node = data.rslt.obj;
45
+ var name = data.rslt.name;
46
+ var position = data.rslt.position;
47
+ var new_parent = data.rslt.parent;
48
+
49
+ jQuery.ajax({
50
+ type: "POST",
51
+ dataType: "json",
52
+ url: "/admin/taxonomies/" + node_taxonomy(new_parent) + "/taxons/",
53
+ data: ({"taxon[name]": name, "taxon[parent_id]": new_parent.attr("id"), "taxon[position]": position, authenticity_token: AUTH_TOKEN}),
54
+ error: handle_ajax_error,
55
+ success: function(data, result) {
56
+ node.attr('id', data.taxon.id);
57
+ }
58
+ });
59
+ };
60
+
61
+ var handle_rename = function(e, data) {
62
+ last_rollback = data.rlbk;
63
+ var node = data.rslt.obj;
64
+ var name = data.rslt.new_name;
65
+
66
+ jQuery.ajax({
67
+ type: "POST",
68
+ dataType: "json",
69
+ url: "/admin/taxonomies/" + node_taxonomy(node) + "/taxons/" + node.attr("id"),
70
+ data: ({_method: "put", "taxon[name]": name, authenticity_token: AUTH_TOKEN}),
71
+ error: handle_ajax_error
72
+ });
73
+ };
74
+
75
+ var handle_delete = function(e, data) {
76
+ last_rollback = data.rlbk;
77
+ var node = data.rslt.obj;
78
+
79
+
80
+ jConfirm('Are you sure you want to delete this taxon?', 'Confirm Taxon Deletion', function(r) {
81
+ if (r) {
82
+ jQuery.ajax({
83
+ type: "POST",
84
+ dataType: "json",
85
+ url: "/admin/taxonomies/0/taxons/" + node.attr("id"),
86
+ data: ({_method: "delete", authenticity_token: AUTH_TOKEN}),
87
+ error: handle_ajax_error
88
+ });
89
+ } else {
90
+ jQuery.jstree.rollback(last_rollback);
91
+ last_rollback = null;
92
+ }
93
+ });
94
+ };
95
+
96
+
97
+ var handle_select = function (event, data) {
98
+ data.rslt.obj.parents('.jstree-closed').each(function () {
99
+ data.inst.open_node(this);
100
+ });
101
+ };
102
+
103
+ jQuery(document).ready(function() {
104
+
105
+
106
+ var conf = {
107
+ "ui" : { "initially_select" : [ "<%= current_taxon.id %>" ] },
108
+ "core" : { "initially_open" : [ "<%= current_taxon.id %>" ] },
109
+ "themes" : {
110
+ "theme" : "apple",
111
+ "url" : "/assets/jquery.jstree/themes/apple/style.css"
112
+ },
113
+ "strings" : {
114
+ "new_node" : new_taxon,
115
+ "loading" : loading + "..."
116
+ },
117
+ "crrm" : {
118
+ "move" : {
119
+ "check_move" : function (m) {
120
+ var position = m.cp;
121
+ var node = m.o;
122
+ var new_parent = m.np;
123
+
124
+ if (!new_parent) return false; //no parent
125
+ if (node.attr("rel") == "root") return false; //can't drag root
126
+ if (new_parent.attr("id") == "taxonomy_tree") return false;
127
+ return true;
128
+ }
129
+ }
130
+ },
131
+ "contextmenu" : {
132
+ "items" : function(obj) {
133
+ var id_of_node = obj.attr("id");
134
+ var type_of_node = obj.attr("rel");
135
+ var menu = {};
136
+ if (type_of_node == "root") {
137
+ menu = {
138
+ "create" : {
139
+ "label" : "Create",
140
+ "action" : function (obj) {
141
+ this.create(obj);
142
+ }
143
+ },
144
+ "paste" : {
145
+ "separator_before" : true,
146
+ "label" : "Paste",
147
+ "action" : function (obj) {
148
+ is_cut = false;
149
+ this.paste(obj);
150
+ },
151
+ "_disabled" : is_cut == false
152
+ },
153
+ "edit" : {
154
+ "separator_before" : true,
155
+ "label" : "Edit",
156
+ "action" : function (obj) {
157
+ window.location = "/admin/taxonomies/" + node_taxonomy(obj) + "/taxons/" + obj.attr("id") + "/edit/";
158
+
159
+ }
160
+ }
161
+ }
162
+ } else {
163
+ menu = {
164
+ "create" : {
165
+ "label" : "Create",
166
+ "action" : function (obj) {
167
+ this.create(obj);
168
+ }
169
+ },
170
+ "rename" : {
171
+ "label" : "Rename",
172
+ "action" : function (obj) {
173
+ this.rename(obj);
174
+ }
175
+ },
176
+ "remove" : {
177
+ "label" : "Remove",
178
+ "action" : function (obj) {
179
+ this.remove(obj);
180
+ }
181
+ },
182
+ "cut" : {
183
+ "separator_before" : true,
184
+ "label" : "Cut",
185
+ "action" : function (obj) {
186
+ is_cut = true;
187
+ this.cut(obj);
188
+ }
189
+ },
190
+ "paste" : {
191
+ "label" : "Paste",
192
+ "action" : function (obj) {
193
+ is_cut = false;
194
+ this.paste(obj);
195
+ },
196
+ "_disabled" : is_cut == false
197
+ },
198
+ "edit" : {
199
+ "separator_before" : true,
200
+ "label" : "Edit",
201
+ "action" : function (obj) {
202
+ window.location = "/admin/taxonomies/" + node_taxonomy(obj) + "/taxons/" + obj.attr("id") + "/edit/";
203
+ }
204
+ }
205
+ }
206
+ }
207
+ return menu;
208
+ }
209
+ },
210
+
211
+ "plugins" : [ "themes", "json_data", "html_data", "dnd", "crrm", "contextmenu", "ui"]
212
+ };
213
+
214
+
215
+ $('#taxonomy_tree').jstree().delegate("a", "click", function(event, data) {
216
+ event.preventDefault();
217
+ $('#progress').show();
218
+ window.location.href = this.href
219
+ });
220
+
221
+ jQuery("#taxonomy_tree").jstree(conf)
222
+ .bind("select_node.jstree", handle_select)
223
+ .bind("move_node.jstree", handle_move)
224
+ .bind("remove.jstree", handle_delete)
225
+ .bind("create.jstree", handle_create)
226
+ .bind("rename.jstree", handle_rename);
227
+
228
+
229
+ jQuery(document).keypress(function(e) {
230
+ if (e.keyCode == 13) {
231
+ e.preventDefault();
232
+ }
233
+ });
234
+
235
+ $('#check-all').change(function(e) {
236
+ if (this.checked) {
237
+ $('.product-select').each(function() {
238
+ this.checked = true;
239
+ });
240
+ } else {
241
+ $('.product-select').each(function() {
242
+ this.checked = false;
243
+ });
244
+ }
245
+ });
246
+
247
+ $('#products_actions button').click(function() {
248
+
249
+ var count = $(".product-select:checked").length;
250
+ if (!count) {
251
+ alert('<%= t('sim.select_products') %>');
252
+ return false;
253
+ }
254
+
255
+ $('.product-select').clone().appendTo('#product_ids_form');
256
+ switch ($("input[name='products_action']:checked").val()) {
257
+ case 'publish_from':
258
+ $('#product_available_on').clone().appendTo('#product_ids_form');
259
+ break;
260
+ case 'hide':
261
+ $('#product_hide').clone().appendTo('#product_ids_form');
262
+ break;
263
+ case 'add_taxon':
264
+ $('<input>').attr({
265
+ type: 'hidden',
266
+ name: 'product[add_taxon]',
267
+ value: $('#product_add_taxon').val()
268
+ }).appendTo('#product_ids_form');
269
+ break;
270
+ case 'delete':
271
+ var del = confirm("<%= t('sim.confirm_destroy') %>");
272
+ if (!del) {
273
+ return false
274
+ }
275
+ $('#product_ids_form input[name=_method]').val('delete');
276
+ break;
277
+ default:
278
+ alert('<%= t('sim.select_action') %>');
279
+ return false;
280
+ }
281
+
282
+ $('#actions button').attr("disabled", "true");
283
+ $('#product_ids_form').submit();
284
+ });
285
+
286
+ $('.change-availability').live('click', function() {
287
+ $(this).parents('form').submit();
288
+ });
289
+
290
+ $('#per_page').change(function() {
291
+ $('#per_page_form').submit();
292
+ });
293
+ });
294
+
295
+ </script>
296
+
297
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <li id="<%= taxon.id %>" <%= "rel='root' data-taxonomy='#{taxon.taxonomy_id}'".html_safe if taxon.root? %>>
2
+ <%= link_to taxon.name, admin_edit_multiple_products_path(taxon) %>
3
+ <span>(<%= taxon.not_deleted_products_count %>)</span>
4
+ <% if taxon.children.any? %>
5
+ <ul><%= render :partial => 'taxon', :collection => taxon.children, :locals => {:current_taxon => current_taxon} %></ul>
6
+ <% end %>
7
+ </li>
8
+
@@ -0,0 +1,115 @@
1
+ <%= render :partial => 'spree/admin/shared/product_sub_menu' %>
2
+
3
+ <% content_for :page_title do %>
4
+ <%= t("sim.edit_multiple_products") %>
5
+ <% end %>
6
+
7
+ <% content_for :page_actions do %>
8
+ <li id="new_product_link">
9
+ <%= button_link_to t("sim.new_product_here"), new_object_url("product[add_taxon]" => @taxon.id), {"data-update" => :new_product, :icon => 'icon-plus', :id => 'admin_new_product'} %>
10
+ </li>
11
+ <% end %>
12
+
13
+ <div id="synergy_imi">
14
+
15
+ <div id="new_product"></div>
16
+ <p id="ajax_error" class="errorExplanation" style="display:none;"></p>
17
+ <% if @taxon.present? %>
18
+
19
+ <%= render :partial => 'js_head', :locals => {:current_taxon => @taxon} %>
20
+
21
+ <% content_for :sidebar do %>
22
+ <div class="sidebar-title">
23
+ <span><%= t("sim.catalog") %></span>
24
+ </div>
25
+ <div id='taxonomy_tree' style=" overflow: auto">
26
+ <ul><%= render :partial => "taxon", :collection => Spree::Taxon.roots, :locals => {:current_taxon => @taxon} %></ul>
27
+ </div>
28
+ <% end %>
29
+
30
+ <div class="twelve columns">
31
+ <fieldset id="products_actions">
32
+ <legend><%= t("sim.with_selected") %>:</legend>
33
+ <div class="four columns">
34
+ <p><label><%= radio_button_tag 'products_action', 'publish_from' %> <%= t("sim.publish_from") %></label>
35
+ <%= text_field 'product', :available_on, :style => 'width:100px', :class => 'datepicker' %></p>
36
+
37
+ <p><label><%= radio_button_tag 'products_action', 'hide' %> <%= t("sim.hide_from_now") %></label></p>
38
+ <%= hidden_field 'product', :available_on, :id => 'product_hide' %>
39
+ </div>
40
+ <div class="four columns">
41
+ <p><label><%= radio_button_tag 'products_action', 'add_taxon' %> <%= t("sim.add_taxon") %></label>
42
+ <% options_for_taxon = [] %>
43
+ <% Spree::Taxon.each_with_level(Spree::Taxon.roots.map{ |root|root.self_and_descendants}.flatten) { |tax, level| options_for_taxon << "<option value='#{tax.id}'>#{'-' * level}#{tax.name}</option>" } %>
44
+ <%= select 'product', :add_taxon, raw(options_for_taxon.join) %></p>
45
+
46
+ <p><label><%= radio_button_tag 'products_action', 'delete' %> <%= t("sim.delete") %></label></p>
47
+ </div>
48
+ <div class="two columns">
49
+ <%= button t("sim.go") %>
50
+ </div>
51
+ </fieldset>
52
+ </div>
53
+
54
+ <div class="clear"></div>
55
+
56
+ <div id="products_list" class="twelve columns">
57
+ <div id="current_path"><%= @taxon.self_and_ancestors.map {|tax| link_to tax.name, admin_edit_multiple_products_path(tax) }.join(" > ").html_safe %></div>
58
+
59
+ <table class="index" id="listing_products">
60
+ <thead>
61
+ <tr>
62
+ <th><%= check_box_tag 'check-all', 1, false, :id => 'check-all' %></th>
63
+ <th><%= t("sku") %></th>
64
+ <th><%= sort_link @search, :name, t("name") %></th>
65
+ <th><%= sort_link @search, :master_default_price_amount, t("master_price") %></th>
66
+ <th><%= t("sim.photo") %></th>
67
+ <th class="actions"></th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ <% @collection.each do |product| %>
72
+ <tr id="<%= dom_id product %>">
73
+ <td><%= check_box_tag 'product_ids[]', product.id, false, :class => 'product-select' %></td>
74
+ <td><%= product.sku %></td>
75
+ <td><%= product.name %></td>
76
+ <td><%= product.price %></td>
77
+ <td class="has_image"><%= product.images.any? ? image_tag('admin/icons/camera.png', :title => t("sim.has_photo")) : '' %></td>
78
+ <td class="actions">
79
+ <%= form_for product, :url => admin_product_path(product), :remote => true, :html => {:style => 'display:inline'} do |f| %>
80
+ <% if product.active? %>
81
+ <%= link_to ' ', 'javascript:void(0);', :class => 'change-availability icon-check no-text', :title => t('sim.published_hide') %>
82
+ <% else %>
83
+ <%= link_to ' ', 'javascript:void(0);', :class => 'change-availability icon-check-empty no-text', :title => t('sim.unpublished_show') %>
84
+ <% end %>
85
+ <%= f.hidden_field :available_on, :class => 'product_available_on', :value => (product.active? ? '' : Time.zone.now.strftime('%Y/%m/%d')) %>
86
+ <%- end %>
87
+ <% unless product.deleted? %>
88
+ <%= link_to_edit product, :no_text => true %>
89
+ <%= link_to_clone product, :no_text => true %>
90
+ <%= link_to_delete product, :no_text => true %>
91
+ <%= link_to_with_icon 'icon-search', t("preview"), product, :no_text => true, :target => "_blank" %>
92
+ <% end %>
93
+ </td>
94
+ </tr>
95
+ <% end %>
96
+ </tbody>
97
+ </table>
98
+ <%= form_tag admin_edit_multiple_products_path(@taxon), :id => 'product_ids_form', :method => :put, :style => "display:none" do %>
99
+ <% end %>
100
+ <%= paginate(@collection, :previous_label => "&#171; #{t('previous')}", :next_label => "#{t('next')} &#187;") %>
101
+ <%= form_tag '', :method => :get, :id => 'per_page_form' do %>
102
+ <%= label_tag 'per_page', t('sim.per_page') %>
103
+ <%= select_tag 'per_page', options_for_select([["10"], ["50"], ["100"], [t(:all), "10000"]], session[:im_per_page].to_s) %>
104
+ <% end %>
105
+ </div>
106
+ <% else %>
107
+ <p><%= t('sim.requires_at_least_one_taxon') %></p>
108
+ <% end %>
109
+ </div>
110
+
111
+
112
+
113
+
114
+
115
+
@@ -0,0 +1,2 @@
1
+ $('#product_<%= @object.id%> form a').replaceWith($("<%= escape_javascript @object.active? ? link_to(' ', 'javascript:void(0);', :class => 'change-availability icon-check no-text', :title => t('sim.published_hide')) : link_to(' ', 'javascript:void(0);', :class => 'change-availability icon-check-empty no-text', :title => t('sim.unpublished_show')) %>"));
2
+ $('#product_<%= @object.id%> .product_available_on').val('<%= @object.active? ? '' : Time.zone.now.strftime('%Y/%m/%d') %>');
@@ -0,0 +1,25 @@
1
+ ---
2
+ en:
3
+ sim:
4
+ edit_multiple_products: "Inventory management"
5
+ with_selected: "With selected"
6
+ catalog: "Catalog"
7
+ publish_from: "Publish from"
8
+ hide_from_now: "Hide from now"
9
+ add_taxon: "Add taxon"
10
+ delete: "Delete"
11
+ go: "Go"
12
+ per_page: "Per page"
13
+ photo: "Photo"
14
+ has_photo: "This product has photo"
15
+ products_successfully_updated: "Products successfully updated"
16
+ products_successfully_deleted: "Products successfully deleted"
17
+ requires_at_least_one_taxon: "Requires at least one taxon"
18
+ select_action: "Select action"
19
+ select_products: "Select products"
20
+ new_product_here: "New product in this category"
21
+ confirm_destroy: "Do you really want to delete chosen products?"
22
+ products_actions: "Actions"
23
+ published_hide: "Published (hide on click)"
24
+ unpublished_show: "Unpublished (show by click)"
25
+
@@ -0,0 +1,25 @@
1
+ ---
2
+ ru:
3
+ sim:
4
+ edit_multiple_products: "Управление номенклатурой"
5
+ with_selected: "С выбранными"
6
+ catalog: "Каталог"
7
+ publish_from: "Опубликовать с"
8
+ hide_from_now: "Скрыть с этого момента"
9
+ add_taxon: "Добавить таксон"
10
+ delete: "Удалить"
11
+ go: "Применить"
12
+ per_page: "Количество на странице"
13
+ photo: "Фото"
14
+ has_photo: "Этот товар имеет изображение"
15
+ products_successfully_updated: "Товары успешно обновлены"
16
+ products_successfully_deleted: "Товары успешно удалены"
17
+ requires_at_least_one_taxon: "Необходим хотя бы один таксон"
18
+ select_action: "Выберите действие"
19
+ select_products: "Выберите товары"
20
+ new_product_here: "Новый товар в этой категории"
21
+ confirm_destroy: "Вы действительно хотите удалить выбранные товары?"
22
+ products_actions: "Действия"
23
+ published_hide: "Опубликовано (по нажатию - скрыть)"
24
+ unpublished_show: "Не опубликовано (по нажатию - опубликовать)"
25
+
@@ -0,0 +1,9 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ namespace :admin do
3
+ match 'edit_multiple_products' => "products#edit_multiple", :as => :edit_multiple_products_start, :via => :get
4
+ match 'edit_multiple_products/:id' => "products#edit_multiple", :as => :edit_multiple_products, :via => :get
5
+ match 'edit_multiple_products/:id' => "products#update_multiple", :via => :put
6
+ match 'edit_multiple_products/:id' => "products#destroy_multiple", :via => :delete
7
+ match 'switch_product_availability/:id' => "products#switch_availability", :via => :put, :as => :switch_product_availability
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module SynergyInventoryManagement
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ def add_stylesheets
5
+ inject_into_file "app/assets/stylesheets/admin/all.css", " *= require admin/synergy_imi\n", :before => /\*\//, :verbose => true
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ require 'spree_core'
2
+
3
+ module SynergyInventoryManagement
4
+ class Engine < Rails::Engine
5
+
6
+ config.autoload_paths += %W(#{config.root}/lib)
7
+
8
+ initializer :assets do |config|
9
+ Rails.application.config.assets.precompile += %w( admin/synergy_imi.css )
10
+ end
11
+
12
+ def self.activate
13
+ Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
14
+ Rails.env.production? ? require(c) : load(c)
15
+ end
16
+ end
17
+
18
+ config.to_prepare &method(:activate).to_proc
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #$:.push File.expand_path("../lib", __FILE__)
3
+ #require "synergy_inventory_management/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.platform = Gem::Platform::RUBY
7
+ s.name = "synergy_inventory_management"
8
+ s.version = '1.3.0'
9
+ s.authors = 'Sergey Chazov (Service & Consulting)'
10
+ s.email = 'service@secoint.ru'
11
+ s.homepage = "https://github.com/secoint/synergy_inventory_management"
12
+ s.summary = 'Manage inventory'
13
+ s.description = 'Manage inventory'
14
+
15
+ s.rubyforge_project = "synergy_inventory_management"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ s.required_ruby_version = '>= 1.8.7'
22
+ s.add_dependency('spree_core', '>= 1.3.0')
23
+
24
+ # specify any dependencies here; for example:
25
+ # s.add_development_dependency "rspec"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: synergy_inventory_management
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sergey Chazov (Service & Consulting)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: spree_core
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.3.0
30
+ description: Manage inventory
31
+ email: service@secoint.ru
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - Versionfile
41
+ - app/assets/images/admin/icons/camera.png
42
+ - app/assets/stylesheets/admin/synergy_imi.css
43
+ - app/controllers/spree/admin/products_controller_decorator.rb
44
+ - app/models/spree/product_decorator.rb
45
+ - app/models/spree/taxon_decorator.rb
46
+ - app/overrides/sim_admin_product_sub_tabs.rb
47
+ - app/overrides/sim_new_product_taxon_field.rb
48
+ - app/views/spree/admin/products/_js_head.erb
49
+ - app/views/spree/admin/products/_taxon.erb
50
+ - app/views/spree/admin/products/edit_multiple.html.erb
51
+ - app/views/spree/admin/products/update.js.erb
52
+ - config/locales/en.yml
53
+ - config/locales/ru.yml
54
+ - config/routes.rb
55
+ - lib/generators/synergy_inventory_management/install/install_generator.rb
56
+ - lib/synergy_inventory_management.rb
57
+ - synergy_inventory_management.gemspec
58
+ homepage: https://github.com/secoint/synergy_inventory_management
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.8.7
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: synergy_inventory_management
78
+ rubygems_version: 1.8.24
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Manage inventory
82
+ test_files: []