├── app ├── helpers │ └── inventory_helper.rb ├── models │ ├── inventory_warehouse.rb │ ├── inventory_category.rb │ ├── inventory_part.rb │ ├── inventory_providor.rb │ └── inventory_movement.rb ├── views │ ├── settings │ │ └── _rim_settings.erb │ └── inventory │ │ ├── reports.erb │ │ ├── _category_form.html.erb │ │ ├── _warehouse_form.html.erb │ │ ├── categories.html.erb │ │ ├── _part_form.html.erb │ │ ├── warehouses.html.erb │ │ ├── _top_menu.html.erb │ │ ├── providors.html.erb │ │ ├── parts.html.erb │ │ ├── _providor_form.html.erb │ │ ├── _movement_out_form.html.erb │ │ ├── _movement_in_form.html.erb │ │ ├── movements.html.erb │ │ └── index.html.erb └── controllers │ └── inventory_controller.rb ├── test ├── test_helper.rb ├── fixtures │ ├── inventory_categories.yml │ ├── inventory_warehouses.yml │ ├── inventory_parts.yml │ ├── inventory_movements.yml │ └── inventory_providors.yml ├── functional │ └── inventory_controller_test.rb └── unit │ ├── inventory_part_test.rb │ ├── inventory_category_test.rb │ ├── inventory_movement_test.rb │ ├── inventory_providor_test.rb │ └── inventory_warehouse_test.rb ├── db └── migrate │ ├── 008_modifications_for_permission.rb │ ├── 004_create_inventory_categories.rb │ ├── 006_create_inventory_warehouses.rb │ ├── 001_create_inventory_parts.rb │ ├── 002_create_inventory_movements.rb │ ├── 003_create_inventory_providors.rb │ ├── 005_foreign_keys_and_views.rb │ └── 007_keys_and_modifications.rb ├── assets └── stylesheets │ └── inventory.css ├── init.rb ├── config ├── routes.rb └── locales │ ├── en.yml │ └── es.yml └── README.rdoc /app/helpers/inventory_helper.rb: -------------------------------------------------------------------------------- 1 | module InventoryHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/models/inventory_warehouse.rb: -------------------------------------------------------------------------------- 1 | class InventoryWarehouse < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the Redmine helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 3 | -------------------------------------------------------------------------------- /test/fixtures/inventory_categories.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | name: MyString 5 | descriptrion: 6 | two: 7 | id: 2 8 | name: MyString 9 | descriptrion: 10 | -------------------------------------------------------------------------------- /app/models/inventory_category.rb: -------------------------------------------------------------------------------- 1 | class InventoryCategory < ActiveRecord::Base 2 | #t.column :name, :string 3 | #t.column :description, :text 4 | 5 | has_many :inventory_parts 6 | 7 | validates_presence_of :name 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/inventory_warehouses.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | name: MyString 5 | location: MyString 6 | two: 7 | id: 2 8 | name: MyString 9 | location: MyString 10 | -------------------------------------------------------------------------------- /test/functional/inventory_controller_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryControllerTest < ActionController::TestCase 4 | # Replace this with your real tests. 5 | def test_truth 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/unit/inventory_part_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryPartTest < Test::Unit::TestCase 4 | fixtures :inventory_parts 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/unit/inventory_category_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryCategoryTest < Test::Unit::TestCase 4 | fixtures :inventory_categories 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/unit/inventory_movement_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryMovementTest < Test::Unit::TestCase 4 | fixtures :inventory_movements 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/unit/inventory_providor_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryProvidorTest < Test::Unit::TestCase 4 | fixtures :inventory_providors 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/unit/inventory_warehouse_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class InventoryWarehouseTest < Test::Unit::TestCase 4 | fixtures :inventory_warehouses 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/views/settings/_rim_settings.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 |
Hide Donate Add> 7 |
-------------------------------------------------------------------------------- /db/migrate/008_modifications_for_permission.rb: -------------------------------------------------------------------------------- 1 | class ModificationsForPermission < ActiveRecord::Migration[5.1] 2 | def self.up 3 | add_column :inventory_warehouses, :user_manager_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :inventory_warehouses, :user_manager_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/004_create_inventory_categories.rb: -------------------------------------------------------------------------------- 1 | class CreateInventoryCategories < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table :inventory_categories do |t| 4 | t.column :name, :string 5 | t.column :description, :text 6 | end 7 | end 8 | 9 | def self.down 10 | drop_table :inventory_categories 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/006_create_inventory_warehouses.rb: -------------------------------------------------------------------------------- 1 | class CreateInventoryWarehouses < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table :inventory_warehouses do |t| 4 | t.column :name, :string 5 | t.column :location, :string 6 | end 7 | end 8 | 9 | def self.down 10 | drop_table :inventory_warehouses 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/fixtures/inventory_parts.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | part_number: 5 | manufacturer: 6 | category: 7 | description: 8 | value: 9 | state: 1 10 | two: 11 | id: 2 12 | part_number: 13 | manufacturer: 14 | category: 15 | description: 16 | value: 17 | state: 1 18 | -------------------------------------------------------------------------------- /assets/stylesheets/inventory.css: -------------------------------------------------------------------------------- 1 | #inventory-menu{ 2 | border-bottom:2px solid #6692CC; 3 | margin: 10px -10px 0px -10px; 4 | } 5 | 6 | #inventory-menu ul{ 7 | padding:0; 8 | margin:0; 9 | margin-bottom:5px; 10 | } 11 | 12 | #inventory-menu ul li{ 13 | display: inline; 14 | list-style-type: none; 15 | padding-right: 20px; 16 | border:2px solid #6692CC; 17 | border-bottom:none; 18 | padding:5px; 19 | } -------------------------------------------------------------------------------- /test/fixtures/inventory_movements.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | part_number: 5 | quantity: 6 | document: 7 | document_type: 8 | value: 9 | providor_id: 10 | destiny_id: 11 | date: 12 | user_id: 1 13 | two: 14 | id: 2 15 | part_number: 16 | quantity: 17 | document: 18 | document_type: 19 | value: 20 | providor_id: 21 | destiny_id: 22 | date: 23 | user_id: 1 24 | -------------------------------------------------------------------------------- /db/migrate/001_create_inventory_parts.rb: -------------------------------------------------------------------------------- 1 | class CreateInventoryParts < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table :inventory_parts do |t| 4 | t.column :part_number, :string 5 | t.column :manufacturer, :string 6 | t.column :inventory_category_id, :integer 7 | t.column :description, :text 8 | t.column :value, :float 9 | t.column :status, :integer 10 | end 11 | end 12 | 13 | def self.down 14 | drop_table :inventory_parts 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/inventory_part.rb: -------------------------------------------------------------------------------- 1 | class InventoryPart < ActiveRecord::Base 2 | #t.column :part_number, :string 3 | #t.column :manufacturer, :string 4 | #t.column :inventory_category_id, :integer 5 | #t.column :description, :text 6 | #t.column :value, :float 7 | #t.column :status, :integer 8 | 9 | has_many :inventory_movements 10 | belongs_to :inventory_category 11 | 12 | validates_presence_of :part_number, :inventory_category, :value, :status 13 | validates_uniqueness_of :part_number 14 | end 15 | -------------------------------------------------------------------------------- /app/views/inventory/reports.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('reports').capitalize %>

3 |
4 | 5 | <%= select_tag(:warehouse, options_for_select(@warehouses,params[:warehouse]),{:onchange=>'submit();'}) %> 6 |
7 |

8 | ' onclick="location.href='/inventory/report_export/input_invoice?warehouse='+$('#warehouse').val()" /> 9 |

