예제 #1
0
    def test_valid_payment_segment_logging(self, mock_track):
        """
        Verify the "Payment Info Entered" Segment event is fired after payment info is validated
        """
        basket = create_basket(owner=self.user, site=self.site)

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)
        properties = {'checkout_id': basket.order_number}

        user_tracking_id, lms_client_id, lms_ip = parse_tracking_context(
            self.user)
        context = {
            'ip': lms_ip,
            'Google Analytics': {
                'clientId': lms_client_id
            }
        }

        mixin.handle_payment({}, basket)
        # ensure that the only two Segment events fired are 'Product Added' and 'Payment Info Entered'
        self.assertEqual(mock_track.call_count, 2)
        mock_track.assert_called_with(user_tracking_id,
                                      'Payment Info Entered',
                                      properties,
                                      context=context)
예제 #2
0
    def test_create_assignments_for_multi_use_per_customer(self, __):
        """
        Verify the `create_assignments_for_multi_use_per_customer` works as expected for `MULTI_USE_PER_CUSTOMER`.
        """
        coupon_max_global_applications = 10
        enterprise_offer = EnterpriseOfferFactory(
            max_global_applications=coupon_max_global_applications)
        voucher = VoucherFactory(usage=Voucher.MULTI_USE_PER_CUSTOMER)
        voucher.offers.add(enterprise_offer)
        basket = create_basket(owner=self.user, site=self.site)
        basket.vouchers.add(voucher)
        order = create_order(user=self.user, basket=basket)

        assert OfferAssignment.objects.all().count() == 0

        EdxOrderPlacementMixin().create_assignments_for_multi_use_per_customer(
            order)
        EdxOrderPlacementMixin().update_assigned_voucher_offer_assignment(
            order)

        assert OfferAssignment.objects.all().count(
        ) == coupon_max_global_applications
        assert OfferAssignment.objects.filter(
            offer=enterprise_offer,
            code=voucher.code,
            user_email=basket.owner.email,
            status=OFFER_ASSIGNED).count() == 9
        assert OfferAssignment.objects.filter(
            offer=enterprise_offer,
            code=voucher.code,
            user_email=basket.owner.email,
            status=OFFER_REDEEMED).count() == 1
예제 #3
0
    def test_order_number_collision(self, _mock_track):
        """
        Verify that an attempt to create an order with the same number as an existing
        order causes an exception to be raised.
        """
        order_placement_mixin = EdxOrderPlacementMixin()

        basket = self.order.basket

        shipping_method = NoShippingRequired()
        shipping_charge = shipping_method.calculate(basket)

        order_total = OrderTotalCalculator().calculate(basket, shipping_charge)

        with self.assertRaises(ValueError):
            order_placement_mixin.handle_order_placement(
                self.order.number,
                self.user,
                basket,
                None,
                shipping_method,
                shipping_charge,
                None,
                order_total,
            )
