synergy_inventory_management 1.3.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.
@@ -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: []