osm 1.2.25 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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