osm 1.2.25 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ef940d8e32de06f687dcb5632b3a2b98389927b
4
- data.tar.gz: 637568e6e8cb0c2cfdee813869a8b949b308f42a
3
+ metadata.gz: 651a0cdcdd3d694935cd01e1500494bf8cd4c7cf
4
+ data.tar.gz: 3e28fa840b8b180292c469f068f6667c01f9bbc9
5
5
  SHA512:
6
- metadata.gz: a9ccff69771e68971f65b5c9251c19bf6269d9fd14724e1421d417eba447a28ec3b8c5a609bb9a3793c9e0b6c4300b0579a9456a5fb468d32231584344dbdbe5
7
- data.tar.gz: b4a25bc8837f41b43d54586318f79eb6cd32792954db3a70fcc81a0c998929cc8b0103705400ba92494896f97c961b9d0b92f6fc43341d7742545ff1c4e9092b
6
+ metadata.gz: 6a31dd0a2da75b15f7c53fb2346e238d0044c3ecca6f04ad14c49bd06818f856987d185474f88a016b76f5df76e889e10464012ace57829bad3d6e049cf8c617
7
+ data.tar.gz: 0bf8a3ac116819ddda8b030c5caad9b6e77896be6b0e9d075ff92905bc82fda39c536d9ca215d58e221a1e40e9e5bbbba830feaff57abda82488c8e84d4f934b
@@ -1,3 +1,10 @@
1
+ ## Version 1.3.0
2
+
3
+ * Add fetching of file names for events (unable to download)
4
+ * Add fetching of payment schedules - Osm::OnlinePayment::Schedule
5
+ * Add fetching of member's payments for a schedule
6
+ * Add updating a member's payment status
7
+
1
8
  ## Version 1.2.25
2
9
 
3
10
  * Add fetching My.SCOUT parent login history - Osm::Myscout::ParentLoginHistory.get_for_section
data/README.md CHANGED
@@ -113,8 +113,13 @@ however it should be noted that when the OSM API adds a feature it can be diffic
113
113
  * Groupings (e.g. Sixes, Patrols)
114
114
  * Invoices (Gold required)
115
115
  * Members
116
+ * My.SCOUT Templates
116
117
  * Notepad
117
118
  * Notepads
119
+ * Online Payments:
120
+ * Schedules
121
+ * Payment Statuses
122
+ * Payment Status History
118
123
  * Programme
119
124
  * Register Data
120
125
  * Register Structure
@@ -140,6 +145,8 @@ however it should be noted that when the OSM API adds a feature it can be diffic
140
145
  * Grouping
141
146
  * Invoices (Gold required)
142
147
  * Member
148
+ * My.SCOUT Templates
149
+ * Online payments - payment status
143
150
  * Register Attendance
144
151
 
145
152
  ### Create
data/lib/osm.rb CHANGED
@@ -174,7 +174,7 @@ module Osm
174
174
  replace_with = options[:replace_with] || {}
175
175
 
176
176
  values = instance.attributes.sort.map{ |(k,v)|
177
- (replace_with.keys.include?(k) && !v.nil?) ? "#{k}.#{replace_with[k]}: #{v.try(replace_with[k])}" : "#{k}: #{v.inspect}"
177
+ (replace_with.keys.include?(k) && !v.nil?) ? "#{k}.#{replace_with[k]}: #{v.try(replace_with[k]).inspect}" : "#{k}: #{v.inspect}"
178
178
  }
179
179
 
180
180
  return "#<#{instance.class.name} #{values.join(', ')} >"
@@ -28,6 +28,8 @@ module Osm
28
28
  # @return [Boolean] if the event has been archived
29
29
  # @!attribute [rw] badges
30
30
  # @return [Array<Osm::Event::BadgeLink>] the badge links for the event
31
+ # @!attribute [rw] files
32
+ # @return [Array<String>] the files attached to this event
31
33
  # @!attribute [rw] columns
32
34
  # @return [Array<Osm::Event::Column>] the custom columns for the event
33
35
  # @!attribute [rw] notepad
@@ -59,6 +61,7 @@ module Osm
59
61
  attribute :notes, :type => String, :default => ''
60
62
  attribute :archived, :type => Boolean, :default => false
61
63
  attribute :badges, :default => []
64
+ attribute :files, :default => []
62
65
  attribute :columns, :default => []
63
66
  attribute :notepad, :type => String, :default => ''
64
67
  attribute :public_notepad, :type => String, :default => ''
@@ -72,8 +75,8 @@ module Osm
72
75
 
73
76
  if ActiveModel::VERSION::MAJOR < 4
74
77
  attr_accessible :id, :section_id, :name, :start, :finish, :cost, :location, :notes, :archived,
75
- :fields, :badges, :columns, :notepad, :public_notepad, :confirm_by_date, :allow_changes,
76
- :reminders, :attendance_limit, :attendance_limit_includes_leaders,
78
+ :fields, :badges, :files, :columns, :notepad, :public_notepad, :confirm_by_date,
79
+ :allow_changes, :reminders, :attendance_limit, :attendance_limit_includes_leaders,
77
80
  :attendance_reminder, :allow_booking
78
81
  end
79
82
 
@@ -83,6 +86,7 @@ module Osm
83
86
  validates_presence_of :name
84
87
  validates :badges, :array_of => {:item_type => Osm::Event::BadgeLink, :item_valid => true}
85
88
  validates :columns, :array_of => {:item_type => Osm::Event::Column, :item_valid => true}
89
+ validates :files, :array_of => {:item_type => String}
86
90
  validates_inclusion_of :allow_changes, :in => [true, false]
87
91
  validates_inclusion_of :reminders, :in => [true, false]
88
92
  validates_inclusion_of :attendance_limit_includes_leaders, :in => [true, false]
@@ -120,7 +124,12 @@ module Osm
120
124
  unless data['items'].nil?
121
125
  data['items'].map { |i| i['eventid'].to_i }.each do |event_id|
122
126
  event_data = api.perform_query("events.php?action=getEvent&sectionid=#{section_id}&eventid=#{event_id}")
127
+ files_data = api.perform_query("ext/uploads/events/?action=listAttachments&sectionid=#{section_id}&eventid=#{event_id}")
128
+ files = files_data.is_a?(Hash) ? files_data['files'] : files_data
129
+ files = [] unless files.is_a?(Array)
130
+
123
131
  event = self.new_event_from_data(event_data)
132
+ event.files = files
124
133
  events.push event
125
134
  ids.push event.id
126
135
  cache_write(api, ['event', event.id], event)
