models.py 12 KB
Newer Older
Skia's avatar
Skia committed
1
from django.db import models, DataError
Skia's avatar
Skia committed
2
from django.utils.translation import ugettext_lazy as _
3
from django.utils import timezone
Skia's avatar
Skia committed
4
from django.conf import settings
Skia's avatar
Skia committed
5
from django.core.urlresolvers import reverse
Skia's avatar
Skia committed
6
from django.forms import ValidationError
Skia's avatar
Skia committed
7

Skia's avatar
Skia committed
8
from datetime import timedelta
Skia's avatar
Skia committed
9
10
import random
import string
11

Skia's avatar
Skia committed
12
from club.models import Club
13
14
from accounting.models import CurrencyField
from core.models import Group, User
15
16
from subscription.models import Subscriber
from subscription.views import get_subscriber
Skia's avatar
Skia committed
17

18
19
20
21
22
23
24
class Customer(models.Model):
    """
    This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
    is used by other accounting classes as reference to the customer, rather than using User
    """
    user = models.OneToOneField(User, primary_key=True)
    account_id = models.CharField(_('account id'), max_length=10, unique=True)
Skia's avatar
Skia committed
25
    amount = CurrencyField(_('amount'))
26
27
28
29

    class Meta:
        verbose_name = _('customer')
        verbose_name_plural = _('customers')
Skia's avatar
Skia committed
30
        ordering = ['account_id',]
31
32

    def __str__(self):
Skia's avatar
Skia committed
33
        return "%s - %s" % (self.user.username, self.account_id)
34

Skia's avatar
Skia committed
35
36
37
38
39
40
    def generate_account_id(number):
        number = str(number)
        letter = random.choice(string.ascii_lowercase)
        while Customer.objects.filter(account_id=number+letter).exists():
            letter = random.choice(string.ascii_lowercase)
        return number+letter
41

Skia's avatar
Skia committed
42
43
44
45
46
    def save(self, *args, **kwargs):
        if self.amount < 0:
            raise ValidationError(_("Not enough money"))
        super(Customer, self).save(*args, **kwargs)

47
48
49
50
51
52
53
54
55
class ProductType(models.Model):
    """
    This describes a product type
    Useful only for categorizing, changes are made at the product level for now
    """
    name = models.CharField(_('name'), max_length=30)
    description = models.TextField(_('description'), null=True, blank=True)
    icon = models.ImageField(upload_to='products', null=True, blank=True)

Skia's avatar
Skia committed
56
57
58
    class Meta:
        verbose_name = _('product type')

59
60
61
62
63
64
65
66
67
68
69
    def is_owned_by(self, user):
        """
        Method to see if that object can be edited by the given user
        """
        if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
            return True
        return False

    def __str__(self):
        return self.name

Skia's avatar
Skia committed
70
71
72
    def get_absolute_url(self):
        return reverse('counter:producttype_list')

73
74
75
76
class Product(models.Model):
    """
    This describes a product, with all its related informations
    """
77
    name = models.CharField(_('name'), max_length=64)
78
    description = models.TextField(_('description'), blank=True)
79
80
    product_type = models.ForeignKey(ProductType, related_name='products', verbose_name=_("product type"), null=True, blank=True,
            on_delete=models.SET_NULL)
81
    code = models.CharField(_('code'), max_length=16, blank=True)
82
83
84
    purchase_price = CurrencyField(_('purchase price'))
    selling_price = CurrencyField(_('selling price'))
    special_selling_price = CurrencyField(_('special selling price'))
85
86
    icon = models.ImageField(upload_to='products', null=True, blank=True, verbose_name=_("icon"))
    club = models.ForeignKey(Club, related_name="products", verbose_name=_("club"))
87
88
    limit_age = models.IntegerField(_('limit age'), default=0)
    tray = models.BooleanField(_('tray price'), default=False)
89
90
91
    parent_product = models.ForeignKey('self', related_name='children_products', verbose_name=_("parent product"), null=True,
            blank=True, on_delete=models.SET_NULL)
    buying_groups = models.ManyToManyField(Group, related_name='products', verbose_name=_("buying groups"))
92

Skia's avatar
Skia committed
93
94
95
    class Meta:
        verbose_name = _('product')

96
    def is_owned_by(self, user):
97
98
99
100
101
102
103
104
105
106
        """
        Method to see if that object can be edited by the given user
        """
        if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
            return True
        return False

    def __str__(self):
        return self.name

Skia's avatar
Skia committed
107
108
109
    def get_absolute_url(self):
        return reverse('counter:product_list')

Skia's avatar
Skia committed
110
111
112
113
class Counter(models.Model):
    name = models.CharField(_('name'), max_length=30)
    club = models.ForeignKey(Club, related_name="counters")
    products = models.ManyToManyField(Product, related_name="counters", blank=True)
114
    type = models.CharField(_('counter type'),
Skia's avatar
Skia committed
115
            max_length=255,
Skia's avatar
Skia committed
116
            choices=[('BAR',_('Bar')), ('OFFICE',_('Office')), ('EBOUTIC',_('Eboutic'))])
