bbmb 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/LICENSE.txt +339 -0
- data/Manifest.txt +91 -0
- data/README.txt +25 -0
- data/Rakefile +28 -0
- data/bin/admin +71 -0
- data/bin/bbmbd +61 -0
- data/lib/bbmb.rb +9 -0
- data/lib/bbmb/config.rb +106 -0
- data/lib/bbmb/html/state/change_password.rb +50 -0
- data/lib/bbmb/html/state/current_order.rb +81 -0
- data/lib/bbmb/html/state/customer.rb +109 -0
- data/lib/bbmb/html/state/customers.rb +52 -0
- data/lib/bbmb/html/state/favorites.rb +19 -0
- data/lib/bbmb/html/state/favorites_result.rb +21 -0
- data/lib/bbmb/html/state/global.rb +62 -0
- data/lib/bbmb/html/state/history.rb +95 -0
- data/lib/bbmb/html/state/info.rb +23 -0
- data/lib/bbmb/html/state/json.rb +16 -0
- data/lib/bbmb/html/state/login.rb +76 -0
- data/lib/bbmb/html/state/order.rb +21 -0
- data/lib/bbmb/html/state/orders.rb +19 -0
- data/lib/bbmb/html/state/result.rb +64 -0
- data/lib/bbmb/html/state/show_pass.rb +16 -0
- data/lib/bbmb/html/state/viral/admin.rb +41 -0
- data/lib/bbmb/html/state/viral/customer.rb +143 -0
- data/lib/bbmb/html/util/known_user.rb +51 -0
- data/lib/bbmb/html/util/multilingual.rb +18 -0
- data/lib/bbmb/html/util/session.rb +52 -0
- data/lib/bbmb/html/util/validator.rb +55 -0
- data/lib/bbmb/html/view/backorder.rb +24 -0
- data/lib/bbmb/html/view/change_password.rb +42 -0
- data/lib/bbmb/html/view/copyleft.rb +41 -0
- data/lib/bbmb/html/view/current_order.rb +482 -0
- data/lib/bbmb/html/view/customer.rb +152 -0
- data/lib/bbmb/html/view/customers.rb +145 -0
- data/lib/bbmb/html/view/favorites.rb +162 -0
- data/lib/bbmb/html/view/favorites_result.rb +26 -0
- data/lib/bbmb/html/view/foot.rb +21 -0
- data/lib/bbmb/html/view/head.rb +32 -0
- data/lib/bbmb/html/view/history.rb +60 -0
- data/lib/bbmb/html/view/info.rb +33 -0
- data/lib/bbmb/html/view/json.rb +20 -0
- data/lib/bbmb/html/view/list_prices.rb +51 -0
- data/lib/bbmb/html/view/login.rb +46 -0
- data/lib/bbmb/html/view/multilingual.rb +17 -0
- data/lib/bbmb/html/view/navigation.rb +30 -0
- data/lib/bbmb/html/view/order.rb +123 -0
- data/lib/bbmb/html/view/orders.rb +50 -0
- data/lib/bbmb/html/view/result.rb +107 -0
- data/lib/bbmb/html/view/search.rb +28 -0
- data/lib/bbmb/html/view/show_pass.rb +60 -0
- data/lib/bbmb/html/view/template.rb +61 -0
- data/lib/bbmb/model/customer.rb +96 -0
- data/lib/bbmb/model/order.rb +255 -0
- data/lib/bbmb/model/product.rb +99 -0
- data/lib/bbmb/model/promotion.rb +52 -0
- data/lib/bbmb/model/quota.rb +27 -0
- data/lib/bbmb/model/subject.rb +46 -0
- data/lib/bbmb/persistence/none.rb +6 -0
- data/lib/bbmb/persistence/odba.rb +42 -0
- data/lib/bbmb/persistence/odba/model/customer.rb +47 -0
- data/lib/bbmb/persistence/odba/model/order.rb +50 -0
- data/lib/bbmb/persistence/odba/model/product.rb +26 -0
- data/lib/bbmb/persistence/odba/model/quota.rb +12 -0
- data/lib/bbmb/util/invoicer.rb +126 -0
- data/lib/bbmb/util/mail.rb +140 -0
- data/lib/bbmb/util/multilingual.rb +57 -0
- data/lib/bbmb/util/numbers.rb +67 -0
- data/lib/bbmb/util/password_generator.rb +44 -0
- data/lib/bbmb/util/polling_manager.rb +135 -0
- data/lib/bbmb/util/server.rb +159 -0
- data/lib/bbmb/util/target_dir.rb +36 -0
- data/lib/bbmb/util/transfer_dat.rb +34 -0
- data/lib/bbmb/util/updater.rb +27 -0
- data/test/data/ydim.yml +2 -0
- data/test/model/test_customer.rb +75 -0
- data/test/model/test_order.rb +426 -0
- data/test/model/test_product.rb +238 -0
- data/test/model/test_promotion.rb +40 -0
- data/test/stub/persistence.rb +57 -0
- data/test/suite.rb +12 -0
- data/test/test_bbmb.rb +18 -0
- data/test/util/test_invoicer.rb +189 -0
- data/test/util/test_mail.rb +359 -0
- data/test/util/test_money.rb +71 -0
- data/test/util/test_password_generator.rb +27 -0
- data/test/util/test_polling_manager.rb +312 -0
- data/test/util/test_server.rb +189 -0
- data/test/util/test_target_dir.rb +82 -0
- data/test/util/test_transfer_dat.rb +45 -0
- metadata +190 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Html::View::Orders -- bbmb.ch -- 27.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'bbmb/html/view/template'
|
5
|
+
|
6
|
+
module BBMB
|
7
|
+
module Html
|
8
|
+
module View
|
9
|
+
class OrdersComposite < HtmlGrid::List
|
10
|
+
BACKGROUND_ROW = 'bg'
|
11
|
+
BACKGROUND_SUFFIX = ''
|
12
|
+
COMPONENTS = {
|
13
|
+
#[0,0] => :order_id,
|
14
|
+
[0,0] => :commit_time,
|
15
|
+
[1,0] => :size,
|
16
|
+
[2,0] => :item_count,
|
17
|
+
[3,0] => :total,
|
18
|
+
}
|
19
|
+
CSS_CLASS = 'list'
|
20
|
+
CSS_HEAD_MAP = {
|
21
|
+
[1,0] => 'right',
|
22
|
+
[2,0] => 'right',
|
23
|
+
[3,0] => 'right',
|
24
|
+
}
|
25
|
+
CSS_MAP = {
|
26
|
+
[1,0,2] => 'right',
|
27
|
+
[3,0] => 'total',
|
28
|
+
}
|
29
|
+
LOOKANDFEEL_MAP = {
|
30
|
+
:total => :order_total,
|
31
|
+
}
|
32
|
+
SORT_DEFAULT = :commit_time
|
33
|
+
SORT_REVERSE = true
|
34
|
+
def commit_time(model)
|
35
|
+
link = HtmlGrid::Link.new(:commit_time, model, @session, self)
|
36
|
+
link.value = model.commit_time.strftime("%d.%m.%Y %H:%M")
|
37
|
+
link.href = @lookandfeel._event_url(:order, :order_id => model.order_id)
|
38
|
+
link.css_class = 'commit-time'
|
39
|
+
link
|
40
|
+
end
|
41
|
+
def items(model)
|
42
|
+
model.positions.inject(0) { |memo, pos| memo + pos.quantity }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
class Orders < Template
|
46
|
+
CONTENT = OrdersComposite
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Html::View::Result -- bbmb.ch -- 21.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'bbmb/html/view/backorder'
|
5
|
+
require 'bbmb/html/view/list_prices'
|
6
|
+
require 'bbmb/html/view/multilingual'
|
7
|
+
require 'bbmb/html/view/search'
|
8
|
+
require 'bbmb/html/view/template'
|
9
|
+
require 'htmlgrid/errormessage'
|
10
|
+
require 'htmlgrid/formlist'
|
11
|
+
|
12
|
+
module BBMB
|
13
|
+
module Html
|
14
|
+
module View
|
15
|
+
class Products < HtmlGrid::FormList
|
16
|
+
include Backorder
|
17
|
+
include ListPrices
|
18
|
+
include Multilingual
|
19
|
+
include HtmlGrid::ErrorMessage
|
20
|
+
CSS_CLASS = 'list'
|
21
|
+
if BBMB.config.enable_price_levels
|
22
|
+
COMPONENTS = {
|
23
|
+
[0,0] => :quantity,
|
24
|
+
[1,0] => :description,
|
25
|
+
[2,0] => :backorder,
|
26
|
+
[3,0] => :price,
|
27
|
+
[4,0] => :price_levels,
|
28
|
+
[5,0] => :price2,
|
29
|
+
[6,0] => :price3,
|
30
|
+
[4,1] => :price4,
|
31
|
+
[5,1] => :price5,
|
32
|
+
[6,1] => :price6
|
33
|
+
}
|
34
|
+
CSS_HEAD_MAP = {
|
35
|
+
[3,0] => 'right',
|
36
|
+
[4,0] => 'right',
|
37
|
+
}
|
38
|
+
CSS_MAP = {
|
39
|
+
[0,0] => 'tiny',
|
40
|
+
[1,0] => 'description',
|
41
|
+
[3,0,4,2] => 'right'
|
42
|
+
}
|
43
|
+
OFFSET_STEP = [0,2]
|
44
|
+
HEAD_OFFSET_STEP = [0,1]
|
45
|
+
else
|
46
|
+
COMPONENTS = {
|
47
|
+
[0,0] => :quantity,
|
48
|
+
[1,0] => :description,
|
49
|
+
[2,0] => :backorder,
|
50
|
+
[3,0] => :price,
|
51
|
+
}
|
52
|
+
CSS_HEAD_MAP = {
|
53
|
+
[3,0] => 'right',
|
54
|
+
}
|
55
|
+
CSS_MAP = {
|
56
|
+
[0,0] => 'tiny',
|
57
|
+
[1,0] => 'description',
|
58
|
+
[3,0] => 'right'
|
59
|
+
}
|
60
|
+
OFFSET_STEP = [0,1]
|
61
|
+
end
|
62
|
+
EVENT = :order_product
|
63
|
+
FORM_NAME = 'products'
|
64
|
+
SORT_DEFAULT = :description
|
65
|
+
def init
|
66
|
+
super
|
67
|
+
error_message
|
68
|
+
end
|
69
|
+
def compose_footer(matrix)
|
70
|
+
super unless(@model.empty?)
|
71
|
+
end
|
72
|
+
def quantity(product)
|
73
|
+
name = "quantity[#{product.article_number}]"
|
74
|
+
input = HtmlGrid::InputText.new(name, product, @session, self)
|
75
|
+
input.value = @model.ordered_quantity(product)
|
76
|
+
input.css_class = 'tiny'
|
77
|
+
script = "if(this.value == '0') this.value = '';"
|
78
|
+
input.set_attribute('onFocus', script)
|
79
|
+
script = "if(this.value == '') this.value = '0';"
|
80
|
+
input.set_attribute('onBlur', script)
|
81
|
+
input
|
82
|
+
end
|
83
|
+
end
|
84
|
+
class ResultComposite < HtmlGrid::DivComposite
|
85
|
+
COMPONENTS = {
|
86
|
+
[0,0] => Search,
|
87
|
+
[1,0] => :product_count,
|
88
|
+
[0,1] => Products,
|
89
|
+
}
|
90
|
+
CSS_ID_MAP = ['toolbar']
|
91
|
+
def product_count(model)
|
92
|
+
span = HtmlGrid::Span.new(model, @session, self)
|
93
|
+
span.value = if(model.size == 1)
|
94
|
+
@lookandfeel.lookup(:product_found)
|
95
|
+
else
|
96
|
+
@lookandfeel.lookup(:products_found, model.size)
|
97
|
+
end
|
98
|
+
span.css_class = 'guide'
|
99
|
+
span
|
100
|
+
end
|
101
|
+
end
|
102
|
+
class Result < Template
|
103
|
+
CONTENT = ResultComposite
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Html::View::Search -- bbmb.ch -- 21.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'htmlgrid/divform'
|
5
|
+
|
6
|
+
module BBMB
|
7
|
+
module Html
|
8
|
+
module View
|
9
|
+
class Search < HtmlGrid::DivForm
|
10
|
+
CSS_ID = 'search'
|
11
|
+
COMPONENTS = {
|
12
|
+
[0,0] => :query,
|
13
|
+
[1,0] => :submit,
|
14
|
+
}
|
15
|
+
EVENT = :search
|
16
|
+
FORM_NAME = 'search'
|
17
|
+
SYMBOL_MAP = {
|
18
|
+
:query => HtmlGrid::InputText
|
19
|
+
}
|
20
|
+
def init
|
21
|
+
super
|
22
|
+
url = @lookandfeel._event_url(event, :query => nil)
|
23
|
+
self.onsubmit = "document.location.href='#{url}'+this.query.value; return false"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Html::View::ShowPass -- bbmb.ch -- 19.10.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'bbmb/html/view/template'
|
5
|
+
require 'htmlgrid/div'
|
6
|
+
|
7
|
+
module BBMB
|
8
|
+
module Html
|
9
|
+
module View
|
10
|
+
class ShowPassInnerComposite < HtmlGrid::Composite
|
11
|
+
COMPONENTS = {
|
12
|
+
[0,0] => :email,
|
13
|
+
[0,1] => :cleartext,
|
14
|
+
}
|
15
|
+
DEFAULT_CLASS = HtmlGrid::Value
|
16
|
+
LABELS = true
|
17
|
+
end
|
18
|
+
class Address < HtmlGrid::Composite
|
19
|
+
COMPONENTS = {
|
20
|
+
[0,0] => :to_str
|
21
|
+
}
|
22
|
+
end
|
23
|
+
class ShowPassComposite < HtmlGrid::DivComposite
|
24
|
+
COMPONENTS = {
|
25
|
+
[0,0] => :admin_address,
|
26
|
+
[0,1] => :customer_address,
|
27
|
+
[0,2] => " ",
|
28
|
+
[0,3] => ShowPassInnerComposite,
|
29
|
+
}
|
30
|
+
CSS_ID_MAP = {
|
31
|
+
0 => 'admin-address',
|
32
|
+
2 => 'divider',
|
33
|
+
}
|
34
|
+
CSS_MAP = ['address', 'address']
|
35
|
+
def admin_address(model)
|
36
|
+
#div = HtmlGrid::Div.new(model, @session, self)
|
37
|
+
#div.css_id = 'admin-address'
|
38
|
+
#div.value =
|
39
|
+
BBMB.config.admin_address.gsub("\n", "<br>")
|
40
|
+
end
|
41
|
+
def customer_address(model)
|
42
|
+
model.address_lines.join('<br>')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
class ShowPass < Template
|
46
|
+
COMPONENTS = {
|
47
|
+
[0,0] => :logo,
|
48
|
+
[0,1] => ShowPassComposite,
|
49
|
+
}
|
50
|
+
SYMBOL_MAP = {
|
51
|
+
:logo => HtmlGrid::Image,
|
52
|
+
}
|
53
|
+
def init
|
54
|
+
super
|
55
|
+
self.onload = 'window.opener.location.reload();'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Html::View::Template -- bbmb.ch -- 15.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'bbmb/html/view/head'
|
5
|
+
require 'bbmb/html/view/foot'
|
6
|
+
require 'htmlgrid/divtemplate'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
module HtmlGrid
|
10
|
+
class Composite
|
11
|
+
LEGACY_INTERFACE = false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
module BBMB
|
15
|
+
module Html
|
16
|
+
module View
|
17
|
+
class Template < HtmlGrid::DivTemplate
|
18
|
+
COMPONENTS = {
|
19
|
+
[0,0] => :head,
|
20
|
+
[0,1] => :content,
|
21
|
+
[0,2] => :foot,
|
22
|
+
}
|
23
|
+
CSS_ID_MAP = ['head', 'content', 'foot']
|
24
|
+
CSS_FILES = ['bbmb.css']
|
25
|
+
HEAD = Head
|
26
|
+
HTTP_HEADERS = {
|
27
|
+
"Content-Type" => "text/html; charset=utf-8",
|
28
|
+
"Cache-Control" => "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0",
|
29
|
+
"Pragma" => "no-cache",
|
30
|
+
"Expires" => Time.now.httpdate,
|
31
|
+
"P3P" => "CP='OTI NID CUR OUR STP ONL UNI PRE'",
|
32
|
+
}
|
33
|
+
FOOT = Foot
|
34
|
+
META_TAGS = [
|
35
|
+
{
|
36
|
+
"http-equiv" => "robots",
|
37
|
+
"content" => "nofollow, noindex",
|
38
|
+
},
|
39
|
+
]
|
40
|
+
def css_links(context)
|
41
|
+
if(@lookandfeel.enabled?(:external_css, false))
|
42
|
+
css_link(context, @lookandfeel.resource_external(:external_css))
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
def http_headers(*args)
|
48
|
+
headers = super
|
49
|
+
headers.store('Refresh',
|
50
|
+
"#{BBMB.config.session_timeout}; URL=#{@lookandfeel._event_url(:logout)}")
|
51
|
+
headers
|
52
|
+
end
|
53
|
+
def title(context)
|
54
|
+
parts = [:html_title, *@session.state.direct_event].collect { |key|
|
55
|
+
@lookandfeel.lookup(key) }.compact
|
56
|
+
context.title { parts.join(' | ') }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Model::Customer -- bbmb.ch -- 14.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'thread'
|
5
|
+
require 'bbmb/model/order'
|
6
|
+
|
7
|
+
module BBMB
|
8
|
+
module Model
|
9
|
+
class Customer
|
10
|
+
attr_reader :customer_id, :email, :archive, :quotas
|
11
|
+
attr_accessor :address1, :address2, :address3, :canton, :city,
|
12
|
+
:drtitle, :ean13, :fax, :firstname, :language, :lastname,
|
13
|
+
:organisation, :phone_business, :phone_mobile, :phone_private, :plz,
|
14
|
+
:status, :title, :terms_last_accepted, :order_confirmation
|
15
|
+
def initialize(customer_id, email=nil)
|
16
|
+
@archive = {}
|
17
|
+
self.customer_id = customer_id if customer_id
|
18
|
+
@email = email
|
19
|
+
@favorites = Order.new(self)
|
20
|
+
@protected = {}
|
21
|
+
@quotas = []
|
22
|
+
end
|
23
|
+
def add_quota(quota)
|
24
|
+
@quotas.push(quota).uniq!
|
25
|
+
quota
|
26
|
+
end
|
27
|
+
def address_lines
|
28
|
+
[
|
29
|
+
@organisation,
|
30
|
+
[@drtitle, @firstname, @lastname].compact.join(' '),
|
31
|
+
@address1,
|
32
|
+
@address2,
|
33
|
+
@address3,
|
34
|
+
[@plz, @city].compact.join(' '),
|
35
|
+
].compact
|
36
|
+
end
|
37
|
+
def commit_order!(commit_time = Time.now)
|
38
|
+
Thread.exclusive {
|
39
|
+
id = @archive.keys.max.to_i.next
|
40
|
+
order = current_order
|
41
|
+
order.commit!(id, commit_time)
|
42
|
+
@archive.store(id, order)
|
43
|
+
@current_order = nil
|
44
|
+
order
|
45
|
+
}
|
46
|
+
end
|
47
|
+
def current_order
|
48
|
+
@current_order ||= Order.new(self)
|
49
|
+
end
|
50
|
+
def customer_id=(customer_id)
|
51
|
+
if other = Customer.find_by_customer_id(customer_id)
|
52
|
+
raise "Duplicate customer_id #{customer_id}"
|
53
|
+
else
|
54
|
+
@customer_id = customer_id
|
55
|
+
end
|
56
|
+
end
|
57
|
+
def quota(article_id)
|
58
|
+
@quotas.find { |quota| quota.article_number == article_id }
|
59
|
+
end
|
60
|
+
def email=(email)
|
61
|
+
if(@email || email)
|
62
|
+
raise "Invalid email address: nil" unless email
|
63
|
+
## notify the server of this change, as it affects the user-data
|
64
|
+
BBMB.server.rename_user(@email, email)
|
65
|
+
@email = email
|
66
|
+
end
|
67
|
+
end
|
68
|
+
def favorites
|
69
|
+
@favorites ||= Order.new(self)
|
70
|
+
end
|
71
|
+
def inject_order(order, commit_time = Time.now)
|
72
|
+
Thread.exclusive {
|
73
|
+
id = @archive.keys.max.to_i.next
|
74
|
+
order.commit!(id, commit_time)
|
75
|
+
@archive.store(id, order)
|
76
|
+
order
|
77
|
+
}
|
78
|
+
end
|
79
|
+
def order(commit_id)
|
80
|
+
@archive[commit_id.to_i]
|
81
|
+
end
|
82
|
+
def orders
|
83
|
+
@archive.values
|
84
|
+
end
|
85
|
+
def protect!(key)
|
86
|
+
@protected.store(key, true)
|
87
|
+
end
|
88
|
+
def protects?(key)
|
89
|
+
@protected.fetch(key, false)
|
90
|
+
end
|
91
|
+
def turnover
|
92
|
+
orders.inject(0) { |memo, order| order.total + memo }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Model::Order -- bbmb.ch -- 22.09.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'encoding/character/utf-8'
|
5
|
+
require 'bbmb/config'
|
6
|
+
require 'bbmb/util/numbers'
|
7
|
+
require 'csv'
|
8
|
+
|
9
|
+
module BBMB
|
10
|
+
module Model
|
11
|
+
class Order
|
12
|
+
class Info
|
13
|
+
include Util::Numbers
|
14
|
+
attr_accessor :ean13, :pcode, :description
|
15
|
+
int_accessor :quantity
|
16
|
+
end
|
17
|
+
class Position
|
18
|
+
include Util::Numbers
|
19
|
+
attr_reader :product
|
20
|
+
int_accessor :quantity
|
21
|
+
money_accessor :price_effective
|
22
|
+
def initialize(quantity, product)
|
23
|
+
@quantity = quantity
|
24
|
+
@product = product
|
25
|
+
end
|
26
|
+
def commit!
|
27
|
+
@product = @product.to_info
|
28
|
+
end
|
29
|
+
def freebies
|
30
|
+
if(promo = @product.current_promo)
|
31
|
+
promo.freebies(@quantity)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
def method_missing(name, *args, &block)
|
35
|
+
@product.send(name, *args, &block)
|
36
|
+
end
|
37
|
+
def price
|
38
|
+
@price_effective || price_effective
|
39
|
+
end
|
40
|
+
def price_effective(qty = @quantity)
|
41
|
+
@product.price_effective(qty)
|
42
|
+
end
|
43
|
+
def price_qty(qty = @quantity)
|
44
|
+
@product.price_qty(qty)
|
45
|
+
end
|
46
|
+
def total
|
47
|
+
price * @quantity
|
48
|
+
end
|
49
|
+
def respond_to?(name)
|
50
|
+
super || @product.respond_to?(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
include Enumerable
|
54
|
+
include Util::Numbers
|
55
|
+
attr_reader :commit_id, :commit_time, :customer, :positions, :unavailable
|
56
|
+
attr_accessor :comment, :reference
|
57
|
+
int_accessor :priority
|
58
|
+
money_accessor :shipping
|
59
|
+
def initialize(customer)
|
60
|
+
@customer = customer
|
61
|
+
@positions = []
|
62
|
+
@unavailable = []
|
63
|
+
end
|
64
|
+
def add(quantity, product)
|
65
|
+
pos = nil
|
66
|
+
if(pos = position(product))
|
67
|
+
if(quantity.zero?)
|
68
|
+
@positions.delete(pos)
|
69
|
+
else
|
70
|
+
pos.quantity = quantity
|
71
|
+
end
|
72
|
+
elsif(quantity.nonzero?)
|
73
|
+
pos = Position.new(quantity, product)
|
74
|
+
@positions.push(pos)
|
75
|
+
end
|
76
|
+
if(pos && quantity.nonzero?)
|
77
|
+
pos.price_effective = price_effective(pos)
|
78
|
+
end
|
79
|
+
pos
|
80
|
+
end
|
81
|
+
def additional_info
|
82
|
+
info = {}
|
83
|
+
[ :comment, :priority, :reference ].each { |key|
|
84
|
+
if(value = self.send(key))
|
85
|
+
info.store(key, value)
|
86
|
+
end
|
87
|
+
}
|
88
|
+
info
|
89
|
+
end
|
90
|
+
def calculate_effective_prices
|
91
|
+
@positions.each { |pos|
|
92
|
+
pos.price_effective = price_effective(pos)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
def clear
|
96
|
+
@positions.clear
|
97
|
+
@unavailable.clear
|
98
|
+
end
|
99
|
+
def commit!(commit_id, commit_time)
|
100
|
+
raise "can't commit empty order" if(empty?)
|
101
|
+
calculate_effective_prices
|
102
|
+
@positions.each { |pos|
|
103
|
+
pos.commit!
|
104
|
+
}
|
105
|
+
@unavailable.clear
|
106
|
+
@commit_time = commit_time
|
107
|
+
@commit_id = commit_id
|
108
|
+
end
|
109
|
+
def each(&block)
|
110
|
+
@positions.each(&block)
|
111
|
+
end
|
112
|
+
def empty?
|
113
|
+
@positions.empty?
|
114
|
+
end
|
115
|
+
def filename
|
116
|
+
sprintf("%s-%s.txt", order_id, @commit_time.strftime('%Y%m%d%H%M%S'))
|
117
|
+
end
|
118
|
+
def increment(quantity, product)
|
119
|
+
if(pos = position(product))
|
120
|
+
quantity += pos.quantity
|
121
|
+
end
|
122
|
+
add(quantity, product)
|
123
|
+
end
|
124
|
+
def item_count
|
125
|
+
@positions.inject(0) { |memo, pos| memo + pos.quantity }
|
126
|
+
end
|
127
|
+
def i2_body
|
128
|
+
lines = []
|
129
|
+
offset = 1
|
130
|
+
@positions.each_with_index { |position, idx|
|
131
|
+
lines.push *i2_position(idx + offset, position, position.quantity)
|
132
|
+
if(freebies = position.freebies)
|
133
|
+
offset += 1
|
134
|
+
lines.push *i2_position(idx + offset, position, freebies)
|
135
|
+
lines.push "603:21"
|
136
|
+
end
|
137
|
+
}
|
138
|
+
lines.join("\n")
|
139
|
+
end
|
140
|
+
def i2_header
|
141
|
+
lines = [
|
142
|
+
"001:7601001000681",
|
143
|
+
"002:ORDERX",
|
144
|
+
"003:220",
|
145
|
+
"010:%s" % filename,
|
146
|
+
"100:%s" % BBMB.config.i2_100,
|
147
|
+
"101:%s" % @reference,
|
148
|
+
"201:CU",
|
149
|
+
"202:%s" % @customer.customer_id,
|
150
|
+
"201:BY",
|
151
|
+
"202:1075",
|
152
|
+
"231:%s" % @customer.organisation,
|
153
|
+
]
|
154
|
+
if(@comment && !@comment.empty?)
|
155
|
+
lines.push "236:%s" % _formatted_comment
|
156
|
+
end
|
157
|
+
lines.push "237:61"
|
158
|
+
if(@priority)
|
159
|
+
lines.push "238:%i" % @priority
|
160
|
+
end
|
161
|
+
lines.push "250:ADE",
|
162
|
+
sprintf("251:%i%05i", @customer.customer_id, @commit_id),
|
163
|
+
"300:4", "301:%s" % @commit_time.strftime('%Y%m%d')
|
164
|
+
lines.join("\n")
|
165
|
+
end
|
166
|
+
def i2_position(line, position, quantity)
|
167
|
+
["500:%i" % line,
|
168
|
+
"501:%s" % position.ean13,
|
169
|
+
"502:%s" % position.article_number,
|
170
|
+
"520:%s" % quantity,
|
171
|
+
"521:PCE", "540:2", "541:%s" % @commit_time.strftime('%Y%m%d')]
|
172
|
+
end
|
173
|
+
def order_id
|
174
|
+
sprintf "%s-%s", @customer.customer_id, @commit_id
|
175
|
+
end
|
176
|
+
def position(product)
|
177
|
+
@positions.find { |pos| pos.product == product }
|
178
|
+
end
|
179
|
+
def price_effective(pos)
|
180
|
+
((quota = quota(pos.article_number)) && quota.price) \
|
181
|
+
|| pos.price_effective
|
182
|
+
end
|
183
|
+
def quantity(product)
|
184
|
+
if(pos = position(product))
|
185
|
+
pos.quantity
|
186
|
+
else
|
187
|
+
0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
def quota(article_number)
|
191
|
+
@customer.quota(article_number)
|
192
|
+
end
|
193
|
+
def reverse!
|
194
|
+
@positions.reverse!
|
195
|
+
end
|
196
|
+
def size
|
197
|
+
@positions.size
|
198
|
+
end
|
199
|
+
def sort!(*args, &block)
|
200
|
+
@positions.sort!(*args, &block)
|
201
|
+
end
|
202
|
+
def sort_by(*args, &block)
|
203
|
+
twin = dup
|
204
|
+
twin.positions.replace @positions.sort_by(*args, &block)
|
205
|
+
twin
|
206
|
+
end
|
207
|
+
def total
|
208
|
+
@positions.inject(@shipping) { |memo, pos| pos.total + memo }
|
209
|
+
end
|
210
|
+
def total_incl_vat
|
211
|
+
if rate = BBMB.config.vat_rate
|
212
|
+
total * (100 + rate.to_f) / 100
|
213
|
+
end
|
214
|
+
end
|
215
|
+
def to_csv
|
216
|
+
result = ''
|
217
|
+
CSV::Writer.generate(result,
|
218
|
+
BBMB.config.target_format_fs,
|
219
|
+
BBMB.config.target_format_rs) { |writer|
|
220
|
+
@positions.each { |position|
|
221
|
+
writer << [
|
222
|
+
@customer.customer_id,
|
223
|
+
@customer.ean13,
|
224
|
+
@commit_time.strftime('%d%m%Y'),
|
225
|
+
@commit_id,
|
226
|
+
position.pcode,
|
227
|
+
position.ean13,
|
228
|
+
position.article_number,
|
229
|
+
position.quantity,
|
230
|
+
position.price,
|
231
|
+
@reference,
|
232
|
+
_formatted_comment(' '),
|
233
|
+
]
|
234
|
+
}
|
235
|
+
}
|
236
|
+
result
|
237
|
+
end
|
238
|
+
def to_i2
|
239
|
+
i2_header << "\n" << i2_body << "\n"
|
240
|
+
end
|
241
|
+
def to_target_format
|
242
|
+
self.send("to_#{BBMB.config.target_format}")
|
243
|
+
end
|
244
|
+
def vat
|
245
|
+
if rate = BBMB.config.vat_rate
|
246
|
+
total * rate.to_f / 100
|
247
|
+
end
|
248
|
+
end
|
249
|
+
private
|
250
|
+
def _formatted_comment(replacement=';')
|
251
|
+
u(@comment.to_s.gsub(/[\r\n]+/, replacement))[0,60] if @comment
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|