bbmb 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/History.txt +5 -0
  2. data/LICENSE.txt +339 -0
  3. data/Manifest.txt +91 -0
  4. data/README.txt +25 -0
  5. data/Rakefile +28 -0
  6. data/bin/admin +71 -0
  7. data/bin/bbmbd +61 -0
  8. data/lib/bbmb.rb +9 -0
  9. data/lib/bbmb/config.rb +106 -0
  10. data/lib/bbmb/html/state/change_password.rb +50 -0
  11. data/lib/bbmb/html/state/current_order.rb +81 -0
  12. data/lib/bbmb/html/state/customer.rb +109 -0
  13. data/lib/bbmb/html/state/customers.rb +52 -0
  14. data/lib/bbmb/html/state/favorites.rb +19 -0
  15. data/lib/bbmb/html/state/favorites_result.rb +21 -0
  16. data/lib/bbmb/html/state/global.rb +62 -0
  17. data/lib/bbmb/html/state/history.rb +95 -0
  18. data/lib/bbmb/html/state/info.rb +23 -0
  19. data/lib/bbmb/html/state/json.rb +16 -0
  20. data/lib/bbmb/html/state/login.rb +76 -0
  21. data/lib/bbmb/html/state/order.rb +21 -0
  22. data/lib/bbmb/html/state/orders.rb +19 -0
  23. data/lib/bbmb/html/state/result.rb +64 -0
  24. data/lib/bbmb/html/state/show_pass.rb +16 -0
  25. data/lib/bbmb/html/state/viral/admin.rb +41 -0
  26. data/lib/bbmb/html/state/viral/customer.rb +143 -0
  27. data/lib/bbmb/html/util/known_user.rb +51 -0
  28. data/lib/bbmb/html/util/multilingual.rb +18 -0
  29. data/lib/bbmb/html/util/session.rb +52 -0
  30. data/lib/bbmb/html/util/validator.rb +55 -0
  31. data/lib/bbmb/html/view/backorder.rb +24 -0
  32. data/lib/bbmb/html/view/change_password.rb +42 -0
  33. data/lib/bbmb/html/view/copyleft.rb +41 -0
  34. data/lib/bbmb/html/view/current_order.rb +482 -0
  35. data/lib/bbmb/html/view/customer.rb +152 -0
  36. data/lib/bbmb/html/view/customers.rb +145 -0
  37. data/lib/bbmb/html/view/favorites.rb +162 -0
  38. data/lib/bbmb/html/view/favorites_result.rb +26 -0
  39. data/lib/bbmb/html/view/foot.rb +21 -0
  40. data/lib/bbmb/html/view/head.rb +32 -0
  41. data/lib/bbmb/html/view/history.rb +60 -0
  42. data/lib/bbmb/html/view/info.rb +33 -0
  43. data/lib/bbmb/html/view/json.rb +20 -0
  44. data/lib/bbmb/html/view/list_prices.rb +51 -0
  45. data/lib/bbmb/html/view/login.rb +46 -0
  46. data/lib/bbmb/html/view/multilingual.rb +17 -0
  47. data/lib/bbmb/html/view/navigation.rb +30 -0
  48. data/lib/bbmb/html/view/order.rb +123 -0
  49. data/lib/bbmb/html/view/orders.rb +50 -0
  50. data/lib/bbmb/html/view/result.rb +107 -0
  51. data/lib/bbmb/html/view/search.rb +28 -0
  52. data/lib/bbmb/html/view/show_pass.rb +60 -0
  53. data/lib/bbmb/html/view/template.rb +61 -0
  54. data/lib/bbmb/model/customer.rb +96 -0
  55. data/lib/bbmb/model/order.rb +255 -0
  56. data/lib/bbmb/model/product.rb +99 -0
  57. data/lib/bbmb/model/promotion.rb +52 -0
  58. data/lib/bbmb/model/quota.rb +27 -0
  59. data/lib/bbmb/model/subject.rb +46 -0
  60. data/lib/bbmb/persistence/none.rb +6 -0
  61. data/lib/bbmb/persistence/odba.rb +42 -0
  62. data/lib/bbmb/persistence/odba/model/customer.rb +47 -0
  63. data/lib/bbmb/persistence/odba/model/order.rb +50 -0
  64. data/lib/bbmb/persistence/odba/model/product.rb +26 -0
  65. data/lib/bbmb/persistence/odba/model/quota.rb +12 -0
  66. data/lib/bbmb/util/invoicer.rb +126 -0
  67. data/lib/bbmb/util/mail.rb +140 -0
  68. data/lib/bbmb/util/multilingual.rb +57 -0
  69. data/lib/bbmb/util/numbers.rb +67 -0
  70. data/lib/bbmb/util/password_generator.rb +44 -0
  71. data/lib/bbmb/util/polling_manager.rb +135 -0
  72. data/lib/bbmb/util/server.rb +159 -0
  73. data/lib/bbmb/util/target_dir.rb +36 -0
  74. data/lib/bbmb/util/transfer_dat.rb +34 -0
  75. data/lib/bbmb/util/updater.rb +27 -0
  76. data/test/data/ydim.yml +2 -0
  77. data/test/model/test_customer.rb +75 -0
  78. data/test/model/test_order.rb +426 -0
  79. data/test/model/test_product.rb +238 -0
  80. data/test/model/test_promotion.rb +40 -0
  81. data/test/stub/persistence.rb +57 -0
  82. data/test/suite.rb +12 -0
  83. data/test/test_bbmb.rb +18 -0
  84. data/test/util/test_invoicer.rb +189 -0
  85. data/test/util/test_mail.rb +359 -0
  86. data/test/util/test_money.rb +71 -0
  87. data/test/util/test_password_generator.rb +27 -0
  88. data/test/util/test_polling_manager.rb +312 -0
  89. data/test/util/test_server.rb +189 -0
  90. data/test/util/test_target_dir.rb +82 -0
  91. data/test/util/test_transfer_dat.rb +45 -0
  92. 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] => "&nbsp;",
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