Exemplo n.º 1
0
class TestOfferApplicator(TestCase):

    def setUp(self):
        self.applicator = Applicator()
        self.basket = BasketFactory()
        rng = RangeFactory(includes_all_products=True)
        self.condition = ConditionFactory(
            range=rng, type=ConditionFactory._meta.model.VALUE,
            value=D('100'), proxy_class=None)
        self.benefit = BenefitFactory(
            range=rng, type=BenefitFactory._meta.model.FIXED,
            value=D('10'))

    def test_applies_offer_multiple_times_by_default(self):
        add_product(self.basket, D('100'), 5)
        offer = ConditionalOfferFactory(
            pk=1, condition=self.condition, benefit=self.benefit)
        self.applicator.apply
        self.applicator.apply_offers(self.basket, [offer])
        line = self.basket.all_lines()[0]
        self.assertTrue(line.quantity_with_offer_discount(offer) == 5)

    def test_respects_maximum_applications_field(self):
        add_product(self.basket, D('100'), 5)
        offer = ConditionalOfferFactory(
            pk=1, condition=self.condition, benefit=self.benefit,
            max_basket_applications=1)
        self.applicator.apply_offers(self.basket, [offer])
        line = self.basket.all_lines()[0]
        self.assertTrue(line.quantity_with_offer_discount(offer) == 5)
        applications = self.basket.offer_applications.applications
        self.assertTrue(applications[1]['freq'] == 1)

    def test_uses_offers_in_order_of_descending_priority(self):
        self.applicator.get_site_offers = Mock(
            return_value=[models.ConditionalOffer(
                name="offer1", condition=self.condition, benefit=self.benefit,
                priority=1)])

        self.applicator.get_user_offers = Mock(
            return_value=[models.ConditionalOffer(
                name="offer2", condition=self.condition, benefit=self.benefit,
                priority=-1)])

        offers = self.applicator.get_offers(self.basket)
        priorities = [offer.priority for offer in offers]
        self.assertEqual(sorted(priorities, reverse=True), priorities)

    def test_get_site_offers(self):
        models.ConditionalOffer.objects.create(
            name="globaloffer", condition=self.condition,
            benefit=self.benefit, offer_type=models.ConditionalOffer.SITE)
        models.ConditionalOffer.objects.create(
            name="sessionoffer", condition=self.condition,
            benefit=self.benefit, offer_type=models.ConditionalOffer.SESSION)

        site_offers = Applicator().get_site_offers()
        # Only one offer should be returned
        self.assertEqual(len(site_offers), 1)
        self.assertEqual(site_offers[0].name, "globaloffer")
Exemplo n.º 2
0
class TestOfferApplicator(TestCase):

    def setUp(self):
        self.applicator = Applicator()
        self.basket = BasketFactory()
        rng = RangeFactory(includes_all_products=True)
        self.condition = ConditionFactory(
            range=rng, type=ConditionFactory._meta.model.VALUE,
            value=D('100'), proxy_class=None)
        self.benefit = BenefitFactory(
            range=rng, type=BenefitFactory._meta.model.FIXED,
            value=D('10'))

    def test_applies_offer_multiple_times_by_default(self):
        add_product(self.basket, D('100'), 5)
        offer = ConditionalOfferFactory(
            pk=1, condition=self.condition, benefit=self.benefit)
        self.applicator.apply
        self.applicator.apply_offers(self.basket, [offer])
        line = self.basket.all_lines()[0]
        self.assertTrue(line.quantity_with_offer_discount(offer) == 5)

    def test_respects_maximum_applications_field(self):
        add_product(self.basket, D('100'), 5)
        offer = ConditionalOfferFactory(
            pk=1, condition=self.condition, benefit=self.benefit,
            max_basket_applications=1)
        self.applicator.apply_offers(self.basket, [offer])
        line = self.basket.all_lines()[0]
        self.assertTrue(line.quantity_with_offer_discount(offer) == 5)
        applications = self.basket.offer_applications.applications
        self.assertTrue(applications[1]['freq'] == 1)

    def test_uses_offers_in_order_of_descending_priority(self):
        self.applicator.get_site_offers = Mock(
            return_value=[models.ConditionalOffer(
                name="offer1", condition=self.condition, benefit=self.benefit,
                priority=1)])

        self.applicator.get_user_offers = Mock(
            return_value=[models.ConditionalOffer(
                name="offer2", condition=self.condition, benefit=self.benefit,
                priority=-1)])

        offers = self.applicator.get_offers(self.basket)
        priorities = [offer.priority for offer in offers]
        self.assertEqual(sorted(priorities, reverse=True), priorities)

    def test_get_site_offers(self):
        models.ConditionalOffer.objects.create(
            name="globaloffer", condition=self.condition,
            benefit=self.benefit, offer_type=models.ConditionalOffer.SITE)
        models.ConditionalOffer.objects.create(
            name="sessionoffer", condition=self.condition,
            benefit=self.benefit, offer_type=models.ConditionalOffer.SESSION)

        site_offers = Applicator().get_site_offers()
        # Only one offer should be returned
        self.assertEqual(len(site_offers), 1)
        self.assertEqual(site_offers[0].name, "globaloffer")
