osm 1.0.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +30 -0
- data/README.md +17 -4
- data/lib/osm.rb +4 -1
- data/lib/osm/budget.rb +136 -0
- data/lib/osm/event.rb +12 -5
- data/lib/osm/giftaid.rb +292 -0
- data/lib/osm/invoice.rb +425 -0
- data/lib/osm/member.rb +24 -8
- data/lib/osm/model.rb +4 -2
- data/lib/osm/register.rb +9 -11
- data/lib/osm/section.rb +36 -19
- data/lib/osm/sms.rb +6 -5
- data/lib/osm/term.rb +2 -2
- data/spec/osm/budget_spec.rb +205 -0
- data/spec/osm/event_spec.rb +12 -0
- data/spec/osm/giftaid_spec.rb +224 -0
- data/spec/osm/invoice_spec.rb +699 -0
- data/spec/osm/member_spec.rb +21 -1
- data/spec/osm/section_spec.rb +16 -1
- data/spec/osm/sms_spec.rb +90 -3
- data/spec/osm/term_spec.rb +1 -1
- data/version.rb +1 -1
- metadata +8 -2
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
## Version 1.2.0
|
2
|
+
|
3
|
+
* Trying to fetch the currrent Term for a Section which doesn;t have one now raises an Osm::Error::NoCurrentTerm instead of an Osm::Error
|
4
|
+
* Add personal details options to Section:
|
5
|
+
* myscout\_details attribute [Boolean] for whether personal details are enabled
|
6
|
+
* myscout\_details\_expires attribute [Date] for expiry date of subscription
|
7
|
+
* myscout\_details\_email\_changes\_to attribute [String] where to send update emails to
|
8
|
+
* Osm::Member
|
9
|
+
* myscout\_link method now accepts :details to get a link to the "Perosnal details" page
|
10
|
+
* myscout\_link method can now link to a specific event by also passing in the id (optional 3rd parameter)
|
11
|
+
* Addition of myscout\_link\_key method to get the member's unique key for use in myscout links
|
12
|
+
* Osm::Section
|
13
|
+
* subscription\_level\_name method is marked as depricated, ready for removal in version 2.0 -> replace with Osm::SUBSCRIPTION\_LEVEL\_NAMES[section.subscription\_level]
|
14
|
+
* myscout\_programme\_show attribute added - how much of the programme do parents see?
|
15
|
+
* Addition of two new constants:
|
16
|
+
* Osm::SUBSCRIPTION\_LEVEL\_NAMES - an Array of Strings containing the human name of each subscription level (starts with "Unknown" to make indexing work nicely)
|
17
|
+
* Osm::SUBSCRIPTION\_LEVELS - an Array of Symbols for each level (starts with nil to make indexing work nicely)
|
18
|
+
* Register - get\_structure now only includes dates
|
19
|
+
* Addition of attendance\_reminder attribute to event
|
20
|
+
* Abillity to send Sms messages to multiple numbers
|
21
|
+
* Add Gift Aid:
|
22
|
+
* Get Payments
|
23
|
+
* Update Payments
|
24
|
+
* Get Payment/Member Data
|
25
|
+
* Update Payment/Member Data
|
26
|
+
* Add Finances:
|
27
|
+
* Budget (Get, Add, Update, Delete)
|
28
|
+
* Invoices (Get, Add, Update, Delete)
|
29
|
+
* Invoice Items (Get, Add, Update, Delete)
|
30
|
+
|
1
31
|
## Version 1.0.6
|
2
32
|
|
3
33
|
* Add badge\_links to Meeting
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ Use the [Online Scout Manager](https://www.onlinescoutmanager.co.uk) API.
|
|
32
32
|
Add to your Gemfile and run the `bundle` command to install it.
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
gem 'osm', '~> 1.
|
35
|
+
gem 'osm', '~> 1.2'
|
36
36
|
```
|
37
37
|
|
38
38
|
Configure the gem during the initalization of the app (e.g. if using rails then config/initializers/osm.rb would look like):
|
@@ -90,6 +90,7 @@ however it should be noted that when the OSM API adds a feature it can be diffic
|
|
90
90
|
* Details for each badge
|
91
91
|
* Requirements for evening
|
92
92
|
* Badge stock levels
|
93
|
+
* Budgets (Gold required)
|
93
94
|
* Due Badges
|
94
95
|
* Evening
|
95
96
|
* Event (Silver required)
|
@@ -98,7 +99,10 @@ however it should be noted that when the OSM API adds a feature it can be diffic
|
|
98
99
|
* Event Attendance (Silver required)
|
99
100
|
* Flexi Record Data
|
100
101
|
* Flexi Record Structure
|
102
|
+
* Gift Aid Data
|
103
|
+
* Gift Aid Structure
|
101
104
|
* Groupings (e.g. Sixes, Patrols)
|
105
|
+
* Invoices (Gold required)
|
102
106
|
* Members
|
103
107
|
* Notepad
|
104
108
|
* Notepads
|
@@ -116,39 +120,48 @@ however it should be noted that when the OSM API adds a feature it can be diffic
|
|
116
120
|
* Activity
|
117
121
|
* Badges (Silver required for activity, Bronze for core, challenge and staged):
|
118
122
|
* Which requirements each member has met
|
123
|
+
* Budget (Gold required)
|
119
124
|
* Evening
|
120
125
|
* Event (Silver required)
|
121
126
|
* Event Attendance (Silver required)
|
122
127
|
* Event Column (Silver required)
|
123
128
|
* Flexi Record Column
|
124
129
|
* Flexi Record Data
|
130
|
+
* Gift Aid Payment
|
125
131
|
* Grouping
|
132
|
+
* Invoices (Gold required)
|
126
133
|
* Member
|
127
134
|
* Register Attendance
|
128
135
|
|
129
136
|
### Create
|
137
|
+
* Budget (Gold required)
|
130
138
|
* Evening
|
131
139
|
* Event (Silver required)
|
132
140
|
* Event Column (Silver required)
|
133
|
-
* Member
|
134
141
|
* Flexi Record Column
|
142
|
+
* Gift Aid Payment
|
143
|
+
* Invoices (Gold required)
|
144
|
+
* Member
|
135
145
|
|
136
146
|
### Delete
|
147
|
+
* Budget (Gold required)
|
137
148
|
* Evening
|
138
149
|
* Event (Silver required)
|
139
150
|
* Event Column (Silver required)
|
140
151
|
* Flexi Record Column
|
152
|
+
* Invoices (Gold required)
|
141
153
|
|
142
154
|
### Actions
|
143
155
|
* Authorise an app to use the API on a user's behalf
|
144
156
|
* Add activity to programme
|
145
157
|
* Send an SMS to member(s)
|
146
158
|
|
159
|
+
## Parts not/never supported
|
160
|
+
* Campsite Directory
|
147
161
|
|
148
162
|
## Parts of the OSM API currently NOT supported (may not be an exhaustive list):
|
149
163
|
|
150
164
|
See the [Roadmap page in the wiki](https://github.com/robertgauld/osm/wiki/Roadmap) for more details.
|
151
165
|
|
152
|
-
* Gift aid (Everything) (Gold required) [issue 75]
|
153
|
-
* Finances (Everything) (Gold required) [issues 76 & 77]]
|
154
166
|
* MyScout (Everything)
|
167
|
+
* Adult Section Specific Stuff
|
data/lib/osm.rb
CHANGED
@@ -11,9 +11,10 @@ module Osm
|
|
11
11
|
# Declare exceptions
|
12
12
|
class Error < Exception; end
|
13
13
|
class ConnectionError < Error; end
|
14
|
-
class Forbidden < Error; end
|
14
|
+
class Forbidden < Osm::Error; end
|
15
15
|
class ArgumentIsInvalid < ArgumentError; end
|
16
16
|
class ObjectIsInvalid < Error; end
|
17
|
+
class Osm::Error::NoCurrentTerm < Osm::Error; end
|
17
18
|
|
18
19
|
private
|
19
20
|
# Set constants
|
@@ -26,6 +27,8 @@ module Osm
|
|
26
27
|
OSM_DATETIME_FORMAT_HUMAN = '%d/%m/%Y %H:%M:%S'
|
27
28
|
OSM_TIME_REGEX = /\A(?:[0-1][0-9]|2[0-3]):[0-5][0-9]\Z/
|
28
29
|
OSM_DATE_REGEX = /\A(?:[1-9]\d{3}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1]))|(?:(?:0?[1-9]|[1-2][0-9]|3[0-1])\/(?:0?[1-9]|1[0-2])\/(?:\d{2}|[1-9]\d{3}))\Z/
|
30
|
+
SUBSCRIPTION_LEVEL_NAMES = ['Unknown', 'Bronze', 'Silver', 'Gold', 'Gold+']
|
31
|
+
SUBSCRIPTION_LEVELS = [nil, :bronze, :silver, :gold, :gold_plus]
|
29
32
|
end
|
30
33
|
|
31
34
|
# Require file for this gem
|
data/lib/osm/budget.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
module Osm
|
2
|
+
|
3
|
+
class Budget < Osm::Model
|
4
|
+
# @!attribute [rw] id
|
5
|
+
# @return [Fixnum] The OSM ID for the budget
|
6
|
+
# @!attribute [rw] section_id
|
7
|
+
# @return [Fixnum] The OSM ID for the section the budget belongs to
|
8
|
+
# @!attribute [rw] name
|
9
|
+
# @return [String] The name of the budget
|
10
|
+
|
11
|
+
attribute :id, :type => Integer
|
12
|
+
attribute :section_id, :type => Integer
|
13
|
+
attribute :name, :type => String
|
14
|
+
|
15
|
+
attr_accessible :id, :section_id, :name
|
16
|
+
|
17
|
+
validates_numericality_of :id, :only_integer=>true, :greater_than=>0, :unless => Proc.new { |r| r.id.nil? }
|
18
|
+
validates_numericality_of :section_id, :only_integer=>true, :greater_than=>0
|
19
|
+
validates_presence_of :name
|
20
|
+
|
21
|
+
|
22
|
+
# @!method initialize
|
23
|
+
# Initialize a new Budget
|
24
|
+
# @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
|
25
|
+
|
26
|
+
|
27
|
+
# Get budgets for a section
|
28
|
+
# @param [Osm::Api] api The api to use to make the request
|
29
|
+
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the structure for
|
30
|
+
# @!macro options_get
|
31
|
+
# @return [Array<Osm::Budget>] representing the donations made
|
32
|
+
def self.get_for_section(api, section, options={})
|
33
|
+
Osm::Model.require_ability_to(api, :read, :finance, section, options)
|
34
|
+
section_id = section.to_i
|
35
|
+
cache_key = ['budgets', section_id]
|
36
|
+
|
37
|
+
if !options[:no_cache] && Osm::Model.cache_exist?(api, cache_key)
|
38
|
+
return Osm::Model.cache_read(api, cache_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
data = api.perform_query("finances.php?action=getCategories§ionid=#{section_id}")
|
42
|
+
|
43
|
+
budgets = []
|
44
|
+
data = data['items']
|
45
|
+
if data.is_a?(Array)
|
46
|
+
data.each do |budget|
|
47
|
+
budgets.push Budget.new(
|
48
|
+
:id => Osm::to_i_or_nil(budget['categoryid']),
|
49
|
+
:section_id => Osm::to_i_or_nil(budget['sectionid']),
|
50
|
+
:name => budget['name'],
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Osm::Model.cache_write(api, cache_key, budgets) unless budgets.nil?
|
56
|
+
return budgets
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Create the budget in OSM
|
61
|
+
# @param [Osm::Api] api The api to use to make the request
|
62
|
+
# @return [Boolean] whether the budget was created
|
63
|
+
# @raise [Osm::ObjectIsInvalid] If the Budget is invalid
|
64
|
+
# @raise [Osm::Error] If the budget already exists in OSM
|
65
|
+
def create(api)
|
66
|
+
raise Osm::Error, 'the budget already exists in OSM' unless id.nil?
|
67
|
+
raise Osm::ObjectIsInvalid, 'budget is invalid' unless valid?
|
68
|
+
Osm::Model.require_ability_to(api, :write, :finance, section_id)
|
69
|
+
|
70
|
+
data = api.perform_query("finances.php?action=addCategory§ionid=#{section_id}")
|
71
|
+
if data.is_a?(Hash) && data['ok'].eql?(true)
|
72
|
+
# The cached budgets for the section will be out of date - remove them
|
73
|
+
cache_delete(api, ['budgets', section_id])
|
74
|
+
budgets = Budget.get_for_section(api, section_id, {:no_cache => true})
|
75
|
+
budget = budgets.sort.select{ |b| b.name.eql?('** Unnamed **') }[-1]
|
76
|
+
return false if budget.nil? # a new blank budget was NOT created
|
77
|
+
budget.name = name
|
78
|
+
if budget.update(api)
|
79
|
+
self.id = budget.id
|
80
|
+
return true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
|
86
|
+
# Update budget in OSM
|
87
|
+
# @param [Osm::Api] api The api to use to make the request
|
88
|
+
# @return [Boolean] whether the budget was updated
|
89
|
+
# @raise [Osm::ObjectIsInvalid] If the Budget is invalid
|
90
|
+
def update(api)
|
91
|
+
raise Osm::ObjectIsInvalid, 'budget is invalid' unless valid?
|
92
|
+
Osm::Model.require_ability_to(api, :write, :finance, section_id)
|
93
|
+
|
94
|
+
data = api.perform_query("finances.php?action=updateCategory§ionid=#{section_id}", {
|
95
|
+
'categoryid' => id,
|
96
|
+
'column' => 'name',
|
97
|
+
'value' => name,
|
98
|
+
'section_id' => section_id,
|
99
|
+
'row' => 0,
|
100
|
+
})
|
101
|
+
if (data.is_a?(Hash) && data['ok'].eql?(true))
|
102
|
+
# The cached budgets for the section will be out of date - remove them
|
103
|
+
cache_delete(api, ['budgets', section_id])
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
# Delete budget from OSM
|
110
|
+
# @param [Osm::Api] api The api to use to make the request
|
111
|
+
# @return [Boolean] whether the budget was deleted
|
112
|
+
def delete(api)
|
113
|
+
Osm::Model.require_ability_to(api, :write, :finance, section_id)
|
114
|
+
|
115
|
+
data = api.perform_query("finances.php?action=deleteCategory§ionid=#{section_id}", {
|
116
|
+
'categoryid' => id,
|
117
|
+
})
|
118
|
+
if (data.is_a?(Hash) && data['ok'].eql?(true))
|
119
|
+
# The cached budgets for the section will be out of date - remove them
|
120
|
+
cache_delete(api, ['budgets', section_id])
|
121
|
+
return true
|
122
|
+
end
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# Compare Budget based on section_id then name
|
128
|
+
def <=>(another)
|
129
|
+
result = self.section_id <=> another.try(:section_id)
|
130
|
+
result = self.name <=> another.try(:name) if result == 0
|
131
|
+
return result
|
132
|
+
end
|
133
|
+
|
134
|
+
end # Class Budget
|
135
|
+
|
136
|
+
end
|
data/lib/osm/event.rb
CHANGED
@@ -37,6 +37,8 @@ module Osm
|
|
37
37
|
# @return [Fixnum] the maximum number of people who can attend the event (0 = no limit)
|
38
38
|
# @!attendance [rw] attendance_limit_includes_leaders
|
39
39
|
# @return [Boolean] whether the attendance limit includes leaders
|
40
|
+
# @!attribute [rw] attendance_reminder
|
41
|
+
# @return [Fixnum] how many days before the event to send a reminder to those attending (0 (off), 1, 3, 7, 14, 21, 28)
|
40
42
|
# @!attribute [rw] allow_booking
|
41
43
|
# @return [Boolean] whether booking is allowed through My.SCOUT
|
42
44
|
|
@@ -57,11 +59,13 @@ module Osm
|
|
57
59
|
attribute :reminders, :type => Boolean, :default => true
|
58
60
|
attribute :attendance_limit, :type => Integer, :default => 0
|
59
61
|
attribute :attendance_limit_includes_leaders, :type => Boolean, :default => false
|
62
|
+
attribute :attendance_reminder, :type => Integer, :default => 0
|
60
63
|
attribute :allow_booking, :type => Boolean, :default => true
|
61
64
|
|
62
65
|
attr_accessible :id, :section_id, :name, :start, :finish, :cost, :location, :notes, :archived,
|
63
66
|
:fields, :columns, :notepad, :public_notepad, :confirm_by_date, :allow_changes,
|
64
|
-
:reminders, :attendance_limit, :attendance_limit_includes_leaders,
|
67
|
+
:reminders, :attendance_limit, :attendance_limit_includes_leaders,
|
68
|
+
:attendance_reminder, :allow_booking
|
65
69
|
|
66
70
|
validates_numericality_of :id, :only_integer=>true, :greater_than=>0, :allow_nil => true
|
67
71
|
validates_numericality_of :section_id, :only_integer=>true, :greater_than=>0
|
@@ -72,6 +76,7 @@ module Osm
|
|
72
76
|
validates_inclusion_of :reminders, :in => [true, false]
|
73
77
|
validates_inclusion_of :attendance_limit_includes_leaders, :in => [true, false]
|
74
78
|
validates_inclusion_of :allow_booking, :in => [true, false]
|
79
|
+
validates_inclusion_of :attendance_reminder, :in => [0, 1, 3, 7, 14, 21, 28]
|
75
80
|
validates_format_of :cost, :with => /\A(?:\d+\.\d{2}|TBC)\Z/
|
76
81
|
|
77
82
|
|
@@ -84,7 +89,7 @@ module Osm
|
|
84
89
|
# @param [Osm::Api] api The api to use to make the request
|
85
90
|
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the events for
|
86
91
|
# @!macro options_get
|
87
|
-
# @option options [Boolean] :include_archived (optional) if true then archived
|
92
|
+
# @option options [Boolean] :include_archived (optional) if true then archived events will also be returned
|
88
93
|
# @return [Array<Osm::Event>]
|
89
94
|
def self.get_for_section(api, section, options={})
|
90
95
|
require_ability_to(api, :read, :events, section, options)
|
@@ -122,13 +127,13 @@ module Osm
|
|
122
127
|
# Get an event
|
123
128
|
# @param [Osm::Api] api The api to use to make the request
|
124
129
|
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the events for
|
125
|
-
# @param [Fixnum] event_id The id of the event to get
|
130
|
+
# @param [Fixnum, #to_i] event_id The id of the event to get
|
126
131
|
# @!macro options_get
|
127
|
-
# @option options [Boolean] :include_archived (optional) if true then archived activities will also be returned
|
128
132
|
# @return [Osm::Event, nil] the event (or nil if it couldn't be found
|
129
133
|
def self.get(api, section, event_id, options={})
|
130
134
|
require_ability_to(api, :read, :events, section, options)
|
131
135
|
section_id = section.to_i
|
136
|
+
event_id = event_id.to_i
|
132
137
|
cache_key = ['event', event_id]
|
133
138
|
|
134
139
|
if !options[:no_cache] && cache_exist?(api, cache_key)
|
@@ -162,6 +167,7 @@ module Osm
|
|
162
167
|
'allowChanges' => event.allow_changes ? 'true' : 'false',
|
163
168
|
'disablereminders' => !event.reminders ? 'true' : 'false',
|
164
169
|
'attendancelimit' => event.attendance_limit,
|
170
|
+
'attendancereminder' => event.attendance_reminder,
|
165
171
|
'limitincludesleaders' => event.attendance_limit_includes_leaders ? 'true' : 'false',
|
166
172
|
'allowbooking' => event.allow_booking ? 'true' : 'false',
|
167
173
|
})
|
@@ -200,6 +206,7 @@ module Osm
|
|
200
206
|
'allowChanges' => allow_changes ? 'true' : 'false',
|
201
207
|
'disablereminders' => !reminders ? 'true' : 'false',
|
202
208
|
'attendancelimit' => attendance_limit,
|
209
|
+
'attendancereminder' => attendance_reminder,
|
203
210
|
'limitincludesleaders' => attendance_limit_includes_leaders ? 'true' : 'false',
|
204
211
|
'allowbooking' => allow_booking ? 'true' : 'false',
|
205
212
|
})
|
@@ -392,6 +399,7 @@ module Osm
|
|
392
399
|
:reminders => !event_data['disablereminders'].eql?('1'),
|
393
400
|
:attendance_limit => event_data['attendancelimit'].to_i,
|
394
401
|
:attendance_limit_includes_leaders => event_data['limitincludesleaders'].eql?('1'),
|
402
|
+
:attendance_reminder => event_data['attendancereminder'].to_i,
|
395
403
|
:allow_booking => event_data['allowbooking'].eql?('1'),
|
396
404
|
)
|
397
405
|
|
@@ -403,7 +411,6 @@ module Osm
|
|
403
411
|
end
|
404
412
|
event.columns = columns
|
405
413
|
return event
|
406
|
-
|
407
414
|
end
|
408
415
|
|
409
416
|
|
data/lib/osm/giftaid.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
module Osm
|
2
|
+
|
3
|
+
class GiftAid
|
4
|
+
|
5
|
+
# Get donations
|
6
|
+
# @param [Osm::Api] api The api to use to make the request
|
7
|
+
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the structure for
|
8
|
+
# @param [Osm::Term, Fixnum, #to_i, nil] term The term (or its ID) to get the structure for, passing nil causes the current term to be used
|
9
|
+
# @!macro options_get
|
10
|
+
# @return [Array<Osm::GiftAid::Donation>] representing the donations made
|
11
|
+
def self.get_donations(api, section, term=nil, options={})
|
12
|
+
Osm::Model.require_ability_to(api, :read, :finance, section, options)
|
13
|
+
section_id = section.to_i
|
14
|
+
term_id = term.nil? ? Osm::Term.get_current_term_for_section(api, section).id : term.to_i
|
15
|
+
cache_key = ['gift_aid_donations', section_id, term_id]
|
16
|
+
|
17
|
+
if !options[:no_cache] && Osm::Model.cache_exist?(api, cache_key)
|
18
|
+
return Osm::Model.cache_read(api, cache_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
data = api.perform_query("giftaid.php?action=getStructure§ionid=#{section_id}&termid=#{term_id}")
|
22
|
+
|
23
|
+
structure = []
|
24
|
+
if data.is_a?(Array)
|
25
|
+
data = (data.size == 2) ? data[1] : []
|
26
|
+
if data.is_a?(Hash) && data['rows'].is_a?(Array)
|
27
|
+
data['rows'].each do |row|
|
28
|
+
structure.push Donation.new(
|
29
|
+
:donation_date => Osm::parse_date(row['field']),
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Osm::Model.cache_write(api, cache_key, structure) unless structure.nil?
|
36
|
+
return structure
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get donation data
|
40
|
+
# @param [Osm::Api] api The api to use to make the request
|
41
|
+
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the register for
|
42
|
+
# @param [Osm::Term, Fixnum, #to_i, nil] term The term (or its ID) to get the register for, passing nil causes the current term to be used
|
43
|
+
# @!macro options_get
|
44
|
+
# @return [Array<Osm::GiftAid::Data>] representing the donations of each member
|
45
|
+
def self.get_data(api, section, term=nil, options={})
|
46
|
+
Osm::Model.require_ability_to(api, :read, :finance, section, options)
|
47
|
+
section_id = section.to_i
|
48
|
+
term_id = term.nil? ? Osm::Term.get_current_term_for_section(api, section).id : term.to_i
|
49
|
+
cache_key = ['gift_aid_data', section_id, term_id]
|
50
|
+
|
51
|
+
if !options[:no_cache] && Osm::Model.cache_exist?(api, cache_key)
|
52
|
+
return Osm::Model.cache_read(api, cache_key)
|
53
|
+
end
|
54
|
+
|
55
|
+
data = api.perform_query("giftaid.php?action=getGrid§ionid=#{section_id}&termid=#{term_id}")
|
56
|
+
|
57
|
+
to_return = []
|
58
|
+
if data.is_a?(Hash) && data['items'].is_a?(Array)
|
59
|
+
data = data['items']
|
60
|
+
data.each do |item|
|
61
|
+
if item.is_a?(Hash)
|
62
|
+
unless item['scoutid'].to_i < 0 # It's a total row
|
63
|
+
donations = {}
|
64
|
+
item.each do |key, value|
|
65
|
+
if key.match(Osm::OSM_DATE_REGEX)
|
66
|
+
donations[Osm::parse_date(key)] = value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
to_return.push Osm::GiftAid::Data.new(
|
70
|
+
:member_id => Osm::to_i_or_nil(item['scoutid']),
|
71
|
+
:grouping_id => Osm::to_i_or_nil(item ['patrolid']),
|
72
|
+
:section_id => section_id,
|
73
|
+
:first_name => item['firstname'],
|
74
|
+
:last_name => item['lastname'],
|
75
|
+
:tax_payer_name => item['parentname'],
|
76
|
+
:tax_payer_address => item['address'],
|
77
|
+
:tax_payer_postcode => item['postcode'],
|
78
|
+
:total => item['total'],
|
79
|
+
:donations => donations,
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
Osm::Model.cache_write(api, cache_key, to_return)
|
85
|
+
end
|
86
|
+
return to_return
|
87
|
+
end
|
88
|
+
|
89
|
+
# Update information for a donation
|
90
|
+
# @param [Hash] data
|
91
|
+
# @option data [Osm::Api] :api The api to use to make the request
|
92
|
+
# @option data [Osm::Section] :section the section to update the register for
|
93
|
+
# @option data [Osm::Term, #to_i, nil] :term The term (or its ID) to get the register for, passing nil causes the current term to be used
|
94
|
+
# @option data [Osm::Evening, DateTime, Date] :evening the evening to update the register on
|
95
|
+
# @option data [Fixnum, Array<Fixnum>, Osm::Member, Array<Osm::Member>, #to_i, Array<#to_i>] :members the members (or their ids) to update
|
96
|
+
# @option data [Date, #strftime] :donation_date the date the donation was made
|
97
|
+
# @option data [String, #to_s] :amount the donation amount
|
98
|
+
# @option data [String, #to_s] :note the description for the donation
|
99
|
+
# @return [Boolean] whether the update succedded
|
100
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:section] is missing
|
101
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:donation_date] is missing
|
102
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:amount] is missing
|
103
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:note] is missing
|
104
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:members] is missing
|
105
|
+
# @raise [Osm::ArgumentIsInvalid] If data[:api] is missing
|
106
|
+
def self.update_donation(data={})
|
107
|
+
raise Osm::ArgumentIsInvalid, ':section is missing' if data[:section].nil?
|
108
|
+
raise Osm::ArgumentIsInvalid, ':donation_date is missing' if data[:donation_date].nil?
|
109
|
+
raise Osm::ArgumentIsInvalid, ':amount is missing' if data[:amount].nil?
|
110
|
+
raise Osm::ArgumentIsInvalid, ':note is missing' if data[:note].nil?
|
111
|
+
raise Osm::ArgumentIsInvalid, ':members is missing' if data[:members].nil?
|
112
|
+
raise Osm::ArgumentIsInvalid, ':api is missing' if data[:api].nil?
|
113
|
+
api = data[:api]
|
114
|
+
Osm::Model.require_ability_to(api, :write, :finance, data[:section])
|
115
|
+
|
116
|
+
term_id = data[:term].nil? ? Osm::Term.get_current_term_for_section(api, data[:section]).id : data[:term].to_i
|
117
|
+
section_id = data[:section].to_i
|
118
|
+
|
119
|
+
data[:members] = [*data[:members]].map{ |member| member.to_i.to_s } # Make sure it's an Array of Strings
|
120
|
+
|
121
|
+
response = api.perform_query("giftaid.php?action=update§ionid=#{section_id}&termid=#{term_id}", {
|
122
|
+
'scouts' => data[:members].inspect,
|
123
|
+
'sectionid' => section_id,
|
124
|
+
'donatedate'=> data[:donation_date].strftime(Osm::OSM_DATE_FORMAT),
|
125
|
+
'amount' => data[:amount],
|
126
|
+
'notes' => data[:note],
|
127
|
+
})
|
128
|
+
|
129
|
+
# The cached donations and data will be out of date - remove them
|
130
|
+
Osm::Model.cache_delete(api, ['gift_aid_donations', section_id, term_id])
|
131
|
+
Osm::Model.cache_delete(api, ['gift_aid_data', section_id, term_id])
|
132
|
+
|
133
|
+
return response.is_a?(Array)
|
134
|
+
end
|
135
|
+
|
136
|
+
class Donation < Osm::Model
|
137
|
+
# @!attribute [rw] donation_date
|
138
|
+
# @return [Date] When the payment was made
|
139
|
+
|
140
|
+
attribute :donation_date, :type => Date
|
141
|
+
|
142
|
+
attr_accessible :note, :donation_date
|
143
|
+
|
144
|
+
validates_presence_of :donation_date
|
145
|
+
|
146
|
+
|
147
|
+
# @!method initialize
|
148
|
+
# Initialize a new RegisterField
|
149
|
+
# @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
|
150
|
+
|
151
|
+
|
152
|
+
# Compare Payment based on donation_date then note
|
153
|
+
def <=>(another)
|
154
|
+
return self.donation_date <=> another.try(:donation_date)
|
155
|
+
end
|
156
|
+
|
157
|
+
end # Class GiftAid::Donation
|
158
|
+
|
159
|
+
|
160
|
+
class Data < Osm::Model
|
161
|
+
# @!attribute [rw] member_id
|
162
|
+
# @return [Fixnum] The OSM ID for the member
|
163
|
+
# @!attribute [rw] grouping_id
|
164
|
+
# @return [Fixnum] The OSM ID for the member's grouping
|
165
|
+
# @!attribute [rw] section_id
|
166
|
+
# @return [Fixnum] The OSM ID for the member's section
|
167
|
+
# @!attribute [rw] first_name
|
168
|
+
# @return [String] The member's first name
|
169
|
+
# @!attribute [rw] last_name
|
170
|
+
# @return [String] The member's last name
|
171
|
+
# @!attribute [rw] tax_payer_name
|
172
|
+
# @return [String] The tax payer's name
|
173
|
+
# @!attribute [rw] tax_payer_address
|
174
|
+
# @return [String] The tax payer's street address
|
175
|
+
# @!attribute [rw] tax_payer_postcode
|
176
|
+
# @return [String] The tax payer's postcode
|
177
|
+
# @!attribute [rw] total
|
178
|
+
# @return [String] Total
|
179
|
+
# @!attribute [rw] donations
|
180
|
+
# @return [DirtyHashy] The data for each payment - keys are the date, values are the value of the payment
|
181
|
+
|
182
|
+
attribute :member_id, :type => Integer
|
183
|
+
attribute :grouping_id, :type => Integer
|
184
|
+
attribute :section_id, :type => Integer
|
185
|
+
attribute :first_name, :type => String
|
186
|
+
attribute :last_name, :type => String
|
187
|
+
attribute :tax_payer_name, :type => String
|
188
|
+
attribute :tax_payer_address, :type => String
|
189
|
+
attribute :tax_payer_postcode, :type => String
|
190
|
+
attribute :total, :type => String
|
191
|
+
attribute :donations, :type => Object, :default => DirtyHashy.new
|
192
|
+
|
193
|
+
attr_accessible :member_id, :first_name, :last_name, :section_id, :grouping_id, :total, :tax_payer_name, :tax_payer_address, :tax_payer_postcode, :donations
|
194
|
+
|
195
|
+
validates_numericality_of :member_id, :only_integer=>true, :greater_than=>0
|
196
|
+
validates_numericality_of :grouping_id, :only_integer=>true, :greater_than_or_equal_to=>-2
|
197
|
+
validates_numericality_of :section_id, :only_integer=>true, :greater_than=>0
|
198
|
+
validates_presence_of :first_name
|
199
|
+
validates_presence_of :last_name
|
200
|
+
|
201
|
+
validates :donations, :hash => {:key_type => Date, :value_type => String}
|
202
|
+
|
203
|
+
|
204
|
+
# @!method initialize
|
205
|
+
# Initialize a new registerData
|
206
|
+
# @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
|
207
|
+
# Override initialize to set @orig_attributes
|
208
|
+
old_initialize = instance_method(:initialize)
|
209
|
+
define_method :initialize do |*args|
|
210
|
+
ret_val = old_initialize.bind(self).call(*args)
|
211
|
+
self.donations = DirtyHashy.new(self.donations)
|
212
|
+
self.donations.clean_up!
|
213
|
+
return ret_val
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# Update data in OSM
|
218
|
+
# @param [Osm::Api] api The api to use to make the request
|
219
|
+
# @return [Boolean] whether the data was updated in OSM
|
220
|
+
# @raise [Osm::ObjectIsInvalid] If the Data is invalid
|
221
|
+
def update(api)
|
222
|
+
raise Osm::ObjectIsInvalid, 'data is invalid' unless valid?
|
223
|
+
require_ability_to(api, :write, :finance, section_id)
|
224
|
+
term_id = Osm::Term.get_current_term_for_section(api, section_id).id
|
225
|
+
|
226
|
+
updated = true
|
227
|
+
fields = [
|
228
|
+
['tax_payer_name', 'parentname', tax_payer_name],
|
229
|
+
['tax_payer_address', 'address', tax_payer_address],
|
230
|
+
['tax_payer_postcode', 'postcode', tax_payer_postcode],
|
231
|
+
]
|
232
|
+
fields.each do |field|
|
233
|
+
if changed_attributes.include?(field[0])
|
234
|
+
result = api.perform_query("giftaid.php?action=updateScout", {
|
235
|
+
'scoutid' => member_id,
|
236
|
+
'termid' => term_id,
|
237
|
+
'column' => field[1],
|
238
|
+
'value' => field[2],
|
239
|
+
'sectionid' => section_id,
|
240
|
+
'row' => 0,
|
241
|
+
})
|
242
|
+
if result.is_a?(Hash)
|
243
|
+
(result['items'] || []).each do |i|
|
244
|
+
if i['scoutid'] == member_id.to_s
|
245
|
+
updated = false unless i[field[1]] == field[2]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
reset_changed_attributes if updated
|
252
|
+
|
253
|
+
donations.changes.each do |date, (was,now)|
|
254
|
+
date = date.strftime(Osm::OSM_DATE_FORMAT)
|
255
|
+
result = api.perform_query("giftaid.php?action=updateScout", {
|
256
|
+
'scoutid' => member_id,
|
257
|
+
'termid' => term_id,
|
258
|
+
'column' => date,
|
259
|
+
'value' => now,
|
260
|
+
'sectionid' => section_id,
|
261
|
+
'row' => 0,
|
262
|
+
})
|
263
|
+
if result.is_a?(Hash)
|
264
|
+
(result['items'] || []).each do |i|
|
265
|
+
if i['scoutid'] == member_id.to_s
|
266
|
+
updated = false unless i[date] == now
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
donations.clean_up! if updated
|
272
|
+
|
273
|
+
Osm::Model.cache_delete(api, ['gift_aid_data', section_id, term_id]) if updated
|
274
|
+
|
275
|
+
return updated
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
# Compare Data based on section_id, grouping_id, last_name then first_name
|
280
|
+
def <=>(another)
|
281
|
+
result = self.section_id <=> another.try(:section_id)
|
282
|
+
result = self.grouping_id <=> another.try(:grouping_id) if result == 0
|
283
|
+
result = self.last_name <=> another.try(:last_name) if result == 0
|
284
|
+
result = self.first_name <=> another.try(:last_name) if result == 0
|
285
|
+
return result
|
286
|
+
end
|
287
|
+
|
288
|
+
end # Class GiftAid::Data
|
289
|
+
|
290
|
+
end # Class GiftAid
|
291
|
+
|
292
|
+
end
|