diff --git a/archilance/settings/dev.py b/archilance/settings/dev.py index 9488b9b..0ad6f0c 100644 --- a/archilance/settings/dev.py +++ b/archilance/settings/dev.py @@ -2,7 +2,7 @@ from .base import * AUTH_PASSWORD_VALIDATORS = [] -INSTALLED_APPS += ['debug_toolbar',] +# INSTALLED_APPS += ['debug_toolbar',] try: from .local import * diff --git a/projects/forms.py b/projects/forms.py index ace9a58..e8d6dcf 100644 --- a/projects/forms.py +++ b/projects/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.db.models import Q from django.forms import ModelForm, HiddenInput, Form, Select from django.forms.models import inlineformset_factory from mptt.forms import TreeNodeChoiceField @@ -117,13 +118,34 @@ class ProjectTrashForm(Form): self.req = kwargs.pop('req') super().__init__(*args, **kwargs) - if self.req.user.is_authenticated(): - self.fields['pk'].queryset = self.req.user.projects + self.fields['pk'].queryset = self.req.user.projects.filter(state='active') + + +class ProjectRestoreForm(Form): + pk = forms.ModelChoiceField(queryset=Project.objects.none()) - def clean(self): - cleaned_data = super().clean() + def __init__(self, *args, **kwargs): + self.req = kwargs.pop('req') + super().__init__(*args, **kwargs) - if not self.req.user.is_authenticated(): - raise forms.ValidationError('Пользователь не залогинен') + self.fields['pk'].queryset = self.req.user.projects.filter(state='trashed') + + +class ProjectDeleteForm(Form): + pk = forms.ModelChoiceField(queryset=Project.objects.none()) + + def __init__(self, *args, **kwargs): + self.req = kwargs.pop('req') + super().__init__(*args, **kwargs) + + self.fields['pk'].queryset = self.req.user.projects.filter(Q(state='active') | Q(state='trashed')) + + +class ProjectEditForm(Form): + pk = forms.ModelChoiceField(queryset=Project.objects.none()) + + def __init__(self, *args, **kwargs): + self.req = kwargs.pop('req') + super().__init__(*args, **kwargs) - return cleaned_data + self.fields['pk'].queryset = self.req.user.projects.filter(state='active') diff --git a/projects/templates/project_edit_test.html b/projects/templates/project_edit_test.html new file mode 100644 index 0000000..1f782b5 --- /dev/null +++ b/projects/templates/project_edit_test.html @@ -0,0 +1,12 @@ +{% extends 'partials/base.html' %} + +{% block content %} +
+ {% csrf_token %} + + {{ form }} + + + +
+{% endblock %} diff --git a/projects/templates/projects/project_list.html b/projects/templates/project_list.html similarity index 100% rename from projects/templates/projects/project_list.html rename to projects/templates/project_list.html diff --git a/projects/templates/projects/portfolio_form.html b/projects/templates/projects/portfolio_form.html deleted file mode 100644 index 95b8a4e..0000000 --- a/projects/templates/projects/portfolio_form.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends 'partials/base.html' %} -{% block content %} -

Добавление

-
- - {{ form.errors }} - {% csrf_token %} - - {% for field in form %} -
- {{ field.label }} - {{ field }} -
- {% endfor %} - - -
-{% endblock %}s diff --git a/projects/templates/projects/project_detail.html b/projects/templates/projects/project_detail.html deleted file mode 100644 index 8821bfa..0000000 --- a/projects/templates/projects/project_detail.html +++ /dev/null @@ -1,275 +0,0 @@ -{% extends 'partials/base.html' %} - -{% load staticfiles %} -{% load humanize %} - - -{% block content %} - {% include 'partials/header.html' %} - -
-

{{ object }}

-
-
-
- {% if request.user.is_contractor %} -
-

{{ object.price | intcomma }}

-
- {% endif %} - -
- -
- execitor-image -
-
- -

- {{ object.user.get_full_name }}[ivanov_petr] -

- -
-
- -
-
-
-

- Специализации: -

-
- {{ object.specialization }} -
-
-
-
- - {% if object.secure_transaction %} -
-
- -

Есть допуск СРО

- -
- {% endif %} -
-
-
-
-
- -
-
-

