コード例 #1
0
    def test_match_discount_quantity(self):
        """Method to test the quantity calculation of discount code"""

        with self.app.test_request_context():
            ticket = TicketFactory()
            discount_code = DiscountCodeTicketFactory(tickets_number=5)
            discount_code.tickets.append(ticket)

            order_without_discount = OrderFactory(status='completed')

            db.session.commit()

            # Attendees associated with the order without discount code should not be counted
            AttendeeFactoryBase.create_batch(
                10, order_id=order_without_discount.id, ticket_id=ticket.id)

            self.assertTrue(
                TicketingManager.match_discount_quantity(discount_code,
                                                         ticket_holders=[1]))

            order_with_discount = OrderFactory(
                status='completed', discount_code_id=discount_code.id)

            db.session.commit()

            # Attendees associated with the order with discount code should be counted
            AttendeeFactoryBase.create_batch(5,
                                             order_id=order_with_discount.id,
                                             ticket_id=ticket.id)

            self.assertFalse(
                TicketingManager.match_discount_quantity(discount_code,
                                                         ticket_holders=[1]))
            self.assertEqual(5, discount_code.confirmed_attendees_count)
コード例 #2
0
    def create_object(self, data, view_kwargs):
        """
        create_object method for the Charges layer
        charge the user using paypal or stripe
        :param data:
        :param view_kwargs:
        :return:
        """

        if view_kwargs.get('order_identifier').isdigit():
            # when id is passed
            order = Order.query.filter_by(id=view_kwargs['order_identifier']).first()
        else:
            # when identifier is passed
            order = Order.query.filter_by(identifier=view_kwargs['order_identifier']).first()

        if not order:
            raise ObjectNotFound({'parameter': 'order_identifier'},
                                 "Order with identifier: {} not found".format(view_kwargs['order_identifier']))
        elif order.status == 'cancelled' or order.status == 'expired' or order.status == 'completed':
            raise ConflictException({'parameter': 'id'},
                                    "You cannot charge payments on a cancelled, expired or completed order")
        elif (not order.amount) or order.amount == 0:
            raise ConflictException({'parameter': 'id'},
                                    "You cannot charge payments on a free order")

        data['id'] = order.id

        # charge through stripe
        if order.payment_mode == 'stripe':
            if not data.get('stripe'):
                raise UnprocessableEntity({'source': ''}, "stripe token is missing")
            if not order.event.can_pay_by_stripe:
                raise ConflictException({'': ''}, "This event doesn't accept payments by Stripe")

            success, response = TicketingManager.charge_stripe_order_payment(order, data['stripe'])
            data['status'] = success
            data['message'] = response

        # charge through paypal
        elif order.payment_mode == 'paypal':
            if (not data.get('paypal_payer_id')) or (not data.get('paypal_payment_id')):
                raise UnprocessableEntity({'source': ''}, "paypal_payer_id or paypal_payment_id or both missing")
            if not order.event.can_pay_by_paypal:
                raise ConflictException({'': ''}, "This event doesn't accept payments by Paypal")

            success, response = TicketingManager.charge_paypal_order_payment(order, data['paypal_payer_id'],
                                                                             data['paypal_payment_id'])
            data['status'] = success
            data['message'] = response

        return data
コード例 #3
0
    def create_object(self, data, view_kwargs):
        order = Order.query.filter_by(id=view_kwargs['id']).first()
        if order.payment_mode == 'stripe':
            if data.get('stripe') is None:
                raise UnprocessableEntity({'source': ''}, "stripe token is missing")
            success, response = TicketingManager.charge_stripe_order_payment(order, data['stripe'])
            if not success:
                raise UnprocessableEntity({'source': 'stripe_token_id'}, response)

        elif order.payment_mode == 'paypal':
            success, response = TicketingManager.charge_paypal_order_payment(order)
            if not success:
                raise UnprocessableEntity({'source': ''}, response)
        return order
