Пример #1
0
def test_exchange_rates_are_working():
    """
    https://github.com/EuroPython/epcon/issues/617
    """
    # by default responses raises ConnectionError, so before we set up any
    # responses it's good to test how the code behaves when there is no
    # connection.
    with raises(ExchangeRate.DoesNotExist):
        get_ecb_rates_for_currency("GBP")
    assert len(responses.calls) == 0

    # This step will store new value in cache and in db
    clear_all_the_caches()
    responses.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
    fetch_and_store_latest_ecb_exrates()
    assert len(responses.calls) == 1

    assert get_ecb_rates_for_currency("GBP") == (
        # date is used in example xml, so it will be returned here as well
        date(2018, 3, 6),
        Decimal('0.89165'))
    assert len(responses.calls) == 1  # no additional calls to API

    with raises(CurrencyNotSupported):
        get_ecb_rates_for_currency("JPY")
    assert len(responses.calls) == 1

    a = convert_from_EUR_using_latest_exrates(Decimal("10"), "GBP")
    b = {
        'converted': Decimal('8.92'),
        'using_exrate_date': date(2018, 3, 6),
        'exrate': Decimal('0.89165')
    }
    assert a == b
    assert len(responses.calls) == 1  # no additional calls to API
Пример #2
0
def test_create_invoice_with_many_items(client):
    """
    This test is meant to be used to test invoice template design.
    It creates a lot of different items on the invoice, and after that we can
    use serve(content) to easily check in the browser how the Invoice looks
    like.

    Freezing it at 2018 so we can easily check EP2018 invoices.
    """
    responses.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)

    Email.objects.create(code='purchase-complete')
    user = make_user()

    vat_rate_20, _ = Vat.objects.get_or_create(value=20)
    CONFERENCE = settings.CONFERENCE_CONFERENCE

    pre_create_typical_fares_for_conference(CONFERENCE, vat_rate_20)

    # Don't need to set dates for this test.
    # set_early_bird_fare_dates(CONFERENCE, yesterday, tomorrow)
    # set_regular_fare_dates(CONFERENCE, yesterday, tomorrow)
    random_fares = random.sample(Fare.objects.all(), 3)

    order = OrderFactory(user=user.assopy_user,
                         items=[(fare, {
                             'qty': i
                         }) for i, fare in enumerate(random_fares, 1)])
    with responses.RequestsMock() as rsps:
        # mocking responses for the invoice VAT exchange rate feature
        rsps.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
        fetch_and_store_latest_ecb_exrates()

    order.confirm_order(date.today())
Пример #3
0
 def setUp(self):
     self.user = make_user()
     make_basic_fare_setup()
     with responses.RequestsMock() as rsps:
         # mocking responses for the invoice VAT exchange rate feature
         rsps.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
         fetch_and_store_latest_ecb_exrates()
Пример #4
0
def create_order_and_invoice(assopy_user, fare, keep_as_placeholder=False):
    today = date.today()
    order = OrderFactory(user=assopy_user, items=[(fare, {'qty': 1})])

    with responses.RequestsMock() as rsps:
        # mocking responses for the invoice VAT exchange rate feature
        rsps.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
        fetch_and_store_latest_ecb_exrates()

    order.confirm_order(today)

    # confirm_order by default creates placeholders, but for most of the tests
    # we can upgrade them to proper invoices anyway.
    invoice = Invoice.objects.get(order=order)
    if keep_as_placeholder:
        return invoice
    return upgrade_invoice_placeholder_to_real_invoice(invoice)