- {{ object.text }} -

-
-
- - Ответить на проект - -
-
- -
-
-
- - {% if perms.projects.add_answer %} -
- {{ form.errors }} - {% csrf_token %} -
- -
-
-

Стоимость

- - -
-
- -
-
-

Бюджет

- {{ form.cost_type }} -
-
- -
-
-
-
-
-

Срок

- - -
-
- -
-
-

Тип срока

- {{ form.term_type }} -
-
- -
-
-
-
-
-

Текст

- {{ form.project }} -
-
-
-
-
-
-
- -
- {% endif %} - -
- - - {% if request.user.is_customer %} -
-
-

Исполнители

-
-
-
- - - -
-
-
- -
-
-

Сравнить кандидатов

-
-
-
- {% endif %} - -
- {% for answer in object.answers.all %} -
-
- -
- execitor-image -
-
-

- {{ answer.user.get_full_name }} -

- -
-
- -
-
-

Есть допуск СРО

-
-
-
-

Цена: {{ answer.cost| intcomma }} - -

-

- Срок: {{ answer.term }} {{ answer.term_type }} -

-

Опубликован: {{ answer.created }}

-
-
- {% if request.user.is_customer %} - Кандидат - предложить проект - {% endif %} - отказ -
-
-
-
-

- Иванов Петр Иванович -

- 13.0.2016 / 21:05 -
- - - - - -
-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum - laoreet. -

-
-
-
-
- {% endfor %} -
- -
- {% include 'partials/pagination.html' %} -
- - {% include 'partials/footer.html' %} -{% endblock %} diff --git a/projects/templates/projects/project_form.html b/projects/templates/projects/project_form.html deleted file mode 100644 index 63d0ed9..0000000 --- a/projects/templates/projects/project_form.html +++ /dev/null @@ -1,278 +0,0 @@ -{% extends 'partials/base.html' %} - -{% block content %} -
-

Новый заказ

-
- -
- {% csrf_token %} - {{ form_project.errors }} - {{ form_realty.errors }} -
- {{ project_form.errors }} -
-

Формирование заказа

-
-

Название заказа

- - {{ project_form.name.errors }} -
-
-

Подробно опишите задание

- -
-
-
-
-

Дополнительно