117
    sellers = models.ManyToManyField(Subscriber, verbose_name=_('sellers'), related_name='counters', blank=True)
Skia's avatar
Skia committed
118
119
    edit_groups = models.ManyToManyField(Group, related_name="editable_counters", blank=True)
    view_groups = models.ManyToManyField(Group, related_name="viewable_counters", blank=True)
Skia's avatar
Skia committed
120
    barmen_session = {}
Skia's avatar
Skia committed
121

Skia's avatar
Skia committed
122
123
124
    class Meta:
        verbose_name = _('counter')

Skia's avatar
Skia committed
125
    def __getattribute__(self, name):
126
127
        if name == "edit_groups":
            return Group.objects.filter(name=self.club.unix_name+settings.SITH_BOARD_SUFFIX).all()
Skia's avatar
Skia committed
128
129
130
131
        return object.__getattribute__(self, name)

    def __str__(self):
        return self.name
Skia's avatar
Skia committed
132
133

    def get_absolute_url(self):
Skia's avatar
Skia committed
134
135
        if self.type == "EBOUTIC":
            return reverse('eboutic:main')
Skia's avatar
Skia committed
136
137
        return reverse('counter:details', kwargs={'counter_id': self.id})

138
    def is_owned_by(self, user):
139
140
        return user.is_in_group(settings.SITH_GROUPS['counter-admin']['name'])

Skia's avatar
Skia committed
141
    def can_be_viewed_by(self, user):
142
143
144
145
        if self.type == "BAR" or self.type == "EBOUTIC":
            return True
        sub = get_subscriber(request.user)
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or sub in self.sellers
146

Skia's avatar
Skia committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
    def add_barman(counter_id, user_id):
        """
        Logs a barman in to the given counter
        A user is stored as a tuple with its login time
        """
        counter_id = int(counter_id)
        user_id = int(user_id)
        if counter_id not in Counter.barmen_session.keys():
            Counter.barmen_session[counter_id] = {'users': {(user_id, timezone.now())}, 'time': timezone.now()}
        else:
            Counter.barmen_session[counter_id]['users'].add((user_id, timezone.now()))

    def del_barman(counter_id, user_id):
        """
        Logs a barman out and store its permanency
        """
        counter_id = int(counter_id)
        user_id = int(user_id)
        user_tuple = None
        for t in Counter.barmen_session[counter_id]['users']:
            if t[0] == user_id: user_tuple = t
        Counter.barmen_session[counter_id]['users'].remove(user_tuple)
        u = User.objects.filter(id=user_id).first()
        c = Counter.objects.filter(id=counter_id).first()
        Permanency(user=u, counter=c, start=user_tuple[1], end=Counter.barmen_session[counter_id]['time']).save()

Skia's avatar
Skia committed
173
    def get_barmen_list(self):
Skia's avatar
Skia committed
174
        """
Skia's avatar
Skia committed
175
        Returns the barman list as list of User
Skia's avatar
Skia committed
176
177
178

        Also handle the timeout of the barmen
        """
179
        bl = []
Skia's avatar
Skia committed
180
        counter_id = self.id
181
        if counter_id in list(Counter.barmen_session.keys()):
Skia's avatar
Skia committed
182
183
            for b in Counter.barmen_session[counter_id]['users']:
                # Reminder: user is stored as a tuple with its login time
Skia's avatar
Skia committed
184
                bl.append(User.objects.filter(id=b[0]).first())
185
186
187
            if (timezone.now() - Counter.barmen_session[counter_id]['time']) < timedelta(minutes=settings.SITH_BARMAN_TIMEOUT):
                Counter.barmen_session[counter_id]['time'] = timezone.now()
            else:
Skia's avatar
Skia committed
188
189
190
                for b in bl:
                    Counter.del_barman(counter_id, b.id)
                bl = []
191
192
193
                Counter.barmen_session[counter_id]['users'] = set()
        return bl

Skia's avatar
Skia committed
194
195
    def get_random_barman(self):
        bl = self.get_barmen_list()
Skia's avatar
Skia committed
196
        return bl[random.randrange(0, len(bl))]
Skia's avatar
Skia committed
197

Sli's avatar
Sli committed
198
199
    def is_open(self):
        response = False
Skia's avatar
Skia committed
200
        if len(self.get_barmen_list()) > 0:
Sli's avatar
Sli committed
201
202
203
            response = True
        return response

Skia's avatar
Skia committed
204
205
206
    def barman_list(self):
        return [b.id for b in self.get_barmen_list()]

Skia's avatar
Skia committed
207
208
209
210
211
212
class Refilling(models.Model):
    """
    Handle the refilling
    """
    counter = models.ForeignKey(Counter, related_name="refillings", blank=False)
    amount = CurrencyField(_('amount'))
213
214
    operator = models.ForeignKey(User, related_name="refillings_as_operator", blank=False)
    customer = models.ForeignKey(Customer, related_name="refillings", blank=False)
215
    date = models.DateTimeField(_('date'))