예제 #4
0
    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification, and create Source and PaymentEvent
        objects.
        """
        basket = create_basket(owner=self.user, site=self.site)

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)
        processor_name = DummyProcessor.NAME
        total = basket.total_incl_tax
        reference = basket.id

        with LogCapture(LOGGER_NAME) as logger:
            mixin.handle_payment({}, basket)
            logger.check_present((
                LOGGER_NAME, 'INFO',
                'payment_received: amount="{}", basket_id="{}", currency="{}", '
                'processor_name="{}", reference="{}", user_id="{}"'.format(
                    total, basket.id, basket.currency, processor_name,
                    reference, self.user.id)))

        # pylint: disable=protected-access

        # Validate a payment Source was created
        source_type = SourceType.objects.get(code=processor_name)
        label = self.user.username
        self.assert_basket_matches_source(basket, mixin._payment_sources[-1],
                                          source_type, reference, label)

        # Validate the PaymentEvent was created
        paid_type = PaymentEventType.objects.get(code='paid')
        self.assert_valid_payment_event_fields(mixin._payment_events[-1],
                                               total, paid_type,
                                               processor_name, reference)
예제 #5
0
    def test_create_offer_assignments_for_updated_max_uses(self, __):
        """
        Verify the `create_assignments_for_multi_use_per_customer` works as expected for
        `MULTI_USE_PER_CUSTOMER` when `max_global_applications` is updated for existing voucher.
        """
        coupon_max_global_applications = 1
        enterprise_offer = EnterpriseOfferFactory(
            max_global_applications=coupon_max_global_applications)
        voucher = VoucherFactory(usage=Voucher.MULTI_USE_PER_CUSTOMER)
        voucher.offers.add(enterprise_offer)
        basket = create_basket(owner=self.user, site=self.site)
        basket.vouchers.add(voucher)
        order = create_order(user=self.user, basket=basket)

        assert OfferAssignment.objects.all().count() == 0

        EdxOrderPlacementMixin().create_assignments_for_multi_use_per_customer(
            order)
        EdxOrderPlacementMixin().update_assigned_voucher_offer_assignment(
            order)

        assert OfferAssignment.objects.all().count(
        ) == coupon_max_global_applications
        assert OfferAssignment.objects.filter(
            offer=enterprise_offer,
            code=voucher.code,
            user_email=basket.owner.email,
            status=OFFER_REDEEMED).count() == 1

        # update max_global_applications
        coupon_new_max_global_applications = 5
        enterprise_offer.max_global_applications = coupon_new_max_global_applications
        enterprise_offer.save()

        assert voucher.enterprise_offer.max_global_applications == coupon_new_max_global_applications

        EdxOrderPlacementMixin().create_assignments_for_multi_use_per_customer(
            order)

        assert OfferAssignment.objects.all().count(
        ) == coupon_new_max_global_applications
        assert OfferAssignment.objects.filter(
            offer=enterprise_offer,
            code=voucher.code,
            user_email=basket.owner.email,
            status=OFFER_ASSIGNED).count() == 4
        assert OfferAssignment.objects.filter(
            offer=enterprise_offer,
            code=voucher.code,
            user_email=basket.owner.email,
            status=OFFER_REDEEMED).count() == 1

        # call once again to verify nothing is created because all available slots are assigned
        EdxOrderPlacementMixin().create_assignments_for_multi_use_per_customer(
            order)
        assert OfferAssignment.objects.all().count(
        ) == coupon_new_max_global_applications
예제 #6
0
 def test_handle_successful_order_no_email_opt_in(self, _):
     """
     Verify that the post checkout defaults email_opt_in to false.
     """
     with mock.patch('ecommerce.extensions.checkout.mixins.post_checkout.send') as mock_send:
         mixin = EdxOrderPlacementMixin()
         mixin.handle_successful_order(self.order)
         send_arguments = {'sender': mixin, 'order': self.order, 'request': None, 'email_opt_in': False}
         mock_send.assert_called_once_with(**send_arguments)
예제 #7
0
    def test_payment_not_accepted_segment_logging(self, mock_track):
        """
        Verify if the payment is not accepted, we still log the processor response
        """
        tracking_context = {
            'ga_client_id': 'test-client-id',
            'lms_user_id': 'test-user-id',
            'lms_ip': '127.0.0.1'
        }
        self.user.tracking_context = tracking_context
        self.user.save()

        basket = create_basket(owner=self.user, site=self.site)

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)

        user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(
            self.user)
        context = {
            'ip': lms_ip,
            'Google Analytics': {
                'clientId': ga_client_id
            },
            'page': {
                'url': 'https://testserver.fake/'
            },
        }
        with self.assertRaises(Exception):
            mixin.handle_payment({}, basket)

        # Verify the correct events are fired to Segment
        calls = []

        properties = translate_basket_line_for_segment(basket.lines.first())
        properties['cart_id'] = basket.id
        calls.append(
            mock.call(user_tracking_id,
                      'Product Added',
                      properties,
                      context=context))

        properties = {
            'basket_id': basket.id,
            'payment_error': 'Exception',
            'success': False,
            'processor_name': DummyProcessor.NAME,
        }
        calls.append(
            mock.call(user_tracking_id,
                      'Payment Processor Response',
                      properties,
                      context=context))

        mock_track.assert_has_calls(calls)
예제 #8
0
    def test_non_free_basket_order(self, __):
        """ Verify an error is raised for non-free basket. """
        basket = create_basket(empty=True)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=10))

        with self.assertRaises(BasketNotFreeError):
            EdxOrderPlacementMixin().place_free_order(basket)
예제 #9
0
    def test_handle_post_order_for_bulk_purchase(self, __):
        """
        Ensure that the bulk purchase order is linked to the provided business
        client when the method `handle_post_order` is invoked.
        """
        toggle_switch(ENROLLMENT_CODE_SWITCH, True)

        course = CourseFactory(partner=self.partner)
        course.create_or_update_seat('verified',
                                     True,
                                     50,
                                     create_enrollment_code=True)
        enrollment_code = Product.objects.get(
            product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
        user = UserFactory()
        basket = BasketFactory(owner=user, site=self.site)
        basket.add_product(enrollment_code, quantity=1)
        order = create_order(number=1, basket=basket, user=user)
        request_data = {
            'organization': 'Dummy Business Client',
            PURCHASER_BEHALF_ATTRIBUTE: 'False',
        }
        # Manually add organization and purchaser attributes on the basket for testing
        basket_add_organization_attribute(basket, request_data)

        EdxOrderPlacementMixin().handle_post_order(order)

        # Now verify that a new business client has been created in current
        # order is now linked with that client through Invoice model.
        business_client = BusinessClient.objects.get(
            name=request_data['organization'])
        assert Invoice.objects.get(
            order=order).business_client == business_client
예제 #10
0
    def test_order_no_lms_user_id(self, mock_track):
        """
        Ensure that expected values are substituted when no LMS user id
        was available.
        """
        tracking_context = {
            'ga_client_id': 'test-client-id',
            'lms_user_id': 'test-user-id',
            'lms_ip': '127.0.0.1'
        }
        self.user.tracking_context = tracking_context
        self.user.lms_user_id = None
        self.user.save()

        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure event is being tracked
        self.assertTrue(mock_track.called)
        # ensure event data is correct
        self.assert_correct_event(
            mock_track,
            self.order,
            ECOM_TRACKING_ID_FMT.format(self.user.id),
            tracking_context['ga_client_id'],
            tracking_context['lms_ip'],
            self.order.number,
            self.order.currency,
            self.order.user.email,
            self.order.total_excl_tax,
            self.order.
            total_excl_tax,  # value for revenue field is same as total.
            check_traits=True,
        )
예제 #11
0
    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification.
        """
        amount = Decimal('9.99')
        basket_id = 'test-basket-id'
        currency = 'USD'
        processor_name = 'test-processor-name'
        reference = 'test-reference'
        user_id = '1'

        mock_source = Mock(currency=currency)
        mock_payment_event = Mock(amount=amount,
                                  processor_name=processor_name,
                                  reference=reference)
        mock_handle_processor_response = Mock(
            return_value=(mock_source, mock_payment_event))
        mock_payment_processor = Mock(
            handle_processor_response=mock_handle_processor_response)

        with patch(
                'ecommerce.extensions.checkout.mixins.EdxOrderPlacementMixin.payment_processor',
                mock_payment_processor):
            mock_basket = Mock(id=basket_id, owner=Mock(id=user_id))
            with LogCapture(LOGGER_NAME) as l:
                EdxOrderPlacementMixin().handle_payment(Mock(), mock_basket)
                l.check((
                    LOGGER_NAME, 'INFO',
                    'payment_received: amount="{}", basket_id="{}", currency="{}", '
                    'processor_name="{}", reference="{}", user_id="{}"'.format(
                        amount, basket_id, currency, processor_name, reference,
                        user_id)))
