osm 1.0.6 → 1.2.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.
- 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
|