diff --git a/src/customer/admin.py b/src/customer/admin.py
index 99ecb6a..8a4f5ef 100644
--- a/src/customer/admin.py
+++ b/src/customer/admin.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
+from django.utils import timezone
from customer import forms
from customer import models
@@ -12,12 +13,45 @@ class UserProfileAdmin(admin.ModelAdmin):
class LicenseAdmin(admin.ModelAdmin):
- list_display = ('get_company', 'term', 'status', 'order_date', 'date_from', 'date_to')
+ list_display = (
+ 'user_email',
+ 'license_owner',
+ 'term_display',
+ 'status',
+ 'order_date',
+ 'date_from',
+ 'date_to',
+ 'balance_days',
+ )
list_display_links = list_display
search_fields = ('company__email',)
list_filter = ('status', 'term', 'order_date', 'date_from', 'date_to')
+ def user_email(self, obj):
+ if obj.company.users.first():
+ return obj.company.users.first().email
+ else:
+ return 'Нет пользователя'
+
+ user_email.short_description = 'Email пользователя'
+
+ def term_display(self, obj):
+ return obj.get_term()
+
+ term_display.short_description = 'Срок лицензии'
+
+ def license_owner(self, obj):
+ return obj.get_company()
+
+ license_owner.short_description = 'Компания'
+
+ def balance_days(self, obj):
+ delta = obj.date_to - timezone.now().date()
+ return delta.days
+
+ balance_days.short_description = 'Остаток дней'
+
class BankAccountAdmin(admin.ModelAdmin):
class Media:
diff --git a/src/customer/consts.py b/src/customer/consts.py
index 6de6261..7b58d5a 100644
--- a/src/customer/consts.py
+++ b/src/customer/consts.py
@@ -4,28 +4,28 @@ IP_PROFILE = 1
ORG_PROFILE = 2
PROFILE_TYPES = (
- (IP_PROFILE, u'Индивидуальный предприниматель'),
- (ORG_PROFILE, u'Организация'),
+ (IP_PROFILE, 'Индивидуальный предприниматель'),
+ (ORG_PROFILE, 'Организация'),
)
LICENSE_STATUSES = (
- (-1, u'Пробный период'),
- (0, u'Не оплачен'),
- (1, u'Оплачен'),
- (2, u'Активирован'),
- (3, u'Срок действия истёк'),
- (4, u'Заморожен'),
+ (-1, 'Пробный период'),
+ (0, 'Не оплачен'),
+ (1, 'Оплачен'),
+ (2, 'Активирован'),
+ (3, 'Срок действия истёк'),
+ (4, 'Заморожен'),
)
PAYFORMS = (
- (-1, u'Бесплатно'),
- (0, u'Безналичный расчёт'),
- (1, u'Банковская карта'),
+ (-1, 'Бесплатно'),
+ (0, 'Безналичный расчёт'),
+ (1, 'Банковская карта'),
)
TERMS = (
- (1, u'1 месяц'),
- (6, u'6 месяцев'),
- (12, u'12 месяцев'),
- (24, u'24 месяца'),
+ (1, '1 месяц'),
+ (6, '6 месяцев'),
+ (12, '12 месяцев'),
+ (24, '24 месяца'),
)
diff --git a/src/customer/emails.py b/src/customer/emails.py
new file mode 100644
index 0000000..b4659a3
--- /dev/null
+++ b/src/customer/emails.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from celery import task
+
+from django.conf import settings
+from django.core.mail import EmailMessage
+from django.template.loader import render_to_string
+
+from commons.utils import get_site_url
+
+
+@task
+def send_offer_buy_licence_for_get_bonus(user_email, url):
+ template_name = 'emails/offer_big_period_purchased_license_bonus.txt'
+ subject = 'Документор: Получите бонус'
+ dict_context = {'user_email': user_email,
+ 'support_email': settings.SUPPORT_EMAIL,
+ 'url': url,
+ 'site_url': get_site_url()
+ }
+ email_body = render_to_string(template_name, dict_context)
+ email = EmailMessage(subject=subject, to=(user_email,), body=email_body)
+ return email.send()
diff --git a/src/customer/models.py b/src/customer/models.py
index f792a5d..a461ab4 100644
--- a/src/customer/models.py
+++ b/src/customer/models.py
@@ -50,25 +50,25 @@ class UploadAndRename(object):
class UserProfile(models.Model):
"""Профиль пользователя."""
- profile_type = models.PositiveSmallIntegerField(u'Тип профиля', choices=consts.PROFILE_TYPES)
+ profile_type = models.PositiveSmallIntegerField('Тип профиля', 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='')
+ boss_surname = models.CharField('Фамилия', max_length=30, default='',
+ help_text='Используется для строки "подпись" в документах.')
+ boss_name = models.CharField('Имя', max_length=30, default='')
+ boss_midname = models.CharField('Отчество', max_length=30, default='')
# длина: 10 для организаций, 12 для ИП
- inn = models.CharField(u'ИНН', max_length=12, default='')
+ inn = models.CharField('ИНН', max_length=12, default='')
# длина: 13 для организаций, 15 для ИП
- ogrn = models.CharField(u'ОГРН/ОГРНИП', max_length=15, default='')
+ ogrn = models.CharField('ОГРН/ОГРНИП', max_length=15, default='')
# длина: 8 для организаций, 8 или 10 для ИП
- okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='')
+ okpo = models.CharField('ОКПО', max_length=10, blank=True, default='')
glavbuh_surname = models.CharField('Фамилия', max_length=30, blank=True, default='',
help_text='Используется для строки "подпись" в документах.')
- glavbuh_name = models.CharField(u'Имя', max_length=30, blank=True, default='')
- glavbuh_midname = models.CharField(u'Отчество', max_length=30, blank=True, default='')
+ glavbuh_name = models.CharField('Имя', max_length=30, blank=True, default='')
+ glavbuh_midname = models.CharField('Отчество', max_length=30, blank=True, default='')
address = models.CharField(
'Фактический адрес',
@@ -77,17 +77,17 @@ class UserProfile(models.Model):
help_text='Будет подставляться в создаваемые счета, акты и накладные.'
)
- jur_address = models.CharField(u'Юридический адрес', max_length=256, blank=True, default='',
- help_text=u'Как в учредительных документах.')
+ jur_address = models.CharField('Юридический адрес', max_length=256, blank=True, default='',
+ help_text='Как в учредительных документах.')
- real_address = models.CharField(u'Почтовый адрес', max_length=256, blank=True, default='',
- help_text=u'Используется только для карточки компании.')
+ real_address = models.CharField('Почтовый адрес', max_length=256, blank=True, default='',
+ help_text='Используется только для карточки компании.')
- phone_code = models.CharField(u'Код города', max_length=10, blank=True, default='')
- phone = models.CharField(u'Номер телефона', max_length=20, blank=True, default='')
+ phone_code = models.CharField('Код города', max_length=10, blank=True, default='')
+ phone = models.CharField('Номер телефона', 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='')
+ fax_code = models.CharField('Код города', max_length=10, blank=True, default='')
+ fax = models.CharField('Номер телефона', max_length=20, blank=True, default='')
email = models.EmailField(
'Электронная почта',
@@ -108,36 +108,36 @@ class UserProfile(models.Model):
max_length=256,
blank=True,
default='',
- help_text=u'Требуется для счет-фактуры.'
+ help_text='Требуется для счет-фактуры.'
)
- ip_reg_date = models.DateField(u'Дата регистрации ИП', blank=True, null=True)
+ ip_reg_date = models.DateField('Дата регистрации ИП', blank=True, null=True)
# поля, только для Организации
name = models.CharField(
'Краткое название организации',
max_length=256,
default='',
- help_text=u'Будет подставляться в создаваемые документы.'
+ help_text='Будет подставляться в создаваемые документы.'
)
full_name = models.CharField(
'Полное название организации',
max_length=256,
blank=True,
default='',
- help_text=u'Как в учредительных документах.'
+ help_text='Как в учредительных документах.'
)
- kpp = models.CharField(u'КПП', max_length=9, default='')
+ kpp = models.CharField('КПП', max_length=9, default='')
boss_title = models.CharField(
- u'Должность руководителя',
+ 'Должность руководителя',
max_length=256,
blank=True,
default=''
)
na_osnovanii = models.CharField(
- u'Действует на основании',
+ 'Действует на основании',
max_length=256,
blank=True,
default=''
@@ -151,13 +151,13 @@ class UserProfile(models.Model):
upload_to=UploadAndRename(PROFILE_IMAGES_UPLOAD_DIR, 'boss_sign.png')
)
glavbuh_sign = models.ImageField(
- u'Подпись бухгалтера',
+ 'Подпись бухгалтера',
blank=True,
default='',
upload_to=UploadAndRename(PROFILE_IMAGES_UPLOAD_DIR, 'glavbuh_sign.png')
)
stamp = models.ImageField(
- u'Печать',
+ 'Печать',
blank=True,
default='',
upload_to=UploadAndRename(PROFILE_IMAGES_UPLOAD_DIR, 'stamp.png')
@@ -169,30 +169,30 @@ class UserProfile(models.Model):
upload_to=UploadAndRename(PROFILE_IMAGES_UPLOAD_DIR, 'logo.png')
)
- 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)
+ created_at = models.DateTimeField('Создан', auto_now_add=True)
+ updated_at = models.DateTimeField('Изменен', auto_now=True)
+ active = models.BooleanField('Активен', default=False)
+ confirmed = models.BooleanField('Подтверждён', default=False)
user_session_key = models.CharField(
- u'Ключ сессии (служебная информация)',
+ 'Ключ сессии (служебная информация)',
max_length=256,
blank=True,
default='',
- help_text=u'Руками не трогать...'
+ help_text='Руками не трогать...'
)
objects = managers.UserProfileManager()
class Meta:
- verbose_name = u'Реквизиты (профиль)'
- verbose_name_plural = u'Реквизиты (профили)'
+ verbose_name = 'Реквизиты (профиль)'
+ verbose_name_plural = 'Реквизиты (профили)'
def __unicode__(self):
- return u'%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or u'не указан')
+ return '%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or 'не указан')
def __str__(self):
- return u'%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or u'не указан')
+ return '%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or 'не указан')
def save(self, *args, **kwargs):
self.inn = only_numerics(self.inn)
@@ -268,54 +268,52 @@ class UserProfile(models.Model):
`ИП ФИО` или `Название Организации`.
"""
if self.profile_type == consts.IP_PROFILE:
- return u'ИП %s' % self.get_boss_full_fio()
+ return 'ИП %s' % self.get_boss_full_fio()
elif self.profile_type == consts.ORG_PROFILE:
return self.name.strip()
- return u''
+ return ''
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 '%s/%s' % (self.inn, kpp,)
return self.inn
def get_boss_title(self):
"""Текст 'Индивидуальный предприниматель' или 'Руководитель организации'."""
if self.profile_type == consts.IP_PROFILE:
- return u'Индивидуальный предприниматель'
+ return 'Индивидуальный предприниматель'
elif self.profile_type == consts.ORG_PROFILE:
- return u'Руководитель организации'
- return u''
+ return 'Руководитель организации'
+ return ''
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''
+ return '%s %s.%s.' % (self.boss_surname, self.boss_name[0], self.boss_midname[0],)
+ return ''
def get_boss_full_fio(self):
"""Полное ФИО руководителя ИП/организации."""
- return (u'%s %s %s' % (self.boss_surname, self.boss_name, self.boss_midname,)).strip()
+ return ('%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''
+ return f'{self.glavbuh_surname} {self.glavbuh_name[0]}. {self.glavbuh_midname[0]}.'
+ return ''
def get_glavbuh_full_fio(self):
"""Полное ФИО главного бухгалтера."""
- return (u'%s %s %s' % (self.glavbuh_surname, self.glavbuh_name,
- self.glavbuh_midname,)).strip()
+ return f'{self.glavbuh_surname} {self.glavbuh_name} {self.glavbuh_midname}'
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()
+ phone_code = f'({phone_code})'
+ return f'{phone_code} {self.phone}'
def get_email(self):
try:
@@ -326,8 +324,8 @@ class UserProfile(models.Model):
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()
+ fax_code = '(%s)' % fax_code if fax_code else fax_code
+ return ('%s %s' % (fax_code, self.fax,)).strip()
def validate_has_profile_account(self):
"""
@@ -344,34 +342,34 @@ 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)
+ bik = models.CharField('БИК', max_length=10)
+ name = models.CharField('Наименование банка', max_length=256)
short_name = models.CharField(
'Сокращенное название банка',
max_length=100,
blank=True,
default=''
)
- korr_account = models.CharField(u'Корр. счет', max_length=20)
- account = models.CharField(u'Расчетный счет', max_length=20)
+ korr_account = models.CharField('Корр. счет', max_length=20)
+ account = models.CharField('Расчетный счет', max_length=20)
- is_main = models.BooleanField(u'Основной счет', default=False)
+ is_main = models.BooleanField('Основной счет', default=False)
- created_at = models.DateTimeField(u'Создан', auto_now_add=True)
- updated_at = models.DateTimeField(u'Изменен', auto_now=True)
+ created_at = models.DateTimeField('Создан', auto_now_add=True)
+ updated_at = models.DateTimeField('Изменен', auto_now=True)
objects = managers.BankAccountManager()
class Meta:
- verbose_name = u'Расчётный счет'
- verbose_name_plural = u'Расчётные счета'
+ verbose_name = 'Расчётный счет'
+ verbose_name_plural = 'Расчётные счета'
ordering = ['-created_at']
def __unicode__(self):
- return (u'%s, %s' % (self.account, self.short_name[0:30] or self.name[0:30],)).strip()
+ return ('%s, %s' % (self.account, self.short_name[0:30] or self.name[0:30],)).strip()
def __str__(self):
- return (u'%s, %s' % (self.account, self.short_name[0:30] or self.name[0:30],)).strip()
+ return ('%s, %s' % (self.account, self.short_name[0:30] or self.name[0:30],)).strip()
def save(self, *args, **kwargs):
self.bik = only_numerics(self.bik)
@@ -399,7 +397,7 @@ class Client(models.Model):
"""Контрагенты."""
company = models.ForeignKey(UserProfile, related_name='clients')
- name = models.CharField(u'Наименование', max_length=256, db_index=True)
+ name = models.CharField('Наименование', max_length=256, db_index=True)
name_short_self = models.CharField(
'Короткое наименование',
@@ -414,52 +412,52 @@ class Client(models.Model):
blank=True
)
- inn = models.CharField(u'ИНН', max_length=12)
- kpp = models.CharField(u'КПП', max_length=9, blank=True, default='') # Организация
- ogrn = models.CharField(u'ОГРН', max_length=15, default='')
- okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='') # ИП
- address = models.CharField(u'Юр. адрес', max_length=256)
+ inn = models.CharField('ИНН', max_length=12)
+ kpp = models.CharField('КПП', max_length=9, blank=True, default='') # Организация
+ ogrn = models.CharField('ОГРН', max_length=15, default='')
+ okpo = models.CharField('ОКПО', max_length=10, blank=True, default='') # ИП
+ address = models.CharField('Юр. адрес', 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_bik = models.CharField('БИК', max_length=10, blank=True, default='')
+ bank_name = models.CharField('Наименование банка', max_length=256, blank=True, default='')
bank_short_name = models.CharField(
'Сокращенное наименование банка',
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='')
+ bank_korr_account = models.CharField('Корр. счет', max_length=20, blank=True, default='')
+ bank_account = models.CharField('Расчетный счет', 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_skype = models.CharField(u'Skype', max_length=20, blank=True, default='')
- contact_other = models.CharField(u'Другое', max_length=256, blank=True, default='')
+ contact_name = models.CharField('Имя', max_length=50, blank=True, default='')
+ contact_email = models.EmailField('E-mail', max_length=50, blank=True, default='')
+ contact_phone = models.CharField('Телефон', max_length=50, blank=True, default='')
+ contact_skype = models.CharField('Skype', max_length=20, blank=True, default='')
+ contact_other = models.CharField('Другое', max_length=256, blank=True, default='')
- created_at = models.DateTimeField(u'Создан', auto_now_add=True)
- updated_at = models.DateTimeField(u'Изменен', auto_now=True)
+ created_at = models.DateTimeField('Создан', auto_now_add=True)
+ updated_at = models.DateTimeField('Изменен', auto_now=True)
objects = managers.ClientManager()
class Meta:
- verbose_name = u'Контрагент'
- verbose_name_plural = u'Контрагенты'
+ verbose_name = 'Контрагент'
+ verbose_name_plural = 'Контрагенты'
ordering = ['name', '-created_at']
def __unicode__(self):
if self.name_short_self:
- return (u'%s, %s' % (self.name_short_dadata, self.name_short_self)).strip()
+ return ('%s, %s' % (self.name_short_dadata, self.name_short_self)).strip()
else:
- return (u'%s, ИНН %s' % (self.name, self.inn or u'не указан',)).strip()
+ return ('%s, ИНН %s' % (self.name, self.inn or 'не указан',)).strip()
def __str__(self):
if self.name_short_self:
- return (u'%s, %s' % (self.name_short_dadata, self.name_short_self)).strip()
+ return ('%s, %s' % (self.name_short_dadata, self.name_short_self)).strip()
else:
- return (u'%s, ИНН %s' % (self.name, self.inn or u'не указан',)).strip()
+ return ('%s, ИНН %s' % (self.name, self.inn or 'не указан',)).strip()
def save(self, *args, **kwargs):
self.inn = only_numerics(self.inn)
@@ -475,7 +473,7 @@ class Client(models.Model):
"""Возвращает пару ИНН/КПП или только ИНН, если КПП не заполнен."""
kpp = self.kpp.strip()
if kpp:
- return u'%s/%s' % (self.inn, kpp,)
+ return '%s/%s' % (self.inn, kpp,)
return self.inn
@@ -486,47 +484,47 @@ class UserProfileFilters(models.Model):
company = models.OneToOneField(UserProfile, related_name='profile_filters', primary_key=True)
# общие фильтры
- show_profile_type = models.BooleanField(u'Тип профиля', default=True)
+ show_profile_type = models.BooleanField('Тип профиля', default=True)
- show_inn = models.BooleanField(u'ИНН', default=True)
- show_ogrn = models.BooleanField(u'ОГРН/ОГРНИП', default=True)
- show_okpo = models.BooleanField(u'ОКПО', default=True)
+ show_inn = models.BooleanField('ИНН', default=True)
+ show_ogrn = models.BooleanField('ОГРН/ОГРНИП', default=True)
+ show_okpo = models.BooleanField('ОКПО', default=True)
- show_glavbuh = models.BooleanField(u'Главный бухгалтер', default=True)
+ show_glavbuh = models.BooleanField('Главный бухгалтер', default=True)
- show_bank_account = models.BooleanField(u'Банковские реквизиты', default=True)
+ show_bank_account = models.BooleanField('Банковские реквизиты', default=True)
bank_account = models.ForeignKey(
BankAccount,
related_name='+',
- verbose_name=u'Расчетный счет',
+ verbose_name='Расчетный счет',
blank=True,
null=True,
default=None
)
- show_contact_info = models.BooleanField(u'Контактная информация', default=True)
- show_address = models.BooleanField(u'Фактический адрес', default=True)
- show_jur_address = 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_contact_info = models.BooleanField('Контактная информация', default=True)
+ show_address = models.BooleanField('Фактический адрес', default=True)
+ show_jur_address = models.BooleanField('Юридический адрес', default=True)
+ show_real_address = models.BooleanField('Почтовый адрес', default=True)
+ show_phone = models.BooleanField('Телефон', default=True)
+ show_fax = models.BooleanField('Факс', default=True)
+ show_email = models.BooleanField('Электронная почта', default=True)
+ show_site = models.BooleanField('Сайт', default=True)
- show_logo = models.BooleanField(u'Логотип', default=True)
+ show_logo = models.BooleanField('Логотип', 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_ip_boss_fio = models.BooleanField('Фамилия, Имя, Отчество', default=True)
+ show_svid_gos_reg = models.BooleanField('Свид-во о гос. регистрации', default=True)
+ show_ip_reg_date = models.BooleanField('Дата регистрации ИП', 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_name = models.BooleanField('Краткое название организации', default=True)
+ show_full_name = models.BooleanField('Полное название организации', default=True)
+ show_kpp = models.BooleanField('КПП', default=True)
show_org_boss_title_and_fio = models.BooleanField(
'Руководитель (Должность, ФИО)', default=True)
- show_na_osnovanii = models.BooleanField(u'Действует на основании', default=True)
+ show_na_osnovanii = models.BooleanField('Действует на основании', default=True)
objects = managers.UserProfileFiltersManager()
@@ -550,29 +548,33 @@ class License(models.Model):
related_name='licenses',
verbose_name='пользователь'
)
- 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'форма оплаты',
+ term = models.IntegerField(verbose_name='срок лицензии')
+ date_from = models.DateField('дата начала', null=True, blank=True)
+ date_to = models.DateField('дата окончания', null=True, blank=True)
+ payform = models.IntegerField(verbose_name='форма оплаты',
choices=consts.PAYFORMS, default=0)
- status = models.IntegerField(verbose_name=u'статус лицензии',
+ status = models.IntegerField(verbose_name='статус лицензии',
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)
+ order_date = models.DateField(verbose_name='дата заказа', auto_now_add=True)
+ paid_date = models.DateField(verbose_name='дата оплаты', null=True, blank=True)
+ pay_sum = models.IntegerField(verbose_name='сумма оплаты')
+ deleted = models.BooleanField('удалено', default=False)
+
+ class Meta:
+ verbose_name = 'Лицензии'
+ verbose_name_plural = 'Лицензии'
def __init__(self, *args, **kwargs):
super(License, self).__init__(*args, **kwargs)
self.__prev_date = self.paid_date
def __str__(self):
- return u'%s - %s %s (%d %s)' % (
+ return '%s - %s %s (%d %s)' % (
self.company.get_company_name(),
self.term,
- numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
+ numeral.choose_plural(self.term, "месяц, месяца, месяцев"),
self.pay_sum,
- numeral.choose_plural(self.pay_sum, u"рубль, рубля, рублей"),
+ numeral.choose_plural(self.pay_sum, "рубль, рубля, рублей"),
)
def save(self, *args, **kwargs):
@@ -599,47 +601,35 @@ class License(models.Model):
if self.status == 0:
if self.payform == 0:
- return u'Скачать счёт' % reverse(
- 'customer_license_get_doc',
- kwargs={'order_num': self.id}
- )
+ url = reverse('customer_license_get_doc', kwargs={'order_num': self.id})
+ return f'Скачать счёт'
elif self.payform == 1:
- return u'Оплатить счёт'
+ return 'Оплатить счёт'
elif self.payform == 2:
- return u'Скачать квитанцию' % reverse(
- 'customer_license_get_doc',
- kwargs={'order_num': self.id}
- )
+ url = reverse('customer_license_get_doc', kwargs={'order_num': self.id})
+ return f'Скачать квитанцию'
elif self.status in [1, 2]:
- return u'История операций'
+ return 'История операций'
else:
return ''
def get_term(self):
if self.term == 0:
- return u'45 дней'
+ return '45 дней'
else:
- return u'%s %s' % (self.term,
- numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
- )
+ return f'{self.term} {numeral.choose_plural(self.term, "месяц, месяца, месяцев")}'
def get_paid_status(self):
if self.status == 1:
- return u'Лицензия оплачена, ещё не активирована'
+ return 'Лицензия оплачена, ещё не активирована'
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"день, дня, дней"),
- )
+ left_str = f'{left.months} ' \
+ f'{numeral.choose_plural(left.months, "месяц, месяца, месяцев")} ' \
+ f'{left.days} {numeral.choose_plural(left.days, "день, дня, дней")}'
else:
- left_str = '%d %s' % (
- left.days,
- numeral.choose_plural(left.days, u"день, дня, дней"),
- )
+ left_str = f'{left.days} {numeral.choose_plural(left.days, "день, дня, дней")}'
return f'Лицензия активирована: осталось {left_str}'
elif self.status == 3:
return 'Время истекло'
@@ -652,9 +642,10 @@ class LicensePrice(models.Model):
choices=consts.TERMS)
price = models.IntegerField(verbose_name='сумма оплаты')
+ class Meta:
+ verbose_name = 'Прайс на лицензии'
+ verbose_name_plural = 'Прайсы на лицензии'
+
def __str__(self):
- return u'%s %s (%d %s)' % (self.term,
- numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
- self.price,
- numeral.choose_plural(self.price, u"рубль, рубля, рублей"),
- )
+ return f'{self.term} {numeral.choose_plural(self.term, "месяц, месяца, месяцев")} ' \
+ f'({self.price} {numeral.choose_plural(self.price, "рубль, рубля, рублей")})'
diff --git a/src/customer/tasks.py b/src/customer/tasks.py
index 148328e..c34053e 100644
--- a/src/customer/tasks.py
+++ b/src/customer/tasks.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import
import traceback
+from django.core.urlresolvers import reverse
from django.utils import timezone
from datetime import datetime, timedelta
@@ -12,7 +13,7 @@ from django.core.mail import mail_admins
from myauth.models import DokUser, ConfirmEmail
-
+from . import emails
from .models import License, UserProfile
from .utils import check_one_profile
@@ -41,8 +42,10 @@ def check_license():
@shared_task
def delete_not_activated_users():
+ today = timezone.now().date()
+ date_join = today - timezone.timedelta(days=5)
users = DokUser.objects.filter(profile__active=False, profile__confirmed=False).\
- filter(profile__created_at__lte=timezone.now() - timezone.timedelta(days=5))
+ filter(date_joined__lte=date_join)
if users:
for user in users:
profile = user.profile
@@ -52,3 +55,18 @@ def delete_not_activated_users():
if confirm:
confirm.delete()
user.delete()
+
+
+@shared_task
+def send_offer_for_get_bonus():
+ today = timezone.now().date()
+ date_join_start = today - timezone.timedelta(days=9)
+ date_join_end = today - timezone.timedelta(days=8)
+ users = DokUser.objects.filter(profile__active=True, profile__confirmed=True).\
+ filter(date_joined__gt=date_join_start, date_joined__lt=date_join_end)
+
+ for user in users:
+ licenses = License.objects.filter(company=user.profile).filter(status=2)
+ if not licenses:
+ url = reverse('customer_order_license')
+ emails.send_offer_buy_licence_for_get_bonus.delay(user.email, url)
diff --git a/src/dokumentor/settings/common.py b/src/dokumentor/settings/common.py
index 3450e0d..38d5ddf 100644
--- a/src/dokumentor/settings/common.py
+++ b/src/dokumentor/settings/common.py
@@ -273,6 +273,10 @@ CELERYBEAT_SCHEDULE = {
'task': 'src.customer.tasks.delete_not_activated_users',
'schedule': timedelta(days=1),
},
+ 'send_offer_for_get_bonus': {
+ 'task': 'src.customer.tasks.send_offer_for_get_bonus',
+ 'schedule': timedelta(days=1),
+ },
}
CAPTCHA_OUTPUT_FORMAT = \
diff --git a/src/myauth/views.py b/src/myauth/views.py
index dc3b8c0..baeab16 100644
--- a/src/myauth/views.py
+++ b/src/myauth/views.py
@@ -19,8 +19,6 @@ from customer.models import UserProfile, UserProfileFilters, License
from myauth import forms, models, emails
-REGISTRATION_OPEN = getattr(settings, 'REGISTRATION_OPEN', True)
-
@sensitive_variables()
def _create_user(request, **kwargs):
@@ -69,7 +67,7 @@ def register(request):
success_msg = 'Дождитесь письма на указанный Вами адрес и перейдите по ссылке в письме.'
registration_closed_url = 'myauth_registration_closed'
- if not REGISTRATION_OPEN:
+ if not settings.REGISTRATION_OPEN:
return redirect(registration_closed_url)
if request.method == 'POST':
@@ -87,7 +85,7 @@ def register(request):
else:
form = form_class(prefix=form_prefix)
- return render(request, template_name, {'form': form, })
+ return render(request, template_name, {'form': form})
@sensitive_variables()
@@ -141,7 +139,7 @@ def reset(request):
else:
form = form_class(prefix=form_prefix)
- return render(request, template_name, {'form': form, })
+ return render(request, template_name, {'form': form})
@sensitive_variables()
@@ -250,7 +248,7 @@ def login(request):
else:
form = form_class(prefix=form_prefix)
- return render(request, template_name, {'form': form, })
+ return render(request, template_name, {'form': form})
def logout(request):
@@ -258,6 +256,7 @@ def logout(request):
request.user.profile.user_session_key = ''
request.user.profile.save()
except:
+ # TODO: ???
pass
django_logout(request)
response = redirect('/')
diff --git a/src/tests/conftest.py b/src/tests/conftest.py
index 66f6afa..57c43ad 100644
--- a/src/tests/conftest.py
+++ b/src/tests/conftest.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
pytest_plugins = [
- 'src.tests.fixtures.auth',
+ 'src.tests.fixtures.models',
'src.tests.fixtures.common'
]
diff --git a/src/tests/fixtures/auth.py b/src/tests/fixtures/auth.py
deleted file mode 100644
index ae52297..0000000
--- a/src/tests/fixtures/auth.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-import pytest
-from factories.models import UserFactory, ConfirmEmail
-
-
-@pytest.fixture
-def user():
- return UserFactory()
-
-
-@pytest.fixture
-def confirm_email():
- return ConfirmEmail()
diff --git a/src/tests/fixtures/models.py b/src/tests/fixtures/models.py
new file mode 100644
index 0000000..0bc2e7d
--- /dev/null
+++ b/src/tests/fixtures/models.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+import pytest
+from factories.models import UserFactory, ConfirmEmailFactory
+
+
+@pytest.fixture
+def user():
+ return UserFactory()
+
+
+@pytest.fixture
+def confirm_email():
+ return ConfirmEmailFactory()
+
+
+@pytest.fixture()
+def profile():
+ _user = UserFactory()
+ return _user.profile
diff --git a/src/tests/test_tasks.py b/src/tests/test_tasks.py
index 44e3695..a729997 100644
--- a/src/tests/test_tasks.py
+++ b/src/tests/test_tasks.py
@@ -6,14 +6,15 @@ from django.utils import timezone
from myauth.models import DokUser, ConfirmEmail
from customer.models import UserProfile
-from customer.tasks import delete_not_activated_users
+from customer.tasks import delete_not_activated_users, send_offer_for_get_bonus
dates_gte_five = [timezone.now() - timezone.timedelta(days=100),
timezone.now() - timezone.timedelta(days=15),
- timezone.now() - timezone.timedelta(days=5)]
+ timezone.now() - timezone.timedelta(days=6)]
-dates_lt_five = [timezone.now() - timezone.timedelta(days=4),
+dates_lt_five = [timezone.now() - timezone.timedelta(days=5),
+ timezone.now() - timezone.timedelta(days=4),
timezone.now() - timezone.timedelta(days=3),
timezone.now() - timezone.timedelta(days=2),
timezone.now() - timezone.timedelta(days=1),
@@ -24,10 +25,10 @@ dates_lt_five = [timezone.now() - timezone.timedelta(days=4),
@pytest.mark.django_db
def test_delete_not_activated_users_great_five_days(user, create_date):
user.is_active = False
+ user.date_joined = create_date
profile = user.profile
profile.active = False
profile.confirmed = False
- profile.created_at = create_date
profile.save()
user.save()
ConfirmEmail.objects.get_or_create(user=user)
@@ -43,10 +44,10 @@ def test_delete_not_activated_users_great_five_days(user, create_date):
@pytest.mark.django_db
def test_delete_not_activated_users_less_five_day(user, create_date):
user.is_active = False
+ user.date_joined = create_date
profile = user.profile
profile.active = False
profile.confirmed = False
- profile.created_at = create_date
profile.save()
user.save()
ConfirmEmail.objects.get_or_create(user=user)
@@ -56,3 +57,10 @@ def test_delete_not_activated_users_less_five_day(user, create_date):
assert DokUser.objects.count() == 1
assert UserProfile.objects.count() == 1
assert ConfirmEmail.objects.count() == 1
+
+
+@pytest.mark.django_db
+def test_send_offer_for_get_bonus(user):
+ send_offer_for_get_bonus()
+ print(user)
+ pass
diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py
index 448f3e5..35be02c 100644
--- a/src/tests/test_utils.py
+++ b/src/tests/test_utils.py
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
import pytest
+from django.utils import timezone
+
from commons.utils import get_site_url
+from customer.utils import check_one_profile
def test_utils_with_request(mocked_request):
@@ -15,5 +18,6 @@ def test_utils_without_request():
@pytest.mark.django_db
-def test_check_one_profile():
+def test_check_one_profile(profile):
+ check_one_profile(profile, timezone.now(), manual=False)
pass
diff --git a/templates/emails/greeting.txt b/templates/emails/greeting.txt
index 37eccc9..e6f24c2 100644
--- a/templates/emails/greeting.txt
+++ b/templates/emails/greeting.txt
@@ -8,6 +8,6 @@
С адреса {{ robot_email }} Вам будут приходить созданные Вами документы и служебные уведомления. Пожалуйста, добавьте его в список своих контактов, чтобы не пропустить ничего важного.
По всем вопросам можно писать на почту {{ support_email }} или в форму "обратная связь", расположенную внизу всех страниц сайте Документор ({{ site_url|default:'https://dokumentor.ru' }}).
----
+--
Это письмо отправлено роботом. Пожалуйста не отвечайте на него. По всем вопросам пишите на {{ support_email }}
diff --git a/templates/emails/offer_big_period_purchased_license_bonus.txt b/templates/emails/offer_big_period_purchased_license_bonus.txt
new file mode 100644
index 0000000..001f2b6
--- /dev/null
+++ b/templates/emails/offer_big_period_purchased_license_bonus.txt
@@ -0,0 +1,19 @@
+Здравствуйте!
+
+Прошло уже несколько дней, как Вы зарегистрировались в Документоре и, надеемся, что он Вам нравится.
+Если так, то для Вас есть специальное предложение: бонус - ещё 2 или 3 бесплатных месяца за доверие и сотрудничество.
+
+Сейчас у Вас осталось 40 дней бесплатного периода. И это время в любом случае будет бесплатным.
+Но, если Вы приобретёте лицензию на 1 или 2 года в течение следующих 10 дней, то мы подарим Вам ещё 2 или 3 месяца бесплатного времени.
+
+Лицензия на 1 год стоит 1800 рублей, подарок к ней - ещё 2 месяца Лицензия на 2 года стоит 3000 рублей, подарок к ней - ещё 3 месяца
+Ваш бесплатный период не уменьшится в любом случае. Срок новой лицензии начнётся только после того, как закончится бесплатное время.
+Так мы сможем быстрее развивать Документор и внедрять в него новые возможности, которые будут делать Вашу работу ещё удобнее. А Вы сможете пользоваться Документором без повышения цен в течение всего оплаченного и подаренного времени.
+
+Если Вы согласны, купите лицензию здесь {{ site_url }}{{ url }}. Если нет - ничего страшного ;)
+
+{{ site_url }}
+
+--
+
+Это письмо отправлено роботом. Пожалуйста не отвечайте на него. По всем вопросам пишите на {{ support_email }}
diff --git a/tox.ini b/tox.ini
index 084ca60..6c02c02 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,4 +1,4 @@
[flake8]
ignore = E731 F405
max-line-length = 99
-exclude = ./venv/*, ./node_modules/*, **/conf/**, **/migrations/**, **/templates/**, static/*
+exclude = ./venv/*, ./node_modules/*, **/conf/**, **/migrations/**, **/templates/**, static/*, conf/*