Module: ProjectorPWS

Defined in:
lib/projector_pws.rb,
lib/projector_pws/legacy.rb,
lib/projector_pws/version.rb

Overview

ProjectorPWS Module

Defined Under Namespace

Modules: Legacy

Constant Summary collapse

WSDL =

Service Definition

'https://secure.projectorpsa.com/OpsProjectorWcfSvc/PwsProjectorServices.svc?wsdl'
BASE_URL =

Base URL

'https://secure.projectorpsa.com'
URL_PATH =

URL Path

'OpsProjectorWcfSvc/PwsProjectorServices.svc'
NAMESPACE =

Namespaces

'http://projectorpsa.com/PwsProjectorServices/'
EXTRA_NAMESPACES =
{
  'xmlns:req' => 'http://projectorpsa.com/DataContracts/Requests/',
  'xmlns:tim' => 'http://projectorpsa.com/DataContracts/Shared/TimeAndCost/',
  'xmlns:rep' => 'http://projectorpsa.com/DataContracts/Shared/Report/',
  'xmlns:com' => 'http://projectorpsa.com/DataContracts/Shared/Common/'
}
WEEK_START =

Work Week

:monday
WEEK_END =
:friday
WEEKEND_START_TIME =

Weekend Start Time

16
DAYS_PER_WEEK =

Days Per Week

5
HOURS_PER_DAY =

Hours Per Day

8
HOURS_PER_DAY_REAL =

Real Hours Per Day

24
MINUTES_PER_HOUR =

Minutes Per Hour

60
MINUTES_PER_DAY =

Minutes Per Day

HOURS_PER_DAY * MINUTES_PER_HOUR
MINUTES_PER_WEEK =

Minutes Per Week

MINUTES_PER_DAY * DAYS_PER_WEEK
VERSION =

Version

'0.1.19'

Class Method Summary collapse

Class Method Details

.authenticate(username, password, account_code = nil) ⇒ Object

Authenticate



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/projector_pws.rb', line 67

def self.authenticate username, password,  = nil

  # Prepare URLs
  next_url = BASE_URL
  last_url = ''

  # Prepare Credentials
  creds = {}
  creds['req:AccountCode'] =  if 
  creds['req:Password'] = password
  creds['req:UserName'] = username

  # Loop to obtain Ticket
  c = nil
  ticket = nil
  until ticket

    # Authenticate
    begin
      c = open next_url
      r = c.call(:pws_authenticate, message: { 'pws:serviceRequest' => creds }).body[:pws_authenticate_response][:pws_authenticate_result]
      last_url = next_url
      next_url = r[:redirect_url]
    rescue Savon::SOAPFault => e
      raise "Authentication failed - #{e.to_hash[:fault][:detail][:pws_fault][:messages][:pws_message][:error_text]}"
    end

    raise 'API Error (redirect loop)' if next_url == last_url
    ticket = r[:session_ticket]
  end

  # Yield if block given
  if block_given?
    r = yield c, ticket
    unauthenticate c, ticket
    return r
  end

  [c, ticket]
end

.compute_resource_active_hours(wsched) ⇒ Object

Compute Resource Active Hours



281
282
283
284
285
286
287
288
# File 'lib/projector_pws.rb', line 281

def self.compute_resource_active_hours wsched

  # Extract Working Minutes
  work = wsched.inject(0) { |a, e| a + e[:working_minutes].to_i }

  # Determine Active Hours
  work.to_f / MINUTES_PER_HOUR.to_f
end

.compute_resource_free_hours(scheduled_hours, wsched) ⇒ Object

Compute Resource Free Hours



291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/projector_pws.rb', line 291

def self.compute_resource_free_hours scheduled_hours, wsched

  # Get active hours
  active_hours = compute_resource_active_hours wsched

  # Compute time off and holidays into active hours
  active_hours = active_hours - scheduled_hours[:toff]
  active_hours = active_hours - (scheduled_hours[:hday] || 0)

  # Compute free time (active unscheduled time)
  active_hours - scheduled_hours[:work]
end

.current_week_endObject

Current Week End



388
389
390
# File 'lib/projector_pws.rb', line 388

def self.current_week_end
  current_week_start.wnext(WEEK_END)
end

.current_week_startObject

Current Week Start