@@ -0,0 +1,521 @@
1
+ module Osm
2
+
3
+ class OnlinePayment
4
+
5
+ class Schedule < Osm::Model
6
+ class Payment < Osm::Model; end # Ensure the constant exists for the validators
7
+ class PaymentStatus < Osm::Model; end # Ensure the constant exists for validators
8
+
9
+ SORT_BY = [:section_id, :name, :id]
10
+ PAY_NOW_OPTIONS = {
11
+ -1 => 'Allowed at all times',
12
+ 0 => 'Permanently disabled',
13
+ 7 => 'Allowed within 1 week of due day',
14
+ 14 => 'Allowed within 2 weeks of due day',
15
+ 21 => 'Allowed within 3 weeks of due day',
16
+ 28 => 'Allowed within 4 weeks of due day',
17
+ 42 => 'Allowed within 6 weeks of due day',
18
+ 56 => 'Allowed within 8 weeks of due day',
19
+ }
20
+
21
+ # @!attribute [rw] id
22
+ # @return [FixNum] the schedule's ID
23
+ # @!attribute [rw] section_id
24
+ # @return [FixNum] the ID of the section the schedule belongs to
25
+ # @!attribute [rw] account_id
26
+ # @return [FixNum] the ID of the bank account this schedule is tied to
27
+ # @!attribute [rw] name
28
+ # @return [String] the name of the schedule
29
+ # @!attribute [rw] description
30
+ # @return [String] the description of what the schedule is for
31
+ # @!attribute [rw] archived
32
+ # @return [Boolean] whether the schedule has been archived
33
+ # @!attribute [rw] gift_aid
34
+ # @return [Boolean] whether payments made using this schedule are eligable for gift aid
35
+ # @!attribute [rw] require_all
36
+ # @return [Boolean] whether to require all payments within the schedule by default
37
+ # @!attribute [rw] pay_now
38
+ # @return [FixNum] controls the use of the pay now feature in OSM, see the PAY_NOW_OPTIONS hash
39
+ # @!attribute [rw] annual_limit
40
+ # @return [String] the maximum amount you'll be able to collect in a rolling 12 month period using this schedule
41
+ # @!attribute [rw] payments
42
+ # @return [Array<Payment>] the payments which make up this schedule
43
+
44
+
45
+ attribute :id, type: Integer
46
+ attribute :section_id, type: Integer
47
+ attribute :account_id, type: Integer
48
+ attribute :name, type: String
49
+ attribute :description, type: String, default: ''
50
+ attribute :archived, type: Boolean
51
+ attribute :gift_aid, type: Boolean
52
+ attribute :require_all, type: Boolean
53
+ attribute :pay_now, type: Integer
54
+ attribute :annual_limit, type: String
55
+ attribute :payments, type: Object, default: []
56
+
57
+ if ActiveModel::VERSION::MAJOR < 4
58
+ attr_accessible :id, :section_id, :account_id, :name, :description, :archived, :gift_aid,
59
+ :require_all, :pay_now, :annual_limit, :payments
60
+ end
61
+
62
+ validates_numericality_of :id, only_integer: true, greater_than: 0
63
+ validates_numericality_of :section_id, only_integer: true, greater_than: 0
64
+ validates_numericality_of :account_id, only_integer: true, greater_than: 0
65
+ validates_presence_of :annual_limit
66
+ validates_presence_of :name
67
+ validates_inclusion_of :pay_now, in: PAY_NOW_OPTIONS.keys
68
+ validates_inclusion_of :archived, in: [true, false]
69
+ validates_inclusion_of :gift_aid, in: [true, false]
70
+ validates_inclusion_of :require_all, in: [true, false]
71
+ validates :payments, array_of: {item_type: Osm::OnlinePayment::Schedule::Payment, item_valid: true}
72
+
73
+
74
+ # @!method initialize
75
+ # Initialize a new Schedule
76
+ # @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
77
+
78
+
79
+ # Get a simple list of schedules for a section
80
+ # @param [Osm::Api] api The api to use to make the request
81
+ # @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the due badges for
82
+ # @!macro options_get
83
+ # @return [Array<Hash>]
84
+ def self.get_list_for_section(api, section, options={})
85
+ require_ability_to(api, :read, :finance, section, options)
86
+ section_id = section.to_i
87
+ cache_key = ['online_payments', 'schedule_list', section_id]
88
+
89
+ if !options[:no_cache] && Osm::Model.cache_exist?(api, cache_key)
90
+ return cache_read(api, cache_key)
91
+ end
92
+
93
+ data = api.perform_query("ext/finances/onlinepayments/?action=getSchemes&sectionid=#{section_id}")
94
+ data = data.is_a?(Hash) ? data['items'] : nil
95
+ data ||= []
96
+ data.map!{ |i| {id: Osm::to_i_or_nil(i['schemeid']), name: i['name'].to_s } }
97
+
98
+ cache_write(api, cache_key, data)
99
+ return data
100
+ end
101
+
102
+
103
+ # Get all payment schedules for a section
104
+ # @param [Osm::Api] api The api to use to make the request
105
+ # @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the due badges for
106
+ # @!macro options_get
107
+ # @return [Array<Osm::OnlinePayment::Schedule>]
108
+ def self.get_for_section(api, section, options={})
109
+ require_ability_to(api, :read, :finance, section, options)
110
+
111
+ get_list_for_section(api, section, options).map do |schedule|
112
+ get(api, section, schedule[:id], options)
113
+ end
114
+ end
115
+
116
+
117
+ # Get a payment schedules for a section
118
+ # @param [Osm::Api] api The api to use to make the request
119
+ # @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the due badges for
120
+ # @param [Fixnum, #to_i] schedule The ID of the payment schedule to get
121
+ # @!macro options_get
122
+ # @return [Array<Osm::OnlinePayment::Schedule>]
123
+ def self.get(api, section, schedule, options={})
124
+ require_ability_to(api, :read, :finance, section, options)
125
+ section_id = section.to_i
126
+ schedule_id = schedule.to_i
127
+ cache_key = ['online_payments', 'schedule', schedule_id]
128
+
129
+ if !options[:no_cache] && cache_exist?(api, cache_key)
130
+ data = cache_read(api, cache_key)
131
+ return data if data.section_id.eql?(section_id)
132
+ end
133
+
134
+ data = api.perform_query("ext/finances/onlinepayments/?action=getPaymentSchedule&sectionid=#{section_id}&schemeid=#{schedule_id}&allpayments=true")
135
+ schedule = new(
136
+ id: Osm::to_i_or_nil(data['schemeid']),
137
+ section_id: section_id,
138
+ account_id: Osm::to_i_or_nil(data['accountid']),
139
+ name: data['name'],
140
+ description: data['description'],
141
+ archived: data['archived'].eql?('1'),
142
+ gift_aid: data['giftaid'].eql?('1'),
143
+ require_all: data['defaulton'].eql?('1'),
144
+ pay_now: data['paynow'],
145
+ annual_limit: data['preauth_amount'],
146
+ )
147
+
148
+ (data['payments'] || []).each do |payment_data|
149
+ payment = Payment.new(
150
+ amount: payment_data['amount'],
151
+ archived: payment_data['archived'].eql?('1'),
152
+ due_date: Osm::parse_date(payment_data['date']),
153
+ name: payment_data['name'].to_s,
154
+ id: Osm::to_i_or_nil(payment_data['paymentid']),
155
+ schedule: schedule,
156
+ )
157
+ schedule.payments.push payment
158
+ end
159
+
160
+ cache_write(api, cache_key, schedule)
161
+ return schedule
162
+ end
163
+
164
+
165
+ # Get payments made by members for the schedule
166
+ # @param [Osm::Api] api The api to use to make the request
167
+ # @param [Osm::Term, Fixnum, #to_i] term The term (or it's id) to get details for (defaults to current term)
168
+ # @!macro options_get
169
+ # @return [Array<Osm::OnlinePayment::Schedule::PaymentsForMember>]
170
+ def get_payments_for_members(api, term=nil, options={})
171
+ require_ability_to(api, :read, :finance, section_id, options)
172
+
173
+ if term.nil?
174
+ section = Osm::Section.get(api, section_id, options)
175
+ term = section.waiting? ? -1 : Osm::Term.get_current_term_for_section(api, section)
176
+ end
177
+
178
+ cache_key = ['online_payments', 'for_members', id, term.to_i]
179
+ if !options[:no_cache] && cache_exist?(api, cache_key)
180
+ return cache_read(api, cache_key)
181
+ end
182
+
183
+ data = api.perform_query("ext/finances/onlinepayments/?action=getPaymentStatus&sectionid=#{section_id}&schemeid=#{id}&termid=#{term.to_i}")
184
+ data = data['items'] || []
185
+ data.map! do |item|
186
+ payments_data = {}
187
+ payments.each do |payment|
188
+ unless item[payment.id.to_s].nil?
189
+ payments_data[payment.id] = PaymentStatus.build_from_json(item[payment.id.to_s], payment)
190
+ end
191
+ end
192
+
193
+ PaymentsForMember.new(
194
+ member_id: Osm::to_i_or_nil(item['scoutid']),
195
+ section_id: section_id,
196
+ grouping_id: Osm::to_i_or_nil(item['patrolid']),
197
+ first_name: item['firstname'],
198
+ last_name: item['lastname'],
199
+ start_date: require_all ? Osm::parse_date(item['startdate']) : nil,
200
+ direct_debit: item['directdebit'].downcase.to_sym,
201
+ payments: payments_data,
202
+ schedule: self,
203
+ )
204
+ end
205
+
206
+ cache_write(api, cache_key, data)
207
+ return data
208
+ end
209
+
210
+
211
+
212
+ # Get unarchived payments for the schedule
213
+ # @return [Array<Osm::OnlinePayment::Schedule::Payment>]
214
+ def current_payments
215
+ payments.select{ |p| !p.archived? }
216
+ end
217
+ # Check if there are any unarchived payments for the schedule
218
+ # @return [Boolean]
219
+ def current_payments?
220
+ payments.any?{ |p| !p.archived? }
221
+ end
222
+
223
+ # Get archived payments for the schedule
224
+ # @return [Array<Osm::OnlinePayment::Schedule::Payment>]
225
+ def archived_payments
226
+ payments.select{ |p| p.archived? }
227
+ end
228
+ # Check if there are any archived payments for the schedule
229
+ # @return [Boolean]
230
+ def archived_payments?
231
+ payments.any?{ |p| p.archived? }
232
+ end
233
+
234
+ def to_s
235
+ "#{id} -> #{name}"
236
+ end
237
+
238
+
239
+ class Payment < Osm::Model
240
+ # @!attribute [rw] id
241
+ # @return [FixNum] the payment's ID
242
+ # @!attribute [rw] amount
243
+ # @return [Sreing] the amount of the payment
244
+ # @!attribute [rw] name
245
+ # @return [String] the name given to the payment
246
+ # @!attribute [rw] archived
247
+ # @return [Boolean] whether the payment has been archived
248
+ # @!attribute [rw] date
249
+ # @return [Date] the payment's due date
250
+ # @!attribute [rw] schedule
251
+ # @return [Osm::OnlnePayment::Schedule] the schedule the payment belongs to
252
+
253
+ attribute :id, type: Integer
254
+ attribute :amount, type: String
255
+ attribute :name, type: String
256
+ attribute :archived, type: Boolean
257
+ attribute :due_date, type: Object
258
+ attribute :schedule, type: Object
259
+
260
+ if ActiveModel::VERSION::MAJOR < 4
261
+ attr_accessible :id, :amount, :name, :archived, :due_date, :schedule
262
+ end
263
+
264
+ validates_numericality_of :id, only_integer: true, greater_than: 0
265
+ validates_presence_of :amount
266
+ validates_presence_of :name
267
+ validates_presence_of :due_date
268
+ validates_presence_of :schedule
269
+ validates_inclusion_of :archived, in: [true, false]
270
+
271
+
272
+ # @!method initialize
273
+ # Initialize a new Payment
274
+ # @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
275
+
276
+
277
+ # Check if the payment is past due (or will be past due on the passed date)
278
+ # @param [Date] date The date to check for (defaults to today)
279
+ # @return [Boolean]
280
+ def past_due?(date=Date.today)
281
+ date > due_date
282
+ end
283
+
284
+ def inspect
285
+ Osm.inspect_instance(self, {:replace_with => {'schedule' => :to_s}})
286
+ end
287
+
288
+ end # Schedule::Payment class
289
+
290
+
291
+ class PaymentsForMember < Osm::Model
292
+ attribute :first_name, type: String
293
+ attribute :last_name, type: String
294
+ attribute :member_id, type: Integer
295
+ attribute :direct_debit, type: Object
296
+ attribute :start_date, type: Object
297
+ attribute :payments, type: Object, default: {} # payment_id -> Array of statuses
298
+ attribute :schedule, type: Object
299
+
300
+ if ActiveModel::VERSION::MAJOR < 4
301
+ attr_accessible :first_name, :last_name, :member_id, :direct_debit, :start_date, :payments, :schedule
302
+ end
303
+
304
+ validates_numericality_of :member_id, only_integer: true, greater_than: 0
305
+ validates_presence_of :first_name
306
+ validates_presence_of :last_name
307
+ validates_presence_of :schedule
308
+ validates_inclusion_of :direct_debit, in: [:active, :inactive, :cancelled]
309
+ validates :payments, hash: {key_type: Fixnum, value_type: Array}
310
+
311
+
312
+ # @!method initialize
313
+ # Initialize a new Schedule
314
+ # @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
315
+
316
+
317
+ # Get the most recent status for a member's payment
318
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to check
319
+ # @return [Boolean]
320
+ def latest_status_for(payment)
321
+ @latest_status ||= Hash[ payments.map{ |k,v| [k, v.sort[0]] } ]
322
+ @latest_status[payment.to_i]
323
+ end
324
+
325
+ # Check if the status of a member's payment is considered paid
326
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to check
327
+ # @return [Boolean, nil]
328
+ def paid?(payment)
329
+ status = latest_status_for(payment.to_i)
330
+ return nil if status.nil?
331
+ [:paid, :paid_manually, :received, :initiated].include?(status.status)
332
+ end
333
+
334
+ # Check if the status of a member's payment is considered unpaid
335
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to check
336
+ # @return [Boolean, nil]
337
+ def unpaid?(payment)
338
+ status = latest_status_for(payment.to_i)
339
+ return nil if status.nil?
340
+ [:required].include?(status.status)
341
+ end
342
+
343
+ # Check if a payment is over due (or will be over due on the passed date)
344
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to check
345
+ # @param [Date] date The date to check for (defaults to today)
346
+ # @return [Boolean] whether the member's payment is unpaid and the payment's due date has passed
347
+ def over_due?(payment, date=nil)
348
+ unpaid?(payment) && payment.past_due?(date)
349
+ end
350
+
351
+ # Check if the member has an active direct debit for this schedule
352
+ # @return [Boolean]
353
+ def active_direct_debit?
354
+ direct_debit.eql?(:active)
355
+ end
356
+
357
+ # Update the status of a payment for the member in OSM
358
+ # @param [Osm::Api] api The api to use to make the request
359
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to update
360
+ # @param [Symbol] status What to update the status to (:required, :not_required or :paid_manually)
361
+ # @param [Boolean] gift_aid Whether to update the gift aid record too (only relevant when setting to :paid_manually)
362
+ # @return [Boolean] whether the update was made in OSM
363
+ def update_payment_status(api, payment, status, gift_aid=false)
364
+ payment_id = payment.to_i
365
+ fail ArgumentError, "#{payment_id} is not a valid payment for the schedule." unless schedule.payments.map(&:id).include?(payment_id)
366
+ fail ArgumentError, "status must be either :required, :not_required or :paid_manually. You passed in #{status.inspect}" unless [:required, :not_required, :paid_manually].include?(status)
367
+
368
+ gift_aid = false unless payment.schedule.gift_aid?
369
+ api_status = {
370
+ required: 'Payment required',
371
+ not_required: 'Payment not required',
372
+ paid_manually: 'Paid manually',
373
+ }[status]
374
+
375
+ data = api.perform_query("ext/finances/onlinepayments/?action=updatePaymentStatus", {
376
+ 'sectionid' => schedule.section_id,
377
+ 'schemeid' => schedule.id,
378
+ 'scoutid' => member_id,
379
+ 'paymentid' => payment_id,
380
+ 'giftaid' => gift_aid,
381
+ 'value' => api_status,
382
+ })
383
+
384
+ data = data[payment_id.to_s]
385
+ return false if data.nil? # No data (at all) for this payment
386
+ data = PaymentStatus.build_from_json(data)
387
+ return false if data.nil? # No history for payment so it didn't get updated
388
+ data = data.sort[0]
389
+ return false if data.nil? # No history for payment so it didn't get updated
390
+ return false unless data.status.eql?(status) # Latest status is not what we set
391
+ return true
392
+ end
393
+
394
+ # Mark a payment as required by the member
395
+ # @param [Osm::Api] api The api to use to make the request
396
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to update
397
+ # @return [Boolean] whether the update was made in OSM
398
+ def mark_payment_required(api, payment)
399
+ update_payment_status(api, payment, :required)
400
+ end
401
+
402
+ # Mark a payment as not required by the member
403
+ # @param [Osm::Api] api The api to use to make the request
404
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to update
405
+ # @return [Boolean] whether the update was made in OSM
406
+ def mark_payment_not_required(api, payment)
407
+ update_payment_status(api, payment, :not_required)
408
+ end
409
+
410
+ # Mark a payment as paid by the member
411
+ # @param [Osm::Api] api The api to use to make the request
412
+ # @param [Osm::OnlinePayment::Schedule::Payment, Fixnum, #to_i] payment The payment (or it's ID) to update
413
+ # @param [Boolean] gift_aid Whether to update the gift aid record too
414
+ # @return [Boolean] whether the update was made in OSM
415
+ def mark_payment_paid_manually(api, payment, gift_aid=false)
416
+ update_payment_status(api, payment, :paid_manually, gift_aid)
417
+ end
418
+
419
+ end # Schedule::PaymentsForMember class
420
+
421
+
422
+ class PaymentStatus < Osm::Model
423
+ VALID_STATUSES = [:required, :not_required, :initiated, :paid, :received, :paid_manually]
424
+
425
+ attribute :id, type: Integer
426
+ attribute :payment, type: Object
427
+ attribute :timestamp, type: Object
428
+ attribute :status, type: Object
429
+ attribute :details, type: String
430
+ attribute :updated_by, type: String
431
+ attribute :updated_by_id, type: Integer
432
+
433
+ if ActiveModel::VERSION::MAJOR < 4
434
+ attr_accessible :id, :payment, :timestamp, :status, :details, :updated_by, :updated_by_id
435
+ end
436
+
437
+ validates_numericality_of :id, only_integer: true, greater_than: 0
438
+ validates_numericality_of :updated_by_id, only_integer: true, greater_than_or_equal_to: -2
439
+ validates_presence_of :payment
440
+ validates_presence_of :timestamp
441
+ validates_presence_of :updated_by
442
+ validates_inclusion_of :status, in: VALID_STATUSES
443
+
444
+
445
+ # @!method initialize
446
+ # Initialize a new PaymentStatus
447
+ # @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
448
+
449
+
450
+ # @!method required?
451
+ # Whether the status is :required
452
+ # @return (Boolean)
453
+ # @!method not_required?
454
+ # Whether the status is :not_required
455
+ # @return (Boolean)
456
+ # @!method initiated?
457
+ # Whether the status is :initiated
458
+ # @return (Boolean)
459
+ # @!method paid?
460
+ # Whether the status is :paid
461
+ # @return (Boolean)
462
+ # @!method received?
463
+ # Whether the status is :received
464
+ # @return (Boolean)
465
+ # @!method paid_manually?
466
+ # Whether the status is :paid_manually
467
+ # @return (Boolean)
468
+ VALID_STATUSES.each do |attribute|
469
+ define_method "#{attribute}?" do
470
+ status.eql?(attribute)
471
+ end
472
+ end
473
+
474
+
475
+ def <=>(another)
476
+ result = -(self.timestamp <=> another.try(:timestamp))
477
+ result = self.payment <=> another.try(:payment) if result.eql?(0)
478
+ result = self.id <=> another.try(:id) if result.eql?(0)
479
+ return result
480
+ end
481
+
482
+ def inspect
483
+ Osm.inspect_instance(self, {:replace_with => {'payment' => :id}})
484
+ end
485
+
486
+ protected
487
+ def self.build_from_json(json, payment=nil)
488
+ data = ActiveSupport::JSON.decode(json)
489
+ return [] unless data.is_a?(Hash)
490
+ data = data['status']
491
+ return [] unless data.is_a?(Array)
492
+
493
+ status_map = {
494
+ 'Payment required' => :required,
495
+ 'Payment not required' => :not_required,
496
+ 'Initiated' => :initiated,
497
+ 'Paid' => :paid,
498
+ 'Received' => :received,
499
+ 'Paid manually' => :paid_manually,
500
+ }
501
+
502
+ data.map! do |item|
503
+ new(
504
+ id: Osm::to_i_or_nil(item['statusid']),
505
+ payment: payment,
506
+ timestamp: Time.strptime(item['statustimestamp'], '%d/%m/%Y %H:%M'),
507
+ status: status_map[item['status']],
508
+ details: item['details'],
509
+ updated_by: item['firstname'],
510
+ updated_by_id: item['who'].to_i,
511
+ )
512
+ end
513
+ end
514
+
515
+ end # Schedule::PaymentStatus class
516
+
517
+ end # Schedule class
518
+
519
+ end
520
+
521
+ end
@@ -32,5 +32,6 @@ Gem::Specification.new do |s|
32
32
  s.add_development_dependency 'rb-inotify', '~> 0.9'
