models.py 9.85 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

25
26
27
28
29
from datetime import date, timedelta
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
30
31
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
Skia's avatar
Skia committed
32
from django.contrib.auth.forms import PasswordResetForm
33

34
35
36
37
from dateutil.relativedelta import relativedelta

import math

38
from core.models import User
39
from core.utils import get_start_of_semester
40

Skia's avatar
Skia committed
41

42
def validate_type(value):
Skia's avatar
Skia committed
43
    if value not in settings.SITH_SUBSCRIPTIONS.keys():
Sli's avatar
Sli committed
44
        raise ValidationError(_("Bad subscription type"))
45

Krophil's avatar
Krophil committed
46

47
def validate_payment(value):
48
    if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
Sli's avatar
Sli committed
49
        raise ValidationError(_("Bad payment method"))
50

Krophil's avatar
Krophil committed
51

52
class Subscription(models.Model):
Sli's avatar
Sli committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    member = models.ForeignKey(User, related_name="subscriptions")
    subscription_type = models.CharField(
        _("subscription type"),
        max_length=255,
        choices=(
            (k, v["name"]) for k, v in sorted(settings.SITH_SUBSCRIPTIONS.items())
        ),
    )
    subscription_start = models.DateField(_("subscription start"))
    subscription_end = models.DateField(_("subscription end"))
    payment_method = models.CharField(
        _("payment method"),
        max_length=255,
        choices=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD,
    )
    location = models.CharField(
        choices=settings.SITH_SUBSCRIPTION_LOCATIONS,
        max_length=20,
        verbose_name=_("location"),
    )
73

74
    class Meta:
Sli's avatar
Sli committed
75
        ordering = ["subscription_start"]
76
77

    def clean(self):
78
        try:
Sli's avatar
Sli committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
            for s in (
                Subscription.objects.filter(member=self.member)
                .exclude(pk=self.pk)
                .all()
            ):
                if (
                    s.is_valid_now()
                    and s.subscription_end
                    - timedelta(weeks=settings.SITH_SUBSCRIPTION_END)
                    > date.today()
                ):
                    raise ValidationError(
                        _("You can not subscribe many time for the same period")
                    )
Krophil's avatar
Krophil committed
93
        except:  # This should not happen, because the form should have handled the data before, but sadly, it still
Sli's avatar
Sli committed
94
95
            # calls the model validation :'(
            # TODO see SubscriptionForm's clean method
Skia's avatar
Skia committed
96
            raise ValidationError(_("Subscription error"))
97

98
99
100
    def save(self):
        super(Subscription, self).save()
        from counter.models import Customer
Sli's avatar
Sli committed
101

102
        if not Customer.objects.filter(user=self.member).exists():
Sli's avatar
Sli committed
103
104
105
106
107
108
109
110
111
            last_id = (
                Customer.objects.count() + 1504
            )  # Number to keep a continuity with the old site
            Customer(
                user=self.member,
                account_id=Customer.generate_account_id(last_id + 1),
                amount=0,
            ).save()
            form = PasswordResetForm({"email": self.member.email})
Skia's avatar
Skia committed
112
            if form.is_valid():
Sli's avatar
Sli committed
113
114
115
116
117
118
                form.save(
                    use_https=True,
                    email_template_name="core/new_user_email.jinja",
                    subject_template_name="core/new_user_email_subject.jinja",
                    from_email="ae@utbm.fr",
                )
Skia's avatar
Skia committed
119
        self.member.make_home()
Sli's avatar
Sli committed
120
        if settings.IS_OLD_MYSQL_PRESENT:
Skia's avatar
Skia committed
121
            import MySQLdb
Sli's avatar
Sli committed
122

Krophil's avatar
Krophil committed
123
            try:  # Create subscription on the old site: TODO remove me!
Sli's avatar
Sli committed
124
                LOCATION = {"SEVENANS": 5, "BELFORT": 6, "MONTBELIARD": 9, "EBOUTIC": 5}
Sli's avatar
Sli committed
125
                TYPE = {
Sli's avatar
Sli committed
126
127
128
129
130
131
132
133
134
135
136
137
138
                    "un-semestre": 0,
                    "deux-semestres": 1,
                    "cursus-tronc-commun": 2,
                    "cursus-branche": 3,
                    "membre-honoraire": 4,
                    "assidu": 5,
                    "amicale/doceo": 6,
                    "reseau-ut": 7,
                    "crous": 8,
                    "sbarro/esta": 9,
                    "cursus-alternant": 10,
                    "welcome-semestre": 11,
                    "deux-mois-essai": 12,
Krophil's avatar
Krophil committed
139
                }
Sli's avatar
Sli committed
140
                PAYMENT = {
Krophil's avatar
Krophil committed
141
142
143
144
145
146
147
                    "CHECK": 1,
                    "CARD": 2,
                    "CASH": 3,
                    "OTHER": 4,
                    "EBOUTIC": 5,
                    "OTHER": 0,
                }
Sli's avatar
Sli committed
148
149
150

                db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
                c = db.cursor()
Sli's avatar
Sli committed
151
152
153
154
155
156
157
158
159
160
161
162
                c.execute(
                    """INSERT INTO ae_cotisations (id_utilisateur, date_cotis, date_fin_cotis, mode_paiement_cotis,
                type_cotis, id_comptoir) VALUES (%s, %s, %s, %s, %s, %s)""",
                    (
                        self.member.id,
                        self.subscription_start,
                        self.subscription_end,
                        PAYMENT[self.payment_method],
                        TYPE[self.subscription_type],
                        LOCATION[self.location],
                    ),
                )
Sli's avatar
Sli committed
163
164
                db.commit()
            except Exception as e:
Krophil's avatar
Krophil committed
165
                with open(settings.BASE_DIR + "/subscription_fail.log", "a") as f:
Sli's avatar
Sli committed
166
167
168
169
                    print(
                        "FAIL to add subscription to %s to old site" % (self.member),
                        file=f,
                    )
Sli's avatar
Sli committed
170
171
                    print("Reason: %s" % (repr(e)), file=f)
                db.rollback()
172

173
    def get_absolute_url(self):
Sli's avatar
Sli committed
174
        return reverse("core:user_edit", kwargs={"user_id": self.member.pk})
175

176
    def __str__(self):
177
        if hasattr(self, "member") and self.member is not None:
Sli's avatar
Sli committed
178
            return self.member.username + " - " + str(self.pk)
179
        else:
Sli's avatar
Sli committed
180
            return "No user - " + str(self.pk)
181

182
    @staticmethod
183
    def compute_start(d=None, duration=1, user=None):
184
185
        """
        This function computes the start date of the subscription with respect to the given date (default is today),
Skia's avatar
Skia committed
186
        and the start date given in settings.SITH_START_DATE.
187
        It takes the nearest past start date.
Skia's avatar
Skia committed
188
        Exemples: with SITH_START_DATE = (8, 15)
189
190
191
192
            Today      -> Start date
            2015-03-17 -> 2015-02-15
            2015-01-11 -> 2014-08-15
        """
Skia's avatar
Skia committed
193
194
        if not d:
            d = date.today()
195
        if user is not None and user.subscriptions.exists():
Skia's avatar
Skia committed
196
197
198
            last = user.subscriptions.last()
            if last.is_valid_now():
                d = last.subscription_end
Krophil's avatar
Krophil committed
199
        if duration <= 2:  # Sliding subscriptions for 1 or 2 semesters
200
            return d
201
        return get_start_of_semester(d)
202
203

    @staticmethod
204
    def compute_end(duration, start=None, user=None):
205
206
207
208
209
210
211
212
213
214
        """
        This function compute the end date of the subscription given a start date and a duration in number of semestre
        Exemple:
            Start - Duration -> End date
            2015-09-18 - 1 -> 2016-03-18
            2015-09-18 - 2 -> 2016-09-18
            2015-09-18 - 3 -> 2017-03-18
            2015-09-18 - 4 -> 2017-09-18
        """
        if start is None:
215
216
            start = Subscription.compute_start(duration=duration, user=user)

Sli's avatar
Sli committed
217
218
219
220
221
        return start + relativedelta(
            months=round(6 * duration),
            days=math.ceil((6 * duration - round(6 * duration)) * 30),
        )

Skia's avatar
Skia committed
222
    def can_be_edited_by(self, user):
Skia's avatar
Skia committed
223
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
Skia's avatar
Skia committed
224

225
    def is_valid_now(self):
Sli's avatar
Sli committed
226
227
228
229
        return (
            self.subscription_start <= date.today()
            and date.today() <= self.subscription_end
        )
230

Krophil's avatar
Krophil committed
231

232
def guy_test(date, duration=4):
Sli's avatar
Sli committed
233
234
235
236
237
238
239
    print(
        str(date)
        + " - "
        + str(duration)
        + " -> "
        + str(Subscription.compute_start(date, duration))
    )
Krophil's avatar
Krophil committed
240
241


242
def bibou_test(duration, date=date.today()):
Sli's avatar
Sli committed
243
244
245
246
247
248
249
250
251
252
253
    print(
        str(date)
        + " - "
        + str(duration)
        + " -> "
        + str(
            Subscription.compute_end(
                duration, Subscription.compute_start(date, duration)
            )
        )
    )
Krophil's avatar
Krophil committed
254
255


256
257
258
259
260
261
262
263
264
def guy():
    guy_test(date(2015, 7, 11))
    guy_test(date(2015, 8, 11))
    guy_test(date(2015, 2, 17))
    guy_test(date(2015, 3, 17))
    guy_test(date(2015, 1, 11))
    guy_test(date(2015, 2, 11))
    guy_test(date(2015, 8, 17))
    guy_test(date(2015, 9, 17))
Sli's avatar
Sli committed
265
    print("=" * 80)
266
267
268
269
270
271
272
273
    guy_test(date(2015, 7, 11), 1)
    guy_test(date(2015, 8, 11), 2)
    guy_test(date(2015, 2, 17), 3)
    guy_test(date(2015, 3, 17), 4)
    guy_test(date(2015, 1, 11), 1)
    guy_test(date(2015, 2, 11), 2)
    guy_test(date(2015, 8, 17), 3)
    guy_test(date(2015, 9, 17), 4)
Sli's avatar
Sli committed
274
    print("=" * 80)
275
276
277
278
279
280
281
282
    bibou_test(1, date(2015, 2, 18))
    bibou_test(2, date(2015, 2, 18))
    bibou_test(3, date(2015, 2, 18))
    bibou_test(4, date(2015, 2, 18))
    bibou_test(1, date(2015, 9, 18))
    bibou_test(2, date(2015, 9, 18))
    bibou_test(3, date(2015, 9, 18))
    bibou_test(4, date(2015, 9, 18))
Sli's avatar
Sli committed
283
    print("=" * 80)
Skia's avatar
Skia committed
284
    bibou_test(1, date(2000, 2, 29))
285
286
287
    bibou_test(2, date(2000, 2, 29))
    bibou_test(1, date(2000, 5, 31))
    bibou_test(1, date(2000, 7, 31))
288
289
290
291
292
    bibou_test(1)
    bibou_test(2)
    bibou_test(3)
    bibou_test(4)

Krophil's avatar
Krophil committed
293

294
295
if __name__ == "__main__":
    guy()