#ARC-18 Fixes

remotes/origin/PR-39
Mukhtar 9 years ago
parent aef8ad3dd4
commit e390d8c250
  1. 2
      api/urls.py
  2. 20
      api/views.py
  3. 2
      archilance/settings/base.py
  4. 1
      assets/css/jquery.jgrowl.min.css
  5. 5
      assets/css/main.css
  6. 171
      assets/js/chat.js
  7. 28
      assets/js/chat_contractor.js
  8. 3
      chat/admin.py
  9. 20
      chat/chat.py
  10. 2
      chat/filters.py
  11. 30
      chat/migrations/0014_newmessage.py
  12. 12
      chat/models.py
  13. 18
      chat/templates/chat_contractor.html
  14. 83
      chat/templates/chat_customer.html
  15. 4
      chat/templates/review_add_modal.html
  16. 35
      projects/migrations/0030_auto_20160912_1912.py
  17. 7
      projects/models.py
  18. 3
      templates/partials/base.html
  19. 4
      templates/partials/header.html
  20. 7
      users/serializers.py
  21. 1
      users/templates/templatetags/user_new_count_orders.html
  22. 18
      users/templatetags/user_tags.py
  23. 30
      wallets/views.py
  24. 20
      work_sell/migrations/0013_auto_20160912_1912.py

@ -30,7 +30,7 @@ router.register(r'contractorresume', ContractorResumeViewSet)
router.register(r'contractorresumefiles', ContractorResumeFilesViewSet) router.register(r'contractorresumefiles', ContractorResumeFilesViewSet)
router.register(r'documents', DocumentViewSet) router.register(r'documents', DocumentViewSet)
router.register(r'locations', LocationViewSet) router.register(r'locations', LocationViewSet)
router.register(r'message', MessageViewSet) router.register(r'message', MessageViewSet, base_name='Message')
router.register(r'note', NoteViewSet) router.register(r'note', NoteViewSet)
router.register(r'orders', OrderViewSet) router.register(r'orders', OrderViewSet)
router.register(r'portfolio-photos', PortfolioPhotoViewSet) router.register(r'portfolio-photos', PortfolioPhotoViewSet)

@ -28,7 +28,7 @@ from common.models import Location
from common.serializers import LocationSerializer from common.serializers import LocationSerializer
from common.filters import LocationFilterSet from common.filters import LocationFilterSet
from chat.models import Message, Notes, Documents from chat.models import Message, Notes, Documents, NewMessage
from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer
from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet
@ -112,12 +112,12 @@ class NoteViewSet(ModelViewSet):
class MessageViewSet(ModelViewSet): class MessageViewSet(ModelViewSet):
queryset = Message.objects.all() # queryset = Message.objects.all()
serializer_class = MessageSerializer serializer_class = MessageSerializer
filter_class = MessageFilterSet filter_class = MessageFilterSet
def get_queryset(self): def get_queryset(self):
queryset = Message.objects.filter(is_delete=False) queryset = Message.objects.all()
search_param = self.request.query_params.get('operand', None) search_param = self.request.query_params.get('operand', None)
recipent_id = self.request.query_params.get('recipent_id', None) recipent_id = self.request.query_params.get('recipent_id', None)
sender_id = self.request.query_params.get('sender_id', None) sender_id = self.request.query_params.get('sender_id', None)
@ -126,7 +126,15 @@ class MessageViewSet(ModelViewSet):
if search_param == 'in': if search_param == 'in':
queryset = queryset.filter(Q(sender__in=[sender_id,recipent_id]),Q(recipent__in=[sender_id,recipent_id])).\ queryset = queryset.filter(Q(sender__in=[sender_id,recipent_id]),Q(recipent__in=[sender_id,recipent_id])).\
filter(order__isnull=True).\ filter(order__isnull=True).\
filter(~Q(sender=F('recipent'))).order_by('created') filter(~Q(sender=F('recipent')))
queryset = queryset.order_by('created')
return queryset
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
qs = NewMessage.objects.filter(message__in=queryset, user=self.request.user).delete()
return queryset return queryset
@ -167,13 +175,12 @@ class LocationViewSet(ModelViewSet):
filter_class = LocationFilterSet filter_class = LocationFilterSet
class PortfolioPagination(PageNumberPagination): class PortfolioPagination(PageNumberPagination):
page_size = settings.API_PAGE_SIZE # Default page size page_size = settings.API_PAGE_SIZE # Default page size
page_size_query_param = 'page_size' # Provide custom page size through a query param page_size_query_param = 'page_size' # Provide custom page size through a query param
max_page_size = 1000 max_page_size = 1000
class PortfolioViewSet(ModelViewSet): class PortfolioViewSet(ModelViewSet):
queryset = Portfolio.objects.all() queryset = Portfolio.objects.all()
serializer_class = PortfolioSerializer serializer_class = PortfolioSerializer
@ -181,7 +188,6 @@ class PortfolioViewSet(ModelViewSet):
pagination_class = PortfolioPagination pagination_class = PortfolioPagination
class PortfolioPhotoViewSet(ModelViewSet): class PortfolioPhotoViewSet(ModelViewSet):
queryset = PortfolioPhoto.objects.all() queryset = PortfolioPhoto.objects.all()
serializer_class = PortfolioPhotoSerializer serializer_class = PortfolioPhotoSerializer