예제 #12
0
    def test_handle_successful_order(self, mock_track):
        """
        Ensure that tracking events are fired with correct content when order
        placement event handling is invoked.
        """
        tracking_context = {
            'lms_user_id': 'test-user-id',
            'lms_client_id': 'test-client-id',
            'lms_ip': '127.0.0.1'
        }
        self.user.tracking_context = tracking_context
        self.user.save()

        with LogCapture(LOGGER_NAME) as l:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            # ensure event is being tracked
            self.assertTrue(mock_track.called)
            # ensure event data is correct
            self.assert_correct_event(mock_track, self.order,
                                      tracking_context['lms_user_id'],
                                      tracking_context['lms_client_id'],
                                      tracking_context['lms_ip'],
                                      self.order.number, self.order.currency,
                                      self.order.total_excl_tax)
            l.check((
                LOGGER_NAME, 'INFO',
                'order_placed: amount="{}", basket_id="{}", contains_coupon="{}", currency="{}",'
                ' order_number="{}", user_id="{}"'.format(
                    self.order.total_excl_tax, self.order.basket.id,
                    self.order.contains_coupon, self.order.currency,
                    self.order.number, self.order.user.id)))
예제 #13
0
    def test_handle_successful_free_order(self, mock_track):
        """Verify that tracking events are not emitted for free orders."""
        order = self.create_order(free=True, status=ORDER.OPEN)
        EdxOrderPlacementMixin().handle_successful_order(order)

        # Verify that no event was emitted.
        self.assertFalse(mock_track.called)