-------------------------------------------------------------------------------- /test/fixtures/inventory_providors.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | identification: 5 | name: 6 | address0: 7 | address1: 8 | city: 9 | state: 10 | country: 11 | phone0: 12 | phone1: 13 | fax: 14 | business: 15 | email: 16 | contact0: 17 | contact1: MyString 18 | two: 19 | id: 2 20 | identification: 21 | name: 22 | address0: 23 | address1: 24 | city: 25 | state: 26 | country: 27 | phone0: 28 | phone1: 29 | fax: 30 | business: 31 | email: 32 | contact0: 33 | contact1: MyString 34 | -------------------------------------------------------------------------------- /app/models/inventory_providor.rb: -------------------------------------------------------------------------------- 1 | class InventoryProvidor < ActiveRecord::Base 2 | #t.column :identification, :string 3 | #t.column :name, :string 4 | #t.column :address0, :string 5 | #t.column :address1, :string 6 | #t.column :city, :string 7 | #t.column :state, :string 8 | #t.column :country, :string 9 | #t.column :phone0, :string 10 | #t.column :phone1, :string 11 | #t.column :fax, :string 12 | #t.column :business, :string 13 | #t.column :email, :string 14 | #t.column :contact0, :string 15 | #t.column :contact1, :string 16 | 17 | validates_presence_of :identification, :name 18 | 19 | 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/002_create_inventory_movements.rb: -------------------------------------------------------------------------------- 1 | class CreateInventoryMovements < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table :inventory_movements do |t| 4 | t.column :inventory_part_id, :integer 5 | t.column :quantity, :float 6 | t.column :document, :string 7 | t.column :document_type, :integer 8 | t.column :value, :float 9 | t.column :inventory_providor_id, :integer 10 | t.column :project_id, :string 11 | t.column :other_destiny, :string 12 | t.column :date, :datetime 13 | t.column :user_id, :integer 14 | end 15 | end 16 | 17 | def self.down 18 | drop_table :inventory_movements 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | Redmine::Plugin.register :redmine_inventory_manager do 2 | name 'Redmine Inventory Manager Plugin' 3 | author 'Daniel Anguita O.' 4 | description 'Take your warehouse or office inventory on the same platform of your projects' 5 | version '0.9' 6 | url 'https://github.com/danielanguita/Redmine-Inventory-Manager' 7 | 8 | permission :inventory, {:inventory => [:index, :movements, :categories, :parts, :warehouses, :providors]}, :public => false 9 | 10 | menu :top_menu, :inventory, { :controller => 'inventory', :action => 'index' }, { :caption => 'Inventory', :before => 'admin'} 11 | 12 | settings :default => {'empty' => true}, :partial => 'settings/rim_settings' 13 | end 14 | 15 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # Plugin's routes 2 | # See: http://guides.rubyonrails.org/routing.html 3 | get 'inventory', to: 'inventory#index' 4 | 5 | get 'inventory(/:action(/:id))', controller: :inventory 6 | post 'inventory(/:action(/:id))', controller: :inventory 7 | put 'inventory(/:action(/:id))', controller: :inventory 8 | 9 | get 'inventory(/categories:action(/:id))', controller: :inventory 10 | post 'inventory(/categories:action(/:id))', controller: :inventory 11 | put 'inventory(/categories:action(/:id))', controller: :inventory 12 | 13 | get 'inventory(/parts:action(/:id))', controller: :inventory 14 | post 'inventory(/parts:action(/:id))', controller: :inventory 15 | put 'inventory(/parts:action(/:id))', controller: :inventory 16 | -------------------------------------------------------------------------------- /db/migrate/003_create_inventory_providors.rb: -------------------------------------------------------------------------------- 1 | class CreateInventoryProvidors < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table :inventory_providors do |t| 4 | t.column :identification, :string 5 | t.column :name, :string 6 | t.column :address0, :string 7 | t.column :address1, :string 8 | t.column :city, :string 9 | t.column :state, :string 10 | t.column :country, :string 11 | t.column :phone0, :string 12 | t.column :phone1, :string 13 | t.column :fax, :string 14 | t.column :business, :string 15 | t.column :email, :string 16 | t.column :contact0, :string 17 | t.column :contact1, :string 18 | end 19 | end 20 | 21 | def self.down 22 | drop_table :inventory_providors 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/views/inventory/_category_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for :inventory_category, @inventory_category, 2 | :url => {:action => 'categories', :id => @inventory_category}, 3 | :html => {:id => 'category-form'} do |f| %> 4 | <%= error_messages_for 'inventory_category' %> 5 |

6 | <%= f.text_field :name, :size => 15, :required => true %> 7 |
<%= f.text_field :description, :size => 100 %> 8 | <% if params[:edit] %> 9 | <% else %><% end %> 10 |

11 |

12 | <%= submit_tag l(:button_submit) %> 13 | <% if params[:edit] %>' onclick='location.href="categories"' /><% end %> 14 |

15 | <% end %> -------------------------------------------------------------------------------- /app/views/inventory/_warehouse_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for :inventory_warehouse, @inventory_warehouse, 2 | :url => {:action => 'warehouses', :id => @inventory_warehouse}, 3 | :html => {:id => 'warehouse-form'} do |f| %> 4 | <%= error_messages_for 'inventory_warehouse' %> 5 |

6 | <%= f.text_field :name, :size => 15, :required => true %> 7 |
<%= f.text_field :location, :size => 100 %> 8 |
<%= f.select :user_manager_id, @users %> 9 | <% if params[:edit] %> 10 | <% else %><% end %> 11 |

12 |

13 | <%= submit_tag l(:button_submit) %> 14 | <% if params[:edit] %>' onclick='location.href="warehouses"' /><% end %> 15 |

16 | <% end %> -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | #### THIS PLUG IS IN DEVELOPMENT. IF YOU NEED ANY FUNCTIONALITY RELATED TO THIS PLUGIN ADD IT IN THE ISSUES 2 | 3 | 4 | *Description* 5 | Take your warehouse or office inventory on the same platform of your projects. 6 | If you want a feature you can add issue. 7 | 8 | *Instalation* 9 | 1.- Install Redmine (http://www.redmine.org/wiki/1/RedmineInstall) 10 | 2.- Get the plugin and install 11 | $> cd #{RAILS_ROOT}/plugins 12 | $> git clone https://github.com/danielanguita/redmine_inventory_manager 13 | $> cd .. 14 | $> rake redmine:plugins:migrate RAILS_ENV=production 15 | 3.- Start your server and enjoy! :D 16 | 17 | *Notes* 18 | - upgraded to support redmine 5.1 19 | - Only two languages supportes for now (spanish and english) 20 | 21 | *Contributors* 22 | - Daniel Anguita @danielanguita 23 | - Imanol Alvarez @w0www 24 | - bluenevus @bluenevus 25 | - Emiliano Baum @emilianobaum 26 | 27 | Agrego a db/migrate y a controllers [5.1] para la version de redmine en uso. -------------------------------------------------------------------------------- /app/models/inventory_movement.rb: -------------------------------------------------------------------------------- 1 | class InventoryMovement < ActiveRecord::Base 2 | #t.column :inventory_part_id, :integer 3 | #t.column :quantity, :float 4 | #t.column :document, :string 5 | #t.column :document_type, :integer 6 | #t.column :value, :float 7 | #t.column :inventory_providor_id, :integer 8 | #t.column :project_id, :integer 9 | #t.column :another_destiny :string 10 | #t.column :date, :datetime 11 | #t.column :user_id, :integer 12 | 13 | belongs_to :inventory_part 14 | belongs_to :inventory_providor 15 | belongs_to :user 16 | belongs_to :project 17 | 18 | validates_presence_of :inventory_part, :quantity, :date, :user_id 19 | 20 | def doctype 21 | doc_types = { 22 | 1 => l('invoice'), 23 | 2 => l('ticket'), 24 | 3 => l('proforma-invoice'), 25 | 4 => l("waybill"), 26 | 5 => l("inventory") 27 | } 28 | if self.document_type 29 | return doc_types[document_type] 30 | end 31 | return nil 32 | end 33 | 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/views/inventory/categories.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('inventory_categories') %>

3 | <% if @has_permission %> 4 |
5 |

6 | <% if params[:edit] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('Category') %> 7 |

8 |
style='display:none;'<% end %>> 9 | <%= render :partial => 'category_form' %> 10 |
11 |
12 | <% end %> 13 | 14 | 15 | 16 | <% @categories.each do |c| %> 17 | 18 | 19 | <% if @has_permission %> 20 | 21 | 22 | <% end %> 23 | 24 | <% end %> 25 |
Id#<%= l('field_name') %><%= l('field_description') %>
<%= c.id %><%= c.name %><%= c.description %><%= link_to image_tag('edit.png'), 'categories?edit='+c.id.to_s %><%= link_to image_tag('delete.png'), 'categories?delete='+c.id.to_s, :confirm => l('delete_confirmation') %>
-------------------------------------------------------------------------------- /app/views/inventory/_part_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for :inventory_part, @inventory_part, 2 | :url => {:action => 'parts', :id => @inventory_part}, 3 | :html => {:id => 'part-form'} do |f| %> 4 | <%= error_messages_for 'inventory_part' %> 5 |

