ydim_html 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/History.txt +6 -0
  2. data/LICENSE.txt +339 -0
  3. data/Manifest.txt +47 -0
  4. data/README.txt +21 -0
  5. data/Rakefile +28 -0
  6. data/bin/ydim-htmld +38 -0
  7. data/lib/ydim/html.rb +10 -0
  8. data/lib/ydim/html/config.rb +37 -0
  9. data/lib/ydim/html/state/ajax_values.rb +16 -0
  10. data/lib/ydim/html/state/autoinvoice.rb +93 -0
  11. data/lib/ydim/html/state/confirm.rb +15 -0
  12. data/lib/ydim/html/state/debitor.rb +81 -0
  13. data/lib/ydim/html/state/debitors.rb +23 -0
  14. data/lib/ydim/html/state/global.rb +99 -0
  15. data/lib/ydim/html/state/global_predefine.rb +13 -0
  16. data/lib/ydim/html/state/init.rb +20 -0
  17. data/lib/ydim/html/state/invoice.rb +179 -0
  18. data/lib/ydim/html/state/invoices.rb +69 -0
  19. data/lib/ydim/html/state/pdf.rb +16 -0
  20. data/lib/ydim/html/util/lookandfeel.rb +132 -0
  21. data/lib/ydim/html/util/server.rb +36 -0
  22. data/lib/ydim/html/util/session.rb +27 -0
  23. data/lib/ydim/html/util/validator.rb +62 -0
  24. data/lib/ydim/html/view/ajax_values.rb +22 -0
  25. data/lib/ydim/html/view/autoinvoice.rb +79 -0
  26. data/lib/ydim/html/view/autoinvoices.rb +41 -0
  27. data/lib/ydim/html/view/confirm.rb +27 -0
  28. data/lib/ydim/html/view/debitor.rb +111 -0
  29. data/lib/ydim/html/view/debitors.rb +44 -0
  30. data/lib/ydim/html/view/htmlgrid.rb +103 -0
  31. data/lib/ydim/html/view/init.rb +29 -0
  32. data/lib/ydim/html/view/invoice.rb +217 -0
  33. data/lib/ydim/html/view/invoices.rb +158 -0
  34. data/lib/ydim/html/view/navigation.rb +28 -0
  35. data/lib/ydim/html/view/pdf.rb +23 -0
  36. data/lib/ydim/html/view/template.rb +70 -0
  37. data/test/selenium.rb +1687 -0
  38. data/test/selenium/selenium-server.jar +0 -0
  39. data/test/selenium/test_autoinvoice.rb +318 -0
  40. data/test/selenium/test_debitor.rb +343 -0
  41. data/test/selenium/test_debitors.rb +46 -0
  42. data/test/selenium/test_init.rb +104 -0
  43. data/test/selenium/test_invoice.rb +295 -0
  44. data/test/selenium/test_invoices.rb +175 -0
  45. data/test/selenium/unit.rb +124 -0
  46. data/test/stub/http_server.rb +139 -0
  47. data/test/suite.rb +14 -0
  48. metadata +138 -0
