remotes/origin/PR-39
Mukhtar 10 years ago
commit 58d97a49c7
  1. 4
      api/urls.py
  2. 2
      archilance/management/commands/generate_projects.py
  3. 10
      archilance/util.py
  4. 87
      assets/css/main.css
  5. 2
      projects/forms.py
  6. 13
      projects/models.py
  7. 4
      projects/templates/project_detail.html
  8. 12
      projects/views.py
  9. 36
      templates/cms_pages/cms_page.html
  10. 18
      templates/partials/footer.html
  11. 3
      templates/partials/header.html
  12. 10
      users/models.py
  13. 4
      users/templates/contractor_filter.html
  14. 30
      users/templates/contractor_office.html
  15. 8
      users/templates/contractor_profile.html
  16. 11
      users/templates/user_financial_info_edit.html
  17. 55
      users/views.py
  18. 2
      wallets/templates/score-detail.html
  19. 19
      work_sell/migrations/0006_remove_worksell_team.py
  20. 9
      work_sell/models.py

@ -8,6 +8,7 @@ from .views import (
LocationViewSet,
MessageViewSet,
NoteViewSet,
OrderViewSet,
PortfolioPhotoViewSet,
PortfolioViewSet,
ProjectViewSet,
@ -17,7 +18,6 @@ from .views import (
StageViewSet,
TeamViewSet,
UserViewSet,
OrderViewSet,
)
@ -30,10 +30,10 @@ router.register(r'documents', DocumentViewSet)
router.register(r'locations', LocationViewSet)
router.register(r'message', MessageViewSet)
router.register(r'note', NoteViewSet)
router.register(r'orders', OrderViewSet)
router.register(r'portfolio-photos', PortfolioPhotoViewSet)
router.register(r'portfolios', PortfolioViewSet)
router.register(r'projects', ProjectViewSet)
router.register(r'orders', OrderViewSet)
router.register(r'realties', RealtyViewSet)
router.register(r'reviews', ReviewViewSet)
router.register(r'specializations', SpecializationViewSet)

@ -78,7 +78,7 @@ class Command(BaseCommand):
project=project,
contractor=_.sample((None, User.contractor_objects.order_by('?').first())),
secure=_.sample((True, False)),
status=_.sample((True, False)),
status=_.sample(Order.STATUSES)[0],
)
return project

@ -1,4 +1,5 @@
from django.core import validators
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import _get_queryset
from django.utils import timezone
from pprint import pprint, pformat
@ -54,6 +55,15 @@ def get_or_none(klass, *args, **kwargs):
return None
def get_related_or_none(obj, attr):
try:
rel = getattr(obj, attr)
except ObjectDoesNotExist:
rel = None
return rel
def get_attr_or_none(klass, *args, attr=None, **kwargs):
object = get_or_none(klass, *args, **kwargs)