6 | <%= f.text_field :part_number, :size => 20, :required => true %> 7 |
<%= f.text_field :manufacturer, :size => 30, :required => true %> 8 |
<%= f.select(:inventory_category_id, @categories, {:include_blank => false}) %> 9 |
<%= f.text_field :description, :size => 30 %> 10 |
<%= f.text_field :where, :size => 30 %> 11 |
<%= f.text_field :value, :size => 6 %> 12 |
<%= f.select(:status, @statuses) %> 13 | <% if params[:edit] %> 14 | <% else %><% end %> 15 |

16 |

17 | <%= submit_tag l(:button_submit) %> 18 | <% if params[:edit] %>' onclick='location.href="parts"' /><% end %> 19 |

20 | <% end %> -------------------------------------------------------------------------------- /app/views/inventory/warehouses.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('inventory_warehouses') %>

3 | <% if @has_permission %> 4 |
5 |

6 | <% if params[:edit] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('Warehouse') %> 7 |

8 |
style='display:none;'<% end %>> 9 | <%= render :partial => 'warehouse_form' %> 10 |
11 |
12 | <% end %> 13 | 14 | 15 | 16 | <% @warehouses.each do |w| %> 17 | 18 | 19 | <% if @has_permission %> 20 | 21 | 22 | <% end %> 23 | 24 | <% end %> 25 |
Id#<%= l('field_name') %><%= l('field_location') %><%= l('field_user_manager') %>
<%= w.id %><%= w.name %><%= w.location %><%= User.find(w.user_manager_id).login rescue "s/a" %><%= link_to image_tag('edit.png'), 'warehouses?edit='+w.id.to_s %><%= link_to image_tag('delete.png'), 'warehouses?delete='+w.id.to_s, :confirm => l('delete_confirmation') %>
-------------------------------------------------------------------------------- /app/views/inventory/_top_menu.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :header_tags do %> 2 | <%= stylesheet_link_tag 'inventory', :plugin => 'redmine_inventory_manager' %> 3 | <% end %> 4 |
5 | 14 |
-------------------------------------------------------------------------------- /app/views/inventory/providors.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('inventory_providors') %>

3 | <% if @has_permission %> 4 |
5 |

6 | <% if params[:edit] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('Providor') %> 7 |

8 |
style='display:none;'<% end %>> 9 | <%= render :partial => 'providor_form' %> 10 |
11 |
12 | <% end %> 13 | 14 | 15 | 16 | <% @providors.each do |p| %> 17 | 18 | 19 | 20 | 21 | <% if @has_permission %> 22 | 23 | 24 | <% end %> 25 | 26 | <% end %> 27 |
Id#<%= l('field_identification') %><%= l('field_name') %><%= l('field_address0') %><%= l('field_phone0') %>
<%= p.id %><%= p.identification %><%= p.name %><%= p.address0 %>, <%= p.address1 %>, <%= p.city %><%= p.phone0 %> - <%= p.phone1 %><%= link_to image_tag('edit.png'), 'providors?edit='+p.id.to_s %><%= link_to image_tag('delete.png'), 'providors?delete='+p.id.to_s, :confirm => l('delete_confirmation') %>
-------------------------------------------------------------------------------- /db/migrate/005_foreign_keys_and_views.rb: -------------------------------------------------------------------------------- 1 | class ForeignKeysAndViews < ActiveRecord::Migration[5.1] 2 | def self.up 3 | execute <<-SQL 4 | ALTER TABLE `inventory_movements` ADD 5 | CONSTRAINT `fk_inventory_movement_part` FOREIGN KEY (`inventory_part_id`) 6 | REFERENCES `inventory_parts`(`id`) 7 | ON DELETE NO ACTION 8 | ON UPDATE NO ACTION; 9 | SQL 10 | 11 | execute <<-SQL 12 | ALTER TABLE `inventory_movements` ADD 13 | CONSTRAINT `fk_inventory_movement_providor` FOREIGN KEY (`inventory_providor_id`) 14 | REFERENCES `inventory_providors`(`id`) 15 | ON DELETE NO ACTION 16 | ON UPDATE NO ACTION; 17 | SQL 18 | 19 | execute <<-SQL 20 | ALTER TABLE `inventory_parts` ADD 21 | CONSTRAINT `fk_inventory_parts_category` FOREIGN KEY (`inventory_category_id`) 22 | REFERENCES `inventory_categories`(`id`) 23 | ON DELETE NO ACTION 24 | ON UPDATE NO ACTION; 25 | SQL 26 | end 27 | 28 | def self.down 29 | execute "ALTER TABLE `inventory_movements` DROP FOREIGN KEY `fk_inventory_movement_part`" 30 | execute "ALTER TABLE `inventory_movements` DROP FOREIGN KEY `fk_inventory_movement_providor`" 31 | execute "ALTER TABLE `inventory_parts` DROP FOREIGN KEY `fk_inventory_parts_category`" 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/views/inventory/parts.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('inventory_parts') %>

3 | <% if @has_permission %> 4 |
5 |

6 | <% if params[:edit] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('Part') %> 7 |

8 |
style='display:none;'<% end %>> 9 | <%= render :partial => 'part_form' %> 10 |
11 |
12 | <% end %> 13 | 14 | 15 | 16 | <% @parts.each do |p| %> 17 | 18 | 19 | 20 | 21 | <% if @has_permission %> 22 | 23 | 24 | <% end %> 25 | 26 | <% end %> 27 |
Id#<%= l('field_part_number') %><%= l('field_manufacturer') %><%= l('field_description') %><%= l('field_category') %><%= l('field_value') %><%= l('field_status') %>
<%= p.id %><%= p.part_number %><%= p.manufacturer %><%= p.description %><%= p.inventory_category.name %><%= number_to_currency(p.value) %><%= @statuses_array[p.status] %><%= link_to image_tag('edit.png'), 'parts?edit='+p.id.to_s %><%= link_to image_tag('delete.png'), 'parts?delete='+p.id.to_s, :confirm => l('delete_confirmation') %>
-------------------------------------------------------------------------------- /app/views/inventory/_providor_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for :inventory_providor, @inventory_providor, 2 | :url => {:action => 'providors', :id => @inventory_providor}, 3 | :html => {:id => 'providor-form'} do |f| %> 4 | <%= error_messages_for 'inventory_providor' %> 5 |

6 | <%= f.text_field :identification, :size => 20, :required => true %> 7 |
<%= f.text_field :name, :size => 30, :required => true %> 8 |
<%= f.text_field :address0, :size => 30 %> 9 |
<%= f.text_field :address1, :size => 30 %> 10 |
<%= f.text_field :city, :size => 20 %> 11 |
<%= f.text_field :state, :size => 20 %> 12 |
<%= f.text_field :country, :size => 20 %> 13 |
<%= f.text_field :phone0, :size => 10 %> 14 |
<%= f.text_field :phone1, :size => 10 %> 15 |
<%= f.text_field :fax, :size => 10 %> 16 |
<%= f.text_field :business, :size => 40 %> 17 |
<%= f.text_field :email, :size => 20 %> 18 |
<%= f.text_field :contact0, :size => 20 %> 19 |
<%= f.text_field :contact1, :size => 20 %> 20 | <% if params[:edit] %> 21 | <% else %><% end %> 22 |

23 |

24 | <%= submit_tag l(:button_submit) %> 25 | <% if params[:edit] %>' onclick='location.href="providors"' /><% end %> 26 |

27 | <% end %> -------------------------------------------------------------------------------- /app/views/inventory/_movement_out_form.html.erb: -------------------------------------------------------------------------------- 1 | 16 | <%= labelled_form_for :inventory_out_movement, @inventory_out_movement, 17 | :url => {:action => 'movements', :id => @inventory_out_movement}, 18 | :html => {:id => 'part-out-form'} do |f| %> 19 | <%= error_messages_for 'inventory_out_movement' %> 20 |

21 | <%= f.select(:warehouse_from_id, @warehouses,{:disabled => true}) %> 22 |
<%= f.select(:inventory_part_id, @parts) %> 23 |
<%= f.text_field :serial_number, :size => 8 %> 24 |
<%= f.text_field :quantity, :size => 4, :required => true %> 25 |
<%= f.select(:document_type, @doc_types) %> 26 |
<%= f.text_field :document, :size => 30 %> 27 |
28 | <%= select_tag("to_options", options_for_select(@to_options, params[:to_options]), {:onchange => 'show_to_input($("#to_options").val());'}) %> 29 |
style='display:none'<% end %>><%= f.select(:user_to_id, @users, {}, {:disabled => (params[:to_options] == 'user_to_id' ? false:true)}) %> 30 | style='display:none'<% end %>><%= f.select(:project_id, @inv_projects, {}, {:disabled => (params[:to_options] == 'project_id' ? false:true)}) %> 31 | <% if params[:edit_out] %> 32 | <% else %><% end %> 33 |

