diff --git a/app/controllers/administration/online_stores_controller.rb b/app/controllers/administration/online_stores_controller.rb
new file mode 100644
index 000000000..edeb5caea
--- /dev/null
+++ b/app/controllers/administration/online_stores_controller.rb
@@ -0,0 +1,52 @@
+class Administration::OnlineStoresController < AdministrationController
+ def index
+ @online_stores = OnlineStore.all
+ end
+
+ def show
+ render :edit
+ end
+
+ def new
+ @online_store = OnlineStore.new
+ end
+
+ def edit; end
+
+ def create
+ @online_store = OnlineStore.new(online_store_params)
+
+ if @online_store.save
+ redirect_to online_stores_url, notice: t('.success')
+ else
+ render :new
+ end
+ end
+
+ def update
+ if @online_store.update(online_store_params)
+ redirect_to online_stores_url, notice: t('.success')
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @online_store.destroy
+
+ redirect_to online_stores_url, notice: t('.success')
+ end
+
+ private
+
+ def find_resource
+ @online_store = OnlineStore.find(params[:id])
+ end
+
+ def online_store_params
+ params.require(:online_store).permit(:name,
+ :online_store_theme_id,
+ { product_ids: [] },
+ :type)
+ end
+end
diff --git a/app/models/company.rb b/app/models/company.rb
index f4a25f0a8..66ac4e10b 100644
--- a/app/models/company.rb
+++ b/app/models/company.rb
@@ -73,6 +73,7 @@ class Company < ActiveRecord::Base
has_many :customer_transports, through: :customers, source: :transports
has_many :mail_servers
has_many :incoming_mails, through: :mail_servers
+ has_many :online_stores
accepts_nested_attributes_for :bank_accounts, :users
diff --git a/app/models/online_store.rb b/app/models/online_store.rb
new file mode 100644
index 000000000..5d499df10
--- /dev/null
+++ b/app/models/online_store.rb
@@ -0,0 +1,22 @@
+class OnlineStore < BaseModel
+ include PupenextSingleTableInheritance
+
+ belongs_to :company
+ belongs_to :theme, foreign_key: :online_store_theme_id, class_name: 'OnlineStoreTheme'
+
+ has_and_belongs_to_many :products, join_table: :online_stores_products
+
+ validates :name, presence: true, length: { in: 1..255 }
+
+ def to_s
+ name
+ end
+
+ def self.child_class_names
+ [
+ OnlineStore::Magento,
+ OnlineStore::PrestaShop,
+ OnlineStore::Pupeshop
+ ].index_by(&:to_s)
+ end
+end
diff --git a/app/models/online_store/magento.rb b/app/models/online_store/magento.rb
new file mode 100644
index 000000000..661a962be
--- /dev/null
+++ b/app/models/online_store/magento.rb
@@ -0,0 +1,5 @@
+class OnlineStore::Magento < OnlineStore
+ def self.model_name
+ OnlineStore.model_name
+ end
+end
diff --git a/app/models/online_store/presta_shop.rb b/app/models/online_store/presta_shop.rb
new file mode 100644
index 000000000..8a597dbe5
--- /dev/null
+++ b/app/models/online_store/presta_shop.rb
@@ -0,0 +1,5 @@
+class OnlineStore::PrestaShop < OnlineStore
+ def self.model_name
+ OnlineStore.model_name
+ end
+end
diff --git a/app/models/online_store/pupeshop.rb b/app/models/online_store/pupeshop.rb
new file mode 100644
index 000000000..444351eac
--- /dev/null
+++ b/app/models/online_store/pupeshop.rb
@@ -0,0 +1,5 @@
+class OnlineStore::Pupeshop < OnlineStore
+ def self.model_name
+ OnlineStore.model_name
+ end
+end
diff --git a/app/models/online_store_theme.rb b/app/models/online_store_theme.rb
new file mode 100644
index 000000000..43ea278c7
--- /dev/null
+++ b/app/models/online_store_theme.rb
@@ -0,0 +1,9 @@
+class OnlineStoreTheme < ActiveRecord::Base
+ has_many :online_stores
+
+ validates :name, presence: true, length: { in: 1..255 }
+
+ def to_s
+ name
+ end
+end
diff --git a/app/models/product.rb b/app/models/product.rb
index e8fd9fc7d..556abf6f4 100644
--- a/app/models/product.rb
+++ b/app/models/product.rb
@@ -22,6 +22,8 @@ class Product < BaseModel
o.has_one :cover_thumbnail, -> { where(kayttotarkoitus: :th).order(:jarjestys, :tunnus) }
end
+ has_and_belongs_to_many :online_stores, join_table: :online_stores_products
+
delegate :images, to: :attachments
delegate :thumbnails, to: :attachments
@@ -131,6 +133,10 @@ def contract_price?(target)
end
end
+ def to_s
+ "#{tuoteno}: #{nimitys}"
+ end
+
private
def defaults
diff --git a/app/views/administration/online_stores/_form.html.erb b/app/views/administration/online_stores/_form.html.erb
new file mode 100644
index 000000000..b2e724b67
--- /dev/null
+++ b/app/views/administration/online_stores/_form.html.erb
@@ -0,0 +1,22 @@
+<%= render 'administration/form_errors', resource: @online_store %>
+
+<%= simple_form_for @online_store,
+ builder: AdminFormBuilder,
+ defaults: { input_html: { class: :large } },
+ alias_set: params[:alias_set] do |f| %>
+
+
+ <%= f.input :name %>
+ <%= f.association :theme %>
+ <%= f.input :type, collection: OnlineStore.child_class_names.keys,
+ label_method: :demodulize,
+ include_blank: false %>
+ <%= f.association :products, collection: Product.active,
+ input_html: { class: %w(large chosen-select) } %>
+
+
+
+
+ <%= f.button :submit %>
+
+<% end %>
diff --git a/app/views/administration/online_stores/edit.html.erb b/app/views/administration/online_stores/edit.html.erb
new file mode 100644
index 000000000..fcc73a443
--- /dev/null
+++ b/app/views/administration/online_stores/edit.html.erb
@@ -0,0 +1,11 @@
+<%= return_link(t('administration.online_stores.index.header'), online_stores_path) %>
+
+<%= render 'administration/header', text: t('administration.online_stores.index.header') %>
+<%= render 'form' %>
+
+
+
+<%= button_to(t('.button_remove'),
+ @online_store,
+ data: { confirm: t('administration.online_stores.edit.confirm_remove') },
+ method: 'delete') %>
diff --git a/app/views/administration/online_stores/index.html.erb b/app/views/administration/online_stores/index.html.erb
new file mode 100644
index 000000000..9cc1bfd47
--- /dev/null
+++ b/app/views/administration/online_stores/index.html.erb
@@ -0,0 +1,27 @@
+<%= render 'administration/header', text: t('.header') %>
+
+
+
+
+
+
+ | <%= OnlineStore.human_attribute_name(:name) %> |
+ <%= OnlineStore.human_attribute_name(:theme) %> |
+ <%= OnlineStore.human_attribute_name(:type) %> |
+
+
+
+
+ <% @online_stores.each do |online_store| %>
+
+ | <%= link_to online_store, online_store %> |
+ <%= online_store.theme %> |
+ <%= online_store.type.demodulize %> |
+
+ <% end %>
+
+
+
+
+
+<%= button_to_new 'online_store', new_online_store_path %>
diff --git a/app/views/administration/online_stores/new.html.erb b/app/views/administration/online_stores/new.html.erb
new file mode 100644
index 000000000..fb3e7ddc0
--- /dev/null
+++ b/app/views/administration/online_stores/new.html.erb
@@ -0,0 +1,4 @@
+<%= return_link(t('administration.online_stores.index.header'), online_stores_path) %>
+
+<%= render 'administration/header', text: t('administration.online_stores.index.header') %>
+<%= render 'form' %>
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index e6620513b..0351515bc 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -222,6 +222,11 @@ fi:
smtp_password: SMPT-palvelimen salasana
smtp_server: SMPT-palvelin
smtp_username: SMPT-palvelimen käyttäjätunnus
+ online_store:
+ name: Nimi
+ products: Tuotteet
+ theme: Teema
+ type: Tyyppi
package:
erikoispakkaus: Erikoispakkaus
jarjestys: Järjestys
@@ -395,6 +400,7 @@ fi:
keyword/customer_subcategory: Asiakasryhmä
keyword/revenue_expenditure: Vaihtoehtoinen meno
mail_server: Sähköpostipalvelin
+ online_store: Verkkokauppa
package: Pakkaus
package_code: Pakkauskoodi
packing_area: Pakkaamo
@@ -570,6 +576,18 @@ fi:
header: Sähköpostipalvelimet
new:
header: Uusi sähköpostipalvelin
+ online_stores:
+ create:
+ success: Verkkokauppa luotiin onnistuneesti
+ destroy:
+ success: Verkkokauppa poistettiin onnistuneesti
+ edit:
+ button_remove: Poista verkkokauppa
+ confirm_remove: Haluatko varmasti poistaa tämän verkkokakupan?
+ index:
+ header: Verkkokaupat
+ update:
+ success: Verkkokauppa päivitettiin onnistuneesti
packages:
create:
create_success: Pakkaus lisättiin onnistuneesti
diff --git a/config/routes.rb b/config/routes.rb
index 5954d4b7c..c59bd7322 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -57,6 +57,7 @@
resources :fiscal_years, except: :destroy
resources :incoming_mails, only: :index
resources :mail_servers
+ resources :online_stores
resources :packages
resources :packing_areas
resources :printers
diff --git a/db/migrate/20151118134711_create_online_store_themes.rb b/db/migrate/20151118134711_create_online_store_themes.rb
new file mode 100644
index 000000000..b50937050
--- /dev/null
+++ b/db/migrate/20151118134711_create_online_store_themes.rb
@@ -0,0 +1,9 @@
+class CreateOnlineStoreThemes < ActiveRecord::Migration
+ def change
+ create_table :online_store_themes do |t|
+ t.string :name
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20151123071517_create_online_stores.rb b/db/migrate/20151123071517_create_online_stores.rb
new file mode 100644
index 000000000..c2af2b9a4
--- /dev/null
+++ b/db/migrate/20151123071517_create_online_stores.rb
@@ -0,0 +1,12 @@
+class CreateOnlineStores < ActiveRecord::Migration
+ def change
+ create_table :online_stores do |t|
+ t.string :name
+ t.string :type
+ t.references :online_store_theme, index: true, foreign_key: true
+ t.references :company, index: true, foreign_key: true
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20151123074934_create_online_stores_products.rb b/db/migrate/20151123074934_create_online_stores_products.rb
new file mode 100644
index 000000000..a52d574f0
--- /dev/null
+++ b/db/migrate/20151123074934_create_online_stores_products.rb
@@ -0,0 +1,8 @@
+class CreateOnlineStoresProducts < ActiveRecord::Migration
+ def change
+ create_table :online_stores_products do |t|
+ t.references :online_store, index: true, foreign_key: true
+ t.references :product, index: true, foreign_key: true
+ end
+ end
+end
diff --git a/db/migrate/20151123084500_add_online_stores_menu.rb b/db/migrate/20151123084500_add_online_stores_menu.rb
new file mode 100644
index 000000000..72880504a
--- /dev/null
+++ b/db/migrate/20151123084500_add_online_stores_menu.rb
@@ -0,0 +1,18 @@
+require File.expand_path('test/permission_helper')
+include PermissionHelper
+
+class AddOnlineStoresMenu < ActiveRecord::Migration
+ def up
+ PermissionHelper.add_item(
+ program: 'Tuotehallinta',
+ name: 'Verkkokaupat',
+ uri: 'pupenext/online_stores'
+ )
+ end
+
+ def down
+ PermissionHelper.remove_all(
+ uri: 'pupenext/online_stores'
+ )
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7ab16e386..37e7de974 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1705,6 +1705,32 @@
add_index "oikeu", ["yhtio", "kuka", "sovellus"], name: "sovellus_index", using: :btree
add_index "oikeu", ["yhtio", "sovellus", "nimi", "alanimi"], name: "menut_index", using: :btree
+ create_table "online_store_themes", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "online_stores", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.string "type", limit: 255
+ t.integer "online_store_theme_id", limit: 4
+ t.integer "company_id", limit: 4
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ add_index "online_stores", ["company_id"], name: "index_online_stores_on_company_id", using: :btree
+ add_index "online_stores", ["online_store_theme_id"], name: "index_online_stores_on_online_store_theme_id", using: :btree
+
+ create_table "online_stores_products", force: :cascade do |t|
+ t.integer "online_store_id", limit: 4
+ t.integer "product_id", limit: 4
+ end
+
+ add_index "online_stores_products", ["online_store_id"], name: "index_online_stores_products_on_online_store_id", using: :btree
+ add_index "online_stores_products", ["product_id"], name: "index_online_stores_products_on_product_id", using: :btree
+
create_table "pakkaamo", primary_key: "tunnus", force: :cascade do |t|
t.string "yhtio", limit: 5, default: "", null: false
t.string "nimi", limit: 150, default: "", null: false
diff --git a/test/controllers/administration/online_stores_controller_test.rb b/test/controllers/administration/online_stores_controller_test.rb
new file mode 100644
index 000000000..984d220e5
--- /dev/null
+++ b/test/controllers/administration/online_stores_controller_test.rb
@@ -0,0 +1,70 @@
+require 'test_helper'
+
+class Administration::OnlineStoresControllerTest < ActionController::TestCase
+ fixtures %w(
+ online_store_themes
+ online_stores
+ )
+
+ setup do
+ login users(:bob)
+
+ @theme_one = online_store_themes(:one)
+ @theme_two = online_store_themes(:two)
+
+ @pupeshop = online_stores(:pupeshop)
+ @magento = online_stores(:magento)
+ end
+
+ test "index works" do
+ get :index
+
+ assert_response :success
+ assert_not_nil assigns(:online_stores)
+ end
+
+ test "new works" do
+ get :new
+
+ assert_response :success
+ end
+
+ test "create works" do
+ assert_difference('OnlineStore.count', 1) do
+ post :create, online_store: { name: 'Test name 1',
+ online_store_theme_id: @theme_one.id,
+ type: 'OnlineStore::Pupeshop'}
+ end
+
+ assert_redirected_to online_stores_url
+ end
+
+ test "show renders edit" do
+ get :show, id: @pupeshop
+
+ assert_response :success
+ assert_template :edit
+ end
+
+ test "edit works" do
+ get :edit, id: @pupeshop
+
+ assert_response :success
+ end
+
+ test "update works" do
+ patch :update, id: @pupeshop, online_store: { name: 'Updated name',
+ online_store_theme_id: @theme_two.id,
+ type: 'OnlineStore::Magento'}
+
+ assert_redirected_to online_stores_url
+ end
+
+ test "destroy works" do
+ assert_difference('OnlineStore.count', -1) do
+ delete :destroy, id: @pupeshop
+ end
+
+ assert_redirected_to online_stores_path
+ end
+end
diff --git a/test/fixtures/online_store_themes.yml b/test/fixtures/online_store_themes.yml
new file mode 100644
index 000000000..62e345966
--- /dev/null
+++ b/test/fixtures/online_store_themes.yml
@@ -0,0 +1,5 @@
+one:
+ name: Theme 1
+
+two:
+ name: Theme 2
diff --git a/test/fixtures/online_stores.yml b/test/fixtures/online_stores.yml
new file mode 100644
index 000000000..f24a8ea2b
--- /dev/null
+++ b/test/fixtures/online_stores.yml
@@ -0,0 +1,13 @@
+pupeshop:
+ company: acme
+ name: Pupeshop
+ type: OnlineStore::Pupeshop
+ theme: one
+ products: hammer, helmet
+
+magento:
+ company: acme
+ name: Magento
+ type: OnlineStore::Magento
+ theme: two
+ products: helmet
diff --git a/test/fixtures/permissions.yml b/test/fixtures/permissions.yml
index e8efd459d..a4afea01c 100644
--- a/test/fixtures/permissions.yml
+++ b/test/fixtures/permissions.yml
@@ -23,6 +23,7 @@
full_installments
incoming_mails
mail_servers
+ online_stores
packages
packing_areas
pending_product_updates
diff --git a/test/models/company_test.rb b/test/models/company_test.rb
index c51f1a5ef..03a46fb69 100644
--- a/test/models/company_test.rb
+++ b/test/models/company_test.rb
@@ -54,6 +54,7 @@ class CompanyTest < ActiveSupport::TestCase
assert @acme.warehouses.count > 0
assert @acme.incoming_mails.count > 0
assert @acme.permissions.count > 0
+ assert @acme.online_stores.count > 0
assert_equal 1, @acme.menus.count
assert_equal menus(:acme_menu_1), @acme.menus.first
diff --git a/test/models/online_store_test.rb b/test/models/online_store_test.rb
new file mode 100644
index 000000000..765aff0a2
--- /dev/null
+++ b/test/models/online_store_test.rb
@@ -0,0 +1,38 @@
+require 'test_helper'
+
+class OnlineStoreTest < ActiveSupport::TestCase
+ fixtures %w(
+ online_store_themes
+ online_stores
+ products
+ )
+
+ setup do
+ @pupeshop = online_stores(:pupeshop)
+ @magento = online_stores(:magento)
+
+ @theme_one = online_store_themes(:one)
+ @theme_two = online_store_themes(:two)
+ end
+
+ test 'fixtures are valid' do
+ assert @pupeshop.valid?, @pupeshop.errors.full_messages
+ assert @magento.valid?, @magento.errors.full_messages
+ end
+
+ test 'associations work' do
+ assert_equal @theme_one, @pupeshop.theme
+ assert_equal @theme_two, @magento.theme
+
+ assert_not_empty @pupeshop.products
+ assert_not_empty @magento.products
+
+ assert_equal companies(:acme), @pupeshop.company
+ assert_equal companies(:acme), @magento.company
+ end
+
+ test 'STI works' do
+ assert_instance_of OnlineStore::Pupeshop, @pupeshop
+ assert_instance_of OnlineStore::Magento, @magento
+ end
+end
diff --git a/test/models/online_store_theme_test.rb b/test/models/online_store_theme_test.rb
new file mode 100644
index 000000000..4735fe72e
--- /dev/null
+++ b/test/models/online_store_theme_test.rb
@@ -0,0 +1,22 @@
+require 'test_helper'
+
+class OnlineStoreThemeTest < ActiveSupport::TestCase
+ fixtures %w(
+ online_store_themes
+ )
+
+ setup do
+ @one = online_store_themes(:one)
+ @two = online_store_themes(:two)
+ end
+
+ test 'fixtures are valid' do
+ assert @one.valid?, @one.errors.full_messages
+ assert @two.valid?, @two.errors.full_messages
+ end
+
+ test 'associations work' do
+ assert_not_empty @one.online_stores
+ assert_not_empty @two.online_stores
+ end
+end
diff --git a/test/models/product_test.rb b/test/models/product_test.rb
index 9631b666a..cb624f863 100644
--- a/test/models/product_test.rb
+++ b/test/models/product_test.rb
@@ -11,6 +11,7 @@ class ProductTest < ActiveSupport::TestCase
keyword/customer_subcategories
keywords
manufacture_order/rows
+ online_stores
pending_updates
product/keywords
product/suppliers
@@ -52,6 +53,201 @@ class ProductTest < ActiveSupport::TestCase
assert_includes @product.product_links, category_links(:product_category_shirts_hammer)
assert_includes @product.product_categories, category_products(:product_category_shirts)
assert @product.warehouses.count > 0
+ assert_not_empty @product.online_stores
+ end
+
+ test 'product stock' do
+ stock = @product.stock
+ assert stock > 0
+
+ loc = @product.shelf_locations.first.dup
+ loc.saldo = 100
+ loc.hyllytaso = 'foo'
+ loc.save!
+
+ assert_equal stock + 100, @product.stock
+
+ @product.update_attribute :ei_saldoa, :no_inventory_management
+ assert_equal 0, @product.stock
+ end
+
+ test 'product reserved stock' do
+ # these rows should affect reserved stock, let's zero them out
+ @product.sales_order_rows.update_all(varattu: 0)
+ @product.manufacture_rows.update_all(varattu: 0)
+ @product.stock_transfer_rows.update_all(varattu: 0)
+ assert_equal 0, @product.stock_reserved
+
+ brand = keywords :brand_tools
+ assert_equal brand.name, @product.brand.name
+
+ @product.sales_order_rows.first.update!(varattu: 10)
+ assert_equal 10, @product.stock_reserved
+
+ @product.manufacture_rows.first.update!(varattu: 5)
+ assert_equal 15, @product.stock_reserved
+
+ @product.stock_transfer_rows.first.update!(varattu: 6)
+ assert_equal 21, @product.stock_reserved
+
+ @product.update_attribute :ei_saldoa, :no_inventory_management
+ assert_equal 0, @product.stock_reserved
+ end
+
+ test 'product stock available' do
+ # these should affect stock available, let's zero them out
+ @product.shelf_locations.update_all(saldo: 0)
+ @product.sales_order_rows.update_all(varattu: 0)
+ @product.manufacture_rows.update_all(varattu: 0)
+ @product.stock_transfer_rows.update_all(varattu: 0)
+ assert_equal 0, @product.stock_available
+
+ @product.sales_order_rows.first.update!(varattu: 10)
+ assert_equal -10, @product.stock_available
+
+ @product.shelf_locations.first.update!(saldo: 100)
+ assert_equal 90, @product.stock_available
+
+ @product.update_attribute :ei_saldoa, :no_inventory_management
+ assert_equal 0, @product.stock_available
+ end
+
+ test 'sales_order_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ one = @product.sales_order_rows.first.dup
+ two = @product.sales_order_rows.first.dup
+
+ # SO rows due to be picked today or in the past, should reserve stock (picked or not picked)
+ one.update! kerayspvm: Date.today, varattu: 10, keratty: ''
+ two.update! kerayspvm: Date.today, varattu: 10, keratty: 'joe'
+ assert_equal 20, @product.stock_reserved
+
+ # SO rows due to be picked in the future, should not affect reserve stock, unless picked
+ one.update! kerayspvm: 1.day.from_now, varattu: 5, keratty: ''
+ two.update! kerayspvm: 1.day.from_now, varattu: 15, keratty: 'joe'
+ assert_equal 15, @product.stock_reserved
+ end
+
+ test 'manufacture_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ one = @product.manufacture_rows.first.dup
+ two = @product.manufacture_rows.first.dup
+
+ # MF rows due to be picked today or in the past, should reserve stock (picked or not picked)
+ one.update! kerayspvm: Date.today, varattu: 10, keratty: ''
+ two.update! kerayspvm: Date.today, varattu: 10, keratty: 'joe'
+ assert_equal 20, @product.stock_reserved
+
+ # MF rows due to be picked in the future, should not affect reserve stock, unless picked
+ one.update! kerayspvm: 1.day.from_now, varattu: 5, keratty: ''
+ two.update! kerayspvm: 1.day.from_now, varattu: 15, keratty: 'joe'
+ assert_equal 15, @product.stock_reserved
+ end
+
+ test 'stock_transfer_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ one = @product.stock_transfer_rows.first.dup
+ two = @product.stock_transfer_rows.first.dup
+
+ # ST rows due to be picked today or in the past, should reserve stock (picked or not picked)
+ one.update! kerayspvm: Date.today, varattu: 10, keratty: ''
+ two.update! kerayspvm: Date.today, varattu: 10, keratty: 'joe'
+ assert_equal 20, @product.stock_reserved
+
+ # ST rows due to be picked in the future, should not affect reserve stock, unless picked
+ one.update! kerayspvm: 1.day.from_now, varattu: 5, keratty: ''
+ two.update! kerayspvm: 1.day.from_now, varattu: 15, keratty: 'joe'
+ assert_equal 15, @product.stock_reserved
+ end
+
+ test 'manufacture_composite_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ # MF composite rows due to be picked today or earlier, should decrease stock reservation
+ @product.manufacture_composite_rows.first.update! kerayspvm: Date.today, varattu: 10, keratty: ''
+ assert_equal -10, @product.stock_reserved
+
+ # MF composite rows due to be picked in the future, should not affect reserve stock
+ @product.manufacture_composite_rows.first.update! kerayspvm: 1.day.from_now, varattu: 10, keratty: ''
+ assert_equal 0, @product.stock_reserved
+ end
+
+ test 'manufacture_recursive_composite_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ # MF recursive composite rows due to be picked today or earlier, should decrease stock reservation
+ @product.manufacture_recursive_composite_rows.first.update! kerayspvm: Date.today, varattu: 10, keratty: ''
+ assert_equal -10, @product.stock_reserved
+
+ # MF recursive composite rows due to be picked in the future, should not reserve stock
+ @product.manufacture_recursive_composite_rows.first.update! kerayspvm: 1.day.from_now, varattu: 10, keratty: ''
+ assert_equal 0, @product.stock_reserved
+ end
+
+ test 'purchase_order_rows product stock reserved by pick date' do
+ # set stock management by pick date
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date
+ assert_equal 0, @product.stock_reserved
+
+ # PO rows due in today or earlier, should decrease stock reservation
+ @product.purchase_order_rows.first.update! toimaika: Date.today, varattu: 10
+ assert_equal -10, @product.stock_reserved
+
+ # PO rows in the future, should not affect reserve stock
+ @product.purchase_order_rows.first.update! toimaika: 1.day.from_now, varattu: 10
+ assert_equal 0, @product.reload.stock_reserved
+ end
+
+ test 'product stock reserved by pick date with future reservations' do
+ # set stock management by pick date with future reservations
+ # this uses same logic as stock_management_by_pick_date, so we don't need to test everything again
+ # this returns the "worst case" stock reserve, se we'll never sell out our stock
+ @product.company.parameter.update! saldo_kasittely: :stock_management_by_pick_date_and_with_future_reservations
+ assert_equal 0, @product.stock_reserved
+
+ one = @product.stock_transfer_rows.first
+ two = @product.stock_transfer_rows.first.dup
+ po_one = @product.purchase_order_rows.first
+
+ # sell 100 day one, we have 100 reserved no matter what date
+ one.update! varattu: 100, kerayspvm: 1.day.from_now
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.ago)
+ assert_equal 100, @product.stock_reserved
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.from_now)
+ assert_equal 100, @product.stock_reserved(stock_date: 2.day.from_now)
+ assert_equal 100, @product.stock_reserved(stock_date: 3.day.from_now)
+ assert_equal 100, @product.stock_reserved(stock_date: 9.day.from_now)
+
+ # purchase 60 day three, we have 100 reserved before purchase, and 40 after
+ po_one.update! varattu: 60, toimaika: 3.day.from_now
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.ago)
+ assert_equal 100, @product.stock_reserved
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.from_now)
+ assert_equal 100, @product.stock_reserved(stock_date: 2.day.from_now)
+ assert_equal 40, @product.stock_reserved(stock_date: 3.day.from_now)
+ assert_equal 40, @product.stock_reserved(stock_date: 9.day.from_now)
+
+ # sell 30 day five, we have 100 reserved before puchase, and 70 after
+ two.update! varattu: 30, kerayspvm: 5.day.from_now
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.ago)
+ assert_equal 100, @product.stock_reserved
+ assert_equal 100, @product.stock_reserved(stock_date: 1.day.from_now)
+ assert_equal 100, @product.stock_reserved(stock_date: 2.day.from_now)
+ assert_equal 70, @product.stock_reserved(stock_date: 3.day.from_now)
+ assert_equal 70, @product.stock_reserved(stock_date: 9.day.from_now)
end
test 'valid status' do