From 841f27ee617a82c2fc307547a026c1dad54c08b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Shesterkin Date: Tue, 27 Jun 2017 12:13:39 +0300 Subject: [PATCH] fix test --- src/customer/admin.py | 36 ++- src/customer/consts.py | 30 +- src/customer/emails.py | 22 ++ src/customer/models.py | 301 +++++++++--------- src/customer/tasks.py | 22 +- src/dokumentor/settings/common.py | 4 + src/myauth/views.py | 11 +- src/tests/conftest.py | 2 +- src/tests/fixtures/auth.py | 13 - src/tests/fixtures/models.py | 19 ++ src/tests/test_tasks.py | 18 +- src/tests/test_utils.py | 6 +- templates/emails/greeting.txt | 2 +- ...fer_big_period_purchased_license_bonus.txt | 19 ++ tox.ini | 2 +- 15 files changed, 306 insertions(+), 201 deletions(-) create mode 100644 src/customer/emails.py delete mode 100644 src/tests/fixtures/auth.py create mode 100644 src/tests/fixtures/models.py create mode 100644 templates/emails/offer_big_period_purchased_license_bonus.txt 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/*