#ARC-18 Add arbitration add

remotes/origin/PR-39
Mukhtar 10 years ago
parent f7b25a91c4
commit 7ce32488f4
  1. 1
      api/views.py
  2. 155
      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. 89
      chat/templates/chat_contractor.html
  8. 117
      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. 33
      projects/migrations/0017_arbitration.py
  15. 1
      projects/mixins.py
  16. 18
      projects/models.py
  17. 2
      projects/serializers.py
  18. 4
      projects/templates/project_detail.html
  19. 2
      projects/urls.py
  20. 28
      projects/views.py
  21. 3
      ratings/templatetags/specializtions_tags.py
  22. 1
      reviews/__init__.py
  23. 5
      reviews/apps.py
  24. 31
      reviews/signals.py

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

@ -1,6 +1,6 @@
var SocketHandler = function () { var SocketHandler = function () {
domain = domain.replace(':' + port, ''); domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + '/chat/' + userId + '/'; var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var sock = new WebSocket(url); var sock = new WebSocket(url);
var intervalId; var intervalId;
sock.onopen = function () { sock.onopen = function () {
@ -21,7 +21,7 @@ var SocketHandler = function () {
inbox = document.getElementById('message-chat-team-space'); inbox = document.getElementById('message-chat-team-space');
} else if (message.answer_type == 'approve_stages') { } else if (message.answer_type == 'approve_stages') {
var resOrderId = message.order_id; var resOrderId = message.order_id;
$.jGrowl(message.msg,{ $.jGrowl(message.msg, {
life: 4000 life: 4000
}); });
setTimeout(function () { setTimeout(function () {
@ -34,12 +34,12 @@ var SocketHandler = function () {
var classMessage = 'youChat'; var classMessage = 'youChat';
var senderName = 'Вы'; var senderName = 'Вы';
var timeMessage = message.msg_time; var timeMessage = message.msg_time;
if (message.sender_id != userId){ if (message.sender_id != userId) {
senderName = message.sender_name; senderName = message.sender_name;
classMessage = ''; classMessage = '';
} }
inbox.innerHTML += '<div class="col-lg-12 insetCommChat '+ classMessage +'"><div class="topCommChat">' + inbox.innerHTML += '<div class="col-lg-12 insetCommChat ' + classMessage + '"><div class="topCommChat">' +
'<p class="nameCommChat">'+ senderName +'</p> <span>' + timeMessage + '</span></div>' + '<p class="nameCommChat">' + senderName + '</p> <span>' + timeMessage + '</span></div>' +
'<p class="textCommChat">' + textMessage + '</p></div>'; '<p class="textCommChat">' + textMessage + '</p></div>';
} }
@ -98,17 +98,32 @@ function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
} }
var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken');
$(function () { $(function () {
var currentHash = URI(location.href).hash();
console.log(currentHash);
if (currentHash.indexOf("#order") == 0) {
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 () { setTimeout(function () {
$(".user-block").first().trigger('click'); $(".user-block").first().trigger('click');
}, 10); }, 10);
setTimeout(function () { setTimeout(function () {
$(".order-block").last().trigger('click'); $(".order-block").last().trigger('click');
$("a[href='#tab2']").trigger('click');
}, 100); }, 100);
}
$('.deleteMess').on('click', function (e) { $('.deleteMess').on('click', function (e) {
@ -123,10 +138,10 @@ $(function () {
beforeSend: function (xhr) { beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
}, },
data: {'sender_id':senderId, 'recipent_id': recipentId}, data: {'sender_id': senderId, 'recipent_id': recipentId},
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
if (json.status == 'ok'){ if (json.status == 'ok') {
_this.parent().remove(); _this.parent().remove();
$("#message-chat-space").html(""); $("#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) order = models.ForeignKey(Order, related_name='documents', null=True, blank=True)
team = models.ForeignKey(Team, 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') 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): def __str__(self):
return self.file.url 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"> <div class="messageBlock box-sizing disTab">
<p>Заказы</p> <p>Заказы</p>
{% for order in orders %} {% 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 }}"> id="orderBlock{{ order.id }}" data-recipent-id="{{ order.project.customer.pk }}" data-id="{{ order.id }}">
<span class="dimovChat"></span> <span class="dimovChat"></span>
<p class="titleOB">{{ order }}</p> <p class="titleOB">{{ order }}</p>
@ -126,18 +126,19 @@
</div> </div>
<div class="col-lg-6 commChat"> <div class="col-lg-6 commChat">
<div id="message-chat-order-space"></div> <div id="message-chat-order-space"></div>
<form id="chat-contractor-order"> <form id="chat-order-add">
<input type="hidden" id="orderId"/> <input type="hidden" id="orderId">
<input type="hidden" id="senderOrderId" value="{{ request.user.pk }}"/> <input type="hidden" id="senderId" value="{{ request.user.pk }}"/>
<input type="hidden" id="recipentOrderId"/> <input type="hidden" id="recipentId">
<textarea id="chat" class="box-sizing"></textarea> <textarea id="chat" class="box-sizing"></textarea>
<div class="bunChat"> <div class="bunChat">
<div class="setChat box-sizing"> <div class="setChat box-sizing upload">
<input type="file" name="file" id="upload-document-order"/>
<p>Прикрепить файл</p> <p>Прикрепить файл</p>
<span>Не более 10 файлов с общим объемом 500мб</span>
</div> </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> </div>
</form> </form>
@ -181,6 +182,13 @@
</a> </a>
</div> </div>
<!-- Review add -->
{% include 'review_add_modal.html' %}
<!-- -->
<!-- Arbitration add -->
{% include 'arbitration_modal.html' %}
<!-- -->
<div class="textAreaBlock2 box-sizing disTab"> <div class="textAreaBlock2 box-sizing disTab">
<ul class="notes-block"> <ul class="notes-block">
@ -260,10 +268,8 @@
<div class="progress-bar progress-bar-success"></div> <div class="progress-bar progress-bar-success"></div>
</div> </div>
<div id="document-send"> <div id="document-send"></div>
<a href="#" id="add-team-chat-message">отправить</a>
</div>
<a href="javascript:void(0)" id="add-team-chat-message">отправить</a>
</div> </div>
</form> </form>
</div> </div>
@ -291,19 +297,22 @@
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
var currentChatUser = {{ request.user.pk }}; var currentChatUser = {{ request.user.pk }};
var socket = new SocketHandler();
var form = document.getElementById('message_form'); var form = document.getElementById('message_form');
var csrftoken = getCookie('csrftoken');
setTimeout(function () { setTimeout(function () {
$(".team-order-block").first().trigger('click'); $(".team-order-block").first().trigger('click');
}, 1000); }, 1000);
var url = '/chat/create/';
var url = '/work_sell/basic/';
//Загрузка документов //Загрузка документов
$('#upload-document-team').fileupload({ $('#upload-document-team').fileupload({
url: url, 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, crossDomain: false,
beforeSend: function (xhr, settings) { beforeSend: function (xhr, settings) {
$('#progress .progress-bar').css( $('#progress .progress-bar').css(
@ -324,6 +333,9 @@
var document_send = $(htmlImg).appendTo("#document-send"); var document_send = $(htmlImg).appendTo("#document-send");
}); });
}, },
fail: function(e){
console.log(e);
},
progressall: function (e, data) { progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10); var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .progress-bar').css( $('#progress .progress-bar').css(
@ -424,12 +436,19 @@
var orderId = $(this).attr('data-id'); var orderId = $(this).attr('data-id');
var recipentId = $(this).attr('data-recipent-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); $("#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); $("#add-form-order-note #recipentNote").val(recipentId);
var inbox = document.getElementById('message-chat-order-space'); var inbox = document.getElementById('message-chat-order-space');
inbox.innerHTML = ''; inbox.innerHTML = '';
@ -544,15 +563,21 @@
}else{ }else{
stageWork += '<p>Этап ожидает завершения статуса от заказчика</p>'; stageWork += '<p>Этап ожидает завершения статуса от заказчика</p>';
} }
stageWork += '<a href="#">Обратитьсяв арбитраж</a>'; if (data.secure) {
stageWork += '<a href="#" data-toggle="modal" data-target="#arbitration-add">Обратитьсяв арбитраж</a>';
}
$("#stagesWork").html(stageWork); $("#stagesWork").html(stageWork);
}else{ }else{
$("#completeWork").hide(); $("#completeWork").hide();
} }
if(stagesCompleted.length == stagesResults.length){ if(stagesCompleted.length == stagesResults.length && stagesCompleted.length>0){
$("#leaveReview").show(); $("#leaveReview").show();
} }
if (data.status == 'completed'){
$("#leaveReview").hide();
}
}); });
@ -699,6 +724,7 @@
}); });
$("#team-chat-form #chatText").val(""); $("#team-chat-form #chatText").val("");
$("#document-send").html("");
}); });
// Добавить сообщение для контакта // Добавить сообщение для контакта
@ -720,27 +746,6 @@
$("#chat").val(""); $("#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> </script>
{% endblock %} {% endblock %}

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

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

@ -2,6 +2,7 @@ from django.conf import urls
from .views import ( from .views import (
ChatUserView, ChatUserView,
DocumentCreateView,
messages_delete, messages_delete,
) )
@ -10,4 +11,5 @@ app_name = 'chat'
urlpatterns = [ urlpatterns = [
urls.url(r'^$', ChatUserView.as_view(), name='chat-user'), urls.url(r'^$', ChatUserView.as_view(), name='chat-user'),
urls.url(r'^messages_delete/$', messages_delete, name='chat-messages_delete'), 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 import json
from django.shortcuts import render from django.shortcuts import render
from django.conf import settings 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.http import HttpResponse, Http404
from django.db.models import Q from django.db.models import Q
from django.contrib.auth.mixins import LoginRequiredMixin 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 projects.models import Order
from wallets.models import Transaction from wallets.models import Transaction
from users.models import User, Team 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): class ChatUserView(LoginRequiredMixin, View):
template_name = '' template_name = ''
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
print(request.path)
# import code; code.interact(local=dict(globals(), **locals())) # import code; code.interact(local=dict(globals(), **locals()))
user_id = request.GET.get('user_id',None) user_id = request.GET.get('user_id',None)
if request.user.is_customer(): if request.user.is_customer():

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

@ -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 from django.utils import timezone
class LastAccessMixin(object): class LastAccessMixin(object):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated(): if request.user.is_authenticated():

@ -219,6 +219,24 @@ class Order(models.Model):
verbose_name_plural = 'Заказы' 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 = ( STATUSES = (
('not_agreed','Не согласован'), ('not_agreed','Не согласован'),
('in_process','В процессе'), ('in_process','В процессе'),

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

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

@ -21,6 +21,7 @@ from .views import (
RejectProjectAnswerView, RejectProjectAnswerView,
sort_candidates, sort_candidates,
PortfolioDetail, PortfolioDetail,
ArbitrationCreateView,
) )
app_name = 'projects' 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'^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'^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+)/?$', PortfolioDetail.as_view(), name='contractor-portfolio-detail'),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'), 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.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Q, F 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.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView, CreateView, DeleteView, View, UpdateView, TemplateView, FormView from django.views.generic import ListView, DetailView, CreateView, DeleteView, View, UpdateView, TemplateView, FormView
from hitcount.models import HitCount from hitcount.models import HitCount
@ -18,7 +18,7 @@ import pydash as _; _.map = _.map_; _.filter = _.filter_
import re import re
from .mixins import LastAccessMixin 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 import util
from archilance.mixins import BaseMixin from archilance.mixins import BaseMixin
from users.models import User, Team from users.models import User, Team
@ -658,7 +658,7 @@ class OfferOrderView(View):
order.team = answer.author order.team = answer.author
order.save() 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) return HttpResponseRedirect(redirect_url)
@ -725,4 +725,26 @@ class PortfolioDetail(DetailView):
template_name = 'portfolio_detail.html' 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())) # 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) @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 ratings = Team.objects.get(pk=team_id).rating
return { return {
'ratings': ratings, 'ratings': ratings,
'class_name': class_name,
'deals': Order.objects.filter(secure=True, team_id=team_id, status=1).count(), '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_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(), '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): 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()
Loading…
Cancel
Save