
11 changed files with 126 additions and 50 deletions
@ -1,26 +1,4 @@
@@ -1,26 +1,4 @@
|
||||
Copyright (c) 2013 [name of plugin creator] |
||||
Copyright (c) 2013 s.f.m.c. GmbH |
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, |
||||
are permitted provided that the following conditions are met: |
||||
|
||||
* Redistributions of source code must retain the above copyright notice, |
||||
this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright notice, |
||||
this list of conditions and the following disclaimer in the documentation |
||||
and/or other materials provided with the distribution. |
||||
* Neither the name Spree nor the names of its contributors may be used to |
||||
endorse or promote products derived from this software without specific |
||||
prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
AGPLv3 The code is AGPLv3 |
||||
|
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
module Spree |
||||
class StockMailer < ActionMailer::Base |
||||
helper 'spree/base' |
||||
|
||||
def find_empty_products() |
||||
Spree::Product.where(:count_on_hand => 0).order("name").select {|prod| |
||||
prod.deleted_at.nil? } |
||||
end |
||||
|
||||
def find_empty_variants() |
||||
Spree::Variant.where(:count_on_hand => 0).order("sku").select {|var| |
||||
var.deleted_at.nil? and not var.is_master? |
||||
} |
||||
end |
||||
|
||||
def generate_estimate(var) |
||||
shipped_week = Spree::InventoryUnit.find(:all, :conditions => { |
||||
:state => 'shipped', |
||||
:variant_id => var.id, |
||||
:updated_at => @last_week.midnight..@today.end_of_day, |
||||
}).size() |
||||
shipped_month = Spree::InventoryUnit.find(:all, :conditions => { |
||||
:state => 'shipped', |
||||
:variant_id => var.id, |
||||
:updated_at => @last_month.midnight..@today.end_of_day, |
||||
}).size() |
||||
shipped_year = Spree::InventoryUnit.find(:all, :conditions => { |
||||
:state => 'shipped', |
||||
:variant_id => var.id, |
||||
:updated_at => @last_year.midnight..@today.end_of_day, |
||||
}).size() |
||||
|
||||
# Normalize to units per day... with a shared secret about the range |
||||
norm_week = shipped_week / 7.0 |
||||
norm_month = shipped_month / 30.0 |
||||
norm_year = shipped_year / 365.0 |
||||
|
||||
# A weighted mean with magic numbers pulled out of thing air. |
||||
mean = ((0.6 * norm_week) + (0.25 * norm_month) + (0.15 * norm_year)) / 1.0 |
||||
if var.sku.empty? |
||||
name = "Prd " + var.product.sku |
||||
else |
||||
name = "Sku " + var.sku |
||||
end |
||||
|
||||
{'weighted_mean' => mean, 'variant' => var, 'product' => var.product, 'name' => name} |
||||
end |
||||
|
||||
def generate_forecast() |
||||
@today = Date.today |
||||
@last_week = @today - 7 |
||||
@last_month = @today - 30 |
||||
@last_year = @today - 365 |
||||
|
||||
forecast = [] |
||||
vars = Spree::Variant.where("deleted_at IS NULL").order("sku").select {|var| |
||||
var.product.deleted_at.nil? and var.count_on_hand > 0 } |
||||
|
||||
vars.each {|var| |
||||
forecast.push(generate_estimate(var)) |
||||
} |
||||
|
||||
forecast |
||||
end |
||||
|
||||
def stock_report_email() |
||||
@empty_products = find_empty_products() |
||||
@empty_variants = find_empty_variants() |
||||
@forecast = generate_forecast() |
||||
mail(:to => 'webshop@sysmocom.de', |
||||
:subject => 'Stock report of the week') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
Dear Shop-Owner, |
||||
|
||||
below is the current stock report and estimates on when the products |
||||
will run low. |
||||
|
||||
Estimates: |
||||
<% @forecast.each do |dict| %> |
||||
<%= dict['name'] %> => <%= dict['weighted_mean'].round(2) %> days until sold out. |
||||
<% end %> |
||||
|
||||
|
||||
Empty Products: |
||||
<% @empty_products.each do |product| %> |
||||
<%= product.name %> |
||||
<% end %> |
||||
|
||||
|
||||
Empty Variants: |
||||
<% @empty_variants.each do |variant| %> |
||||
<%= variant.name %> |
||||
<% end %> |
@ -1,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
require 'spree_core' |
||||
require 'spree_sysmocom_stock/engine' |
||||
require 'sysmocom_stock/engine' |
||||
|
||||
|
@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
|
||||
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. |
||||
|
||||
ENGINE_ROOT = File.expand_path('../..', __FILE__) |
||||
ENGINE_PATH = File.expand_path('../../lib/spree_sysmocom_stock/engine', __FILE__) |
||||
ENGINE_PATH = File.expand_path('../../lib/sysmocom_stock/engine', __FILE__) |
||||
|
||||
require 'rails/all' |
||||
require 'rails/engine/commands' |
||||
|
Loading…
Reference in new issue