34 |

35 | <%= submit_tag l(:button_submit) %> 36 | <% if params[:edit_out] %>' onclick='location.href="movements"' /><% end %> 37 |

38 | <% end %> -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | "en": 2 | inventory: Inventory 3 | reports: Reports 4 | export: Export 5 | field_identification: ID/EIN 6 | field_name: Name 7 | field_address0: Address 8 | field_address1: Address 2 9 | field_city: City 10 | field_state: State 11 | field_country: Country 12 | field_phone0: Phone 13 | field_phone1: 2nd Phone 14 | field_fax: Fax 15 | field_business: Industry/Activity 16 | field_email: E-Mail 17 | field_contact0: Contact 18 | field_contact1: Contact 2 19 | field_inventory_category: Category 20 | field_manufacturer: Manufacturer/Branch 21 | field_part_number: Product 22 | field_inventory_part: Product 23 | field_inventory_providor: Providor 24 | field_quantity: Quantity 25 | field_squantity: Qty 26 | field_document: Document 27 | field_document_type: Document Type 28 | field_location: Location 29 | field_user_from: User 30 | field_warehouse_from: Warehouse 31 | field_user_to: User 32 | field_warehouse_to: Enter to Warehouse 33 | field_serial_number: Serial Number 34 | inventory_stock: Inventory Stock 35 | inventory_movements: Movements 36 | inventory_parts: Products 37 | inventory_categories: Categories 38 | inventory_warehouses: Warehouses 39 | inventory_providors: Providors 40 | inventory_manager: Inventory 41 | Edit: Edit 42 | Create: Create 43 | Delete: Delete 44 | Cancel: Cancel 45 | input_movements: In Movement 46 | output_movements: Out Movement 47 | User: User 48 | Project: Project 49 | Providor: Providor 50 | Warehouse: Warehouse 51 | Part: Product 52 | Category: Category 53 | all_warehouses: All Warehouses 54 | delete_confirmation: Are you sure you want to DELETE the registry? 55 | cant_delete_register: You can not delete the record. Probably should be related to anotherone 56 | out_of_stock: There is enough stock to make the out movement. 57 | field_where: Where is it kept? 58 | field_user_manager: Warehouse Manager 59 | permission_denied: You don't have permission to make changes on that Warehouse. 60 | field_short_part_number: Product 61 | From: From 62 | To: To 63 | Date: Date 64 | out_movement_list: Out Movements List 65 | in_movement_list: In Movements Lits 66 | part_comes_from: Product comes from 67 | part_goes_to: Product is delivered to 68 | inputs: Inputs 69 | outputs: Outputs 70 | stock: Stock 71 | last_movement: Last Movement 72 | total: Total 73 | inventory_value: Inventory Value 74 | invoice: Invoice 75 | proforma-invoice: Proforma Invoice 76 | waybill: Waybill 77 | ticket: Ticket 78 | active: Active 79 | obsolet: Obsolet 80 | discontinued: Discontinued 81 | inventory_inputs_document_report: In Movements w/Buying Documents 82 | -------------------------------------------------------------------------------- /config/locales/es.yml: -------------------------------------------------------------------------------- 1 | "es": 2 | inventory: Inventario 3 | reports: Informes 4 | field_identification: RUT 5 | field_name: Nombre 6 | field_address0: Dirección 7 | field_address1: Dirección 2 8 | field_city: Ciudad 9 | field_state: Comuna 10 | field_country: País 11 | field_phone0: Teléfono 12 | field_phone1: Teléfono 2 13 | field_fax: Fax 14 | field_business: Giro 15 | field_email: E-Mail 16 | field_contact0: Contacto 17 | field_contact1: Contacto 2 18 | field_inventory_category: Categoría 19 | field_manufacturer: Marca 20 | field_part_number: Producto 21 | field_inventory_part: Producto 22 | field_inventory_providor: Proveedor 23 | field_quantity: Cantidad 24 | field_squantity: Cant. 25 | field_document: Documento 26 | field_document_type: Tipo Documento 27 | field_location: Locación 28 | field_user_from: Usuario 29 | field_warehouse_from: Bodega 30 | field_user_to: Usuario 31 | field_warehouse_to: Entra a Bodega 32 | field_serial_number: Numero de Serie 33 | inventory_stock: Stock de Inventario 34 | inventory_movements: Movimientos 35 | inventory_parts: Productos 36 | inventory_categories: Categorías 37 | inventory_warehouses: Bodegas 38 | inventory_providors: Proveedores 39 | inventory_manager: Inventario 40 | Edit: Editar 41 | Create: Crear 42 | Delete: Eliminar 43 | Cancel: Cancelar 44 | input_movements: Movimiento de Entrada 45 | output_movements: Movimiento de Salida 46 | User: Usuario 47 | Project: Projecto 48 | Providor: Proveedor 49 | Warehouse: Bodega 50 | Part: Producto 51 | Category: Categoría 52 | all_warehouses: Todas las Bodegas 53 | delete_confirmation: Esta seguro que desea ELIMINAR el registro? 54 | cant_delete_register: No es posible eliminar el registro. Probablemente debe estar relacionado con otro. 55 | out_of_stock: No existe stock suficiente para realizar el movimiento de salida. 56 | field_where: ¿Donde se guarda? 57 | field_user_manager: Administrador de Bodega 58 | permission_denied: No tienes permiso para realizar cambios en esa Bodega 59 | field_short_part_number: Producto 60 | From: De 61 | To: To 62 | Date: Fecha 63 | out_movement_list: Lista de Salidas 64 | in_movement_list: Lista de Entradas 65 | part_comes_from: Producto proviene de 66 | part_goes_to: Producto se entrega a 67 | inputs: Entradas 68 | outputs: Salidas 69 | stock: Stock 70 | last_movement: Último Movimiento 71 | total: Total 72 | inventory_value: Inventario Valorizado 73 | invoice: Factura 74 | proforma-invoice: Factura Proforma 75 | waybill: Guía de Despacho 76 | ticket: Boleta 77 | active: Vigente 78 | obsolet: Obsoleto 79 | discontinued: Descontinuado 80 | export: Exportar 81 | reports: Informes 82 | inventory_inputs_document_report: Entradas c/Documentos de Compra 83 | -------------------------------------------------------------------------------- /app/views/inventory/_movement_in_form.html.erb: -------------------------------------------------------------------------------- 1 | 26 | <%= labelled_form_for :inventory_in_movement, @inventory_in_movement, 27 | :url => {:action => 'movements', :id => @inventory_in_movement}, 28 | :html => {:id => 'part-in-form'} do |f| %> 29 | <%= error_messages_for 'inventory_in_movement' %> 30 |

31 | 32 | <%= select_tag("from_options", options_for_select(@from_options, params[:from_options]), {:onchange => "show_from_input($('#from_options').val());"}) %> 33 |
style='display:none'<% end %>><%= f.select(:user_from_id, @users, {}, {:disabled => (params[:from_options] == 'user_from_id' ? false:true)}) %> 34 | style='display:none'<% end %>><%= f.select(:warehouse_from_id, @warehouses, {}, {:disabled => (params[:from_options] == 'warehouse_from_id' ? false:true)}) %> 35 | style='display:none'<% end %>><%= f.select(:inventory_providor_id, @providors, {}, {:disabled => (params[:from_options] == 'inventory_providor_id' ? false:true)}) %> 36 |
<%= f.select(:inventory_part_id, @parts, {}, {:onchange => "update_default_value($('#inventory_in_movement_inventory_part_id').val());"}) %> 37 |
<%= f.text_field :serial_number, :size => 8 %> 38 |
<%= f.text_field :quantity, :size => 4, :required => true %> 39 |
<%= f.select(:document_type, @doc_types) %> 40 |
<%= f.text_field :document, :size => 30 %> 41 |
<%= f.text_field :value, :size => 10 %> 42 |
<%= f.select(:warehouse_to_id, @warehouses, {},{:id => 'in_movement_warehouse_to'}) %> 43 | <% if params[:edit_in] %> 44 | <% else %><% end %> 45 |

