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

    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
79
    description = models.TextField(_('description'), blank=True)
    product_type = models.ForeignKey(ProductType, related_name='products', null=True, blank=True)
80
    code = models.CharField(_('code'), max_length=16, blank=True)
81
82
83
84
85
86
    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)
108
    type = models.CharField(_('counter type'),
Skia's avatar
Skia committed
109
            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()

Skia's avatar
Skia committed
167
    def get_barmen_list(self):
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 = self.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
189
    def get_random_barman(self):
        bl = self.get_barmen_list()
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
    def is_open(self):
        response = False
Skia's avatar
Skia committed
194
        if len(self.get_barmen_list()) > 0:
Sli's avatar
Sli committed
195
196
197
            response = True
        return response

Skia's avatar
Skia committed
198
199
200
    def barman_list(self):
        return [b.id for b in self.get_barmen_list()]

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

Skia's avatar
Skia committed
216
217
218
    class Meta:
        verbose_name = _("refilling")

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

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

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

Skia's avatar
Skia committed
248
249
250
    class Meta:
        verbose_name = _("selling")

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

    def save(self, *args, **kwargs):
        self.full_clean()
Skia's avatar
Skia committed
257
258
259
260
        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
261
262
        super(Selling, self).save(*args, **kwargs)

Skia's avatar
Skia committed
263
264
265
266
267
268
269
270
271
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
272
273
274
    class Meta:
        verbose_name = _("permanency")

Skia's avatar
Skia committed
275
276
277
278
    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"))

279