383
384
385
# File 'lib/projector_pws.rb', line 383

def self.current_week_start
  is_weekend? ? Date.today.wnext(WEEK_START) : (Date.today.monday? ? Date.today : Date.today.wlast(WEEK_START))
end

.get_cost_centers(c, ticket) ⇒ Object

Get Cost Centers



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/projector_pws.rb', line 206

def self.get_cost_centers c, ticket

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:IncludeEngagementCostCentersFlag'] = true
  params['tim:IncludeResourceCostCentersFlag'] = true
  params['tim:IncludeNonResourceNonEngagementCostCentersFlag'] = true

  # Fetch Resources
  c.call(:pws_get_cost_center_list, message: { 'pws:serviceRequest' => params }).body[:pws_get_cost_center_list_response][:pws_get_cost_center_list_result][:cost_centers]
end

.get_expense_document(c, ticket, doc_number, include_cost_cards = true, include_receipts = false) ⇒ Object

Get Expense Document



219
220
221
222
223
224
225
226
227
228
229
# File 'lib/projector_pws.rb', line 219

def self.get_expense_document c, ticket, doc_number, include_cost_cards = true, include_receipts = false

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:ExpenseDocumentIdentity'] = { 'com:DocumentNumber' => doc_number }
  params['tim:RetrieveCostCardsFlag'] = include_cost_cards
  params['tim:RetrieveReceiptsFlag'] = include_receipts

  # Fetch Expense Document
  c.call(:pws_get_expense_document, message: { 'pws:serviceRequest' => params }).body[:pws_get_expense_document_response][:pws_get_expense_document_result][:expense_document]
end

.get_expense_documents(c, ticket, reps = nil) ⇒ Object

Get Expense Documents



232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/projector_pws.rb', line 232

def self.get_expense_documents c, ticket, reps = nil

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  if reps
    params['tim:ExpenseDocumentIdentity'] = []
    reps.each { |r| params['tim:ExpenseDocumentIdentity'] << { 'com:DocumentNumber' => r } }
  end

  # Fetch Expense Documents
  c.call(:pws_get_expense_document, message: { 'pws:serviceRequest' => params }).body[:pws_get_expense_document_response][:pws_get_expense_document_result]
end

.get_free_resources(c, ticket, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Free Resources (non-busy) with associated minutes



268
269
270
271
272
273
274
275
276
277
278
# File 'lib/projector_pws.rb', line 268

def self.get_free_resources c, ticket, start_date = current_week_start, end_date = current_week_end

  # Get all resources
  res = ProjectorPWS.get_resources c, ticket

  # Collect free time
  res.collect { |r| {
    resource: r,
    free_hours: get_resource_free_hours(c, ticket, r, start_date, end_date).to_f
  } }.select { |x| x[:free_hours] > 0 }
end

.get_report(c, ticket, report_uid, format = nil) ⇒ Object

Get Report Output



116
117
118
119
120
121
122
123
124
125
# File 'lib/projector_pws.rb', line 116

def self.get_report c, ticket, report_uid, format = nil

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['rep:ReportIdentity'] = { 'com:ReportUid' => report_uid }
  params['rep:Format'] = format if format

  # Fetch Report
  c.call(:pws_get_report_output, message: { 'pws:serviceRequest' => params }).body
end

.get_resource_active_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end, wsched = nil) ⇒ Object

Get Resource Active Hours



313
314
315
316
317
318
319
320
# File 'lib/projector_pws.rb', line 313

def self.get_resource_active_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end, wsched = nil

  # Acquire working schedule for resource
  wsched ||= ProjectorPWS.get_resource_working_schedule(c, ticket, resource[:resource_uid], start_date, end_date)[:pws_working_schedule_day] rescue(return(0))
  wsched = [wsched] unless wsched.is_a? Array
  wsched.compact!
  compute_resource_active_hours wsched
end

.get_resource_available_timeoff(c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Available Timeoff



167
168
169
170
171
172
173
174
175
176
177
# File 'lib/projector_pws.rb', line 167

def self.get_resource_available_timeoff c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
  params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid } if resource_uid
  params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

  # Fetch Resource Available Timeoff
  c.call(:pws_get_resource_available_time_off, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_available_time_off_response][:pws_get_resource_available_time_off_result][:resource_available_time_off_schedule]
end

