def test_absolute_benefit_offer_availability(self): """ Verify that enterprise offer condition returns correct result for an absolute benefit with discount value greater than course price. """ offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=factories.EnterpriseAbsoluteDiscountBenefitFactory( value=150), max_discount=Decimal(300), total_discount=Decimal(200)) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_contains_content_items_failure(self): """ Ensure the condition returns false if the contains_content_item call fails. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str( self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, contains_content=False, raise_exception=True) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_email_status_update_success( self, post_data, response_data, status_code, ): """ Verify the endpoint updated the email status in offer_assignment """ enterprise_offer = factories.EnterpriseOfferFactory(max_global_applications=None) offer_assignment = factories.OfferAssignmentFactory( offer=enterprise_offer, code='jfhrmndk554lwre', user_email='*****@*****.**', ) post_data['offer_assignment_id'] = offer_assignment.id response_data['offer_assignment_id'] = offer_assignment.id response = self.client.post(self.path, data=json.dumps(post_data), content_type='application/json') self.assertEqual(response.status_code, status_code) self.assertDictEqual(response_data, response.json()) updated_offer_assignment = OfferAssignment.objects.get(id=offer_assignment.id) self.assertEqual(updated_offer_assignment.status, OFFER_ASSIGNED)
def test_max_user_applications_clean(self): """ Verify that `clean` for `max_user_applications` field is working as expected. """ num_applications = 3 expected_errors = { 'max_user_applications': [ 'Ensure new value must be greater than or equal to consumed({}) value.' .format(num_applications) ] } # create an enterprise offer that can be used upto 5 times and has already been used 3 times offer = factories.EnterpriseOfferFactory(max_user_applications=5, max_user_discount=500) for _ in range(num_applications): order = OrderFactory(user=self.user, status=ORDER.COMPLETE) OrderDiscountFactory(order=order, offer_id=offer.id, amount=10) # now try to update the offer with max_user_applications set to 2 # which is less than the number of times this offer has already been used data = self.generate_data(max_user_applications=2) self.assert_form_errors(data, expected_errors, instance=offer)
def test_offer_form_with_per_user_increased_limits(self): """ Verify that an existing enterprise offer can be updated with per user increased limits. """ data = self.generate_data(max_user_applications=40, max_user_discount=7000) self.mock_specific_enterprise_customer_api( data['enterprise_customer_uuid']) # create an enterprise offer with both max_global_applications and max_discount offer = factories.EnterpriseOfferFactory(max_user_applications=30, max_user_discount=5000) # now try to update the offer with increased values form = EnterpriseOfferForm(request=self.request, data=data, instance=offer) self.assertTrue(form.is_valid()) offer = form.save() self.assertEqual(offer.max_user_applications, data['max_user_applications']) self.assertEqual(offer.max_user_discount, data['max_user_discount'])
def test_absolute_benefit_offer_availability_with_max_user_discount(self): """ Verify that enterprise offer condition returns correct result for an absolute benefit with discount value greater than course price. """ offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=factories.EnterpriseAbsoluteDiscountBenefitFactory(value=150), max_user_discount=150 ) for _ in range(5): order = OrderFactory(user=self.user, status=ORDER.COMPLETE) OrderDiscountFactory(order=order, offer_id=offer.id, amount=10) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_save_edit(self): """ Previously-created ConditionalOffer, Benefit, and Condition instances should be updated. """ offer = factories.EnterpriseOfferFactory() ecm = EnterpriseContractMetadata( discount_value=self.contract_discount_value, discount_type=self.contract_discount_type, amount_paid=self.prepaid_invoice_amount, ) offer.enterprise_contract_metadata = ecm data = self.generate_data( enterprise_customer_uuid=offer.condition.enterprise_customer_uuid, benefit_type=Benefit.FIXED ) self.mock_specific_enterprise_customer_api(data['enterprise_customer_uuid']) form = EnterpriseOfferForm(request=self.request, data=data, instance=offer) form.is_valid() form.save() offer.refresh_from_db() self.assert_enterprise_offer_conditions( offer, data['enterprise_customer_uuid'], data['enterprise_customer_name'], data['enterprise_customer_catalog_uuid'], data['benefit_value'], data['benefit_type'], 'Discount of type {} provided by {} for {}.'.format( ConditionalOffer.SITE, data['enterprise_customer_name'][:48], data['enterprise_customer_catalog_uuid'] ), data['contract_discount_type'], data['contract_discount_value'], data['prepaid_invoice_amount'], data['sales_force_id'], data['max_global_applications'], data['max_discount'], data['max_user_applications'], data['max_user_discount'], )
def create_data(self, voucher_type, max_uses, assignments): """ Create vouchers, offers and offer assignments. """ codes = {assignment['code'] for assignment in assignments} emails = sorted( {assignment['user_email'] for assignment in assignments}) quantity = len(codes) voucher_types = (Voucher.MULTI_USE, Voucher.ONCE_PER_CUSTOMER, Voucher.MULTI_USE_PER_CUSTOMER) num_of_offers = quantity if voucher_type in voucher_types else 1 offers = [] for __ in range(num_of_offers): offers.append( factories.EnterpriseOfferFactory( max_global_applications=max_uses)) coupon = self.create_coupon(quantity=quantity, voucher_type=voucher_type) for index, info in enumerate( zip(coupon.attr.coupon_vouchers.vouchers.all(), codes)): voucher, code = info voucher.code = code voucher.offers.add(offers[index] if len(offers) > 1 else offers[0]) voucher.save() data = {'codes': codes, 'emails': emails} serializer = CouponCodeAssignmentSerializer(data=data, context={ 'coupon': coupon, 'template': 'An Email Template' }) if serializer.is_valid(): serializer.save() assignments = serializer.data
def test_is_satisfied(self, num_orders, email, offer_status, condition_result, mock_request): """ Ensure that condition returns expected result. """ mock_request.return_value = self.request voucher = factories.VoucherFactory(usage=Voucher.SINGLE_USE, num_orders=num_orders) enterprise_offer = factories.EnterpriseOfferFactory( max_global_applications=None) voucher.offers.add(enterprise_offer) basket = BasketFactory(site=self.site, owner=UserFactory(email=email)) basket.vouchers.add(voucher) factories.OfferAssignmentFactory( offer=enterprise_offer, code=voucher.code, user_email=email, status=offer_status, ) assert self.condition.is_satisfied(enterprise_offer, basket) == condition_result
def test_is_satisfied_with_course_entitlement_request_error(self): """ Ensure the condition returns False if an error occurs while fetching course details. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.entitlement) self.mock_course_detail_endpoint_error( self.entitlement, discovery_api_url=self.site_configuration.discovery_api_url, error=ReqConnectionError ) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str(self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.entitlement.attr.UUID], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_with_course_entitlement(self): """ Ensure the condition returns true if the basket contains a course entitlement. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement ) self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str(self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) self.mock_catalog_contains_course_runs( [self.entitlement.attr.UUID], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid, ) self.assertTrue(self.condition.is_satisfied(offer, basket))
def test_init(self): """ The constructor should pull initial data from the passed-in instance. """ enterprise_offer = factories.EnterpriseOfferFactory() ecm = EnterpriseContractMetadata( discount_value=25, discount_type=EnterpriseContractMetadata.PERCENTAGE, amount_paid=12345) enterprise_offer.enterprise_contract_metadata = ecm form = EnterpriseOfferForm(instance=enterprise_offer) self.assertEqual(uuid.UUID(form['enterprise_customer_uuid'].value()), enterprise_offer.condition.enterprise_customer_uuid) self.assertEqual( uuid.UUID(form['enterprise_customer_catalog_uuid'].value()), enterprise_offer.condition.enterprise_customer_catalog_uuid) self.assertEqual(form['benefit_type'].value(), enterprise_offer.benefit.proxy().benefit_class_type) self.assertEqual(form['benefit_value'].value(), enterprise_offer.benefit.value) self.assertEqual(form['contract_discount_type'].value(), EnterpriseContractMetadata.PERCENTAGE) self.assertEqual(form['contract_discount_value'].value(), 25) self.assertEqual(form['prepaid_invoice_amount'].value(), 12345)
def test_multi_use_per_customer_voucher(self): """ Verify `MULTI_USE_PER_CUSTOMER` behaves as expected. """ voucher_data = dict(self.data, usage=Voucher.MULTI_USE_PER_CUSTOMER) enterprise_offer = factories.EnterpriseOfferFactory( max_global_applications=3) voucher = factories.VoucherFactory(**voucher_data) voucher.offers.add(enterprise_offer) user1 = UserFactory(email='*****@*****.**') user2 = UserFactory(email='*****@*****.**') self.use_voucher(voucher, user1) is_available, message = voucher.is_available_to_user(user1) assert (is_available, message) == (True, '') is_available, message = voucher.is_available_to_user(user2) assert (is_available, message) == (False, 'This voucher is assigned to another user.')
def test_offer_availability_with_max_user_discount(self, discount_type, num_prev_orders, benefit_value, is_satisfied): """ Verify that enterprise offer with discount type percentage and absolute, condition returns correct result based on user limits in the offer. """ benefits = { Benefit.PERCENTAGE: factories.EnterprisePercentageDiscountBenefitFactory( value=benefit_value), Benefit.FIXED: factories.EnterpriseAbsoluteDiscountBenefitFactory( value=benefit_value), } offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=benefits[discount_type], max_user_discount=150) for _ in range(num_prev_orders): order = OrderFactory(user=self.user, status=ORDER.COMPLETE) OrderDiscountFactory(order=order, offer_id=offer.id, amount=10) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertEqual(self.condition.is_satisfied(offer, basket), is_satisfied)
def test_is_satisfied_when_owner_has_no_assignment(self): """ Ensure that condition returns expected result the basket owner has no assignments. # voucher has free slots(3) available, no offer assignment for basket owner, # assignments(2) exist for other users, voucher has some redemptions(num_orders = 2) # basket owner is allowed to redeem the voucher """ code = 'TA7WCQD3T4C7GHZ4' num_orders = 2 max_global_applications = 7 enterprise_offer = factories.EnterpriseOfferFactory(max_global_applications=max_global_applications) voucher = factories.VoucherFactory(usage=Voucher.MULTI_USE, code=code, num_orders=num_orders) voucher.offers.add(enterprise_offer) factories.OfferAssignmentFactory(offer=enterprise_offer, code=code, user_email='*****@*****.**') factories.OfferAssignmentFactory(offer=enterprise_offer, code=code, user_email='*****@*****.**') basket = factories.BasketFactory(site=self.site, owner=factories.UserFactory(email='*****@*****.**')) basket.vouchers.add(voucher) assert self.condition.is_satisfied(enterprise_offer, basket) is True
def test_offer_availability_with_max_discount(self, discount_type, total_discount, benefit_value, is_satisfied): """ Verify that enterprise offer with discount type percentage and absolute, condition returns correct result based on total_discount(consumed discount so far) and discount on course price covered by the offer. """ benefits = { Benefit.PERCENTAGE: factories.EnterprisePercentageDiscountBenefitFactory( value=benefit_value), Benefit.FIXED: factories.EnterpriseAbsoluteDiscountBenefitFactory( value=benefit_value), } offer = factories.EnterpriseOfferFactory( partner=self.partner, benefit=benefits[discount_type], max_discount=Decimal(5000), total_discount=total_discount) basket = BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) basket.add_product(self.entitlement) self.mock_course_detail_endpoint( discovery_api_url=self.site_configuration.discovery_api_url, course=self.entitlement) self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, self.site.siteconfiguration.enterprise_api_url, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) self.assertEqual(self.condition.is_satisfied(offer, basket), is_satisfied)
def test_email_status_update_success( self, post_data, response_data, status_code ): """ Verify the endpoint updated the email status in offer_assignment """ enterprise_offer = factories.EnterpriseOfferFactory(max_global_applications=None) offer_assignment = factories.OfferAssignmentFactory( offer=enterprise_offer, code='jfhrmndk554lwre', user_email='*****@*****.**', status=OFFER_ASSIGNED ) OfferAssignmentEmailAttempt.objects.create(offer_assignment=offer_assignment, send_id=post_data.get('send_id')) with LogCapture(level=logging.INFO) as log: with mock.patch("ecommerce.extensions.api.v2.views.assignmentemail.SailthruClient.receive_hardbounce_post", return_value=True): response = self.client.post(self.path, data=json.dumps(post_data), content_type='application/json') log.check_present() self.assertEqual(response.status_code, status_code) self.assertDictEqual(response_data, response.json()) updated_offer_assignment = OfferAssignment.objects.get(id=offer_assignment.id) self.assertEqual(updated_offer_assignment.status, OFFER_ASSIGNMENT_EMAIL_BOUNCED)
def setup_enterprise_coupon_data(self, mock_learner_api=True): offer = factories.EnterpriseOfferFactory( partner=self.partner, condition=self.condition, offer_type=ConditionalOffer.VOUCHER) basket = factories.BasketFactory(site=self.site, owner=self.user) basket.add_product(self.course_run.seat_products[0]) if mock_learner_api: self.mock_enterprise_learner_api( learner_id=self.user.id, enterprise_customer_uuid=str( self.condition.enterprise_customer_uuid), course_run_id=self.course_run.id, ) else: self.mock_enterprise_learner_api_for_learner_with_no_enterprise() self.mock_catalog_contains_course_runs( [self.course_run.id], self.condition.enterprise_customer_uuid, enterprise_customer_catalog_uuid=self.condition. enterprise_customer_catalog_uuid, ) return offer, basket
def test_is_satisfied_empty_basket(self): """ Ensure the condition returns False if the basket is empty. """ offer = factories.EnterpriseOfferFactory(partner=self.partner, condition=self.condition) basket = factories.BasketFactory(site=self.site, owner=self.user) self.assertTrue(basket.is_empty) self.assertFalse(self.condition.is_satisfied(offer, basket))
def test_is_satisfied_site_mismatch(self): """ Ensure the condition returns False if the offer partner does not match the basket site partner. """ offer = factories.EnterpriseOfferFactory(partner=SiteConfigurationFactory().partner, condition=self.condition) basket = factories.BasketFactory(site=self.site, owner=self.user) basket.add_product(self.test_product) self.assertFalse(self.condition.is_satisfied(offer, basket))
def setUp(self): super(BackfillOpportunityIdsCommandTests, self).setUp() # coupons and offers created for these enterprises are missing opportunity ids in database self.enterprise_without_opportunity_ids = { 'af4b351f-5f1c-4fc3-af41-48bb38fcb161': 'sf100', '8212a8d8-c6b1-4023-8754-4d687c43d72f': 'sf200', } # coupons and offers created for these enterprise have the opportunity id self.enterprise_with_opportunity_id = ('68c7b796-3d9c-4038-a870-3b5eb75c5d39', 'sf999') # create enterprise coupons, enterprise offers and manual orders without opportunity id for enterprise_customer, __ in self.enterprise_without_opportunity_ids.items(): self.init_data(enterprise_customer) # create an enterprise coupon, enterprise offer and manual order offer with opportunity id self.init_data( self.enterprise_with_opportunity_id[0], self.enterprise_with_opportunity_id[1], ) # multi contracts data setup for coupons and offers self.multi_contract_vouchers = [ ['0a434fa0-2edb-416a-ba24-0d504cc8f6d2', 1345, 'Voucher', 3000, 'OID400'], ['0a434fa0-2edb-416a-ba24-0d504cc8f6d2', 2876, 'Voucher', 3000, 'OID400'], ['0a434fa0-2edb-416a-ba24-0d504cc8f6d2', 9034, 'Voucher', 3000, 'OID450'], ['0a434fa0-2edb-416a-ba24-0d504cc8f6d2', 2223, 'Voucher', 3001, 'OID444'], ] created = [] for record in self.multi_contract_vouchers: if record[3] not in created: created.append(record[3]) coupon = self.create_coupon(enterprise_customer=record[0]) coupon.id = record[3] coupon.save() self.multi_contract_ent_offers = [ ['56b250c3-0050-47da-819c-e677fb8c03be', 4000, 'Site', '', 'OID500'], ['56b250c3-0050-47da-819c-e677fb8c03be', 4000, 'Site', '', 'OID500'], ['56b250c3-0050-47da-819c-e677fb8c03be', 4000, 'Site', '', 'OID550'], ['56b250c3-0050-47da-819c-e677fb8c03be', 4001, 'Site', '', 'OID555'], ] created = [] for record in self.multi_contract_ent_offers: if record[1] not in created: created.append(record[1]) factories.EnterpriseOfferFactory( id=record[1], condition=factories.EnterpriseCustomerConditionFactory( enterprise_customer_uuid=record[0] ) ) self.multi_contract_manual_offers = [ ['1204f563-ef3d-47cb-85c4-f67de61c5733', 6000, 'User', '', 'OID600'], ['1204f563-ef3d-47cb-85c4-f67de61c5733', 6000, 'User', '', 'OID600'], ['1204f563-ef3d-47cb-85c4-f67de61c5733', 6000, 'User', '', 'OID650'], ['1204f563-ef3d-47cb-85c4-f67de61c5733', 6001, 'User', '', 'OID666'], ] created = [] for record in self.multi_contract_manual_offers: if record[1] not in created: created.append(record[1]) factories.ManualEnrollmentOrderOfferFactory( id=record[1], condition=factories.ManualEnrollmentOrderDiscountConditionFactory( enterprise_customer_uuid=record[0] ) ) self.multi_contract_enterprises_with_opportunity_ids_only = [ ['1e57ef1f-c96b-47dd-9a7e-0bb24022ede5', '', '', '', 'OID700'], ['11e0a60d-0f79-4d96-a55b-4e126cb34a51', '', '', '', 'OID800'], ['9b951dd8-4bbd-4b5b-9186-c4bee1c96e1a', '', '', '', 'OID900'], ] for record in self.multi_contract_enterprises_with_opportunity_ids_only: self.init_data(record[0])