@ -5524,7 +5524,7 @@ input[type="radio"]:checked + span {
width: 216px;
}
.proj_answ_form .textAreaBlock2 input[name="term"]{
width: 262px;
width: 258px;
}
.proj_answ_form .textAreaBlock2 p span{
position: absolute;
@ -5759,7 +5759,7 @@ input[type="radio"]:checked + span {
margin-bottom: 0 !important
}
.infoProfile .dropdown-menu{
height: 316px;
height: auto;
}
.form-regestration{
padding: 20px 0 40px 0;
@ -5819,4 +5819,87 @@ input[type="radio"]:checked + span {
text-transform: initial;
letter-spacing: normal;
}
.fr_answer{
width: 100%;
height: 110px;
border: 1px solid #cdcdcd;
resize: none;
padding: 10px 15px;
color: #3c1a06;
font-family: 'Arial-MT-Regular', sans-serif;
font-size: 14px;
float: left;
margin: 0px 0px 0px 55px;
}
.fr_answer_sen{
float: left;
border-radius: 40px;
font-family: 'pfdintextcomppro-regular', sans-serif;
letter-spacing: 2px;
color: #373737;
margin: 20px 0 0 55px;
font-size: 15px;
border: 1px solid #BEBEBE;
padding: 17px 46px;
text-transform: uppercase;
}
.fa-pencil:before, .fa-times:before{
color: #fff;
}
.mainMenu li.active > a {
border-color: #ff0029;
}
.mainMenu li.icon_tm1.active span{
background: url('../img/listMain2.png') no-repeat !important;
}
.mainMenu li.officeList.active span {
background: url('../img/list4tml.png') no-repeat center !important;
}
.mainMenu li.icon_tm2.active span{
background: url('../img/listMain2.png') no-repeat center !important;
}
.mainMenu li.icon_tm3.active span{
background: url('../img/listMain2.png') no-repeat right !important;
}
.faq_page_inn{
padding: 40px 0;
}
.faq_page_inn h2 {
font-size: 32px;
font-weight: normal;
margin-bottom: 20px;
margin-top: 10px;
}
.faq_page_inn h3{
font-weight: 600;
margin-bottom: 12px;
margin-top: 28px;
font-size: 1.17em;
}
.faq_page_inn ol, .faq_page_inn ul{
padding-left: 40px;
margin-top: 20px;
}
.faq_page_inn ol li, .faq_page_inn ul li{
padding-left: 8px;
list-style: inherit !important;
display: list-item;
list-style-type:inherit !important;
margin-top: 5px;
}
.faq_page_inn a{
text-decoration: underline !important;
}
.faq_page_inn a:hover{
text-decoration: none !important;
}
.faq_page_inn div{
margin-top: 20px;
}
a.linkS2[data-target="#withdraw-money"]{
float: none;
display: table;
margin: auto;
margin-bottom: 52px;
}
/*end_new*/

@ -182,7 +182,7 @@ class RealtyForm(forms.ModelForm):
class PortfolioForm(forms.ModelForm):
duplicate = forms.BooleanField(required=False,label='Some label here')
duplicate = forms.BooleanField(required=False, label='Some label here')
images_ids = forms.CharField(required=True)
class Meta:

@ -204,12 +204,13 @@ class Order(models.Model):
('process', 'В процессе'),
('completed', 'Завершен'),
)
contractor = models.ForeignKey(User, null=True, blank=True, related_name='orders')
team = models.ForeignKey(Team, null=True, blank=True, related_name='orders')
contractor = models.ForeignKey(User, null=True, blank=True, related_name='orders') # Related name should've been "contractor_orders"
created = models.DateTimeField(default=timezone.now)
project = models.OneToOneField(Project, related_name='order')
secure = models.BooleanField(default=False)
status = models.CharField(max_length=30, choices=STATUSES, default='created')
team = models.ForeignKey(Team, null=True, blank=True, related_name='orders')
def __str__(self):
return self.project.name
@ -287,7 +288,7 @@ class Portfolio(models.Model):
budget = models.DecimalField(max_digits=10, decimal_places=0, default=0, null=True, blank=True)
building_classification = models.ForeignKey(BuildingClassfication, related_name='portfolios', null=True, blank=True)
construction_type = models.ForeignKey(ConstructionType, related_name='portfolios', null=True, blank=True)
created = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True, auto_created=True)
currency = models.CharField(max_length=20, default='rur', choices=CURRENCIES, null=True, blank=True)
description = models.TextField()
location = TreeForeignKey('common.Location', related_name='portfolios', null=True, blank=True)
@ -297,19 +298,19 @@ class Portfolio(models.Model):
term_type = models.CharField(max_length=20, choices=TERMS, default='hour', null=True, blank=True)
user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True)
worksell = models.BooleanField(default=False)
def __str__(self):
return self.name
def get_prev(self):
try:
return self.get_previous_by_created()
return self.get_previous_by_created(user=self.user)
except self.DoesNotExist:
return None
def get_next(self):
try:
return self.get_next_by_created()
return self.get_next_by_created(user=self.user)
except self.DoesNotExist:
return None