.get_resource_cost_cards(c, ticket, resource_uid = nil, exclude_before = nil, flags = { approved: true }) ⇒ Object

Get Resource Cost Cards



193
194
195
196
197
198
199
200
201
202
203
# File 'lib/projector_pws.rb', line 193

def self.get_resource_cost_cards c, ticket, resource_uid = nil, exclude_before = nil, flags = { approved: true }

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:ExcludeApprovedCardsBefore'] = exclude_before.strftime '%Y-%m-%dz' if exclude_before
  %w(approved approved_to_pay draft paid received rejected submitted transmitted).each { |f| params["tim:Include#{f.camelcase}Flag"] = flags[f.to_sym] if flags.include? f.to_sym }
  params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid } if resource_uid

  # Fetch Resource Cost Cards
  c.call(:pws_get_resource_cost_cards, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_cost_cards_response][:pws_get_resource_cost_cards_result][:cost_cards]
end

.get_resource_free_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end, scheduled_hours = nil, wsched = nil) ⇒ Object

Get Resource Free Hours



305
306
307
308
309
310
# File 'lib/projector_pws.rb', line 305

def self.get_resource_free_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end, scheduled_hours = nil, wsched = nil

  # Get scheduled hours
  scheduled_hours ||= get_resource_scheduled_hours c, ticket, resource, start_date, end_date
  compute_resource_free_hours scheduled_hours, wsched
end

.get_resource_schedule(c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Schedule



152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/projector_pws.rb', line 152

def self.get_resource_schedule c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
  params['tim:IncludeScheduledTimeFlag'] = :true
  params['tim:IncludeTimeOffFlag'] = :true
  params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid } if resource_uid
  params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

  # Fetch Resource Schedule
  c.call(:pws_get_resource_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_schedule_response][:pws_get_resource_schedule_result][:resource_schedule]
end

.get_resource_scheduled_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end, sched = nil) ⇒ Object

Get Resource Scheduled Hours



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/projector_pws.rb', line 323

def self.get_resource_scheduled_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end, sched = nil

  # Get schedule
  sched ||= get_resource_schedule(c, ticket, resource[:resource_uid], start_date, end_date) rescue(return({ work: 0, toff: 0, hday: 0 }))

  # Extract scheduled work
  sched_work = sched[:roles][:pws_schedule_role] rescue []
  sched_work = [sched_work] unless sched_work.is_a? Array
  sched_work.compact!

  # Extract scheduled time off
  time_off = sched[:time_off][:pws_schedule_time_off] rescue []
  time_off = [time_off] unless time_off.is_a? Array
  time_off.compact!

  # Extract Holidays
  holidays = sched[:holidays][:pws_schedule_holiday] rescue []
  holidays = [holidays] unless holidays.is_a? Array
  holidays.compact!

  # Run through time off { collect total minutes }
  time_off_minutes = time_off.inject(0) do |a, e|

    # Extract Dates
    dates = e[:time_off_dates][:pws_schedule_time_off_date] rescue []
    dates = [dates] unless dates.is_a? Array
    dates.compact!

    # Collect time off dates
    a + dates.inject(0) { |da, de| da + time_off_minutes(de[:time_off_minutes].to_i) }
  end

  # Run through Holidays { collect total minutes }
  holiday_minutes = holidays.reject { |e| e[:date] < start_date || e[:date] > end_date || e[:date].saturday? || e[:date].sunday? }.inject(0) { |a, e| a + time_off_minutes(e[:time_off_minutes].to_i) }

  # Run through schedule { collect total minutes }
  scheduled_minutes = sched_work.inject(0) do |a, e|

    # Extract bookings
    bookings = e[:bookings][:pws_schedule_booking] rescue []
    bookings = [bookings] unless bookings.is_a? Array
    bookings.compact!

    # Collect bookings
    a + (bookings.inject(0) { |ba, be| ba + be[:scheduled_minutes].to_i })
  end

  {
    work: scheduled_minutes.to_f / MINUTES_PER_HOUR.to_f,
      toff: time_off_minutes.to_f / MINUTES_PER_HOUR.to_f,
      hday: holiday_minutes.to_f / MINUTES_PER_HOUR.to_f
  }
end

