module ProjectorPWS

ProjectorPWS Module: Root Module for ProjectorPWS.

ProjectorPWS Module: Root Module for ProjectorPWS.

ProjectorPWS Module

Constants

BASE_URL

Base URL

DAYS_PER_WEEK

Days Per Week

EXTRA_NAMESPACES
HOURS_PER_DAY

Hours Per Day

HOURS_PER_DAY_REAL

Real Hours Per Day

MINUTES_PER_DAY

Minutes Per Day

MINUTES_PER_HOUR

Minutes Per Hour

MINUTES_PER_WEEK

Minutes Per Week

NAMESPACE

Namespaces

URL_PATH

URL Path

VERSION

Version

WEEKEND_START_TIME

Weekend Start Time

WEEK_END
WEEK_START

Work Week

WSDL

Service Definition

Public Class Methods

authenticate(username, password, account_code = nil) { |c, ticket| ... } click to toggle source

Authenticate

# File lib/projector_pws.rb, line 68
def self.authenticate username, password, account_code = nil

        # Prepare URLs
        next_url = BASE_URL
        last_url = ''

        # Prepare Credentials
        creds = {}
        creds['req:AccountCode'] = account_code if account_code
        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) click to toggle source

Compute Resource Active Hours

# File lib/projector_pws.rb, line 377
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) click to toggle source

Compute Resource Free Hours

# File lib/projector_pws.rb, line 387
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_end() click to toggle source

Current Week End

# File lib/projector_pws.rb, line 486
def self.current_week_end
        current_week_start.wnext(WEEK_END)
end
current_week_start() click to toggle source

Current Week Start

# File lib/projector_pws.rb, line 481
def self.current_week_start
        is_weekend? ? Date.today.wnext(WEEK_START) : (Date.today.monday? ? Date.today : Date.today.wlast(WEEK_START))
end
get_client(c, ticket, client_uid) click to toggle source
# File lib/projector_pws.rb, line 190
def self.get_client c, ticket, client_uid
        params = {'req:SessionTicket' => ticket }
        params['req:ClientIdentities'] = { 'com:PwsClientRef' => { 'com:ClientUid' => client_uid }}
        c.call(:pws_get_client, message: {'pws:serviceRequest' => params}).body[:pws_get_client_response][:pws_get_client_result][:clients][:pws_client_element]
end
get_cost_centers(c, ticket) click to toggle source

Get Cost Centers

# File lib/projector_pws.rb, line 302
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_engagement(c, ticket, engagement_uid) click to toggle source
# File lib/projector_pws.rb, line 172
def self.get_engagement c, ticket, engagement_uid
        raise "Engagement Code Needed" if !engagement_uid
        # prepare params
        params = {'req:SessionTicket' => ticket }
        params['sch:EngagementIdentities'] = {'com:PwsEngagementRef' => {'com:EngagementUid' => engagement_uid}}
        # Fetch Engagement Details
        c.call(:pws_get_engagement, message: {'pws:serviceRequest' => params}).body[:pws_get_engagement_response][:pws_get_engagement_result][:engagements][:pws_engagement_element]
end
get_engagements(c, ticket, include_closed = false, max_rows = nil) click to toggle source
# File lib/projector_pws.rb, line 181
def self.get_engagements c, ticket, include_closed = false, max_rows = nil
        # Prepare Params
        params = {'req:SessionTicket' => ticket }
        params['sch:IncludeClosedFlag'] = include_closed if include_closed && (include_closed.class == TrueClass || include_closed.class == FalseClass)
        params['sch:MaxRowsToReturn'] = max_rows if max_rows && max_rows.class == Integer
        # Fetch Engagement List
        c.call(:pws_get_engagement_list, message: {'pws:serviceRequest' => params}).body[:pws_get_engagement_list_response][:pws_get_engagement_list_result][:engagements][:pws_engagement_summary]
end
get_expense_document(c, ticket, doc_number, include_cost_cards = true, include_receipts = false) click to toggle source

Get Expense Document

# File lib/projector_pws.rb, line 315
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) click to toggle source

Get Expense Documents

# File lib/projector_pws.rb, line 328
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) click to toggle source

Get Free Resources (non-busy) with associated minutes

# File lib/projector_pws.rb, line 364
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_project(c, ticket, project_uid) click to toggle source

Get Project Details

# File lib/projector_pws.rb, line 139
def self.get_project c, ticket, project_uid
        # prepare params
        params = {'req:SessionTicket' => ticket }
        params['sch:Mode'] = "R"
        # Had to build  the Hash this way to counter 'String not Matched Issue', ruby -v 2.5.1, rails -v 5.1.6
        if project_uid
                params['sch:ProjectIdentities'] = {'com:PwsProjectRef' =>  { 'com:ProjectUid' => project_uid } }
        end

        # Fetch Project Details
        c.call(:pws_get_project, message: {'pws:serviceRequest' => params}).body[:pws_get_project_response][:pws_get_project_result][:projects][:pws_project_element]
end
get_project_time_baseline(c, ticket, time_baseline_uid) click to toggle source

Get Project Time Baseline