33
33
  s.add_development_dependency 'coveralls', '~> 0.7'
34
34
  s.add_development_dependency 'simplecov', '~> 0.7'
35
+ s.add_development_dependency 'listen', '< 3.1' # Actually required by another dependency but >= 3.1 causing issues
35
36
 
36
37
  end
@@ -16,6 +16,7 @@ describe "Event" do
16
16
  :notes => 'None',
17
17
  :archived => '0',
18
18
  :badges => [],
19
+ :files => [],
19
20
  :columns => [],
20
21
  :notepad => 'notepad',
21
22
  :public_notepad => 'public notepad',
@@ -39,6 +40,7 @@ describe "Event" do
39
40
  event.notes.should == 'None'
40
41
  event.archived.should == false
41
42
  event.badges.should == []
43
+ event.files.should == []
42
44
  event.columns.should == []
43
45
  event.notepad.should == 'notepad'
44
46
  event.public_notepad.should == 'public notepad'
@@ -208,6 +210,7 @@ describe "Event" do
208
210
 
209
211
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvents&sectionid=1&showArchived=true", :body => @events_body.to_json, :content_type => 'application/json')
210
212
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvent&sectionid=1&eventid=2", :body => @event_body.to_json, :content_type => 'application/json')
213
+ FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/ext/uploads/events/?action=listAttachments&sectionid=1&eventid=2", :body => '{"files":["file1.txt", "file2.txt"]}', :content_type => 'application/json')
211
214
 
