remotes/origin/PR-39
ArturBaybulatov 10 years ago
commit d912bfcb8f
  1. 1
      api/views.py
  2. 165
      assets/js/chat.js
  3. 22
      chat/migrations/0007_auto_20160826_1458.py
  4. 2
      chat/models.py
  5. 19
      chat/response.py
  6. 33
      chat/templates/arbitration_modal.html
  7. 91
      chat/templates/chat_contractor.html
  8. 121
      chat/templates/chat_customer.html
  9. 16
      chat/templates/review_add_modal.html
  10. 2
      chat/urls.py
  11. 15
      chat/utils.py
  12. 26
      chat/views.py
  13. 8
      projects/admin.py
  14. 2
      projects/forms.py
  15. 33
      projects/migrations/0017_arbitration.py
  16. 1
      projects/mixins.py
  17. 26
      projects/models.py
  18. 2
      projects/serializers.py
  19. 6
      projects/templates/project_detail.html
  20. 2
      projects/urls.py
  21. 28
      projects/views.py
  22. 3
      ratings/templatetags/specializtions_tags.py
  23. 1
      reviews/__init__.py
  24. 5
      reviews/apps.py
  25. 31
      reviews/signals.py
  26. 18
      templates/partials/footer.html
  27. 2
      users/templates/contractor_filter.html
  28. 8
      work_sell/models.py