46 |

47 | <%= submit_tag l(:button_submit) %> 48 | <% if params[:edit_in] %>' onclick='location.href="movements"' /><% end %> 49 |

50 | <% end %> -------------------------------------------------------------------------------- /db/migrate/007_keys_and_modifications.rb: -------------------------------------------------------------------------------- 1 | class KeysAndModifications < ActiveRecord::Migration[5.1] 2 | def self.up 3 | execute <<-SQL 4 | ALTER TABLE `inventory_parts` ADD 5 | UNIQUE INDEX `uk_inventory_part_part_number`(`part_number`); 6 | SQL 7 | 8 | execute <<-SQL 9 | ALTER TABLE `inventory_categories` ADD 10 | UNIQUE INDEX `uk_inventory_category_name`(`name`); 11 | SQL 12 | 13 | execute <<-SQL 14 | ALTER TABLE `inventory_providors` ADD 15 | UNIQUE INDEX `uk_inventory_providor_identification`(`identification`); 16 | SQL 17 | 18 | add_column :inventory_movements, :user_from_id, :integer 19 | add_column :inventory_movements, :user_to_id, :integer 20 | add_column :inventory_movements, :warehouse_to_id, :integer 21 | add_column :inventory_movements, :warehouse_from_id, :integer 22 | add_column :inventory_movements, :serial_number, :string 23 | 24 | execute <<-SQL 25 | ALTER TABLE `inventory_movements` ADD 26 | CONSTRAINT `fk_inventory_movement_warehouse_to` FOREIGN KEY (`warehouse_to_id`) 27 | REFERENCES `inventory_warehouses`(`id`) 28 | ON DELETE NO ACTION 29 | ON UPDATE NO ACTION; 30 | SQL 31 | 32 | execute <<-SQL 33 | ALTER TABLE `inventory_movements` ADD 34 | CONSTRAINT `fk_inventory_movement_warehouse_from` FOREIGN KEY (`warehouse_from_id`) 35 | REFERENCES `inventory_warehouses`(`id`) 36 | ON DELETE NO ACTION 37 | ON UPDATE NO ACTION; 38 | SQL 39 | 40 | execute <<-SQL 41 | ALTER TABLE `inventory_movements` ADD 42 | CONSTRAINT `fk_inventory_movement_user` FOREIGN KEY (`user_id`) 43 | REFERENCES `users`(`id`) 44 | ON DELETE NO ACTION 45 | ON UPDATE NO ACTION; 46 | SQL 47 | 48 | execute <<-SQL 49 | ALTER TABLE `inventory_movements` ADD 50 | CONSTRAINT `fk_inventory_movement_user_from` FOREIGN KEY (`user_from_id`) 51 | REFERENCES `users`(`id`) 52 | ON DELETE NO ACTION 53 | ON UPDATE NO ACTION; 54 | SQL 55 | 56 | execute <<-SQL 57 | ALTER TABLE `inventory_movements` ADD 58 | CONSTRAINT `fk_inventory_movement_user_to` FOREIGN KEY (`user_to_id`) 59 | REFERENCES `users`(`id`) 60 | ON DELETE NO ACTION 61 | ON UPDATE NO ACTION; 62 | SQL 63 | 64 | add_column :inventory_parts, :where, :string 65 | 66 | end 67 | 68 | def self.down 69 | execute "ALTER TABLE `inventory_parts` DROP INDEX `uk_inventory_part_part_number`" 70 | execute "ALTER TABLE `inventory_categories` DROP INDEX `uk_inventory_category_name`" 71 | execute "ALTER TABLE `inventory_providors` DROP INDEX `uk_inventory_providor_identification`" 72 | 73 | execute "ALTER TABLE `inventory_movements` DROP INDEX `fk_inventory_movements_user`" 74 | execute "ALTER TABLE `inventory_movements` DROP INDEX `fk_inventory_movements_user_from`" 75 | execute "ALTER TABLE `inventory_movements` DROP INDEX `fk_inventory_movements_user_to`" 76 | execute "ALTER TABLE `inventory_movements` DROP INDEX `fk_inventory_movement_warehouse_from`" 77 | execute "ALTER TABLE `inventory_movements` DROP INDEX `fk_inventory_movement_warehouse_to`" 78 | 79 | remove_column :inventory_movements, :user_from_id 80 | remove_column :inventory_movements, :user_to_id 81 | remove_column :inventory_movements, :warehouse_to_id 82 | remove_column :inventory_movements, :warehouse_from_id 83 | remove_column :inventory_movements, :serial_number 84 | remove_column :inventory_parts, :where 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /app/views/inventory/movements.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l(:inventory_movements) %>

3 | 4 |
5 | <% if @has_permission %> 6 |
7 |

8 | <% if params[:edit_in] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('input_movements') %> 9 |

10 |
style='display:none;'<% end %>> 11 | <%= render :partial => 'movement_in_form' %> 12 |
13 |
14 | <% end %> 15 | 16 |

<%= l('in_movement_list') %>

17 | 26 | 27 | 28 | 29 | <% @movements_in.each do |p| %> 30 | 31 | 32 | 41 | 42 | 43 | 44 | 45 | <% if @has_permission %> 46 | 47 | 48 | <% end %> 49 | 50 | <% end %> 51 |
Id#<%= l('field_short_part_number') %><%= l('From') %><%= l('Warehouse') %><%= l('field_squantity') %>$<%= l('field_document') %><%= l('Date') %>
<%= p.id %><%= p.inventory_part.part_number %> 33 | <% if p.user_from_id %> 34 | <%= User.find(p.user_from_id).login %> 35 | <% elsif p.inventory_providor %> 36 | <%= p.inventory_providor.name %> 37 | <% elsif p.warehouse_from_id %> 38 | <%= InventoryWarehouse.find(p.warehouse_from_id).name %> 39 | <% end %> 40 | <%= InventoryWarehouse.find(p.warehouse_to_id).name rescue "" %><%= p.quantity %><%= number_to_currency(p.value) %><%= p.document %><%= p.date.strftime("%Y-%m-%d") %><%= link_to image_tag('edit.png'), 'movements?edit_in='+p.id.to_s %><%= link_to image_tag('delete.png'), 'movements?delete='+p.id.to_s, :confirm => l('delete_confirmation') %>
52 |
53 | <% if @has_permission %> 54 |
55 |

56 | <% if params[:edit_out] %><%= l('Edit') %><% else %><%= l('Create') %><% end%> <%= l('output_movements') %> 57 |

58 |
style='display:none;'<% end %>> 59 | <%= render :partial => 'movement_out_form' %> 60 |
61 |
62 | <% end %> 63 | 64 |

<%= l('out_movement_list') %>

65 | 66 | 67 | <% @movements_out.each do |p| %> 68 | 69 | 70 | 71 | 72 | 79 | 80 | 81 | <% if @has_permission %> 82 | 83 | 84 | <% end %> 85 | 86 | <% end %> 87 |
Id#<%= l('field_short_part_number') %><%= l('Warehouse') %><%= l('To') %><%= l('field_squantity') %><%= l('field_document') %><%= l('Date') %>
<%= p.id %><%= p.inventory_part.part_number %><%= InventoryWarehouse.find(p.warehouse_from_id).name rescue "" %> 73 | <% if p.user_to_id %> 74 | <%= User.find(p.user_to_id).login %> 75 | <% elsif p.project %> 76 | <%= p.project.name %> 77 | <% end %> 78 | <%= p.quantity %><%= p.document %><%= p.date %><%= link_to image_tag('edit.png'), 'movements?edit_out='+p.id.to_s %><%= link_to image_tag('delete.png'), 'movements?delete='+p.id.to_s, :confirm => l('delete_confirmation') %>
88 |
-------------------------------------------------------------------------------- /app/views/inventory/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'top_menu' %> 2 |

<%= l('inventory_stock') %>