예제 #14
0
    def test_handle_successful_async_order(self, __):
        """
        Verify that a Waffle Sample can be used to control async order fulfillment.
        """
        sample, created = Sample.objects.get_or_create(
            name='async_order_fulfillment',
            defaults={
                'percent':
                100.0,
                'note':
                'Determines what percentage of orders are fulfilled asynchronously.',
            })

        if not created:
            sample.percent = 100.0
            sample.save()

        with mock.patch(
                'ecommerce.extensions.checkout.mixins.fulfill_order.delay'
        ) as mock_delay:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            mock_delay.assert_called_once_with(
                self.order.number,
                site_code=self.partner.short_code,
                email_opt_in=False)
예제 #15
0
    def test_handle_post_order_for_seat_purchase(self, __):
        """
        Ensure that the single seat purchase order is not linked any business
        client when the method `handle_post_order` is invoked.
        """
        toggle_switch(ENROLLMENT_CODE_SWITCH, False)

        course = CourseFactory(partner=self.partner)
        verified_product = course.create_or_update_seat('verified', True, 50)
        user = UserFactory()
        basket = BasketFactory(owner=user, site=self.site)
        basket.add_product(verified_product, quantity=1)
        order = create_order(number=1, basket=basket, user=user)
        request_data = {
            'organization': 'Dummy Business Client',
            PURCHASER_BEHALF_ATTRIBUTE: 'False',
        }
        # Manually add organization and purchaser attributes on the basket for testing
        basket_add_organization_attribute(basket, request_data)

        EdxOrderPlacementMixin().handle_post_order(order)

        # Now verify that the single seat order is not linked to business
        # client by checking that there is no record for BusinessClient.
        assert not BusinessClient.objects.all()
예제 #16
0
    def test_valid_payment_segment_logging(self, mock_track):
        """
        Verify the "Payment Info Entered" Segment event is fired after payment info is validated
        """
        tracking_context = {'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1'}
        self.user.tracking_context = tracking_context
        self.user.save()

        basket = create_basket(owner=self.user, site=self.site)

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)

        user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(self.user)
        context = {
            'ip': lms_ip,
            'Google Analytics': {
                'clientId': ga_client_id
            },
            'page': {
                'url': 'https://testserver.fake/'
            },
        }

        mixin.handle_payment({}, basket)

        # Verify the correct events are fired to Segment
        calls = []

        properties = translate_basket_line_for_segment(basket.lines.first())
        properties['cart_id'] = basket.id
        calls.append(mock.call(user_tracking_id, 'Product Added', properties, context=context))

        properties = {
            'checkout_id': basket.order_number,
            'step': 1,
            'payment_method': 'Visa | ' + DummyProcessor.NAME,
        }
        calls.append(mock.call(user_tracking_id, 'Checkout Step Completed', properties, context=context))
        properties['step'] = 2
        calls.append(mock.call(user_tracking_id, 'Checkout Step Viewed', properties, context=context))
        calls.append(mock.call(user_tracking_id, 'Checkout Step Completed', properties, context=context))

        properties = {'checkout_id': basket.order_number}
        calls.append(mock.call(user_tracking_id, 'Payment Info Entered', properties, context=context))

        mock_track.assert_has_calls(calls)
