bbmb 2.0.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.
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