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

Sli's avatar
Sli committed
192
193
194
195
196
197
    def is_open(self):
        response = False
        if len(Counter.get_barmen_list(self.id)) > 0:
            response = True
        return response

Skia's avatar
Skia committed
198
199
200
201
202
203
class Refilling(models.Model):
    """
    Handle the refilling
    """
    counter = models.ForeignKey(Counter, related_name="refillings", blank=False)
    amount = CurrencyField(_('amount'))
204
205
    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
206
207
    date = models.DateTimeField(_('date'), auto_now=True)
    payment_method = models.CharField(_('payment method'), max_length=255,
208
209
210
            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
211
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
212

Skia's avatar
Skia committed
213
214
215
    class Meta:
        verbose_name = _("refilling")

Skia's avatar
Skia committed
216
    def __str__(self):
Skia's avatar
Skia committed
217
        return "Refilling: %.2f for %s" % (self.amount, self.customer.user.get_display_name())
Skia's avatar
Skia committed
218
219
220
221
222
223

    # 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
224
225
226
227
        if not self.is_validated:
            self.customer.amount += self.amount
            self.customer.save()
            self.is_validated = True
Skia's avatar
Skia committed
228
        super(Refilling, self).save(*args, **kwargs)
Skia's avatar
Skia committed
229
230
231
232
233

class Selling(models.Model):
    """
    Handle the sellings
    """
Skia's avatar
Skia committed
234
235
    label = models.CharField(_("label"), max_length=30)
    product = models.ForeignKey(Product, related_name="sellings", null=True, blank=True)
Skia's avatar
Skia committed
236
237
238
    counter = models.ForeignKey(Counter, related_name="sellings", blank=False)
    unit_price = CurrencyField(_('unit price'))
    quantity = models.IntegerField(_('quantity'))
239
240
    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
241
    date = models.DateTimeField(_('date'), auto_now=True)
Skia's avatar
Skia committed
242
    is_validated = models.BooleanField(_('is validated'), default=False)
Skia's avatar
Skia committed
243

Skia's avatar
Skia committed
244
245
246
    class Meta:
        verbose_name = _("selling")

Skia's avatar
Skia committed
247
    def __str__(self):
Skia's avatar
Skia committed
248
        return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
Skia's avatar
Skia committed
249
250
251
252
                self.quantity*self.unit_price, self.customer.user.get_display_name())

    def save(self, *args, **kwargs):
        self.full_clean()
Skia's avatar
Skia committed
253
254
255
256
        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
257
258
        super(Selling, self).save(*args, **kwargs)

Skia's avatar
Skia committed
259
260
261
262
263
264
265
266
267
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
268
269
270
    class Meta:
        verbose_name = _("permanency")

Skia's avatar
Skia committed
271
272
273
274
    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"))

275