models.py 10.2 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

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
33
34

    def __str__(self):
        return self.user.username

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
77
78
79
80
81
82
83
84
85
86
class Product(models.Model):
    """
    This describes a product, with all its related informations
    """
    name = models.CharField(_('name'), max_length=30)
    description = models.TextField(_('description'), blank=True)
    product_type = models.ForeignKey(ProductType, related_name='products', null=True, blank=True)
    code = models.CharField(_('code'), max_length=10)
    purchase_price = CurrencyField(_('purchase price'))
    selling_price = CurrencyField(_('selling price'))
    special_selling_price = CurrencyField(_('special selling price'))
    icon = models.ImageField(upload_to='products', null=True, blank=True)
    club = models.ForeignKey(Club, related_name="products")

Skia's avatar
Skia committed
87
88
89
    class Meta:
        verbose_name = _('product')

90
    def is_owned_by(self, user):
91
92
93
94
95
96
97
98
99
100
        """
        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
101
102
103
    def get_absolute_url(self):
        return reverse('counter:product_list')

Skia's avatar
Skia committed
104
105
106
107
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)
Skia's avatar
Skia committed
108
109
    type = models.CharField(_('subscription type'),
            max_length=255,
Skia's avatar
Skia committed
110
            choices=[('BAR',_('Bar')), ('OFFICE',_('Office')), ('EBOUTIC',_('Eboutic'))])
111
    sellers = models.ManyToManyField(Subscriber, verbose_name=_('sellers'), related_name='counters', blank=True)
Skia's avatar
Skia committed
112
113
    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
114
    barmen_session = {}
Skia's avatar
Skia committed
115

Skia's avatar
Skia committed
116
117
118
    class Meta:
        verbose_name = _('counter')

Skia's avatar
Skia committed
119
    def __getattribute__(self, name):
120
121
        if name == "edit_groups":
            return Group.objects.filter(name=self.club.unix_name+settings.SITH_BOARD_SUFFIX).all()
Skia's avatar
Skia committed
122
123
124
125
        return object.__getattribute__(self, name)

    def __str__(self):
        return self.name
Skia's avatar
Skia committed
126
127

    def get_absolute_url(self):
Skia's avatar
Skia committed
128
129
        if self.type == "EBOUTIC":
            return reverse('eboutic:main')
Skia's avatar
Skia committed
130
131
        return reverse('counter:details', kwargs={'counter_id': self.id})

132
    def is_owned_by(self, user):
133
134
        return user.is_in_group(settings.SITH_GROUPS['counter-admin']['name'])

Skia's avatar
Skia committed
135
    def can_be_viewed_by(self, user):
136
137
138
139
        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
140

Skia's avatar
Skia committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    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()

167
    def get_barmen_list(counter_id):
Skia's avatar
Skia committed
168
        """
Skia's avatar
Skia committed
169
        Returns the barman list as list of User
Skia's avatar
Skia committed
170
171
172

        Also handle the timeout of the barmen
        """
173
        bl = []
Skia's avatar
Skia committed
174
        counter_id = int(counter_id)
175
        if counter_id in list(Counter.barmen_session.keys()):
Skia's avatar
Skia committed
176
177
            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
178
                bl.append(User.objects.filter(id=b[0]).first())
179
180
181
            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
182
183
184
                for b in bl:
                    Counter.del_barman(counter_id, b.id)
                bl = []
185
186
187
                Counter.barmen_session[counter_id]['users'] = set()
        return bl

Skia's avatar
Skia committed
188
    def get_random_barman(counter_id):
Skia's avatar
Skia committed
189
        bl = Counter.get_barmen_list(counter_id)
Skia's avatar
Skia committed
190
        return bl[randrange(0, len(bl))]
Skia's avatar
Skia committed
191
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)
Skia's avatar
Skia committed
200
201
    date = models.DateTimeField(_('date'), auto_now=True)
    payment_method = models.CharField(_('payment method'), max_length=255,
202
203
204
            choices=settings.SITH_COUNTER_PAYMENT_METHOD, default='cash')
    bank = models.CharField(_('bank'), max_length=255,
            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
213
214
215
216
217

    # def get_absolute_url(self):
    #     return reverse('counter:details', kwargs={'counter_id': self.id})

    def save(self, *args, **kwargs):
        self.full_clean()
Skia's avatar
Skia committed
218
219
220
221
        if not self.is_validated:
            self.customer.amount += self.amount
            self.customer.save()
            self.is_validated = True
Skia's avatar
Skia committed
222
        super(Refilling, self).save(*args, **kwargs)
Skia's avatar
Skia committed
223
224
225
226
227

class Selling(models.Model):
    """
    Handle the sellings
    """
Skia's avatar
Skia committed
228
229
    label = models.CharField(_("label"), max_length=30)
    product = models.ForeignKey(Product, related_name="sellings", null=True, blank=True)
Skia's avatar
Skia committed
230
231
232
    counter = models.ForeignKey(Counter, related_name="sellings", blank=False)
    unit_price = CurrencyField(_('unit price'))
    quantity = models.IntegerField(_('quantity'))
233
234
    seller = models.ForeignKey(User, related_name="sellings_as_operator", blank=False)
    customer = models.ForeignKey(Customer, related_name="buyings", blank=False)
Skia's avatar
Skia committed
235
    date = models.DateTimeField(_('date'), auto_now=True)
Skia's avatar
Skia committed
236
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
237

Skia's avatar
Skia committed
238
239
240
    class Meta:
        verbose_name = _("selling")

Skia's avatar
Skia committed
241
    def __str__(self):
Skia's avatar
Skia committed
242
        return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
Skia's avatar
Skia committed
243
244
245
246
                self.quantity*self.unit_price, self.customer.user.get_display_name())

    def save(self, *args, **kwargs):
        self.full_clean()
Skia's avatar
Skia committed
247
248
249
250
        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
251
252
        super(Selling, self).save(*args, **kwargs)

Skia's avatar
Skia committed
253
254
255
256
257
258
259
260
261
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
262
263
264
    class Meta:
        verbose_name = _("permanency")

Skia's avatar
Skia committed
265
266
267
268
    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"))

269