예제 #17
0
 def test_handle_successful_order_no_segment_key(self, mock_track):
     """
     Ensure that tracking events do not fire when there is no Segment key
     configured.
     """
     EdxOrderPlacementMixin().handle_successful_order(self.order)
     # ensure no event was fired
     self.assertFalse(mock_track.called)
예제 #18
0
    def test_place_free_order(self, __):
        """ Verify an order is placed and the basket is submitted. """
        basket = create_basket(empty=True)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=0))
        order = EdxOrderPlacementMixin().place_free_order(basket)

        self.assertIsNotNone(order)
        self.assertEqual(basket.status, Basket.SUBMITTED)
예제 #19
0
    def test_duplicate_order_attempt_logging(self):
        """
        Verify that attempts at creation of a duplicate order are logged correctly
        """
        prior_order = create_order()
        dummy_request = RequestFactory(SERVER_NAME='testserver.fake').get('')
        dummy_mixin = EdxOrderPlacementMixin()
        dummy_mixin.payment_processor = Cybersource(self.site)

        with LogCapture(self.DUPLICATE_ORDER_LOGGER_NAME) as lc:
            with self.assertRaises(ValueError):
                dummy_mixin.create_order(dummy_request, prior_order.basket,
                                         None)
                lc.check((self.DUPLICATE_ORDER_LOGGER_NAME, 'ERROR',
                          self.get_duplicate_order_error_message(
                              payment_processor='Cybersource',
                              order=prior_order)), )
예제 #20
0
    def test_handle_successful_free_order(self, mock_track):
        """Verify that tracking events are not emitted for free orders."""
        order = self.create_order(free=True, status=ORDER.OPEN)
        EdxOrderPlacementMixin().handle_successful_order(order)

        # Ensure that only 'Product Added' event is emitted (in order creation) and not 'Order Completion' event.
        self.assertEqual(mock_track.call_count, 1)
        event_name = mock_track.call_args[0][1]
        self.assertEqual(event_name, 'Product Added')
