remotes/origin/PR-39
ArturBaybulatov 9 years ago
commit 6a010ba0ce
  1. 51
      assets/css/extra.css
  2. 100
      assets/css/main.css
  3. 62
      assets/js/chat.js
  4. 9
      assets/js/chat_contractor.js
  5. 54
      assets/js/chat_customer.js
  6. 14
      chat/chat.py
  7. 2
      chat/serializers.py
  8. 14
      chat/templates/chat_contractor.html
  9. 12
      chat/templates/chat_customer.html
  10. 13
      chat/templates/reverse_stage_modal.html
  11. 27
      chat/views.py
  12. 20
      projects/migrations/0037_auto_20160919_1126.py
  13. 2
      projects/serializers.py
  14. 6
      projects/templates/project_detail.html
  15. 20
      ratings/migrations/0002_historyrating_type.py
  16. 10
      ratings/models.py
  17. 8
      users/models.py
  18. 2
      users/templates/templatetags/user_new_count_orders.html
  19. 8
      users/templatetags/user_tags.py
  20. 40
      work_sell/templates/worksell_edit.html

@ -189,3 +189,54 @@
font-style: italic;
}
.trashedOrderBlock {
width: 100%;
float: left;
margin-bottom: -1px;
padding: 15px;
background-color: #F7F7F7;
border-top: 1px solid #72767C;
border-bottom: 1px solid #72767C;
position: relative;
cursor: pointer;
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.trashedOrderBlock:hover {
background-color: white;
border-top: 1px solid black;
border-bottom: 1px solid black;
-webkit-transform: scale(1.03);
-moz- transform: scale(1.03);
transform: scale(1.03);
box-shadow: 0 0 10px rgba(0,0,0,0.7);
z-index: 999;
}
.count-tab{
margin-left: 10px;
width: 35px;
height: 35px;
border-radius: 100%;
line-height: 35px;
text-align: center;
font-size: 14px;
font-family: 'Arial-MT-Regular', sans-serif;
position: absolute;
top: 14px;
cursor: pointer;
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
background-color: #ebebeb;
}
li a:active .count-tab, li a:hover .count-tab{
background-color: #FF0027;
color: white;
}

@ -827,9 +827,8 @@ footer:after {
box-shadow: 0 0 10px rgba(0,0,0,0.7);
}
.projectPro:hover .titlePro {
color: #fb2c2d;
text-decoration: underline;
.titlePro a:hover {
text-decoration: underline !important;
}
.projectPro:hover .leftPro:before {
@ -2279,14 +2278,14 @@ input[type="checkbox"]:checked + span {
height: 22px;
background: url('../img/listExecutor.png') no-repeat left;
background-size: cover;
background-position: 0 -18px;
background-position: 0 -19px;
}
.listExecutor li:last-child a:before {
height: 19px;
background: url('../img/listExecutor.png') no-repeat left;
background-size: cover;
background-position: 0 -47px;
background-position: 0 -48px;
}
.doneBlock {
@ -2777,14 +2776,16 @@ input[type="checkbox"]:checked + span {
cursor: move;
}
.compTable tr:hover:not(:first-child) {
.compTable tr:hover {
z-index: 999;
display: flex;
display: table-row;
position: relative;
-webkit-transform: scale(1.04);
-moz-transform: scale(1.04);
transform: scale(1.04);
box-shadow: 0 0 10px rgba(0,0,0,0.8);
-webkit-transform: scale(1.02);
-moz-transform: scale(1.02);
transform: scale(1.02);
/*box-shadow: 0 0 10px rgba(0,0,0,0.8);*/
outline: 1px solid #DAD9D9;
cursor: move;
}
.compTable tr:hover td:first-child {
@ -2804,10 +2805,15 @@ input[type="checkbox"]:checked + span {
border-bottom: 1px solid #DAD9D9 !important;
}
.compTable tr th, .compTable tr td {
display: block;
/*.compTable tr th, .compTable tr td {
display: block
float: left;
text-align: center;
}*/
.compTable tr th, .compTable tr td {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.compTable tr th:first-child,
@ -2909,7 +2915,7 @@ input[type="checkbox"]:checked + span {
.compTable tr td:nth-child(6) ul {
float: left;
margin: 29px 0 0 72px;
margin: 0px 0 0 72px;
}
.compTable tr td:nth-child(6) ul li {
@ -2974,7 +2980,6 @@ input[type="checkbox"]:checked + span {
font-weight: bold;
font-family: Arial, Verdana, Helvetica, sans-serif;
position: relative;
margin-bottom: 20px;
position: relative;
width: 100%;
float: left;
@ -3711,7 +3716,7 @@ input[type="checkbox"]:checked + span {
height: 11px;
background: url('../img/delDoc.png') no-repeat center;
background-size: cover;
right: -40px;
right: -20px !important;
top: 2px;
cursor: pointer;
}
@ -4868,6 +4873,7 @@ input[type="checkbox"]:checked + span {
.inset-mb {
display: table;
float: left;
margin-left: 15px;
}
.inset-mb:first-child {
@ -5059,7 +5065,7 @@ input[type="radio"]:checked + span {
.list-summ li:nth-child(3):before {
width: 22px;
height: 22px;
background: url('../img/chat.png') no-repeat center;
background: url('../img/skype.png') no-repeat center;
background-size: cover;
left: -42px;
top: 0;
@ -5998,7 +6004,7 @@ a.linkS2[data-target="#withdraw-money"]{
}
input[type="radio"]{
opacity: 1;
opacity: 0;
}
.wr-inset-pluss{
@ -6020,8 +6026,6 @@ input[type="radio"]{
}
.welcomeMain{
line-height: 48px;
font-family: 'pfdintextcomppro-regular', sans-serif;
font-size: 43.5px;
}
.menuUser > div{
padding: 10px 0;
@ -6048,4 +6052,60 @@ input[type="radio"]{
-moz-transform: scale(1.04);
transform: scale(1.04);
}
.new-rass2 .btn-submit-link{
display: table;
margin: 15px 0px 15px -15px !important;
}
button[data-id="stagesSelect"], button[data-id="stagesSelect"]:hover{
outline: 1px solid #ccc !important;
margin-top: 13px;
}
.polsF1 .linkS122, .polsF1 .linkS122:hover{
font-family: 'pfdintextcomppro-regular', sans-serif;
letter-spacing: 2px;
text-transform: uppercase;
border: none;
border-radius: 40px;
display: inline-block;
color: #fff;
padding: 12px 30px;
font-size: 15px;
margin-bottom: 20px;
background-color: #FE0029 !important;
width: 300px;
margin-bottom: 0;
}
#paymentfromSite, #paymentfromSite:hover{
font-family: 'pfdintextcomppro-regular', sans-serif;
letter-spacing: 2px;
text-transform: uppercase;
border: 1px solid #BEBEBE;
border-radius: 40px;
display: inline-block;
color: #373737;
padding: 12px 30px;
font-size: 15px;
margin-top: 20px;
margin-bottom: 20px;
background: transparent;
width: 300px;
}
li.officeList.icon_tml > a > p{
display: inline-block;
padding: 5px 7px 3px 7px;
background: #ff0000;
border-radius: 35px;
position: absolute;
right: -34px;
top: -4px;
}
.comm44 > div{
padding: 15px;
}
.logo + .badge{
padding: 7px;
margin-top: 5px;
border-radius: 15px;
background-color: #00BB7B;
}
/*end_new*/

@ -64,7 +64,7 @@ var SocketHandler = function () {
} else if (message.answer_type == 'approve_stages') {
var resOrderId = message.order_id;
$.jGrowl(message.msg, {
life: 4000
life: 15000
});
setTimeout(function () {
$("#orderBlock" + resOrderId).trigger('click');
@ -185,51 +185,6 @@ function dialog (message, yesCallback, notCallback) {
}
$(".messageBlock").on('click','.trashedOrderBlock',function(){
$("#chat-order-add").css("display", "none");
$('.order-block, .trashedOrderBlock').each(function () {
$(this).removeClass('orAct');
});
$(this).addClass('orAct');
var inbox = document.getElementById('message-chat-order-space');
var docList = document.getElementById('documentOrderSpace');
inbox.innerHTML = '';
docList.innerHTML = '';
var orderId = $(this).attr('data-id');
location.hash = '#order' + orderId;
$.ajax({
url: '/api/message',
type: 'GET',
data: {csrfmiddlewaretoken: csrftoken, 'order': orderId, 'team__isnull': 'true'},
dataType: 'json',
success: function (json) {
$.each(json.results, function (i, v) {
var senderName = 'Вы';
var className = 'youChat';
if (v.sender.id !== userId) {
senderName = v.sender.username;
className = '';
}
inbox.innerHTML += '<div class="col-lg-12 insetCommChat ' + className + '"><div class="topCommChat">' +
'<p class="nameCommChat">' + senderName + '</p><span>' + v.created + '</span></div>' +
'<p class="textCommChat">' + v.text + '</p></div>';
});
var height = inbox.scrollHeight;
inbox.scrollTop = height;
}
});
$("#order-stages").html("");
$("#completeWork").hide();
$("#add-form-order-note").hide();
});
// Информация о заказе
$(".messageBlock").on('click','.full-order-info', function (e) {
e.preventDefault();
@ -608,15 +563,14 @@ function dialog (message, yesCallback, notCallback) {
var recipentId = $("#chat-order-add #recipentId").val();
var senderId = $("#chat-order-add #senderId").val();
var orderId = $("#chat-order-add #orderId").val();
if (chatMessage) {
var sendLinks = $("#document-send-order a");
var sendLinks = $("#document-send-order a");
if (chatMessage || sendLinks.length > 0) {
var sendLinkIds = "";
var documentLinks = "";
var documentAttachFiles = "";
$.each(sendLinks, function (i, v) {
sendLinkIds += $(this).attr('data-id') + ';';
documentLinks += 'Входящий файл: <br> <a href="'+ $(this).attr('href') + '">'+ $(this).text() +'</a><br>';
documentLinks += 'Приложенный файл. скачать: <br> <a href="'+ $(this).attr('href') + '">'+ $(this).text() +'</a><br>';
documentAttachFiles += '<li style="word-break: break-all;">' +
'<a class="file-link" href="' + $(this).attr('href') + '">' + $(this).text() + '</a>' +
'<div class="remove-document" data-id="' + $(this).attr('data-id') + '" style="right:-10px;"></div></li>';
@ -650,16 +604,18 @@ function dialog (message, yesCallback, notCallback) {
var chatMessage = $("#chat").val();
var recipentId = $("#recipentContactId").val();
var senderId = $("#senderContactId").val();
if (chatMessage) {
var sendLinks = $("#document-send-contact a");
if (chatMessage || sendLinks.length > 0) {
$("#contact-chat-form .errorEmptyMessage").hide();
var sendLinks = $("#document-send-contact a");
var sendLinkIds = "";
var documentLinks = "";
var documentAttachFiles = "";
$.each(sendLinks, function (i, v) {
sendLinkIds += $(this).attr('data-id') + ';';
documentLinks += 'Входящий файл: <br> <a href="'+ $(this).attr('href') + '">'+ $(this).text() +'</a><br>';
documentLinks += 'Приложенный файл. скачать: <br> <a href="'+ $(this).attr('href') + '">'+ $(this).text() +'</a><br>';
documentAttachFiles += '<li style="word-break: break-all;">' +
'<a class="file-link" href="' + $(this).attr('href') + '">' + $(this).text() + '</a>' +
'<div class="remove-document" data-id="' + $(this).attr('data-id') + '" style="right:-10px;"></div></li>';

@ -588,15 +588,14 @@ $(function () {
var orderId = $("#team-chat-form #orderTeamId").val();
var documentSendIds = $("#documentSendIds").val();
var teamIds = $("#team-chat-form #teamIds").val();
if (chatMessage) {
var sendLinks = $("#document-send a");
var sendLinks = $("#document-send a");
if (chatMessage || sendLinks.length > 0) {
var sendLinkIds = "";
var documentLinks = "";
var documentAttachFiles = "";
$.each(sendLinks, function (i, v) {
sendLinkIds += $(this).attr('data-id') + ';';
documentLinks += 'Входящий файл: <br> <a href="' + $(this).attr('href') + '">' + $(this).text() + '</a><br>';
documentLinks += 'Приложенный файл. скачать: <br> <a href="' + $(this).attr('href') + '">' + $(this).text() + '</a><br>';
documentAttachFiles += '<li style="word-break: break-all;">' +
'<a class="file-link" href="' + $(this).attr('href') + '">' + $(this).text() + '</a>' +
'<div class="remove-document" data-id="' + $(this).attr('data-id') + '" style="right:-10px;"></div></li>';
@ -622,6 +621,8 @@ $(function () {
$("#team-chat-form #chatText").val("");
$("#document-send").html("");
$("#documentSendIds").val("");
} else {
$("#team-chat-form .errorEmptyMessage").show();
}
});

@ -137,7 +137,7 @@ $(function () {
'<p>Этап</p><form class="update-stages-form" data-stage-id="' + v.id + '" id="stage-form-' + v.pos + '">' +
'<label>Название</label><input class="form-control" type="text" name="name" value="' + v.name + '"><p class="error error-name"></p>' +
'<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" name="pos" type="hidden" 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" type="hidden" name="status" value="send_approve">' +
'<label>Срок</label><input class="term-picker form-control datepicker" type="text" name="term" value="' + v.term + '" ><p class="error error-term"></p>' +
@ -303,6 +303,7 @@ $(function () {
$(".new-stages-form").each(function (i, v) {
var _this = $(this);
console.log($(this).serialize());
$.ajax({
url: '/api/stages/',
type: 'POST',
@ -332,7 +333,7 @@ $(function () {
$(".update-stages-form").each(function (i, v) {
var _this = $(this);
var currentStageId = parseInt($(this).attr('data-stage-id'));
console.log($(this).serialize());
$.ajax({
url: '/api/stages/' + currentStageId + '/',
type: 'PUT',
@ -366,6 +367,7 @@ $(function () {
"msg": "Этапы для заказа " + currentOrderId + " изменены",
}
});
$.jGrowl("Этапы для заказа изменены и отправлены исполнителю", { life: 15000});
});
@ -390,7 +392,7 @@ $(function () {
'<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label>Срок</label><input class="term-picker form-control datepicker" name="term" type="text"><p class="error error-term"></p>' +
'<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="text"></form></div>';
'<input class="form-control" name="pos" value="' + pos + '" type="hidden"></form></div>';
lastFormStage.after(addFormTemplate);
}
@ -408,7 +410,7 @@ $(function () {
}
});
// Нажимаем на кнопку архивные сообщения
$("#trashed-button").on('click',function(e){
e.preventDefault();
var state = $(this).attr('data-show');
@ -452,6 +454,50 @@ $(function () {
});
// Нажимаем на заказ в архмвных заказах
$(".messageBlock").on('click','.trashedOrderBlock',function(){
$("#chat-order-add").css("display", "none");
$('.order-block, .trashedOrderBlock').each(function () {
$(this).removeClass('orAct');
});
$(this).addClass('orAct');
var inbox = document.getElementById('message-chat-order-space');
var docList = document.getElementById('documentOrderSpace');
inbox.innerHTML = '';
docList.innerHTML = '';
var orderId = $(this).attr('data-id');
location.hash = '#order' + orderId;
$.ajax({
url: '/api/message',
type: 'GET',
data: {csrfmiddlewaretoken: csrftoken, 'order': orderId, 'team__isnull': 'true'},
dataType: 'json',
success: function (json) {
$.each(json.results, function (i, v) {
var senderName = 'Вы';
var className = 'youChat';
if (v.sender.id !== userId) {
senderName = v.sender.username;
className = '';
}
inbox.innerHTML += '<div class="col-lg-12 insetCommChat ' + className + '"><div class="topCommChat">' +
'<p class="nameCommChat">' + senderName + '</p><span>' + v.created + '</span></div>' +
'<p class="textCommChat">' + v.text + '</p></div>';
});
var height = inbox.scrollHeight;
inbox.scrollTop = height;
}
});
$("#order-stages").html("");
$("#completeWork").hide();
$("#add-form-order-note").hide();
$("#reserveSpace").hide();
});
// Для заказов все вытащить
$('.order-block').on('click', function () {

@ -24,12 +24,13 @@ class ChatHandler(websocket.WebSocketHandler):
waiters = set()
def open(self, *args, **kwargs):
self.user_id = kwargs.get('user_id',1)
self.user_id = kwargs.get('user_id', 1)
self.waiters.add((self.user_id, self))
# @gen.coroutine
def on_message(self, message):
parsed = escape.json_decode(message)
if 'dummy' in parsed:
return
@ -50,9 +51,10 @@ class ChatHandler(websocket.WebSocketHandler):
# order_id = data['data'].get('order_id')
# message = data['data'].get('msg', 'Этапы обновлены')
# answer_type = data['format_type']
# waiters = tuple(w for c, w in self.waiters if c == recipent_id or c == sender_id)
# waiters = tuple(w for c, w in self.waiters if c == recipent_id)
# for waiter in waiters:
# waiter.write_message({'msg': message, 'order_id': order_id, 'answer_type': answer_type})
# print(waiter)
# # waiter.write_message({'msg': message, 'order_id': order_id, 'answer_type': answer_type})
@gen.coroutine
def add_message(self, message_data):
@ -127,7 +129,11 @@ class ChatHandler(websocket.WebSocketHandler):
if docs_links:
message += '<br><br>' + docs_links;
waiters = tuple(w for c, w in self.waiters if c == recipent_id or c == sender_id)
if message_type:
waiters = tuple(w for c, w in self.waiters if c == recipent_id)
else:
waiters = tuple(w for c, w in self.waiters if c == recipent_id or c == sender_id)
for waiter in waiters:
waiter.write_message({'msg': message,
'msg_time': msg_time,

@ -63,7 +63,7 @@ class MessageSerializer(ModelSerializer):
out = obj.text
documents = obj.documents.all()
if len(documents)>0:
documents_str = '<br>'.join(['Входящий файл: <br><a target="_blank" href="/chat/download/' + doc.file.name + '">' + doc.file.name + '</a>' for doc in documents])
documents_str = '<br>'.join(['Приложенный файл. скачать: <br><a target="_blank" href="/chat/download/' + doc.file.name + '">' + doc.file.name + '</a>' for doc in documents])
out += '<br><br>' + documents_str
return out

@ -1,12 +1,13 @@
{% extends 'partials/base.html' %}
{% load staticfiles %}
{% load thumbnail %}
{% load user_tags %}
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<h1>Чат {{ request.user }} {{ request.user.pk }}</h1>
<h1>Чат</h1>
</div>
<div class="btnReadyBlock disTab">
@ -16,15 +17,15 @@
<ul class="nav nav-tabs nav-justified">
<li role="presentation">
<a href="#tab1" data-toggle="tab">Личные</a>
<a href="#tab1" data-toggle="tab">Личные<span class="count-tab">{{ contacts_users_count }}</span></a>
</li>
<li role="presentation">
<a href="#tab2" data-toggle="tab">Заказчики</a>
<a href="#tab2" data-toggle="tab">Заказчики<span class="count-tab">{{ orders_ms_count }}</span></a>
</li>
<li role="presentation">
<a href="#tab3" data-toggle="tab">Исполнители,Группы</a>
<a href="#tab3" data-toggle="tab">Исполнители,Группы<span class="count-tab">{{ teams_ms_count }}</span></a>
</li>
</ul>
@ -56,8 +57,8 @@
</p>
<a href="#" data-id="{{ contact.id }}" class="conMess">Контакты</a>
<span class="contact-count-{{ contact.pk|add:request.user.pk }}">0</span>
<span class="contact-count-{{ contact.pk|add:request.user.pk }}">
{% get_new_count_for_contact contact request.user %}</span>
<a href="#" class="deleteMess" data-recipent-id="{{ contact.pk }}">
Удалить контакт
</a>
@ -320,6 +321,7 @@
<input type="hidden" name="team_ids" id="teamIds">
<input type="hidden" name="document-send" id="documentSendIds">
<textarea id="chatText" class="chat-textarea box-sizing"></textarea>
<p class="errorEmptyMessage" style="color: red;display:none;">Пустое сообщение нельзя отправить</p>
<div class="bunChat">
<div class="setChat box-sizing upload">
<input type="file" name="file" id="upload-document-team">

@ -1,12 +1,13 @@
{% extends 'partials/base.html' %}
{% load staticfiles %}
{% load thumbnail %}
{% load user_tags %}
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<h1>Чат {{ request.user.get_score }}</h1>
<h1>Чат</h1>
</div>
<div class="btnReadyBlock disTab">
<div class="triangle1"></div>
@ -14,10 +15,11 @@
<div class="profileTabs2">
<ul class="nav nav-tabs nav-justified">
<li role="presentation">
<a href="#tab1" data-toggle="tab">Личные</a>
<a href="#tab1" data-toggle="tab">Личные<span class="count-tab">{{ contacts_users_count }}</span></a>
</li>
<li role="presentation">
<a href="#tab2" data-toggle="tab">Исполнители</a>
<a href="#tab2" data-toggle="tab">Исполнители<span class="count-tab">{{ orders_ms_count }}</span>
</a>
</li>
</ul>
</div>
@ -52,7 +54,9 @@
<a href="#" data-id="{{ contact.id }}" class="conMess">Контакты</a>
<span class="contact-count-{{ contact.pk|add:request.user.pk }}">0</span>
<span class="contact-count-{{ contact.pk|add:request.user.pk }}">
{% get_new_count_for_contact contact request.user %}
</span>
<a href="#" class="deleteMess" data-recipent-id="{{ contact.pk }}">
Удалить контакт
</a>

@ -37,7 +37,7 @@
</div>
<div class="col-lg-6">
<select id="stagesSelect">
<select class="selectpicker -project-work-type-select-field" id="stagesSelect">
</select>
</div>
</div>
@ -45,8 +45,8 @@
<div class="searchF1 polsF1 polsFF radio-afe" style="padding-top: 20px;padding-left: 50px;">
<p class="titleStepss">Резервирование средств</p>
<form id="stages-pay-form" action="{{ YANDEX_MONEY.url }}" method="POST">{% csrf_token %}
<div class="modal-body">
<div style="height: 150px;">
<div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<input type="hidden" name="sum" id="stageSumPay">
<input type='hidden' name='shopId' value='{{ YANDEX_MONEY.shop_id }}'>
@ -57,13 +57,8 @@
<input type='hidden' name='stagesId' id='stagesIds' value=''>
<input type='hidden' name='ordermodalId' id='ordermodalId'>
</div>
<button type="submit" class="btn btn-primary btn-lg btn-block">Пополнить</button>
<button type="submit" class="btn btn-primary btn-lg btn-block linkS122">Пополнить</button>
</div>
</div>
<div class="modal-footer">
</div>
</form>

@ -9,7 +9,7 @@ from wsgiref.util import FileWrapper
from .response import JSONResponse, response_mimetype
from .utils import serialize
from .models import Message, Documents
from .models import Message, Documents, NewMessage
from projects.models import Order, Project
from wallets.models import Transaction
from users.models import User, Team
@ -53,14 +53,25 @@ class ChatUserView(LoginRequiredMixin, View):
users_ids.append(b)
if user_id:
users_ids.append(int(user_id))
contacts_users = User.objects.filter(pk__in=users_ids)
chat_messages = Message.objects.filter(Q(sender=request.user.pk) | Q(recipent=request.user.pk))
orders = request.user.customer_projects.select_related('order').filter(state='active').exclude(order__contractor__isnull=True, order__team__isnull=True)
order_ids = [order.order.pk for order in orders]
transaction = Transaction.objects.get_or_create(customer=request.user, type='reservation', complete=False)
contacts_users_count = request.user.new_messages.filter(message__sender__in=users_ids,
message__order__isnull=True,
message__team__isnull=True
).count()
orders_ms_count = request.user.new_messages.filter(message__order__in=order_ids, message__team__isnull=True).count()
self.template_name = 'chat_customer.html'
return render(request, self.template_name, {'contacts_users': contacts_users,
'chat_messages': chat_messages,
'contacts_users_count': contacts_users_count,
'orders_ms_count': orders_ms_count,
'orders': orders,
'transaction': transaction[0],
'YANDEX_MONEY': settings.YANDEX_MONEY,
@ -88,17 +99,31 @@ class ChatUserView(LoginRequiredMixin, View):
users_ids.append(b)
if user_id:
users_ids.append(int(user_id))
contacts_users = User.objects.filter(pk__in=users_ids)
contacts_users_count = request.user.new_messages.filter(message__sender__in=users_ids,
message__order__isnull=True,
message__team__isnull=True
).count()
chat_messages = Message.objects.filter(Q(sender=request.user.pk) | Q(recipent=request.user.pk)).order_by(
'created')
your_teams = Team.objects.filter(Q(contractors__id=request.user.pk) | Q(owner=request.user))
orders_ms_count = request.user.new_messages.filter(message__order__in=orders, message__team__isnull=True).count()
teams_ms_count = request.user.new_messages.filter(message__team__in=your_teams, message__order__isnull=True).count()
self.template_name = 'chat_contractor.html'
return render(request, self.template_name, {'orders': orders,
'contacts_users': contacts_users,
'contacts_users_count': contacts_users_count,
'orders_ms_count': orders_ms_count,
'teams_ms_count': teams_ms_count,
'chat_messages': chat_messages,
'team_orders': team_orders,
'your_teams': your_teams,
})

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-19 08:26
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0036_auto_20160917_2135'),
]
operations = [
migrations.AlterField(
model_name='project',
name='work_type',
field=models.IntegerField(choices=[(1, 'Проектирование'), (2, 'Проверка документации'), (3, 'Устранение замечаний в проекте')], default=1),
),
]

@ -80,7 +80,7 @@ class StageSerializer(ModelSerializer):
term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y',])
def validate(self, data):
if hasattr(data, 'pos') and data['pos'] > 1:
if 'pos' in data and data['pos'] > 1:
pos = data['pos'] -1
stage_last = Stage.objects.filter(order=data['order'], pos=pos)
if stage_last:

@ -936,13 +936,13 @@
{% endfor %}
<div class="comm44 disTab">
<div class="col-lg-10 col-lg-offset-1">
<div class="col-lg-10 col-lg-offset-1" style="padding:0;">
<form action="{% url 'projects:create-answer-message' pk=answer.pk %}" method="POST" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'projects:detail' pk=project.pk %}">
<div><textarea name="text" class="fr_answer"></textarea></div>
<div><button type="submit" class="fr_answer_sen">Отправить</button></div>
<div><textarea style="margin-left:0;" name="text" class="fr_answer"></textarea></div>
<div><button style="margin-left:0;" type="submit" class="fr_answer_sen">Отправить</button></div>
</form>
</div>
</div>

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-19 08:26
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ratings', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='historyrating',
name='type',
field=models.CharField(choices=[('OCCUPANCY_PROFILE', 'occupancy_profile'), ('SECURE_DEAL', 'secure_deal'), ('REVIEW', 'review'), ('MONEY_SPENT', 'money_spent'), ('PUBLICATION_PROJECT', 'publication_project'), ('VISIT_SITE', 'visit_site'), ('CHOICE_CONTRACTOR', 'choice_contractor')], default='review', max_length=50),
),
]

@ -5,11 +5,21 @@ from specializations.models import Specialization
class HistoryRating(models.Model):
TYPES_HISTORY_RATING = (
('OCCUPANCY_PROFILE', 'occupancy_profile'),
('SECURE_DEAL', 'secure_deal'),
('REVIEW', 'review'),
('MONEY_SPENT', 'money_spent'),
('PUBLICATION_PROJECT', 'publication_project'),
('VISIT_SITE', 'visit_site'),
('CHOICE_CONTRACTOR', 'choice_contractor'),
)
user = models.ForeignKey(User, related_name='history_ratings', null=True, blank=True)
team = models.ForeignKey(Team, related_name='history_ratings', null=True, blank=True)
rating = models.IntegerField(default=0)
created = models.DateTimeField(default=timezone.now)
description = models.TextField(blank=True)
type = models.CharField(max_length=50, choices=TYPES_HISTORY_RATING, default='review')
def __str__(self):
return '{0}'.format(self.rating)

@ -5,8 +5,6 @@ from django.db import models
from django.db.models import Sum
from django.utils import timezone, formats
from mptt.models import TreeForeignKey, TreeManyToManyField
from pprint import pprint
import datetime
import pydash as _; _.map = _.map_; _.filter = _.filter_
from archilance import util
@ -171,7 +169,8 @@ class User(AbstractBaseUser, PermissionsMixin):
return self.email
def get_full_name(self):
return self.first_name + ' ' + self.last_name
full_name = self.first_name + ' ' + self.last_name
return full_name or self.username
def get_profile_image(self):
return self.avatar
@ -215,9 +214,6 @@ class User(AbstractBaseUser, PermissionsMixin):
def has_team(self):
return self.is_contractor() and bool(util.get_related_or_none(self, 'team'))
def is_owner_profile(self, user_id):
pass
def is_owner_team(self):
return Team.objects.filter(owner=self.pk).exists()

@ -1 +1 @@
{{ new_count }}
<p>{{ new_count }}</p>

@ -60,3 +60,11 @@ def count_new_message_orders(context, user):
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
@register.simple_tag
def get_new_count_for_contact(contact, current_user):
count = current_user.new_messages.filter(message__sender=contact, message__order__isnull=True, message__team__isnull=True).count()
return count

@ -70,7 +70,9 @@
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="col-lg-4">
{{ form.currency}}
<div class="polsF1" style="padding:0;">
{{ form.currency}}
</div>
</div>
</div>
</div>
@ -82,21 +84,27 @@
<input type="text" class="box-sizing" name="{{ form.term.html_name }}" value="{{ form.term.value }}">
</div>
<div class="col-lg-4">
{{ form.term_type }}
<div class="polsF1" style="padding:0;">
{{ form.term_type }}
</div>
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
</div>
<div class="polsF1 polsF2 disTab">
<p>Вид строительства <span style="color: red">{{ form.construction_type.errors.as_text }}</span></p>
{{ form.construction_type }}
<div class="col-lg-6">
<div class="polsF1 disTab" style="padding:0;">
<p>Вид строительства <span style="color: red">{{ form.construction_type.errors.as_text }}</span></p>
{{ form.construction_type }}
</div>
</div>
<div class="polsF1 polsF2 disTab">
<p>Классификация здания <span style="color: red">{{ form.building_classification.errors.as_text }}</span></p>
{{ form.building_classification }}
<div class="col-lg-6">
<div class="polsF1 disTab" style="padding:0;">
<p>Классификация здания <span style="color: red">{{ form.building_classification.errors.as_text }}</span></p>
{{ form.building_classification }}
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab -live-image-upload-container">
@ -130,13 +138,13 @@
</script>
<div class="-res"></div>
<div class="col-xs-12">
<input type="file" name="image" multiple class="-live-image-upload-field" style="display: none">
<a href="#" onclick="$(this).closest('.-live-image-upload-container').find('.-live-image-upload-field').first().click(); return false" class="btn btn-default add_file_to_port">
Выберите файлы
</a>
<div class="row">
<div class="col-xs-12">
<input type="file" name="image" multiple class="-live-image-upload-field" style="display: none">
<a href="#" onclick="$(this).closest('.-live-image-upload-container').find('.-live-image-upload-field').first().click(); return false" class="btn btn-default add_file_to_port" style="margin-top:20px;">
Выберите файлы
</a>
</div>
</div>
</div>
@ -144,7 +152,7 @@
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<div class="links-filter">
<input class="btn-submit-link" type="submit" value="Сохранить проект">
</div>

Loading…
Cancel
Save