@ -313,8 +313,8 @@
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'projects:detail' pk=project.pk %}">
<div><textarea name="text"></textarea></div>
<div><button type="submit">Отправить</button></div>
<div><textarea name="text" class="fr_answer"></textarea></div>
<div><button type="submit" class="fr_answer_sen">Отправить</button></div>
</form>
</div>
</div>

@ -59,8 +59,7 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
answer = _.first(_.filter(project_answers, lambda a: a.author == contractor))
if not answer:
try: team = contractor.team
except Team.DoesNotExist: team = None
team = util.get_related_or_none(contractor, 'team')
if team:
answer = _.first(_.filter(project_answers, lambda a: a.author == team))
@ -68,8 +67,10 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
context.update({'answer': answer})
if not answer:
try: contractor.team; context.update({'can_answer_as_team': True})
except Team.DoesNotExist: pass
team = util.get_related_or_none(contractor, 'team')
if team:
context.update({'can_answer_as_team': True})
if request.GET.get('answer_as_team') == 'on':
context.update({'answer_as_team': True})
@ -154,8 +155,7 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
if isinstance(answer.author, User) and answer.author == request.user:
message.contractor_or_team = request.user
elif isinstance(answer.author, Team):
try: team = request.user.team
except Team.DoesNotExist: team = None
team = util.get_related_or_none(request.user, 'team')
if team and answer.author == team:
message.contractor_or_team = team

@ -9,14 +9,32 @@
</div>
</div>
</section>
<div class="container">
<div class="row faq_page_inn">
{% for block in page.body %}
{% if block.block_type == 'heading' %}
<h2>{{ block.value }}</h2>
{% else %}
<section class="block-{{ block.block_type }}">
{{ block }}
<h2>FAQ</h2>
<p>Lorem Ipsum has been the industry's standard dummy text ever since <a href="#">the 1500s</a>, when an <b>unknown</b> printer <strong>took a</strong> galley of type and scrambled it to make a type specimen book. It has </p>
<h3>is simply dummy text of the printing and typesetting industry</h3>
<ol>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum</li>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum</li>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum</li>
</ol>
<ul>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum</li>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum.</li>
<li>Many desktop publishing packages and web page editors now use Lorem Ipsum</li>
</ul>
<div>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum </div>
</section>
{% endif %}
{% endfor %}
</div>
</div>
{% for block in page.body %}
{% if block.block_type == 'heading' %}
<h2>{{ block.value }}</h2>
{% else %}
<section class="block-{{ block.block_type }}">
{{ block }}
</section>
{% endif %}
{% endfor %}
{% endblock %}

@ -19,6 +19,7 @@
<div class="col-lg-3 col-lg-offset-2 listF1">
<p>Профиль</p>
<ul>
{% if not request.user.is_authenticated %}
<li>
<a href="{% url 'auth_login' %}">Войти</a>
</li>
@ -26,14 +27,21 @@
<li>
<a href="{% url 'registration_register' %}">Регистрация</a>
</li>
<li>
<a href="{% url 'password_reset_recover' %}">Востановить доступ</a>
</li>
<li>
<a href="{% url 'projects:customer-project-create' %}">Опубликовать проект</a>
</li>
{% else %}
{% if request.user.is_customer %}
<li>
<a href="{% url 'projects:customer-project-create' %}">Опубликовать проект</a>
</li>
{% else %}
<li>
<a href="{% url 'users:contractor-office' pk=request.user.pk %}">Мой офис</a>
</li>
{% endif %}
{% endif %}
</ul>
</div>

@ -89,18 +89,21 @@
<span class="glyphicon glyphicon-menu-hamburger" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu menu-drop-new">
{% if request.user.is_contractor %}
<li class="icon_mm1">
<a href="{% url 'auth_login' %}">
Войти заказчиком
<span></span>
</a>
</li>
{% else %}
<li class="icon_mm2">
<a href="{% url 'auth_login' %}">
Войти исполнителем
<span></span>
</a>
</li>
{% endif %}
<li class="icon_mm3">
<a href="{% url 'chat:chat-user' %}">Сообщения<span></span></a>
</li>