212
215
  Osm::Model.stub(:get_user_permissions) { {:events => [:read, :write]} }
213
216
  end
@@ -252,9 +255,16 @@ describe "Event" do
252
255
  event.badges[1].badge_type.should == :staged
253
256
  event.badges[1].requirement_id.should == 4
254
257
  event.badges[1].data.should == '1'
258
+ event.files.should == ['file1.txt', 'file2.txt']
255
259
  event.valid?.should == true
256
260
  end
257
261
 
262
+ it "Handles no files being an empty array not a hash" do
263
+ FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/ext/uploads/events/?action=listAttachments&sectionid=1&eventid=2", :body => '[]', :content_type => 'application/json')
264
+ expect{ @event = Osm::Event.get(@api, 1, 2) }.to_not raise_error
265
+ @event.files.should == []
266
+ end
267
+
258
268
  it "Handles a blank config" do
259
269
  @event_body['config'] = ''
260
270
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvent&sectionid=1&eventid=2", :body => @event_body.to_json,:content_type => 'application/json')
@@ -313,6 +323,8 @@ describe "Event" do
313
323
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvents&sectionid=1&showArchived=true", :body => body.to_json, :content_type => 'application/json')
314
324
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvent&sectionid=1&eventid=1", :body => {'config' => '[]', 'archived' => '0', 'eventid' => '1'}.to_json, :content_type => 'application/json')
315
325
  FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/events.php?action=getEvent&sectionid=1&eventid=2", :body => {'config' => '[]', 'archived' => '1', 'eventid' => '2'}.to_json, :content_type => 'application/json')
