forked from CGM_Public/pretix_original
Added voucher data model [backwards incompatible]
This commit is contained in:
@@ -54,6 +54,9 @@ Carts and Orders
|
|||||||
.. autoclass:: pretix.base.models.Order
|
.. autoclass:: pretix.base.models.Order
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.AbstractPosition
|
||||||
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pretix.base.models.OrderPosition
|
.. autoclass:: pretix.base.models.OrderPosition
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
@@ -68,3 +71,11 @@ Logging
|
|||||||
|
|
||||||
.. autoclass:: pretix.base.models.LogEntry
|
.. autoclass:: pretix.base.models.LogEntry
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Vouchers
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.Voucher
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. _cleanerversion: https://github.com/swisscom/cleanerversion
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.9 on 2015-12-13 11:44
|
# Generated by Django 1.9 on 2016-02-09 09:40
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@@ -46,17 +47,18 @@ class Migration(migrations.Migration):
|
|||||||
name='CartPosition',
|
name='CartPosition',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('cart_id', models.CharField(blank=True, max_length=255, null=True, verbose_name='Cart ID (e.g. session key)')),
|
|
||||||
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Price')),
|
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Price')),
|
||||||
|
('attendee_name', models.CharField(blank=True, help_text='Empty, if this product is not an admission ticket', max_length=255, null=True, verbose_name='Attendee name')),
|
||||||
|
('voucher_discount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)),
|
||||||
|
('base_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||||
|
('cart_id', models.CharField(blank=True, max_length=255, null=True, verbose_name='Cart ID (e.g. session key)')),
|
||||||
('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
||||||
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
||||||
('attendee_name', models.CharField(blank=True, help_text='Empty, if this product is not an admission ticket', max_length=255, null=True, verbose_name='Attendee name')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'Cart positions',
|
'verbose_name_plural': 'Cart positions',
|
||||||
'verbose_name': 'Cart position',
|
'verbose_name': 'Cart position',
|
||||||
},
|
},
|
||||||
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Event',
|
name='Event',
|
||||||
@@ -161,7 +163,7 @@ class Migration(migrations.Migration):
|
|||||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variations', to='pretixbase.Item')),
|
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variations', to='pretixbase.Item')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('position',),
|
'ordering': ('position', 'id'),
|
||||||
'verbose_name_plural': 'Product variations',
|
'verbose_name_plural': 'Product variations',
|
||||||
'verbose_name': 'Product variation',
|
'verbose_name': 'Product variation',
|
||||||
},
|
},
|
||||||
@@ -211,7 +213,9 @@ class Migration(migrations.Migration):
|
|||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Price')),
|
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Price')),
|
||||||
('attendee_name', models.CharField(blank=True, help_text='Empty, if this product is not an admission ticket', max_length=255, null=True, verbose_name='Attendee name')),
|
('attendee_name', models.CharField(blank=True, help_text='Empty, if this product is not an admission ticket', max_length=255, null=True, verbose_name='Attendee name')),
|
||||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='positions', to='pretixbase.Item', verbose_name='Item')),
|
('voucher_discount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)),
|
||||||
|
('base_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||||
|
('item', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Item', verbose_name='Item')),
|
||||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='positions', to='pretixbase.Order', verbose_name='Order')),
|
('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='positions', to='pretixbase.Order', verbose_name='Order')),
|
||||||
('variation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.ItemVariation', verbose_name='Variation')),
|
('variation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.ItemVariation', verbose_name='Variation')),
|
||||||
],
|
],
|
||||||
@@ -219,7 +223,6 @@ class Migration(migrations.Migration):
|
|||||||
'verbose_name_plural': 'Order positions',
|
'verbose_name_plural': 'Order positions',
|
||||||
'verbose_name': 'Order position',
|
'verbose_name': 'Order position',
|
||||||
},
|
},
|
||||||
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Organizer',
|
name='Organizer',
|
||||||
@@ -299,11 +302,33 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
bases=(models.Model, pretix.base.models.base.LoggingMixin),
|
bases=(models.Model, pretix.base.models.base.LoggingMixin),
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Voucher',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('code', models.CharField(max_length=255, verbose_name='Voucher code')),
|
||||||
|
('valid_until', models.DateTimeField(blank=True, null=True, verbose_name='Valid until')),
|
||||||
|
('block_quota', models.BooleanField(default=False, help_text="If activated, this voucher will be substracted from the affected product's quotas, such that it is guaranteed that anyone with this voucher code does receive a ticket.", verbose_name='Reserve ticket from quota')),
|
||||||
|
('allow_ignore_quota', models.BooleanField(default=False, help_text='If activated, a holder of this voucher code can buy tickets, even if there are none left.', verbose_name='Allow to bypass quota')),
|
||||||
|
('price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Set product price to')),
|
||||||
|
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vouchers', to='pretixbase.Event', verbose_name='Event')),
|
||||||
|
('item', models.ManyToManyField(help_text="This product is added to the user's cart if the voucher is redeemed.", related_name='vouchers', to='pretixbase.Item', verbose_name='Product')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Vouchers',
|
||||||
|
'verbose_name': 'Voucher',
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='organizer',
|
model_name='organizer',
|
||||||
name='permitted',
|
name='permitted',
|
||||||
field=models.ManyToManyField(related_name='organizers', through='pretixbase.OrganizerPermission', to=settings.AUTH_USER_MODEL),
|
field=models.ManyToManyField(related_name='organizers', through='pretixbase.OrganizerPermission', to=settings.AUTH_USER_MODEL),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderposition',
|
||||||
|
name='voucher',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Voucher'),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name='item',
|
||||||
name='category',
|
name='category',
|
||||||
@@ -332,16 +357,25 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cartposition',
|
model_name='cartposition',
|
||||||
name='item',
|
name='item',
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Item', verbose_name='Item'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Item', verbose_name='Item'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cartposition',
|
model_name='cartposition',
|
||||||
name='variation',
|
name='variation',
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.ItemVariation', verbose_name='Variation'),
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.ItemVariation', verbose_name='Variation'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='cartposition',
|
||||||
|
name='voucher',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Voucher'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='cachedticket',
|
model_name='cachedticket',
|
||||||
name='order',
|
name='order',
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Order'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Order'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='voucher',
|
||||||
|
unique_together=set([('event', 'code')]),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
@@ -6,14 +6,16 @@ from .items import (
|
|||||||
)
|
)
|
||||||
from .log import LogEntry
|
from .log import LogEntry
|
||||||
from .orders import (
|
from .orders import (
|
||||||
CachedTicket, CartPosition, ObjectWithAnswers, Order, OrderPosition,
|
AbstractPosition, CachedTicket, CartPosition, Order, OrderPosition,
|
||||||
QuestionAnswer, generate_secret,
|
QuestionAnswer, generate_secret,
|
||||||
)
|
)
|
||||||
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
||||||
|
from .vouchers import Voucher
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
'Versionable', 'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
||||||
'ItemCategory', 'Item', 'ItemVariation', 'Question', 'Quota', 'Order', 'CachedTicket', 'QuestionAnswer',
|
'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question',
|
||||||
'ObjectWithAnswers', 'OrderPosition', 'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock',
|
'BaseRestriction', 'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'AbstractPosition', 'OrderPosition',
|
||||||
'cachedfile_name', 'itempicture_upload_to', 'generate_secret', 'LogEntry'
|
'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to',
|
||||||
|
'generate_secret', 'Voucher', 'LogEntry'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
@@ -263,7 +264,63 @@ class QuestionAnswer(models.Model):
|
|||||||
answer = models.TextField()
|
answer = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
class ObjectWithAnswers:
|
class AbstractPosition(models.Model):
|
||||||
|
"""
|
||||||
|
A position can either be one line of an order or an item placed in a cart.
|
||||||
|
|
||||||
|
:param item: The selected item
|
||||||
|
:type item: Item
|
||||||
|
:param variation: The selected ItemVariation or null, if the item has no properties
|
||||||
|
:type variation: ItemVariation
|
||||||
|
:param datetime: The datetime this item was put into the cart
|
||||||
|
:type datetime: datetime
|
||||||
|
:param expires: The date until this item is guarenteed to be reserved
|
||||||
|
:type expires: datetime
|
||||||
|
:param price: The price of this item
|
||||||
|
:type price: decimal.Decimal
|
||||||
|
:param attendee_name: The attendee's name, if entered.
|
||||||
|
:type attendee_name: str
|
||||||
|
:param voucher: A voucher that has been applied to this sale
|
||||||
|
:type voucher: Voucher
|
||||||
|
:param voucher_discount: The absolute discount granted by the applied voucher
|
||||||
|
:type voucher_discount: decimal.Decimal
|
||||||
|
:param base_price: The base price without any discounts applied
|
||||||
|
:type base_price: decimal.Decimal
|
||||||
|
"""
|
||||||
|
item = models.ForeignKey(
|
||||||
|
Item,
|
||||||
|
verbose_name=_("Item"),
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
variation = models.ForeignKey(
|
||||||
|
ItemVariation,
|
||||||
|
null=True, blank=True,
|
||||||
|
verbose_name=_("Variation"),
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
price = models.DecimalField(
|
||||||
|
decimal_places=2, max_digits=10,
|
||||||
|
verbose_name=_("Price")
|
||||||
|
)
|
||||||
|
attendee_name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Attendee name"),
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text=_("Empty, if this product is not an admission ticket")
|
||||||
|
)
|
||||||
|
voucher = models.ForeignKey(
|
||||||
|
'Voucher', null=True, blank=True
|
||||||
|
)
|
||||||
|
voucher_discount = models.DecimalField(
|
||||||
|
default=Decimal('0.00'), decimal_places=2, max_digits=10
|
||||||
|
)
|
||||||
|
base_price = models.DecimalField(
|
||||||
|
decimal_places=2, max_digits=10, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def cache_answers(self):
|
def cache_answers(self):
|
||||||
"""
|
"""
|
||||||
Creates two properties on the object.
|
Creates two properties on the object.
|
||||||
@@ -281,22 +338,22 @@ class ObjectWithAnswers:
|
|||||||
q.answer = ""
|
q.answer = ""
|
||||||
self.questions.append(q)
|
self.questions.append(q)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.voucher is None and self.base_price is None:
|
||||||
|
self.base_price = self.price
|
||||||
|
if self.voucher_discount != Decimal('0.00') and self.base_price is not None:
|
||||||
|
self.price = self.base_price - self.voucher_discount
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
class OrderPosition(ObjectWithAnswers, models.Model):
|
|
||||||
|
class OrderPosition(AbstractPosition):
|
||||||
"""
|
"""
|
||||||
An OrderPosition is one line of an order, representing one ordered items
|
An OrderPosition is one line of an order, representing one ordered items
|
||||||
of a specified type (or variation).
|
of a specified type (or variation). This has all properties of
|
||||||
|
AbstractPosition.
|
||||||
|
|
||||||
:param order: The order this is a part of
|
:param order: The order this is a part of
|
||||||
:type order: Order
|
:type order: Order
|
||||||
:param item: The ordered item
|
|
||||||
:type item: Item
|
|
||||||
:param variation: The ordered ItemVariation or null, if the item has no properties
|
|
||||||
:type variation: ItemVariation
|
|
||||||
:param price: The price of this item
|
|
||||||
:type price: decimal.Decimal
|
|
||||||
:param attendee_name: The attendee's name, if entered.
|
|
||||||
:type attendee_name: str
|
|
||||||
"""
|
"""
|
||||||
order = models.ForeignKey(
|
order = models.ForeignKey(
|
||||||
Order,
|
Order,
|
||||||
@@ -304,28 +361,6 @@ class OrderPosition(ObjectWithAnswers, models.Model):
|
|||||||
related_name='positions',
|
related_name='positions',
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
item = models.ForeignKey(
|
|
||||||
Item,
|
|
||||||
verbose_name=_("Item"),
|
|
||||||
related_name='positions',
|
|
||||||
on_delete=models.PROTECT
|
|
||||||
)
|
|
||||||
variation = models.ForeignKey(
|
|
||||||
ItemVariation,
|
|
||||||
null=True, blank=True,
|
|
||||||
verbose_name=_("Variation"),
|
|
||||||
on_delete=models.PROTECT
|
|
||||||
)
|
|
||||||
price = models.DecimalField(
|
|
||||||
decimal_places=2, max_digits=10,
|
|
||||||
verbose_name=_("Price")
|
|
||||||
)
|
|
||||||
attendee_name = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name=_("Attendee name"),
|
|
||||||
blank=True, null=True,
|
|
||||||
help_text=_("Empty, if this product is not an admission ticket")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Order position")
|
verbose_name = _("Order position")
|
||||||
@@ -335,11 +370,9 @@ class OrderPosition(ObjectWithAnswers, models.Model):
|
|||||||
def transform_cart_positions(cls, cp: List, order) -> list:
|
def transform_cart_positions(cls, cp: List, order) -> list:
|
||||||
ops = []
|
ops = []
|
||||||
for cartpos in cp:
|
for cartpos in cp:
|
||||||
op = OrderPosition(
|
op = OrderPosition(order=order)
|
||||||
order=order, item=cartpos.item, variation=cartpos.variation,
|
for f in AbstractPosition._meta.fields:
|
||||||
price=cartpos.price, attendee_name=cartpos.attendee_name
|
setattr(op, f.name, getattr(cartpos, f.name))
|
||||||
)
|
|
||||||
op.save()
|
|
||||||
for answ in cartpos.answers.all():
|
for answ in cartpos.answers.all():
|
||||||
answ.orderposition = op
|
answ.orderposition = op
|
||||||
answ.cartposition = None
|
answ.cartposition = None
|
||||||
@@ -348,31 +381,19 @@ class OrderPosition(ObjectWithAnswers, models.Model):
|
|||||||
ops.append(op)
|
ops.append(op)
|
||||||
|
|
||||||
|
|
||||||
class CartPosition(ObjectWithAnswers, models.Model):
|
class CartPosition(AbstractPosition):
|
||||||
"""
|
"""
|
||||||
A cart position is similar to a order line, except that it is not
|
A cart position is similar to a order line, except that it is not
|
||||||
yet part of a binding order but just placed by some user in his or
|
yet part of a binding order but just placed by some user in his or
|
||||||
her cart. It therefore normally has a much shorter expiration time
|
her cart. It therefore normally has a much shorter expiration time
|
||||||
than an ordered position, but still blocks an item in the quota pool
|
than an ordered position, but still blocks an item in the quota pool
|
||||||
as we do not want to throw out users while they're clicking through
|
as we do not want to throw out users while they're clicking through
|
||||||
the checkout process.
|
the checkout process. This has all properties of AbstractPosition.
|
||||||
|
|
||||||
:param event: The event this belongs to
|
:param event: The event this belongs to
|
||||||
:type event: Evnt
|
:type event: Evnt
|
||||||
:param item: The selected item
|
|
||||||
:type item: Item
|
|
||||||
:param cart_id: The user session that contains this cart position
|
:param cart_id: The user session that contains this cart position
|
||||||
:type cart_id: str
|
:type cart_id: str
|
||||||
:param variation: The selected ItemVariation or null, if the item has no properties
|
|
||||||
:type variation: ItemVariation
|
|
||||||
:param datetime: The datetime this item was put into the cart
|
|
||||||
:type datetime: datetime
|
|
||||||
:param expires: The date until this item is guarenteed to be reserved
|
|
||||||
:type expires: datetime
|
|
||||||
:param price: The price of this item
|
|
||||||
:type price: decimal.Decimal
|
|
||||||
:param attendee_name: The attendee's name, if entered.
|
|
||||||
:type attendee_name: str
|
|
||||||
"""
|
"""
|
||||||
event = models.ForeignKey(
|
event = models.ForeignKey(
|
||||||
Event,
|
Event,
|
||||||
@@ -382,21 +403,6 @@ class CartPosition(ObjectWithAnswers, models.Model):
|
|||||||
max_length=255, null=True, blank=True,
|
max_length=255, null=True, blank=True,
|
||||||
verbose_name=_("Cart ID (e.g. session key)")
|
verbose_name=_("Cart ID (e.g. session key)")
|
||||||
)
|
)
|
||||||
item = models.ForeignKey(
|
|
||||||
Item,
|
|
||||||
verbose_name=_("Item"),
|
|
||||||
on_delete=models.CASCADE
|
|
||||||
)
|
|
||||||
variation = models.ForeignKey(
|
|
||||||
ItemVariation,
|
|
||||||
null=True, blank=True,
|
|
||||||
verbose_name=_("Variation"),
|
|
||||||
on_delete=models.CASCADE
|
|
||||||
)
|
|
||||||
price = models.DecimalField(
|
|
||||||
decimal_places=2, max_digits=10,
|
|
||||||
verbose_name=_("Price")
|
|
||||||
)
|
|
||||||
datetime = models.DateTimeField(
|
datetime = models.DateTimeField(
|
||||||
verbose_name=_("Date"),
|
verbose_name=_("Date"),
|
||||||
auto_now_add=True
|
auto_now_add=True
|
||||||
@@ -404,12 +410,6 @@ class CartPosition(ObjectWithAnswers, models.Model):
|
|||||||
expires = models.DateTimeField(
|
expires = models.DateTimeField(
|
||||||
verbose_name=_("Expiration date")
|
verbose_name=_("Expiration date")
|
||||||
)
|
)
|
||||||
attendee_name = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name=_("Attendee name"),
|
|
||||||
blank=True, null=True,
|
|
||||||
help_text=_("Empty, if this product is not an admission ticket")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Cart position")
|
verbose_name = _("Cart position")
|
||||||
|
|||||||
68
src/pretix/base/models/vouchers.py
Normal file
68
src/pretix/base/models/vouchers.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .event import Event
|
||||||
|
from .items import Item
|
||||||
|
from .orders import CartPosition, OrderPosition
|
||||||
|
|
||||||
|
|
||||||
|
class Voucher(models.Model):
|
||||||
|
event = models.ForeignKey(
|
||||||
|
Event,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="vouchers",
|
||||||
|
verbose_name=_("Event"),
|
||||||
|
)
|
||||||
|
code = models.CharField(
|
||||||
|
verbose_name=_("Voucher code"),
|
||||||
|
max_length=255
|
||||||
|
)
|
||||||
|
valid_until = models.DateTimeField(
|
||||||
|
blank=True, null=True,
|
||||||
|
verbose_name=_("Valid until")
|
||||||
|
)
|
||||||
|
block_quota = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("Reserve ticket from quota"),
|
||||||
|
help_text=_(
|
||||||
|
"If activated, this voucher will be substracted from the affected product\'s quotas, such that it is "
|
||||||
|
"guaranteed that anyone with this voucher code does receive a ticket."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
allow_ignore_quota = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("Allow to bypass quota"),
|
||||||
|
help_text=_(
|
||||||
|
"If activated, a holder of this voucher code can buy tickets, even if there are none left."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
price = models.DecimalField(
|
||||||
|
verbose_name=_("Set product price to"),
|
||||||
|
decimal_places=2, max_digits=10, null=True, blank=True
|
||||||
|
)
|
||||||
|
item = models.ManyToManyField(
|
||||||
|
Item, related_name='vouchers',
|
||||||
|
verbose_name=_("Product"),
|
||||||
|
help_text=_(
|
||||||
|
"This product is added to the user's cart if the voucher is redeemed."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Voucher")
|
||||||
|
verbose_name_plural = _("Vouchers")
|
||||||
|
unique_together = (("event", "code"),)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.code = self.code.upper()
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def is_ordered(self) -> int:
|
||||||
|
return OrderPosition.objects.current.filter(
|
||||||
|
voucher=self.voucher
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
def is_in_cart(self) -> int:
|
||||||
|
return CartPosition.objects.current.filter(
|
||||||
|
voucher=self.voucher
|
||||||
|
).count()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.9 on 2015-12-13 11:44
|
# Generated by Django 1.9 on 2016-02-09 09:40
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@@ -11,7 +11,7 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('pretixbase', '0002_auto_20151213_1144'),
|
('pretixbase', '0002_auto_20160209_0940'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|||||||
Reference in New Issue
Block a user