@ -147,11 +147,11 @@ class User(AbstractBaseUser, PermissionsMixin):
if self.location:
if self.location.level == 3:
return self.location.parent.parent.name + ', ' + self.location.name
else:
return None
else:
return None
elif self.location.level == 2:
return self.location.parent.name + ', ' + self.location.name
elif self.location.level == 1:
return self.location.name
@property
def is_staff(self):
return self.is_superuser

@ -42,7 +42,7 @@
</div>
<div class="searchF1 polsF1 polsFF polsF3">
<div class="searchF1 polsF1 polsFF polsF3 resSearchF1">
<div class="col-lg-3">
<a href="#" class="findReal" onclick="$(this).closest('form').submit(); return false">
найти исполнителя
@ -142,7 +142,7 @@
type="submit"
name="{{ form.party_types.html_name }}"
value="{{ val }}"
class="{% if val == last_party_types %}active{% endif %} btn btn-default">
class="{% if val == last_party_types or not last_party_types and val == 'all' %}active{% endif %} btn btn-default">
{{ text }}
</button>
{% endfor %}

@ -17,12 +17,12 @@
<div class="buttonGP disTab">
<div class="btn-group valProject2 val-pro3" role="group">
<button type="button" class="btn btn-default">
{{ contractor.team.name }}
<span><mark>0</mark></span>
</button>
{% if not contractor.is_owner_team %}
{% if contractor.team %}
<button type="button" class="btn btn-default">
{{ contractor.team.name }}
<span><mark>0</mark></span>
</button>
{% else %}
<button type="button" class="btn btn-default add-group" data-toggle="modal" data-target="#myModal">
+ Добавить группу
</button>
@ -58,7 +58,7 @@
</div>
</div>
{% if contractor.is_owner_team %}
{% if contractor.team %}
<div class="projectsBlock disTab">
<div class="col-lg-12">
<div class="col-lg-3 divCol3">
@ -82,11 +82,11 @@
<div class="block-users">
<p>Состав группы</p>
{% for p in participants %}
{% for c in team_members %}
<div class="message-new">
<div class="imgMess">
{% if p.avatar %}
{% thumbnail p.avatar "60x60" crop="center" as im %}
{% if c.avatar %}
{% thumbnail c.avatar "60x60" crop="center" as im %}
<img src="{{ im.url }}" alt="mess-image">
{% endthumbnail %}
{% else %}
@ -95,10 +95,10 @@
</div>
<p class="nameMess" style="float: none">
<a href="{% url 'users:contractor-profile' p.pk %}">{{ p.get_full_name }}</a>
<a href="{% url 'users:contractor-profile' c.pk %}">{{ c.get_full_name }}</a>
</p>
<p><span>{{ p.get_popular_specialization }}</span></p>
<p><span>{{ c.get_popular_specialization }}</span></p>
</div>
{% empty %}
В группе пока нет участников
@ -110,15 +110,15 @@
<div class="col-lg-9 divCol9">
<div class="col-lg-4">
<p class="nameUser">{{ contractor.team.name }}</p>
<p class="cityUser">Россия, Москва</p>
<p class="cityUser">{{ contractor.get_location }}</p>
<p class="navv">На сайте {{ contractor.team.created }}</p>
<p class="navv">
Кол-во человек: <span>{{ participants_count }}</span>
Кол-во человек: <span>{{ team_member_count }}</span>
</p>
<p class="navv">
Выполненных проектов: <span>0</span>
Выполненных проектов: <span>{{ completed_project_count }}</span>
</p>
<div class="statusUser st-new">Свободен</div>