3 | <%= form_tag do %> 4 | 5 | <%= select_tag(:warehouse, options_for_select(@warehouses,params[:warehouse]),{:onchange=>'submit();'}) %> 6 | CSV' onclick="location.href='/inventory/inventory_stock_xls?warehouse='+$('#warehouse').val()" /> 7 | <% end %> 8 | 9 | 10 | 11 | 12 | 13 | <% 14 | total = 0 15 | @stock.each do |s| %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | <% 28 | total += s[4].to_f*s[7].to_f 29 | end %> 30 | 31 | 32 | 33 | 34 |
<%= l('field_short_part_number') %><%= l('field_category') %><%= l('field_description') %><%= l('field_value') %><%= l('inputs') %><%= l('outputs') %><%= l('stock') %><%= l('last_movement') %><%= l('total') %>
<%= s[0] %><% if s[1] and s[1].length > 0 %> (<%= s[1] %>)<% end %><%= s[2] %><%= s[3] %><%= number_to_currency(s[4]) %><%= s[5] %><%= s[6] %><%= s[7] %><%= s[8] %><%= number_to_currency(s[4].to_f*s[7].to_f) %>
<%= l('inventory_value') %><%= number_to_currency(total) %>
35 | 36 | <% unless Setting.plugin_redmine_inventory_manager["hide_donate_add"] %> 37 |

Help to keep this plugin up to date!

