models.py 12.4 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
class Counter(models.Model):
    name = models.CharField(_('name'), max_length=30)
112
113
    club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
    products = models.ManyToManyField(Product, related_name="counters", verbose_name=_("products"), 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
120
    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
121
122
123
    class Meta:
        verbose_name = _('counter')

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

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

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

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

Skia's avatar
Skia committed
140
    def can_be_viewed_by(self, user):
141
142
143
144
        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
145

146
    def add_barman(self, user):
Skia's avatar
Skia committed
147
148
149
150
        """
        Logs a barman in to the given counter
        A user is stored as a tuple with its login time
        """
151
152
153
        Permanency(user=user, counter=self, start=timezone.now(), end=None).save()

    def del_barman(self, user):
Skia's avatar
Skia committed
154
155
156
        """
        Logs a barman out and store its permanency
        """
157
158
159
160
        perm = Permanency.objects.filter(counter=self, user=user, end=None).all()
        for p in perm:
            p.end = p.activity
            p.save()
Skia's avatar
Skia committed
161

Skia's avatar
Skia committed
162
    def get_barmen_list(self):
Skia's avatar
Skia committed
163
        """
Skia's avatar
Skia committed
164
        Returns the barman list as list of User
Skia's avatar
Skia committed
165
166
167

        Also handle the timeout of the barmen
        """
168
        pl = Permanency.objects.filter(counter=self, end=None).all()
169
        bl = []
170
171
172
173
        for p in pl:
            if timezone.now() - p.activity < timedelta(minutes=settings.SITH_BARMAN_TIMEOUT):
                p.save() # Update activity
                bl.append(p.user)
174
            else:
175
176
                p.end = p.activity
                p.save()
177
178
        return bl

Skia's avatar
Skia committed
179
180
    def get_random_barman(self):
        bl = self.get_barmen_list()
Skia's avatar
Skia committed
181
        return bl[random.randrange(0, len(bl))]
Skia's avatar
Skia committed
182

Sli's avatar
Sli committed
183
184
    def is_open(self):
        response = False
Skia's avatar
Skia committed
185
        if len(self.get_barmen_list()) > 0:
Sli's avatar
Sli committed
186
187
188
            response = True
        return response

Skia's avatar
Skia committed
189
190
191
    def barman_list(self):
        return [b.id for b in self.get_barmen_list()]

Skia's avatar
Skia committed
192
193
194
195
196
197
class Refilling(models.Model):
    """
    Handle the refilling
    """
    counter = models.ForeignKey(Counter, related_name="refillings", blank=False)
    amount = CurrencyField(_('amount'))
198
199
    operator = models.ForeignKey(User, related_name="refillings_as_operator", blank=False)
    customer = models.ForeignKey(Customer, related_name="refillings", blank=False)
200
    date = models.DateTimeField(_('date'))
Skia's avatar
Skia committed
201
    payment_method = models.CharField(_('payment method'), max_length=255,
202
            choices=settings.SITH_COUNTER_PAYMENT_METHOD, default='CASH')
203
    bank = models.CharField(_('bank'), max_length=255,
204
            choices=settings.SITH_COUNTER_BANK, default='OTHER')
Skia's avatar
Skia committed
205
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
206

Skia's avatar
Skia committed
207
208
209
    class Meta:
        verbose_name = _("refilling")

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

Skia's avatar
Skia committed
213
214
215
    def is_owned_by(self, user):
        return user.can_edit(self.counter) and self.payment_method != "CARD"

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

Skia's avatar
Skia committed
219
220
221
222
223
    def delete(self, *args, **kwargs):
        self.customer.amount -= self.amount
        self.customer.save()
        super(Refilling, self).delete(*args, **kwargs)

Skia's avatar
Skia committed
224
    def save(self, *args, **kwargs):
225
        if not self.date:
Skia's avatar
Skia committed
226
            self.date = timezone.now()
Skia's avatar
Skia committed
227
        self.full_clean()
Skia's avatar
Skia committed
228
229
230
231
        if not self.is_validated:
            self.customer.amount += self.amount
            self.customer.save()
            self.is_validated = True
Skia's avatar
Skia committed
232
        super(Refilling, self).save(*args, **kwargs)
Skia's avatar
Skia committed
233
234
235
236
237

class Selling(models.Model):
    """
    Handle the sellings
    """
238
    label = models.CharField(_("label"), max_length=64)
239
240
241
    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
242
243
    unit_price = CurrencyField(_('unit price'))
    quantity = models.IntegerField(_('quantity'))
244
245
246
247
248
    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
249
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
250

Skia's avatar
Skia committed
251
252
253
    class Meta:
        verbose_name = _("selling")

Skia's avatar
Skia committed
254
    def __str__(self):
Skia's avatar
Skia committed
255
        return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
Skia's avatar
Skia committed
256
257
                self.quantity*self.unit_price, self.customer.user.get_display_name())

Skia's avatar
Skia committed
258
259
260
261
262
263
264
265
    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
266
    def save(self, *args, **kwargs):
267
        if not self.date:
Skia's avatar
Skia committed
268
            self.date = timezone.now()
Skia's avatar
Skia committed
269
        self.full_clean()
Skia's avatar
Skia committed
270
271
272
273
        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
274
275
        super(Selling, self).save(*args, **kwargs)

Skia's avatar
Skia committed
276
277
278
279
class Permanency(models.Model):
    """
    This class aims at storing a traceability of who was barman where and when
    """
280
281
    user = models.ForeignKey(User, related_name="permanencies", verbose_name=_("user"))
    counter = models.ForeignKey(Counter, related_name="permanencies", verbose_name=_("counter"))
Skia's avatar
Skia committed
282
    start = models.DateTimeField(_('start date'))
283
284
    end = models.DateTimeField(_('end date'), null=True)
    activity = models.DateTimeField(_('last activity date'), auto_now=True)
Skia's avatar
Skia committed
285

Skia's avatar
Skia committed
286
287
288
    class Meta:
        verbose_name = _("permanency")

Skia's avatar
Skia committed
289
    def __str__(self):
Skia's avatar
Skia committed
290
291
        return "%s in %s from %s" % (self.user, self.counter,
                self.start.strftime("%Y-%m-%d %H:%M:%S"))
Skia's avatar
Skia committed
292

Skia's avatar
Skia committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
class CashRegisterSummary(models.Model):
    user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
    counter = models.ForeignKey(Counter, related_name="cash_summaries", verbose_name=_("counter"))
    date = models.DateTimeField(_('date'))
    comment = models.TextField(_('comment'), null=True, blank=True)
    emptied = models.BooleanField(_('emptied'), default=False)

    class Meta:
        verbose_name = _("cash register summary")

    def __str__(self):
        return "At %s by %s - Total: %s €" % (self.counter, self.user, self.get_total())

    def get_total(self):
        t = 0
        for it in self.items.all():
            t += it.quantity * it.value
        return t

    def save(self, *args, **kwargs):
        if not self.id:
            self.date = timezone.now()
        return super(CashRegisterSummary, self).save(*args, **kwargs)

class CashRegisterSummaryItem(models.Model):
    cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
    value = CurrencyField(_("value"))
    quantity = models.IntegerField(_('quantity'), default=0)
    check = models.BooleanField(_('check'), default=False)

    class Meta:
        verbose_name = _("cash register summary item")
325