def setUp(self): super(DefaultStrategyTests, self).setUp() self.strategy = DefaultStrategy() course = CourseFactory(id='a/b/c', name='Demo Course', partner=self.partner) self.honor_seat = course.create_or_update_seat('honor', False, 0)
class DefaultStrategyTests(CourseCatalogTestMixin, TestCase): def setUp(self): super(DefaultStrategyTests, self).setUp() self.strategy = DefaultStrategy() course = Course.objects.create(id='a/b/c', name='Demo Course') self.honor_seat = course.create_or_update_seat('honor', False, 0) def test_seat_class(self): """ Verify the property returns the course seat Product Class. """ self.assertEqual(self.strategy.seat_class, self.seat_product_class) def test_availability_policy_not_expired(self): """ If the course seat's expiration date has not passed, the seat should be available for purchase. """ product = self.honor_seat product.expires = None stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(self.honor_seat, stock_record) self.assertIsInstance(actual, availability.Available) product.expires = pytz.utc.localize(datetime.datetime.max) actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Available) def test_availability_policy_expired(self): """ If the course seat's expiration date has passed, the seat should NOT be available for purchase. """ product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Unavailable)
def assert_expired_product_availability(self, is_staff, available): request = RequestFactory() request.user = self.create_user(is_staff=is_staff) strategy = DefaultStrategy(request) product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, available)
class DefaultStrategyTests(DiscoveryTestMixin, TestCase): def setUp(self): super(DefaultStrategyTests, self).setUp() self.strategy = DefaultStrategy() course = CourseFactory(id='a/b/c', name='Demo Course', partner=self.partner) self.honor_seat = course.create_or_update_seat('honor', False, 0) def test_seat_class(self): """ Verify the property returns the course seat Product Class. """ self.assertEqual(self.strategy.seat_class, self.seat_product_class) def test_availability_policy_not_expired(self): """ If the course seat's expiration date has not passed, the seat should be available for purchase. """ product = self.honor_seat product.expires = None stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(self.honor_seat, stock_record) self.assertIsInstance(actual, availability.Available) product.expires = pytz.utc.localize(datetime.datetime.max) actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Available) def test_availability_policy_expired(self): """ If the course seat's expiration date has passed, the seat should NOT be available for purchase. """ product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Unavailable) @ddt.unpack @ddt.data( (True, availability.Available), (False, availability.Unavailable), ) def test_expired_seats_availability_for_users(self, is_staff, available): """ A product is unavailable for students if the current date is beyond the product's expiration date. But for Admin products are always available. """ self.assert_expired_product_availability(is_staff, available) def assert_expired_product_availability(self, is_staff, available): request = RequestFactory() request.user = self.create_user(is_staff=is_staff) strategy = DefaultStrategy(request) product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, available)
class DefaultStrategyTests(CourseCatalogTestMixin, UserMixin, PartnerMixin, TestCase): def setUp(self): super(DefaultStrategyTests, self).setUp() self.strategy = DefaultStrategy() course = Course.objects.create(id='a/b/c', name='Demo Course') partner = self.create_partner('edx') self.honor_seat = course.create_or_update_seat('honor', False, 0, partner) def test_seat_class(self): """ Verify the property returns the course seat Product Class. """ self.assertEqual(self.strategy.seat_class, self.seat_product_class) def test_availability_policy_not_expired(self): """ If the course seat's expiration date has not passed, the seat should be available for purchase. """ product = self.honor_seat product.expires = None stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(self.honor_seat, stock_record) self.assertIsInstance(actual, availability.Available) product.expires = pytz.utc.localize(datetime.datetime.max) actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Available) def test_availability_policy_expired(self): """ If the course seat's expiration date has passed, the seat should NOT be available for purchase. """ product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = self.strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, availability.Unavailable) @ddt.unpack @ddt.data( (True, availability.Available), (False, availability.Unavailable), ) def test_expired_seats_availability_for_users(self, is_staff, available): """ A product is unavailable for students if the current date is beyond the product's expiration date. But for Admin products are always available. """ self.assert_expired_product_availability(is_staff, available) def assert_expired_product_availability(self, is_staff, available): request = RequestFactory() request.user = self.create_user(is_staff=is_staff) strategy = DefaultStrategy(request) product = self.honor_seat product.expires = pytz.utc.localize(datetime.datetime.min) stock_record = product.stockrecords.first() actual = strategy.availability_policy(product, stock_record) self.assertIsInstance(actual, available)
def test_get_enterprise_customer_from_enterprise_offer(self, discount_value): """ Verify that "get_enterprise_customer_from_enterprise_offer" returns `None` if expected conditions are not met. """ course = CourseFactory(name='EnterpriseConsentErrorTest', partner=PartnerFactory()) product = course.create_or_update_seat('verified', False, 50) benefit = EnterprisePercentageDiscountBenefitFactory(value=discount_value) offer = EnterpriseOfferFactory(benefit=benefit) # set wrong priority to invalidate the condition in util offer.priority = 111 self.mock_enterprise_learner_api( learner_id=self.learner.id, enterprise_customer_uuid=str(offer.condition.enterprise_customer_uuid), course_run_id=course.id, ) self.mock_catalog_contains_course_runs( [course.id], str(offer.condition.enterprise_customer_uuid), enterprise_customer_catalog_uuid=str(offer.condition.enterprise_customer_catalog_uuid), contains_content=True, ) basket = BasketFactory(site=self.site, owner=self.create_user()) basket.add_product(product) basket.strategy = DefaultStrategy() Applicator().apply_offers(basket, [offer]) self.assertIsNone(get_enterprise_customer_from_enterprise_offer(basket))
def test_offer(self): # Our offer is for 100%, so all lines should end up with a price of 0. offer = factories.ProgramOfferFactory(benefit=factories.PercentageDiscountBenefitWithoutRangeFactory(value=100)) basket = factories.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) # Add one course run seat from each course to the basket. 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: 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) # 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)
def prepare_get_offers_response(self, quantity=1, seat_type='verified', seats=None): """Helper method for creating response the voucher offers endpoint. Args: quantity (int): Number of course runs Returns: The products, request and vouchers created. """ course_run_info = { 'count': quantity, 'next': 'path/to/the/next/page', 'results': [] } products = [] new_range, __ = Range.objects.get_or_create( catalog_query='*:*', course_seat_types=seat_type) if seats: for seat in seats: course_run_info['results'].append({ 'image': { 'src': 'path/to/the/course/image' }, 'key': seat.course_id, 'start': '2016-05-01T00:00:00Z', 'title': seat.title, 'enrollment_end': None }) new_range.add_product(seat) else: for _ in range(quantity): course, seat = self.create_course_and_seat(seat_type=seat_type) course_run_info['results'].append({ 'image': { 'src': 'path/to/the/course/image' }, 'key': course.id, 'start': '2016-05-01T00:00:00Z', 'title': course.name, 'enrollment_end': None }) new_range.add_product(seat) products.append(seat) self.mock_course_runs_endpoint( self.site_configuration.discovery_api_url, query='*:*', course_run_info=course_run_info) voucher, __ = prepare_voucher(_range=new_range) factory = APIRequestFactory() request = factory.get('/?code={}&page_size=6'.format(voucher.code)) request.site = self.site request.user = self.user request.strategy = DefaultStrategy() return products, request, voucher
def test_offer(self): # Our offer is for 100%, so all lines should end up with a price of 0. offer = factories.ProgramOfferFactory( site=self.site, benefit=factories.PercentageDiscountBenefitWithoutRangeFactory(value=100) ) basket = factories.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) # 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)
def setUp(self): super(EnterpriseAPITests, self).setUp() self.course_run = CourseFactory() self.learner = self.create_user(is_staff=True) self.client.login(username=self.learner.username, password=self.password) # Enable enterprise functionality toggle_switch(settings.ENABLE_ENTERPRISE_ON_RUNTIME_SWITCH, True) self.request.user = self.learner self.request.site = self.site self.request.strategy = DefaultStrategy()
def test_omitting_unavailable_seats(self): """ Verify an unavailable seat is omitted from offer page results. """ course1, seat1 = self.create_course_and_seat() course2, seat2 = self.create_course_and_seat() course_run_info = { 'count': 2, 'next': 'path/to/the/next/page', 'results': [{ 'key': course1.id, 'title': course1.name, 'start': '2016-05-01T00:00:00Z', 'image': { 'src': 'path/to/the/course/image' } }, { 'key': course2.id, 'title': course2.name, 'start': '2016-05-01T00:00:00Z', 'image': { 'src': 'path/to/the/course/image' } }] } self.mock_dynamic_catalog_course_runs_api( query='*:*', course_run_info=course_run_info) new_range, __ = Range.objects.get_or_create(catalog_query='*:*') new_range.add_product(seat1) new_range.add_product(seat2) voucher, __ = prepare_voucher(_range=new_range) voucher, products = get_voucher_and_products_from_code(voucher.code) factory = APIRequestFactory() request = factory.get('/?code={}&page_size=6'.format(voucher.code)) request.site = self.site request.strategy = DefaultStrategy() offers = VoucherViewSet().get_offers(products=products, request=request, voucher=voucher)['results'] self.assertEqual(len(offers), 2) products[1].expires = pytz.utc.localize(datetime.datetime.min) offers = VoucherViewSet().get_offers(products=products, request=request, voucher=voucher)['results'] self.assertEqual(len(offers), 1)
def setUp(self): super(EntitlementsTests, self).setUp() self.learner = self.create_user(is_staff=True) self.client.login(username=self.learner.username, password=self.password) # Enable enterprise functionality toggle_switch(settings.ENABLE_ENTERPRISE_ON_RUNTIME_SWITCH, True) self.course = CourseFactory(id='edx/Demo_Course/DemoX') course_seat = self.course.create_or_update_seat('verified', False, 100, partner=self.partner) stock_record = StockRecord.objects.get(product=course_seat) self.catalog = Catalog.objects.create(partner=self.partner) self.catalog.stock_records.add(stock_record) self.request.user = self.learner self.request.site = self.site self.request.strategy = DefaultStrategy()
def setUp(self): super(DefaultStrategyTests, self).setUp() self.strategy = DefaultStrategy() course = Course.objects.create(id='a/b/c', name='Demo Course') self.honor_seat = course.create_or_update_seat('honor', False, 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
def prepare_offers_listing_request(self, code): factory = APIRequestFactory() request = factory.get('/?code={}&page_size=6'.format(code)) request.site = self.site request.strategy = DefaultStrategy() return request