コード例 #4
0
    def after_create_object(self, order, data, view_kwargs):
        """
        after create object method for OrderListPost Class
        :param order: Object created from mashmallow_jsonapi
        :param data:
        :param view_kwargs:
        :return:
        """
        order_tickets = {}
        for holder in order.ticket_holders:
            save_to_db(holder)
            if not order_tickets.get(holder.ticket_id):
                order_tickets[holder.ticket_id] = 1
            else:
                order_tickets[holder.ticket_id] += 1

        order.user = current_user

        # create pdf tickets.
        create_pdf_tickets_for_holder(order)

        for ticket in order_tickets:
            od = OrderTicket(order_id=order.id,
                             ticket_id=ticket,
                             quantity=order_tickets[ticket])
            save_to_db(od)

        order.quantity = order.tickets_count
        save_to_db(order)
        if not has_access('is_coorganizer', event_id=data['event']):
            TicketingManager.calculate_update_amount(order)

        # send e-mail and notifications if the order status is completed
        if order.status == 'completed':
            send_email_to_attendees(order, current_user.id)
            send_notif_to_attendees(order, current_user.id)

            order_url = make_frontend_url(path='/orders/{identifier}'.format(
                identifier=order.identifier))
            for organizer in order.event.organizers:
                send_notif_ticket_purchase_organizer(organizer,
                                                     order.invoice_number,
                                                     order_url,
                                                     order.event.name,
                                                     order.identifier)

        data['user_id'] = current_user.id
コード例 #5
0
    def before_create_object(self, data, view_kwargs):
        """
        before create object method for OrderListPost Class
        :param data:
        :param view_kwargs:
        :return:
        """
        for ticket_holder in data['ticket_holders']:
            # Ensuring that the attendee exists and doesn't have an associated order.
            try:
                ticket_holder_object = self.session.query(
                    TicketHolder).filter_by(id=int(ticket_holder),
                                            deleted_at=None).one()
                if ticket_holder_object.order_id:
                    raise ConflictException(
                        {'pointer': '/data/relationships/attendees'},
                        "Order already exists for attendee with id {}".format(
                            str(ticket_holder)))
            except NoResultFound:
                raise ConflictException(
                    {'pointer': '/data/relationships/attendees'},
                    "Attendee with id {} does not exists".format(
                        str(ticket_holder)))

        if data.get('cancel_note'):
            del data['cancel_note']

        if data.get('payment_mode') != 'free' and not data.get('amount'):
            raise ConflictException({'pointer': '/data/attributes/amount'},
                                    "Amount cannot be null for a paid order")

        if not data.get('amount'):
            data['amount'] = 0
        # Apply discount only if the user is not event admin
        if data.get('discount') and not has_access('is_coorganizer',
                                                   event_id=data['event']):
            discount_code = safe_query_without_soft_deleted_entries(
                self, DiscountCode, 'id', data['discount'], 'discount_code_id')
            if not discount_code.is_active:
                raise UnprocessableEntity({'source': 'discount_code_id'},
                                          "Inactive Discount Code")
            else:
                now = datetime.utcnow()
                valid_from = datetime.strptime(discount_code.valid_from,
                                               '%Y-%m-%d %H:%M:%S')
                valid_till = datetime.strptime(discount_code.valid_till,
                                               '%Y-%m-%d %H:%M:%S')
                if not (valid_from <= now <= valid_till):
                    raise UnprocessableEntity({'source': 'discount_code_id'},
                                              "Inactive Discount Code")
                if not TicketingManager.match_discount_quantity(
                        discount_code, data['ticket_holders']):
                    raise UnprocessableEntity({'source': 'discount_code_id'},
                                              'Discount Usage Exceeded')

            if discount_code.event.id != data[
                    'event'] and discount_code.user_for == TICKET:
                raise UnprocessableEntity({'source': 'discount_code_id'},
                                          "Invalid Discount Code")
