remotes/origin/PR-39
ArturBaybulatov 9 years ago
commit 61c05d93e2
  1. 8
      assets/css/main.css
  2. 86
      assets/js/chat.js
  3. 25
      assets/js/chat_contractor.js
  4. 142
      assets/js/chat_customer.js
  5. 1
      chat/serializers.py
  6. 1
      chat/templates/chat_contractor.html
  7. 20
      projects/models.py
  8. 10
      projects/serializers.py
  9. 2
      templates/partials/base.html
  10. 5
      users/models.py
  11. 237
      users/templates/contractor_office.html
  12. 26
      users/templates/customer_profile_reviews.html
  13. 29
      users/templatetags/user_tags.py

@ -237,14 +237,14 @@ ul li {
} }
.changeBlock1 > a, .changeBlock1 > a:link, .changeBlock1 > a:visited { .changeBlock1 > a, .changeBlock1 > a:link, .changeBlock1 > a:visited {
background: url('../img/button1.png') no-repeat 25px , white; background: url('../img/button1.png') no-repeat 25px , black;
color: black; color: white;
padding: 24px 26px 20px 72px; padding: 24px 26px 20px 72px;
} }
.changeBlock2 > a, .changeBlock2 > a:link, .changeBlock2 > a:visited { .changeBlock2 > a, .changeBlock2 > a:link, .changeBlock2 > a:visited {
background: url('../img/button2.png') no-repeat 27px, black; background: url('../img/button2.png') no-repeat 27px, white;
color: white; color: black;
padding: 24px 26px 20px 72px; padding: 24px 26px 20px 72px;
} }

