clevic 0.13.0.b9 → 0.13.0.b10
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.
- data/History.txt +3 -0
- data/lib/clevic/action_builder.rb +16 -16
- data/lib/clevic/ar_methods.rb +22 -22
- data/lib/clevic/attribute_list.rb +5 -5
- data/lib/clevic/cache_table.rb +18 -18
- data/lib/clevic/dataset_roller.rb +3 -3
- data/lib/clevic/default_view.rb +4 -4
- data/lib/clevic/delegate.rb +8 -8
- data/lib/clevic/delegates/combo_delegate.rb +22 -22
- data/lib/clevic/delegates/distinct_delegate.rb +5 -5
- data/lib/clevic/delegates/relational_delegate.rb +6 -6
- data/lib/clevic/delegates/set_delegate.rb +3 -3
- data/lib/clevic/dirty.rb +1 -1
- data/lib/clevic/emitter.rb +3 -3
- data/lib/clevic/extensions.rb +4 -4
- data/lib/clevic/field.rb +68 -68
- data/lib/clevic/field_valuer.rb +26 -26
- data/lib/clevic/filter_command.rb +6 -6
- data/lib/clevic/framework.rb +3 -3
- data/lib/clevic/generic_format.rb +2 -2
- data/lib/clevic/many_field.rb +3 -3
- data/lib/clevic/model_builder.rb +88 -84
- data/lib/clevic/model_column.rb +13 -13
- data/lib/clevic/ordered_dataset.rb +7 -7
- data/lib/clevic/qt/action_builder.rb +3 -3
- data/lib/clevic/qt/browser.rb +28 -28
- data/lib/clevic/qt/clipboard.rb +5 -5
- data/lib/clevic/qt/combo_delegate.rb +12 -12
- data/lib/clevic/qt/distinct_delegate.rb +1 -1
- data/lib/clevic/qt/extensions.rb +4 -4
- data/lib/clevic/qt/qt_combo_box.rb +7 -7
- data/lib/clevic/qt/relational_delegate.rb +5 -5
- data/lib/clevic/qt/search_dialog.rb +15 -15
- data/lib/clevic/qt/simplest_delegate.rb +2 -2
- data/lib/clevic/qt/table_model.rb +46 -46
- data/lib/clevic/qt/table_view.rb +48 -48
- data/lib/clevic/qt/text_delegate.rb +9 -9
- data/lib/clevic/rails_models_loaders.rb +3 -3
- data/lib/clevic/record.rb +8 -8
- data/lib/clevic/sampler.rb +6 -6
- data/lib/clevic/sequel_ar_adapter.rb +22 -22
- data/lib/clevic/sequel_clevic.rb +10 -10
- data/lib/clevic/sequel_meta.rb +5 -5
- data/lib/clevic/sequel_naked.rb +4 -4
- data/lib/clevic/swing/action.rb +20 -20
- data/lib/clevic/swing/action_builder.rb +2 -2
- data/lib/clevic/swing/boolean_delegate.rb +3 -3
- data/lib/clevic/swing/browser.rb +37 -37
- data/lib/clevic/swing/cell_editor.rb +13 -13
- data/lib/clevic/swing/cell_renderer.rb +7 -7
- data/lib/clevic/swing/clipboard.rb +19 -19
- data/lib/clevic/swing/combo_delegate.rb +26 -26
- data/lib/clevic/swing/confirm_dialog.rb +7 -7
- data/lib/clevic/swing/delegate.rb +4 -4
- data/lib/clevic/swing/extensions.rb +24 -24
- data/lib/clevic/swing/field.rb +1 -1
- data/lib/clevic/swing/relational_delegate.rb +2 -2
- data/lib/clevic/swing/row_header.rb +32 -32
- data/lib/clevic/swing/search_dialog.rb +31 -31
- data/lib/clevic/swing/selection_model.rb +12 -12
- data/lib/clevic/swing/swing_table_index.rb +6 -6
- data/lib/clevic/swing/table_model.rb +30 -30
- data/lib/clevic/swing/table_view.rb +54 -54
- data/lib/clevic/swing/table_view_focus.rb +4 -4
- data/lib/clevic/swing/tag_delegate.rb +14 -14
- data/lib/clevic/swing/tag_editor.rb +4 -4
- data/lib/clevic/swing/text_area_delegate.rb +6 -6
- data/lib/clevic/swing/text_delegate.rb +4 -4
- data/lib/clevic/table_index.rb +14 -14
- data/lib/clevic/table_model.rb +30 -30
- data/lib/clevic/table_searcher.rb +19 -19
- data/lib/clevic/table_view.rb +92 -92
- data/lib/clevic/table_view_paste.rb +19 -19
- data/lib/clevic/version.rb +1 -1
- data/lib/clevic/view.rb +22 -22
- data/models/accounts_models.rb +10 -10
- data/models/examples.rb +2 -2
- data/models/times_models.rb +32 -32
- data/models/values_models.rb +2 -2
- data/test/test_cache_table.rb +15 -15
- data/test/test_helper.rb +7 -7
- data/test/test_model_index_extensions.rb +6 -6
- data/test/test_table_model.rb +6 -6
- data/test/test_table_searcher.rb +25 -25
- metadata +33 -35
@@ -12,7 +12,7 @@ class TableView
|
|
12
12
|
def paste
|
13
13
|
busy_cursor do
|
14
14
|
sanity_check_read_only
|
15
|
-
|
15
|
+
|
16
16
|
# Try text/html then text/plain as tsv or csv
|
17
17
|
# LATER maybe use the java-native-application at some point for
|
18
18
|
# cut'n'paste internally?
|
@@ -28,14 +28,14 @@ class TableView
|
|
28
28
|
rescue PasteError => e
|
29
29
|
show_error e.message
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
# Paste suitable html to the selection
|
33
33
|
# Check for presence of tr tags, and make sure there are no colspan or rowspan attributes
|
34
34
|
# on td tags.
|
35
35
|
def paste_html
|
36
36
|
emit_status_text "Fetching data."
|
37
37
|
html = clipboard.html
|
38
|
-
|
38
|
+
|
39
39
|
# This should really be factored out somewhere and tested thoroughly
|
40
40
|
emit_status_text "Analysing data."
|
41
41
|
doc =
|
@@ -44,7 +44,7 @@ class TableView
|
|
44
44
|
else
|
45
45
|
Hpricot.parse( html )
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# call the plain text paste if we don't have tabular data
|
49
49
|
if doc.search( "//tr" ).size == 0
|
50
50
|
paste_text
|
@@ -62,13 +62,13 @@ class TableView
|
|
62
62
|
#{cell_list}
|
63
63
|
EOF
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
# run through the tabular data and convert to simple array
|
67
67
|
emit_status_text "Pasting data."
|
68
68
|
ary = ( doc / :tr ).map do |row|
|
69
69
|
( row / :td ).map do |cell|
|
70
70
|
# trim leading and trailing \r\n\t
|
71
|
-
|
71
|
+
|
72
72
|
# check for br
|
73
73
|
unless cell.search( '//br' ).empty?
|
74
74
|
# treat br as separate lines
|
@@ -79,11 +79,11 @@ class TableView
|
|
79
79
|
end.gsub( /^[\r\n\t]*/, '').gsub( /[\r\n\t]*$/, '')
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
paste_array ary
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
# LATER probably need a PasteParser or something, to figure
|
88
88
|
# out if a file is tsv or csv
|
89
89
|
# Try tsv first, because number formats often have embedded ','.
|
@@ -92,7 +92,7 @@ class TableView
|
|
92
92
|
# TODO could also heuristically check paste selection area
|
93
93
|
def paste_text
|
94
94
|
text = clipboard.text
|
95
|
-
|
95
|
+
|
96
96
|
case text
|
97
97
|
when /\t/
|
98
98
|
paste_array( CSV.parse( text, :col_sep => "\t" ) )
|
@@ -103,7 +103,7 @@ class TableView
|
|
103
103
|
paste_value_to_selection( text )
|
104
104
|
end
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
# Paste array to either a single selection or a matching multiple selection
|
108
108
|
# TODO Check for rectangularness, ie csv_arr.map{|row| row.size}.uniq.size == 1
|
109
109
|
def paste_array( arr )
|
@@ -124,21 +124,21 @@ class TableView
|
|
124
124
|
if selection_model.ranges.size != 1
|
125
125
|
raise PasteError, "Can't paste tabular data to multiple selection."
|
126
126
|
end
|
127
|
-
|
127
|
+
|
128
128
|
if selection_model.ranges.first.height != arr.size
|
129
129
|
raise PasteError, "Height of paste area (#{selection_model.ranges.first.height}) doesn't match height of data (#{arr.size})."
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
if selection_model.ranges.first.width != arr.first.size
|
133
133
|
raise PasteError, "Width of paste area (#{selection_model.ranges.first.width}) doesn't match width of data (#{arr.first.size})."
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
# size is the same, so do the paste
|
137
137
|
paste_to_index( selected_index, arr )
|
138
138
|
end
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
# set all indexes in the selection to the value
|
143
143
|
def paste_value_to_selection( value )
|
144
144
|
selection_model.selected_indexes.each do |index|
|
@@ -146,7 +146,7 @@ class TableView
|
|
146
146
|
# save records to db via view, so we get error messages
|
147
147
|
save_row( index )
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
# notify of changed data
|
151
151
|
model.data_changed do |change|
|
152
152
|
sorted = selection_model.selected_indexes.sort
|
@@ -154,7 +154,7 @@ class TableView
|
|
154
154
|
change.bottom_right = sorted.last
|
155
155
|
end
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
# Paste an array to the index, replacing whatever is at that index
|
159
159
|
# and whatever is at other indices matching the size of the pasted
|
160
160
|
# csv array. Create new rows if there aren't enough.
|
@@ -163,7 +163,7 @@ class TableView
|
|
163
163
|
csv_arr.each_with_index do |row,row_index|
|
164
164
|
# append row if we need one
|
165
165
|
model.add_new_item if top_left_index.row + row_index >= model.row_count
|
166
|
-
|
166
|
+
|
167
167
|
row.each_with_index do |field, field_index|
|
168
168
|
unless top_left_index.column + field_index >= model.column_count
|
169
169
|
# do paste
|
@@ -183,7 +183,7 @@ class TableView
|
|
183
183
|
# save records to db via view, so we get error messages
|
184
184
|
save_row( top_left_index.choppy {|i| i.row += row_index; i.column = 0 } )
|
185
185
|
end
|
186
|
-
|
186
|
+
|
187
187
|
# make the gui refresh
|
188
188
|
model.data_changed do |change|
|
189
189
|
change.top_left = top_left_index
|
@@ -193,7 +193,7 @@ class TableView
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
end
|
196
|
-
|
196
|
+
|
197
197
|
end
|
198
198
|
|
199
199
|
end
|
data/lib/clevic/version.rb
CHANGED
data/lib/clevic/view.rb
CHANGED
@@ -5,28 +5,28 @@ module Clevic
|
|
5
5
|
# This contains the definition of a particular view of an entity.
|
6
6
|
# See Clevic::ModelBuilder.
|
7
7
|
class View
|
8
|
-
|
8
|
+
|
9
9
|
class << self
|
10
10
|
def define_ui_block( &block )
|
11
11
|
@define_ui_block ||= block
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def order
|
15
15
|
@order ||= []
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# sometimes order has duplicates. So this is all unique
|
19
19
|
# defined views in order of definition, or as specified.
|
20
20
|
def views
|
21
21
|
order.uniq
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def []( view_name )
|
25
25
|
order.find do |view|
|
26
26
|
view.name =~ /#{view_name.to_s}/i
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Handle situations where the array passed to
|
31
31
|
# Clevic::View.order has entity_class
|
32
32
|
# objects in it. In other words, if there is one, pass back it's
|
@@ -40,7 +40,7 @@ module Clevic
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def entity_class( *args )
|
45
45
|
if args.size == 0
|
46
46
|
@entity_class || raise( "entity_class not specified for #{name}" )
|
@@ -48,11 +48,11 @@ module Clevic
|
|
48
48
|
self.entity_class = args.first
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def entity_class=( some_class )
|
53
53
|
@entity_class = some_class
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def widget_name( *args )
|
57
57
|
if args.size == 0
|
58
58
|
# the class name by default
|
@@ -62,7 +62,7 @@ module Clevic
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
# args can be anything that has a writer method. Often this
|
67
67
|
# will be entity_class
|
68
68
|
# block contains the ModelBuilder DSL
|
@@ -74,33 +74,33 @@ module Clevic
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
# use block from constructor, or class ui block from eg Clevic::Record
|
79
79
|
def define_ui_block
|
80
80
|
@define_ui_block || self.class.define_ui_block
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
# For descendants to override easily
|
84
84
|
def entity_class
|
85
85
|
@entity_class || self.class.entity_class
|
86
86
|
end
|
87
87
|
attr_writer :entity_class
|
88
|
-
|
88
|
+
|
89
89
|
# The title to display, eg in a tab
|
90
90
|
def title
|
91
91
|
@title || self.class.name
|
92
92
|
end
|
93
93
|
attr_writer :title
|
94
|
-
|
94
|
+
|
95
95
|
def fields
|
96
96
|
@fields ||= define_ui.fields
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
# used by the framework-specific code to name widgets
|
100
100
|
def widget_name
|
101
101
|
@widget_name || self.class.widget_name
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
def model_builder( value = nil, &block )
|
105
105
|
if value.nil?
|
106
106
|
@model_builder ||= ModelBuilder.new( self )
|
@@ -110,7 +110,7 @@ module Clevic
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
attr_writer :model_builder
|
113
|
-
|
113
|
+
|
114
114
|
# return a default UI constructed from model metadata
|
115
115
|
def define_ui
|
116
116
|
if define_ui_block.nil?
|
@@ -123,23 +123,23 @@ module Clevic
|
|
123
123
|
model_builder( &define_ui_block )
|
124
124
|
end
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
# callback for view/model specific actions
|
128
128
|
def define_actions( table_view, action_builder )
|
129
129
|
end
|
130
|
-
|
130
|
+
|
131
131
|
# callback for notify
|
132
132
|
def notify_field( table_view, model_index )
|
133
133
|
ndc = model_index.field.notify_data_changed
|
134
134
|
case ndc
|
135
135
|
when Proc
|
136
136
|
ndc.call( self, table_view, model_index )
|
137
|
-
|
137
|
+
|
138
138
|
when Symbol
|
139
139
|
send( ndc, table_view, model_index )
|
140
140
|
end
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
# Define data changed events. Default is to call notify_data_changed
|
144
144
|
# for each field in the rectangular area defined by top_left and bottom_right
|
145
145
|
# (which are include Clevic::TableIndex)
|
@@ -157,10 +157,10 @@ module Clevic
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
160
|
-
|
160
|
+
|
161
161
|
# callback for key presses
|
162
162
|
def notify_key_press( table_view, key_press_event, current_model_index )
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
end
|
166
166
|
end
|
data/models/accounts_models.rb
CHANGED
@@ -16,9 +16,9 @@ db.test_connection
|
|
16
16
|
class Entry < Sequel::Model
|
17
17
|
many_to_one :debit, :class_name => 'Account', :key => :debit_id
|
18
18
|
many_to_one :credit, :class_name => 'Account', :key => :credit_id
|
19
|
-
|
19
|
+
|
20
20
|
include Clevic::Record
|
21
|
-
|
21
|
+
|
22
22
|
define_ui do
|
23
23
|
plain :date, :sample => '88-WWW-99'
|
24
24
|
distinct :supplier do |f|
|
@@ -28,7 +28,7 @@ class Entry < Sequel::Model
|
|
28
28
|
f.notify_data_changed = lambda do |entity_view, table_view, model_index|
|
29
29
|
if model_index.entity.credit.nil? && model_index.entity.debit.nil?
|
30
30
|
entity_view.update_from_description( model_index )
|
31
|
-
|
31
|
+
|
32
32
|
# move edit cursor to amount field
|
33
33
|
table_view.selection_model.clear
|
34
34
|
table_view.override_next_index( model_index.choppy( :column => :amount ) )
|
@@ -43,10 +43,10 @@ class Entry < Sequel::Model
|
|
43
43
|
plain :cheque_number
|
44
44
|
plain :active, :sample => 'WW'
|
45
45
|
plain :vat, :label => 'VAT', :sample => 'WW', :tooltip => 'Does this include VAT?'
|
46
|
-
|
46
|
+
|
47
47
|
dataset.order( :date, :id )
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# Copy the values for the credit and debit fields
|
51
51
|
# from the previous similar entry with a similar description
|
52
52
|
def self.update_from_description( current_index )
|
@@ -56,13 +56,13 @@ class Entry < Sequel::Model
|
|
56
56
|
filter( current_index.attribute.to_sym => current_index.attribute_value ). \
|
57
57
|
order( :date.desc ). \
|
58
58
|
first
|
59
|
-
|
59
|
+
|
60
60
|
if similar != nil
|
61
61
|
# set the values
|
62
62
|
current_index.entity.debit = similar.debit
|
63
63
|
current_index.entity.credit = similar.credit
|
64
64
|
current_index.entity.category = similar.category
|
65
|
-
|
65
|
+
|
66
66
|
# emit signal to that whole row has changed
|
67
67
|
current_index.model.data_changed do |change|
|
68
68
|
change.top_left = current_index.choppy( :column => 0 )
|
@@ -75,9 +75,9 @@ end
|
|
75
75
|
class Account < Sequel::Model
|
76
76
|
one_to_many :debits, :class_name => 'Entry', :key => :debit_id, :order => :date
|
77
77
|
one_to_many :credits, :class_name => 'Entry', :key => :credit_id, :order => :date
|
78
|
-
|
78
|
+
|
79
79
|
include Clevic::Record
|
80
|
-
|
80
|
+
|
81
81
|
# define how fields are displayed
|
82
82
|
define_ui do
|
83
83
|
plain :name
|
@@ -86,7 +86,7 @@ class Account < Sequel::Model
|
|
86
86
|
plain :pastel_number, :alignment => :right, :label => 'Pastel'
|
87
87
|
plain :fringe, :format => "%.1f"
|
88
88
|
plain :active
|
89
|
-
|
89
|
+
|
90
90
|
dataset.order( :name, :account_type )
|
91
91
|
end
|
92
92
|
end
|
data/models/examples.rb
CHANGED
@@ -31,10 +31,10 @@ See also Clevic::ModelBuilder and Clevic::Field
|
|
31
31
|
|
32
32
|
== Work hours database using Sqlite
|
33
33
|
:include:models/times_sqlite_models.rb
|
34
|
-
|
34
|
+
|
35
35
|
== Work hours database using Postgres
|
36
36
|
:include:models/times_psql_models.rb
|
37
|
-
|
37
|
+
|
38
38
|
== Work hours database using ActiveRecord style models
|
39
39
|
:include:models/times_ar_style_models.rb
|
40
40
|
|
data/models/times_models.rb
CHANGED
@@ -11,7 +11,7 @@ class Entry < Sequel::Model
|
|
11
11
|
many_to_one :invoice
|
12
12
|
many_to_one :activity
|
13
13
|
many_to_one :project
|
14
|
-
|
14
|
+
|
15
15
|
# spans of time more than 8 ours are coloured violet
|
16
16
|
# because they're often the result of typos.
|
17
17
|
def time_color
|
@@ -19,21 +19,21 @@ class Entry < Sequel::Model
|
|
19
19
|
# 8 hours
|
20
20
|
'darkviolet' if self.end - start > 8.hours
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# tooltip for spans of time > 8 hours
|
24
24
|
def time_tooltip
|
25
25
|
return if self.end.nil? || start.nil?
|
26
26
|
'Time interval greater than 8 hours' if self.end - start > 8.hours
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
define_ui do
|
30
30
|
plain :date, :sample => '28-WWW-08'
|
31
|
-
|
31
|
+
|
32
32
|
# The project field
|
33
33
|
relational :project do |field|
|
34
34
|
field.display = :project
|
35
35
|
field.dataset.filter( :active => true ).order{ lower(project) }
|
36
|
-
|
36
|
+
|
37
37
|
# handle data changed events. In this case,
|
38
38
|
# auto-fill-in the invoice field.
|
39
39
|
field.notify_data_changed do |entity_view, table_view, model_index|
|
@@ -45,31 +45,31 @@ class Entry < Sequel::Model
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
relational :invoice do |f|
|
50
50
|
f.display 'invoice_number'
|
51
51
|
f.dataset.filter( :status => 'not sent' ).order( :invoice_number )
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# call time_color method for foreground color value
|
55
55
|
plain :start, :foreground => :time_color, :tooltip => :time_tooltip
|
56
|
-
|
56
|
+
|
57
57
|
# another way to call time_color method for foreground color value
|
58
58
|
plain :end, :foreground => lambda{|x| x.time_color}, :tooltip => :time_tooltip
|
59
|
-
|
59
|
+
|
60
60
|
# multiline text
|
61
61
|
text :description, :sample => 'This is a long string designed to hold lots of data and description'
|
62
|
-
|
62
|
+
|
63
63
|
relational :activity do |f|
|
64
64
|
f.display 'activity'
|
65
65
|
f.sample 'Troubleshooting'
|
66
66
|
f.dataset.filter( :active => true ).order{ lower(activity) }
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
distinct :module, :tooltip => 'Module or sub-project'
|
70
70
|
plain :charge, :tooltip => 'Is this time billable?'
|
71
71
|
distinct :person, :default => 'John', :tooltip => 'The person who did the work'
|
72
|
-
|
72
|
+
|
73
73
|
dataset.order( :date, :start, :id )
|
74
74
|
end
|
75
75
|
|
@@ -77,11 +77,11 @@ class Entry < Sequel::Model
|
|
77
77
|
action_builder.action :smart_copy, 'Smart Copy', :shortcut => 'Ctrl+"' do
|
78
78
|
smart_copy( view )
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
action_builder.action :invoice_from_project, 'Invoice from Project', :shortcut => 'Ctrl+Shift+I' do
|
82
82
|
invoice_from_project( view, view.current_index ) do
|
83
83
|
# execute the block if the invoice is changed
|
84
|
-
|
84
|
+
|
85
85
|
# save this before selection model is cleared
|
86
86
|
current_index = view.current_index
|
87
87
|
view.selection_model.clear
|
@@ -89,35 +89,35 @@ class Entry < Sequel::Model
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
# do a smart copy from the previous line
|
94
94
|
def self.smart_copy( view )
|
95
95
|
view.sanity_check_read_only
|
96
96
|
view.sanity_check_ditto
|
97
|
-
|
97
|
+
|
98
98
|
# need a reference to current_index here, because selection_model.clear will
|
99
99
|
# invalidate view.current_index. And anyway, its shorter and easier to read.
|
100
100
|
current_index = view.current_index
|
101
101
|
if current_index.row >= 1
|
102
102
|
# fetch previous item
|
103
103
|
previous_item = view.model.collection[current_index.row - 1]
|
104
|
-
|
104
|
+
|
105
105
|
# copy the relevant fields
|
106
106
|
current_index.entity.date = previous_item.date if current_index.entity.date.nil?
|
107
107
|
# depends on previous line
|
108
108
|
current_index.entity.start = previous_item.end if current_index.entity.date == previous_item.date
|
109
|
-
|
109
|
+
|
110
110
|
# copy rest of fields
|
111
111
|
[:project, :invoice, :activity, :module, :charge, :person].each do |attr|
|
112
112
|
current_index.entity.send( "#{attr.to_s}=", previous_item.send( attr ) )
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
# tell view to update
|
116
116
|
view.model.data_changed do |change|
|
117
117
|
change.top_left = current_index.choppy( :column => 0 )
|
118
118
|
change.bottom_right = current_index.choppy( :column => view.model.fields.size - 1 )
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
# move to the first empty time field
|
122
122
|
next_field =
|
123
123
|
if current_index.entity.start.nil?
|
@@ -125,7 +125,7 @@ class Entry < Sequel::Model
|
|
125
125
|
else
|
126
126
|
:end
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
# next cursor location
|
130
130
|
view.selection_model.clear
|
131
131
|
view.current_index = current_index.choppy( :column => next_field )
|
@@ -142,10 +142,10 @@ class Entry < Sequel::Model
|
|
142
142
|
unless invoice.nil?
|
143
143
|
# make a reference to the invoice
|
144
144
|
current_index.entity.invoice = invoice
|
145
|
-
|
145
|
+
|
146
146
|
# update view from top_left to bottom_right
|
147
147
|
table_view.model.data_changed( current_index.choppy( :column => :invoice ) )
|
148
|
-
|
148
|
+
|
149
149
|
unless block.nil?
|
150
150
|
if block.arity == 1
|
151
151
|
block.call( invoice )
|
@@ -156,14 +156,14 @@ class Entry < Sequel::Model
|
|
156
156
|
end
|
157
157
|
end
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
end
|
161
161
|
|
162
162
|
class Invoice < Sequel::Model
|
163
163
|
include Clevic::Record
|
164
|
-
|
164
|
+
|
165
165
|
one_to_many :entries
|
166
|
-
|
166
|
+
|
167
167
|
define_ui do
|
168
168
|
plain :date
|
169
169
|
distinct :client
|
@@ -173,7 +173,7 @@ class Invoice < Sequel::Model
|
|
173
173
|
plain :quote_date, :format => '%d-%b-%y', :edit_format => '%d-%b-%Y', :tooltip => 'the date and time when the quote was supplied', :default => lambda{|x| DateTime.now}
|
174
174
|
plain :quote_amount
|
175
175
|
plain :description
|
176
|
-
|
176
|
+
|
177
177
|
dataset.order( :invoice_number )
|
178
178
|
end
|
179
179
|
end
|
@@ -182,17 +182,17 @@ class Project < Sequel::Model
|
|
182
182
|
one_to_many :entries
|
183
183
|
|
184
184
|
include Clevic::Record
|
185
|
-
|
185
|
+
|
186
186
|
define_ui do
|
187
187
|
plain :project
|
188
188
|
plain :description
|
189
189
|
distinct :client
|
190
190
|
plain :rate
|
191
191
|
plain :active
|
192
|
-
|
192
|
+
|
193
193
|
dataset.order( :project )
|
194
194
|
end
|
195
|
-
|
195
|
+
|
196
196
|
# Return the latest invoice for this project
|
197
197
|
# Not part of the UI.
|
198
198
|
def latest_invoice
|
@@ -207,12 +207,12 @@ class Activity < Sequel::Model
|
|
207
207
|
one_to_many :entries
|
208
208
|
|
209
209
|
include Clevic::Record
|
210
|
-
|
210
|
+
|
211
211
|
# define how fields are displayed
|
212
212
|
define_ui do
|
213
213
|
plain :activity
|
214
214
|
plain :active
|
215
|
-
|
215
|
+
|
216
216
|
dataset.order( :activity )
|
217
217
|
end
|
218
218
|
end
|