data/lib/ydim/html.rb ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # Html -- ydim -- 14.12.2006 -- hwyss@ywesee.com
3
+ VERSION = '1.0.0'
4
+ module YDIM
5
+ module Html
6
+ class << self
7
+ attr_accessor :config, :logger
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # Html.config -- ydim -- 14.12.2006 -- hwyss@ywesee.com
3
+
4
+ require 'rclconf'
5
+ require 'ydim/html'
6
+
7
+ module YDIM
8
+ module Html
9
+ ydim_default_dir = '/var/ydim/'
10
+ if(home = ENV['HOME'])
11
+ ydim_default_dir = File.join(home, '.ydim')
12
+ end
13
+ default_config_files = [
14
+ 'etc/ydim-htmld.yml',
15
+ File.join(ydim_default_dir, 'ydim-htmld.yml'),
16
+ '/etc/ydim/ydim-htmld.yml',
17
+ ]
18
+ defaults = {
19
+ 'client_url' => 'druby://localhost:0',
20
+ 'config' => default_config_files,
21
+ 'currency' => 'CHF',
22
+ 'email' => nil,
23
+ 'html_url' => 'druby://localhost:12376',
24
+ 'http_server' => 'http://localhost',
25
+ 'log_file' => $stdout,
26
+ 'log_level' => 'DEBUG',
27
+ 'md5_pass' => nil,
28
+ 'proxy_url' => 'druby://localhost:0',
29
+ 'server_url' => 'druby://localhost:12375',
30
+ 'root_key' => nil,
31
+ 'user' => nil,
32
+ 'ydim_dir' => ydim_default_dir,
33
+ }
34
+ @config = RCLConf::RCLConf.new(ARGV, defaults)
35
+ @config.load(@config.config)
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::AjaxValues -- ydim -- 18.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'sbsm/state'
5
+ require 'ydim/html/view/ajax_values'
6
+
7
+ module YDIM
8
+ module Html
9
+ module State
10
+ class AjaxValues < SBSM::State
11
+ VOLATILE = true
12
+ VIEW = Html::View::AjaxValues
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::AutoInvoice -- ydim -- 13.12.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/invoice'
5
+ require 'ydim/html/view/autoinvoice'
6
+
7
+ module YDIM
8
+ module Html
9
+ module State
10
+ class AjaxAutoInvoice < SBSM::State
11
+ VOLATILE = true
12
+ VIEW = Html::View::AutoInvoiceComposite
13
+ end
14
+ module AutoInvoiceKeys
15
+ def invoice_key
16
+ :autoinvoice
17
+ end
18
+ def invoice_keys
19
+ super + [ :date, :reminder_body, :reminder_subject ]
20
+ end
21
+ def invoice_mandatory
22
+ [ :description, :currency, :invoice_interval ]
23
+ end
24
+ end
25
+ class CreateAutoInvoice < CreateInvoice
26
+ include AutoInvoiceKeys
27
+ VIEW = Html::View::AutoInvoice
28
+ def update
29
+ _update(AutoInvoice)
30
+ end
31
+ end
32
+ class AutoInvoice < Invoice
33
+ include AutoInvoiceKeys
34
+ VIEW = Html::View::AutoInvoice
35
+ def ajax_invoice
36
+ _do_update
37
+ AjaxAutoInvoice.new(@session, @model)
38
+ end
39
+ def format_invoice
40
+ lnf = @session.lookandfeel
41
+ total = lnf.lookup(:total_netto)
42
+ item_widths = [total.length]
43
+ qty_widths = []
44
+ float = false
45
+ @model.items.each do |item|
46
+ item_widths.push item.text.length
47
+ qty = item.quantity
48
+ qty_widths.push qty.to_i.to_s.length
49
+ float ||= (qty.to_i != qty.to_f)
50
+ end
51
+ item_width = item_widths.max.to_i.next
52
+ qty_width = qty_widths.max.to_i
53
+ total_width = ("%3.2f" % @model.total_netto).length
54
+ fmt = if float
55
+ qty_width += 3
56
+ "%#{qty_width}.2f x %-#{item_width}s %s %#{total_width}.2f\n"
57
+ else
58
+ "%#{qty_width}i x %-#{item_width}s %s %#{total_width}.2f\n"
59
+ end
60
+ invoice = "<invoice>\n"
61
+ currency = @model.currency
62
+ @model.items.each { |item|
63
+ invoice << sprintf(fmt, item.quantity.to_f, item.text,
64
+ currency, item.total_netto)
65
+ }
66
+ fmt = "%#{qty_width + 2}s %-#{item_width}s %s %#{total_width}.2f\n"
67
+ invoice << sprintf(fmt, '', total,
68
+ currency, @model.total_netto)
69
+ invoice << "</invoice>"
70
+ end
71
+ def generate_invoice
72
+ _do_update
73
+ if((id = @session.user_input(:unique_id)) \
74
+ && @model.unique_id == id.to_i)
75
+ Invoice.new(@session, @session.generate_invoice(id))
76
+ end
77
+ end
78
+ def _do_update_invoice(input)
79
+ ptrn = %r{<invoice>.*</invoice>}m
80
+ if(body = input[:reminder_body])
81
+ test = body.gsub(ptrn, "").strip
82
+ if(test.empty?)
83
+ input.store(:reminder_body, nil)
84
+ else
85
+ input.store(:reminder_body, body.gsub(ptrn, format_invoice))
86
+ end
87
+ end
88
+ super(input)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Confirm -- ydim -- 18.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/global_predefine'
5
+ require 'ydim/html/view/confirm'
6
+
7
+ module YDIM
8
+ module Html
9
+ module State
10
+ class Confirm < Global
11
+ VIEW = Html::View::Confirm
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Debitor -- ydim -- 12.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/global_predefine'
5
+ require 'ydim/html/state/ajax_values'
6
+ require 'ydim/html/state/invoices'
7
+ require 'ydim/html/view/debitor'
8
+
9
+ module YDIM
10
+ module Html
11
+ module State
12
+ class AjaxAutoInvoices < SBSM::State
13
+ VOLATILE = true
14
+ VIEW = Html::View::AutoInvoiceList
15
+ end
16
+ class Debitor < Global
17
+ include AjaxInvoiceMethods
18
+ attr_reader :model, :autoinvoice_infos, :invoice_infos
19
+ VIEW = Html::View::Debitor
20
+ def init
21
+ super
22
+ load_autoinvoices
23
+ load_invoices
24
+ end
25
+ def ajax_collect_garbage
26
+ @session.collect_garbage(@model.unique_id)
27
+ AjaxInvoices.new(@session, [])
28
+ end
29
+ def ajax_delete_autoinvoice
30
+ if(id = @session.user_input(:unique_id))
31
+ @session.delete_autoinvoice(id)
32
+ end
33
+ AjaxAutoInvoices.new(@session, load_autoinvoices)
34
+ end
35
+ def ajax_invoices
36
+ super(@invoice_infos)
37
+ end
38
+ def generate_invoice
39
+ if(id = @session.user_input(:unique_id))
40
+ Invoice.new(@session, @session.generate_invoice(id.to_i))
41
+ end
42
+ end
43
+ def update
44
+ mandatory = [ :contact, :debitor_type, :emails,
45
+ :location, :name, ]
46
+ defaults = {}
47
+ keys = mandatory.dup.push(:address_lines, :contact_firstname,
48
+ :contact_title, :country, :salutation, :phone)
49
+ input = defaults.update(user_input(keys, mandatory))
50
+ mails = input[:emails]
51
+ if mails && mails.size > 3
52
+ @errors.store :emails, create_error('e_too_many_emails',
53
+ :emails, mails.join(', '))
54
+ input[:emails] = mails[0,3]
55
+ end
56
+ unless(error? || @model.unique_id)
57
+ @model = @session.create_debitor
58
+ end
59
+ update_model(input)
60
+ self
61
+ end
62
+ private
63
+ def load_autoinvoices
64
+ invoices = @model.autoinvoice_infos
65
+ @autoinvoice_infos = sort_invoices(currency_convert(invoices))
66
+ end
67
+ def load_invoices
68
+ invoices = @model.invoice_infos(@session.user_input(:status) \
69
+ || 'is_open')
70
+ @invoice_infos = sort_invoices(currency_convert(invoices))
71
+ end
72
+ def update_model(input)
73
+ input.each { |key, val|
74
+ @model.send("#{key}=".to_sym, val)
75
+ }
76
+ @model.odba_store if(@model.unique_id)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Debitors -- ydim -- 12.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/global_predefine'
5
+ require 'ydim/html/state/debitor'
6
+ require 'ydim/html/view/debitors'
7
+ require 'ydim/debitor'
8
+
9
+ module YDIM
10
+ module Html
11
+ module State
12
+ class Debitors < Global
13
+ VIEW = Html::View::Debitors
14
+ def init
15
+ lnf = @session.lookandfeel
16
+ @model = @session.debitors.sort_by do |deb|
17
+ [ lnf.lookup(deb.debitor_type) || '', deb.name.to_s.downcase ]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Global -- ydim -- 12.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'sbsm/state'
5
+ require 'ydim/html/state/init'
6
+ require 'ydim/html/state/autoinvoice'
7
+ require 'ydim/html/state/debitor'
8
+ require 'ydim/html/state/debitors'
9
+ require 'ydim/html/state/invoice'
10
+ require 'ydim/html/state/invoices'
11
+ require 'ydim/html/state/pdf'
12
+
13
+ module YDIM
14
+ module Html
15
+ module State
16
+ class Global < SBSM::State
17
+ attr_accessor :sortby, :sort_reverse
18
+ class Stub
19
+ def initialize
20
+ @carry = {}
21
+ end
22
+ def carry(key, val)
23
+ @carry.store(key, val)
24
+ end
25
+ def method_missing(key, *args)
26
+ if(match = /^(.*)=$/.match(key.to_s))
27
+ @carry[match[1].to_sym] = args.first
28
+ else
29
+ @carry[key]
30
+ end
31
+ end
32
+ def respond_to?(key)
33
+ true
34
+ end
35
+ end
36
+ EVENT_MAP = {
37
+ :debitors => Debitors,
38
+ :invoices => Invoices,
39
+ }
40
+ def autoinvoice
41
+ if(id = @session.user_input(:unique_id))
42
+ AutoInvoice.new(@session, @session.autoinvoice(id))
43
+ end
44
+ end
45
+ def create_autoinvoice
46
+ _create_invoice(CreateAutoInvoice, nil)
47
+ end
48
+ def create_debitor
49
+ Debitor.new(@session, YDIM::Debitor.new(nil))
50
+ end
51
+ def create_invoice
52
+ _create_invoice(CreateInvoice)
53
+ end
54
+ def _create_invoice(nextclass, date=Date.today)
55
+ if((id = @session.user_input(:unique_id)) \
56
+ && (debitor = @session.debitor(id.to_i)))
57
+ invoice = Stub.new
58
+ invoice.carry(:debitor, debitor)
59
+ invoice.carry(:date, date)
60
+ invoice.carry(:precision, 2)
61
+ if debitor.foreign?
62
+ invoice.carry(:suppress_vat, true)
63
+ end
64
+ nextclass.new(@session, invoice)
65
+ end
66
+ end
67
+ def debitor
68
+ if(id = @session.user_input(:unique_id))
69
+ Debitor.new(@session, @session.debitor(id.to_i))
70
+ end
71
+ end
72
+ def logout
73
+ @session.logout
74
+ State::Init.new(@session, nil)
75
+ end
76
+ def invoice
77
+ if(id = @session.user_input(:unique_id))
78
+ Invoice.new(@session, @session.invoice(id.to_i))
79
+ end
80
+ end
81
+ def pdf
82
+ if(id = @session.user_input(:unique_id))
83
+ Pdf.new(@session, @session.invoice(id.to_i))
84
+ end
85
+ end
86
+ def send_invoice
87
+ if(id = @session.user_input(:unique_id))
88
+ sort_args = { :sortby => (@sortby || []).first,
89
+ :sort_reverse => @sort_reverse }
90
+ recipients = @session.send_invoice(id.to_i, sort_args)
91
+ message = @session.lookandfeel.lookup(:confirm_send_invoice,
92
+ recipients.join(', '))
93
+ Html::State::Confirm.new(@session, message)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Global -- ydim -- 12.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'sbsm/state'
5
+
6
+ module YDIM
7
+ module Html
8
+ module State
9
+ class Global < SBSM::State
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Init -- ydim -- 12.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/debitors'
5
+ require 'ydim/html/view/init'
6
+
7
+ module YDIM
8
+ module Html
9
+ module State
10
+ class Init < SBSM::State
11
+ VIEW = Html::View::Init
12
+ def login
13
+ if(res = @session.login)
14
+ Debitors.new(@session, nil)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+ # Html::State::Invoice -- ydim -- 16.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'ydim/html/state/global_predefine'
5
+ require 'ydim/html/state/ajax_values'
6
+ require 'ydim/html/state/confirm'
7
+ require 'ydim/html/view/invoice'
8
+
9
+ module YDIM
10
+ module Html
11
+ module State
12
+ class AjaxItems < SBSM::State
13
+ VOLATILE = true
14
+ VIEW = Html::View::ItemList
15
+ end
16
+ class AjaxInvoice < SBSM::State
17
+ VOLATILE = true
18
+ VIEW = Html::View::InvoiceComposite
19
+ end
20
+ module InvoiceKeys
21
+ def invoice_key
22
+ :invoice
23
+ end
24
+ def invoice_keys
25
+ invoice_mandatory + [:precision, :suppress_vat]
26
+ end
27
+ def invoice_mandatory
28
+ [:description, :date, :currency]
29
+ end
30
+ end
31
+ class CreateInvoice < Global
32
+ include InvoiceKeys
33
+ VIEW = Html::View::Invoice
34
+ attr_reader :model
35
+ def update
36
+ _update(Invoice)
37
+ end
38
+ def _update(nextclass)
39
+ input = user_input(invoice_keys, invoice_mandatory)
40
+ input[:precision] = (input[:precision] || 2).to_i
41
+ unless(error?)
42
+ @model = @session.send("create_#{invoice_key}",
43
+ @model.debitor.unique_id)
44
+ @model.payment_period = 30
45
+ input.each { |key, val|
46
+ @model.send("#{key}=", val)
47
+ }
48
+ @model.odba_store
49
+ nextclass.new(@session, @model)
50
+ end
51
+ end
52
+ end
53
+ class Invoice < Global
54
+ include InvoiceKeys
55
+ class SortableInvoice
56
+ def initialize invoice
57
+ @invoice = invoice
58
+ end
59
+ def items
60
+ @items ||= @invoice.items
61
+ end
62
+ def reverse!
63
+ items.reverse!
64
+ end
65
+ def respond_to? *args
66
+ @invoice.respond_to?(*args) || super
67
+ end
68
+ def sort! &block
69
+ items.sort! &block
70
+ end
71
+ def update!
72
+ @items = nil
73
+ end
74
+ def method_missing name, *args, &block
75
+ @invoice.send name, *args, &block
76
+ end
77
+ end
78
+ VIEW = Html::View::Invoice
79
+ attr_reader :model
80
+ def init
81
+ @model = SortableInvoice.new @model
82
+ super
83
+ end
84
+ def ajax_create_item
85
+ if(id = @session.user_input(:unique_id))
86
+ begin
87
+ @session.add_items(id.to_i, [{:time => Time.now}], invoice_key)
88
+ rescue IndexError
89
+ end
90
+ @model.update! ## @model is a SortableInvoice
91
+ end
92
+ AjaxItems.new(@session, @model.items)
93
+ end
94
+ def ajax_delete_item
95
+ if((id = @session.user_input(:unique_id)) \
96
+ && (idx = @session.user_input(:index)))
97
+ begin
98
+ @session.delete_item(id.to_i, idx.to_i, invoice_key)
99
+ rescue IndexError
100
+ end
101
+ @model.update! ## @model is a SortableInvoice
102
+ end
103
+ AjaxItems.new(@session, @model.items)
104
+ end
105
+ def ajax_item
106
+ data = {}
107
+ if((id = @session.user_input(:unique_id)) \
108
+ && (idx = @session.user_input(:index)))
109
+ begin
110
+ keys = [:text, :quantity, :unit, :price]
111
+ input = user_input(keys).delete_if { |key, val| val.nil? }
112
+ item = @session.update_item(id.to_i, idx.to_i, input,
113
+ invoice_key)
114
+ input.each { |key, val| data.store("#{key}[#{item.index}]", val) }
115
+ data.store("total_netto#{item.index}", item.total_netto)
116
+ rescue IndexError
117
+ end
118
+ end
119
+ [:total_netto, :vat, :total_brutto].each { |key|
120
+ data.store(key, @model.send(key))
121
+ }
122
+ AjaxValues.new(@session, data)
123
+ end
124
+ def ajax_invoice
125
+ _do_update
126
+ AjaxInvoice.new(@session, @model)
127
+ end
128
+ def pdf
129
+ newstate = super
130
+ newstate.sortby, newstate.sort_reverse = @sortby, @sort_reverse
131
+ newstate
132
+ end
133
+ def send_invoice
134
+ _do_update
135
+ super
136
+ end
137
+ def update
138
+ _do_update
139
+ self
140
+ end
141
+ def _do_update
142
+ if((id = @session.user_input(:unique_id)) \
143
+ && @model.unique_id == id.to_i)
144
+ ## update items
145
+ keys = [:text, :quantity, :unit, :price]
146
+ data = {}
147
+ user_input(keys).each { |key, hash|
148
+ hash.each { |idx, value|
149
+ (data[idx] ||= {}).store(key, value)
150
+ } unless hash.nil?
151
+ }
152
+ target = origin = nil
153
+ converter = if((target = @session.user_input(:currency)) \
154
+ && (origin = @model.currency) \
155
+ && origin != target)
156
+ @session.currency_converter
157
+ end
158
+ data.each { |idx, item|
159
+ if(converter)
160
+ item[:price] = converter.convert(item[:price], origin, target)
161
+ end
162
+ @session.update_item(id.to_i, idx.to_i, item, invoice_key)
163
+ }
164
+
165
+ _do_update_invoice(user_input(invoice_keys, invoice_mandatory))
166
+ @model.update! ## @model is a SortableInvoice
167
+ @model.odba_store
168
+ end
169
+ end
170
+ def _do_update_invoice(input)
171
+ input[:precision] = (input[:precision] || 2).to_i
172
+ input.each { |key, val|
173
+ @model.send("#{key}=", val)
174
+ }
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end