326
+ FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/ext/uploads/events/?action=listAttachments&sectionid=1&eventid=1", :body => '[]', :content_type => 'application/json')
327
+ FakeWeb.register_uri(:post, "https://www.onlinescoutmanager.co.uk/ext/uploads/events/?action=listAttachments&sectionid=1&eventid=2", :body => '[]', :content_type => 'application/json')
316
328
 
317
329
  events = Osm::Event.get_for_section(@api, 1)
318
330
  OsmTest::Cache.clear
@@ -0,0 +1,490 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe "Online payments" do
5
+
6
+ describe "Schedule" do
7
+
8
+ it "Create" do
9
+ schedule = Osm::OnlinePayment::Schedule.new(
10
+ id: 1,
11
+ section_id: 2,
12
+ account_id: 3,
13
+ name: 'A payment schedule',
14
+ description: 'What this payment schedule is used for',
15
+ archived: true,
16
+ gift_aid: true,
17
+ require_all: true,
18
+ pay_now: 14,
19
+ annual_limit: '100',
20
+ payments: [],
21
+ )
22
+ schedule.id.should == 1
23
+ schedule.section_id.should == 2
24
+ schedule.account_id.should == 3
25
+ schedule.name.should == 'A payment schedule'
26
+ schedule.description.should == 'What this payment schedule is used for'
27
+ schedule.archived.should == true
28
+ schedule.gift_aid.should == true
29
+ schedule.require_all.should == true
30
+ schedule.pay_now.should == 14
31
+ schedule.annual_limit.should == '100'
32
+ schedule.payments.should == []
33
+ schedule.valid?.should == true
34
+ end
35
+
36
+ it "Provides current payments" do
37
+ payment1 = Osm::OnlinePayment::Schedule::Payment.new(id: 1, archived: false)
38
+ payment2 = Osm::OnlinePayment::Schedule::Payment.new(id: 2, archived: true)
39
+ schedule = Osm::OnlinePayment::Schedule.new(payments: [payment1, payment2])
40
+ schedule.current_payments.should == [payment1]
41
+ end
42
+
43
+ it "Checks for current payments" do
44
+ payment1 = Osm::OnlinePayment::Schedule::Payment.new(id: 1, archived: false)
45
+ payment2 = Osm::OnlinePayment::Schedule::Payment.new(id: 2, archived: true)
46
+ schedule = Osm::OnlinePayment::Schedule.new()
47
+
48
+ schedule.payments = [payment1]
49
+ schedule.current_payments?.should == true
50
+
51
+ schedule.payments = [payment2]
52
+ schedule.current_payments?.should == false
53
+ end
54
+
55
+ it "Provides archived payments" do
56
+ payment1 = Osm::OnlinePayment::Schedule::Payment.new(id: 1, archived: false)
57
+ payment2 = Osm::OnlinePayment::Schedule::Payment.new(id: 2, archived: true)
58
+ schedule = Osm::OnlinePayment::Schedule.new(payments: [payment1, payment2])
59
+ schedule.archived_payments.should == [payment2]
60
+ end
61
+
62
+ it "Checks for archived payments" do
63
+ payment1 = Osm::OnlinePayment::Schedule::Payment.new(id: 1, archived: false)
64
+ payment2 = Osm::OnlinePayment::Schedule::Payment.new(id: 2, archived: true)
65
+ schedule = Osm::OnlinePayment::Schedule.new()
66
+
67
+ schedule.payments = [payment2]
68
+ schedule.archived_payments?.should == true
69
+
70
+ schedule.payments = [payment1]
71
+ schedule.archived_payments?.should == false
72
+ end
73
+
74
+ it "Sorts by section_id, name then id" do
75
+ schedule1 = Osm::OnlinePayment::Schedule.new(section_id: 1, name: 'A', id: 1)
76
+ schedule2 = Osm::OnlinePayment::Schedule.new(section_id: 2, name: 'A', id: 1)
77
+ schedule3 = Osm::OnlinePayment::Schedule.new(section_id: 2, name: 'B', id: 1)
78
+ schedule4 = Osm::OnlinePayment::Schedule.new(section_id: 2, name: 'B', id: 2)
79
+ schedules = [schedule3, schedule2, schedule4, schedule1]
80
+ schedules.sort.should == [schedule1, schedule2, schedule3, schedule4]
81
+ end
82
+
83
+ it "Converts to a string" do
84
+ schedule = Osm::OnlinePayment::Schedule.new(id: 1, name: 'Name')
85
+ schedule.to_s.should == '1 -> Name'
86
+ end
87
+
88
+
89
+ describe "Uses OSM's API" do
90
+
91
+ it "Gets summary list" do
92
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=getSchemes&sectionid=1'){ {'items'=>[{"schemeid"=>"539","name"=>"Events"}]} }
93
+ result = Osm::OnlinePayment::Schedule.get_list_for_section(@api, 1)
94
+ result.should == [{id: 539, name: 'Events'}]
95
+ end
96
+
97
+ it "Gets an individual schedule" do
98
+ data = {"schemeid"=>"2","sectionid"=>"1","accountid"=>"3","name"=>"Schedule name","preauth_amount"=>"12.34","description"=>"Schedule description","giftaid"=>"1","defaulton"=>"1","paynow"=>"-1","archived"=>"1","payments"=>[{"paymentid"=>"4","schemeid"=>"2","date"=>"2013-03-21","amount"=>"1.23","name"=>"Payment name","archived"=>"1"}]}
99
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=getPaymentSchedule&sectionid=1&schemeid=2&allpayments=true'){ data }
100
+ schedule = Osm::OnlinePayment::Schedule.get(@api, 1, 2)
101
+ schedule.id.should == 2
102
+ schedule.section_id.should == 1
103
+ schedule.account_id.should == 3
104
+ schedule.name.should == 'Schedule name'
105
+ schedule.description.should == 'Schedule description'
106
+ schedule.archived.should == true
107
+ schedule.gift_aid.should == true
108
+ schedule.require_all.should == true
109
+ schedule.pay_now.should == -1
110
+ schedule.annual_limit.should == '12.34'
111
+ schedule.payments.count.should == 1
112
+ schedule.valid?.should == true
113
+ payment = schedule.payments[0]
114
+ payment.id.should == 4
115
+ payment.amount.should == '1.23'
116
+ payment.name.should == 'Payment name'
117
+ payment.archived.should == true
118
+ payment.due_date.should == Date.new(2013, 3, 21)
119
+ payment.schedule.should == schedule
120
+ payment.valid?.should == true
121
+ end
122
+
123
+ it "Gets all schedules for a section" do
124
+ Osm::OnlinePayment::Schedule.should_receive(:get_list_for_section).with(@api, 5, {}){ [{id: 6, name: 'A'}, {id: 7, name: 'B'}] }
125
+ Osm::OnlinePayment::Schedule.should_receive(:get).with(@api, 5, 6, {}){ 'A' }
126
+ Osm::OnlinePayment::Schedule.should_receive(:get).with(@api, 5, 7, {}){ 'B' }
127
+ Osm::OnlinePayment::Schedule.get_for_section(@api, 5).should == ['A', 'B']
128
+ end
129
+
130
+ describe "Gets member's payments" do
131
+
132
+ before :each do
133
+ @payment = Osm::OnlinePayment::Schedule::Payment.new(id: 4)
134
+ @schedule = Osm::OnlinePayment::Schedule.new(
135
+ id: 1,
136
+ section_id: 2,
137
+ payments: [@payment]
138
+ )
139
+ body = {'items'=>[ {
140
+ 'directdebit'=>'Active', 'firstname'=>'John', 'lastname'=>'Snow', 'patrolid'=>'5', 'scoutid'=>'6',
141
+ 'startdate'=>'2015-02-03',
142
+ '4'=>'{"status":[{"statusid":"7","scoutid":"6","schemeid":"1","paymentid":"8","statustimestamp":"03/02/2016 20:51","status":"Paid manually","details":"","editable":"1","latest":"1","who":"0","firstname":"System"}]}',
143
+ } ]}
144
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=getPaymentStatus&sectionid=2&schemeid=1&termid=3').once{ body }
145
+ end
146
+
147
+ it 'For a "collect all" schedule' do
148
+ @schedule.require_all = true
149
+ p4m = @schedule.get_payments_for_members(@api, 3)
150
+ p4m.is_a?(Array).should == true
151
+ p4m.size.should == 1
152
+ p4m = p4m[0]
153
+ p4m.member_id.should == 6
154
+ p4m.first_name.should == 'John'
155
+ p4m.last_name.should == 'Snow'
156
+ p4m.start_date.should == Date.new(2015, 2, 3)
157
+ p4m.direct_debit.should == :active
158
+ p4m.payments.size.should == 1
159
+ payment = p4m.payments[4][0]
160
+ payment.id.should == 7
161
+ payment.payment.should == @payment
162
+ payment.timestamp.should == Time.new(2016, 2, 3, 20, 51)
163
+ payment.status.should == :paid_manually
164
+ payment.details.should == ''
165
+ payment.updated_by.should == 'System'
166
+ payment.updated_by_id.should == 0
167
+ payment.valid?.should == true
168
+ p4m.valid?.should == true
169
+ end
170
+
171
+ it 'For a "not collect all" schedule' do
172
+ @schedule.require_all = false
173
+ p4m = @schedule.get_payments_for_members(@api, 3)[0]
174
+ p4m.start_date.should == nil # Only difference to a "collect all" type
175
+ p4m.valid?.should == true
176
+ end
177
+
178
+ it "When it needs to fetch a term" do
179
+ section = Osm::Section.new(id: 2)
180
+ Osm::Term.stub(:get_current_term_for_section).and_return(Osm::Term.new(id: 3))
181
+ Osm::Section.stub(:get).and_return(section)
182
+ p4m = @schedule.get_payments_for_members(@api)[0]
183
+ p4m.member_id.should == 6
184
+ p4m.valid?.should == true
185
+ end
186
+
187
+ end # describe Schedule : Uses OSM's API : Get member's payments
188
+
189
+ end # describe Schedule : Uses OSM's API
190
+
191
+
192
+ describe "Payment" do
193
+
194
+ it "Create" do
195
+ schedule = Osm::OnlinePayment::Schedule.new()
196
+ schedule.stub('valid?'){ true }
197
+ payment = Osm::OnlinePayment::Schedule::Payment.new(
198
+ id: 1,
199
+ amount: '12.34',
200
+ name: 'A payment',
201
+ archived: true,
202
+ due_date: Date.new(2016, 5, 1),
203
+ schedule: schedule,
204
+ )
205
+ payment.id.should == 1
206
+ payment.amount.should == '12.34'
207
+ payment.name.should == 'A payment'
208
+ payment.archived.should == true
209
+ payment.due_date.should == Date.new(2016, 5, 1)
210
+ payment.schedule.should == schedule
211
+ payment.valid?.should == true
212
+ end
213
+
214
+ it "Checks if a payment is past due" do
215
+ payment = Osm::OnlinePayment::Schedule::Payment.new(due_date: Date.new(2016, 5, 2))
216
+ payment.past_due?(Date.new(2016, 5, 1)).should == false
217
+ payment.past_due?(Date.new(2016, 5, 2)).should == false
218
+ payment.past_due?(Date.new(2016, 5, 3)).should == true
219
+ end
220
+
221
+ end # describe Schedule -> Payment
222
+
223
+
224
+ describe "PaymentsForMember" do
225
+
226
+ it "Create" do
227
+ schedule = Osm::OnlinePayment::Schedule.new
228
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(
229
+ first_name: 'John',
230
+ last_name: 'Smith',
231
+ member_id: 1,
232
+ direct_debit: :active,
233
+ start_date: Date.new(2016, 6, 7),
234
+ payments: {},
235
+ schedule: schedule,
236
+ )
237
+ p4m.first_name.should == 'John'
238
+ p4m.last_name.should == 'Smith'
239
+ p4m.member_id.should == 1
240
+ p4m.direct_debit.should == :active
241
+ p4m.start_date.should == Date.new(2016, 6, 7)
242
+ p4m.payments.should == {}
243
+ p4m.schedule.should == schedule
244
+ p4m.valid?.should == true
245
+ end
246
+
247
+ it "Gets most recent status for a payment" do
248
+ payments = {
249
+ 1 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(id: 1, timestamp: Time.new(2016, 1, 2, 3, 4))],
250
+ 2 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(id: 2, timestamp: Time.new(2016, 1, 2, 3, 4))],
251
+ 3 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(id: 3, timestamp: Time.new(2016, 1, 2, 3, 4)), Osm::OnlinePayment::Schedule::PaymentStatus.new(id: 4, timestamp: Time.new(2016, 1, 2, 3, 5))],
252
+ }
253
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(payments: payments)
254
+
255
+ p4m.latest_status_for(1).id.should == 1
256
+ p4m.latest_status_for(Osm::OnlinePayment::Schedule::Payment.new(id: 2)).id.should == 2
257
+ p4m.latest_status_for(3).id.should == 4
258
+ end
259
+
260
+ it "Works out if a payment is paid" do
261
+ payments = {
262
+ 1 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :required)],
263
+ 2 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :not_required)],
264
+ 3 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :initiated)],
265
+ 4 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :paid)],
266
+ 5 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :received)],
267
+ 6 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :paid_manually)],
268
+ }
269
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(payments: payments)
270
+
271
+ p4m.paid?(1).should == false
272
+ p4m.paid?(2).should == false
273
+ p4m.paid?(3).should == true
274
+ p4m.paid?(4).should == true
275
+ p4m.paid?(5).should == true
276
+ p4m.paid?(6).should == true
277
+ p4m.paid?(7).should == nil
278
+ end
279
+
280
+ it "Works out if a payment is unpaid" do
281
+ payments = {
282
+ 1 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :required)],
283
+ 2 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :not_required)],
284
+ 3 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :initiated)],
285
+ 4 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :paid)],
286
+ 5 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :received)],
287
+ 6 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :paid_manually)],
288
+ }
289
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(payments: payments)
290
+
291
+ p4m.unpaid?(1).should == true
292
+ p4m.unpaid?(2).should == false
293
+ p4m.unpaid?(3).should == false
294
+ p4m.unpaid?(4).should == false
295
+ p4m.unpaid?(5).should == false
296
+ p4m.unpaid?(6).should == false
297
+ p4m.unpaid?(7).should == nil
298
+ end
299
+
300
+ it "Tells if the user has an active direct debit" do
301
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(direct_debit: :active)
302
+ p4m.active_direct_debit?.should == true
303
+
304
+ p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(direct_debit: :inactive)
305
+ p4m.active_direct_debit?.should == false
306
+ end
307
+
308
+ describe "Works out if a payment is over due" do
309
+
310
+ before :each do
311
+ @payment = Osm::OnlinePayment::Schedule::Payment.new(id: 1, due_date: Date.new(2016, 1, 2))
312
+ paid_payments = { 1 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :paid, payment: @payment)] }
313
+ @paid = Osm::OnlinePayment::Schedule::PaymentsForMember.new(payments: paid_payments)
314
+ unpaid_payments = { 1 => [Osm::OnlinePayment::Schedule::PaymentStatus.new(status: :required, payment: @payment)] }
315
+ @unpaid = Osm::OnlinePayment::Schedule::PaymentsForMember.new(payments: unpaid_payments)
316
+ end
317
+
318
+ it "Due date in over" do
319
+ date = Date.new(2016, 1, 3)
320
+ @paid.over_due?(@payment, date).should == false
321
+ @unpaid.over_due?(@payment, date).should == true
322
+ end
323
+
324
+ it "Due date in present" do
325
+ # Due today means that it is not over being due
326
+ date = Date.new(2016, 1, 2)
327
+ @paid.over_due?(@payment, date).should == false
328
+ @unpaid.over_due?(@payment, date).should == false
329
+ end
330
+
331
+ it "Due date in future" do
332
+ date = Date.new(2016, 1, 1)
333
+ @paid.over_due?(@payment, date).should == false
334
+ @unpaid.over_due?(@payment, date).should == false
335
+ end
336
+
337
+ end # describe Schedule -> PaymentsForMember : is payment past due?
338
+
339
+ describe "Update a payment in OSM" do
340
+
341
+ before :each do
342
+ @schedule = Osm::OnlinePayment::Schedule.new(id: 10, section_id: 4, gift_aid: true)
343
+ @payment = Osm::OnlinePayment::Schedule::Payment.new(id: 1, schedule: @schedule)
344
+ @schedule.payments = [@payment]
345
+ @status = Osm::OnlinePayment::Schedule::PaymentStatus.new(id: 2, payment: @payment)
346
+ @p4m = Osm::OnlinePayment::Schedule::PaymentsForMember.new(member_id: 3, payments: {1=>[@status]}, schedule: @schedule)
347
+ end
348
+
349
+ describe "Using update_payment_status method" do
350
+ it "Success" do
351
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>false,'value'=>'Payment not required'})
352
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled', '1'=>'{"status":[{"statusid":"6","scoutid":"3","schemeid":"4","paymentid":"1","statustimestamp":"01/02/2003 04:05","status":"Payment not required","details":"","editable":"0","latest":"1","who":"0","firstname":"System generated"}]}'} }
353
+ @p4m.update_payment_status(@api, @payment, :not_required).should == true
354
+ end
355
+
356
+ describe "Failure" do
357
+ it "No history for payment" do
358
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>true,'value'=>'Paid manually'})
359
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled', '1'=>'{"status":[]}'} }
360
+ @p4m.update_payment_status(@api, @payment, :paid_manually, true).should == false
361
+ end
362
+
363
+ it "No payment data" do
364
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>true,'value'=>'Paid manually'})
365
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled'} }
366
+ @p4m.update_payment_status(@api, @payment, :paid_manually, true).should == false
367
+ end
368
+
369
+ it "Latest status is not what we set" do
370
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>true,'value'=>'Paid manually'})
371
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled', '1'=>'{"status":[{"statusid":"6","scoutid":"3","schemeid":"4","paymentid":"1","statustimestamp":"01/02/2003 04:05","status":"Payment not required","details":"","editable":"0","latest":"1","who":"0","firstname":"System generated"}]}'} }
372
+ @p4m.update_payment_status(@api, @payment, :paid_manually, true).should == false
373
+ end
374
+ end
375
+
376
+ it "Fails if payment is not in the schedule" do
377
+ expect{ @p4m.update_payment_status(@api, 2, :paid_manually) }.to raise_error ArgumentError, '2 is not a valid payment for the schedule.'
378
+ end
379
+
380
+ it "Fails if given a bad status" do
381
+ expect{ @p4m.update_payment_status(@api, 1, :invalid) }.to raise_error ArgumentError, 'status must be either :required, :not_required or :paid_manually. You passed in :invalid'
382
+ end
383
+
384
+ describe "Ignores gift aid parameter if appropriate" do # pass in true and check if calls out with false
385
+ it "Schedule is a gift aid one" do
386
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>true,'value'=>'Paid manually'})
387
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled', '1'=>'{"status":[{"statusid":"6","scoutid":"3","schemeid":"4","paymentid":"1","statustimestamp":"01/02/2003 04:05","status":"Paid manually","details":"","editable":"0","latest":"1","who":"0","firstname":"System generated"}]}'} }
388
+ @p4m.update_payment_status(@api, @payment, :paid_manually, true).should == true
389
+ end
390
+
391
+ it "Schedule is NOT a gift aid one" do
392
+ @schedule.gift_aid = false
393
+ @api.should_receive(:perform_query).with('ext/finances/onlinepayments/?action=updatePaymentStatus', {'sectionid'=>4,'schemeid'=>10,'scoutid'=>3,'paymentid'=>1,'giftaid'=>false,'value'=>'Paid manually'})
394
+ .once{ {'scoutid'=>'3', 'firstname'=>'John', 'lastname'=>'Smith', 'patrolid'=>'5', 'startdate'=>'1970-01-01', 'directdebit'=>'cancelled', '1'=>'{"status":[{"statusid":"6","scoutid":"3","schemeid":"4","paymentid":"1","statustimestamp":"01/02/2003 04:05","status":"Paid manually","details":"","editable":"0","latest":"1","who":"0","firstname":"System generated"}]}'} }
395
+ @p4m.update_payment_status(@api, @payment, :paid_manually, true).should == true
396
+ end
397
+ end
398
+
399
+ end # Using update_payment_status method
400
+
401
+ describe "Using" do
402
+ it "mark_payment_required" do
403
+ @p4m.should_receive(:update_payment_status).with(@api, @payment, :required).once{ true }
404
+ @p4m.mark_payment_required(@api, @payment).should == true
405
+ end
406
+
407
+ it "mark_payment_not_required" do
408
+ @p4m.should_receive(:update_payment_status).with(@api, @payment, :not_required).once{ true }
409
+ @p4m.mark_payment_not_required(@api, @payment).should == true
410
+ end
411
+
412
+ describe "mark_payment_paid_manually" do
413
+ it "Updating gift aid" do
414
+ @p4m.should_receive(:update_payment_status).with(@api, @payment, :paid_manually, true).once{ true }
415
+ @p4m.mark_payment_paid_manually(@api, @payment, true).should == true
416
+ end
417
+
418
+ it "Not updating gift aid" do
419
+ @p4m.should_receive(:update_payment_status).with(@api, @payment, :paid_manually, false).once{ true }
420
+ @p4m.mark_payment_paid_manually(@api, @payment, false).should == true
421
+ end
422
+ end
423
+
424
+ end
425
+
426
+ end # describe Schedule -> PaymentsForMember : Update a payment in OSM
427
+
428
+ end # describe Schedule -> PaymentsForMember
429
+
430
+
431
+ describe "Payment status" do
432
+
433
+ it "Create" do
434
+ payment = Osm::OnlinePayment::Schedule::Payment.new
435
+ payment.stub('valid?'){ true }
436
+ status = Osm::OnlinePayment::Schedule::PaymentStatus.new(
437
+ id: 1,
438
+ payment: payment,
439
+ details: 'Details',
440
+ timestamp: Time.new(2016, 4, 5, 6, 7),
441
+ status: :paid,
442
+ updated_by: 'My.SCOUT',
443
+ updated_by_id: -2,
444
+ )
445
+ status.id.should == 1
446
+ status.payment.should == payment
447
+ status.details.should == 'Details'
448
+ status.timestamp.should == Time.new(2016, 4, 5, 6, 7)
449
+ status.status.should == :paid
450
+ status.updated_by.should == 'My.SCOUT'
451
+ status.updated_by_id.should == -2
452
+ status.valid?.should == true
453
+ end
454
+
455
+ it "Sorts by timestamp (desc), payment then id" do
456
+ status1 = Osm::OnlinePayment::Schedule::PaymentStatus.new(timestamp: Time.new(2016, 1, 2, 3, 6), payment: 1, id: 1)
457
+ status2 = Osm::OnlinePayment::Schedule::PaymentStatus.new(timestamp: Time.new(2016, 1, 2, 3, 5), payment: 1, id: 1)
458
+ status3 = Osm::OnlinePayment::Schedule::PaymentStatus.new(timestamp: Time.new(2016, 1, 2, 3, 5), payment: 2, id: 1)
459
+ status4 = Osm::OnlinePayment::Schedule::PaymentStatus.new(timestamp: Time.new(2016, 1, 2, 3, 5), payment: 2, id: 2)
460
+ statuses = [status3, status1, status4, status2]
461
+ statuses.sort.should == [status1, status2, status3, status4]
462
+ end
463
+
464
+ describe "Has status checking method for" do
465
+ before :each do
466
+ @payments = []
467
+ Osm::OnlinePayment::Schedule::PaymentStatus::VALID_STATUSES.each do |status|
468
+ payment = Osm::OnlinePayment::Schedule::PaymentStatus.new(status: status)
469
+ @payments.push payment
470
+ instance_variable_set("@#{status}_payment", payment)
471
+ end
472
+ end
473
+
474
+ Osm::OnlinePayment::Schedule::PaymentStatus::VALID_STATUSES.each do |status|
475
+ it status.to_s do
476
+ payment = instance_variable_get("@#{status}_payment")
477
+ payment.send("#{status}?").should == true
478
+ (Osm::OnlinePayment::Schedule::PaymentStatus::VALID_STATUSES - [status]).each do |i|
479
+ payment.send("#{i}?").should == false
480
+ end
481
+ end
482
+ end
483
+ end
484
+
485
+ end # describe Schedule -> PaymentStatus
486
+
487
+
488
+ end # describe Schedule
489
+
490
+ end
data/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Osm
2
- VERSION = "1.2.25"
2
+ VERSION = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.25
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Gauld
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-30 00:00:00.000000000 Z
11
+ date: 2016-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -202,6 +202,20 @@ dependencies:
202
202
  - - "~>"