예제 #21
0
    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification, and create Source and PaymentEvent
        objects.
        """
        user = factories.UserFactory()
        basket = factories.create_basket()
        basket.owner = user
        basket.save()

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)
        processor_name = DummyProcessor.NAME
        total = basket.total_incl_tax
        reference = basket.id

        with LogCapture(LOGGER_NAME) as l:
            mixin.handle_payment({}, basket)
            l.check(
                (
                    LOGGER_NAME,
                    'INFO',
                    'payment_received: amount="{}", basket_id="{}", currency="{}", '
                    'processor_name="{}", reference="{}", user_id="{}"'.format(
                        total,
                        basket.id,
                        basket.currency,
                        processor_name,
                        reference,
                        user.id
                    )
                )
            )

        # pylint: disable=protected-access

        # Validate a payment Source was created
        source_type = SourceType.objects.get(code=processor_name)
        label = user.username
        self.assert_basket_matches_source(basket, mixin._payment_sources[-1], source_type, reference, label)

        # Validate the PaymentEvent was created
        paid_type = PaymentEventType.objects.get(code='paid')
        self.assert_valid_payment_event_fields(mixin._payment_events[-1], total, paid_type, processor_name, reference)
예제 #22
0
    def test_handle_successful_order_with_email_opt_in(self, expected_opt_in, _):
        """
        Verify that the post checkout sets email_opt_in if it is given.
        """
        BasketAttribute.objects.get_or_create(
            basket=self.order.basket,
            attribute_type=BasketAttributeType.objects.get(name=EMAIL_OPT_IN_ATTRIBUTE),
            value_text=expected_opt_in,
        )

        with mock.patch('ecommerce.extensions.checkout.mixins.post_checkout.send') as mock_send:
            mixin = EdxOrderPlacementMixin()
            mixin.handle_successful_order(self.order)
            send_arguments = {
                'sender': mixin,
                'order': self.order,
                'request': None,
                'email_opt_in': expected_opt_in,
            }
            mock_send.assert_called_once_with(**send_arguments)
예제 #23
0
 def test_handle_successful_order_segment_error(self, mock_track):
     """
     Ensure that exceptions raised while emitting tracking events are
     logged, but do not otherwise interrupt program flow.
     """
     with patch('ecommerce.extensions.analytics.utils.logger.exception') as mock_log_exc:
         mock_track.side_effect = Exception("clunk")
         EdxOrderPlacementMixin().handle_successful_order(self.order)
     # ensure that analytics.track was called, but the exception was caught
     self.assertTrue(mock_track.called)
     # ensure we logged a warning.
     self.assertTrue(mock_log_exc.called_with("Failed to emit tracking event upon order placement."))
예제 #24
0
 def test_handle_successful_order_no_context(self, mock_track):
     """
     Ensure that expected values are substituted when no tracking_context
     was available.
     """
     EdxOrderPlacementMixin().handle_successful_order(self.order)
     # ensure event is being tracked
     self.assertTrue(mock_track.called)
     # ensure event data is correct
     self.assert_correct_event(mock_track, self.order,
                               'ecommerce-{}'.format(self.user.id), None,
                               None, self.order.number, self.order.currency,
                               self.order.total_excl_tax)
예제 #25
0
    def test_send_confirmation_message(self, __):
        """
        Verify the send confirmation message override functions as expected
        """
        request = RequestFactory()
        user = self.create_user()
        user.email = '*****@*****.**'
        request.user = user
        site_from_email = '*****@*****.**'
        site_configuration = SiteConfigurationFactory(partner__name='Tester', from_email=site_from_email)
        request.site = site_configuration.site
        order = factories.create_order()
        order.user = user
        mixin = EdxOrderPlacementMixin()
        mixin.request = request

        # Happy path
        mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
        self.assertEqual(mail.outbox[0].from_email, site_from_email)
        mail.outbox = []

        # Invalid code path (graceful exit)
        mixin.send_confirmation_message(order, 'INVALID_CODE', request.site)
        self.assertEqual(len(mail.outbox), 0)

        # Invalid messages container path (graceful exit)
        with patch('ecommerce.extensions.checkout.mixins.CommunicationEventType.objects.get') as mock_get:
            mock_event_type = Mock()
            mock_event_type.get_messages.return_value = {}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
            self.assertEqual(len(mail.outbox), 0)

            mock_event_type.get_messages.return_value = {'body': None}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
            self.assertEqual(len(mail.outbox), 0)
예제 #26
0
    def test_update_assigned_voucher_offer_assignment(self, __):
        """
        Verify the "update_assigned_voucher_offer_assignment" works as expected.
        """
        enterprise_offer = EnterpriseOfferFactory()
        voucher = VoucherFactory()
        voucher.offers.add(enterprise_offer)
        basket = create_basket(owner=self.user, site=self.site)
        basket.vouchers.add(voucher)
        order = create_order(user=self.user, basket=basket)
        voucher_application = VoucherApplication.objects.create(voucher=voucher, user=self.user, order=order)
        offer_assignment = OfferAssignmentFactory(offer=enterprise_offer, code=voucher.code, user_email=self.user.email)

        EdxOrderPlacementMixin().update_assigned_voucher_offer_assignment(order)

        offer_assignment = OfferAssignment.objects.get(id=offer_assignment.id)
        assert offer_assignment.status == OFFER_REDEEMED
        assert offer_assignment.voucher_application == voucher_application
예제 #27
0
    def test_update_assigned_voucher_offer_assignment(self, __):
        """
        Verify the "update_assigned_voucher_offer_assignment" works as expected.
        """
        enterprise_offer = EnterpriseOfferFactory()
        voucher = VoucherFactory()
        voucher.offers.add(enterprise_offer)
        basket = create_basket(owner=self.user, site=self.site)
        basket.vouchers.add(voucher)
        order = create_order(user=self.user, basket=basket)
        voucher_application = VoucherApplication.objects.create(
            voucher=voucher, user=self.user, order=order)
        offer_assignment = OfferAssignmentFactory(offer=enterprise_offer,
                                                  code=voucher.code,
                                                  user_email=self.user.email)

        # create nudge email templates and subscription records
        for email_type in (DAY3, DAY10, DAY19):
            nudge_email_template = CodeAssignmentNudgeEmailTemplatesFactory(
                email_type=email_type)
            nudge_email = CodeAssignmentNudgeEmailsFactory(
                email_template=nudge_email_template,
                user_email=self.user.email,
                code=voucher.code)

            # verify subscription is active
            assert nudge_email.is_subscribed

        EdxOrderPlacementMixin().update_assigned_voucher_offer_assignment(
            order)

        offer_assignment = OfferAssignment.objects.get(id=offer_assignment.id)
        assert offer_assignment.status == OFFER_REDEEMED
        assert offer_assignment.voucher_application == voucher_application

        # verify that nudge emails subscriptions are inactive
        assert CodeAssignmentNudgeEmails.objects.filter(
            is_subscribed=True).count() == 0
        assert CodeAssignmentNudgeEmails.objects.filter(
            code__in=[voucher.code],
            user_email__in=[self.user.email],
            is_subscribed=False).count() == 3
예제 #28
0
 def test_handle_successful_order_no_context(self, mock_track):
     """
     Ensure that expected values are substituted when no tracking_context
     was available.
     """
     EdxOrderPlacementMixin().handle_successful_order(self.order)
     # ensure event is being tracked
     self.assertTrue(mock_track.called)
     # ensure event data is correct
     self.assert_correct_event(
         mock_track,
         self.order,
         ECOM_TRACKING_ID_FMT.format(self.user.id),
         None,
         None,
         self.order.number,
         self.order.currency,
         self.order.user.email,
         self.order.total_excl_tax,
         self.order.total_excl_tax            # value for revenue field is same as total.
     )
예제 #29
0
    def handle(self, *args, **options):
        site_id = options.get('site_id')
        try:
            site = Site.objects.get(id=site_id)
        except Site.DoesNotExist:
            logger.exception('Site id %s does not exist', site_id)
            raise Exception

        try:
            site_configurations = SiteConfiguration.objects.get(site=site)
            configuration_helpers = site_configurations.edly_client_theme_branding_settings
            voucher_api_url = configuration_helpers.get(
                'LUMSXPAY_VOUCHER_API_URL')
            if not voucher_api_url:
                logger.exception(
                    'Cron Job of Update Payment Statuses is canceled due to no '
                    'LUMSXPAY_VOUCHER_API_URL in client theme branding')
                raise Exception
        except:
            logger.exception(
                'Site Configurations with side id %s does not exist', site_id)
            raise Exception

        try:
            unpaid_challan_baskets = BasketChallanVoucher.objects.filter(
                is_paid=False)
        except:
            logger.exception(
                'could not fetch the unpaid challan baskets from Database')
            raise Exception

        headers = {
            "Authorization":
            configuration_helpers.get('PAYMENT_AUTHORIZATION_KEY'),
            "Content-Type": "application/json"
        }

        paid_vouchers = []
        unpaid_vouchers = unpaid_challan_baskets.values_list('voucher_number',
                                                             flat=True)
        if unpaid_challan_baskets:
            for unpaid_vouchers_lst in list(
                    self.equal_divided_chunks(list(unpaid_vouchers),
                                              self.VOUCHERS_PER_REQUEST)):
                unpaid_vouchers_str = ','.join(unpaid_vouchers_lst)
                url = '{}/{}'.format(voucher_api_url, unpaid_vouchers_str)

                response = requests.get(url, headers=headers)
                if response.status_code == 200:
                    voucher_details = response.json()

                    voucher_data = voucher_details['data']
                    if not isinstance(voucher_details['data'], list):
                        voucher_data = [voucher_details['data']]

                    for voucher in voucher_data:
                        if voucher['paid_status'].lower() == 'paid':
                            paid_vouchers.append(voucher['voucher_id'])
                else:
                    logger.info('VOUCHER API doesnot return 200 OK')
                    return
        else:
            logger.info('No unpaid voucher found for update payment status')
            return

        if not paid_vouchers:
            logger.info('No voucher paid so exiting the job')
            return

        unpaid_basket_ids = unpaid_challan_baskets.filter(
            voucher_number__in=paid_vouchers).values_list('basket_id',
                                                          flat=True)

        paid_baskets = Basket.objects.filter(id__in=unpaid_basket_ids,
                                             status=Basket.OPEN)

        if not paid_baskets:
            logger.info(
                'ERROR: Basket corresponding to voucher does not exist')
            raise Exception

        for basket in paid_baskets:
            shipping_method = NoShippingRequired()
            shipping_charge = shipping_method.calculate(basket)
            basket.strategy = DefaultStrategy()
            order_total = OrderTotalCalculator().calculate(
                basket, shipping_charge)
            user = basket.owner
            billing_address = None
            request = RequestFactory()
            request.site = site
            request.user = user
            request.site.siteconfiguration = site_configurations
            set_thread_variable('request', request)
            order = EdxOrderPlacementMixin().handle_order_placement(
                order_number=basket.order_number,
                user=user,
                basket=basket,
                shipping_address=None,
                shipping_method=shipping_method,
                shipping_charge=shipping_charge,
                billing_address=billing_address,
                order_total=order_total,
                request=request)

            EdxOrderPlacementMixin().handle_post_order(order)
            challan_voucher_basket = BasketChallanVoucher.objects.filter(
                basket_id=basket.id)

            if len(challan_voucher_basket) > 1:
                logger.info(
                    'more than one basket exist with same id in challan table.'
                )
            elif challan_voucher_basket:
                challan_voucher_basket.update(is_paid=True)

        logger.info(
            'Successfully finished the cron job for updating the order payment'
        )
        return
예제 #30
0
    def test_send_confirmation_message(self, __):
        """
        Verify the send confirmation message override functions as expected
        """
        request = RequestFactory()
        user = self.create_user()
        user.email = '*****@*****.**'
        request.user = user
        site_from_email = '*****@*****.**'
        site_configuration = SiteConfigurationFactory(
            partner__name='Tester', from_email=site_from_email)
        request.site = site_configuration.site
        order = factories.create_order()
        order.user = user
        mixin = EdxOrderPlacementMixin()
        mixin.request = request

        # Happy path
        mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
        self.assertEqual(mail.outbox[0].from_email, site_from_email)
        mail.outbox = []

        # Invalid code path (graceful exit)
        mixin.send_confirmation_message(order, 'INVALID_CODE', request.site)
        self.assertEqual(len(mail.outbox), 0)

        # Invalid messages container path (graceful exit)
        with patch(
                'ecommerce.extensions.checkout.mixins.CommunicationEventType.objects.get'
        ) as mock_get:
            mock_event_type = Mock()
            mock_event_type.get_messages.return_value = {}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED',
                                            request.site)
            self.assertEqual(len(mail.outbox), 0)

            mock_event_type.get_messages.return_value = {'body': None}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED',
                                            request.site)
            self.assertEqual(len(mail.outbox), 0)