From 4c2de8364561432db9e3169ccc759b4ef0eb2557 Mon Sep 17 00:00:00 2001 From: Mukhtar Date: Tue, 6 Sep 2016 13:23:53 +0300 Subject: [PATCH] #ARC-18 Add button cancel stages for contractors --- api/views.py | 4 +- archilance/settings/base.py | 16 +- assets/js/chat.js | 27 ++- chat/admin.py | 2 +- chat/filters.py | 29 ++- chat/serializers.py | 2 - chat/templates/chat_contractor.html | 176 +++++++++++++++--- chat/templates/chat_customer.html | 69 ++++--- chat/views.py | 6 +- projects/models.py | 8 +- projects/serializers.py | 13 +- projects/templates/project_detail.html | 16 +- templates/partials/base.html | 2 +- templates/registration/registration_form.html | 6 + 14 files changed, 277 insertions(+), 99 deletions(-) diff --git a/api/views.py b/api/views.py index f66b1d7..4c816c0 100755 --- a/api/views.py +++ b/api/views.py @@ -30,7 +30,7 @@ from common.filters import LocationFilterSet from chat.models import Message, Notes, Documents from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer -from chat.filters import MessageFilterSet, NoteFilterSet, DocumentsFilterSet +from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet from reviews.models import Review from reviews.serializers import ReviewSerializer @@ -72,7 +72,7 @@ class ReviewViewSet(ModelViewSet): class DocumentViewSet(ModelViewSet): queryset = Documents.objects.all() serializer_class = DocumentsSerializer - # filter_class = DocumentsFilterSet + filter_class = DocumentFilterSet permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def get_queryset(self): diff --git a/archilance/settings/base.py b/archilance/settings/base.py index 61049ad..1ab73fa 100644 --- a/archilance/settings/base.py +++ b/archilance/settings/base.py @@ -166,6 +166,7 @@ AUTHENTICATION_BACKENDS = ( 'social.backends.twitter.TwitterOAuth', 'social.backends.vk.VKOAuth2', 'social.backends.odnoklassniki.OdnoklassnikiOAuth2', + 'social.backends.mailru.MailruOAuth2', # 'django.contrib.auth.backends.ModelBackend', 'users.backend.EmailOrUsernameModelBackend', ) @@ -185,8 +186,9 @@ SOCIAL_AUTH_ODNOKLASSNIKI_OAUTH2_KEY = '1247035904' SOCIAL_AUTH_ODNOKLASSNIKI_OAUTH2_SECRET = '9AD83DB399405EEFAE7641BD' SOCIAL_AUTH_ODNOKLASSNIKI_OAUTH2_PUBLIC_NAME = 'CBADEFFLEBABABABA' -SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '499898042244-bt7v18v4f46k8qg98n1ne8u2hjtmj0cn.apps.googleusercontent.com' -SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 's69NCyhSlwY0OuGGT8_dFI7E' +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '54397003678-ejfrg1la2vh2jdjq7fb1upc916kd6djo.apps.googleusercontent.com' +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'UOLE-UM9bo2UL7i3TXy_WPa5' + # SOCIAL_AUTH_TWITTER_KEY = 'YAe05K4IYYxHhA6J1mTOdDBjq' # SOCIAL_AUTH_TWITTER_SECRET = 'iRuYqRRaoGkCD4ip74NICb8FeZMxvM6MZ8HLMbm1jX99o7pcaL' @@ -197,6 +199,10 @@ SOCIAL_AUTH_TWITTER_SECRET = 'WhXRHP6BzNwFS8x94pcaCBwxCSMkAEVm3Rg82XhzUaIqsf2Ur0 SOCIAL_AUTH_VK_OAUTH2_KEY = '5542865' SOCIAL_AUTH_VK_OAUTH2_SECRET = 'BsOSDhmyNiDte7cMJlVq' + +SOCIAL_AUTH_MAILRU_OAUTH2_KEY = 'f7bad5797a375a5eeba6217d64de71f4' +SOCIAL_AUTH_MAILRU_OAUTH2_SECRET = '480fd6d67e9e8625fbc6b6b9a8ec71f0' + SOCIAL_AUTH_VK_OAUTH2_SCOPE = [ 'notify', 'friends', @@ -204,9 +210,9 @@ SOCIAL_AUTH_VK_OAUTH2_SCOPE = [ ] -# SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/' -# SOCIAL_AUTH_NEW_USER_REDIRECT_URL = 'http://proekton.com/' -# SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL = 'http://proekton.com/' +SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/' +SOCIAL_AUTH_NEW_USER_REDIRECT_URL = '/' +SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL = '/' SOCIAL_AUTH_PIPELINE = ( 'social.pipeline.social_auth.social_details', diff --git a/assets/js/chat.js b/assets/js/chat.js index 7789c31..c5632e1 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -1,6 +1,6 @@ var SocketHandler = function () { domain = domain.replace(':' + port, ''); - var url = 'ws://' + domain + ':8888/chat/' + userId + '/'; + var url = 'ws://' + domain + '/chat/' + userId + '/'; var sock = new WebSocket(url); var intervalId; sock.onopen = function () { @@ -192,6 +192,29 @@ $(function () { }); + //Удаление документа + $('.tab-content').on('click','.remove-document', function(e){ + e.preventDefault(); + var dataId = $(this).attr('data-id'); + var _this = $(this); + $.ajax({ + url: '/api/documents/' + dataId +'/', + type: 'DELETE', + beforeSend: function (xhr) { + xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) + }, + dataType: 'json', + success: function(json){ + _this.parent().remove(); + console.log(json); + }, + error: function (e, jqxhr) { + console.log(jqxhr); + } + }); + }); + + // Вытащить сообщения для конактов $('.user-block').on('click', function () { @@ -228,7 +251,7 @@ $(function () { console.log(json); $.each(json.results, function (i, v) { - docList.innerHTML += '
  • ' + v.file + '
  • '; + docList.innerHTML += '
  • ' + v.file + '
  • '; }); }, error: function (e) { diff --git a/chat/admin.py b/chat/admin.py index d91a0a7..0c43269 100644 --- a/chat/admin.py +++ b/chat/admin.py @@ -3,7 +3,7 @@ from .models import Message, Notes, Documents class MessageAdmin(admin.ModelAdmin): - list_display = ('text', 'sender', 'recipent',) + list_display = ('text', 'sender', 'recipent', 'is_new',) class NotesAdmin(admin.ModelAdmin): diff --git a/chat/filters.py b/chat/filters.py index 1cd04b2..2027c08 100644 --- a/chat/filters.py +++ b/chat/filters.py @@ -2,16 +2,16 @@ from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter from .models import Message, Notes, Documents - -class DocumentsFilterSet(FilterSet): - file = AllLookupsFilter() - sender = RelatedFilter('users.filters.UserFilterSet') - recipent = RelatedFilter('users.filters.UserFilterSet') - # order = AllLookupsFilter() - # team = AllLookupsFilter() - - class Meta: - model = Documents +# +# class DocumentsFilterSet(FilterSet): +# file = AllLookupsFilter() +# # sender = RelatedFilter('users.filters.UserFilterSet') +# # recipent = RelatedFilter('users.filters.UserFilterSet') +# # order = RelatedFilter('projects.filters.OrderFilterSet') +# # order = AllLookupsFilter() +# +# class Meta: +# model = Documents class MessageFilterSet(FilterSet): @@ -26,6 +26,15 @@ class MessageFilterSet(FilterSet): model = Message +class DocumentFilterSet(FilterSet): + id = AllLookupsFilter() + # order = RelatedFilter('projects.filters.OrderFilterSet') + + class Meta: + model = Documents + + + class NoteFilterSet(FilterSet): text = AllLookupsFilter() created = AllLookupsFilter() diff --git a/chat/serializers.py b/chat/serializers.py index 00fa0ef..99a7d9c 100644 --- a/chat/serializers.py +++ b/chat/serializers.py @@ -36,7 +36,6 @@ class DocumentsSerializer(ModelSerializer): return obj.file.url - class MessageSerializer(ModelSerializer): sender = UserSerializer() recipent = UserSerializer() @@ -79,4 +78,3 @@ class NoteSerializer(ModelSerializer): 'sender', 'recipent', ) - diff --git a/chat/templates/chat_contractor.html b/chat/templates/chat_contractor.html index 38263a8..4aef569 100644 --- a/chat/templates/chat_contractor.html +++ b/chat/templates/chat_contractor.html @@ -23,11 +23,11 @@ Заказчики - {% if team_orders %} +
  • Исполнители
  • - {% endif %} + @@ -142,9 +142,9 @@
    - - - + + +
    @@ -204,6 +204,15 @@ {% include 'arbitration_modal.html' %} + +
    +

    Входящие документы

    +
      +{# #} +{# Распечатать с помощью ресурса#} +{# #} +
      +
      @@ -220,7 +229,7 @@
      - {% if team_orders %} +
      @@ -262,13 +271,47 @@
      {% endfor %} + + {% for yteam in your_teams %} +
      + +

      + {{ yteam }} +

      +
      +

      + Владелец группы: {{ yteam.owner }} +

      +
        + {% for tuser in yteam.contractors.all %} +
      • {{ tuser }}
      • + {% endfor %} +
      +

      + Чаты: + {% if request.user.pk != torder.team.owner.pk %} + {{ torder.team.owner.username }}, + {% endif %} + {% for tuser in yteam.contractors.all %} + {% if request.user.pk != tuser.pk %} + {{ tuser.username }}, + {% endif %} + {% endfor %} +

      + +
      +
      + + {% endfor %}
      - + @@ -292,7 +335,7 @@
      - {% endif %} + {% include 'order_info.html' %} @@ -322,15 +365,20 @@ }, 1000); var url = '/chat/create/'; + + $("#upload-document-team").bind('fileuploadsubmit', function (e, data) { + data.formData = { + sender: $("#team-chat-form #senderTeamId").val(), + recipent: $("#team-chat-form #recipentTeamId").val(), + order: $("#team-chat-form #orderTeamId").val(), + team: $("#team-chat-form #teamId").val(), + } + console.log(data.formData); + }); + //Загрузка документов $('#upload-document-team').fileupload({ url: url, - formData: { - sender: $("#team-chat-form #senderId").val(), - recipent: $("#team-chat-form #recipentId").val(), - team: $("#team-chat-form #teamId").val(), - order: $("#team-chat-form #orderId").val(), - }, crossDomain: false, beforeSend: function (xhr, settings) { $('#progress .progress-bar').css( @@ -402,10 +450,48 @@ }); + // Согласование этапов + $("#order-stages").on('click', "#cancel-stages", function (e) { + e.preventDefault(); + $(".stage-block-approve").each(function () { + var stageId = $(this).attr('data-id'); + $.ajax({ + url: '/api/stages/' + stageId + '/', + type: 'PATCH', + beforeSend: function (xhr) { + xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')) + }, + data: "status=cancel_approve", + dataType: 'json', + success: function (json) { + console.log(json); + + }, + error: function (e) { + console.log('error'); + console.log(e); + } + }); + + }); + var orderId = $(this).attr('data-order-id'); + + socket.send_stages_approve({ + "format_type": "approve_stages", + "data": { + "sender_id": $(this).attr('data-sender-id'), + "recipent_id": $(this).attr('data-recipent-id'), + "order_id": orderId, + "msg": "Исполнитель отказался от текущих этапов " + orderId, + } + }); + + }); + $(".team-chat-user").on('click', function (e) { e.stopPropagation(); var recipentId = $(this).attr('data-id'); - $("#team-chat-form #recipentId").val(recipentId); + $("#team-chat-form #recipentTeamId").val(recipentId); }); $(".team-order-block").on('click', function () { @@ -418,7 +504,7 @@ var teamId = $(this).attr('data-team-id'); var orderId = $(this).attr('data-order-id'); $("#team-chat-form #teamId").val(teamId); - $("#team-chat-form #orderId").val(orderId); + $("#team-chat-form #orderTeamId").val(orderId); var inbox = document.getElementById('message-chat-team-space'); inbox.innerHTML = ''; @@ -464,6 +550,7 @@ $("#targetCustomerId").val(recipentId); $("#add-form-order-note #recipentNote").val(recipentId); + var docList = document.getElementById('documentOrderSpace'); var inbox = document.getElementById('message-chat-order-space'); inbox.innerHTML = ''; @@ -487,6 +574,24 @@ } }); + $.ajax({ + url:'/api/documents', + type: 'GET', + data:{ + csrfmiddlewaretoken: csrftoken, + 'order': orderId + }, + dataType: 'json', + success: function (json){ + $.each(json.results, function (i, v) { + docList.innerHTML += '
    • '+ v.file+'
    • '; + }); + }, + error: function(e){ + console.log(e); + } + }); + $.ajax({ url: '/api/note/', type: 'GET', @@ -508,7 +613,6 @@ type: 'GET', data:{csrfmiddlewaretoken: csrftoken}, dataType: 'json', - }).then(function(data){ var htmlInbox = ""; var stagesReservedHtml = ""; @@ -522,7 +626,7 @@ if (v.status == "completed"){ stagesCompleted.push(v); } - if(v.status == "not_agreed"){ + if(v.status == "not_agreed" || v.status == "send_approve"){ statusNotAgreed = true; } if(!data.secure){ @@ -544,6 +648,23 @@ } var statusName = ''; + switch(v.status){ + case 'not_agreed': + statusName = 'Не согласован'; + break; + case 'send_approve': + statusName = 'На согласовании'; + break; + case 'cancel_approve': + statusName = 'Исполнитель отказался'; + break; + case 'in_process': + statusName = 'В процессе'; + break; + case 'completed': + statusName = 'Завершен'; + break; + } if (v.status == 'completed'){ statusName = 'Завершен'; } @@ -557,7 +678,10 @@ if (statusNotAgreed) { htmlInbox += '
      ' + 'согласовать
      '; + ' data-order-id="' + orderId + '" href="#">согласовать' + + 'отказаться' + + ''; } } @@ -661,18 +785,17 @@ }); //Добавить сообщение для исполнителей в группе - $("#add-team-chat-message").on('click', function () { + $("#add-team-chat-message").on('click', function (e) { + e.preventDefault(); var chatMessage = $("#team-chat-form #chatText").val(); - var recipentId = $("#team-chat-form #recipentId").val(); - var senderId = $("#team-chat-form #senderId").val(); + var recipentId = $("#team-chat-form #recipentTeamId").val(); + var senderId = $("#team-chat-form #senderTeamId").val(); var teamId = $("#team-chat-form #teamId").val(); - var orderId = $("#team-chat-form #orderId").val(); - + var orderId = $("#team-chat-form #orderTeamId").val(); var documentSendIds = $("#documentSendIds").val(); - console.log(documentSendIds); var teamDocumentIds = documentSendIds.split(';'); + teamDocumentIds.pop(); - console.log(teamDocumentIds); socket.add_team_message({ "format_type": "add_message_team", "data": { @@ -686,6 +809,7 @@ $("#team-chat-form #chatText").val(""); $("#document-send").html(""); + $("#documentSendIds").val(""); }); diff --git a/chat/templates/chat_customer.html b/chat/templates/chat_customer.html index 9d1669d..2a8ad15 100644 --- a/chat/templates/chat_customer.html +++ b/chat/templates/chat_customer.html @@ -89,8 +89,10 @@ {# #} {# Распечатать с помощью ресурса#} {# #} - + +
      +

      Заметки

      @@ -337,6 +339,7 @@ data:{csrfmiddlewaretoken: csrftoken}, dataType: 'json', }).then(function(data){ + var isReviewLeave = data.has_user_review; var stagesResults = data.stages; var stageCount = stagesResults.length; if (stageCount == 0) { @@ -352,12 +355,13 @@ if (stageCount == 0) { htmlInboxStage += '
      ' + '

      Этап 1

      ' + - '' + - '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '
      '; } var statusNotAgreed = true; @@ -384,19 +388,20 @@ stagePaidCount +=1; } - if (v.status == "not_agreed") { + if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') { htmlInbox += '
      ' + '

      Этап

      ' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + '' + - '' + + '' + '
      '; } else { statusNotAgreed = false; htmlInboxStage = ""; - var statusName = ''; + var statusName = ""; if (v.status == 'completed'){ statusName = 'Завершен'; } @@ -464,17 +469,12 @@ $("#reserveSpace").hide(); } - if((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0)){ + if((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)){ $("#leaveReview").show(); - console.log("Все этапы завершены"); }else { $("#leaveReview").hide(); } - if (data.status == 'completed'){ - $("#leaveReview").hide(); - } - $(".stages-paid").html(stagesReservedHtml); }); @@ -593,17 +593,12 @@ } }); -{# setTimeout(function () {#} -{# getStages(currentOrderId,userId,currentRecipentId,secureOrder);#} -{##} -{# }, 1000);#} - }); //Изменение счетчика $('#order-stages-tab').on('change', '#countStage', function () { var countStage = parseInt($(this).val()); - var currentCountStage = $(".numberStepp").length; + var currentCountStage = $("#order-stages .numberStepp").length; if ((countStage<1) || isNaN(countStage)) { countStage = 1; $('#order-stages-tab #countStage').val(currentCountStage); @@ -611,23 +606,23 @@ if (countStage > currentCountStage) { for (var jj = currentCountStage; jj < countStage; jj++) { var pos = jj + 1; - var lastFormStage = $(".numberStepp").last(); + var lastFormStage = $("#order-stages .numberStepp").last(); var orderId = lastFormStage.find('.orderStagesInput').val(); var addFormTemplate = '
      ' + '

      Этап ' + pos + '

      ' + - '' + - '' + - '' + - '' + - '' + - '' - '
      '; + '' + + '' + + '' + + '' + + '' + + '' + + '
      '; lastFormStage.after(addFormTemplate); } } else if (countStage < currentCountStage) { var ii = currentCountStage; - $($(".numberStepp").get().reverse()).each(function () { + $($("#order-stages .numberStepp").get().reverse()).each(function () { var currenFormName = ($(this).find('form').attr('class')); if (ii > countStage) { $(this).remove(); @@ -639,6 +634,8 @@ } }); + + // Для заказов все вытащить $('.order-block').on('click', function () { $("#chat-order-add").css("display", "block"); @@ -675,10 +672,8 @@ }, dataType: 'json', success: function (json){ - console.log(json); - $.each(json.results, function (i, v) { - docList.innerHTML += '
    • '+ v.file+'
    • '; + docList.innerHTML += '
    • '+ v.file+'
    • '; }); }, error: function(e){ diff --git a/chat/views.py b/chat/views.py index 8525bd5..3b93839 100644 --- a/chat/views.py +++ b/chat/views.py @@ -69,6 +69,7 @@ class ChatUserView(LoginRequiredMixin, View): if request.user.is_owner_team(): team_ids.append(request.user.team.pk) team_orders = request.user.team.orders.all() + teams = Team.objects.filter(contractors__id=request.user.pk).all() else: teams = Team.objects.filter(contractors__id=request.user.pk).all() team_orders = Order.objects.filter(team_id__in=[team.pk for team in teams]).all() @@ -89,14 +90,13 @@ class ChatUserView(LoginRequiredMixin, View): chat_messages = Message.objects.filter(Q(sender=request.user.pk) | Q(recipent=request.user.pk)).order_by( 'created') - - + your_teams = teams self.template_name = 'chat_contractor.html' return render(request, self.template_name, {'orders': orders, 'contacts_users': contacts_users, 'chat_messages': chat_messages, 'team_orders': team_orders, - + 'your_teams': your_teams, }) diff --git a/projects/models.py b/projects/models.py index 5136c1e..04fe1ee 100644 --- a/projects/models.py +++ b/projects/models.py @@ -237,9 +237,11 @@ class Arbitration(models.Model): STATUSES = ( - ('not_agreed','Не согласован'), - ('in_process','В процессе'), - ('completed','Завершен'), + ('not_agreed', 'Не согласован'), + ('send_approve', 'На согласовании'), + ('cancel_approve', 'Исполнитель отказался'), + ('in_process', 'В процессе'), + ('completed', 'Завершен'), ) diff --git a/projects/serializers.py b/projects/serializers.py index 92f9b83..4984c64 100755 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -123,9 +123,16 @@ class OrderSerializer(ModelSerializer): ) def get_has_user_review(self,obj): - # obj.project. - print(self.context['request'].user) - return "yes" + curr_user = self.context['request'].user + if curr_user.is_customer(): + return curr_user.customer_reviews.filter(project=obj.project).exists() + elif curr_user.is_contractor(): + if obj.team is None and obj.contractor: + return curr_user.contractor_reviews.filter(project=obj.project).exists() + elif curr_user.team: + return curr_user.team.team_reviews.filter(project=obj.project).exists() + else: + return False class PortfolioPhotoSerializer(ModelSerializer): diff --git a/projects/templates/project_detail.html b/projects/templates/project_detail.html index 8015dfe..da3f7cd 100644 --- a/projects/templates/project_detail.html +++ b/projects/templates/project_detail.html @@ -27,7 +27,9 @@
      {% if project.customer.avatar %} - execitor-image + {% thumbnail project.customer.avatar "125x125" crop="center" as im %} + execitor-image + {% endthumbnail %} {% else %} execitor-image {% endif %} @@ -517,7 +519,9 @@