.get_resource_working_schedule(c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Working Schedule



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/projector_pws.rb', line 180

def self.get_resource_working_schedule c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
  params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid } if resource_uid
  params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

  # Fetch Resource Working Schedule
  c.call(:pws_get_resource_working_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_working_schedule_response][:pws_get_resource_working_schedule_result][:working_schedule]
end

.get_resources(c, ticket, include_inactive = false) ⇒ Object

Get Resources



128
129
130
131
132
133
134
135
136
# File 'lib/projector_pws.rb', line 128

def self.get_resources c, ticket, include_inactive = false

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['req:IncludeInactiveFlag'] = include_inactive

  # Fetch Resources
  c.call(:pws_get_resource_list, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_list_response][:pws_get_resource_list_result][:resources][:pws_resource_summary]
end

.get_time_entry_time_off(c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Time Entry Time Off



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/projector_pws.rb', line 139

def self.get_time_entry_time_off c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
  params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid } if resource_uid
  params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

  # Fetch Time Entry Time Off
  c.call(:pws_get_time_entry_time_off, message: { 'pws:serviceRequest' => params }).body[:pws_get_time_entry_time_off_response][:pws_get_time_entry_time_off_result].try(:[], :time_entry_time_off).try(:[], :pws_time_entry_time_off)
end

.is_weekend?Boolean

Is Weekend

Returns:

  • (Boolean)


393
394
395
# File 'lib/projector_pws.rb', line 393

def self.is_weekend?
  (Date.today.thursday? && (DateTime.now >= DateTime.now.change(hour: WEEKEND_START_TIME))) || Date.today.friday? || Date.today.saturday? || Date.today.sunday?
end

.open(url = BASE_URL) ⇒ Object

Open Client



60
61
62
63
64
# File 'lib/projector_pws.rb', line 60

def self.open url = BASE_URL
  c = Savon.client wsdl: WSDL, endpoint: service_url(url), namespace: NAMESPACE, env_namespace: :soapenv, namespace_identifier: :pws, namespaces: EXTRA_NAMESPACES
  return yield c if block_given?
  c
end

.service_url(base_url) ⇒ Object

Generate Service URL



378
379
380
# File 'lib/projector_pws.rb', line 378

def self.service_url base_url
  "#{base_url}/#{URL_PATH}"
end

.set_cost_cards_payment_workflow_status(c, ticket, cost_card_uids, status, send_approval_email = false, send_approval_to_pay_email = false, send_paid_email = true) ⇒ Object

Update Cost Cards Payment Workflow Status



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/projector_pws.rb', line 246

def self.set_cost_cards_payment_workflow_status c, ticket, cost_card_uids, status, send_approval_email = false, send_approval_to_pay_email = false, send_paid_email = true

  # Prepare Params
  params = { 'req:SessionTicket' => ticket }
  params['tim:PaymentWorkflowStatus'] = status
  params['tim:SendApprovalEmailFlag'] = send_approval_email
  params['tim:SendApprovalToPayEmailFlag'] = send_approval_to_pay_email
  params['tim:SendPaidEmailFlag'] = send_paid_email
  params['tim:StatusOrders'] = {
    'tim:PwsCostPwsChangeOrder' => {
      'tim:ApproveToPayFlag' => true,
      'tim:CostCardIdentities' => {
        'tim:PwsVersionedCostCardRef' => cost_card_uids.collect { |cc| { 'com:CostCardUid' => cc } }
      }
    }
  }

  # Set Cost Card Payment Workflow Status
  c.call(:pws_set_cost_card_payment_workflow_status, message: { 'pws:serviceRequest' => params }).body[:pws_set_cost_card_payment_workflow_status_response][:pws_set_cost_card_payment_workflow_status_result]
end

.time_off_minutes(x) ⇒ Object

Time Off Minutes (adjust for full-day holidays)



398
399
400
# File 'lib/projector_pws.rb', line 398

def self.time_off_minutes x
  (x == HOURS_PER_DAY_REAL * MINUTES_PER_HOUR) ? (HOURS_PER_DAY * MINUTES_PER_HOUR) : x
end

.unauthenticate(c, ticket) ⇒ Object

Unauthenticate



109
110
111
112
113
# File 'lib/projector_pws.rb', line 109

def self.unauthenticate c, ticket

  # Unauthenticate
  c.call(:pws_unauthenticate, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body
end