203
203
  - !ruby/object:Gem::Version
204
204
  version: '0.7'
205
+ - !ruby/object:Gem::Dependency
206
+ name: listen
207
+ requirement: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - "<"
210
+ - !ruby/object:Gem::Version
211
+ version: '3.1'
212
+ type: :development
213
+ prerelease: false
214
+ version_requirements: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - "<"
217
+ - !ruby/object:Gem::Version
218
+ version: '3.1'
205
219
  description: Use the Online Scout Manager API (https://www.onlinescoutmanager.co.uk)
206
220
  to retrieve and save data.
207
221
  email:
@@ -239,6 +253,7 @@ files:
239
253
  - lib/osm/member.rb
240
254
  - lib/osm/model.rb
241
255
  - lib/osm/myscout.rb
256
+ - lib/osm/online_payment.rb
242
257
  - lib/osm/register.rb
243
258
  - lib/osm/section.rb
244
259
  - lib/osm/sms.rb
@@ -263,6 +278,7 @@ files:
263
278
  - spec/osm/member_spec.rb
264
279
  - spec/osm/model_spec.rb
265
280
  - spec/osm/myscout_spec.rb
281
+ - spec/osm/online_payment_spec.rb
266
282
  - spec/osm/osm_spec.rb
267
283
  - spec/osm/register_spec.rb
268
284
  - spec/osm/section_spec.rb