-
-
- {# #} - {#
#} - {# #} - {#

+ добавить файл (до 100 файлов)

#} - {#
#} - {# #} - {# #} -
    -
  • - Архитерурное 2.jpg - 7мб -
    -
  • -
-
-

Тип работы:

-
-
- -

Проектирование

-
- -
- -

Техническое сопровождение

-
-
- -
-
-
-
-
-
Специализация проекта:
-
-
-
-
- - -
-
- - - -{# {{ project_form.specialization }}#} -
-
- -
-
- {# #} -
-
- {# #} -
-
-
-
Бюджет
-
-
-
-
- - -
-
- -
-
- -

или по договоренности

-
-
-
- -

Сделать для исполнителей обязательным для заполнения поля цена и срок

-
-
-
Способ оплаты
-
-
-
- -

- Безопасная сделка (с резервированием бюджета) -

-

- Текст -

-
-
- -

- Прямая оплата Исполнителю на его кошелек/счет -

-

- Текст -

-
-
-
-
-

Расширенный поиск

- -
-
-
-
-
-
-
-
Выбор объекта:
-
Наименование:
-
Классификация здания:
-
Вид строительства:
-
-
-
- -
-
- -
-
- -
-
- -
-
-
-
Местоположение:
-
-
-
- -
-
- -
-
- -

Требуется допуск СРО

-
-
- - - -
-
- -
- - - - - -{% endblock %} -{% block js_block %} - -{% endblock %} diff --git a/projects/templates/projects/testport.html b/projects/templates/projects/testport.html deleted file mode 100644 index b21ecc8..0000000 --- a/projects/templates/projects/testport.html +++ /dev/null @@ -1,18 +0,0 @@ -
{% csrf_token %} - {{ form.as_p }} - -
- Photos - {{ portfolio_photo_form.management_form }} - {{ portfolio_photo_form.non_form_errors }} - {% for form in portfolio_photo_form %} - {{ form.id }} -
- {{ form.img.errors }} - {{ form.img.label_tag }} - {{ form.img }} -
- {% endfor %} -
- -
diff --git a/projects/urls.py b/projects/urls.py index 9187e7b..67985ae 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -7,19 +7,25 @@ from .views import ( OfferOrderView, ProjectComparisonView, ProjectCreateView, + ProjectDeleteView, + ProjectEditView, + ProjectRestoreView, ProjectsView, ProjectTrashView, ProjectView, - ProjectTestView, ) app_name = 'projects' urlpatterns = [ urls.url(r'^$', ProjectsView.as_view(), name='list'), + urls.url(r'^(?P\d+)/$', ProjectView.as_view(), name='detail'), - # urls.url(r'^(?P\d+)/$', ProjectTestView.as_view(), name='detail'), urls.url(r'^(?P\d+)/trash/$', ProjectTrashView.as_view(), name='trash'), + urls.url(r'^(?P\d+)/restore/$', ProjectRestoreView.as_view(), name='restore'), + urls.url(r'^(?P\d+)/delete/$', ProjectDeleteView.as_view(), name='delete'), + urls.url(r'^(?P\d+)/edit/$', ProjectEditView.as_view(), name='edit'), + urls.url(r'^candidate/add/(?P(\d+))/(?P(\d+))/$', add_candidate, name='add-candidate'), urls.url(r'^candidate/comparison/(?P\d+)$', ProjectComparisonView.as_view(), name='comparison'), urls.url(r'^create/$', ProjectCreateView.as_view(), name='project-create'), diff --git a/projects/views.py b/projects/views.py index 75316f2..815a678 100644 --- a/projects/views.py +++ b/projects/views.py @@ -9,7 +9,18 @@ import pydash as _ from users.models import User from .models import Project, Portfolio, Candidate, Answer, Realty, Order -from .forms import ProjectForm, PortfolioForm, AnswerForm, RealtyForm, ProjectTrashForm + +from .forms import ( + AnswerForm, + PortfolioForm, + ProjectDeleteForm, + ProjectEditForm, + ProjectForm, + ProjectRestoreForm, + ProjectTrashForm, + RealtyForm, +) + from .mixins import LastAccessMixin @@ -21,16 +32,16 @@ class ProjectView(LastAccessMixin, View): model = Project form_class = AnswerForm template_name = 'contractor_project_detail.html' - + def get_success_url(self): return reverse('projects:detail', kwargs={'pk': self.object.pk}) - + def get(self, request, *args, **kwargs): if request.user.is_customer(): self.template_name = 'customer_project_detail.html' project = get_object_or_404(Project, pk=kwargs.get('pk')) return render(request, self.template_name, {'project': project, 'form': self.form_class}) - + def post(self, request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseForbidden() @@ -39,7 +50,7 @@ class ProjectView(LastAccessMixin, View): if form.is_valid(): return self.form_valid(request, form, project) return render(request, self.template_name, {'project': project, 'form': self.form_class}) - + def form_valid(self, request, form, project): answer = form.save(commit=False) answer.contractor = request.user @@ -95,22 +106,96 @@ class ProjectCreateView(PermissionRequiredMixin, View): class ProjectTrashView(View): + form_class = ProjectTrashForm + def post(self, req, *args, **kwargs): - form = ProjectTrashForm(_.merge({}, req.POST, kwargs), req=req) + if req.user.is_authenticated(): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'trashed' + project.save() + + messages.info(req, 'Проект перемещён в корзину') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + else: + return HttpResponseForbidden('403 Forbidden') + + redirect_to = req.POST.get('next') + return redirect(redirect_to) - if form.is_valid(): - project = form.cleaned_data.get('pk') - project.state = 'trashed' - project.save() - messages.info(req, 'Form valid') +class ProjectRestoreView(View): + form_class = ProjectRestoreForm + + def post(self, req, *args, **kwargs): + if req.user.is_authenticated(): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'active' + project.save() + + messages.info(req, 'Проект восстановлен из корзины') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) else: - messages.info(req, 'Form invalid:
{msg}
'.format(msg=pformat(form.errors))) + return HttpResponseForbidden('403 Forbidden') + + redirect_to = req.POST.get('next') + return redirect(redirect_to) + +class ProjectDeleteView(View): + form_class = ProjectDeleteForm + + def post(self, req, *args, **kwargs): + if req.user.is_authenticated(): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'deleted' + project.save() + + messages.info(req, 'Проект удалён навсегда') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + else: + return HttpResponseForbidden('403 Forbidden') + redirect_to = req.POST.get('next') return redirect(redirect_to) +class ProjectEditView(View): + form_class = ProjectEditForm + template_name = 'project_edit_test.html' # 'project_edit.html' + + def get(self, req, *args, **kwargs): + form = self.form_class() + return render(req, self.template_name, {'form': form}) + + def post(self, req, *args, **kwargs): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if req.user.is_authenticated(): + if form.is_valid(): + #... + + messages.info(req, 'Проект (как будто) успешно отредактирован') + redirect_to = req.POST.get('next') + return redirect(redirect_to) + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + return render(req, self.template_name, {'form': form}) + else: + return HttpResponseForbidden('403 Forbidden') + + class TestView(View): template_name = 'test_form.html' form_class = RealtyForm diff --git a/users/models.py b/users/models.py index 2444a2e..b8ab0ac 100644 --- a/users/models.py +++ b/users/models.py @@ -38,6 +38,7 @@ class User(AbstractBaseUser, PermissionsMixin): ('free', 'Свободен'), ('busy', 'Занят'), ) + nickname = models.CharField(max_length=50, blank=True,null=True) first_name = models.CharField(max_length=255, blank=True) last_name = models.CharField(max_length=255, blank=True) diff --git a/users/templates/customer_profile_open_projects.html b/users/templates/customer_profile_open_projects.html index 8badf6f..51cb31d 100644 --- a/users/templates/customer_profile_open_projects.html +++ b/users/templates/customer_profile_open_projects.html @@ -35,11 +35,11 @@
- {% for proj in object.projects.all %} + {% for proj in projects %}

- {{ proj.name }} + {{ proj.name }}

    @@ -61,7 +61,7 @@
  • {{ proj.created}}
  • 0
  • 0
  • -
  • {{ object.get_full_name }}
  • +
  • {{ customer.get_full_name }}
@@ -71,7 +71,13 @@

    -
  • Редактировать
  • +
  • +
    + {% csrf_token %} + + +
    +
  • diff --git a/users/templates/customer_profile_trashed_projects.html b/users/templates/customer_profile_trashed_projects.html index b52c6eb..4750dab 100644 --- a/users/templates/customer_profile_trashed_projects.html +++ b/users/templates/customer_profile_trashed_projects.html @@ -33,231 +33,62 @@
-
-
-

- Создание дизайна интерьера квартиры 200m2 -

-
    -
  • - Объект "Карточный домик" -
  • -
  • - 2 ответ от имени группы -
  • -
-

- Компьютерная 3D-визуализация помещений (разработка эксклюзивных художественных решений, тщательная проработка утвержденного варианта, подбор цветовой гаммы и текстуры отделочных материалов). Развертка и разрез стен, декоративные элементы (при необходимости)... -

-
    -
  • - 13.02.2016 -
  • -
  • - 523 -
  • -
  • - 523 -
  • -
  • - Александр Новиков -
  • -
-
-
-

- 35 000 -

-
    -
  • - Восстановить из корзины -
  • -
  • - Удалить навсегда -
  • -
-
-
-
-
-

- Создание дизайна интерьера квартиры 200m2 -

-
    -
  • - Объект "Карточный домик" -
  • -
  • - 2 ответ от имени группы -
  • -
-

- Компьютерная 3D-визуализация помещений (разработка эксклюзивных художественных решений, тщательная проработка утвержденного варианта, подбор цветовой гаммы и текстуры отделочных материалов). Развертка и разрез стен, декоративные элементы (при необходимости)... -

-
    -
  • - 13.02.2016 -
  • -
  • - 523 -
  • -
  • - 523 -
  • -
  • - Александр Новиков -
  • -
-
-
-

- 35 000 -

-
    -
  • - Восстановить из корзины -
  • -
  • - Удалить навсегда -
  • -
-
-
-
-
-

- Создание дизайна интерьера квартиры 200m2 -

-
    -
  • - Объект "Карточный домик" -
  • -
  • - 2 ответ от имени группы -
  • -
-

- Компьютерная 3D-визуализация помещений (разработка эксклюзивных художественных решений, тщательная проработка утвержденного варианта, подбор цветовой гаммы и текстуры отделочных материалов). Развертка и разрез стен, декоративные элементы (при необходимости)... -

-
    -
  • - 13.02.2016 -
  • -
  • - 523 -
  • -
  • - 523 -
  • -
  • - Александр Новиков -
  • -
-
-
-

- 35 000 -

-
    -
  • - Восстановить из корзины -
  • -
  • - Удалить навсегда -
  • -
-
-
-
-
-

- Создание дизайна интерьера квартиры 200m2 -

-
    -
  • - Объект "Карточный домик" -
  • -
  • - 2 ответ от имени группы -
  • -
-

- Компьютерная 3D-визуализация помещений (разработка эксклюзивных художественных решений, тщательная проработка утвержденного варианта, подбор цветовой гаммы и текстуры отделочных материалов). Развертка и разрез стен, декоративные элементы (при необходимости)... -

-
    -
  • - 13.02.2016 -
  • -
  • - 523 -
  • -
  • - 523 -
  • -
  • - Александр Новиков -
  • -
-
-
-

- 35 000 -

-
    -
  • - Восстановить из корзины -
  • -
  • - Удалить навсегда -
  • -
-
-
-
-
-

- Создание дизайна интерьера квартиры 200m2 -

-
    -
  • - Объект "Карточный домик" -
  • -
  • - 2 ответ от имени группы -
  • -
-

- Компьютерная 3D-визуализация помещений (разработка эксклюзивных художественных решений, тщательная проработка утвержденного варианта, подбор цветовой гаммы и текстуры отделочных материалов). Развертка и разрез стен, декоративные элементы (при необходимости)... -

-
    -
  • - 13.02.2016 -
  • -
  • - 523 -
  • -
  • - 523 -
  • -
  • - Александр Новиков -
  • -
-
-
-

- 35 000 -

-
    -
  • - Восстановить из корзины -
  • -
  • - Удалить навсегда -
  • -
-
-
+ {% for proj in projects %} +
+
+

+ {{ proj.name }} +

+
    +
  • + Объект "{{ proj.realty.name }}" +
  • +
  • + 2 ответ от имени группы +
  • +
+

+ {{ proj.text }} +

+
    +
  • + {{ proj.created}} +
  • +
  • + 523 +
  • +
  • + 523 +
  • +
  • + {{ customer.get_full_name }} +
  • +
+
+
+

+ {{ proj.budget }} +

+
    +
  • + + {% csrf_token %} + + + +
  • + +
  • +
    + {% csrf_token %} + + +
    +
  • +
+
+
+ {% endfor %}
{% include 'partials/footer.html' %} diff --git a/users/views.py b/users/views.py index a63fc31..bd0ffd0 100644 --- a/users/views.py +++ b/users/views.py @@ -16,33 +16,50 @@ class UserListView(ListView): class ContractorListView(ListView): model = User template_name = 'contractor_list.html' + context_object_name = 'contractor' class ContractorProfileDetailView(DetailView): model = User template_name = 'contractor_profile.html' + context_object_name = 'contractor' class ContractorOfficeDetailView(DetailView): model = User template_name = 'contractor_office.html' + context_object_name = 'contractor' class CustomerProfileOpenProjectsView(BaseMixin, DetailView): model = User template_name = 'customer_profile_open_projects.html' + context_object_name = 'customer' + + def get_context_data(self, **kwargs): + c = super().get_context_data(**kwargs) + c['projects'] = self.object.projects.filter(state='active') + return c class CustomerProfileTrashedProjectsView(BaseMixin, DetailView): model = User template_name = 'customer_profile_trashed_projects.html' + context_object_name = 'customer' + + def get_context_data(self, **kwargs): + c = super().get_context_data(**kwargs) + c['projects'] = self.object.projects.filter(state='trashed') + return c class CustomerProfileCurrentProjectsView(BaseMixin, DetailView): model = User template_name = 'customer_profile_current_projects.html' + context_object_name = 'customer' class CustomerProfileReviewsView(BaseMixin, DetailView): model = User template_name = 'customer_profile_reviews.html' + context_object_name = 'customer'