@ -266,7 +266,7 @@ if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
PAGE_SIZE = 10 # Non-api page size (regular views) PAGE_SIZE = 10 # Non-api page size (regular views)
API_PAGE_SIZE = 100 # Django REST framework API_PAGE_SIZE = 1000 # Django REST framework
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [

@ -0,0 +1 @@
.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}}

@ -5983,12 +5983,15 @@ a.linkS2[data-target="#withdraw-money"]{
border-radius: 50px; border-radius: 50px;
border: 1px solid #42B476; border: 1px solid #42B476;
} }
input[type="radio"]{ input[type="radio"]{
opacity: 0; opacity: 1;
} }
.wr-inset-pluss{ .wr-inset-pluss{
margin-top: 24px; margin-top: 24px;
} }
.inset-gp .upload2{ .inset-gp .upload2{
float: left; float: left;
margin: -15px 15px 0 12px; margin: -15px 15px 0 12px;

@ -39,8 +39,7 @@ var SocketHandler = function () {
}); });
setTimeout(function () { setTimeout(function () {
$("#orderBlock" + resOrderId).trigger('click'); $("#orderBlock" + resOrderId).trigger('click');
}, 2000); }, 200);
console.log('approve stages');
} }
if (inbox) { if (inbox) {
@ -77,8 +76,9 @@ function csrfSafeMethod(method) {
var socket = new SocketHandler(); var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken'); var csrftoken = getCookie('csrftoken');
$(function () {
$(function () {
function dialog(message, yesCallback, notCallback) { function dialog(message, yesCallback, notCallback) {
$("#dialog_delete .modal-title").html(message); $("#dialog_delete .modal-title").html(message);
var dialog = $("#dialog_delete").modal('show'); var dialog = $("#dialog_delete").modal('show');
@ -93,48 +93,67 @@ $(function () {
} }
var currentHash = URI(location.href).hash(); var currentHash = URI(location.href).hash();
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
var activeTab = $(this).attr('href').substring(1);
var liveHash = URI(location.href).hash();
switch(activeTab){
case 'tab1':
setTimeout(function () {
if(liveHash.indexOf("#user") == 0) {
var userHashId = liveHash.replace("#user", "");
$("#userBlock" + userHashId).trigger('click');
} else {
$(".user-block").first().trigger('click');
}
}, 100);
break;
case 'tab2':
setTimeout(function () {
if(liveHash.indexOf("#order") == 0) {
var ordHashId = liveHash.replace("#order", "");
$("#orderBlock" + ordHashId).trigger('click');
} else {
$(".order-block").first().trigger('click');
}
}, 100);
break;
case 'tab3':
setTimeout(function () {
if(liveHash.indexOf("#teamorder") == 0) {
var teamHashId = liveHash.replace("#teamorder", "");
$("#teamOrderBlock" + teamHashId).trigger('click');
} else if(liveHash.indexOf("#myteam") == 0){
var teamHashId = liveHash.replace("#myteam", "");
$("#teamMyBlock" + teamHashId).trigger('click');
} else {
var firstTeamBlock = $(".team-block").first();
var firstTeamOrder = $(".team-order-block").first();
if (firstTeamOrder.length == 1){
firstTeamOrder.trigger('click');
} else if(firstTeamBlock.length == 1){
firstTeamBlock.trigger('click');
}
}
}, 100);
}
});
if (currentHash.indexOf("#order") == 0) { if (currentHash.indexOf("#order") == 0) {
var ordHashId = currentHash.replace("#order", ""); $("a[href='#tab2']").trigger('click');
setTimeout(function () {
$("#orderBlock" + ordHashId).trigger('click');
$("a[href='#tab2']").trigger('click');
}, 100);
} else if(currentHash.indexOf("#user") == 0){ } else if(currentHash.indexOf("#user") == 0){
var userHashId = currentHash.replace("#user", ""); $("a[href='#tab1']").trigger('click');
setTimeout(function () { } else if (currentHash.indexOf("#teamorder") == 0 || currentHash.indexOf("#myteam") == 0) {
$("#userBlock" + userHashId).trigger('click'); $("a[href='#tab3']").trigger('click');
//$("a[href='#tab1']").trigger('click');
}, 100);
} else if (currentHash.indexOf("#teamorder") == 0) {
var teamHashId = currentHash.replace("#teamorder", "");
$("#teamOrderBlock" + teamHashId).trigger('click');
$("a[href='#tab3']").trigger('click');
} else if (currentHash.indexOf("#myteam") == 0){
var teamHashId = currentHash.replace("#myteam", "");
$("#teamMyBlock" + teamHashId).trigger('click');
$("a[href='#tab3']").trigger('click');
} else { } else {
setTimeout(function () { $("a[href='#tab1']").trigger('click');
$(".user-block").first().trigger('click');
location.hash = '';
}, 10);
setTimeout(function () {
$(".order-block").first().trigger('click');
location.hash = '';
}, 500);
setTimeout(function () {
var firstTeamBlock = $(".team-block").first();
var firstTeamOrder = $(".team-order-block").first();
if (firstTeamOrder.length == 1){
firstTeamOrder.trigger('click');
} else if(firstTeamBlock.length == 1){
firstTeamBlock.trigger('click');
}
location.hash = '';
}, 1000);
} }
// Информация о заказе // Информация о заказе
$(".full-order-info").click('on', function (e) { $(".full-order-info").click('on', function (e) {
e.preventDefault(); e.preventDefault();
@ -220,12 +239,26 @@ $(function () {
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
if (json.status == 'ok') { if (json.status == 'ok') {
socket.send_stages_approve({
"format_type": "approve_stages",
"data": {
"sender_id": json.sender,
"recipent_id": json.recipent,
"order_id": json.order,
"msg": "Заказчик зарезервировал сумму для этапов " + json.stages,
}
});
$("#reserve-stage-modal").modal('hide'); $("#reserve-stage-modal").modal('hide');
$("#orderBlock" + orderId).trigger('click'); $("#orderBlock" + orderId).trigger('click');
}else if(json.status == 'error'){
alert(json.message_error);
} }
}, },
error: function (e, jqxhr) { error: function (e, jqxhr) {
console.log(e); console.log(e);
console.log(jqxhr);
} }
}) })
}); });
@ -275,54 +308,54 @@ $(function () {
docList.innerHTML = ''; docList.innerHTML = '';
$.ajax({ $.ajax({
url: '/api/documents', url: '/api/message',
type: 'GET', type: 'GET',
data: { data: {
csrfmiddlewaretoken: csrftoken, csrfmiddlewaretoken: csrftoken,
'operand': 'in', 'operand': 'in',
'sender_id': userId, 'sender_id': userId,
'recipent_id': contactId, 'recipent_id': contactId
'is_delete': false,
'is_send': true,
}, },
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
console.log(json);
$.each(json.results, function (i, v) { $.each(json.results, function (i, v) {
docList.innerHTML += '<li style="word-break: break-all;"><a class="file-link" href="/chat/download/' + v.file + '">' + v.file + '</a><div class="remove-document" data-id="' + v.id + '" style="right:-10px;"></div></li>'; var senderName = 'Вы';
var className = 'youChat';
if (v.sender.id == contactId) {
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;
error: function (e) { inbox.scrollTop = height;
console.log(e);
} }
}); });
$.ajax({ $.ajax({
url: '/api/message', url: '/api/documents',
type: 'GET', type: 'GET',
data: { data: {
csrfmiddlewaretoken: csrftoken, csrfmiddlewaretoken: csrftoken,
'operand': 'in', 'operand': 'in',
'sender_id': userId, 'sender_id': userId,
'recipent_id': contactId 'recipent_id': contactId,
'is_delete': false,
'is_send': true,
}, },
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
console.log(json);
$.each(json.results, function (i, v) { $.each(json.results, function (i, v) {
var senderName = 'Вы'; docList.innerHTML += '<li style="word-break: break-all;"><a class="file-link" href="/chat/download/' + v.file + '">' + v.file + '</a><div class="remove-document" data-id="' + v.id + '" style="right:-10px;"></div></li>';
var className = 'youChat';
if (v.sender.id == contactId) {
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; error: function (e) {
console.log(e);
} }
}); });
@ -346,7 +379,6 @@ $(function () {
} }
}); });
}); });
$('.deleteMess').on('click', function (e) { $('.deleteMess').on('click', function (e) {
@ -634,7 +666,6 @@ $(function () {
}).prop('disabled', !$.support.fileInput) }).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled'); //Загрузка документов .parent().addClass($.support.fileInput ? undefined : 'disabled'); //Загрузка документов
$("#upload-document-contact").bind('fileuploadsubmit', function (e, data) { $("#upload-document-contact").bind('fileuploadsubmit', function (e, data) {
data.formData = { data.formData = {
sender: $("#contact-chat-form #senderContactId").val(), sender: $("#contact-chat-form #senderContactId").val(),
@ -643,7 +674,6 @@ $(function () {
}); });
$('#upload-document-contact').fileupload({ $('#upload-document-contact').fileupload({
url: '/chat/create/', url: '/chat/create/',
crossDomain: false, crossDomain: false,
@ -677,6 +707,11 @@ $(function () {
}).prop('disabled', !$.support.fileInput) }).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled'); .parent().addClass($.support.fileInput ? undefined : 'disabled');
$('#review-add').on('show.bs.modal', function (e) {
var related = $(e.relatedTarget);
var relatedType = related.attr('data-review-type');
$('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true);
});
}); });

@ -64,7 +64,6 @@ $(function () {
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
console.log(json); console.log(json);
}, },
error: function (e) { error: function (e) {
console.log('error'); console.log('error');
@ -74,7 +73,6 @@ $(function () {
}); });
var orderId = $(this).attr('data-order-id'); var orderId = $(this).attr('data-order-id');
socket.send_stages_approve({ socket.send_stages_approve({
"format_type": "approve_stages", "format_type": "approve_stages",
"data": { "data": {
@ -132,15 +130,23 @@ $(function () {
}); });
$(".team-order-block").on('click', function () { $(".team-order-block").on('click', function () {
$('.team-order-block, .team-block').each(function () { $('.team-order-block, .team-block').each(function () {
$(this).removeClass('orAct'); $(this).removeClass('orAct');
}); });
$(this).addClass('orAct'); $(this).addClass('orAct');
var teamIds = '';
$.each($(this).find('.team-chat-user'), function(i,v){
teamIds += $(this).attr('data-id') + ";";
});
$("#team-chat-form #teamIds").val(teamIds);
var teamId = $(this).attr('data-team-id'); var teamId = $(this).attr('data-team-id');
location.hash = '#teamorder' + teamId;
var orderId = $(this).attr('data-order-id'); var orderId = $(this).attr('data-order-id');
location.hash = '#teamorder' + orderId;
$("#team-chat-form #teamId").val(teamId); $("#team-chat-form #teamId").val(teamId);
$("#team-chat-form #recipentTeamId").val("");
$("#team-chat-form #orderTeamId").val(orderId); $("#team-chat-form #orderTeamId").val(orderId);
$("#add-form-team-note #teamNote").val(teamId); $("#add-form-team-note #teamNote").val(teamId);
@ -219,6 +225,12 @@ $(function () {
}); });
$(this).addClass('orAct'); $(this).addClass('orAct');
var teamIds = '';
$.each($(this).find('.team-chat-user'), function(i,v){
teamIds += $(this).attr('data-id') + ";";
});
$("#team-chat-form #teamIds").val(teamIds);
var inbox = document.getElementById('message-chat-team-space'); var inbox = document.getElementById('message-chat-team-space');
inbox.innerHTML = ''; inbox.innerHTML = '';
@ -229,6 +241,7 @@ $(function () {
location.hash = '#myteam' + teamId; location.hash = '#myteam' + teamId;
$("#team-chat-form #teamId").val(teamId); $("#team-chat-form #teamId").val(teamId);
$("#add-form-team-note #teamNote").val(teamId); $("#add-form-team-note #teamNote").val(teamId);
$("#team-chat-form #recipentTeamId").val("");
$("#team-chat-form #orderTeamId").val(""); $("#team-chat-form #orderTeamId").val("");
$("#add-form-team-note #orderNote").val(""); $("#add-form-team-note #orderNote").val("");
@ -445,11 +458,12 @@ $(function () {
'</div><div><p>' + statusName + '</p></div></div></div>'; '</div><div><p>' + statusName + '</p></div></div></div>';
}); });
if (statusNotAgreed) { if (statusNotAgreed) {
htmlInbox += '<div class="textAreaBlock2 FFD box-sizing disTab">' + htmlInbox += '<div class="textAreaBlock2 FFD box-sizing disTab">' +
'<a id="approve-stages" data-sender-id="{{ request.user.pk }}" data-recipent-id="' + recipentId + '"' + '<a id="approve-stages" data-sender-id="'+ userId +'" data-recipent-id="' + recipentId + '"' +
' data-order-id="' + orderId + '" href="#">согласовать</a>' + ' data-order-id="' + orderId + '" href="#">согласовать</a>' +
'<a id="cancel-stages" data-sender-id="{{ request.user.pk }}" data-recipent-id="' + recipentId + '"' + '<a id="cancel-stages" data-sender-id="'+ userId +'" data-recipent-id="' + recipentId + '"' +
' data-order-id="' + orderId + '" href="#">отказаться</a>' + ' data-order-id="' + orderId + '" href="#">отказаться</a>' +
'</div>'; '</div>';
} }
@ -473,7 +487,7 @@ $(function () {
'<div><p>Срок сдачи ' + stage.term + ' <span>' + stage.cost + '<i class="fa fa-rub"></i></span></p></div></div></div>'; '<div><p>Срок сдачи ' + stage.term + ' <span>' + stage.cost + '<i class="fa fa-rub"></i></span></p></div></div></div>';
if (!stage.close_contractor) { if (!stage.close_contractor) {
stageWork += '<div class="textAreaBlock2 FFD box-sizing disTab"><a href="#" class="closeStage" data-sender-id="{{ request.user.pk }}" data-recipent-id="' + recipentId + '"' + stageWork += '<div class="textAreaBlock2 FFD box-sizing disTab"><a href="#" class="closeStage" data-sender-id="'+ userId +'" data-recipent-id="' + recipentId + '"' +
' data-order-id="' + data.id + '" data-stage-id="' + stage.id + '">Завершить этап</a></div>'; ' data-order-id="' + data.id + '" data-stage-id="' + stage.id + '">Завершить этап</a></div>';
} else { } else {
stageWork += '<div><p>Этап ожидает завершения статуса от заказчика</p><div>'; stageWork += '<div><p>Этап ожидает завершения статуса от заказчика</p><div>';
@ -540,6 +554,7 @@ $(function () {
var teamId = $("#team-chat-form #teamId").val(); var teamId = $("#team-chat-form #teamId").val();
var orderId = $("#team-chat-form #orderTeamId").val(); var orderId = $("#team-chat-form #orderTeamId").val();
var documentSendIds = $("#documentSendIds").val(); var documentSendIds = $("#documentSendIds").val();
var teamIds = $("#team-chat-form #teamIds").val();
if (chatMessage) { if (chatMessage) {
var sendLinks = $("#document-send a"); var sendLinks = $("#document-send a");
@ -561,6 +576,7 @@ $(function () {
"recipent_id": recipentId, "recipent_id": recipentId,
"chat_message": chatMessage, "chat_message": chatMessage,
"team_id": teamId, "team_id": teamId,
"team_ids": teamIds,
"order_id": orderId, "order_id": orderId,
"document_send_links": sendLinkIds, "document_send_links": sendLinkIds,
"document_data": { "document_data": {

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Message, Notes, Documents from .models import Message, Notes, Documents, NewMessage
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):
@ -17,3 +17,4 @@ class DocumentsAdmin(admin.ModelAdmin):
admin.site.register(Message, MessageAdmin) admin.site.register(Message, MessageAdmin)
admin.site.register(Notes, NotesAdmin) admin.site.register(Notes, NotesAdmin)
admin.site.register(Documents, DocumentsAdmin) admin.site.register(Documents, DocumentsAdmin)
admin.site.register(NewMessage)

@ -43,6 +43,9 @@ class ChatHandler(websocket.WebSocketHandler):
@gen.coroutine @gen.coroutine
def approve_stages(self, data): def approve_stages(self, data):
print(data)
data['data']['chat_message'] = data['data']['msg']
self.add_message(data)
sender_id = data['data']['sender_id'] sender_id = data['data']['sender_id']
recipent_id = data['data']['recipent_id'] recipent_id = data['data']['recipent_id']
order_id = data['data'].get('order_id') order_id = data['data'].get('order_id')
@ -54,11 +57,11 @@ class ChatHandler(websocket.WebSocketHandler):
@gen.coroutine @gen.coroutine
def add_message(self, message_data): def add_message(self, message_data):
sender_id = message_data['data']['sender_id'] sender_id = message_data['data']['sender_id']
recipent_id = message_data['data'].get('recipent_id', None) recipent_id = message_data['data'].get('recipent_id', None)
order_id = message_data['data'].get('order_id', None) order_id = message_data['data'].get('order_id', None)
team_id = message_data['data'].get('team_id', None) team_id = message_data['data'].get('team_id', None)
team_ids_raw = message_data['data'].get('team_ids', None)
message = message_data['data'].get('chat_message', None) message = message_data['data'].get('chat_message', None)
docs_send_links = message_data['data'].get('document_send_links', None) docs_send_links = message_data['data'].get('document_send_links', None)
if 'document_data' in message_data['data']: if 'document_data' in message_data['data']:
@ -78,7 +81,7 @@ class ChatHandler(websocket.WebSocketHandler):
team_value = "NULL" if team_id is None or not team_id else team_id team_value = "NULL" if team_id is None or not team_id else team_id
if not team_id and not recipent_id: if not recipent_id:
recipent_id = sender_id recipent_id = sender_id
order_value = "NULL" if order_id is None or not order_id else order_id order_value = "NULL" if order_id is None or not order_id else order_id
@ -92,6 +95,19 @@ class ChatHandler(websocket.WebSocketHandler):
cursor = cursor_list.get('cursor') cursor = cursor_list.get('cursor')
result = cursor.fetchone() result = cursor.fetchone()
message_id = result[0] message_id = result[0]
if team_ids_raw:
team_ids = [t for t in team_ids_raw.rstrip(';').split(';')]
values_str = '';
for t in team_ids:
values_str +='(DEFAULT,{0},{1}),'.format(message_id,t)
values_str = values_str.rstrip(',')
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES{0}".\
format(values_str)
else:
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES(DEFAULT,{0},{1})".\
format(message_id, recipent_id)
yield self.db.execute(insert_new_messages)
if docs_send_links: if docs_send_links:
is_send = 'true' is_send = 'true'
docs_send_ids = docs_send_links.rstrip(';').replace(';', ',') docs_send_ids = docs_send_links.rstrip(';').replace(';', ',')

@ -27,6 +27,8 @@ class MessageFilterSet(FilterSet):
model = Message model = Message
class DocumentFilterSet(FilterSet): class DocumentFilterSet(FilterSet):
id = AllLookupsFilter() id = AllLookupsFilter()
# order = RelatedFilter('projects.filters.OrderFilterSet') # order = RelatedFilter('projects.filters.OrderFilterSet')

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-12 16:12
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 = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('chat', '0013_auto_20160907_1556'),
]
operations = [
migrations.CreateModel(
name='NewMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_messages', to='chat.Message')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_messages', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Новые сообщения',
'verbose_name': 'Новые сообщения',
},
),
]

@ -24,6 +24,18 @@ class Message(models.Model):
verbose_name_plural = 'Сообщения' verbose_name_plural = 'Сообщения'
class NewMessage(models.Model):
user = models.ForeignKey(User, related_name='new_messages')
message = models.ForeignKey(Message, related_name='new_messages')
def __str__(self):
return str(self.id)
class Meta:
verbose_name = 'Новые сообщения'
verbose_name_plural = 'Новые сообщения'
class Notes(models.Model): class Notes(models.Model):
text = models.TextField() text = models.TextField()
created = models.DateTimeField(default=timezone.now) created = models.DateTimeField(default=timezone.now)

@ -15,7 +15,7 @@
<div class="profileTabs2"> <div class="profileTabs2">
<ul class="nav nav-tabs nav-justified"> <ul class="nav nav-tabs nav-justified">
<li role="presentation" class="active"> <li role="presentation">
<a href="#tab1" data-toggle="tab">Личные</a> <a href="#tab1" data-toggle="tab">Личные</a>
</li> </li>
@ -34,7 +34,7 @@
{% include 'dialog_delete.html' %} {% include 'dialog_delete.html' %}
<div class="tab-content"> <div class="tab-content">
<!-- Tab1 contacts block --> <!-- Tab1 contacts block -->
<div class="chatBlock disTab tab-pane fade in active" id="tab1"> <div class="chatBlock disTab tab-pane fade in" id="tab1">
<div class="col-lg-3 wrMessages"> <div class="col-lg-3 wrMessages">
<div class="messageBlock box-sizing disTab"> <div class="messageBlock box-sizing disTab">
<p>Контакты</p> <p>Контакты</p>
@ -190,13 +190,20 @@
</div> </div>
</div> </div>
<div id="leaveReview" style="display: none;">
<div class="closeChat closeChat1" id="leaveReview" style="display: none;"> <div class="closeChat closeChat1">
<a href="#" data-toggle="modal" data-target="#review-add"> <a href="#" data-toggle="modal" data-target="#review-add" data-review-type="positive">
Закрыть проект<br>и оставить отзыв Закрыть проект<br>и оставить отзыв
</a> </a>
</div> </div>
<div class="closeChat closeChat2">
<a href="#" data-toggle="modal" data-target="#review-add" data-review-type="negative">
Закрыть проект<br>и оставить отзыв
</a>
</div>
</div>
<!-- Review add --> <!-- Review add -->
{% include 'review_add_modal.html' %} {% include 'review_add_modal.html' %}
<!-- --> <!-- -->
@ -311,6 +318,7 @@
<input type="hidden" name="recipent" id="recipentTeamId"> <input type="hidden" name="recipent" id="recipentTeamId">
<input type="hidden" name="order" id="orderTeamId"> <input type="hidden" name="order" id="orderTeamId">
<input type="hidden" name="team" id="teamId"> <input type="hidden" name="team" id="teamId">
<input type="hidden" name="team_ids" id="teamIds">
<input type="hidden" name="document-send" id="documentSendIds"> <input type="hidden" name="document-send" id="documentSendIds">
<textarea id="chatText" class="chat-textarea box-sizing"></textarea> <textarea id="chatText" class="chat-textarea box-sizing"></textarea>
<div class="bunChat"> <div class="bunChat">

@ -6,14 +6,14 @@
<div class="container mainScore"> <div class="container mainScore">
<div class="row"> <div class="row">
<div class="col-lg-12 allProjects"> <div class="col-lg-12 allProjects">
<h1>Чат</h1> <h1>Чат {{ request.user.get_score }}</h1>
</div> </div>
<div class="btnReadyBlock disTab"> <div class="btnReadyBlock disTab">
<div class="triangle1"></div> <div class="triangle1"></div>
<div class="col-lg-6 col-lg-offset-3 tabsChat"> <div class="col-lg-6 col-lg-offset-3 tabsChat">
<div class="profileTabs2"> <div class="profileTabs2">
<ul class="nav nav-tabs nav-justified"> <ul class="nav nav-tabs nav-justified">
<li role="presentation" class="active"> <li role="presentation">
<a href="#tab1" data-toggle="tab">Личные</a> <a href="#tab1" data-toggle="tab">Личные</a>
</li> </li>
<li role="presentation"> <li role="presentation">
@ -26,7 +26,7 @@
{% include 'dialog_delete.html' %} {% include 'dialog_delete.html' %}
<div class="tab-content"> <div class="tab-content">
<!-- Tab1 (contacts block)--> <!-- Tab1 (contacts block)-->
<div class="chatBlock disTab tab-pane fade in active" id="tab1"> <div class="chatBlock disTab tab-pane fade in" id="tab1">
<div class="col-lg-3 wrMessages"> <div class="col-lg-3 wrMessages">
<div class="messageBlock box-sizing disTab"> <div class="messageBlock box-sizing disTab">
<p>Контакты</p> <p>Контакты</p>
@ -162,7 +162,7 @@
</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" data-score="">
<p>Этапы работы</p> <p>Этапы работы</p>
<div class="stepssBlock box-sizing disTab"> <div class="stepssBlock box-sizing disTab">
<p class="titleStepss">1 / Согласование условий</p> <p class="titleStepss">1 / Согласование условий</p>
@ -200,11 +200,18 @@
<div id="stagesWork" class="stages-work"></div> <div id="stagesWork" class="stages-work"></div>
</div> </div>
<div class="closeChat closeChat1" id="leaveReview" style="display: none;"> <div id="leaveReview" style="display: none;">
<a href="#" data-toggle="modal" data-target="#review-add"> <div class="closeChat closeChat1">
<a href="#" data-toggle="modal" data-target="#review-add" data-review-type="positive">
Закрыть проект<br>и оставить отзыв Закрыть проект<br>и оставить отзыв
</a> </a>
</div> </div>
<div class="closeChat closeChat2">
<a href="#" data-toggle="modal" data-target="#review-add" data-review-type="negative">
Закрыть проект<br>и оставить отзыв
</a>
</div>
</div>
<div class="col-lg-12 documentsChat"> <div class="col-lg-12 documentsChat">
<p>Прикрепленные документы</p> <p>Прикрепленные документы</p>
@ -460,6 +467,7 @@
}else if((stagesResults.length>0) && (data.secure)){ }else if((stagesResults.length>0) && (data.secure)){
$("#reserveSpace").show(); $("#reserveSpace").show();
} }
htmlInbox = htmlInboxStage + htmlInbox; htmlInbox = htmlInboxStage + htmlInbox;
$("#order-stages").html(htmlInbox); $("#order-stages").html(htmlInbox);
$("#completeWork").hide(); $("#completeWork").hide();
@ -558,6 +566,7 @@
} }
$(".new-stages-form").each(function (i, v) { $(".new-stages-form").each(function (i, v) {
var _this = $(this);
$.ajax({ $.ajax({
url: '/api/stages/', url: '/api/stages/',
type: 'POST', type: 'POST',
@ -567,8 +576,8 @@
data: $(this).serialize(), data: $(this).serialize(),
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
_this.removeClass('new-stages-form').addClass('update-stages-form');
console.log(json); console.log(json);
}, },
error: function (e) { error: function (e) {
console.log('error'); console.log('error');
@ -608,7 +617,7 @@
"sender_id": userId, "sender_id": userId,
"recipent_id": currentRecipentId, "recipent_id": currentRecipentId,
"order_id": currentOrderId, "order_id": currentOrderId,
"msg": "Этапы для заказа "+ currentOrderId +"изменены", "msg": "Этапы для заказа "+ currentOrderId +" изменены",
} }
}); });
@ -683,25 +692,6 @@
inbox.innerHTML = ''; inbox.innerHTML = '';
docList.innerHTML = ''; docList.innerHTML = '';
$.ajax({
url:'/api/documents',
type: 'GET',
data:{
csrfmiddlewaretoken: csrftoken,
'order': orderId,
'is_delete': false,
'is_send': true,
},
dataType: 'json',
success: function (json){
$.each(json.results, function (i, v) {
docList.innerHTML += '<li style="word-break: break-all;"><a class="file-link" href="/chat/download/' + v.file + '">' + v.file+'</a><div class="remove-document" data-id="'+ v.id+'" style="right:-10px;"></div></li>';
});
},
error: function(e){
console.log(e);
}
});
$.ajax({ $.ajax({
url: '/api/message', url: '/api/message',
type: 'GET', type: 'GET',
@ -726,6 +716,44 @@
inbox.scrollTop = height; inbox.scrollTop = height;
} }
}); });
$.ajax({
url: '/api/users/{{ request.user.pk }}/',
type: 'GET',
data: {
csrfmiddlewaretoken: csrftoken,
},
dataType:'json',
success: function(json){
var score = json.score;
$("#order-stages-tab").attr('data-score', score);
},
error: function(e,jqxhr){
console.log(jqxhr);
}
})
$.ajax({
url:'/api/documents',
type: 'GET',
data:{
csrfmiddlewaretoken: csrftoken,
'order': orderId,
'is_delete': false,
'is_send': true,
},
dataType: 'json',
success: function (json){
$.each(json.results, function (i, v) {
docList.innerHTML += '<li style="word-break: break-all;"><a class="file-link" href="/chat/download/' + v.file + '">' + v.file+'</a><div class="remove-document" data-id="'+ v.id+'" style="right:-10px;"></div></li>';
});
},
error: function(e){
console.log(e);
}
});
$.ajax({ $.ajax({
url: '/api/note/', url: '/api/note/',
type: 'GET', type: 'GET',
@ -735,7 +763,6 @@
}, },
dataType: 'json', dataType: 'json',
success: function (json) { success: function (json) {
console.log(json.results);
var noteHtmlInbox = ''; var noteHtmlInbox = '';
$.each(json.results, function (i, v) { $.each(json.results, function (i, v) {
noteHtmlInbox += '<li>' + v.text + '<li>'; noteHtmlInbox += '<li>' + v.text + '<li>';

@ -11,7 +11,8 @@
<form id="review-adds-form" method="POST"> <form id="review-adds-form" method="POST">
<div class="modal-body"> <div class="modal-body">
<div style="height: 250px;"> <div style="height: 250px;">
<div class="searchF1 polsF1 polsFF radio-afer"> {# <div class="searchF1 polsF1 polsFF radio-afer">#}
<div>
<input type="radio" value="positive" <input type="radio" value="positive"
name="type">Положительный name="type">Положительный
@ -21,6 +22,7 @@
<input type="radio" value="neutral" <input type="radio" value="neutral"
name="type">Нейтральный name="type">Нейтральный
</div> </div>
<div class="textAreaBlock2 text-nn box-sizing disTab"> <div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Ваш отзыв</p> <p>Ваш отзыв</p>
<textarea id="text-new" name="text"></textarea> <textarea id="text-new" name="text"></textarea>

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-12 16:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0029_auto_20160908_1159'),
]
operations = [
migrations.AlterField(
model_name='answer',
name='term_type',
field=models.CharField(blank=True, choices=[('day', 'день'), ('hour', 'час'), ('month', 'месяц'), ('project', 'проект')], max_length=10, null=True),
),
migrations.AlterField(
model_name='portfolio',
name='term_type',
field=models.CharField(blank=True, choices=[('day', 'день'), ('hour', 'час'), ('month', 'месяц'), ('project', 'проект')], default='hour', max_length=20, null=True),
),
migrations.AlterField(
model_name='project',
name='term_type',
field=models.CharField(choices=[('day', 'день'), ('hour', 'час'), ('month', 'месяц'), ('project', 'проект')], default='hour', max_length=20),
),
migrations.AlterField(
model_name='stage',
name='term_type',
field=models.CharField(choices=[('day', 'день'), ('hour', 'час'), ('month', 'месяц'), ('project', 'проект')], default='hour', max_length=10),
),
]

@ -220,6 +220,13 @@ class Order(models.Model):
verbose_name = 'Заказ' verbose_name = 'Заказ'
verbose_name_plural = 'Заказы' verbose_name_plural = 'Заказы'
def get_contractor_owner(self):
if self.contractor:
return self.contractor.pk
elif self.team:
return self.team.owner.pk
else:
return None
class Arbitration(models.Model): class Arbitration(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)

@ -24,7 +24,7 @@
<link rel='stylesheet' href='{% static "css/swiper.min.css" %}'> <link rel='stylesheet' href='{% static "css/swiper.min.css" %}'>
<link rel='stylesheet' href='{% static "lib/jquery.fileupload/css/jquery.fileupload.css" %}'> <link rel='stylesheet' href='{% static "lib/jquery.fileupload/css/jquery.fileupload.css" %}'>
<link rel='stylesheet' href='{% static "js/magnific-popup.css" %}'> <link rel='stylesheet' href='{% static "js/magnific-popup.css" %}'>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.css"> <!-- TODO: Download locally --> <link rel='stylesheet' href='{% static "css/jquery.jgrowl.min.css" %}'>
{% block head_css %}{% endblock %} {% block head_css %}{% endblock %}
@ -99,7 +99,6 @@
sock.onmessage = function (event) { sock.onmessage = function (event) {
var notificationData = JSON.parse(event.data); var notificationData = JSON.parse(event.data);
console.log(notificationData);
var outMessage = ""; var outMessage = "";
if (notificationData.answer_type == 'add_message_contact'){ if (notificationData.answer_type == 'add_message_contact'){
outMessage += "<a href='/chat/?user_id=" + notificationData.sender_id + "'>"+ notificationData.msg +"<a>"; outMessage += "<a href='/chat/?user_id=" + notificationData.sender_id + "'>"+ notificationData.msg +"<a>";

@ -27,7 +27,9 @@
{% if request.user.is_contractor %} {% if request.user.is_contractor %}
<li class="officeList icon_tml"> <li class="officeList icon_tml">
<a href="{% url 'users:contractor-office' pk=request.user.pk %}">Мой офис</a> <a href="{% url 'users:contractor-office' pk=request.user.pk %}">
Мой офис {% count_new_message_orders request.user %}
</a>
<span></span> <span></span>
</li> </li>
{% endif %} {% endif %}

@ -36,7 +36,8 @@ class UserSerializer(ModelSerializer):
_type = SerializerMethodField() # Distinguish when used with generic serializers _type = SerializerMethodField() # Distinguish when used with generic serializers
id = ReadOnlyField() id = ReadOnlyField()
fio = SerializerMethodField() fio = SerializerMethodField()
score = SerializerMethodField()
class Meta: class Meta:
model = User model = User
@ -66,6 +67,7 @@ class UserSerializer(ModelSerializer):
'website', 'website',
'phone', 'phone',
'fio', 'fio',
'score',
) )
def get__type(self, obj): def get__type(self, obj):
@ -73,6 +75,9 @@ class UserSerializer(ModelSerializer):
def get_fio(self, obj): def get_fio(self, obj):
return obj.get_full_name() return obj.get_full_name()
def get_score(self, obj):
return obj.get_score()
# def create(self, validated_data): # def create(self, validated_data):
# return User.objects.create(**validated_data) # return User.objects.create(**validated_data)

@ -1,7 +1,6 @@
import math import math
from django import template from django import template
register = template.Library() register = template.Library()
@ -28,10 +27,23 @@ def has_group(user, group_name):
groups = user.groups.all().values_list('name', flat=True) groups = user.groups.all().values_list('name', flat=True)
return True if group_name in groups else False return True if group_name in groups else False
@register.inclusion_tag('templatetags/user_new_count.html', takes_context=True) @register.inclusion_tag('templatetags/user_new_count.html', takes_context=True)
def count_new_message(context, user): def count_new_message(context, user):
from chat.models import Message from chat.models import Message, NewMessage
new_count = Message.objects.filter(is_delete=False, is_new=True, recipent=user).count() new_count = NewMessage.objects.filter(user=user).count()
return {
'new_count': new_count,
}
@register.inclusion_tag('templatetags/user_new_count_orders.html', takes_context=True)
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(), message__team__isnull=True).count()
if user.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,
} }

@ -13,6 +13,7 @@ from django.views.generic.base import View
import logging import logging
from users.models import User from users.models import User
from projects.models import Stage
from .forms import WithDrawForm, TmpCheckOrderForm, TmpPaymentAvisoForm, PayFromScoreForm from .forms import WithDrawForm, TmpCheckOrderForm, TmpPaymentAvisoForm, PayFromScoreForm
from .models import InvoiceHistory, WithDraw, Transaction, PayFromScore from .models import InvoiceHistory, WithDraw, Transaction, PayFromScore
@ -23,13 +24,28 @@ class PayFromScore(CreateView):
def form_valid(self, form): def form_valid(self, form):
if self.request.is_ajax(): if self.request.is_ajax():
self.object = form.save(commit=False) if self.request.user.get_score() >= form.cleaned_data.get('sum'):
self.object.customer = self.request.user stages_pk = [st for st in form.cleaned_data.get('stages_id').split(';') if st]
self.object.save() stages = Stage.objects.filter(pk__in=stages_pk)
data = { st_names = ','.join([st.order.project.name for st in stages]).rstrip(',')
'pk': self.object.pk, stage = stages.first()
'status': 'ok', order = stage.order
} self.object = form.save(commit=False)
self.object.customer = self.request.user
self.object.save()
data = {
'pk': self.object.pk,
'status': 'ok',
'order': order.pk,
'sender': self.request.user.pk,
'recipent': order.get_contractor_owner(),
'stages': st_names,
}
else:
data = {
'status': 'error',
'message_error': 'У вас недостаточно средств',
}
return JsonResponse(data) return JsonResponse(data)
return super().form_valid(form) return super().form_valid(form)

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-12 16:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('work_sell', '0012_merge'),
]
operations = [
migrations.AlterField(
model_name='worksell',
name='term_type',
field=models.CharField(blank=True, choices=[('day', 'день'), ('hour', 'час'), ('month', 'месяц'), ('project', 'проект')], default='hour', max_length=20, null=True),
),
]
Loading…
Cancel
Save