@ -115,6 +115,7 @@ class OrderViewSet(ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
filter_class = OrderFilterSet
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class SpecializationViewSet(ModelViewSet):

@ -21,7 +21,7 @@ var SocketHandler = function () {
inbox = document.getElementById('message-chat-team-space');
} else if (message.answer_type == 'approve_stages') {
var resOrderId = message.order_id;
$.jGrowl(message.msg,{
$.jGrowl(message.msg, {
life: 4000
});
setTimeout(function () {
@ -34,12 +34,12 @@ var SocketHandler = function () {
var classMessage = 'youChat';
var senderName = 'Вы';
var timeMessage = message.msg_time;
if (message.sender_id != userId){
if (message.sender_id != userId) {
senderName = message.sender_name;
classMessage = '';
}
inbox.innerHTML += '<div class="col-lg-12 insetCommChat '+ classMessage +'"><div class="topCommChat">' +
'<p class="nameCommChat">'+ senderName +'</p> <span>' + timeMessage + '</span></div>' +
inbox.innerHTML += '<div class="col-lg-12 insetCommChat ' + classMessage + '"><div class="topCommChat">' +
'<p class="nameCommChat">' + senderName + '</p> <span>' + timeMessage + '</span></div>' +
'<p class="textCommChat">' + textMessage + '</p></div>';
}
@ -98,17 +98,32 @@ function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken');
$(function () {
setTimeout(function () {
$(".user-block").first().trigger('click');
}, 10);
var currentHash = URI(location.href).hash();
console.log(currentHash);
if (currentHash.indexOf("#order") == 0) {
setTimeout(function () {
$(".order-block").last().trigger('click');
$("a[href='#tab2']").trigger('click');
}, 100);
var ordHashId = currentHash.replace("#order", "");
setTimeout(function () {
$("#orderBlock" + ordHashId).trigger('click');
$("a[href='#tab2']").trigger('click');
}, 100);
} else if (currentHash.indexOf("#team") == 0) {
} else {
setTimeout(function () {
$(".user-block").first().trigger('click');
}, 10);
setTimeout(function () {
$(".order-block").last().trigger('click');
}, 100);
}
$('.deleteMess').on('click', function (e) {
@ -123,10 +138,10 @@ $(function () {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: {'sender_id':senderId, 'recipent_id': recipentId},
data: {'sender_id': senderId, 'recipent_id': recipentId},
dataType: 'json',
success: function (json) {
if (json.status == 'ok'){
if (json.status == 'ok') {
_this.parent().remove();
$("#message-chat-space").html("");
}
@ -161,6 +176,130 @@ $(function () {
}
});
});
// Добавление сообщения для заказа.
$('#order-chat-add-message').on('click', function (e) {
e.preventDefault();
var chatMessage = $("#chat-order-add #chat").val();
var recipentId = $("#chat-order-add #recipentId").val();
var senderId = $("#chat-order-add #senderId").val();
var orderId = $("#chat-order-add #orderId").val();
socket.add_contact_message({
"format_type": "add_message_order",
"data": {
"sender_id": senderId,
"recipent_id": recipentId,
"chat_message": chatMessage,
"order_id": orderId,
}
});
$("#chat-order-add #chat").val("");
$("#document-send-order").html("");
});
// Добавление отзыва
$('#order-review-add').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var formData = $("#review-adds-form").serialize();
$.ajax({
url: '/api/reviews/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: formData,
dataType: 'json',
success: function (json) {
console.log("Успешно");
$("#review-add").modal('hide');
$.jGrowl("Ваш отзыв успешно добавлен", {
life: 4000
});
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
// Добавление сообщения в арбитраж
$('#order-arbitration-add').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var formData = $("#arbitration-add-form").serialize();
$.ajax({
url: '/projects/arbitration/create/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: formData,
dataType: 'json',
success: function (json) {
console.log(json);
$("#arbitration-add").modal('hide');
$.jGrowl("Обращение в арбитраж добавлено", {
life: 4000
});
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
//Загрузка документов
$('#upload-document-order').fileupload({
url: '/chat/create/',
formData: {
sender: $("#chat-order-add #senderId").val(),
recipent: $("#chat-order-add #recipentId").val(),
order: $("#chat-order-add #orderId").val(),
},
crossDomain: false,
beforeSend: function (xhr, settings) {
$('#progress .progress-bar').css(
'width',
'0%'
);
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
var currentValue = '';
currentValue += file.id + ';';
//$("#documentSendIds").val(currentValue);
var htmlImg = '<p>' + file.name + '</p>';
var document_send = $(htmlImg).appendTo("#document-send-order");
});
},
fail: function (e) {
console.log(e);
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .progress-bar').css(
'width',
progress + '%'
);
}
}).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled');
});

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-26 11:58
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('chat', '0006_message_is_delete'),
]
operations = [
migrations.AlterField(
model_name='documents',
name='recipent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='recipent_documents', to=settings.AUTH_USER_MODEL),
),
]

@ -44,7 +44,7 @@ class Documents(models.Model):
order = models.ForeignKey(Order, related_name='documents', null=True, blank=True)
team = models.ForeignKey(Team, related_name='documents', null=True, blank=True)
sender = models.ForeignKey(User, related_name='sender_documents')
recipent = models.ForeignKey(User, related_name='recipent_documents')
recipent = models.ForeignKey(User, related_name='recipent_documents', null=True, blank=True)
def __str__(self):
return self.file.url

@ -0,0 +1,19 @@
import json
from django.http import HttpResponse
MIMEANY = '*/*'
MIMEJSON = 'application/json'
MIMETEXT = 'text/plain'
def response_mimetype(request):
can_json = MIMEJSON in request.META['HTTP_ACCEPT']
can_json |= MIMEANY in request.META['HTTP_ACCEPT']
return MIMEJSON if can_json else MIMETEXT
class JSONResponse(HttpResponse):
def __init__(self, obj='', json_opts=None, mimetype=MIMEJSON, *args, **kwargs):
json_opts = json_opts if isinstance(json_opts, dict) else {}
content = json.dumps(obj, **json_opts)
super().__init__(content, mimetype, *args, **kwargs)

@ -0,0 +1,33 @@
<div id="arbitration-add" class="modal fade" role="dialog">
<div class="modal-dialog" role="document" style="width:900px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×
</button>
<h4 class="modal-title">Оставить заявку в арбитраж</h4>
</div>
<form id="arbitration-add-form" method="POST">
<div class="modal-body">
<div style="height: 250px;">
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Ваш текст</p>
<textarea id="text-new" name="text"></textarea>
<input type="hidden" name="order" id="orderArbitrationId" >
<input type="hidden" name="user" value="{{ request.user.pk }}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть
</button>
<button type="button" id="order-arbitration-add" class="btn btn-primary">Отправить
</button>
</div>
</form>
</div>
</div>
</div>

@ -107,7 +107,7 @@
<div class="messageBlock box-sizing disTab">
<p>Заказы</p>
{% for order in orders %}
<div class="orderBlock box-sizing order-block"
<div class="orderBlock box-sizing order-block" data-project-id="{{ order.project.id }}"
id="orderBlock{{ order.id }}" data-recipent-id="{{ order.project.customer.pk }}" data-id="{{ order.id }}">
<span class="dimovChat"></span>
<p class="titleOB">{{ order }}</p>
@ -126,18 +126,19 @@
</div>
<div class="col-lg-6 commChat">
<div id="message-chat-order-space"></div>
<form id="chat-contractor-order">
<input type="hidden" id="orderId"/>
<input type="hidden" id="senderOrderId" value="{{ request.user.pk }}"/>
<input type="hidden" id="recipentOrderId"/>
<form id="chat-order-add">
<input type="hidden" id="orderId">
<input type="hidden" id="senderId" value="{{ request.user.pk }}"/>
<input type="hidden" id="recipentId">
<textarea id="chat" class="box-sizing"></textarea>
<div class="bunChat">
<div class="setChat box-sizing">
<p>Прикрепить файл</p>
<span>Не более 10 файлов с общим объемом 500мб</span>
<div class="setChat box-sizing upload">
<input type="file" name="file" id="upload-document-order"/>
<p>Прикрепить файл</p>
</div>
<a href="javascript:void(0)" id="order-chat-add-message">отправить</a>
<div id="document-send-order"></div>
<a href="#" id="order-chat-add-message">отправить</a>
</div>
</form>
@ -181,6 +182,13 @@
</a>
</div>
<!-- Review add -->
{% include 'review_add_modal.html' %}
<!-- -->
<!-- Arbitration add -->
{% include 'arbitration_modal.html' %}
<!-- -->
<div class="textAreaBlock2 box-sizing disTab">
<ul class="notes-block">
@ -260,10 +268,8 @@
<div class="progress-bar progress-bar-success"></div>
</div>
<div id="document-send">
</div>
<a href="javascript:void(0)" id="add-team-chat-message">отправить</a>
<div id="document-send"></div>
<a href="#" id="add-team-chat-message">отправить</a>
</div>
</form>
</div>
@ -291,19 +297,22 @@
<script type="text/javascript">
$(function () {
var currentChatUser = {{ request.user.pk }};
var socket = new SocketHandler();
var form = document.getElementById('message_form');
var csrftoken = getCookie('csrftoken');
setTimeout(function () {
$(".team-order-block").first().trigger('click');
}, 1000);
var url = '/work_sell/basic/';
var url = '/chat/create/';
//Загрузка документов
$('#upload-document-team').fileupload({
url: url,
formData: {
sender: $("#team-chat-form #senderId").val(),
recipent: $("#team-chat-form #recipentId").val(),
team: $("#team-chat-form #teamId").val(),
order: $("#team-chat-form #orderId").val(),
},
crossDomain: false,
beforeSend: function (xhr, settings) {
$('#progress .progress-bar').css(
@ -324,6 +333,9 @@
var document_send = $(htmlImg).appendTo("#document-send");
});
},
fail: function(e){
console.log(e);
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .progress-bar').css(
@ -424,12 +436,19 @@
var orderId = $(this).attr('data-id');
var recipentId = $(this).attr('data-recipent-id');
$("#chat-contractor-order #orderId").val(orderId);
var projectId = $(this).attr('data-project-id');
$("#chat-order-add #orderId").val(orderId);
$("#add-form-order-note #orderNote").val(orderId);
$("#orderArbitrationId").val(orderId);
$("#projectReviewId").val(projectId);
$("#chat-contractor-order #recipentOrderId").val(recipentId);
$("#chat-order-add #recipentId").val(recipentId);
$("#targetCustomerId").val(recipentId);
$("#add-form-order-note #recipentNote").val(recipentId);
var inbox = document.getElementById('message-chat-order-space');
inbox.innerHTML = '';
@ -544,15 +563,21 @@
}else{
stageWork += '<p>Этап ожидает завершения статуса от заказчика</p>';
}
stageWork += '<a href="#">Обратитьсяв арбитраж</a>';
if (data.secure) {
stageWork += '<a href="#" data-toggle="modal" data-target="#arbitration-add">Обратитьсяв арбитраж</a>';
}
$("#stagesWork").html(stageWork);
}else{
$("#completeWork").hide();
}
if(stagesCompleted.length == stagesResults.length){
if(stagesCompleted.length == stagesResults.length && stagesCompleted.length>0){
$("#leaveReview").show();
}
if (data.status == 'completed'){
$("#leaveReview").hide();
}
});
@ -699,6 +724,7 @@
});
$("#team-chat-form #chatText").val("");
$("#document-send").html("");
});
// Добавить сообщение для контакта
@ -720,27 +746,6 @@
$("#chat").val("");
});
$('#order-chat-add-message').on('click', function () {
var chatMessage = $("#chat-contractor-order #chat").val();
var recipentId = $("#chat-contractor-order #recipentOrderId").val();
var senderId = $("#chat-contractor-order #senderOrderId").val();
var orderId = $("#chat-contractor-order #orderId").val();
socket.add_contact_message({
"format_type": "add_message_order",
"data": {
"sender_id": senderId,
"recipent_id": recipentId,
"chat_message": chatMessage,
"order_id": orderId,
}
});
$("#chat-contractor-order #chat").val("");
});
});
</script>
{% endblock %}

@ -66,7 +66,7 @@
</div>
<form id="contact-chat-form">
<input type="hidden" value="{{ request.user.pk }}" name="senderId" id="senderId"/>
<input type="hidden" value="" name="recipentId" id="recipentId"/>
<input type="hidden" value="" name="recipentId" id="recipentId">
<textarea id="chat" name="chat_message" class="box-sizing"></textarea>
<div class="bunChat">
@ -124,26 +124,22 @@
</div>
<div class="col-lg-6 commChat">
<div id="message-chat-order-space">
</div>
<form id="chat-order-add" style="display:none;">
<div id="message-chat-order-space"></div>
<form id="chat-order-add">
<input type="hidden" name="senderId" id="senderId" value="{{ request.user.pk }}"/>
<input type="hidden" name="recipentId" id="recipentId" value=""/>
<input type="hidden" name="orderId" id="orderId" value=""/>
<input type="hidden" name="recipentId" id="recipentId">
<input type="hidden" name="orderId" id="orderId">
<textarea id="chat" class="box-sizing"></textarea>
<div class="bunChat">
<div class="setChat box-sizing">
<p>Прикрепить файл</p>
<span>
Не более 10 файлов с общим объемом 500мб
</span>
<div class="setChat box-sizing upload">
<input type="file" name="file" id="upload-document-order">
<p>Прикрепить файл</p>
</div>
<div id="document-send-order"></div>
<a href="#" id="order-chat-add-message">отправить</a>
</div>
</form>
</div>
<div class="col-lg-3 wrstepschat" id="order-stages-tab">
<p>Этапы работы</p>
@ -195,12 +191,14 @@
<a href="#">сохранить</a>
</div>
<!-- Review add -->
{% include 'review_add_modal.html' %}
<!-- -->
<!-- Arbitration add -->
{% include 'arbitration_modal.html' %}
<!-- -->
</div>
</div>
<!-- End block (chat order block) -->
@ -222,9 +220,7 @@
<script type="text/javascript">
$(function () {
var currentChatUser = {{ request.user.pk }};
var socket = new SocketHandler();
var form = document.getElementById('message_form');
var csrftoken = getCookie('csrftoken');
$("#reserve-button").on("click",function(e) {
e.preventDefault();
@ -355,12 +351,14 @@
});
if (statusNotAgreed) {
if(!data.secure) {
var orderSecureCheckbox = '';
var orderSecureCheckbox = '';
if(data.secure) {
orderSecureCheckbox = "checked";
}
htmlInbox += '<div class="box-sizing disTab">' +
'<div class="checkbox"><input name="secure" id="secureOrder"'+ orderSecureCheckbox +'type="checkbox" style="opacity:1">' +
'<div class="checkbox"><input name="secure" id="secureOrder" '+ orderSecureCheckbox +' type="checkbox" style="opacity:1">' +
'Перейти в режим безопасной сделки</div></div>';
}
htmlInbox +='<div class="textAreaBlock2 box-sizing disTab">' +
'<a href="#" data-sender-id="' + senderId +'" ' +
@ -384,6 +382,10 @@
stageWork += '<a href="#" class="closeStage" data-order-id="'+ orderId + '" data-sender-id="{{ request.user.pk }}"' +
' data-recipent-id="'+ recipentId + '" data-stage-id="'+ stage.id+'">Закрыть этап '+ stage.pos +'</a>';
}
if (data.secure){
stageWork += '<a href="#" data-toggle="modal" data-target="#arbitration-add">Обратитьсяв арбитраж</a>';
}
$("#stagesWork").html(stageWork);
}
@ -392,11 +394,17 @@
$("#reserveSpace").hide();
}
if(stagesCompleted.length == stagesResults.length){
if((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0)){
$("#leaveReview").show();
console.log("Все этапы завершены");
}else {
$("#leaveReview").hide();
}
if (data.status == 'completed'){
$("#leaveReview").hide();
}
});
}
@ -436,6 +444,29 @@
// Добавление этапов
$("#order-stages").on('click', "#addStagesForm", function (e) {
e.preventDefault();
var currentOrderId = $(this).attr('data-order-id');
var secureOrderEl = $("#secureOrder");
if(secureOrderEl.length > 0) {
var secOrderVal = false;
if (secureOrderEl.prop('checked')) {
secOrderVal = true;
}
$.ajax({
url: '/api/orders/' + currentOrderId + '/',
type: 'PATCH',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: {secure: secOrderVal},
dataType: 'json',
success: function (json){
},
error: function(e){
console.log(e);
}
});
}
$(".new-stages-form").each(function (i, v) {
$.ajax({
url: '/api/stages/',
@ -477,7 +508,7 @@
});
});
var currentOrderId = $(this).attr('data-order-id');
var currentRecipentId = $(this).attr('data-recipent-id');
var secureOrder = true
@ -553,6 +584,7 @@
var secureOrder = $(this).attr('data-secure-deal');
secureOrder = Boolean(secureOrder);
$("#chat-order-add #orderId").val(orderId);
$("#orderArbitrationId").val(orderId);
$("#projectReviewId").val(projectId);
$("#reserve-button").attr('data-order-id', orderId);
$("#targetContractorId").val(recipentId);
@ -642,50 +674,7 @@
$("#chat").val("");
});
// Добавление отзыва
$('#order-review-add').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var formData = $("#review-adds-form").serialize();
$.ajax({
url: '/api/reviews/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: formData,
dataType: 'json',
success: function (json) {
console.log("Успешно");
console.log(json);
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
$('#order-chat-add-message').on('click', function (e) {
e.preventDefault();
var chatMessage = $("#chat-order-add #chat").val();
var recipentId = $("#chat-order-add #recipentId").val();
var senderId = $("#chat-order-add #senderId").val();
var orderId = $("#chat-order-add #orderId").val();
socket.add_contact_message({
"format_type": "add_message_order",
"data": {
"sender_id": senderId,
"recipent_id": recipentId,
"chat_message": chatMessage,
"order_id": orderId,
}
});
$("#chat-order-add #chat").val("");
});
});
var userId = '{{ request.user.pk }}';

@ -25,21 +25,21 @@
<p>Ваш отзыв</p>
<textarea id="text-new" name="text"></textarea>
{% if request.user.is_customer %}
<input type="text" name="from_customer" value="{{ request.user.pk }}"/>
<input type="hidden" name="from_customer" value="{{ request.user.pk }}"/>
<input type="hidden" name="from_contractor">
<input type="hidden" name="from_team">
<input type="text" name="target_contractor" id="targetContractorId"/>
<input type="text" name="target_customer">
<input type="hidden" name="target_contractor" id="targetContractorId"/>
<input type="hidden" name="target_customer">
<input type="hidden" name="target_team">
{% else %}
<input type="hidden" name="from_customer"/>
<input type="hidden" name="from_contractor" value="{{ request.user.pk }}">
<input type="text" name="target_customer">
<input type="hidden" name="target_customer" id="targetCustomerId">
<input type="hidden" name="target_contractor">
<input type="hidden" name="target_team">
<input type="hidden" name="from_team">
{% endif %}
<input type="text" name="project" id="projectReviewId"/>
<input type="hidden" name="target_team">
<input type="hidden" name="project" id="projectReviewId"/>
</div>
</div>
</div>

@ -2,6 +2,7 @@ from django.conf import urls
from .views import (
ChatUserView,
DocumentCreateView,
messages_delete,
)
@ -10,4 +11,5 @@ app_name = 'chat'
urlpatterns = [
urls.url(r'^$', ChatUserView.as_view(), name='chat-user'),
urls.url(r'^messages_delete/$', messages_delete, name='chat-messages_delete'),
urls.url(r'^create/$', DocumentCreateView.as_view()),
]

@ -0,0 +1,15 @@
import mimetypes
from django.core.urlresolvers import reverse
def serialize(instance, file_attr='file'):
obj = getattr(instance, file_attr)
return {
'id': instance.id,
'url': obj.url,
'name': obj.name,
'type': mimetypes.guess_type(obj.path)[0] or 'image/png',
'size': obj.size,
'deleteUrl': '/delete',
'deleteType': 'DELETE',
}

@ -1,22 +1,42 @@
import json
from django.shortcuts import render
from django.conf import settings
from django.views.generic import View
from django.views.generic import View,CreateView
from django.http import HttpResponse, Http404
from django.db.models import Q
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Message
from .response import JSONResponse, response_mimetype
from .utils import serialize
from .models import Message, Documents
from projects.models import Order
from wallets.models import Transaction
from users.models import User, Team
class DocumentCreateView(CreateView):
model = Documents
fields = '__all__'
def form_valid(self, form):
# import code; code.interact(local=dict(globals(), **locals()))
self.object = form.save()
files = [serialize(self.object)]
data = {'files': files}
response = JSONResponse(data, mimetype=response_mimetype(self.request))
response['Content-Disposition'] = 'inline; filename=files.json'
return response
def form_invalid(self, form):
# import code; code.interact(local=dict(globals(), **locals()))
data = json.dumps(form.errors)
return HttpResponse(content=data, status=400, content_type='application/json')
class ChatUserView(LoginRequiredMixin, View):
template_name = ''
def get(self, request, *args, **kwargs):
print(request.path)
# import code; code.interact(local=dict(globals(), **locals()))
user_id = request.GET.get('user_id',None)
if request.user.is_customer():

@ -13,6 +13,7 @@ from .models import (
ProjectFile,
Realty,
Stage,
Arbitration,
)
@ -32,6 +33,10 @@ class ProjectAdmin(admin.ModelAdmin):
form = ProjectAdminForm
class OrderAdmin(admin.ModelAdmin):
list_display = ('project', 'status', 'secure',)
class StageAdmin(admin.ModelAdmin):
list_display = ('name','status','pos','order','is_paid',)
@ -40,10 +45,11 @@ admin.site.register(Answer)
admin.site.register(Portfolio)
admin.site.register(PortfolioPhoto)
admin.site.register(Realty)
admin.site.register(Order)
admin.site.register(Order, OrderAdmin)
admin.site.register(Candidate)
admin.site.register(Stage, StageAdmin)
admin.site.register(BuildingClassfication)
admin.site.register(ConstructionType)
admin.site.register(Project, ProjectAdmin)
admin.site.register(ProjectFile)
admin.site.register(Arbitration)

@ -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:

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-26 09:52
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('projects', '0016_merge'),
]
operations = [
migrations.CreateModel(
name='Arbitration',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
('created', models.DateTimeField(default=django.utils.timezone.now)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.Order')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Арбитраж',
'verbose_name_plural': 'Арбитраж',
},
),
]

@ -1,5 +1,6 @@
from django.utils import timezone
class LastAccessMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():

@ -220,6 +220,24 @@ class Order(models.Model):
verbose_name_plural = 'Заказы'
class Arbitration(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
order = models.ForeignKey(Order)
created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.user.username
class Meta:
verbose_name = 'Арбитраж'
verbose_name_plural = 'Арбитраж'
unique_together = (
('user', 'order'),
)
STATUSES = (
('not_agreed','Не согласован'),
('in_process','В процессе'),
@ -270,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(default=timezone.now, 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)
@ -280,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

@ -84,7 +84,7 @@ class StageSerializer(ModelSerializer):
class OrderSerializer(ModelSerializer):
stages = StageSerializer(many=True)
stages = StageSerializer(many=True, read_only=True)
class Meta:
model = Order

@ -544,7 +544,11 @@
</div>
<div class="col-lg-3 retts">
{% ratings_widget answer.author.pk 'restList2' %}
{% if answer.author|class_name == 'User' %}
{% ratings_widget answer.author.pk 'restList2' %}
{% elif answer.author|class_name == 'Team'%}
{% ratings_team_widget answer.author.pk 'restList2' %}
{% endif %}
{% if answer.author|class_name == 'User' and answer.author.cro %}
<div class="sroUser sroExecutor">

@ -21,6 +21,7 @@ from .views import (
RejectProjectAnswerView,
sort_candidates,
PortfolioDetail,
ArbitrationCreateView,
)
app_name = 'projects'
@ -41,6 +42,7 @@ urlpatterns = [
urls.url(r'^reject-project-answer/(?P<pk>\d+)/$', RejectProjectAnswerView.as_view(), name='reject-project-answer'),
urls.url(r'^portfolio/create/$', contractor_portfolio_create, name='contractor-portfolio-create'),
urls.url(r'^arbitration/create/$', ArbitrationCreateView.as_view(), name='arbitration-create'),
urls.url(r'^portfolio/(?P<pk>\d+)/?$', PortfolioDetail.as_view(), name='contractor-portfolio-detail'),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'),

@ -6,7 +6,7 @@ from django.core.files.base import ContentFile
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Q, F
from django.http import HttpResponseForbidden, HttpResponseRedirect, HttpResponse, Http404
from django.http import HttpResponseForbidden, JsonResponse, HttpResponseRedirect, HttpResponse, Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView, CreateView, DeleteView, View, UpdateView, TemplateView, FormView
from hitcount.models import HitCount
@ -18,7 +18,7 @@ import pydash as _; _.map = _.map_; _.filter = _.filter_
import re
from .mixins import LastAccessMixin
from .models import Project, ProjectFile, Portfolio, PortfolioPhoto, Candidate, Answer, AnswerFile, AnswerMessage, Realty, Order
from .models import Arbitration, Project, ProjectFile, Portfolio, PortfolioPhoto, Candidate, Answer, AnswerFile, AnswerMessage, Realty, Order
from archilance import util
from archilance.mixins import BaseMixin
from users.models import User, Team
@ -658,7 +658,7 @@ class OfferOrderView(View):
order.team = answer.author
order.save()
redirect_url = reverse('chat:chat-user') + '?order_id=' + str(order.pk)
redirect_url = reverse('chat:chat-user') + '#order' + str(order.pk)
return HttpResponseRedirect(redirect_url)
@ -725,4 +725,26 @@ class PortfolioDetail(DetailView):
template_name = 'portfolio_detail.html'
class ArbitrationCreateView(CreateView):
model = Arbitration
fields = (
'order',
'user',
'text',
)
def form_valid(self, form):
if self.request.is_ajax():
self.object = form.save()
data = {
'status': 'ok',
}
return JsonResponse(data)
return super().form_valid(form)
def form_invalid(self, form):
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
return super().form_invalid(form)
# import code; code.interact(local=dict(globals(), **locals()))

@ -41,10 +41,11 @@ def ratings_widget(context, user_id, class_name=None):
@register.inclusion_tag("templatetags/ratings_widget.html", takes_context=True)
def ratings_team_widget(context, team_id):
def ratings_team_widget(context, team_id, class_name=None):
ratings = Team.objects.get(pk=team_id).rating
return {
'ratings': ratings,
'class_name': class_name,
'deals': Order.objects.filter(secure=True, team_id=team_id, status=1).count(),
'reviews_n': Review.objects.filter(target_team_id=team_id, type='neutral').count(),
'reviews_m': Review.objects.filter(target_team_id=team_id, type='negative').count(),

@ -0,0 +1 @@
default_app_config = 'reviews.apps.ReviewConfig'

@ -2,4 +2,7 @@ from django.apps import AppConfig
class ReviewConfig(AppConfig):
name = 'review'
name = 'reviews'
def ready(self):
import reviews.signals

@ -0,0 +1,31 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from users.models import User, Team
from ratings.models import HistoryRating
from .models import Review
@receiver(post_save, sender=Review)
def add_rating_review(sender, instance, created, **kwargs):
hs_rating = HistoryRating()
if instance.target_team:
hs_rating.team = instance.target_team
elif instance.target_contractor or instance.target_customer:
hs_rating.user = instance.target_contractor or instance.target_customer
if instance.type == 'positive':
hs_rating.rating = 1
elif instance.type == 'negative':
hs_rating.rating = -1
else:
hs_rating = 0
hs_rating.description = 'Изменения рейтинга после отзыва'
hs_rating.save()
count_reviews = Review.objects.filter(project=instance.project).count()
if count_reviews == 2:
order = instance.project.order
order.status = 'completed'
order.save()

@ -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>

@ -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 %}

@ -16,8 +16,8 @@ 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(default=timezone.now, 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)
@ -34,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