kpsul/kfet/models.py
Aurélien Delobelle 11452d7633 Ajout des majorations (type concert)
- Ajout des paramètres (modèle Settings) "ADDCOST_AMOUNT" et
  "ADDCOST_FOR" indiquant respectivement le montant et le compte sur lequel
  compter la majoration. Définir l'un de ces paramètres à NULL indique
  qu'il n'y a pas de majoration en cours
- Prise en compte de ces 2 paramètres lors de la validation et
  l'enregistrement d'opérations d'achat (Operation.PURCHASE) dans K-Psul
- Modification du champ "addcost_amount" de Operation. S'il n'y a pas de
  majoration, celui-ci est NULL.
- Correction sur l'enregistrement de "valid_by" dans K-Psul. Celui-ci
  était systématiquement rempli par l'utilisateur connecté ce qui
  n'était pas le comportement souhaité. Il est maintenant rempli
  seulement si une permission autre que kfet.is_team était nécessaire
  pour valider la commande.
- Suppression d'une exception non utilisée dans le modèle Settings
2016-08-08 02:50:04 +02:00

465 lines
16 KiB
Python

from django.db import models
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ValidationError
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}$')],
db_index = True)
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
def perms_to_perform_operation(self, amount):
new_balance = self.balance + amount
perms = []
if new_balance < 0:
perms.append('kfet.can_perform_negative_operations')
return perms
# 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.DecimalField(
max_digits = 6, decimal_places = 2,
default = 0)
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 = None)
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,
blank = True, default = 0)
is_checkout = models.BooleanField(default = True)
# Optional
article = models.ForeignKey(
Article, on_delete = models.PROTECT,
related_name = "operations",
blank = True, null = True, default = None)
article_nb = models.PositiveSmallIntegerField(
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,
blank = True, null = True, default = None)
class GlobalPermissions(models.Model):
class Meta:
managed = False
permissions = (
('is_team', 'Is part of the team'),
('can_perform_deposit', 'Peut effectuer une charge'),
('can_perform_negative_operations',
'Peut enregistrer des commandes en négatif')
)
class Settings(models.Model):
name = models.CharField(
max_length = 45,
unique = True)
value_decimal = models.DecimalField(
max_digits = 6, decimal_places = 2,
blank = True, null = True, default = None)
value_account = models.ForeignKey(
Account, on_delete = models.PROTECT,
blank = True, null = True, default = None)
@staticmethod
def setting_inst(name):
return Settings.objects.get(name=name)
@staticmethod
def SUBVENTION_COF():
try:
return Settings.setting_inst("SUBVENTION_COF").value_decimal
except Settings.DoesNotExist:
return 0
@staticmethod
def ADDCOST_AMOUNT():
try:
return Settings.setting_inst("ADDCOST_AMOUNT").value_decimal
except Settings.DoesNotExist:
return 0
@staticmethod
def ADDCOST_FOR():
try:
return Settings.setting_inst("ADDCOST_FOR").value_account
except Settings.DoesNotExist:
return None;