@ -1,6 +1,5 @@
window.confirm = function (message, callback, caption) { window.confirm = function (message, callback, caption) {
caption = caption || '' caption = caption || ''
$(document.createElement('div')).attr({ $(document.createElement('div')).attr({
title: caption, title: caption,
'class': 'dialog' 'class': 'dialog'
@ -20,7 +19,6 @@ window.confirm = function (message, callback, caption) {
callback() callback()
return true; return true;
} }
}, },
close: function () { close: function () {
$(this).remove(); $(this).remove();
@ -31,7 +29,7 @@ window.confirm = function (message, callback, caption) {
var SocketHandler = function () { var SocketHandler = function () {
domain = domain.replace(':' + port, ''); domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + ':8888/chat/' + userId + '/'; var url = 'ws://' + domain + '/chat/' + userId + '/';
var sock = new WebSocket(url); var sock = new WebSocket(url);
var intervalId; var intervalId;
sock.onopen = function () { sock.onopen = function () {
@ -109,22 +107,6 @@ var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken'); var csrftoken = getCookie('csrftoken');
function test_dialog(message){
var resStatus;
$("#dialog_delete .modal-title").html(message);
$("#dialog_delete").modal('show');
$("#btnYes").click(function (e) {
$("#dialog_delete").modal('hide');
resStatus = true;
});
$("#btnNot").click(function (e) {
$("#dialog_delete").modal('hide');
resStatus = false;
});
return resStatus;
}
$(function () { $(function () {
function dialog (message, yesCallback, notCallback) { function dialog (message, yesCallback, notCallback) {
$("#dialog_delete .modal-title").html(message); $("#dialog_delete .modal-title").html(message);
@ -682,33 +664,6 @@ function dialog (message, yesCallback, notCallback) {
}); });
// Добавление отзыва
$('#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) { $('#order-arbitration-add').on('click', function (e) {
e.preventDefault(); e.preventDefault();
@ -761,7 +716,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json', dataType: 'json',
done: function (e, data) { done: function (e, data) {
$.each(data.result.files, function (index, file) { $.each(data.result.files, function (index, file) {
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a><div class="remove-document" data-id="'+ file.id+'" style="right:-10px;"></div><br />'; var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send-order"); var document_send = $(htmlImg).appendTo("#document-send-order");
}); });
}, },
@ -802,7 +758,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json', dataType: 'json',
done: function (e, data) { done: function (e, data) {
$.each(data.result.files, function (index, file) { $.each(data.result.files, function (index, file) {
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>'; var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send-contact"); var document_send = $(htmlImg).appendTo("#document-send-contact");
}); });
}, },
@ -824,6 +781,39 @@ function dialog (message, yesCallback, notCallback) {
var relatedType = related.attr('data-review-type'); var relatedType = related.attr('data-review-type');
$('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true); $('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true);
}); });
// Добавление отзыва
$('#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(json);
$("#review-add").modal('hide');
//socket.send_stages_approve({
// "format_type": "approve_stages",
// "data": {
// "sender_id": userId,
// "recipent_id": '',
// "order_id": '',
// "msg": "Отзыв добавлен",
// }
//});
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
}); });

@ -48,7 +48,8 @@ $(function () {
var currentValue = $("#documentSendIds").val(); var currentValue = $("#documentSendIds").val();
currentValue += file.id + ';'; currentValue += file.id + ';';
$("#documentSendIds").val(currentValue); $("#documentSendIds").val(currentValue);
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>'; var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send"); var document_send = $(htmlImg).appendTo("#document-send");
}); });
}, },
@ -82,8 +83,7 @@ $(function () {
beforeSend: function (xhr) { beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
}, },
//data: "status=in_process", data: "status=in_process",
data: "status=send_approve",
dataType: 'json', dataType: 'json',
done: function (json) { done: function (json) {
console.log(json); console.log(json);
@ -127,8 +127,8 @@ $(function () {
beforeSend: function (xhr) { beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
}, },
//data: "status=cancel_approve", data: "status=cancel_approve",
data: "status=send_approve", //data: "status=send_approve",
dataType: 'json', dataType: 'json',
done: function (json) { done: function (json) {
console.log(json); console.log(json);
@ -438,6 +438,7 @@ $(function () {
var stagesCompleted = []; var stagesCompleted = [];
if (stagesResults.length > 0) { if (stagesResults.length > 0) {
$.each(stagesResults, function (i, v) { $.each(stagesResults, function (i, v) {
console.log(v.status);
if (v.status == "completed") { if (v.status == "completed") {
stagesCompleted.push(v); stagesCompleted.push(v);
} }
@ -488,7 +489,7 @@ $(function () {
'<p class="titleNumStepp"><span>Этап ' + v.pos + '</span>' + v.name + '</p>' + '<p class="titleNumStepp"><span>Этап ' + v.pos + '</span>' + v.name + '</p>' +
'<p class="textNumStepp">Результаты этапа:' + v.result + '</p><div>' + '<p class="textNumStepp">Результаты этапа:' + v.result + '</p><div>' +
'<p> Срок до ' + v.term + '</p><span>' + v.cost + ' <i class="fa fa-rub"></i></span>' + '<p> Срок до ' + v.term + '</p><span>' + v.cost + ' <i class="fa fa-rub"></i></span>' +
'<p>Cрок заказа рассчитывается с момента резервирования средств</p>' + '<p>Cрок этапа рассчитывается с момента резервирования средств</p>' +
'</div><div><p>' + statusName + '</p></div></div></div>'; '</div><div><p>' + statusName + '</p></div></div></div>';
}); });
@ -534,13 +535,11 @@ $(function () {
$("#completeWork").hide(); $("#completeWork").hide();
} }
if (stagesCompleted.length == stagesResults.length && stagesCompleted.length > 0) { if ((stagesCompleted.length == stagesResults.length) && stagesCompleted.length > 0 && !data.has_user_review) {
$("#leaveReview").show(); $("#leaveReview").show();
} }else{
$("#leaveReview").hide();
if (data.status == 'completed') { }
$("#leaveReview").hide();
}
}); });

@ -99,12 +99,12 @@ $(function () {
if (stageCount == 0) { if (stageCount == 0) {
htmlInboxStage += '<div class="numberStepp box-sizing" id="stage1">' + htmlInboxStage += '<div class="numberStepp box-sizing" id="stage1">' +
'<p>Этап <span class="stage-span-id">1</span></p><form class="new-stages-form" id="stage-form">' + '<p>Этап <span class="stage-span-id">1</span></p><form class="new-stages-form" id="stage-form">' +
'<label for="">Название</label><input class="form-control" name="name" type="text">' + '<label>Название</label><input class="form-control" name="name" type="text"><p class="error error-name"></p>' +
'<label for="">Цена</label><input class="form-control" name="cost" type="text">' + '<label>Цена</label><input class="form-control" name="cost" type="text"><p class="error error-cost"></p>' +
'<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' + '<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' + '<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" name="term" type="text">' + '<label>Срок</label><input class="term-picker form-control datepicker" name="term" type="text"><p class="error error-term"></p>' +
'<label for="">Результат</label><input class="form-control" name="result" type="text">' + '<label>Результат</label><input class="form-control" name="result" type="text"><p class="error error-result"></p>' +
'<input class="form-control" name="pos" value="1" type="hidden">' + '<input class="form-control" name="pos" value="1" type="hidden">' +
'</form></div>'; '</form></div>';
} }
@ -135,12 +135,13 @@ $(function () {
if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') { if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') {
htmlInbox += '<div class="numberStepp box-sizing">' + htmlInbox += '<div class="numberStepp box-sizing">' +
'<p>Этап</p><form class="update-stages-form" data-stage-id="' + v.id + '" id="stage-form-' + v.pos + '">' + '<p>Этап</p><form class="update-stages-form" data-stage-id="' + v.id + '" id="stage-form-' + v.pos + '">' +
'<label for="">Название</label><input class="form-control" type="text" name="name" value="' + v.name + '">' + '<label>Название</label><input class="form-control" type="text" name="name" value="' + v.name + '"><p class="error error-name"></p>' +
'<label for="">Цена</label><input class="form-control" type="text" name="cost" value="' + v.cost + '" >' + '<label>Цена</label><input class="form-control" type="text" name="cost" value="' + v.cost + '" ><p class="error error-cost"></p>' +
'<label>Позиция</label><input class="form-control" name="pos" type="text" value="' + v.pos + '" ><p class="error error-pos"></p>' +
'<input class="form-control orderStagesInput" type="hidden" name="order" value="' + v.order + '">' + '<input class="form-control orderStagesInput" type="hidden" name="order" value="' + v.order + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' + '<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" type="text" name="term" value="' + v.term + '" />' + '<label>Срок</label><input class="term-picker form-control datepicker" type="text" name="term" value="' + v.term + '" ><p class="error error-term"></p>' +
'<label for="">Результат</label><input class="form-control" type="text" name="result" value="' + v.result + '" >' + '<label>Результат</label><input class="form-control" type="text" name="result" value="' + v.result + '" ><p class="error error-result"></p>' +
'</form></div>'; '</form></div>';
} else { } else {
statusNotAgreed = false; statusNotAgreed = false;
@ -226,12 +227,15 @@ $(function () {
$("#reserveSpace").hide(); $("#reserveSpace").hide();
} }
if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) { if (!data.has_user_review) {
$("#leaveReview").show(); if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) {
$("#leaveReview").show();
} else {
$("#leaveReview").hide();
}
} else { } else {
$("#leaveReview").hide(); $("#leaveReview").hide();
} }
$(".stages-paid").html(stagesReservedHtml); $(".stages-paid").html(stagesReservedHtml);
}); });
@ -297,80 +301,75 @@ $(function () {
var callbacks = []; var callbacks = [];
$(".new-stages-form").each(function (i, v) { $(".new-stages-form").each(function (i, v) {
var _this = $(this); var _this = $(this);
callbacks.push($.ajax({ $.ajax({
url: '/api/stages/', url: '/api/stages/',
type: 'POST', type: 'POST',
beforeSend: function (xhr) { beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
}, },
data: $(this).serialize(), data: $(this).serialize(),
dataType: 'json', dataType: 'json'
done: function (json) { })
.done(function (json) {
_this.removeClass('new-stages-form').addClass('update-stages-form'); _this.removeClass('new-stages-form').addClass('update-stages-form');
_this.find('.error').html("");
console.log(json); console.log(json);
}, })
fail: function (xhr, errorMsg,error) { .fail(function (xhr, errorMsg, error) {
$.each(xhr.responseJSON, function(i,v){ console.log(xhr);
$.each(xhr.responseJSON, function (i, v) {
_this.find('.error-' + i).html(v).css('color', 'red');
console.log(v); console.log(v);
console.log(i); console.log(i);
}); });
}, });
}));
}); });
//$.when(callbacks).then(function(x){
// console.log(x);
// $.each(x,function(i,v){
// console.log(v);
// });
// alert("Запросы успешно выполнились");
//}, function(){
// alert("Произошла ошибка в запросах");
//});
$(".update-stages-form").each(function (i, v) { $(".update-stages-form").each(function (i, v) {
var _this = $(this);
var currentStageId = parseInt($(this).attr('data-stage-id')); var currentStageId = parseInt($(this).attr('data-stage-id'));
$.ajax({ $.ajax({
url: '/api/stages/' + currentStageId + '/', url: '/api/stages/' + currentStageId + '/',
type: 'PUT', type: 'PUT',
beforeSend: function (xhr) { beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
}, },
data: $(this).serialize(), data: $(this).serialize(),
dataType: 'json', dataType: 'json'
success: function (json) { })
console.log(json); .done(function (json) {
}, _this.find('.error').html("");
error: function (e) { })
console.log('error'); .fail(function (xhr, errorMsg, error) {
console.log(e); $.each(xhr.responseJSON, function (i, v) {
} _this.find('.error-' + i).html(v).css('color', 'red');
}); console.log(v);
console.log(i);
});
});
}); });
var currentRecipentId = $(this).attr('data-recipent-id'); var currentRecipentId = $(this).attr('data-recipent-id');
var secureOrder = true var secureOrder = true
//socket.send_stages_approve({ socket.send_stages_approve({
// "format_type": "approve_stages", "format_type": "approve_stages",
// "data": { "data": {
// "sender_id": userId, "sender_id": userId,
// "recipent_id": currentRecipentId, "recipent_id": currentRecipentId,
// "order_id": currentOrderId, "order_id": currentOrderId,
// "msg": "Этапы для заказа " + currentOrderId + " изменены", "msg": "Этапы для заказа " + currentOrderId + " изменены",
// } }
//}); });
}); });
//Изменение счетчика //Изменение счетчика
$('#order-stages-tab').on('change', '#countStage', function () { $('#order-stages-tab').on('change', '#countStage', function () {
var countStage = parseInt($(this).val()); var countStage = parseInt($(this).val());
var currentCountStage = $("#order-stages .numberStepp").length; var currentCountStage = $("#order-stages .numberStepp").length;
@ -385,13 +384,13 @@ $(function () {
var orderId = lastFormStage.find('.orderStagesInput').val(); var orderId = lastFormStage.find('.orderStagesInput').val();
var addFormTemplate = '<div class="numberStepp box-sizing" id="stage1">' + var addFormTemplate = '<div class="numberStepp box-sizing" id="stage1">' +
'<p>Этап <span class="stage-span-id">' + pos + '</span></p><form class="new-stages-form" id="stage-form">' + '<p>Этап <span class="stage-span-id">' + pos + '</span></p><form class="new-stages-form" id="stage-form">' +
'<label for="">Название</label><input class="form-control" name="name" type="text">' + '<label>Название</label><input class="form-control" name="name" type="text"><p class="error error-name"></p>' +
'<label for="">Цена</label><input class="form-control" name="cost" type="text">' + '<label>Цена</label><input class="form-control" name="cost" type="text"><p class="error error-cost"></p>' +
'<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' + '<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' + '<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" name="term" type="text">' + '<label>Срок</label><input class="term-picker form-control datepicker" name="term" type="text"><p class="error error-term"></p>' +
'<label for="">Результат</label><input class="form-control" name="result" type="text">' + '<label>Результат</label><input class="form-control" name="result" type="text"><p class="error error-result"></p>' +
'<input class="form-control" name="pos" value="' + pos + '" type="hidden"></form></div>'; '<input class="form-control" name="pos" value="' + pos + '" type="text"></form></div>';
lastFormStage.after(addFormTemplate); lastFormStage.after(addFormTemplate);
} }
@ -409,7 +408,7 @@ $(function () {
} }
}); });
// Для заказов все вытащить // Для заказов все вытащить
$('.order-block').on('click', function () { $('.order-block').on('click', function () {
$("#chat-order-add").css("display", "block"); $("#chat-order-add").css("display", "block");
$("#formsetStage").css("display", "block"); $("#formsetStage").css("display", "block");
@ -465,7 +464,7 @@ $(function () {
}); });
$.ajax({ $.ajax({
url: '/api/users/{{ request.user.pk }}/', url: '/api/users/' + userId + '/',
type: 'GET', type: 'GET',
data: { data: {
csrfmiddlewaretoken: csrftoken, csrfmiddlewaretoken: csrftoken,
@ -520,4 +519,5 @@ $(function () {
getStages(orderId, userId, recipentId, secureOrder); getStages(orderId, userId, recipentId, secureOrder);
}); });
}); })
;

@ -44,6 +44,7 @@ class MessageSerializer(ModelSerializer):
documents = DocumentsSerializer(read_only=True, many=True) documents = DocumentsSerializer(read_only=True, many=True)
text = serializers.SerializerMethodField() text = serializers.SerializerMethodField()
class Meta: class Meta:
model = Message model = Message

@ -235,7 +235,6 @@
</div> </div>
<!-- End block Tab2--> <!-- End block Tab2-->
<!-- Tab3 groups block --> <!-- Tab3 groups block -->
<div class="chatBlock disTab tab-pane fade" id="tab3"> <div class="chatBlock disTab tab-pane fade" id="tab3">
<div class="col-lg-3 wrMessages"> <div class="col-lg-3 wrMessages">

@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from hitcount.models import HitCountMixin from hitcount.models import HitCountMixin
from mptt.models import TreeForeignKey from mptt.models import TreeForeignKey
@ -262,7 +263,7 @@ class Stage(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
order = models.ForeignKey(Order, related_name='stages') order = models.ForeignKey(Order, related_name='stages')
result = models.CharField(max_length=255) result = models.CharField(max_length=255)
term = models.DateField(validators=[validate_term]) term = models.DateField()
term_type = models.CharField(max_length=10, choices=TERM_TYPES, default='hour') term_type = models.CharField(max_length=10, choices=TERM_TYPES, default='hour')
status = models.CharField(choices=STATUSES, max_length=30, default='not_agreed') status = models.CharField(choices=STATUSES, max_length=30, default='not_agreed')
created = models.DateTimeField(default=timezone.now) created = models.DateTimeField(default=timezone.now)
@ -275,12 +276,17 @@ class Stage(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
# def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
# super().clean(*args, **kwargs) pass
# # stage_last = self.__class__.objects.filter(order=self.order).order_by('-pos')[:1]
# def save(self, *args, **kwargs): # if stage_last:
# # self.full_clean() # stage_last = stage_last[0]
# super().save(*args, **kwargs) # if stage_last.term > self.term:
# raise ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
class Meta: class Meta:
ordering = ['pos'] ordering = ['pos']

@ -79,6 +79,16 @@ class RealtySerializer(ModelSerializer):
class StageSerializer(ModelSerializer): class StageSerializer(ModelSerializer):
term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y',]) term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y',])
def validate(self, data):
if hasattr(data, 'pos') and data['pos'] > 1:
pos = data['pos'] -1
stage_last = Stage.objects.filter(order=data['order'], pos=pos)
if stage_last:
stage_last = stage_last[0]
if stage_last.term > data['term']:
raise serializers.ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
return data
class Meta: class Meta:
model = Stage model = Stage

@ -86,7 +86,7 @@
if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) { if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) {
domain = domain.replace(':' + port, ''); domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + ':8888/chat/' + userId + '/'; var url = 'ws://' + domain + '/chat/' + userId + '/';
var sock = new WebSocket(url); var sock = new WebSocket(url);
var intervalId; var intervalId;
sock.onopen = function () { sock.onopen = function () {

@ -11,7 +11,7 @@ import pydash as _; _.map = _.map_; _.filter = _.filter_
from archilance import util from archilance import util
from specializations.models import Specialization from specializations.models import Specialization
# from chat.models import NewMessage
GENDERS = ( GENDERS = (
@ -238,7 +238,7 @@ class Team(models.Model):
specializations = TreeManyToManyField(Specialization, related_name='teams', blank=True) specializations = TreeManyToManyField(Specialization, related_name='teams', blank=True)
contractors = models.ManyToManyField(User, limit_choices_to={'groups__name': 'Исполнители'}, related_name ='teams', blank=True) contractors = models.ManyToManyField(User, limit_choices_to={'groups__name': 'Исполнители'}, related_name ='teams', blank=True)
rating = models.FloatField(default=0.0) rating = models.FloatField(default=0.0)
def __str__(self): def __str__(self):
return self.name return self.name
@ -247,6 +247,7 @@ class Team(models.Model):
verbose_name_plural = 'Команды' verbose_name_plural = 'Команды'
class TeamInvitation(models.Model): class TeamInvitation(models.Model):
contractor1 = models.ForeignKey(User, related_name='invites') contractor1 = models.ForeignKey(User, related_name='invites')
contractor2 = models.ForeignKey(User, related_name='invited_by') contractor2 = models.ForeignKey(User, related_name='invited_by')

@ -2,42 +2,44 @@
{% load specializtions_tags %} {% load specializtions_tags %}
{% load thumbnail %} {% load thumbnail %}
{% load user_tags %}
{% block content %} {% block content %}
{% include 'partials/modals/add_team_member.html' %} {% include 'partials/modals/add_team_member.html' %}
{% include 'partials/header.html' %} {% include 'partials/header.html' %}
<div class="container mainScore"> <div class="container mainScore">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<p class="titleScore">Личный кабинет</p> <p class="titleScore">Личный кабинет</p>
</div> </div>
{% include 'partials/contractor_profile_tabs.html' %} {% include 'partials/contractor_profile_tabs.html' %}
<div class="buttonGP disTab"> <div class="buttonGP disTab">
<div class="btn-group valProject2 val-pro3" role="group"> <div class="btn-group valProject2 val-pro3" role="group">
{% if contractor.team %} {% if contractor.team %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}" class="btn btn-default"> <a href="{% url 'users:team-profile' pk=contractor.team.pk %}" class="btn btn-default">
{{ contractor.team.name }} {{ contractor.team.name }}
<span><mark>0</mark></span> <span><mark>{% get_new_count_message contractor.team request.user %}</mark></span>
</a> </a>
{% else %} {% else %}
<button type="button" class="btn btn-default add-group" data-toggle="modal" data-target="#myModal"> <button type="button" class="btn btn-default add-group" data-toggle="modal"
data-target="#myModal">
+ Добавить группу + Добавить группу
</button> </button>
{% endif %} {% endif %}
</div> </div>
<div class="btn-group valProject2 val-pro3" role="group"> <div class="btn-group valProject2 val-pro3" role="group">
{% for team in contractor.teams.all %} {% for team in contractor.teams.all %}
<a href="{% url 'users:team-profile' pk=team.pk %}" class="btn btn-default"> <a href="{% url 'users:team-profile' pk=team.pk %}" class="btn btn-default">
{{ team.name }} {{ team.name }}
<span><mark>{% get_new_count_message team request.user %}</mark></span>
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -45,18 +47,19 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
<h4 class="modal-title" id="myModalLabel">Добавление новой группы</h4> <h4 class="modal-title" id="myModalLabel">Добавление новой группы</h4>
</div> </div>
<form method="post" action="{% url 'users:team-create' %}">{% csrf_token %} <form method="post" action="{% url 'users:team-create' %}">{% csrf_token %}
<div class="modal-body"> <div class="modal-body">
<div class="textAreaBlock2 text-nn box-sizing disTab"> <div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название <span style="color: red">{{ form_team.name.errors.as_text }}</span></p> <p>Название <span style="color: red">{{ form_team.name.errors.as_text }}</span>
</p>
<input type="text" class="box-sizing" name="{{ form_team.name.html_name }}"> <input type="text" class="box-sizing" name="{{ form_team.name.html_name }}">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button> <button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button> <button type="submit" class="btn btn-primary">Сохранить</button>
@ -66,7 +69,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if contractor.team %} {% if contractor.team %}
<div class="projectsBlock disTab"> <div class="projectsBlock disTab">
<div class="col-lg-12"> <div class="col-lg-12">
@ -75,22 +78,25 @@
<div class="avatarInset"> <div class="avatarInset">
{% if contractor.avatar %} {% if contractor.avatar %}
{% thumbnail contractor.avatar "265x264" crop="center" as im %} {% thumbnail contractor.avatar "265x264" crop="center" as im %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{{ im.url }}" alt="profile-image"></a> <a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img
src="{{ im.url }}" alt="profile-image"></a>
{% endthumbnail %} {% endthumbnail %}
{% else %} {% else %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{% static 'img/profile.jpg' %}" alt="profile-image"></a> <a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img
src="{% static 'img/profile.jpg' %}" alt="profile-image"></a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="menuUser disTab"> <div class="menuUser disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#addTeamMemberModal" class="add-man -add-team-member"> <a href="#" onclick="return false" data-toggle="modal" data-target="#addTeamMemberModal"
class="add-man -add-team-member">
добавить участника добавить участника
</a> </a>
<div class="block-users"> <div class="block-users">
<p>Состав группы</p> <p>Состав группы</p>
{% for c in contractor.team.contractors.all %} {% for c in contractor.team.contractors.all %}
<div class="message-new"> <div class="message-new">
<div class="imgMess"> <div class="imgMess">
@ -102,11 +108,11 @@
<img src="{% static 'img/profile.jpg' %}" alt="mess-image"> <img src="{% static 'img/profile.jpg' %}" alt="mess-image">
{% endif %} {% endif %}
</div> </div>
<p class="nameMess" style="float: none"> <p class="nameMess" style="float: none">
<a href="{% url 'users:contractor-profile' c.pk %}">{{ c.get_full_name }}</a> <a href="{% url 'users:contractor-profile' c.pk %}">{{ c.get_full_name }}</a>
</p> </p>
<p><span>{{ c.get_popular_specialization }}</span></p> <p><span>{{ c.get_popular_specialization }}</span></p>
</div> </div>
{% empty %} {% empty %}
@ -115,31 +121,33 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-9 divCol9"> <div class="col-lg-9 divCol9">
<div class="col-lg-4"> <div class="col-lg-4">
<p class="nameUser"><a href="{% url 'users:team-profile' pk=contractor.team.pk %}">{{ contractor.team.name }}</a></p> <p class="nameUser"><a
href="{% url 'users:team-profile' pk=contractor.team.pk %}">{{ contractor.team.name }}</a>
</p>
<p class="cityUser">{{ contractor.get_location }}</p> <p class="cityUser">{{ contractor.get_location }}</p>
<p class="navv">На сайте {{ contractor.team.created }}</p> <p class="navv">На сайте {{ contractor.team.created }}</p>
<p class="navv"> <p class="navv">
Кол-во человек: <span>{{ contractor.team.contractors.count }}</span> Кол-во человек: <span>{{ contractor.team.contractors.count }}</span>
</p> </p>
<p class="navv"> <p class="navv">
Выполненных проектов: <span>{{ completed_project_count }}</span> Выполненных проектов: <span>{{ completed_project_count }}</span>
</p> </p>
<div class="statusUser st-new">Свободен</div> <div class="statusUser st-new">Свободен</div>
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
{% specialization_team_widget contractor.team.pk %} {% specialization_team_widget contractor.team.pk %}
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
{% ratings_team_widget contractor.team.pk %} {% ratings_team_widget contractor.team.pk %}
{% if contractor.cro %} {% if contractor.cro %}
<div class="sroUser"> <div class="sroUser">
<div class="iconSRO"></div> <div class="iconSRO"></div>
@ -148,7 +156,7 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="col-lg-9"> <div class="col-lg-9">
<div class="profileTabs2"> <div class="profileTabs2">
<ul class="nav nav-tabs nav-justified"> <ul class="nav nav-tabs nav-justified">
@ -164,41 +172,49 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div id="tab11" class="tab-pane fade in active"> <div id="tab11" class="tab-pane fade in active">
<div class="galleryWork2 disTab col-lg-9 -portfolios-container"> <div class="galleryWork2 disTab col-lg-9 -portfolios-container">
<script type="text/x-template" class="-portfolio-item-templ"> <script type="text/x-template" class="-portfolio-item-templ">
<div class="col-lg-4"> <div class="col-lg-4">
<div class="insetCol box-sizing disTab"> <div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover"> <div class="imgGal"
<a class="open-modal-image" href="<%- portfolio.photos[0].img %>"><div class="imgFigure"></div></a> style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
<a class="open-modal-image" href="<%- portfolio.photos[0].img %>">
<div class="imgFigure"></div>
</a>
</div> </div>
</div> </div>
<div class="insetCol2 box-sizing disTab text-center"> <div class="insetCol2 box-sizing disTab text-center">
<a href="<%- portfAbsUrl %>"><%- trunc({length: 50}, portfolio.name) %></a> <a href="<%- portfAbsUrl %>"><%- trunc({length: 50}, portfolio.name)
%></a>
</div> </div>
</div> </div>
</script> </script>
</div> </div>
<div class="col-lg-9 col-lg-offset-3 -more-portfolios-btn"> <div class="col-lg-9 col-lg-offset-3 -more-portfolios-btn">
<div class="linkElse"> <div class="linkElse">
<a href="#" onclick="loadMorePortfolios(); return false" class="showElse">показать еще</a> <a href="#" onclick="loadMorePortfolios(); return false" class="showElse">показать
еще</a>
</div> </div>
</div> </div>
</div> </div>
<div id="tab12" class="tab-pane fade"> <div id="tab12" class="tab-pane fade">
<div class="galleryWork2 disTab -work-sells-container"> <div class="galleryWork2 disTab -work-sells-container">
<script type="text/x-template" class="-work-sell-item-templ"> <script type="text/x-template" class="-work-sell-item-templ">
<div class="col-lg-4"> <div class="col-lg-4">
<div class="insetCol box-sizing disTab"> <div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;"> <div class="imgGal"
<a class="open-modal-image" href="<%- workSell.photos[0].img %>"><div class="imgFigure"></div></a> style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;">
<a class="open-modal-image" href="<%- workSell.photos[0].img %>">
<div class="imgFigure"></div>
</a>
</div> </div>
<div class="cenaImg box-sizing"> <div class="cenaImg box-sizing">
<div class="cenaImgInset"> <div class="cenaImgInset">
<%- workSell.budget %> <i class="fa fa-rub"></i> <%- workSell.budget %> <i class="fa fa-rub"></i>
@ -206,19 +222,21 @@
</div> </div>
</div> </div>
<div class="insetCol2 box-sizing disTab"> <div class="insetCol2 box-sizing disTab">
<a href="<%- workSellAbsUrl %>"><%- trunc({length: 50}, workSell.name) %></a> <a href="<%- workSellAbsUrl %>"><%- trunc({length: 50}, workSell.name)
%></a>
</div> </div>
</div> </div>
</script> </script>
</div> </div>
<div class="col-lg-9 col-lg-offset-3 -more-work-sells-btn"> <div class="col-lg-9 col-lg-offset-3 -more-work-sells-btn">
<div class="linkElse"> <div class="linkElse">
<a href="#" onclick="loadMoreWorkSells(); return false" class="showElse">показать еще</a> <a href="#" onclick="loadMoreWorkSells(); return false" class="showElse">показать
еще</a>
</div> </div>
</div> </div>
</div> </div>
<div id="tab13" class="tab-pane fade"> <div id="tab13" class="tab-pane fade">
{% for review in reviews %} {% for review in reviews %}
<div class="new-comm-44"> <div class="new-comm-44">
@ -226,15 +244,23 @@
<p class="nameComm"> <p class="nameComm">
<a href="#">{{ review.get_sender }}</a> <a href="#">{{ review.get_sender }}</a>
</p> </p>
{% if review.project.deal_type == 'secure_deal' %} {% if review.project.deal_type == 'secure_deal' %}
<span class="dateComm44">Безопасная сделка</span> <span class="dateComm44">Безопасная сделка</span>
{% endif %} {% endif %}
<div class="stars box-sizing"> <div class="stars box-sizing">
<a href="#">положительный отзыв</a> <a href="#">
{% if review.type == 'positive' %}
Положительный отзыв
{% elif review.type == 'negative' %}
Отрицательный отзыв
{% else %}
Нейтральный отзыв
{% endif %}
</a>
</div> </div>
<p class="textComm44"> <p class="textComm44">
{{ review.text|safe }} {{ review.text|safe }}
</p> </p>
@ -250,7 +276,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% include 'partials/footer.html' %} {% include 'partials/footer.html' %}
</div> </div>
</div> </div>
@ -258,116 +284,117 @@
{% block js_block %} {% block js_block %}
<script> <script>
(function() { (function () {
// Pagination --------------------------------------------------- // Pagination ---------------------------------------------------
var $portfoliosContainer = $('.-portfolios-container').first() var $portfoliosContainer = $('.-portfolios-container').first()
var portfolioItemTempl = _.template($portfoliosContainer.find('.-portfolio-item-templ').first().html()) var portfolioItemTempl = _.template($portfoliosContainer.find('.-portfolio-item-templ').first().html())
var $morePortfBtn = $('.-more-portfolios-btn').first() var $morePortfBtn = $('.-more-portfolios-btn').first()
var portfAbsUrl = '/projects/portfolio/' var portfAbsUrl = '/projects/portfolio/'
var $workSellsContainer = $('.-work-sells-container').first() var $workSellsContainer = $('.-work-sells-container').first()
var workSellItemTempl = _.template($workSellsContainer.find('.-work-sell-item-templ').first().html()) var workSellItemTempl = _.template($workSellsContainer.find('.-work-sell-item-templ').first().html())
var $moreWorkSellsfBtn = $('.-more-work-sells-btn').first() var $moreWorkSellsfBtn = $('.-more-work-sells-btn').first()
var workSellAbsUrl = '/work_sell/' var workSellAbsUrl = '/work_sell/'
var contractorId = {{ contractor.pk }} var contractorId =
var teamId = {{ contractor.team.pk }} {{ contractor.pk }}
var teamId =
{{ contractor.team.pk }}
var contractorIds = [contractorId] var contractorIds = [contractorId]
var portfUrl = new URI('/api/portfolios/') var portfUrl = new URI('/api/portfolios/')
var workSellUrl = new URI('/api/work-sells/') var workSellUrl = new URI('/api/work-sells/')
var pageSize = {% if TESTING %}99999{% else %}9{% endif %} var pageSize = {% if TESTING %}99999{% else %}9{% endif %}
$.get('/api/teams/' + teamId + '/').then(function(res) { $.get('/api/teams/' + teamId + '/').then(function (res) {
Array.prototype.push.apply(contractorIds, _.map(function(contractor) { Array.prototype.push.apply(contractorIds, _.map(function (contractor) {
return contractor.id return contractor.id
}, res.contractors)) }, res.contractors))
}) })
.then(loadMorePortfolios) .then(loadMorePortfolios)
.then(loadMoreWorkSells) .then(loadMoreWorkSells)
function loadMorePortfolios() { function loadMorePortfolios() {
var query = portfUrl.query(true) var query = portfUrl.query(true)
portfUrl.setQuery('user__id__in', _.join(',', contractorIds)) portfUrl.setQuery('user__id__in', _.join(',', contractorIds))
portfUrl.setQuery('page_size', pageSize) portfUrl.setQuery('page_size', pageSize)
portfUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1) portfUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1)
$.get(portfUrl.href()) $.get(portfUrl.href())
.then(function(res) { .then(function (res) {
_.each(function(portfolio) { _.each(function (portfolio) {
$portfoliosContainer.append(portfolioItemTempl({ $portfoliosContainer.append(portfolioItemTempl({
portfolio: portfolio, portfolio: portfolio,
portfAbsUrl: portfAbsUrl + portfolio.id + '/', portfAbsUrl: portfAbsUrl + portfolio.id + '/',
trunc: _.truncate, trunc: _.truncate,
})) }))
}, res.results) }, res.results)
if (!res.next) if (!res.next)
$morePortfBtn.css('display', 'none') $morePortfBtn.css('display', 'none')
}) })
.then(function() { .then(function () {
$('.open-modal-image').magnificPopup({type: 'image'}) $('.open-modal-image').magnificPopup({type: 'image'})
}) })
} }
function loadMoreWorkSells() { function loadMoreWorkSells() {
var query = workSellUrl.query(true) var query = workSellUrl.query(true)
workSellUrl.setQuery('contractor', contractorId) workSellUrl.setQuery('contractor', contractorId)
workSellUrl.setQuery('page_size', pageSize) workSellUrl.setQuery('page_size', pageSize)
workSellUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1) workSellUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1)
$.get(workSellUrl.href()).then(function(res) { $.get(workSellUrl.href()).then(function (res) {
_.each(function(ws) { _.each(function (ws) {
$workSellsContainer.append(workSellItemTempl({ $workSellsContainer.append(workSellItemTempl({
workSell: ws, workSell: ws,
workSellAbsUrl: workSellAbsUrl + ws.id + '/', workSellAbsUrl: workSellAbsUrl + ws.id + '/',
trunc: _.truncate, trunc: _.truncate,
})) }))
}, res.results) }, res.results)
if (!res.next) if (!res.next)
$moreWorkSellsfBtn.css('display', 'none') $moreWorkSellsfBtn.css('display', 'none')
}) })
} }
window.loadMorePortfolios = loadMorePortfolios window.loadMorePortfolios = loadMorePortfolios
window.loadMoreWorkSells = loadMoreWorkSells window.loadMoreWorkSells = loadMoreWorkSells
// Add new team member -------------------------------------------- // Add new team member --------------------------------------------
{% if contractor.has_team %} {% if contractor.has_team %}
var $contractorSelect = $('.-contractor-select').first() var $contractorSelect = $('.-contractor-select').first()
var $addTeamMemberModal = $('#addTeamMemberModal') var $addTeamMemberModal = $('#addTeamMemberModal')
var contractorExcludeIds = JSON.parse({{ team_invitation_exclude_contractor_ids|json }}) var contractorExcludeIds = JSON.parse({{ team_invitation_exclude_contractor_ids|json }})
initContractorSelect($contractorSelect, contractorExcludeIds) initContractorSelect($contractorSelect, contractorExcludeIds)
$addTeamMemberModal.find('.-action-button').first().on('click', function($evt) { $addTeamMemberModal.find('.-action-button').first().on('click', function ($evt) {
$addTeamMemberModal.modal('hide') $addTeamMemberModal.modal('hide')
var contractor2Id = $contractorSelect.select2('val') var contractor2Id = $contractorSelect.select2('val')
var createTeamInvitationUrl = format('/users/create-team-invitation/%s/', contractor2Id) var createTeamInvitationUrl = format('/users/create-team-invitation/%s/', contractor2Id)
$.post(createTeamInvitationUrl).then(function(res) { $.post(createTeamInvitationUrl).then(function (res) {
if (res.status === 'success') { if (res.status === 'success') {
socketMain.add_message({ socketMain.add_message({
format_type: 'add_message_contact', format_type: 'add_message_contact',
data: { data: {
sender_id: '{{ contractor.pk }}', sender_id: '{{ contractor.pk }}',
recipent_id: String(contractor2Id), recipent_id: String(contractor2Id),
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:accept-team-invitation' owner_id=contractor.pk %}', chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:accept-team-invitation' owner_id=contractor.pk %}',
}, },
}) })
$.jGrowl('Приглашение успешно отправлено') $.jGrowl('Приглашение успешно отправлено')
} else { } else {
$.jGrowl('Произошла ошибка при отправке приглашения') $.jGrowl('Произошла ошибка при отправке приглашения')

@ -18,23 +18,19 @@
<span class="dateComm44"> <span class="dateComm44">
{{ review.project.get_deal_type_display }} {{ review.project.get_deal_type_display }}
</span> </span>
<div class="stars box-sizing"> <div class="stars box-sizing">
{% for star in review.stars|range %} <a href="#">
<span class="glyphicon glyphicon-star starAct" aria-hidden="true"></span> {% if review.type == 'positive' %}
{% endfor %} Положительный отзыв
{% elif review.type == 'negative' %}
{% for filler_star in 5|sub:review.stars|range %} Отрицательный отзыв
<span class="glyphicon glyphicon-star" aria-hidden="true"></span> {% else %}
{% endfor %} Нейтральный отзыв
{% endif %}
{% if review.stars < 3 %} </a>
<a href="#" onclick="return false" style="color: red">отрицательный отзыв</a>
{% else %}
<a href="#" onclick="return false">положительный отзыв</a>
{% endif %}
</div> </div>
<p class="textComm44">{{ review.text }}</p> <p class="textComm44">{{ review.text }}</p>
</div> </div>
</div> </div>

@ -1,17 +1,18 @@
import math import math
from django import template from django import template
from chat.models import NewMessage
register = template.Library() register = template.Library()
@register.inclusion_tag('templatetags/contractor_indicator.html', takes_context=True) @register.inclusion_tag('templatetags/contractor_indicator.html', takes_context=True)
def contractor_indicator(context, contractor): def contractor_indicator(context, contractor):
fields = ['avatar','cro','first_name','gender', fields = ['avatar', 'cro', 'first_name', 'gender',
'last_name', 'patronym', 'phone', 'last_name', 'patronym', 'phone',
'skype','website','location'] 'skype', 'website', 'location']
indicator_sum = 0 indicator_sum = 0
for f in fields: for f in fields:
if hasattr(contractor,f) and getattr(contractor, f): if hasattr(contractor, f) and getattr(contractor, f):
indicator_sum += 1 indicator_sum += 1
current_indicator = math.ceil(math.ceil(100 / len(fields)) * indicator_sum) current_indicator = math.ceil(math.ceil(100 / len(fields)) * indicator_sum)
@ -39,11 +40,23 @@ def count_new_message(context, user):
@register.inclusion_tag('templatetags/user_new_count_orders.html', takes_context=True) @register.inclusion_tag('templatetags/user_new_count_orders.html', takes_context=True)
def count_new_message_orders(context, user): def count_new_message_orders(context, user):
from chat.models import NewMessage new_count = NewMessage.objects.filter(user=user, message__order__in=user.orders.all(),
new_count = NewMessage.objects.filter(user=user, message__order__in=user.orders.all(), message__team__isnull=True).count() message__team__isnull=True).count()
if user.team: try:
new_count_team = NewMessage.objects.filter(user=user, message__order__in=user.team.orders.all(), message__team__isnull=True).count() team = user.team
new_count +=new_count_team except:
team = None
if team:
new_count_team = NewMessage.objects.filter(user=user, message__order__in=user.team.orders.all(),
message__team__isnull=True).count()
new_count += new_count_team
return { return {
'new_count': new_count, 'new_count': new_count,
} }
@register.simple_tag
def get_new_count_message(team_pk,user=None):
count = NewMessage.objects.filter(user=user, message__team=team_pk, message__order__isnull=True).count()
return count

Loading…
Cancel
Save