Commit f86e1ca3 by Marko Jevtic

(SOL-1327) Implement voucher creation

parent c5c88d94
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from oscar.core.loading import get_model
Category = get_model("catalogue", "Category")
ProductAttribute = get_model("catalogue", "ProductAttribute")
ProductClass = get_model("catalogue", "ProductClass")
def create_product_class(apps, schema_editor):
"""Create a Coupon product class."""
coupon = ProductClass.objects.create(
track_stock=False,
requires_shipping=False,
name='Coupon',
slug='coupon',
)
ProductAttribute.objects.create(
product_class=coupon,
name='Coupon vouchers',
code='coupon_vouchers',
type='entity',
required=False
)
# Create a category for course seats
Category.objects.create(
description='All Coupons',
slug='coupons',
depth=1,
path='0002',
image='',
name='Coupons'
)
def remove_product_class(apps, schema_editor):
""" Reverse function. """
Category.objects.filter(slug='coupon').delete()
ProductClass.objects.filter(slug='coupon').delete()
def remove_enrollment_code(apps, schema_editor):
""" Removes the enrollment code product and it's attributes. """
Category.objects.filter(slug='enrollment_codes').delete()
ProductClass.objects.filter(slug='enrollment_code').delete()
class Migration(migrations.Migration):
dependencies = [
('catalogue', '0001_initial'),
('catalogue', '0012_enrollment_code_product_class')
]
operations = [
migrations.RunPython(remove_enrollment_code),
migrations.RunPython(create_product_class, remove_product_class)
]
from oscar.core.loading import get_model
from oscar.test.factories import ProductFactory, VoucherFactory
from ecommerce.extensions.voucher.models import CouponVouchers
from ecommerce.tests.testcases import TestCase
Product = get_model('catalogue', 'Product')
ProductClass = get_model('catalogue', 'ProductClass')
class CouponProductTest(TestCase):
""" Test coupon products."""
def test_coupon_product(self):
"""Test if a coupon product is properly created."""
coupon_product_class, _ = ProductClass.objects.get_or_create(name='coupon')
coupon_product = ProductFactory(
product_class=coupon_product_class,
title='Test product'
)
voucher = VoucherFactory(code='MYVOUCHER')
voucherList = CouponVouchers.objects.create(coupon=coupon_product)
voucherList.vouchers.add(voucher)
coupon_product.attr.coupon_voucher = voucherList
# clean() is an Oscar validation method for products
self.assertIsNone(coupon_product.clean())
self.assertIsInstance(coupon_product, Product)
self.assertEqual(coupon_product.title, 'Test product')
self.assertEqual(coupon_product.attr.coupon_voucher.vouchers.count(), 1)
self.assertEqual(coupon_product.attr.coupon_voucher.vouchers.first().code, 'MYVOUCHER')
import datetime
from oscar.core.loading import get_model
from oscar.test.factories import ProductFactory
from ecommerce.tests.testcases import TestCase
AttributeOption = get_model('catalogue', 'AttributeOption')
Catalog = get_model('catalogue', 'Catalog')
Course = get_model('courses', 'Course')
Product = get_model('catalogue', 'Product')
ProductClass = get_model('catalogue', 'ProductClass')
class EnrollmentCodeProductTest(TestCase):
""" Testing the creation of an enrollment code product."""
def test_enrollment_code_product(self):
"""
Test if an enrollment code is properly created
and has a course associated with it.
"""
catalog = Catalog.objects.create(
partner_id=self.partner.id
)
ec_product_class = ProductClass.objects.get(slug='enrollment_code')
enrollment_code_product = ProductFactory(
product_class=ec_product_class,
title='Test product'
)
enrollment_code_type = AttributeOption.objects.get(option='single_use')
enrollment_code_product.attr.catalog = catalog
enrollment_code_product.attr.start_date = datetime.date(2016, 11, 30)
enrollment_code_product.attr.end_date = datetime.date(2017, 11, 30)
enrollment_code_product.attr.type = enrollment_code_type
# clean() is an Oscar validation method for products
self.assertIsNone(enrollment_code_product.clean())
"""Tests of the Fulfillment API's fulfillment modules.""" """Tests of the Fulfillment API's fulfillment modules."""
import datetime
import json import json
import ddt import ddt
from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import override_settings
import httpretty import httpretty
import mock import mock
from oscar.core.loading import get_model
from django.conf import settings
from django.test import override_settings
from oscar.core.loading import get_class, get_model
from oscar.test import factories from oscar.test import factories
from oscar.test.newfactories import UserFactory, BasketFactory from oscar.test.newfactories import UserFactory, BasketFactory
from requests.exceptions import ConnectionError, Timeout from requests.exceptions import ConnectionError, Timeout
...@@ -19,13 +19,19 @@ from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin ...@@ -19,13 +19,19 @@ from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.fulfillment.modules import EnrollmentFulfillmentModule from ecommerce.extensions.fulfillment.modules import EnrollmentFulfillmentModule
from ecommerce.extensions.fulfillment.status import LINE from ecommerce.extensions.fulfillment.status import LINE
from ecommerce.extensions.fulfillment.tests.mixins import FulfillmentTestMixin from ecommerce.extensions.fulfillment.tests.mixins import FulfillmentTestMixin
from ecommerce.extensions.voucher.utils import create_vouchers
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
JSON = 'application/json' JSON = 'application/json'
LOGGER_NAME = 'ecommerce.extensions.analytics.utils' LOGGER_NAME = 'ecommerce.extensions.analytics.utils'
Applicator = get_class('offer.utils', 'Applicator')
Benefit = get_model('offer', 'Benefit')
Catalog = get_model('catalogue', 'Catalog')
ProductAttribute = get_model("catalogue", "ProductAttribute") ProductAttribute = get_model("catalogue", "ProductAttribute")
User = get_user_model() ProductClass = get_model('catalogue', 'ProductClass')
StockRecord = get_model('partner', 'StockRecord')
Voucher = get_model('voucher', 'Voucher')
@ddt.ddt @ddt.ddt
...@@ -353,3 +359,35 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -353,3 +359,35 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
# 'x-edx-ga-client-id' and 'x-forwarded-for'. # 'x-edx-ga-client-id' and 'x-forwarded-for'.
self.assertEqual(exp.request.headers.get('x-edx-ga-client-id'), '123.123') self.assertEqual(exp.request.headers.get('x-edx-ga-client-id'), '123.123')
self.assertEqual(exp.request.headers.get('x-forwarded-for'), '11.22.33.44') self.assertEqual(exp.request.headers.get('x-forwarded-for'), '11.22.33.44')
def test_voucher_usage(self):
"""
Test that using a voucher applies offer discount to reduce order price
"""
catalog = Catalog.objects.create(partner=self.partner)
coupon_product_class, _ = ProductClass.objects.get_or_create(name='coupon')
coupon = factories.create_product(
product_class=coupon_product_class,
title='Test product'
)
stock_record = StockRecord.objects.filter(product=self.seat).first()
catalog.stock_records.add(stock_record)
vouchers = create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=100.00,
catalog=catalog,
coupon=coupon,
end_datetime=datetime.datetime.now(),
name="Test Voucher",
quantity=10,
start_datetime=datetime.datetime.now() + datetime.timedelta(days=30),
voucher_type=Voucher.SINGLE_USE
)
voucher = vouchers[0]
seat_basket = self.order.basket
Applicator().apply_offers(seat_basket, voucher.offers.all())
self.assertEqual(seat_basket.total_excl_tax, 0.00)
default_app_config = 'ecommerce.extensions.offer.config.OfferConfig'
from oscar.apps.offer.admin import * # pylint: disable=unused-import,wildcard-import,unused-wildcard-import
class RangeAdminExtended(admin.ModelAdmin):
list_display = ('name', 'catalog',)
raw_id_fields = ('catalog',)
admin.site.unregister(Range)
admin.site.register(Range, RangeAdminExtended)
from oscar.apps.offer import config
class OfferConfig(config.OfferConfig):
name = 'ecommerce.extensions.offer'
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('catalogue', '0013_coupon_product_class'),
('offer', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='range',
name='catalog',
field=models.ForeignKey(related_name='ranges', blank=True, to='catalogue.Catalog', null=True),
),
]
# noinspection PyUnresolvedReferences
from django.db import models
from oscar.apps.offer.abstract_models import AbstractRange
class Range(AbstractRange):
catalog = models.ForeignKey('catalogue.Catalog', blank=True, null=True, related_name='ranges')
def contains_product(self, product):
if self.catalog:
return (
product.id in self.catalog.stock_records.values_list('product', flat=True) or
super(Range, self).contains_product(product)
)
return super(Range, self).contains_product(product)
contains = contains_product
def num_products(self):
return len(self.all_products())
def all_products(self):
if self.catalog:
catalog_products = [record.product for record in self.catalog.stock_records.all()]
return catalog_products + list(super(Range, self).all_products())
return super(Range, self).all_products()
from oscar.apps.offer.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position
from oscar.core.loading import get_model
from oscar.test import factories
from ecommerce.tests.testcases import TestCase
Catalog = get_model('catalogue', 'Catalog')
class RangeTests(TestCase):
def setUp(self):
super(RangeTests, self).setUp()
self.range = factories.RangeFactory()
self.range_with_catalog = factories.RangeFactory()
self.catalog = Catalog.objects.create(partner=self.partner)
self.product = factories.create_product()
self.range.add_product(self.product)
self.range_with_catalog.catalog = self.catalog
self.stock_record = factories.create_stockrecord(self.product, num_in_stock=2)
self.catalog.stock_records.add(self.stock_record)
def test_range_contains_product(self):
"""
contains_product(product) should return Boolean value
"""
self.assertTrue(self.range.contains_product(self.product))
self.assertTrue(self.range_with_catalog.contains_product(self.product))
not_in_range_product = factories.create_product()
self.assertFalse(self.range.contains_product(not_in_range_product))
self.assertFalse(self.range.contains_product(not_in_range_product))
def test_range_number_of_products(self):
"""
num_products() should return number of num_of_products
"""
self.assertEqual(self.range.num_products(), 1)
self.assertEqual(self.range_with_catalog.num_products(), 1)
def test_range_all_products(self):
"""
all_products() should return a list of products in range
"""
self.assertIn(self.product, self.range.all_products())
self.assertEqual(len(self.range.all_products()), 1)
self.assertIn(self.product, self.range_with_catalog.all_products())
self.assertEqual(len(self.range_with_catalog.all_products()), 1)
...@@ -23,6 +23,7 @@ class PaymentEvent(AbstractPaymentEvent): ...@@ -23,6 +23,7 @@ class PaymentEvent(AbstractPaymentEvent):
class Line(AbstractLine): class Line(AbstractLine):
history = HistoricalRecords() history = HistoricalRecords()
# If two models with the same name are declared within an app, Django will only use the first one. # If two models with the same name are declared within an app, Django will only use the first one.
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from oscar.apps.order.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position,wrong-import-order,ungrouped-imports from oscar.apps.order.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position,wrong-import-order,ungrouped-imports
default_app_config = 'ecommerce.extensions.voucher.config.VoucherConfig'
from oscar.apps.voucher.admin import * # pylint: disable=unused-import,wildcard-import,unused-wildcard-import
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from oscar.apps.voucher import config
class VoucherConfig(config.VoucherConfig):
name = 'ecommerce.extensions.voucher'
def ready(self): # pragma: no cover
if settings.VOUCHER_CODE_LENGTH < 1:
raise ImproperlyConfigured("VOUCHER_CODE_LENGTH must be a positive number.")
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
from decimal import Decimal
class Migration(migrations.Migration):
dependencies = [
('order', '0001_initial'),
('offer', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Voucher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='Name', max_length=128, help_text='This will be shown in the checkout and basket once the voucher is entered')),
('code', models.CharField(max_length=128, verbose_name='Code', unique=True, db_index=True, help_text='Case insensitive / No spaces allowed')),
('usage', models.CharField(default='Multi-use', max_length=128, verbose_name='Usage', choices=[('Single use', 'Can be used once by one customer'), ('Multi-use', 'Can be used multiple times by multiple customers'), ('Once per customer', 'Can only be used once per customer')])),
('start_datetime', models.DateTimeField(verbose_name='Start datetime')),
('end_datetime', models.DateTimeField(verbose_name='End datetime')),
('num_basket_additions', models.PositiveIntegerField(default=0, verbose_name='Times added to basket')),
('num_orders', models.PositiveIntegerField(default=0, verbose_name='Times on orders')),
('total_discount', models.DecimalField(default=Decimal('0.00'), max_digits=12, decimal_places=2, verbose_name='Total discount')),
('date_created', models.DateField(auto_now_add=True)),
('offers', models.ManyToManyField(related_name='vouchers', verbose_name='Offers', to='offer.ConditionalOffer')),
],
options={
'verbose_name_plural': 'Vouchers',
'get_latest_by': 'date_created',
'verbose_name': 'Voucher',
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='VoucherApplication',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_created', models.DateField(auto_now_add=True, verbose_name='Date Created')),
('order', models.ForeignKey(verbose_name='Order', to='order.Order')),
('user', models.ForeignKey(null=True, verbose_name='User', to=settings.AUTH_USER_MODEL, blank=True)),
('voucher', models.ForeignKey(verbose_name='Voucher', related_name='applications', to='voucher.Voucher')),
],
options={
'verbose_name_plural': 'Voucher Applications',
'verbose_name': 'Voucher Application',
'abstract': False,
},
bases=(models.Model,),
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('catalogue', '0013_coupon_product_class'),
('voucher', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CouponVouchers',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('coupon', models.ForeignKey(related_name='coupon_vouchers', to='catalogue.Product')),
('vouchers', models.ManyToManyField(related_name='coupon_vouchers', to='voucher.Voucher', blank=True)),
],
),
]
# noinspection PyUnresolvedReferences
from django.db import models
class CouponVouchers(models.Model):
coupon = models.ForeignKey('catalogue.Product', related_name='coupon_vouchers')
vouchers = models.ManyToManyField('voucher.Voucher', blank=True, related_name='coupon_vouchers')
# noinspection PyUnresolvedReferences
from oscar.apps.voucher.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position
import datetime
from django.db import IntegrityError
from django.test import override_settings
from oscar.core.loading import get_model
from oscar.test import factories
from ecommerce.extensions.voucher.utils import create_vouchers
from ecommerce.tests.testcases import TestCase
Benefit = get_model('offer', 'Benefit')
Catalog = get_model('catalogue', 'Catalog')
CouponVouchers = get_model('voucher', 'CouponVouchers')
Product = get_model('catalogue', 'Product')
ProductClass = get_model('catalogue', 'ProductClass')
StockRecord = get_model('partner', 'StockRecord')
Voucher = get_model('voucher', 'Voucher')
VOUCHER_CODE_LENGTH = 1
class UtilTests(TestCase):
course_id = 'edX/DemoX/Demo_Course'
certificate_type = 'test-certificate-type'
provider = None
def setUp(self):
super(UtilTests, self).setUp()
self.catalog = Catalog.objects.create(partner=self.partner)
self.coupon_product_class, _ = ProductClass.objects.get_or_create(name='coupon')
self.coupon = factories.create_product(
product_class=self.coupon_product_class,
title='Test product'
)
self.stock_record = factories.create_stockrecord(self.coupon, num_in_stock=2)
self.catalog.stock_records.add(self.stock_record)
def test_create_vouchers(self):
"""
Test voucher creation
"""
vouchers = create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=100.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Test voucher",
quantity=10,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE
)
self.assertEqual(len(vouchers), 10)
voucher = vouchers[0]
voucher_offer = voucher.offers.first()
coupon_voucher = CouponVouchers.objects.get(coupon=self.coupon)
self.assertEqual(voucher_offer.benefit.type, Benefit.PERCENTAGE)
self.assertEqual(voucher_offer.benefit.value, 100.00)
self.assertEqual(voucher_offer.benefit.range.catalog, self.catalog)
self.assertEqual(len(coupon_voucher.vouchers.all()), 10)
self.assertEqual(voucher.end_datetime, datetime.date(2015, 10, 30))
self.assertEqual(voucher.start_datetime, datetime.date(2015, 10, 1))
self.assertEqual(voucher.usage, Voucher.SINGLE_USE)
@override_settings(VOUCHER_CODE_LENGTH=VOUCHER_CODE_LENGTH)
def test_regenerate_voucher_code(self):
"""
Test that voucher code will be regenerated if it already exists
"""
for code in 'BCDFGHJKL':
create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=100.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Test voucher",
quantity=1,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE,
code=code
)
for _ in range(20):
voucher = create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=100.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Test voucher",
quantity=1,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE
)
self.assertTrue(Voucher.objects.filter(code__iexact=voucher[0].code).exists())
@override_settings(VOUCHER_CODE_LENGTH=0)
def test_nonpositive_voucher_code_length(self):
"""
Test that setting a voucher code length to a nonpositive integer value
raises a ValueError
"""
with self.assertRaises(ValueError):
create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=100.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Test voucher",
quantity=1,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE
)
def test_create_discount_coupon(self):
"""
Test discount voucher creation with specified code
"""
discount_vouchers = create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=25.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Discount code",
quantity=1,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE,
code="XMASC0DE"
)
self.assertEqual(len(discount_vouchers), 1)
self.assertEqual(discount_vouchers[0].code, "XMASC0DE")
with self.assertRaises(IntegrityError):
create_vouchers(
benefit_type=Benefit.PERCENTAGE,
benefit_value=35.00,
catalog=self.catalog,
coupon=self.coupon,
end_datetime=datetime.date(2015, 10, 30),
name="Discount name",
quantity=1,
start_datetime=datetime.date(2015, 10, 1),
voucher_type=Voucher.SINGLE_USE,
code="XMASC0DE"
)
"""Order Utility Classes. """
import logging
import random
import string # pylint: disable=deprecated-module
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from oscar.core.loading import get_model
logger = logging.getLogger(__name__)
Benefit = get_model('offer', 'Benefit')
Condition = get_model('offer', 'Condition')
ConditionalOffer = get_model('offer', 'ConditionalOffer')
CouponVouchers = get_model('voucher', 'CouponVouchers')
Range = get_model('offer', 'Range')
Voucher = get_model('voucher', 'Voucher')
def _get_or_create_offer(product_range, benefit_type, benefit_value):
"""
Return an offer for a catalog with condition and benefit.
If offer doesn't exist, new offer will be created and associated with
provided Offer condition and benefit.
Args:
product_range (Range): Range of products associated with condition
benefit_type (str): Type of benefit associated with the offer
benefit_value (Decimal): Value of benefit associated with the offer
Returns:
Offer
"""
offer_condition, __ = Condition.objects.get_or_create(
range=product_range,
type=Condition.COUNT,
value=1,
)
offer_benefit, __ = Benefit.objects.get_or_create(
range=product_range,
type=benefit_type,
value=benefit_value,
max_affected_items=1,
)
offer_name = "{}-{}".format(offer_benefit.type, offer_benefit.value)
offer, __ = ConditionalOffer.objects.get_or_create(
name=offer_name,
offer_type=ConditionalOffer.VOUCHER,
condition=offer_condition,
benefit=offer_benefit,
)
return offer
def _generate_code_string(length):
"""
Create a string of random characters of specified length
Args:
length (int): Defines the length of randomly generated string
Raises:
ValueError raised if length is less than one.
Returns:
str
"""
if length < 1:
raise ValueError("Voucher code length must be a positive number.")
chars = [
char for char in string.ascii_uppercase + string.digits
if char not in 'AEIOU1'
]
voucher_code = string.join((random.choice(chars) for i in range(length)), '')
if Voucher.objects.filter(code__iexact=voucher_code).exists():
return _generate_code_string(length)
return voucher_code
def _create_new_voucher(code, coupon, end_datetime, name, offer, start_datetime, voucher_type):
"""
Creates a voucher.
If randomly generated voucher code already exists, new code will be generated and reverified.
Args:
code (str): Code associated with vouchers. If not provided, one will be generated.
coupon (Product): Coupon product associated with voucher.
end_datetime (datetime): Voucher end date.
name (str): Voucher name.
offer (Offer): Offer associated with voucher.
start_datetime (datetime): Voucher start date.
voucher_type (str): Voucher usage.
Returns:
Voucher
"""
voucher_code = code or _generate_code_string(settings.VOUCHER_CODE_LENGTH)
voucher = Voucher.objects.create(
name=name,
code=voucher_code,
usage=voucher_type,
start_datetime=start_datetime,
end_datetime=end_datetime
)
voucher.offers.add(offer)
coupon_voucher, __ = CouponVouchers.objects.get_or_create(coupon=coupon)
coupon_voucher.vouchers.add(voucher)
return voucher
def create_vouchers(
benefit_type,
benefit_value,
catalog,
coupon,
end_datetime,
name,
quantity,
start_datetime,
voucher_type,
code=None):
"""
Create vouchers
Args:
benefit_type (str): Type of benefit associated with vouchers.
benefit_value (Decimal): Value of benefit associated with vouchers.
catalog (Catalog): Catalog associated with range of products
to which a voucher can be applied to
coupon (Coupon): Coupon entity associated with vouchers.
end_datetime (datetime): End date for voucher offer
name (str): Voucher name
quantity (int): Number of vouchers to be created.
start_datetime (datetime): Start date for voucher offer.
voucher_type (str): Type of voucher.
code (str): Code associated with vouchers. Defaults to None.
Returns:
List[Voucher]
"""
logger.info("Creating [%d] vouchers catalog [%s]", quantity, catalog.id)
vouchers = []
range_name = (_('Range for {catalog_name}').format(catalog_name=catalog.name))
product_range, __ = Range.objects.get_or_create(
name=range_name,
catalog=catalog,
)
offer = _get_or_create_offer(
product_range=product_range,
benefit_type=benefit_type,
benefit_value=benefit_value
)
for __ in range(quantity):
voucher = _create_new_voucher(
coupon=coupon,
end_datetime=end_datetime,
offer=offer,
start_datetime=start_datetime,
voucher_type=voucher_type,
code=code,
name=name
)
vouchers.append(voucher)
return vouchers
...@@ -30,9 +30,11 @@ OSCAR_APPS = [ ...@@ -30,9 +30,11 @@ OSCAR_APPS = [
'ecommerce.extensions.dashboard', 'ecommerce.extensions.dashboard',
'ecommerce.extensions.dashboard.orders', 'ecommerce.extensions.dashboard.orders',
'ecommerce.extensions.dashboard.users', 'ecommerce.extensions.dashboard.users',
'ecommerce.extensions.offer',
'ecommerce.extensions.order', 'ecommerce.extensions.order',
'ecommerce.extensions.partner', 'ecommerce.extensions.partner',
'ecommerce.extensions.payment', 'ecommerce.extensions.payment',
'ecommerce.extensions.voucher',
]) ])
# END APP CONFIGURATION # END APP CONFIGURATION
...@@ -247,4 +249,7 @@ OSCAR_DASHBOARD_NAVIGATION = [ ...@@ -247,4 +249,7 @@ OSCAR_DASHBOARD_NAVIGATION = [
# Default timeout for Enrollment API calls # Default timeout for Enrollment API calls
ENROLLMENT_FULFILLMENT_TIMEOUT = 7 ENROLLMENT_FULFILLMENT_TIMEOUT = 7
# Coupon code length
VOUCHER_CODE_LENGTH = 8
THUMBNAIL_DEBUG = False THUMBNAIL_DEBUG = False
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment