You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
23 KiB
493 lines
23 KiB
# -*- coding: utf-8 -*-
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
|
|
from PIL import Image
|
|
from pytils import numeral
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from django.db import models
|
|
from django.conf import settings
|
|
from django.db.models import Max
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from . import consts, managers, utils
|
|
from project.myauth.models import DokUser
|
|
|
|
|
|
PROFILE_IMAGES_UPLOAD_DIR = 'customer/profile/' # куда сохранять загруженные изображения
|
|
BOSS_SIGN_IMG_SIZE = (100, 75)
|
|
GLAVBUH_SIGN_IMG_SIZE = (100, 75)
|
|
STAMP_IMG_SIZE = (180, 180)
|
|
LOGO_SIZE = (100, 50)
|
|
|
|
|
|
def get_profile(user):
|
|
"""Возвращает профиль пользователя или None."""
|
|
try:
|
|
return user.profile
|
|
except:
|
|
return None
|
|
|
|
|
|
def upload_to(path, new_filename=None):
|
|
"""Куда и под каким именем сохранить загруженный файл."""
|
|
def get_upload_path(instance, filename):
|
|
filename = new_filename or filename
|
|
try:
|
|
profile_dir = instance.get_first_user().username
|
|
except:
|
|
profile_dir = 'NoUser'
|
|
return os.path.join(path, profile_dir, filename)
|
|
return get_upload_path
|
|
|
|
|
|
class UserProfile(models.Model):
|
|
"""Профиль пользователя."""
|
|
profile_type = models.PositiveSmallIntegerField(u'Тип профиля', choices=consts.PROFILE_TYPES)
|
|
|
|
# общие поля
|
|
boss_surname = models.CharField(u'Фамилия', max_length=30, default='',
|
|
help_text=u'Используется для строки "подпись" в документах.')
|
|
boss_name = models.CharField(u'Имя', max_length=30, default='')
|
|
boss_midname = models.CharField(u'Отчество', max_length=30, default='')
|
|
|
|
inn = models.CharField(u'ИНН', max_length=12, default='') # длина: 10 для организаций, 12 для ИП
|
|
ogrn = models.CharField(u'ОГРН/ОГРНИП', max_length=15, default='') # длина: 13 для организаций, 15 для ИП
|
|
okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='') # длина: 8 для организаций, 8 или 10 для ИП
|
|
|
|
glavbuh_surname = models.CharField(u'Фамилия', max_length=30, blank=True, default='',
|
|
help_text=u'Используется для строки "подпись" в документах.')
|
|
glavbuh_name = models.CharField(u'Имя', max_length=30, blank=True, default='')
|
|
glavbuh_midname = models.CharField(u'Отчество', max_length=30, blank=True, default='')
|
|
|
|
address = models.CharField(u'Адрес для документов', max_length=256, default='',
|
|
help_text=u'Будет подставляться в создаваемые счета, акты и накладные.')
|
|
real_address = models.CharField(u'Фактический адрес', max_length=256, blank=True, default='',
|
|
help_text=u'Используется только для карточки компании.')
|
|
|
|
phone_code = models.CharField(u'Код города', max_length=10, blank=True, default='')
|
|
phone = models.CharField(u'Номер телефона', max_length=20, blank=True, default='')
|
|
|
|
fax_code = models.CharField(u'Код города', max_length=10, blank=True, default='')
|
|
fax = models.CharField(u'Номер телефона', max_length=20, blank=True, default='')
|
|
|
|
email = models.EmailField(u'Электронная почта', max_length=75, blank=True, default='')
|
|
site = models.CharField(u'Сайт', max_length=256, blank=True, default='')
|
|
|
|
# поля, только для ИП
|
|
svid_gos_reg = models.CharField(u'Свид-во о гос. регистрации', max_length=256, blank=True, default='',
|
|
help_text=u'Требуется для счет-фактуры.')
|
|
|
|
ip_reg_date = models.DateField(u'Дата регистрации ИП', blank=True, null=True)
|
|
|
|
# поля, только для Организации
|
|
name = models.CharField(u'Краткое название организации', max_length=256, default='',
|
|
help_text=u'Будет подставляться в создаваемые документы.')
|
|
full_name = models.CharField(u'Полное название организации', max_length=256, blank=True, default='',
|
|
help_text=u'Как в учредительных документах.')
|
|
|
|
kpp = models.CharField(u'КПП', max_length=9, default='')
|
|
|
|
jur_address = models.CharField(u'Юридический (почтовый) адрес', max_length=256, blank=True, default='',
|
|
help_text=u'Как в учредительных документах.')
|
|
|
|
boss_title = models.CharField(u'Должность руководителя', max_length=256, blank=True, default='')
|
|
na_osnovanii = models.CharField(u'Действует на основании', max_length=256, blank=True, default='')
|
|
|
|
# подписи, печать и логотип
|
|
boss_sign = models.ImageField(u'Подпись руководителя', blank=True, default='',
|
|
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'boss_sign.bmp'))
|
|
glavbuh_sign = models.ImageField(u'Подпись бухгалтера', blank=True, default='',
|
|
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'glavbuh_sign.bmp'))
|
|
stamp = models.ImageField(u'Печать', blank=True, default='',
|
|
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'stamp.bmp'))
|
|
logo = models.ImageField(u'Логотип', blank=True, default='',
|
|
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'logo.bmp'))
|
|
|
|
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
|
|
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
|
|
active = models.BooleanField(u'Активен', default=False)
|
|
confirmed = models.BooleanField(u'Подтверждён', default=False)
|
|
|
|
user_session_key = models.CharField(u'Ключ сессии (служебная информация)', max_length=256, blank=True, default='',
|
|
help_text=u'Руками не тро...')
|
|
|
|
objects = managers.UserProfileManager()
|
|
|
|
class Meta:
|
|
verbose_name = u'Реквизиты (профиль)'
|
|
verbose_name_plural = u'Реквизиты (профили)'
|
|
|
|
def __unicode__(self):
|
|
return u'%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or u'не указан')
|
|
|
|
def save(self, *args, **kwargs):
|
|
def process_img(orig_img, size):
|
|
w = orig_img.width
|
|
h = orig_img.height
|
|
if w > size[0] or h > size[1]:
|
|
filename = str(orig_img.path)
|
|
img = Image.open(filename).convert("RGB")
|
|
img.thumbnail(size, Image.ANTIALIAS)
|
|
img.save(filename, 'BMP')
|
|
|
|
super(UserProfile, self).save(*args, **kwargs)
|
|
|
|
if self.boss_sign:
|
|
process_img(self.boss_sign, size=BOSS_SIGN_IMG_SIZE)
|
|
|
|
if self.glavbuh_sign:
|
|
process_img(self.glavbuh_sign, size=GLAVBUH_SIGN_IMG_SIZE)
|
|
|
|
if self.stamp:
|
|
process_img(self.stamp, size=STAMP_IMG_SIZE)
|
|
|
|
if self.logo:
|
|
process_img(self.logo, size=LOGO_SIZE)
|
|
|
|
def is_ip(self):
|
|
return self.profile_type == consts.IP_PROFILE
|
|
|
|
def is_org(self):
|
|
return self.profile_type == consts.ORG_PROFILE
|
|
|
|
def check_name_not_filled(self):
|
|
"""`ИП ФИО` или `Название Организации`."""
|
|
if self.is_ip():
|
|
return self.get_boss_full_fio().strip() == ''
|
|
elif self.is_org():
|
|
return self.name.strip() == ''
|
|
return False
|
|
|
|
def get_main_bank_account(self):
|
|
try:
|
|
bank_accounts = BankAccount.objects.filter(company=self, is_main=True)[0]
|
|
except:
|
|
return None
|
|
|
|
def get_first_user(self):
|
|
try:
|
|
first_user = DokUser.objects.filter(profile=self)[0]
|
|
return first_user
|
|
except:
|
|
return None
|
|
|
|
def check_main_reqs_not_filled(self):
|
|
result = self.check_name_not_filled() or self.inn == '' or self.address == '' or \
|
|
self.get_boss_fio() == '' or self.get_main_bank_account() == ''
|
|
if result:
|
|
return True
|
|
|
|
if self.is_ip():
|
|
return self.ogrn == ''
|
|
elif self.is_org():
|
|
return self.kpp == ''
|
|
|
|
def get_company_name(self):
|
|
"""`ИП ФИО` или `Название Организации`."""
|
|
if self.profile_type == consts.IP_PROFILE:
|
|
return u'ИП %s' % self.get_boss_full_fio()
|
|
elif self.profile_type == consts.ORG_PROFILE:
|
|
return self.name.strip()
|
|
return u''
|
|
|
|
def get_inn_and_kpp(self):
|
|
"""Возвращает пару ИНН/КПП или только ИНН, если это ИП или КПП не заполнен."""
|
|
if self.profile_type == consts.ORG_PROFILE:
|
|
kpp = self.kpp.strip()
|
|
if kpp:
|
|
return u'%s/%s' % (self.inn, kpp,)
|
|
return self.inn
|
|
|
|
def get_boss_title(self):
|
|
"""Текст 'Индивидуальный предприниматель' или 'Руководитель организации'."""
|
|
if self.profile_type == consts.IP_PROFILE:
|
|
return u'Индивидуальный предприниматель'
|
|
elif self.profile_type == consts.ORG_PROFILE:
|
|
return u'Руководитель организации'
|
|
return u''
|
|
|
|
def get_boss_fio(self):
|
|
"""Фамилия и инициалы руководителя ИП/организации."""
|
|
if self.boss_surname and self.boss_name and self.boss_midname:
|
|
return u'%s %s.%s.' % (self.boss_surname, self.boss_name[0], self.boss_midname[0],)
|
|
return u''
|
|
|
|
def get_boss_full_fio(self):
|
|
"""Полное ФИО руководителя ИП/организации."""
|
|
return (u'%s %s %s' % (self.boss_surname, self.boss_name, self.boss_midname,)).strip()
|
|
|
|
def get_glavbuh_fio(self):
|
|
"""Фамилия и инициалы главного бухгалтера."""
|
|
if self.glavbuh_surname and self.glavbuh_name and self.glavbuh_midname:
|
|
return (u'%s %s. %s.' % (self.glavbuh_surname, self.glavbuh_name[0], self.glavbuh_midname[0],)).strip()
|
|
return u''
|
|
|
|
def get_glavbuh_full_fio(self):
|
|
"""Полное ФИО главного бухгалтера."""
|
|
return (u'%s %s %s' % (self.glavbuh_surname, self.glavbuh_name, self.glavbuh_midname,)).strip()
|
|
|
|
def get_full_phone(self):
|
|
"""(Код города) Номер телефона."""
|
|
phone_code = self.phone_code.strip('() ')
|
|
phone_code = u'(%s)' % phone_code if phone_code else phone_code
|
|
return (u'%s %s' % (phone_code, self.phone,)).strip()
|
|
|
|
def get_email(self):
|
|
try:
|
|
return self.get_first_user().email
|
|
except:
|
|
return None
|
|
|
|
def get_full_fax(self):
|
|
"""(Код города) Номер факса."""
|
|
fax_code = self.fax_code.strip('() ')
|
|
fax_code = u'(%s)' % fax_code if fax_code else fax_code
|
|
return (u'%s %s' % (fax_code, self.fax,)).strip()
|
|
|
|
|
|
class BankAccount(models.Model):
|
|
"""Расчетные счета."""
|
|
company = models.ForeignKey(UserProfile, related_name='bank_accounts')
|
|
|
|
bik = models.CharField(u'БИК', max_length=10)
|
|
name = models.CharField(u'Наименование банка', max_length=256)
|
|
address = models.CharField(u'Местонахождение', max_length=256)
|
|
korr_account = models.CharField(u'Корр. счет', max_length=20)
|
|
account = models.CharField(u'Расчетный счет', max_length=20)
|
|
|
|
is_main = models.BooleanField(u'Основной счет', default=False)
|
|
|
|
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
|
|
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
|
|
|
|
objects = managers.BankAccountManager()
|
|
|
|
class Meta:
|
|
verbose_name = u'Расчётный счет'
|
|
verbose_name_plural = u'Расчётные счета'
|
|
ordering = ['-created_at']
|
|
|
|
def __unicode__(self):
|
|
return (u'%s, %s' % (self.account, self.name[0:30],)).strip()
|
|
|
|
def save(self, *args, **kwargs):
|
|
super(BankAccount, self).save(*args, **kwargs)
|
|
company = self.company
|
|
if self.is_main:
|
|
# если задано, что это будет основной счет, то сбросить у остальных счетов пользователя этот признак
|
|
BankAccount.objects.filter(company=self.company, is_main=True).exclude(pk=self.pk).update(is_main=False)
|
|
else:
|
|
# если нет основного счета, то установить его принудительно
|
|
BankAccount.objects.force_main(company=self.company)
|
|
|
|
def delete(self, *args, **kwargs):
|
|
super(BankAccount, self).delete(*args, **kwargs)
|
|
# если нет основного счета, то установить его принудительно
|
|
BankAccount.objects.force_main(company=self.company)
|
|
|
|
|
|
class Client(models.Model):
|
|
"""Контрагенты."""
|
|
company = models.ForeignKey(UserProfile, related_name='clients')
|
|
|
|
name = models.CharField(u'Наименование', max_length=256, db_index=True)
|
|
inn = models.CharField(u'ИНН', max_length=12)
|
|
kpp = models.CharField(u'КПП', max_length=9, blank=True, default='') # Организация
|
|
okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='') # ИП
|
|
address = models.CharField(u'Юр. адрес', max_length=256)
|
|
|
|
# банковские реквизиты
|
|
bank_bik = models.CharField(u'БИК', max_length=10, blank=True, default='')
|
|
bank_name = models.CharField(u'Наименование банка', max_length=256, blank=True, default='')
|
|
bank_address = models.CharField(u'Местонахождение', max_length=256, blank=True, default='')
|
|
bank_korr_account = models.CharField(u'Корр. счет', max_length=20, blank=True, default='')
|
|
bank_account = models.CharField(u'Расчетный счет', max_length=20, blank=True, default='')
|
|
|
|
# контакты
|
|
contact_name = models.CharField(u'Имя', max_length=50, blank=True, default='')
|
|
contact_email = models.EmailField(u'E-mail', max_length=50, blank=True, default='')
|
|
contact_phone = models.CharField(u'Телефон', max_length=50, blank=True, default='')
|
|
contact_icq = models.CharField(u'ICQ', max_length=20, blank=True, default='')
|
|
contact_skype = models.CharField(u'Skype', max_length=20, blank=True, default='')
|
|
contact_other = models.CharField(u'Другое', max_length=256, blank=True, default='')
|
|
|
|
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
|
|
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
|
|
|
|
objects = managers.ClientManager()
|
|
|
|
class Meta:
|
|
verbose_name = u'Контрагент'
|
|
verbose_name_plural = u'Контрагенты'
|
|
ordering = ['name', '-created_at']
|
|
|
|
def __unicode__(self):
|
|
return (u'%s, ИНН %s' % (self.name[0:30], self.inn or u'не указан',)).strip()
|
|
|
|
def get_inn_and_kpp(self):
|
|
"""Возвращает пару ИНН/КПП или только ИНН, если КПП не заполнен."""
|
|
kpp = self.kpp.strip()
|
|
if kpp:
|
|
return u'%s/%s' % (self.inn, kpp,)
|
|
return self.inn
|
|
|
|
|
|
class UserProfileFilters(models.Model):
|
|
"""Фильтрация реквизитов: какие данные показывать/скрывать при генерации карточки компании."""
|
|
company = models.OneToOneField(UserProfile, related_name='profile_filters', primary_key=True)
|
|
|
|
# общие фильтры
|
|
show_profile_type = models.BooleanField(u'Тип профиля', default=True)
|
|
|
|
show_inn = models.BooleanField(u'ИНН', default=True)
|
|
show_ogrn = models.BooleanField(u'ОГРН/ОГРНИП', default=True)
|
|
show_okpo = models.BooleanField(u'ОКПО', default=True)
|
|
|
|
show_glavbuh = models.BooleanField(u'Главный бухгалтер', default=True)
|
|
|
|
show_bank_account = models.BooleanField(u'Банковские реквизиты', default=True)
|
|
bank_account = models.ForeignKey(BankAccount, related_name='+', verbose_name=u'Расчетный счет', blank=True,
|
|
null=True, default=None)
|
|
|
|
show_contact_info = models.BooleanField(u'Контактная информация', default=True)
|
|
show_real_address = models.BooleanField(u'Фактический адрес', default=True)
|
|
show_phone = models.BooleanField(u'Телефон', default=True)
|
|
show_fax = models.BooleanField(u'Факс', default=True)
|
|
show_email = models.BooleanField(u'Электронная почта', default=True)
|
|
show_site = models.BooleanField(u'Сайт', default=True)
|
|
|
|
show_logo = models.BooleanField(u'Логотип', default=True)
|
|
|
|
# только для ИП
|
|
show_ip_boss_fio = models.BooleanField(u'Фамилия, Имя, Отчество', default=True)
|
|
show_svid_gos_reg = models.BooleanField(u'Свид-во о гос. регистрации', default=True)
|
|
show_ip_reg_date = models.BooleanField(u'Дата регистрации ИП', default=True)
|
|
|
|
# только для Организации
|
|
show_name = models.BooleanField(u'Краткое название организации', default=True)
|
|
show_full_name = models.BooleanField(u'Полное название организации', default=True)
|
|
show_kpp = models.BooleanField(u'КПП', default=True)
|
|
show_org_boss_title_and_fio = models.BooleanField(u'Руководитель (Должность, ФИО)', default=True)
|
|
show_na_osnovanii = models.BooleanField(u'Действует на основании', default=True)
|
|
show_jur_address = models.BooleanField(u'Юридический адрес', default=True)
|
|
|
|
objects = managers.UserProfileFiltersManager()
|
|
|
|
class Meta:
|
|
verbose_name = u'Фильтры реквизитов'
|
|
verbose_name_plural = u'Фильтры реквизитов'
|
|
|
|
def __unicode__(self):
|
|
return u'%s' % self.user.email
|
|
|
|
def save(self, *args, **kwargs):
|
|
# всегда включены
|
|
self.show_ip_boss_fio = True
|
|
self.show_name = True
|
|
super(UserProfileFilters, self).save(*args, **kwargs)
|
|
|
|
|
|
class License(models.Model):
|
|
company = models.ForeignKey(UserProfile, related_name='licenses', verbose_name=u'пользователь')
|
|
term = models.IntegerField(verbose_name=u'срок лицензии')
|
|
date_from = models.DateField(u'дата начала', null=True, blank=True)
|
|
date_to = models.DateField(u'дата окончания', null=True, blank=True)
|
|
payform = models.IntegerField(verbose_name=u'форма оплаты',
|
|
choices=consts.PAYFORMS, default=0)
|
|
status = models.IntegerField(verbose_name=u'статус лицензии',
|
|
choices=consts.LICENSE_STATUSES, default=0)
|
|
order_date = models.DateField(verbose_name=u'дата заказа', auto_now_add=True)
|
|
paid_date = models.DateField(verbose_name=u'дата оплаты', null=True, blank=True)
|
|
pay_sum= models.IntegerField(verbose_name=u'сумма оплаты')
|
|
deleted = models.BooleanField(u'удалено', default=False)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(License, self).__init__(*args, **kwargs)
|
|
self.__prev_date = self.paid_date
|
|
|
|
def __unicode__(self):
|
|
return u'%s - %s %s (%d %s)' % (
|
|
self.company.get_company_name(),
|
|
self.term,
|
|
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
|
|
self.pay_sum,
|
|
numeral.choose_plural(self.pay_sum, u"рубль, рубля, рублей"),
|
|
)
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.__prev_date and self.paid_date:
|
|
max_date_license = License.objects.filter(company=self.company).aggregate(Max('date_to'))['date_to__max']
|
|
today = datetime.now().date()
|
|
if max_date_license < today:
|
|
max_date_license = today - timedelta(1)
|
|
self.date_from = max_date_license + relativedelta(days=1)
|
|
self.date_to = self.date_from + relativedelta(months=self.term, days=-1)
|
|
self.company.active = True
|
|
self.company.save()
|
|
self.status = 1
|
|
utils.check_one_profile(self.company, License, datetime.now(), manual=True)
|
|
|
|
super(License, self).save(*args, **kwargs)
|
|
|
|
def get_company(self):
|
|
return self.company.get_company_name()
|
|
|
|
def get_action_link(self):
|
|
|
|
if self.status == 0:
|
|
if self.payform == 0:
|
|
return u'<a href="%s">Скачать счёт</a>' % reverse('customer_license_get_doc', kwargs={'order_num': self.id})
|
|
elif self.payform == 1:
|
|
return u'Оплатить счёт'
|
|
elif self.payform == 2:
|
|
return u'<a href="%s">Скачать квитанцию</a>' % reverse('customer_license_get_doc', kwargs={'order_num': self.id})
|
|
elif self.status in [1, 2]:
|
|
return u'История операций'
|
|
else:
|
|
return ''
|
|
|
|
def get_term(self):
|
|
if self.term == 0:
|
|
return u'45 дней'
|
|
else:
|
|
return u'%s %s' % (self.term,
|
|
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
|
|
)
|
|
|
|
def get_paid_status(self):
|
|
if self.status == 1:
|
|
return u'Лицензия оплачена, ещё не активирована'
|
|
elif self.status in [2, -1]:
|
|
left = relativedelta(self.date_to, datetime.today())
|
|
if left.months:
|
|
left_str = '%d %s %d %s' % (left.months,
|
|
numeral.choose_plural(left.months, u"месяц, месяца, месяцев"),
|
|
left.days,
|
|
numeral.choose_plural(left.days, u"день, дня, дней"),
|
|
)
|
|
else:
|
|
left_str = '%d %s' % (
|
|
left.days,
|
|
numeral.choose_plural(left.days, u"день, дня, дней"),
|
|
)
|
|
return u'Лицензия активирована: осталось %s' % left_str
|
|
elif self.status == 3:
|
|
return u'Время истекло'
|
|
else:
|
|
return None
|
|
|
|
|
|
class LicensePrice(models.Model):
|
|
term = models.IntegerField(verbose_name=u'срок лицензии',
|
|
choices=consts.TERMS)
|
|
price = models.IntegerField(verbose_name=u'сумма оплаты')
|
|
|
|
def __unicode__(self):
|
|
return u'%s %s (%d %s)' % (self.term,
|
|
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
|
|
self.price,
|
|
numeral.choose_plural(self.price, u"рубль, рубля, рублей"),
|
|
)
|
|
|
|
|