@ -383,7 +383,9 @@
{% thumbnail diplom.img "210x334" crop="center" as im %}
<div class="imgGal"
style="background: url('{{ im.url }}') no-repeat center;">
<div class="imgFigure"></div>
<a href="{{ im.url }}" class='open-modal-image'>
<div class="imgFigure"></div>
</a>
</div>
{% endthumbnail %}
</div>
@ -414,7 +416,9 @@
{% thumbnail cro.img "210x334" crop="center" as im %}
<div class="imgGal"
style="background: url('{{ im.url }}') no-repeat center;">
<div class="imgFigure"></div>
<a href="{{ im.url }}" class='open-modal-image'>
<div class="imgFigure"></div>
</a>
</div>
{% endthumbnail %}
</div>

@ -106,7 +106,7 @@
<input type="text" class="searchInp box-sizing" value="{{ fin_info_form.fio.value }}" name="{{ fin_info_form.fio.html_name }}">
</div>
<div class="col-lg-3">
<input type="text" class="box-sizing surr surr2 datepicker" value="{{ fin_info_form.date_of_birth.value }}" name="{{ fin_info_form.date_of_birth.html_name }}">
<input type="text" class="box-sizing surr surr2 datepicker" value="{{ fin_info_form.date_of_birth.value|date:"d.m.Y" }}" name="{{ fin_info_form.date_of_birth.html_name }}">
</div>
<div class="col-lg-3">
<input type="text" class="box-sizing surr surr2" value="{{ fin_info_form.phone.value }}" name="{{ fin_info_form.phone.html_name }}">
@ -165,7 +165,7 @@
<input type="text" value="{{ fin_info_form.subdivision_code.value }}" class="box-sizing surr surr2" placeholder="0033" name="{{ fin_info_form.subdivision_code.html_name }}">
</div>
<div class="col-lg-3">
<input type="text" value="{{ fin_info_form.passport_issue_date.value }}" name="{{ fin_info_form.passport_issue_date.html_name }}" class="box-sizing surr surr2 datepicker" placeholder="11.12.1994">
<input type="text" value="{{ fin_info_form.passport_issue_date.value|date:"d.m.Y" }}" name="{{ fin_info_form.passport_issue_date.html_name }}" class="box-sizing surr surr2 datepicker" placeholder="11.12.1994">
</div>
</div>
@ -207,8 +207,11 @@
<input type="text" value="{{ fin_info_form.credit_card_number.value }}" class="box-sizing surr surr2" name="{{ fin_info_form.credit_card_number.html_name }}">
</div>
<div class="col-lg-3">
{{ fin_info_form.passport_scan.value }}
{% if fin_info_form.passport_scan.value %}
{# {{ fin_info_form.passport_scan.value }}#}
Скан-копия загружена
{% endif %}
<div class="upload2 up-l1 take-new">
<input type="file" name="{{ fin_info_form.passport_scan.html_name }}">
<p>Прикрепить файл</p>

@ -188,8 +188,6 @@ class ContractorFilterView(BaseMixin, View):
context.update({'last_party_types': last_party_types})
if get_contractors:
contractors = User.contractor_objects.all()
@ -216,9 +214,7 @@ class ContractorFilterView(BaseMixin, View):
if constr_type:
contractors = contractors.filter(orders__project__realty__construction_type=constr_type)
if get_teams:
teams = Team.objects.all()
@ -278,7 +274,6 @@ class ContractorFilterView(BaseMixin, View):
count = coll.count()
display_msg = 'Найдено %s групп' % count if count > 0 else 'Ничего не найдено'
order_by = form.cleaned_data.get('order_by')
last_order_by = form.cleaned_data.get('last_order_by')
reverse_order = form.cleaned_data.get('reverse_order')
@ -364,24 +359,42 @@ class ContractorOfficeView(DetailView):
context_object_name = 'contractor'
form_class = TeamForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_contractor() and request.user.pk == int(kwargs.get('pk')):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form_team'] = self.form_class
if self.object.is_owner_team():
participants = self.object.team.contractors.all()
context['participants'] = participants
context['participants_count'] = len(participants)
contractor = self.object
if util.get_related_or_none(contractor, 'team'):
members = contractor.team.contractors.all()
context['team_members'] = members
context['team_member_count'] = len(members)
contractors = tuple(itertools.chain(participants, (self.object,)))
compl_proj = []
portfolios = []
work_sells = []
portfolios = Portfolio.objects.filter(user__in=contractors)
context['portfolios'] = portfolios
compl_proj.extend(tuple(o.project for o in contractor.orders.filter(status='completed')))
portfolios.extend(contractor.portfolios.all())
work_sells.extend(contractor.work_sell.all())
for c in members:
compl_proj.extend(tuple(o.project for o in c.orders.filter(status='completed')))
portfolios.extend(c.portfolios.all())
work_sells.extend(c.work_sell.all())
work_sells = WorkSell.objects.filter(contractor__in=contractors)
context['completed_project_count'] = len(compl_proj)
context['portfolios'] = portfolios
context['work_sells'] = work_sells
context['reviews'] = Review.objects.filter(target_contractor__in=contractors)
context['reviews'] = Review.objects.filter(target_contractor__in=itertools.chain((contractor,), members))
context['form_team'] = self.form_class
return context
@ -403,9 +416,13 @@ class ContractorOfficeProjectsView(BaseMixin, View):
private_open_projects = tuple(a.project for a in contractor.contractor_answers.filter(project__state='active', rejected=False))
private_archived_projects = tuple(a.project for a in contractor.contractor_answers.filter(project__state='active', rejected=True))
team_open_projects = tuple(a.project for a in contractor.team.answers.filter(project__state='active', rejected=False))
team_archived_projects = tuple(a.project for a in contractor.team.answers.filter(project__state='active', rejected=True))
try:
team_open_projects = tuple(a.project for a in contractor.team.answers.filter(project__state='active', rejected=False))
team_archived_projects = tuple(a.project for a in contractor.team.answers.filter(project__state='active', rejected=True))
except:
team_open_projects = ()
team_archived_projects = ()
context['all_project_count'] = \
len(private_open_projects) + len(private_archived_projects) + \

@ -22,7 +22,7 @@
</div>
{% endif %}
<div class="col-lg-6">
<div class="col-lg-12">
<a href="#" data-toggle="modal" data-target="#withdraw-money"
class="linkS linkS2">вывести средства</a>
</div>

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-26 17:25
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('work_sell', '0005_remove_worksell_img'),
]
operations = [
migrations.RemoveField(
model_name='worksell',
name='team',
),
]

