def call_invoicing(sender, instance, **kwargs): """ Билинд для звонков через нашу телефонию Напоминание: instance.user1 - отправитель заявки, instance.user2 - получатель """ if not instance.pk and instance.user2: transaction = Transaction(user=instance.user2, note=u'звонок от %s' % instance) # юзеры без региона должны страдать price_level = instance.user2.region.price_level if instance.user2.region else 'high' # пропущенный звонок тарифицируется как заказ звонка if instance.is_received(): transaction.type = 84 transaction.amount = -get_lead_price('call', instance.deal_type, price_level) else: transaction.type = 85 transaction.amount = -get_lead_price( 'callrequest', instance.deal_type, price_level) # повторные звонки в течение 24 часов не тарифицируеются day_ago = instance.call_time - datetime.timedelta(days=1) if Call.objects.filter(user2=instance.user2, callerid1=instance.callerid1, call_time__gt=day_ago).exists(): transaction.amount = 0 transaction.save() instance.transaction = transaction
def add_transaction(request): from django import forms from django.http import HttpResponseForbidden from paid_services.models import Transaction, TRANSACTION_TYPE_CHOICES if request.user.groups.filter( id=12).count() == 0 and not request.user.is_superuser: return HttpResponseForbidden(u'У вас нет доступа к этой странице') type_choices = [] for transaction_type, type_display in TRANSACTION_TYPE_CHOICES: if transaction_type in (1, 7): type_choices.append((transaction_type, type_display)) class Form(forms.Form): email = forms.EmailField(label=u'E-mail пользователя') type = forms.ChoiceField(label=u'Тип транзакции', choices=type_choices) amount = forms.IntegerField(label=u'Сумма', min_value=1) def clean_email(self): email = self.cleaned_data['email'] try: User.objects.get(email__iexact=email) except User.DoesNotExist: raise forms.ValidationError(u'У нас нет такого пользователя') except User.MultipleObjectsReturned: raise forms.ValidationError(u'Два пользователя с таким e-mail') else: return email if request.POST: form = Form(request.POST) if form.is_valid(): type = int(form.cleaned_data['type']) user = User.objects.get(email__iexact=form.cleaned_data['email']) comment = u'%s, внес %s' % (dict(type_choices)[type], request.user.email) transaction = Transaction(user=user, amount=form.cleaned_data['amount'], type=type, comment=comment) transaction.save() return redirect( reverse('admin:paid_services_transaction_changelist') + ('?user=%d' % user.id)) else: form = Form() return render(request, 'admin/add_transaction.html', locals())
def move_money(self, request): class Form(forms.Form): from_user = UserField(label=u'От кого', help_text=u'e-mail или ID') to_user = UserField(label=u'Кому', help_text=u'e-mail или ID') amount = forms.IntegerField(label=u'Сумма перевода', min_value=1) def clean(self): if self.cleaned_data['from_user'].get_balance( force=True) < self.cleaned_data['amount']: raise forms.ValidationError( u'Недостаточно средств у пользователя %s' % self.cleaned_data['from_user']) if request.POST: from ad.models import Ad from paid_services.models import VipPlacement, InsufficientFundsError form = Form(request.POST) if form.is_valid(): from_user = form.cleaned_data['from_user'] to_user = form.cleaned_data['to_user'] if request.user.is_superuser or request.user.groups.filter(id__in=[1, 12]).exists()\ or (from_user.get_agency() and from_user.get_agency() == to_user.get_agency()): Transaction.move_money(from_user, to_user, form.cleaned_data['amount'], u'перевел %s' % request.user) self.message_user( request, u'Деньги переведены. Баланс пользователя %s: %d грн' % (to_user, to_user.get_balance(force=True)), level=messages.SUCCESS) return redirect(request.POST.get('next', '.')) else: self.message_user( request, u'Пользователи не находятся в одном агентстве', level=messages.ERROR) else: form = Form() return render(request, 'admin/move_money.html', locals())
def realtor_topup(request, realtor_id): agency = request.own_agency realtor = agency.realtors.filter(pk=realtor_id).first() if not agency or not realtor: raise Http404 import decimal amount = request.GET.get('amount') or request.POST.get('amount') amount = decimal.Decimal(amount.replace(',', '.')) balance = request.user.get_balance(force=True) if balance >= amount: Transaction.move_money(request.user, realtor.user, amount, u'через кабинет') return redirect( reverse('agency_admin:realtor_detail', args=[realtor_id])) else: from django.core.cache import cache deficit = abs(balance - amount) cache.set('interrupted_purchase_url_for_user%s' % request.user.id, request.get_full_path(), 60 * 10) return redirect( reverse('profile_balance_topup') + ('?amount=%s' % deficit))
def purchase_paidplacement(self, ad, paidplacement_type, move_money_from_user=None, order=None): from paid_services.models import VipPlacement, Transaction, InsufficientFundsError, PaidPlacement, CatalogPlacement if ad.user != self: raise Exception('User #%d has not an Ad #%d' % (self.id, ad.pk)) price = self.get_paidplacement_price(ad, paidplacement_type) try: if paidplacement_type == 'vip': if VipPlacement.objects.filter(is_active=True, basead=ad).exists(): raise PaidPlacement.AlreadyExistError() transaction = Transaction(user=self, type=53, amount=-price, order=order) transaction.save() VipPlacement(basead=ad, transaction=transaction).save() elif paidplacement_type in ('intl_mesto', 'worldwide'): if CatalogPlacement.objects.filter( is_active=True, basead=ad, catalog=paidplacement_type).exists(): raise PaidPlacement.AlreadyExistError() if paidplacement_type == 'worldwide': transaction = Transaction.objects.create(user=self, type=55, amount=-price, order=order) elif paidplacement_type == 'intl_mesto': transaction = Transaction.objects.create(user=self, type=54, amount=-price, order=order) CatalogPlacement(basead=ad, catalog=paidplacement_type, transaction=transaction).save() except InsufficientFundsError as insufficient_funds_error: if move_money_from_user: Transaction.move_money( move_money_from_user, self, insufficient_funds_error.deficit, u'для покупки платного размещения риелтором') self.purchase_paidplacement(ad, paidplacement_type) else: raise
def purchase_plan(self, plan, move_money_from_user=None, order=None): from paid_services.views import get_plan_action # TODO: надо избавиться от этого, но мешают хаки с подменой active_plan, например, в profile.views_plans.plan active_plan = self.get_active_plan() action = get_plan_action(plan, active_plan) region = self.region or Region.get_capital_province() price = Plan.get_price(region.price_level, plan.ads_limit, self.get_plan_discount()) unexpired_plans = self.get_unexpired_plans().order_by('-end') try: if action: if action == 'purchase': transaction = Transaction.objects.create(user=self, type=11, amount=-price, order=order) purchased_userplan = UserPlan.objects.create( user=self, plan=plan, ads_limit=plan.ads_limit, region=region) if action == 'prolong': transaction = Transaction.objects.create( user=self, type=11, amount=-price, order=order, comment=u'продление для тарифа #%d' % active_plan.id) purchased_userplan = UserPlan.objects.create( user=self, plan=plan, ads_limit=plan.ads_limit, region=region, start=unexpired_plans[0].end, is_active=False) if action == 'upgrade': for unexpired_plan in unexpired_plans: unexpired_plan.cancel(self) active_plan.cancel(self) transaction = Transaction.objects.create(user=self, type=11, amount=-price, order=order) purchased_userplan = UserPlan.objects.create( user=self, plan=plan, ads_limit=plan.ads_limit, region=region) Transaction.objects.filter(id=transaction.id).update( user_plan=purchased_userplan) else: raise Exception('User #%d can not buy plan #%d' % (self.id, plan.id)) except InsufficientFundsError as insufficient_funds_error: if move_money_from_user: Transaction.move_money(move_money_from_user, self, insufficient_funds_error.deficit, u'для покупки тарифа риелтором') self.purchase_plan(plan) else: raise
def buy_plan(self, request): class Form(forms.Form): user = UserField(label=u'Пользователь', help_text=u'email или ID') ads_limit = forms.IntegerField(label=u'Лимит по плану', min_value=1) price = forms.IntegerField(label=u'Цена тарифного плана', min_value=1, required=False) stop_active_plan = forms.BooleanField( label=u'Останавливать текущий', required=False) if request.POST: form = Form(request.POST) if form.is_valid(): user = form.cleaned_data['user'] ads_limit = form.cleaned_data['ads_limit'] if not user.region: from django.utils.safestring import mark_safe self.message_user( request, mark_safe( u'У пользователя отсутствует регион. <a href="%s">Указать регион</a>.' % reverse('admin:custom_user_user_change', args=[user.id])), level=messages.ERROR) return render(request, 'admin/buy_plan.html', locals()) try: plan = Plan.objects.get(is_active=True, ads_limit=ads_limit) discount = user.get_plan_discount() suggest_plan_price = Plan.get_price( user.region.price_level, ads_limit, discount) except Plan.DoesNotExist: plan = Plan.objects.get(pk=18) realtor = user.get_realtor() if realtor: agency_users = realtor.agency.get_realtors().exclude( user=user).values_list('user', flat=True) agency_ads_limits = (UserPlan.objects.filter(end__gt=datetime.datetime.now(), user__in=agency_users).aggregate(sum=Sum('ads_limit'))['sum'] or 0) \ + ads_limit else: agency_ads_limits = ads_limit if agency_ads_limits < 100: discount = 0.2 elif agency_ads_limits < 200: discount = 0.3 elif agency_ads_limits < 500: discount = 0.4 elif agency_ads_limits < 1000: discount = 0.45 else: discount = 0.5 suggest_plan_price = Plan.get_price( user.region.price_level, ads_limit, discount) if 'calculate' in request.POST: form.data._mutable = True form.data['price'] = suggest_plan_price self.message_user( request, u'%s лимит объявлений в агентстве, %s%% скидка. Цена за план %s грн. Баланс пользователя %s грн' % (ads_limit, int(discount * 100), suggest_plan_price, user.get_balance()), level=messages.WARNING) if 'buy' in request.POST: new_plan = UserPlan(user=user, plan=plan, ads_limit=ads_limit, region=user.region) active_plan = user.get_active_plan_using_prefetch() if active_plan: if form.cleaned_data['stop_active_plan']: active_plan.cancel(request.user) self.message_user( request, u'Предыдущий тариф #%s был отменен с возвратом средств' % active_plan.pk, level=messages.ERROR) else: new_plan.start = user.get_unexpired_plans( ).order_by('-end').first().end new_plan.is_active = False plan_price = form.cleaned_data[ 'price'] or suggest_plan_price if not (1 < (plan_price / ads_limit) < 30): self.message_user( request, u'Недопустимая цена за одно объявение: %.02f грн' % (plan_price / float(ads_limit)), level=messages.ERROR) elif user.get_balance(force=True) < plan_price: self.message_user( request, u'Недостаточно средств на счете. Тариф оценен в %s грн, баланс пользователя - %s' % (plan_price, user.get_balance()), level=messages.ERROR) else: # только здесь создается новый план new_plan.save() transaction = Transaction(user=user, type=11, amount=-plan_price, user_plan=new_plan) transaction.save() self.message_user( request, u'Тариф на %s объявлений c %s до %s куплен. Стоимость %s грн.' % (ads_limit, new_plan.start.strftime('%d.%m.%Y %H:%M'), new_plan.end.strftime('%d.%m.%Y %H:%M'), plan_price)) return redirect('.') else: form = Form() return render(request, 'admin/buy_plan.html', locals())
def buy_vip(self, request): vip_discounts_choices = ( (0, 'без скидки'), (10, 'от 10 до 20 объяв - 10%'), (18, 'от 21 до 50 объяв - 18%'), (26, 'от 51 объяв - 26%'), ) weeks_choices = ( (1, 1), (2, 2), (4, 4), ) class Form(forms.Form): ids = forms.CharField(label=u'ID объявлений', widget=forms.Textarea(attrs={'rows': 4})) discount = forms.IntegerField( label=u'Скидка по объему', widget=forms.Select(choices=vip_discounts_choices)) weeks = forms.IntegerField( label=u'Длительность, недель', widget=forms.Select(choices=weeks_choices)) bonus = forms.BooleanField(label=u'Бонус', required=False) if request.POST: from ad.models import Ad from paid_services.models import VipPlacement, InsufficientFundsError now = datetime.datetime.now() form = Form(request.POST) if form.is_valid(): ad_ids = form.cleaned_data['ids'].replace(',', ' ').split() ads = Ad.objects.filter(pk__in=ad_ids) discount = form.cleaned_data['discount'] weeks = form.cleaned_data['weeks'] sum = 0 bonus_comment = u'бонус от менеджера #%d %s' % ( request.user.id, request.user.email) bonus_vips_in_month = Transaction.objects.filter( type=53, time__year=now.year, time__month=now.month, comment=bonus_comment).count() for ad in ads: user = ad.user price = user.get_paidplacement_price( ad, 'vip') * weeks * (100 - discount) / 100 transaction = Transaction(user=user, type=53, amount=-price) # бесплатные ВИПы, один менеджер может дарить не большее 30 випов в месяц if form.cleaned_data['bonus']: if bonus_vips_in_month < 30: transaction.comment = bonus_comment transaction.amount = price = 0 bonus_vips_in_month += 1 weeks = 1 sum += price message = u'[%s, объявление #%s] VIP на %d дней, стоимость %d грн - ' % ( user, ad.pk, weeks * 7, price) if ad.vip_type: self.message_user(request, u'%s уже VIP' % message, level=messages.ERROR) continue if 'buy' in request.POST: try: if price > user.get_balance(force=True): agency_admin = user.get_realtor( ).agency.get_admin_user() Transaction.move_money( agency_admin, user, price, u'покупка VIP для объявления #%s' % ad.id) message += u'(перевод денег с главного аккаунта)' transaction.save() except InsufficientFundsError: self.message_user( request, u'%s недостаточно средств. Баланс %s' % (message, user.get_balance()), level=messages.ERROR) else: vip = VipPlacement.objects.create( basead=ad, days=weeks * 7, transaction=transaction) self.message_user(request, u'%s КУПЛЕН' % message, level=messages.SUCCESS) else: self.message_user(request, u'%s ОЦЕНЕН, еще не куплен' % message, level=messages.WARNING) if not ads: self.message_user(request, u'Объявления не найдены', level=messages.ERROR) if len(ads) > 1: self.message_user(request, u'Итого по всем объявлениям: %s грн' % sum, level=messages.WARNING) else: form = Form() return render(request, 'admin/buy_vip.html', locals())
def lead_activate(request): user_recipient = request.user if 'realtor' in request.GET: user_recipient = user_recipient.get_own_agency().get_realtors().get( pk=request.GET['realtor']).user # периоды лидогенерации создаются в сигнале check_leadgeneration_status у LeadGeneration leadgeneration, created = LeadGeneration.objects.get_or_create( user=user_recipient) leadgeneration.is_active_ads = True leadgeneration.save() balance_limit_for_activation = 200 # выделенный номер стоит 100 грн # TODO: пусть пока будет общая логика и минималка без учета стоимости выделенного номера if user_recipient.get_leadgeneration( ) and user_recipient.get_leadgeneration().dedicated_numbers: balance_limit_for_activation += 100 user_balance = user_recipient.get_balance(force=True) user_plan = user_recipient.get_active_plan() # учитываем возврат денег за тариф при переходе на ППК if user_plan: user_balance += user_plan.get_payback() if not user_recipient.get_realtor() and not user_recipient.is_developer(): messages.error( request, _(u'Данной услугой могут воспользоваться только риелторы и застройщики' )) elif user_balance < balance_limit_for_activation and not user_recipient.has_active_leadgeneration( 'ads'): from paid_services.models import Transaction, Order topup_amount = balance_limit_for_activation - user_balance # если услуги покупаются не риелтора агентства, но у риелтора не хватило денег, # то перекидываем деньги от текущего юзера if user_recipient != request.user and request.user.get_balance( force=True) >= topup_amount: Transaction.move_money(request.user, user_recipient, topup_amount, u'для активации ППК из кабинета агентства') if 'show_topup' not in request.GET: html = ''' <p>%s</p><br/><button class="btn btn-danger btn-lg btn-block" type="button" data-toggle="modal" data-target=".topup-modal">%s</button> ''' % ( _(u'Для активации услуги "Оплата за звонок" остаток на балансе должен быть более %(balance_limit)s грн.' ) % { 'balance_limit': balance_limit_for_activation }, _(u'Пополнить'), ) messages.info(request, html, extra_tags='modal-style1') query_string = request.GET.copy() query_string['topup_amount'] = topup_amount return HttpResponseRedirect( '%s?%s' % (reverse('services:lead'), query_string.urlencode())) elif user_recipient.has_active_leadgeneration('ads'): if user_recipient.ads.count() > 0: messages.success(request, _(u'Оплата за звонок активирована')) else: if request.is_developer_cabinet_enabled: add_property_url = reverse('profile_newhome_object_add') else: add_property_url = reverse('profile_add_property') html = '''<h5>%s<br/><br/>%s:</h5><br/><a href="%s" class="btn btn-danger btn-lg btn-block">%s</a> ''' % ( _(u'Услуга оплата за звонок успешно активирована.'), _(u'Добавьте своё первое объявление'), add_property_url, _(u'Добавить'), ) messages.info(request, html, extra_tags='modal-sm text-center') if 'next' in request.GET: return redirect(request.GET['next']) # возвращается на REFERER, т.к. на исходной странице могут переданы параметры типа ?realtor= return HttpResponseRedirect( request.META.get('HTTP_REFERER', reverse('services:lead')))