コード例 #6
0
    def after_create_object(self, order, data, view_kwargs):
        """
        after create object method for OrderListPost Class
        :param order:
        :param data:
        :param view_kwargs:
        :return:
        """
        order_tickets = {}
        for holder in order.ticket_holders:
            if holder.id != current_user.id:
                pdf = create_save_pdf(
                    render_template('/pdf/ticket_attendee.html',
                                    order=order,
                                    holder=holder))
            else:
                pdf = create_save_pdf(
                    render_template('/pdf/ticket_purchaser.html', order=order))
            holder.pdf_url = pdf
            save_to_db(holder)
            if order_tickets.get(holder.ticket_id) is None:
                order_tickets[holder.ticket_id] = 1
            else:
                order_tickets[holder.ticket_id] += 1
        for ticket in order_tickets:
            od = OrderTicket(order_id=order.id,
                             ticket_id=ticket,
                             quantity=order_tickets[ticket])
            save_to_db(od)
        order.quantity = order.get_tickets_count()
        save_to_db(order)
        if not has_access('is_coorganizer', event_id=data['event']):
            TicketingManager.calculate_update_amount(order)
        send_email_to_attendees(order, current_user.id)
        send_notif_to_attendees(order, current_user.id)

        order_url = make_frontend_url(path='/orders/{identifier}'.format(
            identifier=order.identifier))
        for organizer in order.event.organizers:
            send_notif_ticket_purchase_organizer(organizer,
                                                 order.invoice_number,
                                                 order_url, order.event.name)

        data['user_id'] = current_user.id
コード例 #7
0
ファイル: auth.py プロジェクト: vkvpgt/open-event-server
def calculate_amount():
    data = request.get_json()
    tickets = data['tickets']
    discount_code = None
    if 'discount-code' in data:
        discount_code_id = data['discount-code']
        discount_code = safe_query(db, DiscountCode, 'id', discount_code_id, 'id')
        if not TicketingManager.match_discount_quantity(discount_code, tickets, None):
            return UnprocessableEntityError({'source': 'discount-code'}, 'Discount Usage Exceeded').respond()

    return jsonify(calculate_order_amount(tickets, discount_code))
コード例 #8
0
    def after_create_object(self, order, data, view_kwargs):
        """
        after create object method for OrderListPost Class
        :param order: Object created from mashmallow_jsonapi
        :param data:
        :param view_kwargs:
        :return:
        """
        order_tickets = {}
        for holder in order.ticket_holders:
            save_to_db(holder)
            if not order_tickets.get(holder.ticket_id):
                order_tickets[holder.ticket_id] = 1
            else:
                order_tickets[holder.ticket_id] += 1

        order.user = current_user

        # create pdf tickets.
        create_pdf_tickets_for_holder(order)

        for ticket in order_tickets:
            od = OrderTicket(order_id=order.id, ticket_id=ticket, quantity=order_tickets[ticket])
            save_to_db(od)

        order.quantity = order.tickets_count
        save_to_db(order)
        if not has_access('is_coorganizer', event_id=data['event']):
            TicketingManager.calculate_update_amount(order)

        # send e-mail and notifications if the order status is completed
        if order.status == 'completed':
            send_email_to_attendees(order, current_user.id)
            send_notif_to_attendees(order, current_user.id)

            order_url = make_frontend_url(path='/orders/{identifier}'.format(identifier=order.identifier))
            for organizer in order.event.organizers:
                send_notif_ticket_purchase_organizer(organizer, order.invoice_number, order_url, order.event.name,
                                                     order.identifier)

        data['user_id'] = current_user.id