@ -16,14 +16,13 @@ class WorkSell(models.Model):
budget = models.DecimalField(max_digits=10, decimal_places=0, default=0, null=True, blank=True)
building_classification = models.ForeignKey(BuildingClassfication, related_name='worksells', null=True, blank=True)
construction_type = models.ForeignKey(ConstructionType, related_name='worksells', null=True, blank=True)
contractor = models.ForeignKey(User, related_name='work_sell', null=True, blank=True) # TODO: Pluralize related name
created = models.DateTimeField(default=timezone.now)
contractor = models.ForeignKey(User, related_name='work_sell', null=True, blank=True) # TODO: Pluralize related name
created = models.DateTimeField(auto_now_add=True)
currency = models.CharField(max_length=20, default='rur', choices=CURRENCIES, null=True, blank=True)
description = models.TextField(blank=True)
location = TreeForeignKey('common.Location', related_name='worksells', null=True, blank=True)
name = models.CharField(max_length=255)
specialization = TreeForeignKey(Specialization, related_name='worksells', null=True, blank=True)
team = models.ForeignKey(Team, related_name='work_sells', null=True, blank=True)
term = models.IntegerField(default=0, null=True, blank=True)
term_type = models.CharField(max_length=20, choices=TERMS, default='hour', null=True, blank=True)
@ -35,13 +34,13 @@ class WorkSell(models.Model):
def get_prev(self):
try:
return self.get_previous_by_created()
return self.get_previous_by_created(contractor=self.contractor)
except self.DoesNotExist:
return None
def get_next(self):
try:
return self.get_next_by_created()
return self.get_next_by_created(contractor=self.contractor)
except self.DoesNotExist:
return None

Loading…
Cancel
Save