38 |
39 | 40 | 42 | 43 | 44 |
45 | <% end %> -------------------------------------------------------------------------------- /app/controllers/inventory_controller.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | class InventoryController < ApplicationController 3 | 4 | unloadable 5 | 6 | def reports 7 | @warehouses = InventoryWarehouse.order("name ASC").all.map {|w| [w.name, w.id]} 8 | @warehouses += [l('all_warehouses')] 9 | 10 | unless params[:warehouse] 11 | params[:warehouse] = l('all_warehouses') 12 | end 13 | end 14 | 15 | def report_export 16 | unless params[:id] 17 | params[:id] = nil 18 | end 19 | 20 | add = "" 21 | unless params[:warehouse] 22 | params[:warehouse] = l('all_warehouses') 23 | end 24 | 25 | if params[:warehouse] != l('all_warehouses') 26 | add = " AND warehouse_to_id = #{params[:warehouse]}" 27 | else 28 | add = " AND warehouse_to_id is not null" 29 | end 30 | 31 | if params[:id] == "input_invoice" 32 | @movements = InventoryMovement.where("document is not null and document != '' and document_type <= 3"+add).order('document ASC') 33 | 34 | headers = [l(:From),l(:field_document),l(:field_document_type),l(:field_category),l(:field_short_part_number), 35 | l(:field_serial_number), l(:field_squantity), l(:field_value),l(:total),l(:Date)] 36 | fields = [] 37 | @movements.each do |m| 38 | from = nil 39 | if m.user_from_id 40 | from = User.find(m.user_from_id).login 41 | elsif m.inventory_providor 42 | from = m.inventory_providor.name 43 | elsif m.warehouse_from_id 44 | from = InventoryWarehouse.find(m.warehouse_from_id).name 45 | end 46 | total = (m.quantity * m.value rescue 0) 47 | fields << [from, m.document, m.doctype, m.inventory_part.inventory_category.name, m.inventory_part.part_number, m.serial_number, m.quantity, m.value, total, m.date] 48 | end 49 | 50 | arrays = [] 51 | arrays[0] = headers 52 | arrays[1] = fields 53 | 54 | send_data(to_csv(arrays), :type => 'text/csv; header=present', :filename => 'in_movements_doc.csv') 55 | end 56 | end 57 | 58 | 59 | def index 60 | @warehouses = InventoryWarehouse.order("name ASC").all.map {|w| [w.name, w.id]} 61 | @warehouses += [l('all_warehouses')] 62 | 63 | add = "" 64 | unless params[:warehouse] 65 | params[:warehouse] = l('all_warehouses') 66 | end 67 | 68 | if params[:warehouse] != l('all_warehouses') 69 | add = " AND (`inventory_movements`.`warehouse_from_id` = #{params[:warehouse]} OR " + 70 | "`inventory_movements`.`warehouse_to_id` = #{params[:warehouse]})" 71 | params[:warehouse] = params[:warehouse].to_i 72 | end 73 | @stock = get_stock(add) 74 | end 75 | 76 | def get_stock(warehouse_query) 77 | sql = ActiveRecord::Base.connection() 78 | @stock = sql.execute( 79 | "SELECT in_movements.part_number as part_number, 80 | in_movements.serial_number as serial_number, in_movements.category as category, 81 | in_movements.part_description as description, in_movements.value, 82 | IFNULL(in_movements.quantity,0) as input, 83 | IFNULL(out_movements.quantity,0) as output, 84 | (IFNULL(in_movements.quantity,0)-IFNULL(out_movements.quantity,0)) as stock, 85 | GREATEST(IFNULL(in_movements.last_date,0), IFNULL(out_movements.last_date,0)) as last_movement 86 | FROM 87 | (SELECT `inventory_parts`.`part_number` AS `part_number`, `inventory_movements`.`serial_number` AS `serial_number`, 88 | `inventory_parts`.`value` AS `value`,sum(`inventory_movements`.`quantity`) AS `quantity`, 89 | max(`inventory_movements`.`date`) AS `last_date` 90 | FROM (`inventory_parts` 91 | LEFT JOIN `inventory_movements` on((`inventory_movements`.`inventory_part_id` = `inventory_parts`.`id`))) 92 | WHERE (isnull(`inventory_movements`.`inventory_providor_id`) AND isnull(`inventory_movements`.`user_from_id`)"+warehouse_query+" 93 | AND ((`inventory_movements`.`project_id` is not null) or (`inventory_movements`.`user_to_id` is not null))) 94 | GROUP BY `inventory_parts`.`id`,`inventory_movements`.`serial_number` 95 | ORDER BY `inventory_parts`.`part_number`) as out_movements 96 | RIGHT JOIN 97 | (SELECT `inventory_parts`.`part_number` AS `part_number`, `inventory_categories`.`name` AS `category`,`inventory_parts`.`description` AS `part_description`,`inventory_movements`.`serial_number` AS `serial_number`, 98 | `inventory_parts`.`value` AS `value`,sum(`inventory_movements`.`quantity`) AS `quantity`, 99 | max(`inventory_movements`.`date`) AS `last_date` 100 | FROM (`inventory_parts` 101 | LEFT JOIN `inventory_movements` on((`inventory_movements`.`inventory_part_id` = `inventory_parts`.`id`)) 102 | LEFT JOIN `inventory_categories` on((`inventory_categories`.`id` = `inventory_parts`.`inventory_category_id`))) 103 | WHERE (isnull(`inventory_movements`.`project_id`) and isnull(`inventory_movements`.`user_to_id`))"+warehouse_query+" 104 | GROUP BY `inventory_parts`.`id`,`inventory_movements`.`serial_number` 105 | ORDER BY `inventory_parts`.`part_number`) as in_movements 106 | ON 107 | (out_movements.part_number = in_movements.part_number 108 | AND out_movements.serial_number = in_movements.serial_number) 109 | ORDER BY category, part_number;") 110 | return @stock 111 | end 112 | 113 | def to_csv(arrays) 114 | decimal_separator = l(:general_csv_decimal_separator) 115 | 116 | export = CSV.generate(:col_sep => ";") do |csv| 117 | csv << arrays[0] #.collect {|header| begin; header.to_s; rescue; header.to_s; end } 118 | arrays[1].each do |row| 119 | 0.upto(row.length-1) do |i| 120 | if row[i].is_a?(Numeric) 121 | row[i] = row[i].to_s.gsub('.', decimal_separator) 122 | end 123 | end 124 | csv << row #.collect {|field| begin; field.to_s.encode("UTF-8"); rescue; field.to_s; end } 125 | end 126 | end 127 | 128 | return export 129 | end 130 | 131 | def inventory_stock_xls 132 | add = "" 133 | unless params[:warehouse] 134 | params[:warehouse] = l('all_warehouses') 135 | end 136 | 137 | if params[:warehouse] != l('all_warehouses') 138 | add = " AND (`inventory_movements`.`warehouse_from_id` = #{params[:warehouse]} OR " + 139 | "`inventory_movements`.`warehouse_to_id` = #{params[:warehouse]})" 140 | params[:warehouse] = params[:warehouse].to_i 141 | end 142 | @stock = get_stock(add) 143 | 144 | headers = [l(:field_short_part_number), l(:field_category), l(:field_description), l(:field_value), 145 | l(:inputs), l(:outputs), l(:stock), l(:last_movement), l(:total)] 146 | fields = [] 147 | total = 0 148 | @stock.each do |s| 149 | new_fields = [s[0],s[2],s[3],s[4],s[5],s[6],s[7],s[8],(s[4].to_f*s[7].to_f)] 150 | if s[1] and s[1].length > 0 151 | new_fields[0] << "(#{s[1]})" 152 | end 153 | total += s[4].to_f*s[7].to_f 154 | fields << new_fields 155 | end 156 | fields << [nil,nil,nil,nil,nil,nil,nil,l(:total),total] 157 | 158 | arrays = [] 159 | arrays[0] = headers 160 | arrays[1] = fields 161 | 162 | send_data(to_csv(arrays), :type => 'text/csv; header=present', :filename => 'inventory_stock.csv') 163 | end 164 | 165 | 166 | def ajax_get_part_value 167 | out = '' 168 | if params[:part_id] 169 | if part = InventoryPart.find(params[:part_id]) 170 | out = part.value.to_s 171 | end 172 | end 173 | render :text => out 174 | end 175 | 176 | def ajax_get_part_info 177 | out = [] 178 | if params[:part_number] 179 | if part = InventoryPart.where("part_number = '"+params[:part_number]+"'").first 180 | out = part.to_json 181 | end 182 | end 183 | render :json => out 184 | end 185 | 186 | def check_available_stock(movement) 187 | add = " AND (`inventory_movements`.`warehouse_from_id` = #{movement.warehouse_from_id} OR " + 188 | "`inventory_movements`.`warehouse_to_id` = #{movement.warehouse_from_id}) AND 189 | `inventory_movements`.`inventory_part_id` = #{movement.inventory_part_id}" 190 | 191 | unless movement.serial_number.blank? 192 | add << " AND `inventory_movements`.`serial_number` = '#{movement.serial_number}'" 193 | end 194 | sql = ActiveRecord::Base.connection() 195 | @stock = sql.select_one("SELECT in_movements.part_number as part_number, 196 | in_movements.serial_number as serial_number, 197 | in_movements.value, 198 | IFNULL(in_movements.quantity,0) as input, 199 | IFNULL(out_movements.quantity,0) as output, 200 | (IFNULL(in_movements.quantity,0)-IFNULL(out_movements.quantity,0)) as stock, 201 | GREATEST(IFNULL(in_movements.last_date,0), IFNULL(out_movements.last_date,0)) as last_movement 202 | FROM 203 | (SELECT `inventory_parts`.`part_number` AS `part_number`,`inventory_movements`.`serial_number` AS `serial_number`, 204 | `inventory_parts`.`value` AS `value`,sum(`inventory_movements`.`quantity`) AS `quantity`, 205 | max(`inventory_movements`.`date`) AS `last_date` 206 | FROM (`inventory_parts` 207 | LEFT JOIN `inventory_movements` on((`inventory_movements`.`inventory_part_id` = `inventory_parts`.`id`))) 208 | WHERE (isnull(`inventory_movements`.`inventory_providor_id`) AND isnull(`inventory_movements`.`user_from_id`)"+add+" 209 | AND ((`inventory_movements`.`project_id` is not null) or (`inventory_movements`.`user_to_id` is not null))) 210 | GROUP BY `inventory_parts`.`id`,`inventory_movements`.`serial_number` 211 | ORDER BY `inventory_parts`.`part_number`) as out_movements 212 | RIGHT JOIN 213 | (SELECT `inventory_parts`.`part_number` AS `part_number`,`inventory_movements`.`serial_number` AS `serial_number`, 214 | `inventory_parts`.`value` AS `value`,sum(`inventory_movements`.`quantity`) AS `quantity`, 215 | max(`inventory_movements`.`date`) AS `last_date` 216 | FROM (`inventory_parts` 217 | LEFT JOIN `inventory_movements` on((`inventory_movements`.`inventory_part_id` = `inventory_parts`.`id`))) 218 | WHERE (isnull(`inventory_movements`.`project_id`) and isnull(`inventory_movements`.`user_to_id`))"+add+" 219 | GROUP BY `inventory_parts`.`id`,`inventory_movements`.`serial_number` 220 | ORDER BY `inventory_parts`.`part_number`) as in_movements 221 | ON 222 | (out_movements.part_number = in_movements.part_number 223 | AND out_movements.serial_number = in_movements.serial_number);") 224 | # Uso de safe navigation operator (&.) 225 | return 0 if @stock.nil? || !@stock.key?('stock') 226 | @stock['stock'].to_f 227 | rescue 228 | 0 229 | end 230 | 231 | def user_has_warehouse_permission(user_id, warehouse_id) 232 | if warehouse_id == nil 233 | if InventoryWarehouse.count(:conditions => "user_manager_id = " + user_id.to_s) > 0 234 | return true 235 | end 236 | else 237 | if InventoryWarehouse.count(:conditions => ["user_manager_id = "+user_id.to_s+" and id = "+warehouse_id.to_s]) > 0 238 | return true 239 | end 240 | end 241 | return false 242 | end 243 | 244 | 245 | def movements 246 | @parts = InventoryPart.order("part_number ASC").all.map {|p| [p.part_number,p.id]} 247 | @providors = InventoryProvidor.order("name ASC").all.map {|p| [p.name,p.id]} 248 | @inv_projects = Project.order('name ASC').all.map {|p| [p.name,p.id]} 249 | @users = User.where('status=1').order('lastname ASC, firstname ASC').map {|u| [u.lastname+" "+u.firstname, u.id]} 250 | @warehouses = InventoryWarehouse.order("name ASC").all.map {|w| [w.name, w.id]} 251 | @from_options = {l('User') => 'user_from_id', l('Warehouse') => 'warehouse_from_id', l('Providor') => 'inventory_providor_id'} 252 | @to_options = {l('User') => 'user_to_id', l('Project') => 'project_id'} 253 | @doc_types = { l('invoice') => 1, l('ticket') => 2, l('proforma-invoice') => 3, l("waybill") => 4, l("inventory") => 5} 254 | current_user = find_current_user 255 | @has_permission = current_user.admin? || user_has_warehouse_permission(current_user.id, nil) 256 | 257 | unless params[:from_options] 258 | params[:from_options] = 'user_from_id' 259 | end 260 | 261 | unless params[:to_options] 262 | params[:to_options] = 'user_to_id' 263 | end 264 | 265 | if params[:delete] 266 | mdel = InventoryMovement.find(params[:delete]) rescue false 267 | if mdel 268 | if current_user.admin? or user_has_warehouse_permission(current_user.id, (mdel.warehouse_from_id != nil ? mdel.warehouse_from_id : 0)) or user_has_warehouse_permission(current_user.id, (mdel.warehouse_to_id != nil ? mdel.warehouse_to_id : 0)) 269 | ok = InventoryMovement.delete(mdel) rescue false 270 | unless ok 271 | flash[:error] = l('cant_delete_register') 272 | end 273 | else 274 | flash[:error] = l('permission_denied') 275 | end 276 | end 277 | end 278 | 279 | if params[:edit_in] 280 | @inventory_in_movement = InventoryMovement.find(params[:edit_in]) 281 | if @inventory_in_movement.user_from_id 282 | params[:from_options] = 'user_from_id' 283 | elsif @inventory_in_movement.inventory_providor 284 | params[:from_options] = 'inventory_providor_id' 285 | elsif @inventory_in_movement.warehouse_from_id 286 | params[:from_options] = 'warehouse_from_id' 287 | end 288 | else 289 | @inventory_in_movement = InventoryMovement.new 290 | end 291 | 292 | if params[:inventory_in_movement] 293 | if current_user.admin? or (user_has_warehouse_permission(current_user.id, params[:inventory_in_movement][:warehouse_to_id]) and (@inventory_in_movement.warehouse_to_id == nil ? true : user_has_warehouse_permission(current_user.id, @inventory_in_movement.warehouse_to_id))) 294 | unless params[:edit_in] 295 | @inventory_in_movement = InventoryMovement.new(params[:inventory_in_movement].permit!) 296 | 297 | available_stock = nil 298 | stock_ok = true 299 | if @inventory_in_movement.warehouse_from_id 300 | available_stock = check_available_stock(@inventory_in_movement) 301 | if @inventory_in_movement.quantity and @inventory_in_movement.quantity <= available_stock 302 | stock_ok = false 303 | end 304 | end 305 | 306 | if stock_ok 307 | @inventory_in_movement.user_id = current_user.id 308 | @inventory_in_movement.date = DateTime.now 309 | if @inventory_in_movement.save 310 | @inventory_in_movement = InventoryMovement.new(params[:inventory_in_movement].permit!) 311 | @inventory_in_movement.inventory_part = nil 312 | @inventory_in_movement.serial_number = nil 313 | @inventory_in_movement.quantity = nil 314 | @inventory_in_movement.value = nil 315 | params[:create_in] = true 316 | end 317 | else 318 | flash[:error] = l('out_of_stock') 319 | end 320 | 321 | else 322 | if @inventory_in_movement.update(params[:inventory_in_movement].permit!) 323 | params[:edit_in] = false 324 | end 325 | end 326 | else 327 | flash[:error] = l('permission_denied') 328 | end 329 | end 330 | 331 | if params[:edit_out] 332 | @inventory_out_movement = InventoryMovement.find(params[:edit_out]) 333 | if @inventory_out_movement.user_from_id 334 | params[:to_options] = 'user_to_id' 335 | elsif @inventory_out_movement.inventory_providor 336 | params[:to_options] = 'project_id' 337 | end 338 | else 339 | @inventory_out_movement = InventoryMovement.new 340 | end 341 | 342 | if params[:inventory_out_movement] 343 | if current_user.admin? or (user_has_warehouse_permission(current_user.id, params[:inventory_out_movement][:warehouse_from_id]) and 344 | (@inventory_out_movement.warehouse_from_id == nil ? true : user_has_warehouse_permission(current_user.id, @inventory_out_movement.warehouse_from_id))) 345 | unless params[:edit_out] 346 | @inventory_out_movement = InventoryMovement.new(params[:inventory_out_movement].permit!) 347 | available_stock = check_available_stock(@inventory_out_movement) 348 | if @inventory_out_movement.quantity and @inventory_out_movement.quantity <= available_stock 349 | @inventory_out_movement.user_id = current_user.id 350 | @inventory_out_movement.date = DateTime.now 351 | if @inventory_out_movement.save 352 | @inventory_out_movement = InventoryMovement.new(params[:inventory_out_movement].permit!) 353 | @inventory_out_movement.inventory_part = nil 354 | @inventory_out_movement.serial_number = nil 355 | @inventory_out_movement.quantity = nil 356 | @inventory_out_movement.value = nil 357 | params[:create_out] = true 358 | end 359 | else 360 | flash[:error] = l('out_of_stock') 361 | end 362 | else 363 | ok = true 364 | if @inventory_out_movement.quantity < params[:inventory_out_movement][:quantity].to_f 365 | available_stock = check_available_stock(@inventory_out_movement) 366 | unless (params[:inventory_out_movement][:quantity].to_f - @inventory_out_movement.quantity) <= available_stock 367 | ok = false 368 | end 369 | end 370 | if ok 371 | if @inventory_out_movement.update(params[:inventory_out_movement].permit!) 372 | params[:edit_out] = false 373 | end 374 | else 375 | flash[:error] = l('out_of_stock') 376 | end 377 | end 378 | else 379 | flash[:error] = l('permission_denied') 380 | end 381 | end 382 | 383 | @movements_in = InventoryMovement.where("project_id is null and user_to_id is null").order("date DESC") 384 | @movements_out = InventoryMovement.where("inventory_providor_id is null and user_from_id is null and (project_id is not null or user_to_id is not null)").order("date DESC") 385 | end 386 | 387 | 388 | def categories 389 | @inventory_category = InventoryCategory.new 390 | current_user = find_current_user 391 | @has_permission = current_user.admin? || user_has_warehouse_permission(current_user.id, nil) 392 | 393 | if params[:delete] or params[:edit] or params[:inventory_category] 394 | if @has_permission 395 | 396 | if params[:delete] 397 | ok = InventoryCategory.delete(params[:delete]) rescue false 398 | unless ok 399 | flash[:error] = l('cant_delete_register') 400 | end 401 | end 402 | 403 | if params[:edit] 404 | @inventory_category = InventoryCategory.find(params[:edit]) 405 | else 406 | @inventory_category = InventoryCategory.new 407 | end 408 | 409 | if params[:inventory_category] 410 | # @inventory_category.update(params[:inventory_category].permit!) 411 | # Modificaciones para permitir los cambios 412 | @inventory_category.update(params[:inventory_category]) 413 | if @inventory_category.save 414 | @inventory_category = InventoryCategory.new 415 | params[:edit] = false 416 | params[:create] = false 417 | end 418 | end 419 | 420 | else 421 | flash[:error] = l('permission_denied') 422 | end 423 | end 424 | 425 | @categories = InventoryCategory.all 426 | end 427 | 428 | def parts 429 | @inventory_part = InventoryPart.new 430 | @categories = InventoryCategory.order("name ASC").all.map {|c| [c.name,c.id]} 431 | @statuses = { l('active') => 1, l("obsolet") => 2, l('discontinued') => 3} 432 | @statuses_array = ['',l('active'),l("obsolet"),l('discontinued')] 433 | current_user = find_current_user 434 | @has_permission = current_user.admin? || user_has_warehouse_permission(current_user.id, nil) 435 | if params[:delete] or params[:edit] or params[:inventory_part] 436 | if @has_permission 437 | 438 | if params[:delete] 439 | ok = InventoryPart.delete(params[:delete]) rescue false 440 | unless ok 441 | flash[:error] = l('cant_delete_register') 442 | end 443 | end 444 | 445 | if params[:edit] 446 | @inventory_part = InventoryPart.find(params[:edit]) 447 | else 448 | @inventory_part = InventoryPart.new 449 | end 450 | 451 | if params[:inventory_part] 452 | # @inventory_part.update(params[:inventory_part].permit!) 453 | # Modificaciones para permitir los cambios en objetos ya creados 454 | @inventory_part.update(params[:inventory_part]) 455 | if @inventory_part.save 456 | @inventory_part = InventoryPart.new 457 | params[:edit] = false 458 | params[:create] = false 459 | end 460 | end 461 | 462 | else 463 | flash[:error] = l('permission_denied') 464 | end 465 | end 466 | @parts = InventoryPart.all 467 | end 468 | 469 | def providors 470 | @inventory_providor = InventoryProvidor.new 471 | current_user = find_current_user 472 | @has_permission = current_user.admin? || user_has_warehouse_permission(current_user.id, nil) 473 | if params[:delete] or params[:edit] or params[:inventory_providor] 474 | if @has_permission 475 | 476 | if params[:delete] 477 | ok = InventoryProvidor.delete(params[:delete]) rescue false 478 | unless ok 479 | flash[:error] = l('cant_delete_register') 480 | end 481 | end 482 | 483 | if params[:edit] 484 | @inventory_providor = InventoryProvidor.find(params[:edit]) 485 | else 486 | @inventory_providor = InventoryProvidor.new 487 | end 488 | 489 | if params[:inventory_providor] 490 | @inventory_providor.update(params[:inventory_providor].permit!) 491 | if @inventory_providor.save 492 | @inventory_providor = InventoryProvidor.new 493 | params[:edit] = false 494 | params[:create] = false 495 | end 496 | end 497 | 498 | else 499 | flash[:error] = l('permission_denied') 500 | end 501 | end 502 | 503 | 504 | @providors = InventoryProvidor.all 505 | end 506 | 507 | def warehouses 508 | @inventory_warehouse = InventoryWarehouse.new 509 | @users = User.where('status=1').order('lastname ASC, firstname ASC').map{|u| [u.lastname+" "+u.firstname, u.id]} 510 | @has_permission = find_current_user.admin? 511 | if params[:delete] or params[:edit] or params[:inventory_warehouse] 512 | if @has_permission 513 | if params[:delete] 514 | ok = InventoryWarehouse.delete(params[:delete]) rescue false 515 | unless ok 516 | flash[:error] = l('cant_delete_register') 517 | end 518 | end 519 | 520 | if params[:edit] 521 | @inventory_warehouse = InventoryWarehouse.find(params[:edit]) 522 | else 523 | @inventory_warehouse = InventoryWarehouse.new 524 | end 525 | 526 | if params[:inventory_warehouse] 527 | @inventory_warehouse.update(params[:inventory_warehouse].permit!) 528 | if @inventory_warehouse.save 529 | @inventory_warehouse = InventoryWarehouse.new 530 | params[:edit] = false 531 | params[:create] = false 532 | end 533 | end 534 | else 535 | flash[:error] = l('permission_denied') 536 | end 537 | end 538 | 539 | @warehouses = InventoryWarehouse.all 540 | end 541 | 542 | 543 | end --------------------------------------------------------------------------------