コード例 #9
0
    def before_create_object(self, data, view_kwargs):
        """
        before create object method for OrderListPost Class
        :param data:
        :param view_kwargs:
        :return:
        """
        for ticket_holder in data['ticket_holders']:
            # Ensuring that the attendee exists and doesn't have an associated order.
            try:
                ticket_holder_object = self.session.query(TicketHolder).filter_by(id=int(ticket_holder),
                                                                                  deleted_at=None).one()
                if ticket_holder_object.order_id:
                    raise ConflictException({'pointer': '/data/relationships/attendees'},
                                            "Order already exists for attendee with id {}".format(str(ticket_holder)))
            except NoResultFound:
                raise ConflictException({'pointer': '/data/relationships/attendees'},
                                        "Attendee with id {} does not exists".format(str(ticket_holder)))

        if data.get('cancel_note'):
            del data['cancel_note']

        if data.get('payment_mode') != 'free' and not data.get('amount'):
            raise ConflictException({'pointer': '/data/attributes/amount'},
                                    "Amount cannot be null for a paid order")

        # Apply discount only if the user is not event admin
        if data.get('discount') and not has_access('is_coorganizer', event_id=data['event']):
            discount_code = safe_query_without_soft_deleted_entries(self, DiscountCode, 'id', data['discount'],
                                                                    'discount_code_id')
            if not discount_code.is_active:
                raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code")
            else:
                now = datetime.utcnow()
                valid_from = datetime.strptime(discount_code.valid_from, '%Y-%m-%d %H:%M:%S')
                valid_till = datetime.strptime(discount_code.valid_till, '%Y-%m-%d %H:%M:%S')
                if not (valid_from <= now <= valid_till):
                    raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code")
                if not TicketingManager.match_discount_quantity(discount_code, data['ticket_holders']):
                    raise UnprocessableEntity({'source': 'discount_code_id'}, 'Discount Usage Exceeded')

            if discount_code.event.id != data['event'] and discount_code.user_for == TICKET:
                raise UnprocessableEntity({'source': 'discount_code_id'}, "Invalid Discount Code")
コード例 #10
0
    def before_create_object(self, data, view_kwargs):
        """
        before create object method for OrderListPost Class
        :param data:
        :param view_kwargs:
        :return:
        """
        if data.get('cancel_note'):
            del data['cancel_note']

        # Apply discount only if the user is not event admin
        if data.get('discount') and not has_access('is_coorganizer',
                                                   event_id=data['event']):
            discount_code = safe_query(self, DiscountCode, 'id',
                                       data['discount'], 'discount_code_id')
            if not discount_code.is_active:
                raise UnprocessableEntity({'source': 'discount_code_id'},
                                          "Inactive Discount Code")
            else:
                now = datetime.utcnow()
                valid_from = datetime.strptime(discount_code.valid_from,
                                               '%Y-%m-%d %H:%M:%S')
                valid_till = datetime.strptime(discount_code.valid_till,
                                               '%Y-%m-%d %H:%M:%S')
                if not (valid_from <= now <= valid_till):
                    raise UnprocessableEntity({'source': 'discount_code_id'},
                                              "Inactive Discount Code")
                if not TicketingManager.match_discount_quantity(
                        discount_code, data['ticket_holders']):
                    raise UnprocessableEntity({'source': 'discount_code_id'},
                                              'Discount Usage Exceeded')

            if discount_code.event.id != data[
                    'event'] and discount_code.user_for == TICKET:
                raise UnprocessableEntity({'source': 'discount_code_id'},
                                          "Invalid Discount Code")
コード例 #11
0
    def create_object(self, data, view_kwargs):
        """
        create_object method for the Charges layer
        charge the user using paypal or stripe
        :param data:
        :param view_kwargs:
        :return:
        """

        if view_kwargs.get('order_identifier').isdigit():
            # when id is passed
            order = Order.query.filter_by(
                id=view_kwargs['order_identifier']).first()
        else:
            # when identifier is passed
            order = Order.query.filter_by(
                identifier=view_kwargs['order_identifier']).first()

        if not order:
            raise ObjectNotFound(
                {'parameter': 'order_identifier'},
                "Order with identifier: {} not found".format(
                    view_kwargs['order_identifier']),
            )
        if (order.status == 'cancelled' or order.status == 'expired'
                or order.status == 'completed'):
            raise ConflictError(
                {'parameter': 'id'},
                "You cannot charge payments on a cancelled, expired or completed order",
            )
        if (not order.amount) or order.amount == 0:
            raise ConflictError({'parameter': 'id'},
                                "You cannot charge payments on a free order")

        data['id'] = order.id

        # charge through stripe
        if order.payment_mode == 'stripe':
            if not order.event.can_pay_by_stripe:
                raise ConflictError(
                    {'': ''}, "This event doesn't accept payments by Stripe")

            success, response = TicketingManager.charge_stripe_order_payment(
                order)
            data['status'] = success
            data['message'] = response

        # charge through paypal
        elif order.payment_mode == 'paypal':
            if (not data.get('paypal_payer_id')) or (
                    not data.get('paypal_payment_id')):
                raise UnprocessableEntityError(
                    {'source': ''},
                    "paypal_payer_id or paypal_payment_id or both missing")
            if not order.event.can_pay_by_paypal:
                raise ConflictError(
                    {'': ''}, "This event doesn't accept payments by Paypal")

            success, response = TicketingManager.charge_paypal_order_payment(
                order, data['paypal_payer_id'], data['paypal_payment_id'])
            data['status'] = success
            data['message'] = response

        return data