Пример #5
0
def test_invoices_from_buying_tickets(client):
    """
    This is an example of a full flow, of creating and buying a new ticket.
    """
    # because of 2018 we need to make sure that ECB rates are in place
    responses.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
    fetch_and_store_latest_ecb_exrates()

    assert settings.P3_FARES_ENABLED

    # 1. First create a user with complete profile.
    # default password is 'password123' per django_factory_boy
    user = auth_factories.UserFactory(email='*****@*****.**',
                                      is_active=True)

    # both are required to access user profile page.
    AssopyUserFactory(user=user)
    AttendeeProfile.objects.create(user=user, slug='foobar')

    client.login(email='*****@*****.**', password='******')

    # 2. Let's start with checking if no tickets are available at first
    cart_url = reverse('p3-cart')
    response = client.get(cart_url)
    assert template_used(response, "p3/cart.html")
    assert 'Sorry, no tickets are available' in response.content

    # 3. p3/cart.html is using {% fares_available %} assignment tag to display
    # fares.  For more details about fares check conference/fares.py

    ticket_price = Decimal(100)
    ticket_amount = 20
    social_event_price = Decimal(10)
    social_event_amount = 5

    vat_rate_10, _ = Vat.objects.get_or_create(value=10)
    vat_rate_20, _ = Vat.objects.get_or_create(value=20)

    today = date.today()
    yesterday, tomorrow = today - timedelta(days=1), today + timedelta(days=1)

    CONFERENCE = settings.CONFERENCE_CONFERENCE

    create_fare_for_conference(
        code="TRSP",  # Ticket Regular Standard Personal
        conference=CONFERENCE,
        price=ticket_price,
        start_validity=yesterday,
        end_validity=tomorrow,
        vat_rate=vat_rate_10)

    create_fare_for_conference(code=SOCIAL_EVENT_FARE_CODE,
                               conference=CONFERENCE,
                               price=social_event_price,
                               start_validity=yesterday,
                               end_validity=tomorrow,
                               vat_rate=vat_rate_20)

    # 4. If Fare is created we should have one input on the cart.
    response = client.get(cart_url)
    assert template_used(response, "p3/cart.html")
    _response_content = response.content.decode('utf-8')

    assert 'Sorry, no tickets are available' not in _response_content
    assert 'Buy tickets (1 of 2)' in _response_content

    # There are plenty of tds but only TRSP should have data-fare set
    assert 'td class="fare" data-fare="TRSP">' in _response_content
    assert 'td class="fare" data-fare="TDCP">' not in _response_content
    assert 'td class="fare" data-fare="">' in _response_content
    # social events
    assert 'td class="fare" data-fare="VOUPE03">' in _response_content

    # and one input for TRSP where you can specify how many tickets
    # TODO: maybe it should have a different type than text?
    assert '<input type="text" size="2" name="TRSP"' in _response_content

    # 5. Try buying some tickets
    # FIXME: looks like the max_tickets is enforced only with javascript
    assert ticket_amount > conference_settings.MAX_TICKETS

    response = client.post(
        cart_url,
        {
            'order_type': 'non-deductible',  # == Personal
            'TRSP': ticket_amount,
            'VOUPE03': social_event_amount,
        },
        follow=True)

    billing_url = reverse('p3-billing')
    assert response.status_code == 200
    assert response.request['PATH_INFO'] == billing_url

    assert 'Buy tickets (2 of 2)' in response.content.decode('utf-8')

    # unless you POST to the billing page the Order is not created
    assert Order.objects.count() == 0

    Country.objects.create(iso='PL', name='Poland')
    response = client.post(billing_url, {
        'card_name': 'Joe Doe',
        'payment': 'cc',
        'country': 'PL',
        'address': 'Random 42',
        'cf_code': '31447',
        'code_conduct': True,
    },
                           follow=True)
    assert response.status_code == 200
    assert response.request['PATH_INFO'] == '/accounts/stripe/checkout/1/'

    order = Order.objects.get()
    # FIXME: confirming that max_tickets is only enforced in javascript
    assert order.orderitem_set.all().count() ==\
        ticket_amount + social_event_amount

    # need to create an email template that's used in the purchasing process
    Email.objects.create(code='purchase-complete')

    # no invoices
    assert Invoice.objects.all().count() == 0
    # static date, because of #592 choosing something in 2018
    SOME_RANDOM_DATE = date(2018, 1, 1)
    order.confirm_order(SOME_RANDOM_DATE)
    assert order.payment_date == SOME_RANDOM_DATE

    # # multiple items per invoice, one invoice per vat rate.
    # # 2 invoices but they are both placeholders
    assert Invoice.objects.all().count() == 2
    assert Invoice.objects.filter(
        html=VAT_NOT_AVAILABLE_PLACEHOLDER).count() == 2

    # # and we can then upgrade all invoices to non-placeholders
    for _invoice in Invoice.objects.all():
        upgrade_invoice_placeholder_to_real_invoice(_invoice)

    assert Invoice.objects.all().count() == 2
    assert Invoice.objects.filter(
        html=VAT_NOT_AVAILABLE_PLACEHOLDER).count() == 0

    invoice_vat_10 = Invoice.objects.get(vat__value=10)
    invoice_vat_20 = Invoice.objects.get(vat__value=20)

    # only one orderitem_set instance because they are grouped by fare_code
    # items are ordered desc by price.
    expected_invoice_items_vat_10 = [
        {
            'count': ticket_amount,
            'price': ticket_price * ticket_amount,
            'code': u'TRSP',
            'description': u'ep2018 - Regular Standard Personal'
        },
    ]

    expected_invoice_items_vat_20 = [
        {
            'count': social_event_amount,
            'price': social_event_price * social_event_amount,
            'code': SOCIAL_EVENT_FARE_CODE,
            'description': u'ep2018 - Social Event'
        },
    ]

    assert sequence_equals(invoice_vat_10.invoice_items(),
                           expected_invoice_items_vat_10)

    assert sequence_equals(invoice_vat_20.invoice_items(),
                           expected_invoice_items_vat_20)

    # check numbers for vat 10%
    gross_price_vat_10 = ticket_price * ticket_amount

    net_price_vat_10 = normalize_price(gross_price_vat_10 / Decimal('1.1'))
    vat_value_vat_10 = gross_price_vat_10 - net_price_vat_10

    assert invoice_vat_10.price == gross_price_vat_10
    assert invoice_vat_10.net_price() == net_price_vat_10
    assert invoice_vat_10.vat_value() == vat_value_vat_10
    assert invoice_vat_10.html.startswith('<!DOCTYPE')
    assert len(invoice_vat_10.html) > 1000  # large html blob

    # check numbers for vat 20%
    gross_price_vat_20 = social_event_price * social_event_amount

    net_price_vat_20 = normalize_price(gross_price_vat_20 / Decimal('1.2'))
    vat_value_vat_20 = gross_price_vat_20 - net_price_vat_20

    assert invoice_vat_20.price == gross_price_vat_20
    assert invoice_vat_20.net_price() == net_price_vat_20
    assert invoice_vat_20.vat_value() == vat_value_vat_20
    assert invoice_vat_20.html.startswith('<!DOCTYPE')
    assert len(invoice_vat_20.html) > 1000  # large html blob

    # each OrderItem should have a corresponding Ticket
    assert Ticket.objects.all().count() == ticket_amount + social_event_amount

    # Check if user profile has the tickets and invoices available
    profile_url = reverse('assopy-profile')
    response = client.get(profile_url)

    # order code depends on when this test is run, but invoice code should
    # default to whatever payment_date is (in this case 2018, 1, 1)
    # TODO: currently this test is under freezegun, but we may want to remove
    # it later and replace with APIs that allows to control/specify date for
    # order and invoice.
    assert 'O/18.0001' in response.content.decode('utf-8')
    # there is only one order but two invoices
    assert 'I/18.0001' in response.content.decode('utf-8')
    assert 'I/18.0002' in response.content.decode('utf-8')
