ydim_html 1.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 (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