diff --git a/assets/css/main.css b/assets/css/main.css
index 46f23b6..1dd75f8 100644
--- a/assets/css/main.css
+++ b/assets/css/main.css
@@ -237,14 +237,14 @@ ul li {
}
.changeBlock1 > a, .changeBlock1 > a:link, .changeBlock1 > a:visited {
- background: url('../img/button1.png') no-repeat 25px , white;
- color: black;
+ background: url('../img/button1.png') no-repeat 25px , black;
+ color: white;
padding: 24px 26px 20px 72px;
}
.changeBlock2 > a, .changeBlock2 > a:link, .changeBlock2 > a:visited {
- background: url('../img/button2.png') no-repeat 27px, black;
- color: white;
+ background: url('../img/button2.png') no-repeat 27px, white;
+ color: black;
padding: 24px 26px 20px 72px;
}
diff --git a/assets/js/chat.js b/assets/js/chat.js
index ad3b264..b05cd8a 100644
--- a/assets/js/chat.js
+++ b/assets/js/chat.js
@@ -1,6 +1,5 @@
window.confirm = function (message, callback, caption) {
caption = caption || ''
-
$(document.createElement('div')).attr({
title: caption,
'class': 'dialog'
@@ -20,7 +19,6 @@ window.confirm = function (message, callback, caption) {
callback()
return true;
}
-
},
close: function () {
$(this).remove();
@@ -31,7 +29,7 @@ window.confirm = function (message, callback, caption) {
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 () {
@@ -109,22 +107,6 @@ var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken');
-
-function test_dialog(message){
- var resStatus;
- $("#dialog_delete .modal-title").html(message);
- $("#dialog_delete").modal('show');
- $("#btnYes").click(function (e) {
- $("#dialog_delete").modal('hide');
- resStatus = true;
- });
- $("#btnNot").click(function (e) {
- $("#dialog_delete").modal('hide');
- resStatus = false;
- });
- return resStatus;
-}
-
$(function () {
function dialog (message, yesCallback, notCallback) {
$("#dialog_delete .modal-title").html(message);
@@ -682,33 +664,6 @@ function dialog (message, yesCallback, notCallback) {
});
- // Добавление отзыва
- $('#order-review-add').on('click', function (e) {
- e.preventDefault();
- e.stopPropagation();
- var formData = $("#review-adds-form").serialize();
- $.ajax({
- url: '/api/reviews/',
- type: 'POST',
- beforeSend: function (xhr) {
- xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
- },
- data: formData,
- dataType: 'json',
- success: function (json) {
- console.log("Успешно");
- $("#review-add").modal('hide');
- $.jGrowl("Ваш отзыв успешно добавлен", {
- life: 4000
- });
- },
- error: function (e) {
- console.log('error');
- console.log(e);
- }
- });
- });
-
// Добавление сообщения в арбитраж
$('#order-arbitration-add').on('click', function (e) {
e.preventDefault();
@@ -761,7 +716,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
- var htmlImg = '' + file.name + '
';
+ var htmlImg = '';
var document_send = $(htmlImg).appendTo("#document-send-order");
});
},
@@ -802,7 +758,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
- var htmlImg = '' + file.name + '';
+ var htmlImg = '';
var document_send = $(htmlImg).appendTo("#document-send-contact");
});
},
@@ -824,6 +781,39 @@ function dialog (message, yesCallback, notCallback) {
var relatedType = related.attr('data-review-type');
$('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true);
});
+
+ // Добавление отзыва
+ $('#order-review-add').on('click', function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ var formData = $("#review-adds-form").serialize();
+ $.ajax({
+ url: '/api/reviews/',
+ type: 'POST',
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
+ },
+ data: formData,
+ dataType: 'json',
+ success: function (json) {
+ console.log(json);
+ $("#review-add").modal('hide');
+ //socket.send_stages_approve({
+ // "format_type": "approve_stages",
+ // "data": {
+ // "sender_id": userId,
+ // "recipent_id": '',
+ // "order_id": '',
+ // "msg": "Отзыв добавлен",
+ // }
+ //});
+ },
+ error: function (e) {
+ console.log('error');
+ console.log(e);
+ }
+ });
+ });
});
diff --git a/assets/js/chat_contractor.js b/assets/js/chat_contractor.js
index 5e86dc0..5ba73f1 100644
--- a/assets/js/chat_contractor.js
+++ b/assets/js/chat_contractor.js
@@ -48,7 +48,8 @@ $(function () {
var currentValue = $("#documentSendIds").val();
currentValue += file.id + ';';
$("#documentSendIds").val(currentValue);
- var htmlImg = '' + file.name + '';
+ var htmlImg = '';
var document_send = $(htmlImg).appendTo("#document-send");
});
},
@@ -82,8 +83,7 @@ $(function () {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
- //data: "status=in_process",
- data: "status=send_approve",
+ data: "status=in_process",
dataType: 'json',
done: function (json) {
console.log(json);
@@ -127,8 +127,8 @@ $(function () {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
- //data: "status=cancel_approve",
- data: "status=send_approve",
+ data: "status=cancel_approve",
+ //data: "status=send_approve",
dataType: 'json',
done: function (json) {
console.log(json);
@@ -438,6 +438,7 @@ $(function () {
var stagesCompleted = [];
if (stagesResults.length > 0) {
$.each(stagesResults, function (i, v) {
+ console.log(v.status);
if (v.status == "completed") {
stagesCompleted.push(v);
}
@@ -488,7 +489,7 @@ $(function () {
'Этап ' + v.pos + '' + v.name + '
' +
'Результаты этапа:' + v.result + '
' +
'
Срок до ' + v.term + '
' + v.cost + ' ' +
- '
Cрок заказа рассчитывается с момента резервирования средств
' +
+ '
Cрок этапа рассчитывается с момента резервирования средств
' +
'
';
});
@@ -534,13 +535,11 @@ $(function () {
$("#completeWork").hide();
}
- if (stagesCompleted.length == stagesResults.length && stagesCompleted.length > 0) {
- $("#leaveReview").show();
- }
-
- if (data.status == 'completed') {
- $("#leaveReview").hide();
- }
+ if ((stagesCompleted.length == stagesResults.length) && stagesCompleted.length > 0 && !data.has_user_review) {
+ $("#leaveReview").show();
+ }else{
+ $("#leaveReview").hide();
+ }
});
diff --git a/assets/js/chat_customer.js b/assets/js/chat_customer.js
index 2132ff1..d614782 100644
--- a/assets/js/chat_customer.js
+++ b/assets/js/chat_customer.js
@@ -99,12 +99,12 @@ $(function () {
if (stageCount == 0) {
htmlInboxStage += '';
}
@@ -135,12 +135,13 @@ $(function () {
if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') {
htmlInbox += '';
} else {
statusNotAgreed = false;
@@ -226,12 +227,15 @@ $(function () {
$("#reserveSpace").hide();
}
- if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) {
- $("#leaveReview").show();
+ if (!data.has_user_review) {
+ if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) {
+ $("#leaveReview").show();
+ } else {
+ $("#leaveReview").hide();
+ }
} else {
$("#leaveReview").hide();
}
-
$(".stages-paid").html(stagesReservedHtml);
});
@@ -297,80 +301,75 @@ $(function () {
var callbacks = [];
-
$(".new-stages-form").each(function (i, v) {
var _this = $(this);
- callbacks.push($.ajax({
- url: '/api/stages/',
- type: 'POST',
- beforeSend: function (xhr) {
- xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
- },
- data: $(this).serialize(),
- dataType: 'json',
- done: function (json) {
+ $.ajax({
+ url: '/api/stages/',
+ type: 'POST',
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
+ },
+ data: $(this).serialize(),
+ dataType: 'json'
+ })
+ .done(function (json) {
_this.removeClass('new-stages-form').addClass('update-stages-form');
+ _this.find('.error').html("");
console.log(json);
- },
- fail: function (xhr, errorMsg,error) {
- $.each(xhr.responseJSON, function(i,v){
+ })
+ .fail(function (xhr, errorMsg, error) {
+ console.log(xhr);
+ $.each(xhr.responseJSON, function (i, v) {
+ _this.find('.error-' + i).html(v).css('color', 'red');
console.log(v);
console.log(i);
});
- },
-
- }));
-
+ });
});
- //$.when(callbacks).then(function(x){
- // console.log(x);
- // $.each(x,function(i,v){
- // console.log(v);
- // });
- // alert("Запросы успешно выполнились");
- //}, function(){
- // alert("Произошла ошибка в запросах");
- //});
$(".update-stages-form").each(function (i, v) {
+ var _this = $(this);
var currentStageId = parseInt($(this).attr('data-stage-id'));
+
$.ajax({
- url: '/api/stages/' + currentStageId + '/',
- type: 'PUT',
- beforeSend: function (xhr) {
- xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
- },
- data: $(this).serialize(),
- dataType: 'json',
- success: function (json) {
- console.log(json);
- },
- error: function (e) {
- console.log('error');
- console.log(e);
- }
- });
+ url: '/api/stages/' + currentStageId + '/',
+ type: 'PUT',
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
+ },
+ data: $(this).serialize(),
+ dataType: 'json'
+ })
+ .done(function (json) {
+ _this.find('.error').html("");
+ })
+ .fail(function (xhr, errorMsg, error) {
+ $.each(xhr.responseJSON, function (i, v) {
+ _this.find('.error-' + i).html(v).css('color', 'red');
+ console.log(v);
+ console.log(i);
+ });
+ });
});
-
var currentRecipentId = $(this).attr('data-recipent-id');
var secureOrder = true
- //socket.send_stages_approve({
- // "format_type": "approve_stages",
- // "data": {
- // "sender_id": userId,
- // "recipent_id": currentRecipentId,
- // "order_id": currentOrderId,
- // "msg": "Этапы для заказа " + currentOrderId + " изменены",
- // }
- //});
+ socket.send_stages_approve({
+ "format_type": "approve_stages",
+ "data": {
+ "sender_id": userId,
+ "recipent_id": currentRecipentId,
+ "order_id": currentOrderId,
+ "msg": "Этапы для заказа " + currentOrderId + " изменены",
+ }
+ });
});
- //Изменение счетчика
+//Изменение счетчика
$('#order-stages-tab').on('change', '#countStage', function () {
var countStage = parseInt($(this).val());
var currentCountStage = $("#order-stages .numberStepp").length;
@@ -385,13 +384,13 @@ $(function () {
var orderId = lastFormStage.find('.orderStagesInput').val();
var addFormTemplate = '';
+ '' +
+ '' +
+ '';
lastFormStage.after(addFormTemplate);
}
@@ -409,7 +408,7 @@ $(function () {
}
});
- // Для заказов все вытащить
+// Для заказов все вытащить
$('.order-block').on('click', function () {
$("#chat-order-add").css("display", "block");
$("#formsetStage").css("display", "block");
@@ -465,7 +464,7 @@ $(function () {
});
$.ajax({
- url: '/api/users/{{ request.user.pk }}/',
+ url: '/api/users/' + userId + '/',
type: 'GET',
data: {
csrfmiddlewaretoken: csrftoken,
@@ -520,4 +519,5 @@ $(function () {
getStages(orderId, userId, recipentId, secureOrder);
});
-});
+})
+;
diff --git a/chat/serializers.py b/chat/serializers.py
index 74da60e..31c153c 100644
--- a/chat/serializers.py
+++ b/chat/serializers.py
@@ -44,6 +44,7 @@ class MessageSerializer(ModelSerializer):
documents = DocumentsSerializer(read_only=True, many=True)
text = serializers.SerializerMethodField()
+
class Meta:
model = Message
diff --git a/chat/templates/chat_contractor.html b/chat/templates/chat_contractor.html
index 83b7bc1..095e7fd 100644
--- a/chat/templates/chat_contractor.html
+++ b/chat/templates/chat_contractor.html
@@ -235,7 +235,6 @@
-
diff --git a/projects/models.py b/projects/models.py
index 5b1ae47..3a97c1c 100644
--- a/projects/models.py
+++ b/projects/models.py
@@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
+from django.core.exceptions import ValidationError
from django.utils import timezone
from hitcount.models import HitCountMixin
from mptt.models import TreeForeignKey
@@ -262,7 +263,7 @@ class Stage(models.Model):
name = models.CharField(max_length=255)
order = models.ForeignKey(Order, related_name='stages')
result = models.CharField(max_length=255)
- term = models.DateField(validators=[validate_term])
+ term = models.DateField()
term_type = models.CharField(max_length=10, choices=TERM_TYPES, default='hour')
status = models.CharField(choices=STATUSES, max_length=30, default='not_agreed')
created = models.DateTimeField(default=timezone.now)
@@ -275,12 +276,17 @@ class Stage(models.Model):
def __str__(self):
return self.name
- # def clean(self, *args, **kwargs):
- # super().clean(*args, **kwargs)
- #
- # def save(self, *args, **kwargs):
- # # self.full_clean()
- # super().save(*args, **kwargs)
+ def clean(self, *args, **kwargs):
+ pass
+ # stage_last = self.__class__.objects.filter(order=self.order).order_by('-pos')[:1]
+ # if stage_last:
+ # stage_last = stage_last[0]
+ # if stage_last.term > self.term:
+ # raise ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
+
+ def save(self, *args, **kwargs):
+ self.full_clean()
+ super().save(*args, **kwargs)
class Meta:
ordering = ['pos']
diff --git a/projects/serializers.py b/projects/serializers.py
index e17b96d..3ffbe97 100755
--- a/projects/serializers.py
+++ b/projects/serializers.py
@@ -79,6 +79,16 @@ class RealtySerializer(ModelSerializer):
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:
+ pos = data['pos'] -1
+ stage_last = Stage.objects.filter(order=data['order'], pos=pos)
+ if stage_last:
+ stage_last = stage_last[0]
+ if stage_last.term > data['term']:
+ raise serializers.ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
+ return data
+
class Meta:
model = Stage
diff --git a/templates/partials/base.html b/templates/partials/base.html
index 75da402..6530a3f 100644
--- a/templates/partials/base.html
+++ b/templates/partials/base.html
@@ -86,7 +86,7 @@
if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) {
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 () {
diff --git a/users/models.py b/users/models.py
index 3e95239..e21612e 100644
--- a/users/models.py
+++ b/users/models.py
@@ -11,7 +11,7 @@ import pydash as _; _.map = _.map_; _.filter = _.filter_
from archilance import util
from specializations.models import Specialization
-
+# from chat.models import NewMessage
GENDERS = (
@@ -238,7 +238,7 @@ class Team(models.Model):
specializations = TreeManyToManyField(Specialization, related_name='teams', blank=True)
contractors = models.ManyToManyField(User, limit_choices_to={'groups__name': 'Исполнители'}, related_name ='teams', blank=True)
rating = models.FloatField(default=0.0)
-
+
def __str__(self):
return self.name
@@ -247,6 +247,7 @@ class Team(models.Model):
verbose_name_plural = 'Команды'
+
class TeamInvitation(models.Model):
contractor1 = models.ForeignKey(User, related_name='invites')
contractor2 = models.ForeignKey(User, related_name='invited_by')
diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html
index adc7804..b6785a2 100644
--- a/users/templates/contractor_office.html
+++ b/users/templates/contractor_office.html
@@ -2,42 +2,44 @@
{% load specializtions_tags %}
{% load thumbnail %}
-
+{% load user_tags %}
{% block content %}
{% include 'partials/modals/add_team_member.html' %}
{% include 'partials/header.html' %}
-
+
-
+
{% include 'partials/contractor_profile_tabs.html' %}
-
+