from django.db import models from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied 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) is_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) def __str__(self): return self.name 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, default = 0) stock = models.IntegerField(default = 0) category = models.ForeignKey( ArticleCategory, on_delete = models.PROTECT, related_name = "articles") def __str__(self): return '%s - %s' % (self.category.name, self.name) def get_absolute_url(self): return reverse('kfet.article.read', kwargs={'pk': self.pk}) 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'), )