forked from CGM_Public/pretix_original
Add quota and order models
This commit is contained in:
125
src/tixlbase/migrations/0017_auto_20141017_2148.py
Normal file
125
src/tixlbase/migrations/0017_auto_20141017_2148.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import tixlbase.models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tixlbase', '0016_event_plugins'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CartPosition',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
||||
('session', models.CharField(null=True, max_length=255, blank=True, verbose_name='Session key')),
|
||||
('total', models.DecimalField(max_digits=10, verbose_name='Price', decimal_places=2)),
|
||||
('datetime', models.DateTimeField(verbose_name='Datetime')),
|
||||
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
||||
('event', models.ForeignKey(to='tixlbase.Event', verbose_name='Event')),
|
||||
('item', models.ForeignKey(to='tixlbase.Item', verbose_name='Item')),
|
||||
('user', models.ForeignKey(null=True, blank=True, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
('variation', models.ForeignKey(null=True, blank=True, to='tixlbase.ItemVariation', verbose_name='Variation')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Cart positions',
|
||||
'verbose_name': 'Cart position',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Order',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
||||
('status', models.CharField(max_length=3, choices=[('p', 'pending'), ('n', 'paid'), ('e', 'expired'), ('c', 'cancelled')], verbose_name='Status')),
|
||||
('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
||||
('expires', models.DateTimeField(verbose_name='Expiration date')),
|
||||
('payment_date', models.DateTimeField(verbose_name='Payment date')),
|
||||
('payment_info', models.TextField(verbose_name='Payment information')),
|
||||
('total', models.DecimalField(max_digits=10, verbose_name='Total amount', decimal_places=2)),
|
||||
('event', models.ForeignKey(to='tixlbase.Event', verbose_name='Event')),
|
||||
('user', models.ForeignKey(null=True, blank=True, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Orders',
|
||||
'verbose_name': 'Order',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrderPosition',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
||||
('price', models.DecimalField(max_digits=10, verbose_name='Price', decimal_places=2)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Order positions',
|
||||
'verbose_name': 'Order position',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='QuestionAnswer',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
||||
('answer', models.TextField()),
|
||||
('cartposition', models.ForeignKey(null=True, blank=True, to='tixlbase.CartPosition')),
|
||||
('orderposition', models.ForeignKey(null=True, blank=True, to='tixlbase.OrderPosition')),
|
||||
('question', models.ForeignKey(to='tixlbase.Question')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Quota',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
||||
('name', models.CharField(max_length=200, verbose_name='Name')),
|
||||
('size', models.PositiveIntegerField(verbose_name='Total capacity')),
|
||||
('items', models.ManyToManyField(to='tixlbase.Item', blank=True, verbose_name='Item')),
|
||||
('lock_cache', models.ManyToManyField(to='tixlbase.CartPosition', blank=True)),
|
||||
('order_cache', models.ManyToManyField(to='tixlbase.OrderPosition', blank=True)),
|
||||
('variations', tixlbase.models.VariationsField(to='tixlbase.ItemVariation', blank=True, verbose_name='Variations')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Quotas',
|
||||
'verbose_name': 'Quota',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderposition',
|
||||
name='answers',
|
||||
field=models.ManyToManyField(to='tixlbase.Question', through='tixlbase.QuestionAnswer', verbose_name='Answers'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderposition',
|
||||
name='item',
|
||||
field=models.ForeignKey(to='tixlbase.Item', verbose_name='Item'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderposition',
|
||||
name='order',
|
||||
field=models.ForeignKey(to='tixlbase.Order', verbose_name='Order'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderposition',
|
||||
name='variation',
|
||||
field=models.ForeignKey(null=True, blank=True, to='tixlbase.ItemVariation', verbose_name='Variation'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='payment_term_days',
|
||||
field=models.PositiveIntegerField(verbose_name='Payment term in days', default=14, help_text='The number of days after placing an order the user has to pay to preserve his reservation.'),
|
||||
),
|
||||
]
|
||||
@@ -266,7 +266,7 @@ class Event(models.Model):
|
||||
verbose_name=_("Start of presale"),
|
||||
help_text=_("No items will be sold before this date."),
|
||||
)
|
||||
payment_term_days = models.IntegerField(
|
||||
payment_term_days = models.PositiveIntegerField(
|
||||
default=14,
|
||||
verbose_name=_("Payment term in days"),
|
||||
help_text=_("The number of days after placing an order the user has to pay to preserve his reservation."),
|
||||
@@ -709,13 +709,14 @@ class BaseRestriction(models.Model):
|
||||
)
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
blank=True,
|
||||
null=True,
|
||||
blank=True, null=True,
|
||||
verbose_name=_("Item"),
|
||||
related_name="restrictions_%(app_label)s_%(class)s",
|
||||
)
|
||||
variations = VariationsField(
|
||||
ItemVariation,
|
||||
blank=True,
|
||||
verbose_name=_("Variations"),
|
||||
related_name="restrictions_%(app_label)s_%(class)s",
|
||||
)
|
||||
|
||||
@@ -728,3 +729,203 @@ class BaseRestriction(models.Model):
|
||||
if self.event:
|
||||
self.event.get_cache().clear()
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class Quota(models.Model):
|
||||
"""
|
||||
A quota is a "pool of tickets". It is there to limit the number of items
|
||||
of a certain type to be sold. For example, you could have a quota of 500
|
||||
applied to all your items (because you only have that much space in your
|
||||
building), and also a quota of 100 applied to the VIP tickets for
|
||||
exclusivity. In this case, no more than 500 tickets will be sold in total
|
||||
and no more than 100 of them will be VIP tickets (but 450 normal and 50
|
||||
VIP tickets will be fine).
|
||||
|
||||
As always, a quota can not only be tied to an item, but also to a specific
|
||||
variation. We follow the general rule here: If there are no variations
|
||||
speficied, the quota applies to all of them, and if there are variations
|
||||
specified, the quota applies to those.
|
||||
|
||||
This object holds two fields, "order_cache" and "lock_cache", which are
|
||||
implementation specific and are considered private. It is planned that they
|
||||
are being used as a fallback solution if redis is not available.
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name=_("Name")
|
||||
)
|
||||
size = models.PositiveIntegerField(
|
||||
verbose_name=_("Total capacity")
|
||||
)
|
||||
items = models.ManyToManyField(
|
||||
Item,
|
||||
verbose_name=_("Item"),
|
||||
blank=True
|
||||
)
|
||||
variations = VariationsField(
|
||||
ItemVariation,
|
||||
blank=True,
|
||||
verbose_name=_("Variations")
|
||||
)
|
||||
order_cache = models.ManyToManyField(
|
||||
'OrderPosition',
|
||||
blank=True
|
||||
)
|
||||
lock_cache = models.ManyToManyField(
|
||||
'CartPosition',
|
||||
blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Quota")
|
||||
verbose_name_plural = _("Quotas")
|
||||
|
||||
|
||||
class Order(models.Model):
|
||||
"""
|
||||
An order is created when a user clicks 'buy' on his cart. It holds
|
||||
several OrderPositions and is connected to an user. It has an
|
||||
expiration date: If items run out of capacity, orders which are over
|
||||
their expiration date might be cancelled.
|
||||
|
||||
Important: An order holds its total monetary value, as an order is a
|
||||
piece of 'history' and must not change due to a change in item prices.
|
||||
"""
|
||||
|
||||
STATUS_PENDING = "n"
|
||||
STATUS_PAID = "p"
|
||||
STATUS_EXPIRED = "e"
|
||||
STATUS_CANCELLED = "c"
|
||||
STATUS_CHOICE = (
|
||||
(STATUS_PAID, _("pending")),
|
||||
(STATUS_PENDING, _("paid")),
|
||||
(STATUS_EXPIRED, _("expired")),
|
||||
(STATUS_CANCELLED, _("cancelled")),
|
||||
)
|
||||
|
||||
status = models.CharField(
|
||||
max_length=3,
|
||||
choices=STATUS_CHOICE,
|
||||
verbose_name=_("Status")
|
||||
)
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event")
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
User, null=True, blank=True,
|
||||
verbose_name=_("User")
|
||||
)
|
||||
datetime = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
verbose_name=_("Date")
|
||||
)
|
||||
expires = models.DateTimeField(
|
||||
verbose_name=_("Expiration date")
|
||||
)
|
||||
payment_date = models.DateTimeField(
|
||||
verbose_name=_("Payment date")
|
||||
)
|
||||
payment_info = models.TextField(
|
||||
verbose_name=_("Payment information")
|
||||
)
|
||||
total = models.DecimalField(
|
||||
decimal_places=2, max_digits=10,
|
||||
verbose_name=_("Total amount")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Order")
|
||||
verbose_name_plural = _("Orders")
|
||||
|
||||
|
||||
class QuestionAnswer(models.Model):
|
||||
"""
|
||||
The answer to a Question, connected to an OrderPosition or CartPosition
|
||||
"""
|
||||
orderposition = models.ForeignKey('OrderPosition', null=True, blank=True)
|
||||
cartposition = models.ForeignKey('CartPosition', null=True, blank=True)
|
||||
question = models.ForeignKey(Question)
|
||||
answer = models.TextField()
|
||||
|
||||
|
||||
class OrderPosition(models.Model):
|
||||
"""
|
||||
An OrderPosition is one line of an order, representing one ordered items
|
||||
of a specified type (or variation).
|
||||
|
||||
Important: An OrderPosition holds its total monetary value, as an order is a
|
||||
piece of 'history' and must not change due to a change in item prices.
|
||||
"""
|
||||
order = models.ForeignKey(
|
||||
Order,
|
||||
verbose_name=_("Order")
|
||||
)
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
verbose_name=_("Item")
|
||||
)
|
||||
variation = models.ForeignKey(
|
||||
ItemVariation,
|
||||
null=True, blank=True,
|
||||
verbose_name=_("Variation")
|
||||
)
|
||||
price = models.DecimalField(
|
||||
decimal_places=2, max_digits=10,
|
||||
verbose_name=_("Price")
|
||||
)
|
||||
answers = models.ManyToManyField(
|
||||
Question,
|
||||
through=QuestionAnswer,
|
||||
verbose_name=_("Answers")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Order position")
|
||||
verbose_name_plural = _("Order positions")
|
||||
|
||||
|
||||
class CartPosition(models.Model):
|
||||
"""
|
||||
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
|
||||
her cart. It therefore normally has a much shorter expiration time
|
||||
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
|
||||
the checkout process.
|
||||
"""
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event")
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
User, null=True, blank=True,
|
||||
verbose_name=_("User")
|
||||
)
|
||||
session = models.CharField(
|
||||
max_length=255, null=True, blank=True,
|
||||
verbose_name=_("Session key")
|
||||
)
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
verbose_name=_("Item")
|
||||
)
|
||||
variation = models.ForeignKey(
|
||||
ItemVariation,
|
||||
null=True, blank=True,
|
||||
verbose_name=_("Variation")
|
||||
)
|
||||
total = models.DecimalField(
|
||||
decimal_places=2, max_digits=10,
|
||||
verbose_name=_("Price")
|
||||
)
|
||||
datetime = models.DateTimeField(
|
||||
verbose_name=_("Datetime")
|
||||
)
|
||||
expires = models.DateTimeField(
|
||||
verbose_name=_("Expiration date")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Cart position")
|
||||
verbose_name_plural = _("Cart positions")
|
||||
|
||||
Reference in New Issue
Block a user