Пример #6
0
    def setUp(self):
        """
        We're going to replay some of the setup from the previous test here,
        including buying tickets via test client, instead of just creating them
        through the factory – just to make sure we're definitely in the same
        setup as typical user of the website.
        """
        self.user = make_user()
        make_basic_fare_setup()
        with responses.RequestsMock() as rsps:
            # mocking responses for the invoice VAT exchange rate feature
            rsps.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
            fetch_and_store_latest_ecb_exrates()

        Email.objects.create(code='purchase-complete')
        Email.objects.create(code='ticket-assigned')
        Email.objects.create(code='refund-credit-note')

        self.cart_url = reverse('p3-cart')
        self.billing_url = reverse('p3-billing')
        self.my_profile_url = reverse('assopy-profile')
        self.tickets_url = reverse('assopy-tickets')
        self.p3_tickets_url = reverse('p3-tickets')

        self.client.login(email='*****@*****.**', password='******')
        PURCHASE_SUCCESSFUL_302 = 302

        assert Order.objects.all().count() == 0
        response = self.client.post(self.cart_url, {
            'order_type': 'deductible',
            'TRSP': 3,
        }, follow=True)

        self.assertRedirects(response, self.billing_url,
                             status_code=PURCHASE_SUCCESSFUL_302)
        # Purchase was successful but it's first step, so still no Order
        assert Order.objects.all().count() == 0

        Country.objects.create(iso='PL', name='Poland')
        response = self.client.post(self.billing_url, {
            'card_name': 'Joe Doe',
            'payment': 'cc',
            'country': 'PL',
            'address': 'Random 42',
            'cf_code': '31447',
            'code_conduct': True,
        })
        assert response.status_code == PURCHASE_SUCCESSFUL_302
        assert Order.objects.all().count() == 1
        self.order = Order.objects.get()
        self.ticket = Ticket.objects.latest('id')
        self.ticket_url = reverse('p3-ticket', kwargs={'tid': self.ticket.id})
        # p3_conference is associated TicketConference instance
        self.tc = self.ticket.p3_conference

        self.VALIDATION_SUCCESSFUL_200 = 200
        self.VALIDATION_ERROR_400      = 400
        self.MAIN_USER_EMAIL  = self.user.email
        self.OTHER_USER_EMAIL = '*****@*****.**'

        with responses.RequestsMock() as rsps:
            # mocking responses for the invoice VAT exchange rate feature
            rsps.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML)
            fetch_and_store_latest_ecb_exrates()
Пример #7
0
 def handle(self, *args, **options):
     print("PULLING LATEST ECB RATES")
     fetch_and_store_latest_ecb_exrates()