Exemplo n.º 3
0
 def test_basket_lines_queryset_is_ordered(self):
     # This is needed to make sure a formset is not performing the query
     # again with an order_by clause (losing all calculated discounts)
     basket = BasketFactory()
     product = ProductFactory(title="A product")
     another_product = ProductFactory(title="Another product")
     basket.add_product(product)
     basket.add_product(another_product)
     queryset = basket.all_lines()
     self.assertTrue(queryset.ordered)
Exemplo n.º 4
0
 def test_basket_lines_queryset_is_ordered(self):
     # This is needed to make sure a formset is not performing the query
     # again with an order_by clause (losing all calculated discounts)
     basket = BasketFactory()
     product = ProductFactory(title="A product")
     another_product = ProductFactory(title="Another product")
     basket.add_product(product)
     basket.add_product(another_product)
     queryset = basket.all_lines()
     self.assertTrue(queryset.ordered)
Exemplo n.º 5
0
    def test_available_with_offer(self):
        basket = BasketFactory()
        product1 = ProductFactory()
        product2 = ProductFactory()
        basket.add_product(product1, quantity=1)
        basket.add_product(product2, quantity=10)

        benefit = models.Benefit(
            type=models.Benefit.PERCENTAGE,
            value=10,
            max_affected_items=5,
        )
        benefit.save()

        offer1 = ConditionalOfferFactory(name='offer1', benefit=benefit)
        lines = basket.all_lines()
        assert lines[0].consumer.available(offer1) == 1
        assert lines[1].consumer.available(offer1) == 5
