rawbotz 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -5
- data/Gemfile +2 -0
- data/README.md +60 -6
- data/Rakefile +3 -2
- data/agpl-3.0.txt +661 -0
- data/exe/bcrypt_pw +10 -0
- data/exe/rawbotz_abort_orders +28 -0
- data/exe/rawbotz_process_order_queue +12 -8
- data/exe/rawbotz_stock_update +10 -1
- data/exe/rawbotz_update_local_products +11 -3
- data/exe/rawbotz_update_remote_products +27 -4
- data/lib/rawbotz.rb +13 -5
- data/lib/rawbotz/app.rb +36 -2
- data/lib/rawbotz/cli/order_result_table.rb +13 -0
- data/lib/rawbotz/helpers/format_helper.rb +14 -0
- data/lib/rawbotz/helpers/icon_helper.rb +108 -22
- data/lib/rawbotz/helpers/order_item_color_helper.rb +17 -0
- data/lib/rawbotz/helpers/resource_link_helper.rb +3 -0
- data/lib/rawbotz/mail_template.rb +42 -7
- data/lib/rawbotz/models/sales.rb +21 -0
- data/lib/rawbotz/models/stock.rb +25 -0
- data/lib/rawbotz/models/stock_product.rb +146 -0
- data/lib/rawbotz/models/stock_product_factory.rb +64 -0
- data/lib/rawbotz/processors/order_linker.rb +50 -0
- data/lib/rawbotz/{order_processor.rb → processors/order_processor.rb} +14 -3
- data/lib/rawbotz/processors/organic_product_deliveries_csv.rb +47 -0
- data/lib/rawbotz/{product_updater.rb → processors/product_updater.rb} +71 -6
- data/lib/rawbotz/processors/stock_processor.rb +67 -0
- data/lib/rawbotz/public/rawbotz.css +35 -1
- data/lib/rawbotz/public/rawbotz.js +0 -1
- data/lib/rawbotz/remote_shop.rb +3 -0
- data/lib/rawbotz/routes.rb +3 -0
- data/lib/rawbotz/routes/non_remote_orders.rb +129 -29
- data/lib/rawbotz/routes/orders.rb +55 -4
- data/lib/rawbotz/routes/orders/stock.rb +75 -0
- data/lib/rawbotz/routes/product_links.rb +1 -1
- data/lib/rawbotz/routes/products.rb +19 -22
- data/lib/rawbotz/routes/remote_shop.rb +1 -1
- data/lib/rawbotz/routes/stock.rb +58 -0
- data/lib/rawbotz/routes/suppliers.rb +9 -4
- data/lib/rawbotz/sales_data.rb +13 -0
- data/lib/rawbotz/version.rb +1 -1
- data/lib/rawbotz/views/_hide_unhide_button.haml +1 -1
- data/lib/rawbotz/views/_menu.haml +9 -0
- data/lib/rawbotz/views/index.haml +1 -1
- data/lib/rawbotz/views/layout.haml +0 -1
- data/lib/rawbotz/views/order/_head.haml +64 -19
- data/lib/rawbotz/views/order/_item_table.haml +19 -3
- data/lib/rawbotz/views/order/_order_actions.haml +26 -0
- data/lib/rawbotz/views/order/link_to_remote.haml +30 -0
- data/lib/rawbotz/views/order/non_remote.haml +120 -26
- data/lib/rawbotz/views/order/packlist.haml +62 -5
- data/lib/rawbotz/views/order/packlist.pdf.haml +35 -0
- data/lib/rawbotz/views/order/stock.haml +116 -0
- data/lib/rawbotz/views/order/view.haml +41 -21
- data/lib/rawbotz/views/orders/_order_table.haml +82 -0
- data/lib/rawbotz/views/orders/index.haml +41 -28
- data/lib/rawbotz/views/orders/menu.haml +2 -2
- data/lib/rawbotz/views/orders/non_remotes.haml +14 -3
- data/lib/rawbotz/views/pdf_layout.haml +44 -0
- data/lib/rawbotz/views/product/view.haml +64 -11
- data/lib/rawbotz/views/products/index.haml +1 -1
- data/lib/rawbotz/views/products/table.haml +5 -1
- data/lib/rawbotz/views/remote_cart/index.haml +10 -0
- data/lib/rawbotz/views/remote_order/view.haml +6 -4
- data/lib/rawbotz/views/remote_orders/index.haml +16 -13
- data/lib/rawbotz/views/stock/index.haml +119 -0
- data/lib/rawbotz/views/stock/menu.haml +20 -0
- data/lib/rawbotz/views/stock_product/_value_table_line.haml +22 -0
- data/lib/rawbotz/views/stock_product/value_table.haml +28 -0
- data/lib/rawbotz/views/stockexplorer/stockexplorer.haml +157 -0
- data/lib/rawbotz/views/supplier/orders/table.haml +26 -0
- data/lib/rawbotz/views/supplier/view.haml +56 -10
- data/rawbotz.gemspec +13 -9
- metadata +86 -17
- data/lib/rawbotz/views/order/new.haml +0 -22
@@ -4,6 +4,12 @@
|
|
4
4
|
|
5
5
|
%form.pure-form.pure-form-aligned(action="" method="post")
|
6
6
|
%input(type="hidden" name="_method" value="put")
|
7
|
+
- if @product.hidden?
|
8
|
+
.ui-state-error
|
9
|
+
This product is hidden (in rawbotz).
|
10
|
+
- if !@product.active?
|
11
|
+
.ui-state-error
|
12
|
+
This product is deactivated (in magento).
|
7
13
|
%fieldset
|
8
14
|
.pure-control-group
|
9
15
|
%label(for="product_id") Product ID
|
@@ -21,29 +27,52 @@
|
|
21
27
|
.pure-control-group
|
22
28
|
%label(for="supplier") Supplier
|
23
29
|
= supplier_link @product.supplier
|
24
|
-
.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
- if @product.supplier == settings.supplier || @product.remote_product.present?
|
31
|
+
.pure-control-group
|
32
|
+
%label(for="supplier") Remote Product
|
33
|
+
- if @product.supplier != settings.supplier
|
34
|
+
= error_icon
|
35
|
+
= remote_product_link @product
|
36
|
+
(but not from remote shop!)
|
37
|
+
- elsif @product.remote_product.nil?
|
38
|
+
not linked
|
39
|
+
%a(href="/product/#{@product.id}/link") Linking View
|
40
|
+
- else
|
41
|
+
= remote_product_link @product
|
42
|
+
- if @product.supplier == settings.supplier || @product.remote_product.present?
|
43
|
+
.pure-control-group
|
44
|
+
%label(for="remote_frontend") Remote Frontend Product page
|
45
|
+
%a(href="#{Rawbotz::RemoteShop.product_page_url(@product, settings.conf)}" target="remote_shop")
|
46
|
+
= Rawbotz::RemoteShop.product_page_url(@product, settings.conf)
|
30
47
|
.pure-control-group
|
31
48
|
%label(for="packsize") Pack size
|
32
49
|
= @product.packsize
|
33
50
|
.pure-control-group
|
34
51
|
%label(for="shelve") Shelve
|
35
52
|
= @product.shelve_nr
|
36
|
-
- if @product.supplier_sku
|
53
|
+
- if @product.supplier_sku.present?
|
37
54
|
.pure-control-group
|
38
55
|
%label(for="supplier_sku") Supplier SKU
|
39
56
|
= @product.supplier_sku
|
40
|
-
- if @product.supplier_prod_name
|
57
|
+
- if @product.supplier_prod_name.present?
|
41
58
|
.pure-control-group
|
42
59
|
%label(for="supplier_prod_name") Supplier Product Name
|
43
60
|
= @product.supplier_prod_name
|
61
|
+
- if @product.order_info.present?
|
62
|
+
.pure-control-group
|
63
|
+
%label(for="order_info") Order Info
|
64
|
+
= @product.order_info
|
65
|
+
- if @product.purchase_price.present?
|
66
|
+
.pure-control-group
|
67
|
+
%label(for="purchase_price") Purchase Price
|
68
|
+
= @product.purchase_price
|
69
|
+
.pure-control-group
|
70
|
+
%label(for="organic") Organic
|
71
|
+
= bool_icon @product.organic?
|
72
|
+
|
44
73
|
|
45
74
|
%h2 Actions
|
46
|
-
- if !@product.remote_product.present?
|
75
|
+
- if @product.supplier == settings.supplier && !@product.remote_product.present?
|
47
76
|
= haml "widgets/_link".to_sym, locals: {local_product: @product, redirect_to: 'product'}
|
48
77
|
%br
|
49
78
|
= haml :"_hide_unhide_button", locals: {product: @product}
|
@@ -81,5 +110,29 @@
|
|
81
110
|
|
82
111
|
.pure-u-3-5
|
83
112
|
= haml :"product/_stock_sales_plot", locals: {plot_data: @plot_data, markers: @sales_monthly}
|
84
|
-
|
85
|
-
|
113
|
+
|
114
|
+
%br
|
115
|
+
|
116
|
+
- if @stock_product.present?
|
117
|
+
%h2 Stock and Sales data statistics
|
118
|
+
= haml "stock_product/value_table".to_sym, locals: {stock_product: @stock_product}
|
119
|
+
%br
|
120
|
+
|
121
|
+
- if @product.order_items.present?
|
122
|
+
%h2 Orders
|
123
|
+
%table.pure-table
|
124
|
+
%thead
|
125
|
+
%tr
|
126
|
+
%th Order
|
127
|
+
%th Date
|
128
|
+
%th Num ordered
|
129
|
+
%th Num stocked
|
130
|
+
%tbody
|
131
|
+
- @product.order_items.where("num_ordered > 0").order(updated_at: :desc).find_each do |order_item|
|
132
|
+
%tr
|
133
|
+
%td= order_link order_item.order
|
134
|
+
%td= order_item.order.updated_at
|
135
|
+
%td= order_item.num_ordered
|
136
|
+
%td= order_item.num_stocked
|
137
|
+
|
138
|
+
|
@@ -1,13 +1,17 @@
|
|
1
|
-
%table.pure-table
|
1
|
+
%table.pure-table.pure-table-striped
|
2
2
|
%thead
|
3
3
|
%tr
|
4
4
|
%th
|
5
|
+
= product_icon
|
5
6
|
Product name
|
6
7
|
%th
|
7
8
|
Product ID
|
8
9
|
%th
|
10
|
+
= supplier_icon
|
9
11
|
Supplier
|
10
12
|
%th
|
13
|
+
= stock_icon
|
14
|
+
= sales_icon
|
11
15
|
Stock/ Sales
|
12
16
|
%tbody
|
13
17
|
- products.each do |product|
|
@@ -8,12 +8,16 @@
|
|
8
8
|
%thead
|
9
9
|
%tr
|
10
10
|
%th
|
11
|
+
= product_icon
|
11
12
|
Product
|
12
13
|
%th
|
13
14
|
Local Name
|
14
15
|
%th
|
15
16
|
Quantity
|
16
17
|
%tbody
|
18
|
+
- if @cart_products.empty?
|
19
|
+
%tr
|
20
|
+
%td(colspan=3) Remote Cart empty
|
17
21
|
- @cart_products.each do |product, qty|
|
18
22
|
- if product.nil?
|
19
23
|
%tr
|
@@ -28,3 +32,9 @@
|
|
28
32
|
= local_product_link(product.local_product) rescue "unlinked"
|
29
33
|
%td
|
30
34
|
= qty
|
35
|
+
%br
|
36
|
+
%br
|
37
|
+
%a(href="#{Rawbotz::RemoteShop::cart_page_url settings.conf}" target="remote_shop")
|
38
|
+
= view_icon
|
39
|
+
= external_link_icon
|
40
|
+
View Remote Cart
|
@@ -13,9 +13,11 @@ ID #{@remote_order_id}
|
|
13
13
|
%th Local Name
|
14
14
|
%th Qty
|
15
15
|
%th Refund
|
16
|
-
%th
|
16
|
+
%th
|
17
|
+
= shelve_icon
|
18
|
+
Shelve
|
17
19
|
%tbody
|
18
|
-
- @remote_products_qty.sort_by{|p,_| p.name}.each do |p,
|
20
|
+
- @remote_products_qty.sort_by{|p,_| p.name}.each do |p, qty, refunded|
|
19
21
|
%tr
|
20
22
|
%td
|
21
23
|
= remote_product_link p
|
@@ -28,8 +30,8 @@ ID #{@remote_order_id}
|
|
28
30
|
Link (broken)
|
29
31
|
-else
|
30
32
|
not in database
|
31
|
-
%td=
|
32
|
-
%td
|
33
|
+
%td=qty
|
34
|
+
%td=refunded
|
33
35
|
%td
|
34
36
|
-if shelve = p.local_product.try(:shelve_nr)
|
35
37
|
= shelve
|
@@ -4,17 +4,20 @@
|
|
4
4
|
|
5
5
|
%center
|
6
6
|
%table.pure-table.pure-table-striped
|
7
|
-
%
|
8
|
-
%th Date
|
9
|
-
%th Volume
|
10
|
-
%th Action
|
11
|
-
- @last_orders.each do |p|
|
7
|
+
%thead
|
12
8
|
%tr
|
13
|
-
%
|
14
|
-
%
|
15
|
-
%
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
%
|
20
|
-
|
9
|
+
%th Date
|
10
|
+
%th Volume
|
11
|
+
%th Action
|
12
|
+
%tbody
|
13
|
+
- @last_orders.each do |p|
|
14
|
+
%tr
|
15
|
+
%td=p[0]
|
16
|
+
%td=p[1]
|
17
|
+
%td
|
18
|
+
%a(href="#{p[2]}")
|
19
|
+
Visit remote shop
|
20
|
+
%br
|
21
|
+
%a(href="/remote_order/#{p[2][/\d+/]}")
|
22
|
+
= view_icon
|
23
|
+
View
|
@@ -0,0 +1,119 @@
|
|
1
|
+
= haml "stock/menu".to_sym
|
2
|
+
|
3
|
+
:javascript
|
4
|
+
$( function() {
|
5
|
+
$( document ).tooltip();
|
6
|
+
});
|
7
|
+
|
8
|
+
#modal-plot.modal-plot
|
9
|
+
|
10
|
+
- if !@wrongly_out_of_stocks.nil? && !@wrongly_out_of_stocks.empty?
|
11
|
+
%h2 Products with stock which are set 'not in stock'
|
12
|
+
= haml "products/table".to_sym, locals: {products: @wrongly_out_of_stocks}
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
%h2 Stock/Sales Statistics
|
17
|
+
|
18
|
+
Where stock does not last for 10 days.
|
19
|
+
|
20
|
+
- @suppliers.find_each do |supplier|
|
21
|
+
%h3
|
22
|
+
= supplier_icon
|
23
|
+
= supplier.name
|
24
|
+
|
25
|
+
- if supplier.minimum_order_value.present?
|
26
|
+
%small
|
27
|
+
Minimum Order Value: #{supplier.minimum_order_value}
|
28
|
+
%br
|
29
|
+
- if supplier.delivery_time_days.present?
|
30
|
+
%small
|
31
|
+
Deliver time in days: #{supplier.delivery_time_days}
|
32
|
+
%br
|
33
|
+
|
34
|
+
%table.pure-table.pure-table-striped
|
35
|
+
- predicted_stock_title = "Predicted stock duration (days)"
|
36
|
+
- avg_30_title = "Average sales per 30 days, based on sales in last 30 days, corrected by days where product was out of stock"
|
37
|
+
- avg_60_title = "Average sales per 30 days, based on sales in last 60 days, corrected by days where product was out of stock"
|
38
|
+
- avg_90_title = "Average sales per 30 days, based on sales in last 90 days, corrected by days where product was out of stock"
|
39
|
+
- avg_356_title = "Average sales per 30 days, based on sales in last 365 days, corrected by days where product was out of stock"
|
40
|
+
%thead
|
41
|
+
%tr
|
42
|
+
%th Name
|
43
|
+
%th(title="Current Stock")
|
44
|
+
= stock_icon
|
45
|
+
%th(title="#{predicted_stock_title} based on last 30 days sales")
|
46
|
+
= stock_empty_icon
|
47
|
+
%th(title=avg_30_title)
|
48
|
+
= sales_icon
|
49
|
+
30
|
50
|
+
%th(title=avg_60_title)
|
51
|
+
= sales_icon
|
52
|
+
60
|
53
|
+
%th(title=avg_90_title)
|
54
|
+
= sales_icon
|
55
|
+
90
|
56
|
+
%th(title=avg_90_title)
|
57
|
+
= sales_icon
|
58
|
+
356
|
59
|
+
%th
|
60
|
+
Notes
|
61
|
+
%tbody
|
62
|
+
- suppliers_stock_product = @stock_products.select{|s|s.product.supplier == supplier}
|
63
|
+
- if suppliers_stock_product.empty?
|
64
|
+
%tr
|
65
|
+
%td(colspan=6)
|
66
|
+
Nothing to see here
|
67
|
+
- suppliers_stock_product.sort_by{|s| s.expected_stock_lifetime}.each do |p|
|
68
|
+
%tr
|
69
|
+
- stock_warn = info_icon tooltip_text: "Not enough stock data (first stock record #{p.days_since_first_stock_date.to_i} days ago)!"
|
70
|
+
- sales_warn = info_icon tooltip_text: "Not enough sales data (first sale #{p.num_days_first_sale.to_i} days ago)!"
|
71
|
+
%td
|
72
|
+
= product_link p.product
|
73
|
+
%td(title="Current Stock")
|
74
|
+
= p.current_stock
|
75
|
+
%td(title="#{predicted_stock_title} based on last #{p.sales_per_day_base} days sales")
|
76
|
+
= friendly_float p.expected_stock_lifetime
|
77
|
+
%td(title=avg_30_title)
|
78
|
+
- title = "Average corrected sales, based on last %d days. Sales: %s - Days out of stock: %d" % [30, p.real_sales(30), p.out_of_stock_days(30)]
|
79
|
+
%div(title="#{title}")
|
80
|
+
= friendly_float(p.corrected_sales(30)) || "?"
|
81
|
+
- if p.days_since_first_stock_date < 30
|
82
|
+
= stock_warn
|
83
|
+
- if p.num_days_first_sale < 30
|
84
|
+
= sales_warn
|
85
|
+
%td
|
86
|
+
%div(title="Average corrected sales, based on last 60 days. Sales: #{p.sales_last_60} - Days out of stock: #{p.out_of_stock_days 60}")
|
87
|
+
= friendly_float(p.corrected_sales(60, per_days: 30))
|
88
|
+
-#= "%.2f" % (p.corrected_sales_last_60 / 2.0)
|
89
|
+
- if p.days_since_first_stock_date < 60
|
90
|
+
= stock_warn
|
91
|
+
- if p.num_days_first_sale < 60
|
92
|
+
= sales_warn
|
93
|
+
%td
|
94
|
+
%div(title="Average corrected sales, based on last 90 days. Sales: #{p.sales_last_90} - Days out of stock: #{p.out_of_stock_days 90}")
|
95
|
+
= friendly_float(p.corrected_sales(90, per_days: 30))
|
96
|
+
-#= "%.2f" % (p.corrected_sales_last_90 / 3.0)
|
97
|
+
- if p.days_since_first_stock_date < 90
|
98
|
+
= stock_warn
|
99
|
+
- if p.num_days_first_sale < 90
|
100
|
+
= sales_warn
|
101
|
+
%td
|
102
|
+
%div(title="Average corrected sales, based on last 365 days. Sales: #{p.sales_last_365} - Days out of stock: #{p.out_of_stock_days 365}")
|
103
|
+
= friendly_float(p.corrected_sales(365, per_days: 30))
|
104
|
+
-#= "%.2f" % (p.corrected_sales_last_365 / 365.0 * 30.0)
|
105
|
+
- if p.days_since_first_stock_date < 365
|
106
|
+
= stock_warn
|
107
|
+
- if p.num_days_first_sale < 365
|
108
|
+
= sales_warn
|
109
|
+
%td
|
110
|
+
- if p.num_days_first_sale.to_i < 10
|
111
|
+
New Product! (First sale #{p.num_days_first_sale.to_i} days ago)
|
112
|
+
- if p.days_since_first_stock_date.to_i < 30
|
113
|
+
Not enough stock data (first stock recorded by rawbotz is #{p.days_since_first_stock_date.to_i} days ago)
|
114
|
+
|
115
|
+
%br
|
116
|
+
%center
|
117
|
+
%a.pure-button.pure-button-primary(href="/order/non_remote/#{supplier.id}/new")
|
118
|
+
Make an order for #{supplier.name}
|
119
|
+
%hr
|
@@ -0,0 +1,20 @@
|
|
1
|
+
.icon#headicon
|
2
|
+
= stock_icon
|
3
|
+
|
4
|
+
.pure-u-1
|
5
|
+
.pure-menu.pure-menu-horizontal#submenu
|
6
|
+
%small
|
7
|
+
%center
|
8
|
+
%ul.pure-menu-list
|
9
|
+
%li.pure-menu-item
|
10
|
+
%a.pure-menu-link(href="/stock")
|
11
|
+
= index_icon
|
12
|
+
Stock and Sales
|
13
|
+
%li.pure-menu-item
|
14
|
+
%a.pure-menu-link(href="/stock/alerts")
|
15
|
+
= error_icon
|
16
|
+
Alerts
|
17
|
+
%li.pure-menu-item
|
18
|
+
%a.pure-menu-link(href="/stock/warnings")
|
19
|
+
= warning_icon
|
20
|
+
Warnings
|
@@ -0,0 +1,22 @@
|
|
1
|
+
- if stock_product.days_since_first_stock_date.to_i < num_days
|
2
|
+
- warn = warning_icon(tooltip_text: "First stock value is only #{stock_product.days_since_first_stock_date.to_i} days ago!")
|
3
|
+
- else
|
4
|
+
- warn = ""
|
5
|
+
|
6
|
+
%tr
|
7
|
+
%td
|
8
|
+
= num_days
|
9
|
+
%td
|
10
|
+
- if stock_product.num_days_first_sale.to_i < num_days
|
11
|
+
= warning_icon tooltip_text: "First sale only #{stock_product.num_days_first_sale.to_i} days ago!"
|
12
|
+
= stock_product.real_sales num_days
|
13
|
+
%td
|
14
|
+
= warn
|
15
|
+
= stock_product.out_of_stock_days num_days
|
16
|
+
%td
|
17
|
+
= warn
|
18
|
+
= friendly_float stock_product.corrected_sales num_days
|
19
|
+
%td
|
20
|
+
= warn
|
21
|
+
= friendly_float stock_product.corrected_sales(num_days, per_days: 1)
|
22
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
%table.pure-table.pure-table-striped
|
2
|
+
%thead
|
3
|
+
%tr
|
4
|
+
%th Number of days
|
5
|
+
%th Sales
|
6
|
+
%th Days out of stock
|
7
|
+
%th Corrected sales
|
8
|
+
%th Corrected sales per day
|
9
|
+
%tbody
|
10
|
+
- [30, 60, 90, 365].each do |num_days|
|
11
|
+
= haml "stock_product/_value_table_line".to_sym,
|
12
|
+
locals: {stock_product: stock_product, num_days: num_days}
|
13
|
+
%dl
|
14
|
+
%dt
|
15
|
+
Number of days since first sale
|
16
|
+
%dd
|
17
|
+
= stock_product.num_days_first_sale.to_i
|
18
|
+
%dt
|
19
|
+
Number of days since first stock entry
|
20
|
+
%dd
|
21
|
+
= stock_product.days_since_first_stock_date.to_i
|
22
|
+
|
23
|
+
-#%h3 Sales Prediction
|
24
|
+
-#.pure-u-1-1
|
25
|
+
-# %h2 Stock
|
26
|
+
-# %h4 First registered stock
|
27
|
+
-# -#= stock_product.days_since_first_stock
|
28
|
+
|
@@ -0,0 +1,157 @@
|
|
1
|
+
:javascript
|
2
|
+
$( function() {
|
3
|
+
$( document ).tooltip();
|
4
|
+
});
|
5
|
+
|
6
|
+
%h1 Stockexplorer supplier #{@supplier.name}
|
7
|
+
|
8
|
+
#supplier_info
|
9
|
+
- if @supplier.order_info.present?
|
10
|
+
#flash.ui-state-highlight
|
11
|
+
= info_icon
|
12
|
+
%b Delivery info:
|
13
|
+
= @supplier.order_info
|
14
|
+
|
15
|
+
- if @supplier.delivery_time_days
|
16
|
+
#flash.ui-state-highlight
|
17
|
+
= info_icon
|
18
|
+
%b Delivery Time (in days):
|
19
|
+
= @supplier.delivery_time_days
|
20
|
+
|
21
|
+
- if @supplier.minimum_order_value
|
22
|
+
#flash.ui-state-highlight
|
23
|
+
= info_icon
|
24
|
+
%b Minimum Order Value
|
25
|
+
= @supplier.minimum_order_value
|
26
|
+
€
|
27
|
+
|
28
|
+
- if @supplier.order_info.present? || @supplier.delivery_time_days || @supplier.minimum_order_value
|
29
|
+
%br
|
30
|
+
|
31
|
+
%center
|
32
|
+
%table.pure-table.pure-table-striped
|
33
|
+
%thead
|
34
|
+
%tr
|
35
|
+
%th
|
36
|
+
%th
|
37
|
+
%th
|
38
|
+
%th.left-bordered(colspan=2)
|
39
|
+
%center
|
40
|
+
30 days
|
41
|
+
%th.left-bordered(colspan=3)
|
42
|
+
%center
|
43
|
+
60 days
|
44
|
+
%th.left-bordered(colspan=3)
|
45
|
+
%center
|
46
|
+
90 days
|
47
|
+
%th.left-bordered(colspan=3)
|
48
|
+
%center
|
49
|
+
356 days
|
50
|
+
%th.left-bordered
|
51
|
+
%th
|
52
|
+
%th
|
53
|
+
%tr
|
54
|
+
%th Name
|
55
|
+
%th(title="Packsize")
|
56
|
+
= packsize_icon
|
57
|
+
%th(title="Current Stock")
|
58
|
+
= stock_icon
|
59
|
+
%th.left-bordered(title="Sales in the last 30 days")
|
60
|
+
= sales_icon
|
61
|
+
%th(title="Days where Product was unavailable (out of stock) in the last 30 days")
|
62
|
+
%span.fa-stack
|
63
|
+
%i.fa.fa-shopping-cart.fa-stack-1x
|
64
|
+
%i.fa.fa-ban.fa-stack-2x.text-danger
|
65
|
+
%th.left-bordered(title="Sales in the last 60 days")
|
66
|
+
= sales_icon
|
67
|
+
%th(title="Days where Product was unavailable (out of stock) in the last 60 days")
|
68
|
+
%span.fa-stack
|
69
|
+
%i.fa.fa-shopping-cart.fa-stack-1x
|
70
|
+
%i.fa.fa-ban.fa-stack-2x.text-danger
|
71
|
+
%th(title="The 30 days average sales looking at 60 days. The second value equals takes in account the days in which the product was not available.)")
|
72
|
+
average / cleaned
|
73
|
+
%th.left-bordered(title="Sales in the last 90 days")
|
74
|
+
= sales_icon
|
75
|
+
%th(title="Days where Product was unavailable (out of stock) in the last 90 days")
|
76
|
+
%span.fa-stack
|
77
|
+
%i.fa.fa-shopping-cart.fa-stack-1x
|
78
|
+
%i.fa.fa-ban.fa-stack-2x.text-danger
|
79
|
+
%th(title="The 30 days average sales looking at 90 days. The second value equals takes in account the days in which the product was not available.)")
|
80
|
+
average / cleaned
|
81
|
+
%th.left-bordered(title="Sales in the last 356 days")
|
82
|
+
= sales_icon
|
83
|
+
%th(title="Days where Product was unavailable (out of stock) in the last 356 days")
|
84
|
+
%span.fa-stack
|
85
|
+
%i.fa.fa-shopping-cart.fa-stack-1x
|
86
|
+
%i.fa.fa-ban.fa-stack-2x.text-danger
|
87
|
+
%th(title="The 356 days average sales looking at 90 days. The second value equals takes in account the days in which the product was not available.)")
|
88
|
+
average / cleaned
|
89
|
+
%th.left-bordered Missing
|
90
|
+
%th Purchase Price
|
91
|
+
%th Chart
|
92
|
+
%tbody
|
93
|
+
- @supplier.local_products.find_each do |product|
|
94
|
+
%tr
|
95
|
+
%td(title="Product Name")
|
96
|
+
= product_link product
|
97
|
+
- if product.supplier_prod_name
|
98
|
+
%br
|
99
|
+
%small
|
100
|
+
= product.supplier_sku
|
101
|
+
= product.supplier_prod_name
|
102
|
+
- if product.order_info.present?
|
103
|
+
#flash.ui-state-highlight
|
104
|
+
= error_icon
|
105
|
+
= product.order_info
|
106
|
+
%td(title="Product Packsize")
|
107
|
+
- if product.packsize.present?
|
108
|
+
= product.packsize
|
109
|
+
- else
|
110
|
+
%i.fa.fa-ban
|
111
|
+
%td(title="Product Stock (current)")
|
112
|
+
= @stock[product.product_id]
|
113
|
+
%td.left-bordered(title="Sales in last 30 days")
|
114
|
+
= @sales_30[product.product_id].qty
|
115
|
+
%td(title="Number of days where product was out of stock in the last 30 days")
|
116
|
+
= @out_of_stock_days_30[product.product_id]
|
117
|
+
- if product.stock_items.present? && (Date.today - product.stock_items.order(created_at: :asc).first.created_at.to_date).to_i < 30
|
118
|
+
.stock_warning(title="Warning, first stock for this product is from #{product.stock_items.order(created_at: :asc).first.created_at}")
|
119
|
+
= error_icon
|
120
|
+
%td.left-bordered(title="Sales in the last 60 days")
|
121
|
+
= @sales_60[product.product_id].qty
|
122
|
+
%td
|
123
|
+
= @out_of_stock_days_60[product.product_id]
|
124
|
+
%td
|
125
|
+
= (@sales_60[product.product_id].qty.to_f / 2.0).to_i
|
126
|
+
\/
|
127
|
+
= (@sales_60[product.product_id].qty.to_f / (60 - @out_of_stock_days_60[product.product_id]) * 30).to_i
|
128
|
+
%td.left-bordered(title="Sales in the last 90 days")
|
129
|
+
= @sales_90[product.product_id].qty
|
130
|
+
%td
|
131
|
+
= @out_of_stock_days_90[product.product_id]
|
132
|
+
%td
|
133
|
+
= (@sales_90[product.product_id].qty.to_f / 3.0).to_i
|
134
|
+
\/
|
135
|
+
= (@sales_90[product.product_id].qty.to_f / (90 - @out_of_stock_days_90[product.product_id]) * 30).to_i
|
136
|
+
%td.left-bordered
|
137
|
+
= @sales_356[product.product_id].qty
|
138
|
+
%td
|
139
|
+
= @out_of_stock_days_356[product.product_id]
|
140
|
+
%td
|
141
|
+
= (@sales_356[product.product_id].qty.to_f / 11.86).to_i
|
142
|
+
\/
|
143
|
+
= (@sales_356[product.product_id].qty.to_f / (356 - @out_of_stock_days_356[product.product_id]) * 30).to_i
|
144
|
+
-#%td
|
145
|
+
-# - if product_stock && product_monthly_sales
|
146
|
+
-# -# red/yellow/green
|
147
|
+
-# -# if negative, put button
|
148
|
+
-# = product_stock - product_monthly_sales
|
149
|
+
%td.left-bordered
|
150
|
+
%td
|
151
|
+
- if product.purchase_price
|
152
|
+
= product.purchase_price
|
153
|
+
€
|
154
|
+
%td
|
155
|
+
- stock_url = "/product/#{product.id}/stock_sales_plot"
|
156
|
+
%a.stock_show_action{:href => "", :data => {url: stock_url, product: product.name}}
|
157
|
+
Stock/Sales
|