models.py 9.88 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
from django.core.exceptions import ValidationError
31
from django.urls import reverse
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):
53 54 55
    member = models.ForeignKey(
        User, related_name="subscriptions", on_delete=models.CASCADE
    )
Sli's avatar
Sli committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    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"),
    )
75

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

    def clean(self):
80
        try:
Sli's avatar
Sli committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94
            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
95
        except:  # This should not happen, because the form should have handled the data before, but sadly, it still
Sli's avatar
Sli committed
96 97
            # calls the model validation :'(
            # TODO see SubscriptionForm's clean method
Skia's avatar
Skia committed
98
            raise ValidationError(_("Subscription error"))
99

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

104
        if not Customer.objects.filter(user=self.member).exists():
Sli's avatar
Sli committed
105 106 107 108 109 110 111 112 113
            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})
114
            if form.is_valid():
Sli's avatar
Sli committed
115 116 117 118 119 120
                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
121
        self.member.make_home()
Sli's avatar
Sli committed
122
        if settings.IS_OLD_MYSQL_PRESENT:
Skia's avatar
Skia committed
123
            import MySQLdb
Sli's avatar
Sli committed
124

Krophil's avatar
Krophil committed
125
            try:  # Create subscription on the old site: TODO remove me!
Sli's avatar
Sli committed
126
                LOCATION = {"SEVENANS": 5, "BELFORT": 6, "MONTBELIARD": 9, "EBOUTIC": 5}
Sli's avatar
Sli committed
127
                TYPE = {
Sli's avatar
Sli committed
128 129 130 131 132 133 134 135 136 137 138 139 140
                    "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
141
                }
Sli's avatar
Sli committed
142
                PAYMENT = {
Krophil's avatar
Krophil committed
143 144 145 146 147 148 149
                    "CHECK": 1,
                    "CARD": 2,
                    "CASH": 3,
                    "OTHER": 4,
                    "EBOUTIC": 5,
                    "OTHER": 0,
                }
Sli's avatar
Sli committed
150 151 152

                db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
                c = db.cursor()
Sli's avatar
Sli committed
153 154 155 156 157 158 159 160 161 162 163 164
                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
165 166
                db.commit()
            except Exception as e:
Krophil's avatar
Krophil committed
167
                with open(settings.BASE_DIR + "/subscription_fail.log", "a") as f:
Sli's avatar
Sli committed
168 169 170 171
                    print(
                        "FAIL to add subscription to %s to old site" % (self.member),
                        file=f,
                    )
Sli's avatar
Sli committed
172 173
                    print("Reason: %s" % (repr(e)), file=f)
                db.rollback()
174

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

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

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

    @staticmethod
206
    def compute_end(duration, start=None, user=None):
207 208 209 210 211 212 213 214 215 216
        """
        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:
217 218
            start = Subscription.compute_start(duration=duration, user=user)

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

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

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

Krophil's avatar
Krophil committed
233

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


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


258 259 260 261 262 263 264 265 266
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
267
    print("=" * 80)
268 269 270 271 272 273 274 275
    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
276
    print("=" * 80)
277 278 279 280 281 282 283 284
    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
285
    print("=" * 80)
Skia's avatar
Skia committed
286
    bibou_test(1, date(2000, 2, 29))
287 288 289
    bibou_test(2, date(2000, 2, 29))
    bibou_test(1, date(2000, 5, 31))
    bibou_test(1, date(2000, 7, 31))
290 291 292 293 294
    bibou_test(1)
    bibou_test(2)
    bibou_test(3)
    bibou_test(4)

Krophil's avatar
Krophil committed
295

296 297
if __name__ == "__main__":
    guy()