Exemplo n.º 6
0
class JournalBundleConditionTests(TestCase, JournalMixin):
    def setUp(self):
        super(JournalBundleConditionTests, self).setUp()
        user = self.create_user(is_staff=True)
        self.client.login(username=user.username, password=self.password)

        self.condition = factories.JournalConditionFactory()
        self.offer = factories.JournalBundleOfferFactory(
            partner=self.partner, condition=self.condition)
        self.basket = BasketFactory(site=self.site, owner=UserFactory())
        self.basket.add_product(self.create_product(self.client), 1)

    def test_name(self, mocked_journal_api_response):
        """ The name should contain the program's UUID. """
        mocked_journal_api_response.return_value = None
        expected = 'Basket contains every product in bundle {}'.format(
            self.condition.journal_bundle_uuid)
        self.assertEqual(self.condition.name, expected)

    def test_is_satisfied_with_empty_basket(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with empty basket """
        mocked_journal_api_response.return_value = None
        self.basket.flush()
        self.assertTrue(self.basket.is_empty)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_exception(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'HttpNotFoundError' exception  """
        mocked_journal_api_response.side_effect = HttpNotFoundError
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_slumber_exception(self,
                                                 mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'SlumberBaseException' exception  """
        mocked_journal_api_response.side_effect = SlumberBaseException
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_timeout(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'Timeout' exception  """
        mocked_journal_api_response.side_effect = Timeout
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_without_journal_bundle(self,
                                                 mocked_journal_api_response):
        """ Test the 'is_satisfied' without Journal bundle """
        mocked_journal_api_response.return_value = None
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_without_courses(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' without courses in Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            empty_courses=True)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_some_but_not_all_courses(
            self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with only some of the courses in the Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            multiple_courses=True)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_dummy_product(self,
                                             mocked_journal_api_response):
        """ Test the 'is_satisfied' with dummy product in basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.basket.add_product(
            self.create_product(
                self.client, data=self.get_data_for_create(sku="dummy-sku")),
            1)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_valid_data(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with valid Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            empty_journals=True)
        self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))

    def test_get_applicable_lines(self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' with valid product in basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        applicable_lines = [(line.product.stockrecords.first().price_excl_tax,
                             line) for line in self.basket.all_lines()]
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket),
            applicable_lines)

    def test_get_applicable_lines_with_empty_basket(
            self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' with empty basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    def test_get_applicable_lines_sku_not_in_basket(
            self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where the sku is not in the basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.basket.add_product(
            self.create_product(
                self.client, data=self.get_data_for_create(sku="dummy-sku")),
            1)
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    @mock.patch(
        "ecommerce.extensions.catalogue.models.Product.get_is_discountable")
    def test_get_applicable_lines_product_is_not_discountable(
            self, mocked_product_is_discountable, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where the product is not discountable """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        mocked_product_is_discountable.return_value = False

        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    @mock.patch("oscar.apps.offer.utils.unit_price")
    def test_get_applicable_lines_no_price(self, mocked_unit_price,
                                           mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where there is no price """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        mocked_unit_price.return_value = None
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    def test_get_applicable_lines_no_journal_bundle(self,
                                                    mock_journal_api_response):
        """ Test 'get_applicable_lines' where the journal bundle is None """
        mock_journal_api_response.return_value = None
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])
Exemplo n.º 7
0
    def test_offer(self):
        # Our offer is for 100%, so all lines should end up with a price of 0.
        offer = factories.ProgramOfferFactory(
            partner=self.partner,
            benefit=factories.PercentageDiscountBenefitWithoutRangeFactory(
                value=100))
        basket = BasketFactory(site=self.site, owner=self.create_user())

        program_uuid = offer.condition.program_uuid
        program = self.mock_program_detail_endpoint(
            program_uuid, self.site_configuration.discovery_api_url)
        self.mock_user_data(basket.owner.username)

        # Add one course run seat from each course to the basket.
        products = []
        for course in program['courses']:
            course_run = Course.objects.get(id=course['course_runs'][0]['key'])
            for seat in course_run.seat_products:
                if seat.attr.id_verification_required:
                    products.append(seat)
                    basket.add_product(seat)

        # No discounts should be applied, and each line should have a price of 100.00.
        self.assertEqual(len(basket.offer_applications), 0)
        self.assertEqual(basket.total_discount, 0)
        for line in basket.all_lines():
            self.assertEqual(line.line_price_incl_tax_incl_discounts,
                             Decimal(100))

        # Apply the offers as Oscar will in a request
        basket.strategy = DefaultStrategy()
        Applicator().apply(basket, basket.owner, bundle_id=program_uuid)

        # Our discount should be applied, and each line should have a price of 0
        lines = basket.all_lines()
        self.assertEqual(len(basket.offer_applications), 1)
        self.assertEqual(basket.total_discount, Decimal(100) * len(lines))
        for line in lines:
            self.assertEqual(line.line_price_incl_tax_incl_discounts, 0)

        # Reset the basket and add a voucher.
        basket.reset_offer_applications()
        product_range = RangeFactory(products=products)
        voucher, __ = factories.prepare_voucher(_range=product_range,
                                                benefit_value=50)
        self.mock_account_api(self.request,
                              basket.owner.username,
                              data={'is_active': True})
        self.client.login(username=basket.owner.username,
                          password=self.password)
        self.client.post(reverse('basket:vouchers-add'),
                         data={'code': voucher.code})
        response = self.client.get(reverse('basket:summary'))
        basket = response.context['basket']

        # Verify that voucher-based offer takes precedence over program offer.
        actual_offer_discounts = [
            discount['offer'] for discount in basket.offer_discounts
        ]
        actual_voucher_discounts = [
            discount['offer'] for discount in basket.voucher_discounts
        ]
        self.assertEqual(actual_offer_discounts, [])
        self.assertEqual(actual_voucher_discounts, [voucher.offers.first()])
        lines = basket.all_lines()
        self.assertEqual(len(basket.offer_applications), 1)
        self.assertEqual(basket.total_discount, Decimal(50) * len(lines))
        for line in lines:
            self.assertEqual(line.line_price_incl_tax_incl_discounts, 50)