コード例 #12
0
def calculate_order_amount(tickets, discount_code):
    from app.api.helpers.ticketing import TicketingManager
    if discount_code:
        if not TicketingManager.match_discount_quantity(discount_code, tickets, None):
            return UnprocessableEntityError({'source': 'discount-code'}, 'Discount Usage Exceeded').respond()

    event = tax = tax_included = fees = None
    total_amount = total_tax = total_discount = 0.0
    ticket_list = []
    for ticket_info in tickets:
        tax_amount = tax_percent = 0.0
        tax_data = {}
        discount_amount = discount_percent = 0.0
        discount_data = {}
        sub_total = ticket_fee = 0.0

        ticket_identifier = ticket_info['id']
        quantity = ticket_info['quantity']
        ticket = safe_query_without_soft_deleted_entries(db, Ticket, 'id', ticket_identifier, 'id')
        if not event:
            event = ticket.event
            fees = TicketFees.query.filter_by(currency=event.payment_currency).first()
        elif ticket.event.id != event.id:
            raise UnprocessableEntity({'source': 'data/tickets'}, "Invalid Ticket")

        if not tax and event.tax:
            tax = event.tax
            tax_included = tax.is_tax_included_in_price

        if ticket.type == 'donation':
            price = ticket_info.get('price')
            if not price or price > ticket.max_price or price < ticket.min_price:
                raise UnprocessableEntity({'source': 'data/tickets'}, "Price for donation ticket invalid")
        else:
            price = ticket.price

        if discount_code:
            for code in ticket.discount_codes:
                if discount_code.id == code.id:
                    if code.type == 'amount':
                        discount_amount = code.value
                        discount_percent = (discount_amount / price) * 100
                    else:
                        discount_amount = (price * code.value)/100
                        discount_percent = code.value
                    discount_data = {
                        'code': discount_code.code,
                        'percent': round(discount_percent, 2),
                        'amount': round(discount_amount, 2)
                    }

        if tax:
            if not tax_included:
                tax_amount = ((price - discount_amount) * tax.rate)/100
                tax_percent = tax.rate
            else:
                tax_amount = ((price - discount_amount) * tax.rate)/(100 + tax.rate)
                tax_percent = tax.rate
            tax_data = {
                'percent': round(tax_percent, 2),
                'amount': round(tax_amount, 2),
            }

        total_tax = total_tax + tax_amount * quantity
        total_discount = total_discount + discount_amount*quantity
        if fees and not ticket.is_fee_absorbed:
            ticket_fee = fees.service_fee * (price * quantity) / 100
            if ticket_fee > fees.maximum_fee:
                ticket_fee = fees.maximum_fee
        if tax_included:
            sub_total = ticket_fee + (price - discount_amount)*quantity
        else:
            sub_total = ticket_fee + (price + tax_amount - discount_amount)*quantity
        total_amount = total_amount + sub_total
        ticket_list.append({
            'id': ticket.id,
            'name': ticket.name,
            'price': price,
            'quantity': quantity,
            'discount': discount_data,
            'tax': tax_data,
            'ticket_fee': round(ticket_fee, 2),
            'sub_total': round(sub_total, 2)
        })
    return dict(tax_included=tax_included, total_amount=round(total_amount, 2), total_tax=round(total_tax, 2),
                total_discount=round(total_discount, 2), tickets=ticket_list)