# File lib/projector_pws.rb, line 153
def self.get_project_time_baseline c, ticket, time_baseline_uid
        # prepare params
        params = {'req:SessionTicket' => ticket}
        raise "Time Baseline Uid Missing" if !time_baseline_uid
        params['sch:ProjectTimeBaselineIdentities'] = {'com:PwsProjectTimeBaselineRef' => {'com:ProjectTimeBaselineUid' => time_baseline_uid}}
        # Fetch Time Baseline Info
        c.call(:pws_get_project_time_baseline, message: { 'pws:serviceRequest' => params}).body[:pws_get_project_time_baseline_response][:pws_get_project_time_baseline_result][:project_time_baselines][:pws_project_time_baseline_element]
end
get_project_time_baselines(c, ticket, time_baseline_uids) click to toggle source

Get Project Time Baselines

# File lib/projector_pws.rb, line 163
def self.get_project_time_baselines c, ticket, time_baseline_uids
        # prepare params
        params = {'req:SessionTicket' => ticket}
        raise "Time Baseline Uids Missing" unless time_baseline_uids.is_a?(Array)
        params['sch:ProjectTimeBaselineIdentities'] = time_baseline_uids.collect { |i| { 'com:PwsProjectTimeBaselineRef' => { 'com:ProjectTimeBaselineUid' => i } } }
        # Fetch Time Baseline Info
        c.call(:pws_get_project_time_baseline, message: { 'pws:serviceRequest' => params}).body[:pws_get_project_time_baseline_response][:pws_get_project_time_baseline_result][:project_time_baselines][:pws_project_time_baseline_element]
end
get_projects(c, ticket, engagement_code = nil) click to toggle source

Get Project List

# File lib/projector_pws.rb, line 129
def self.get_projects c, ticket, engagement_code = nil
        # Prepare Params
        params = {'req:SessionTicket' => ticket }
        params['sch:QueryString'] = engagement_code.to_s if engagement_code
        params['sch:IncludeClosedFlag'] = true
        # Fetch Projects
        c.call(:pws_get_project_list, message: {'pws:serviceRequest' => params }).body[:pws_get_project_list_response][:pws_get_project_list_result][:projects][:pws_project_summary]
end
get_report(c, ticket, report_uid, format = nil) click to toggle source

Get Report Output

# File lib/projector_pws.rb, line 117
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) click to toggle source

Get Resource Active Hours

# File lib/projector_pws.rb, line 411
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) click to toggle source

Get Resource Available Timeoff

# File lib/projector_pws.rb, line 263
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 }) click to toggle source

Get Resource Cost Cards

# File lib/projector_pws.rb, line 289
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, logger) click to toggle source

Get Resource Free Hours

# File lib/projector_pws.rb, line 401
def self.get_resource_free_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end, scheduled_hours = nil, wsched = nil, logger

        # Get scheduled hours
        scheduled_hours ||= get_resource_scheduled_hours c, ticket, resource, start_date, end_date
        free_hours = compute_resource_free_hours scheduled_hours, wsched
        logger.info "#FREE HOURS: #{free_hours}"
        free_hours
end
get_resource_schedule(c, ticket, resource_uid = nil, start_date = current_week_start, end_date = current_week_end) click to toggle source

Get Resource Schedule

# File lib/projector_pws.rb, line 248
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) click to toggle source

Get Resource Scheduled Hours

# File lib/projector_pws.rb, line 421
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) click to toggle source

Get Resource Working Schedule

# File lib/projector_pws.rb, line 276
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) click to toggle source

Get Resources

# File lib/projector_pws.rb, line 197
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_cards(c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end) click to toggle source

Get Resource Time Cards

# File lib/projector_pws.rb, line 229
def self.get_time_cards 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:IncludeApprovedFlag'] = :true
        params['tim:IncludeDraftFlag'] = :true
        params['tim:IncludeRejectedFlag'] = :true
        params['tim:IncludeSubmittedFlag'] = :true
        params['tim:IncludeTimeCardsFlag'] = :true
        params['tim:IncludeTimeOffCardsFlag'] = :false
        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_cards, message: { 'pws:serviceRequest' => params }).body[:pws_get_time_cards_response][:pws_get_time_cards_result].try(:[], :time_entry_projects)
end
get_time_entry_time_off(c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end) click to toggle source

Get Resource Time Entry Time Off

# File lib/projector_pws.rb, line 217
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
get_user(c, ticket, user_uid) click to toggle source

Get Single Resource

# File lib/projector_pws.rb, line 207
def self.get_user c, ticket, user_uid
        params = { 'req:SessionTicket' => ticket }
        # Prepare Params
        params['req:UserIdentities'] = { 'com:PwsUserRef' => { 'com:UserUid' => user_uid }}
        # Fetch Resource Details
        c.call(:pws_get_user, message: {'pws:serviceRequest' => params})
end
is_weekend?() click to toggle source

Is Weekend

# File lib/projector_pws.rb, line 491
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) { |c| ... } click to toggle source

Open Client

# File lib/projector_pws.rb, line 61
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) click to toggle source

Generate Service URL

# File lib/projector_pws.rb, line 476
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) click to toggle source

Update Cost Cards Payment Workflow Status

# File lib/projector_pws.rb, line 342
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) click to toggle source

Time Off Minutes (adjust for full-day holidays)

# File lib/projector_pws.rb, line 496
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) click to toggle source

Unauthenticate

# File lib/projector_pws.rb, line 110
def self.unauthenticate c, ticket

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