Skia's avatar
Skia committed
216
    payment_method = models.CharField(_('payment method'), max_length=255,
217
            choices=settings.SITH_COUNTER_PAYMENT_METHOD, default='CASH')
218
    bank = models.CharField(_('bank'), max_length=255,
219
            choices=settings.SITH_COUNTER_BANK, default='OTHER')
Skia's avatar
Skia committed
220
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
221

Skia's avatar
Skia committed
222
223
224
    class Meta:
        verbose_name = _("refilling")

Skia's avatar
Skia committed
225
    def __str__(self):
Skia's avatar
Skia committed
226
        return "Refilling: %.2f for %s" % (self.amount, self.customer.user.get_display_name())
Skia's avatar
Skia committed
227

Skia's avatar
Skia committed
228
229
230
    def is_owned_by(self, user):
        return user.can_edit(self.counter) and self.payment_method != "CARD"

Skia's avatar
Skia committed
231
232
233
    # def get_absolute_url(self):
    #     return reverse('counter:details', kwargs={'counter_id': self.id})

Skia's avatar
Skia committed
234
235
236
237
238
    def delete(self, *args, **kwargs):
        self.customer.amount -= self.amount
        self.customer.save()
        super(Refilling, self).delete(*args, **kwargs)

Skia's avatar
Skia committed
239
    def save(self, *args, **kwargs):
240
        if not self.date:
Skia's avatar
Skia committed
241
            self.date = timezone.now()
Skia's avatar
Skia committed
242
        self.full_clean()
Skia's avatar
Skia committed
243
244
245
246
        if not self.is_validated:
            self.customer.amount += self.amount
            self.customer.save()
            self.is_validated = True
Skia's avatar
Skia committed
247
        super(Refilling, self).save(*args, **kwargs)
Skia's avatar
Skia committed
248
249
250
251
252

class Selling(models.Model):
    """
    Handle the sellings
    """
253
    label = models.CharField(_("label"), max_length=64)
254
255
256
    product = models.ForeignKey(Product, related_name="sellings", null=True, blank=True, on_delete=models.SET_NULL)
    counter = models.ForeignKey(Counter, related_name="sellings", null=True, blank=False, on_delete=models.SET_NULL)
    club = models.ForeignKey(Club, related_name="sellings", null=True, blank=False, on_delete=models.SET_NULL)
Skia's avatar
Skia committed
257
258
    unit_price = CurrencyField(_('unit price'))
    quantity = models.IntegerField(_('quantity'))
259
260
261
262
263
    seller = models.ForeignKey(User, related_name="sellings_as_operator", null=True, blank=False, on_delete=models.SET_NULL)
    customer = models.ForeignKey(Customer, related_name="buyings", null=True, blank=False, on_delete=models.SET_NULL)
    date = models.DateTimeField(_('date'))
    payment_method = models.CharField(_('payment method'), max_length=255,
            choices=[('SITH_ACCOUNT', _('Sith account')), ('CARD', _('Credit card'))], default='SITH_ACCOUNT')
Skia's avatar
Skia committed
264
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
265

Skia's avatar
Skia committed
266
267
268
    class Meta:
        verbose_name = _("selling")

Skia's avatar
Skia committed
269
    def __str__(self):
Skia's avatar
Skia committed
270
        return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
Skia's avatar
Skia committed
271
272
                self.quantity*self.unit_price, self.customer.user.get_display_name())

Skia's avatar
Skia committed
273
274
275
276
277
278
279
280
    def is_owned_by(self, user):
        return user.can_edit(self.counter) and self.payment_method != "CARD"

    def delete(self, *args, **kwargs):
        self.customer.amount += self.quantity * self.unit_price
        self.customer.save()
        super(Selling, self).delete(*args, **kwargs)

Skia's avatar
Skia committed
281
    def save(self, *args, **kwargs):
282
        if not self.date:
Skia's avatar
Skia committed
283
            self.date = timezone.now()
Skia's avatar
Skia committed
284
        self.full_clean()
Skia's avatar
Skia committed
285
286
287
288
        if not self.is_validated:
            self.customer.amount -= self.quantity * self.unit_price
            self.customer.save()
            self.is_validated = True
Skia's avatar
Skia committed
289
290
        super(Selling, self).save(*args, **kwargs)

Skia's avatar
Skia committed
291
292
293
294
295
296
297
298
299
class Permanency(models.Model):
    """
    This class aims at storing a traceability of who was barman where and when
    """
    user = models.ForeignKey(User, related_name="permanencies")
    counter = models.ForeignKey(Counter, related_name="permanencies")
    start = models.DateTimeField(_('start date'))
    end = models.DateTimeField(_('end date'))

Skia's avatar
Skia committed
300
301
302
    class Meta:
        verbose_name = _("permanency")

Skia's avatar
Skia committed
303
304
305
306
    def __str__(self):
        return "%s in %s from %s to %s" % (self.user, self.counter,
                self.start.strftime("%Y-%m-%d %H:%M:%S"), self.end.strftime("%Y-%m-%d %H:%M:%S"))

307