from django.db import models from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import User, AnonymousUser from django.core.validators import RegexValidator from gestioncof.models import CofProfile from django.utils.six.moves import reduce import datetime import re def choices_length(choices): return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) def default_promo(): now = datetime.date.today() return now.month <= 8 and now.year-1 or now.year class Account(models.Model): cofprofile = models.OneToOneField( CofProfile, on_delete = models.PROTECT, related_name = "account_kfet") trigramme = models.CharField( unique = True, max_length = 3, validators = [RegexValidator(regex='^[^a-z]{3}$')]) balance = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0) frozen = models.BooleanField(default = False) # Optional PROMO_CHOICES = [(r,r) for r in range(1980, datetime.date.today().year+1)] promo = models.IntegerField( choices = PROMO_CHOICES, blank = True, null = True, default = default_promo()) nickname = models.CharField( max_length = 255, blank = True, default = "") password = models.CharField( max_length = 255, unique = True, blank = True, null = True, default = None) def __str__(self): return '%s (%s)' % (self.trigramme, self.name) # Propriétés pour accéder aux attributs de user et cofprofile et user @property def user(self): return self.cofprofile.user @property def username(self): return self.cofprofile.user.username @property def first_name(self): return self.cofprofile.user.first_name @property def last_name(self): return self.cofprofile.user.last_name @property def email(self): return self.cofprofile.user.email @property def departement(self): return self.cofprofile.departement @property def is_cof(self): return self.cofprofile.is_cof # Propriétés supplémentaires @property def real_balance(self): if (hasattr(self, 'negative')): return self.balance + self.negative.balance_offset return self.balance @property def name(self): if self.first_name and self.last_name: return '%s %s' % (self.first_name, self.last_name) elif self.first_name: return self.first_name else: return self.last_name @staticmethod def is_validandfree(trigramme): data = { 'is_valid' : False, 'is_free' : False } pattern = re.compile("^[^a-z]{3}$") data['is_valid'] = pattern.match(trigramme) and True or False try: account = Account.objects.get(trigramme=trigramme) except Account.DoesNotExist: data['is_free'] = True return data # Surcharge Méthode save() avec gestions de User et CofProfile # Args: # - data : datas pour User et CofProfile # Action: # - Enregistre User, CofProfile à partir de "data" # - Enregistre Account def save(self, data = {}, *args, **kwargs): if self.pk: # Account update # Updating User with data user = self.user user.first_name = data.get("first_name", user.first_name) user.last_name = data.get("last_name", user.last_name) user.email = data.get("email", user.email) user.save() # Updating CofProfile with data cof = self.cofprofile cof.departement = data.get("departement", cof.departement) cof.save() else: # New account # Checking if user has already an account username = data.get("username") try: user = User.objects.get(username=username) if hasattr(user.profile, "account_kfet"): trigramme = user.profile.account_kfet.trigramme raise Account.UserHasAccount(trigramme) except User.DoesNotExist: pass # Creating or updating User instance (user, _) = User.objects.get_or_create(username=username) if "first_name" in data: user.first_name = data['first_name'] if "last_name" in data: user.last_name = data['last_name'] if "email" in data: user.email = data['email'] user.save() # Creating or updating CofProfile instance (cof, _) = CofProfile.objects.get_or_create(user=user) if "login_clipper" in data: cof.login_clipper = data['login_clipper'] if "departement" in data: cof.departement = data['departement'] cof.save() self.cofprofile = cof super(Account, self).save(*args, **kwargs) # Surcharge de delete # Pas de suppression possible # Cas à régler plus tard def delete(self, *args, **kwargs): pass class UserHasAccount(Exception): def __init__(self, trigramme): self.trigramme = trigramme class AccountNegative(models.Model): account = models.OneToOneField( Account, on_delete = models.PROTECT, related_name = "negative") start = models.DateTimeField(default = datetime.datetime.now) balance_offset = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0) authorized_overdraft = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0) comment = models.CharField(max_length = 255, blank = True) class Checkout(models.Model): created_by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+") name = models.CharField(max_length = 45) valid_from = models.DateTimeField() valid_to = models.DateTimeField() balance = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0) is_protected = models.BooleanField(default = False) def get_absolute_url(self): return reverse('kfet.checkout.read', kwargs={'pk': self.pk}) class Meta: ordering = ['-valid_to'] def __str__(self): return self.name class CheckoutTransfer(models.Model): from_checkout = models.ForeignKey( Checkout, on_delete = models.PROTECT, related_name = "transfers_from") to_checkout = models.ForeignKey( Checkout, on_delete = models.PROTECT, related_name = "transfers_to") amount = models.DecimalField( max_digits = 6, decimal_places = 2) class Statement(models.Model): by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+") checkout = models.ForeignKey( Checkout, on_delete = models.PROTECT, related_name = "statements") balance_old = models.DecimalField(max_digits = 6, decimal_places = 2) balance_new = models.DecimalField(max_digits = 6, decimal_places = 2) amount_taken = models.DecimalField(max_digits = 6, decimal_places = 2) amount_error = models.DecimalField(max_digits = 6, decimal_places = 2) at = models.DateTimeField(auto_now_add = True) class ArticleCategory(models.Model): name = models.CharField(max_length = 45) class Article(models.Model): name = models.CharField(max_length = 45) is_sold = models.BooleanField(default = True) price = models.DecimalField(max_digits = 6, decimal_places = 2) stock = models.IntegerField(default = 0) category = models.ForeignKey( ArticleCategory, on_delete = models.PROTECT, related_name = "articles") class ArticleRule(models.Model): article_on = models.OneToOneField( Article, on_delete = models.PROTECT, related_name = "rule_on") article_to = models.OneToOneField( Article, on_delete = models.PROTECT, related_name = "rule_to") ratio = models.PositiveSmallIntegerField() class Inventory(models.Model): articles = models.ManyToManyField( Article, through = 'InventoryArticle', related_name = "inventories") by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+") at = models.DateTimeField(auto_now_add = True) # Optional order = models.OneToOneField( 'Order', on_delete = models.PROTECT, related_name = "inventory", blank = True, null = True, default = None) class InventoryArticle(models.Model): inventory = models.ForeignKey( Inventory, on_delete = models.PROTECT) article = models.ForeignKey( Article, on_delete = models.PROTECT) stock_old = models.IntegerField() stock_new = models.IntegerField() stock_error = models.IntegerField(default = 0) class Supplier(models.Model): articles = models.ManyToManyField( Article, through = 'SupplierArticle', related_name = "suppliers") name = models.CharField(max_length = 45) address = models.TextField() email = models.EmailField() phone = models.CharField(max_length = 10) comment = models.TextField() class SupplierArticle(models.Model): supplier = models.ForeignKey( Supplier, on_delete = models.PROTECT) article = models.ForeignKey( Article, on_delete = models.PROTECT) BOX_TYPE_CHOICES = ( ("caisse", "Caisse"), ("carton", "Carton"), ("palette", "Palette"), ("fût", "Fût"), ) box_type = models.CharField( choices = BOX_TYPE_CHOICES, max_length = choices_length(BOX_TYPE_CHOICES)) box_capacity = models.PositiveSmallIntegerField() price_HT = models.DecimalField(max_digits = 7, decimal_places = 4) TVA = models.DecimalField(max_digits = 4, decimal_places = 2) rights = models.DecimalField(max_digits = 7, decimal_places = 4) class Order(models.Model): supplier = models.ForeignKey( Supplier, on_delete = models.PROTECT, related_name = "orders") articles = models.ManyToManyField( Article, through = "OrderArticle", related_name = "orders") at = models.DateTimeField(auto_now_add = True) amount = models.DecimalField(max_digits = 6, decimal_places = 2) class OrderArticle(models.Model): order = models.ForeignKey( Order, on_delete = models.PROTECT) article = models.ForeignKey( Article, on_delete = models.PROTECT) quantity_ordered = models.IntegerField() quantity_received = models.IntegerField() class TransferGroup(models.Model): at = models.DateTimeField(auto_now_add = True) # Optional comment = models.CharField( max_length = 255, blank = True, default = "") valid_by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+", blank = True, null = True, default = None) class Transfer(models.Model): group = models.ForeignKey( TransferGroup, on_delete = models.PROTECT, related_name = "transfers") from_acc = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "transfers_from") to_acc = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "transfers_to") amount = models.DecimalField(max_digits = 6, decimal_places = 2) # Optional canceled_by = models.ForeignKey( Account, on_delete = models.PROTECT, null = True, blank = True, default = None, related_name = "+") canceled_at = models.DateTimeField( null = True, blank = True, default = None) class OperationGroup(models.Model): on_acc = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "operations") checkout = models.ForeignKey( Checkout, on_delete = models.PROTECT, related_name = "operations") at = models.DateTimeField(auto_now_add = True) amount = models.IntegerField() is_cof = models.BooleanField(default = False) # Optional comment = models.CharField( max_length = 255, blank = True, default = "") valid_by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+", blank = True, null = True, default = True) class Operation(models.Model): PURCHASE = 'purchase' DEPOSIT = 'deposit' WITHDRAW = 'withdraw' TYPE_ORDER_CHOICES = ( (PURCHASE, 'Achat'), (DEPOSIT, 'Charge'), (WITHDRAW, 'Retrait'), ) group = models.ForeignKey( OperationGroup, on_delete = models.PROTECT, related_name = "+") type = models.CharField( choices = TYPE_ORDER_CHOICES, max_length = choices_length(TYPE_ORDER_CHOICES)) amount = models.DecimalField(max_digits = 6, decimal_places = 2) on_checkout = models.BooleanField(default = True) # Optional article = models.ForeignKey( Article, on_delete = models.PROTECT, related_name = "operations", blank = True, null = True, default = None) canceled_by = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "+", blank = True, null = True, default = None) canceled_at = models.DateTimeField( blank = True, null = True, default = None) addcost_for = models.ForeignKey( Account, on_delete = models.PROTECT, related_name = "addcosts", blank = True, null = True, default = None) addcost_amount = models.DecimalField(max_digits = 6, decimal_places = 2) class GlobalPermissions(models.Model): class Meta: managed = False permissions = ( ('is_team', 'Is part of the team'), )