2018-03-25 16:15:39 +00:00
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
2018-03-25 17:30:42 +00:00
module InsertTickets
# Will generate rows for payment, offsite_payment or charge, tickets, activities
# pass in:
# data: {
# tickets: [{quantity, ticket_level_id}],
# event_id,
# nonprofit_id,
# supporter_id,
# event_discount_id,
# card_id, (if a charge)
# offsite_payment: {kind, check_number},
# kind (offsite, charge, or free)
# }
def self . create ( data )
data = data . with_indifferent_access
ParamValidation . new ( data , {
tickets : { required : true , is_array : true } ,
nonprofit_id : { required : true , is_reference : true } ,
supporter_id : { required : true , is_reference : true } ,
event_id : { required : true , is_reference : true } ,
event_discount_id : { is_reference : true } ,
kind : { included_in : [ 'free' , 'charge' , 'offsite' ] } ,
token : { format : UUID :: Regex } ,
offsite_payment : { is_hash : true }
} )
data [ :tickets ] . each { | t |
ParamValidation . new ( t , { quantity : { is_integer : true , required : true , min : 1 } , ticket_level_id : { is_reference : true , required : true } } )
}
2018-03-30 18:30:45 +00:00
ParamValidation . new ( data [ :offsite_payment ] , { kind : { included_in : %w( cash check ) } } ) if data [ :offsite_payment ] && ! data [ :offsite_payment ] [ :kind ] . blank?
2018-03-25 17:30:42 +00:00
entities = RetrieveActiveRecordItems . retrieve_from_keys ( data , { Supporter = > :supporter_id , Nonprofit = > :nonprofit_id , Event = > :event_id } )
entities . merge! ( RetrieveActiveRecordItems . retrieve_from_keys ( data , { EventDiscount = > :event_discount_id } , true ) )
tl_entities = get_ticket_level_entities ( data )
validate_entities ( entities , tl_entities )
#verify that enough tickets_available
QueryTicketLevels . verify_tickets_available ( data [ :tickets ] )
gross_amount = QueryTicketLevels . gross_amount_from_tickets ( data [ :tickets ] , data [ :event_discount_id ] )
result = { }
if gross_amount > 0
# Create offsite payment for tickets
if data [ :kind ] == 'offsite'
current_user = data [ :current_user ]
# offsite can only come from valid nonprofit users
unless current_user && QueryRoles . is_authorized_for_nonprofit? ( current_user . id , entities [ :nonprofit_id ] . id )
raise AuthenticationError
end
# create payment and offsite payment
result [ 'payment' ] = create_payment ( entities , gross_amount )
result [ 'offsite_payment' ] = create_offsite_payment ( entities , gross_amount , data , result [ 'payment' ] )
# Create charge for tickets
elsif data [ 'kind' ] == 'charge' || ! data [ 'kind' ]
source_token = QuerySourceToken . get_and_increment_source_token ( data [ :token ] , nil )
QuerySourceToken . validate_source_token_type ( source_token )
tokenizable = source_token . tokenizable
## does the card belong to the supporter?
if tokenizable . holder != entities [ :supporter_id ]
raise ParamValidation :: ValidationError . new ( " Supporter #{ entities [ :supporter_id ] . id } does not own card #{ tokenizable . id } " , key : :token )
end
result = result . merge ( InsertCharge . with_stripe ( {
kind : " Ticket " ,
towards : entities [ :event_id ] . name ,
metadata : { kind : " Ticket " , event_id : entities [ :event_id ] . id , nonprofit_id : entities [ :nonprofit_id ] . id } ,
statement : " Tickets #{ entities [ :event_id ] . name } " ,
amount : gross_amount ,
nonprofit_id : entities [ :nonprofit_id ] . id ,
supporter_id : entities [ :supporter_id ] . id ,
card_id : tokenizable . id
} ) )
if result [ 'charge' ] [ 'status' ] == 'failed'
raise ChargeError . new ( result [ 'charge' ] [ 'failure_message' ] )
end
else
raise ParamValidation :: ValidationError . new ( " Ticket costs money but you didn't pay. " , { key : :kind } )
end
end
# Generate the bid ids
data [ 'tickets' ] = generate_bid_ids ( entities [ :event_id ] . id , tl_entities )
result [ 'tickets' ] = generated_ticket_entities ( data [ 'tickets' ] , result , entities )
# Create the activity rows for the tickets
InsertActivities . for_tickets ( result [ 'tickets' ] . map { | t | t . id } )
ticket_ids = result [ 'tickets' ] . map { | t | t . id }
charge_id = result [ 'charge' ] ? result [ 'charge' ] . id : nil
EmailJobQueue . queue ( JobTypes :: TicketMailerReceiptAdminJob , ticket_ids )
EmailJobQueue . queue ( JobTypes :: TicketMailerFollowupJob , ticket_ids , charge_id )
return result
end
# Generate a set of 'bid ids' (ids for each ticket scoped within the event)
def self . generate_bid_ids ( event_id , tickets )
# Generate the bid ids
last_bid_id = Psql . execute (
Qexpr . new . select ( " COUNT(*) " ) . from ( :tickets )
. where ( " event_id=$id " , id : event_id ) ) . first [ 'count' ] . to_i
tickets . zip ( last_bid_id + 1 .. last_bid_id + tickets . count ) . map { | h , id | h . merge ( 'bid_id' = > id ) }
end
#not really needed but used for breaking into the unit test and getting the IDs
def self . generated_ticket_entities ( ticket_data , result , entities )
ticket_data . map { | ticket_request |
t = Ticket . new
t . quantity = ticket_request [ 'quantity' ]
t . ticket_level = ticket_request [ 'ticket_level_id' ]
t . event = entities [ :event_id ]
t . supporter = entities [ :supporter_id ]
t . payment = result [ 'payment' ]
t . charge = result [ 'charge' ]
t . bid_id = ticket_request [ 'bid_id' ]
t . event_discount = entities [ :event_discount_id ]
t . save!
t
} . to_a
end
def self . validate_entities ( entities , tl_entities )
## is supporter deleted? If supporter is deleted, we error!
if entities [ :supporter_id ] . deleted
raise ParamValidation :: ValidationError . new ( " Supporter #{ entities [ :supporter_id ] . id } is deleted " , key : :supporter_id )
end
if entities [ :event_id ] . deleted
raise ParamValidation :: ValidationError . new ( " Event #{ entities [ :event_id ] . id } is deleted " , key : :event_id )
end
#verify that enough tickets_available
tl_entities . each { | i |
if i [ :ticket_level_id ] . deleted
raise ParamValidation :: ValidationError . new ( " Ticket level #{ i [ :ticket_level_id ] . id } is deleted " , key : :tickets )
end
if i [ :ticket_level_id ] . event != entities [ :event_id ]
raise ParamValidation :: ValidationError . new ( " Ticket level #{ i [ :ticket_level_id ] . id } does not belong to event #{ entities [ :event_id ] } " , key : :tickets )
end
}
# Does the supporter belong to the nonprofit?
if entities [ :supporter_id ] . nonprofit != entities [ :nonprofit_id ]
raise ParamValidation :: ValidationError . new ( " Supporter #{ entities [ :supporter_id ] . id } does not belong to nonprofit #{ entities [ :nonprofit_id ] . id } " , key : :supporter_id )
end
## does event belong to nonprofit
if entities [ :event_id ] . nonprofit != entities [ :nonprofit_id ]
raise ParamValidation :: ValidationError . new ( " Event #{ entities [ :event_id ] . id } does not belong to nonprofit #{ entities [ :nonprofit_id ] } " , key : :event_id )
end
if entities [ :event_discount_id ] && entities [ :event_discount_id ] . event != entities [ :event_id ]
raise ParamValidation :: ValidationError . new ( " Event discount #{ entities [ :event_discount_id ] . id } does not belong to event #{ entities [ :event_id ] . id } " , key : :event_discount_id )
end
end
def self . get_ticket_level_entities ( data )
data [ :tickets ] . map { | i |
{
quantity : i [ :quantity ] ,
ticket_level_id : RetrieveActiveRecordItems . retrieve_from_keys ( i , TicketLevel = > :ticket_level_id ) [ :ticket_level_id ]
}
} . to_a
end
def self . create_payment ( entities , gross_amount )
p = Payment . new
p . gross_amount = gross_amount
p . nonprofit = entities [ :nonprofit_id ]
p . supporter = entities [ :supporter_id ]
p . refund_total = 0
p . date = Time . current
p . towards = entities [ :event_id ] . name
p . fee_total = 0
p . net_amount = gross_amount
p . kind = " OffsitePayment "
p . save!
p
end
def self . create_offsite_payment ( entities , gross_amount , data , payment )
p = OffsitePayment . new
p . gross_amount = gross_amount
p . nonprofit = entities [ :nonprofit_id ]
p . supporter = entities [ :supporter_id ]
p . date = Time . current
p . payment = payment
p . kind = data [ 'offsite_payment' ] [ 'kind' ]
p . check_number = data [ 'offsite_payment' ] [ 'check_number' ]
p . save!
p
end
end