forked from DGNum/gestioCOF
fdb105ab84
- Ajout d'une exception Checkout.IsProtected - Rend impossible d'utiliser une caisse (Checkout) protégée (is_protected) dans K-Psul Une caisse protégée est, par exemple, la caisse qui recevra les rechargements par CB en ligne.
419 lines
14 KiB
Python
419 lines
14 KiB
Python
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}$')],
|
|
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
|
|
|
|
# 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']
|
|
|
|
class IsProtected(Exception):
|
|
pass
|
|
|
|
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)
|
|
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,
|
|
default = 0)
|
|
|
|
class GlobalPermissions(models.Model):
|
|
class Meta:
|
|
managed = False
|
|
permissions = (
|
|
('is_team', 'Is part of the team'),
|
|
)
|