add tab deal in client edit

This commit is contained in:
mac 2026-06-21 09:11:22 +03:00
parent b188bbe138
commit 76182895c6
3 changed files with 1375 additions and 1116 deletions

218
js/crm.js
View File

@ -2446,9 +2446,15 @@ function renewClients(id_clients) {
}
function editClientNew(id, funnelId) {
// hide_client_contacts: возвращаем модалку в edit-режим (disabled+warning),
// т. к. add_client_new() в этой же сессии мог переключить её в add-режим.
setAddEditNewContactsState('edit');
if (typeof requisitions_client == 'object') {
requisitions_client.clientId = id;
}
if (typeof deals_client == 'object') {
deals_client.clientId = id;
}
//кастомные поля
@ -4312,6 +4318,7 @@ $(document).ready(function() {
$('#liGlobal').removeClass('active');
$('#liIpoteka').removeClass('active');
$('#liCredit').removeClass('active');
$('#liDeals').removeClass('active');
$('#liInnerDocs').removeClass('active');
$(this).addClass('active');
$('#addEditNew .tabs__content').hide();
@ -4322,6 +4329,21 @@ $(document).ready(function() {
});
$('body').on('click', '#liDeals', function() {
$('#liObject').removeClass('active');
$('#liGlobal').removeClass('active');
$('#liIpoteka').removeClass('active');
$('#liCredit').removeClass('active');
$('#liReq').removeClass('active');
$('#liInnerDocs').removeClass('active');
$(this).addClass('active');
$('#addEditNew .tabs__content').hide();
$('#addEditNew .tabs__content:eq(5)').show();
if (typeof deals_client == 'object') {
deals_client.getList();
}
});
$('body').on('click', '#liInnerDocs', function() {
$('#liObject').removeClass('active');
$('#liGlobal').removeClass('active');
@ -6533,6 +6555,9 @@ $(document).ready(function() {
if (typeof requisitions_client == 'object') {
requisitions_client.clientId = id;
}
if (typeof deals_client == 'object') {
deals_client.clientId = id;
}
//console.log(id);
$('#liGlobal').removeClass('active').show();
$('#liObject').click();
@ -7075,12 +7100,21 @@ $(document).ready(function() {
var text = $(mainId + ' .tabs__content.call #callComment').val();
var date = $(mainId + ' .tabs__content.call #showDatetime').datepicker().val();
//console.log(date);
if (date && $.trim(date)) {
var id_client = $(mainId + ' .tabs__content.call .ms-big-add-client').val();
var users_id = $(mainId + ' .tabs__content.call .ms-crm-client-work').val();
// при редактировании (data-id задан и не 0) поле «Ответственный» скрыто и не меняется — не требуем его
var isEdit = $(this).attr("data-id") !== undefined && $(this).attr("data-id") !== '0';
$(mainId + ' .tabs__content.call #showDatetime').removeClass('invalid');
$(mainId + ' .tabs__content.call .otvetstven').removeClass('invalid');
//console.log(date);
if (date && $.trim(date) && (isEdit || !isEmpty(users_id))) {
var id_client = $(mainId + ' .tabs__content.call .ms-big-add-client').val();
var data = {
"client_id": JSON.stringify(id_client),
"client_id": (!isEmpty(id_client) ? JSON.stringify(id_client) : ''),
"comment": text,
"schedule_date": date,
"users_id": JSON.stringify(users_id),
@ -7107,7 +7141,7 @@ $(document).ready(function() {
method: "post",
data: data,
success: function(d) {
//console.log(data);
console.log(d);
$("#addTaskBg").hide();
$("#addTask").hide();
$("#addEventBg").hide();
@ -7117,6 +7151,14 @@ $(document).ready(function() {
getCalendar();
}
});
} else {
if(isEmpty($.trim(date))){
$(mainId + ' .tabs__content.call #showDatetime').addClass('invalid');
}
if(!isEdit && isEmpty(users_id)){
$(mainId + ' .tabs__content.call .otvetstven').addClass('invalid');
}
}
@ -7133,13 +7175,21 @@ $(document).ready(function() {
var date = $(mainId + ' .tabs__content.show #meetDatetime').datepicker().val();
var address = $(mainId + ' .tabs__content.show #meetAddress').val();
//console.log(date);
if (date && $.trim(date)) {
var id_client = $(mainId + ' .tabs__content.show .ms-big-add-client').val();
var users_id = $(mainId + ' .tabs__content.show .ms-crm-client-work').val();
// при редактировании (data-id задан и не 0) поле «Ответственный» скрыто и не меняется — не требуем его
var isEdit = $(this).attr("data-id") !== undefined && $(this).attr("data-id") !== '0';
var address = $(mainId + ' .tabs__content.show #meetAddress').val();
$(mainId + ' .tabs__content.show #meetDatetime').removeClass('invalid');
$(mainId + ' .tabs__content.show .otvetstven').removeClass('invalid');
//console.log(date);
if (date && $.trim(date) && (isEdit || !isEmpty(users_id))) {
var id_client = $(mainId + ' .tabs__content.show .ms-big-add-client').val();
var data = {
"client_id": JSON.stringify(id_client),
"client_id": (!isEmpty(id_client) ? JSON.stringify(id_client) : ''),
"comment": text,
"schedule_date": date,
"users_id": JSON.stringify(users_id),
@ -7173,6 +7223,14 @@ $(document).ready(function() {
getCalendar();
}
});
} else {
if(isEmpty($.trim(date))){
$(mainId + ' .tabs__content.show #meetDatetime').addClass('invalid');
}
if(!isEdit && isEmpty(users_id)){
$(mainId + ' .tabs__content.show .otvetstven').addClass('invalid');
}
}
@ -7189,13 +7247,21 @@ $(document).ready(function() {
var date = $(mainId + ' .tabs__content.meet #meetDatetime').datepicker().val();
var address = $(mainId + ' .tabs__content.meet #meetAddress').val();
//console.log(date);
if (date && $.trim(date)) {
var id_client = $(mainId + ' .tabs__content.meet .ms-big-add-client').val();
var users_id = $(mainId + ' .tabs__content.meet .ms-crm-client-work').val();
// при редактировании (data-id задан и не 0) поле «Ответственный» скрыто и не меняется — не требуем его
var isEdit = $(this).attr("data-id") !== undefined && $(this).attr("data-id") !== '0';
var address = $(mainId + ' .tabs__content.meet #meetAddress').val();
$(mainId + ' .tabs__content.meet #meetDatetime').removeClass('invalid');
$(mainId + ' .tabs__content.meet .otvetstven').removeClass('invalid');
//console.log(date);
if (date && $.trim(date) && (isEdit || !isEmpty(users_id))) {
var id_client = $(mainId + ' .tabs__content.meet .ms-big-add-client').val();
var data = {
"client_id": JSON.stringify(id_client),
"client_id": (!isEmpty(id_client) ? JSON.stringify(id_client) : ''),
"comment": text,
"schedule_date": date,
"users_id": JSON.stringify(users_id),
@ -7227,6 +7293,14 @@ $(document).ready(function() {
getCalendar();
}
});
} else {
if(isEmpty($.trim(date))){
$(mainId + ' .tabs__content.meet #meetDatetime').addClass('invalid');
}
if(!isEdit && isEmpty(users_id)){
$(mainId + ' .tabs__content.meet .otvetstven').addClass('invalid');
}
}
return false;
@ -7241,13 +7315,21 @@ $(document).ready(function() {
var date = $(mainId + ' .tabs__content.deal #dealDatetime').datepicker().val();
var sum = $(mainId + ' .tabs__content.deal #dealSum').val();
// console.log(date);
if (date && $.trim(date)) {
var id_client = $(mainId + ' .tabs__content.deal .ms-big-add-client').val();
var users_id = $(mainId + ' .tabs__content.deal .ms-crm-client-work').val();
// при редактировании (data-id задан и не 0) поле «Ответственный» скрыто и не меняется — не требуем его
var isEdit = $(this).attr("data-id") !== undefined && $(this).attr("data-id") !== '0';
var sum = $(mainId + ' .tabs__content.deal #dealSum').val();
$(mainId + ' .tabs__content.deal #dealDatetime').removeClass('invalid');
$(mainId + ' .tabs__content.deal .otvetstven').removeClass('invalid');
// console.log(date);
if (date && $.trim(date) && (isEdit || !isEmpty(users_id))) {
var id_client = $(mainId + ' .tabs__content.deal .ms-big-add-client').val();
var data = {
"client_id": JSON.stringify(id_client),
"client_id": (!isEmpty(id_client) ? JSON.stringify(id_client) : ''),
"comment": text,
"schedule_date": date,
"users_id": JSON.stringify(users_id),
@ -7280,6 +7362,14 @@ $(document).ready(function() {
getCalendar();
}
});
} else {
if(isEmpty($.trim(date))){
$(mainId + ' .tabs__content.deal #dealDatetime').addClass('invalid');
}
if(!isEdit && isEmpty(users_id)){
$(mainId + ' .tabs__content.deal .otvetstven').addClass('invalid');
}
}
return false;
@ -7296,12 +7386,20 @@ $(document).ready(function() {
var date = $(mainId + ' .tabs__content.event #dealDatetime').datepicker().val();
var name = $(mainId + ' .tabs__content.event #eventName').val();
//console.log(mainId);
if (date && $.trim(date)) {
var id_client = $(mainId + ' .tabs__content.event .ms-big-add-client').val();
var users_id = $(mainId + ' .tabs__content.event .ms-crm-client-work').val();
// при редактировании (data-id задан и не 0) поле «Ответственный» скрыто и не меняется — не требуем его
var isEdit = $(this).attr("data-id") !== undefined && $(this).attr("data-id") !== '0';
$(mainId + ' .tabs__content.event #dealDatetime').removeClass('invalid');
$(mainId + ' .tabs__content.event .otvetstven').removeClass('invalid');
//console.log(mainId);
if (date && $.trim(date) && (isEdit || !isEmpty(users_id) || !isEmpty($('#addTask .who_work_tasks').val()))) {
var id_client = $(mainId + ' .tabs__content.event .ms-big-add-client').val();
var data = {
"client_id": JSON.stringify(id_client),
"client_id": (!isEmpty(id_client) ? JSON.stringify(id_client) : ''),
"comment": text,
"schedule_date": date,
"users_id": JSON.stringify(users_id),
@ -7334,7 +7432,7 @@ $(document).ready(function() {
if($('#addTask .event .otvetstven1').is(":visible") && $('#addTask .who_work_tasks').val()){
data.who_work = $('#addTask .who_work_tasks').val();
}
console.log(data);
//console.log(data);
$.ajax({
url: '/ajax/addEventsCabinet.php',
@ -7370,6 +7468,14 @@ $(document).ready(function() {
});
}
});
} else {
if(isEmpty($.trim(date))){
$(mainId + ' .tabs__content.event #dealDatetime').addClass('invalid');
}
if(!isEdit && isEmpty(users_id)){
$(mainId + ' .tabs__content.event .otvetstven').addClass('invalid');
}
}
return false;
@ -8269,7 +8375,7 @@ $(document).ready(function() {
// Если первый этап доступен (main == 1), то открываем его
if (firstMain == 1) {
$.post('/ajax/funnel_step_close.php', { 'step': firstStage, 'client': firstClient }, function(res) {
$.post('/ajax/funnel_step_close.php', { 'step': firstStage, 'client': firstClient, 'req': req }, function(res) {
ac.closesStep = true;
ac.step = JSON.parse(res);
ac.clientId = firstClient;
@ -8307,7 +8413,7 @@ $(document).ready(function() {
return;
}
$.post('/ajax/funnel_step_close.php', { 'step': stage, 'client': client }, function(res) {
$.post('/ajax/funnel_step_close.php', { 'step': stage, 'client': client, 'req': req }, function(res) {
ac.closesStep = true;
ac.step = JSON.parse(res);
ac.clientId = client;
@ -8328,7 +8434,7 @@ $(document).ready(function() {
var nextReq = $nextStep.attr("data-req");
var nextAdjacent = $nextStep.attr("data-adjacent");
$.post('/ajax/funnel_step_close.php', { 'step': nextStage, 'client': nextClient }, function(res) {
$.post('/ajax/funnel_step_close.php', { 'step': nextStage, 'client': nextClient, 'req': req }, function(res) {
ac.closesStep = true;
ac.step = JSON.parse(res);
ac.clientId = nextClient;
@ -10618,7 +10724,51 @@ function save_client(params, funnel_id, poles){
});
}
/**
* hide_client_contacts: переключает состояние полей контактов клиента в модалке #send_new.
*
* Модалка #send_new / #addEditNew используется и для создания нового клиента (add_client_new),
* и для редактирования существующего (editClientNew). PHP-рендер задаёт статически disabled+warning
* для hide-юзера это корректно для edit, но мешает create. Здесь динамически переключаем.
*
* mode='add' enable полей email/phone/national_phone, hide warning, навешиваем phoneMask
* mode='edit' если hide-юзер: disable полей + show warning + снимаем phoneMask (значение пишется
* "+X *** *** ** **", плагин маски его сломал бы).
*
* Зависит от window.shouldHideContacts() (см. js/contacts_visibility.js).
*/
function setAddEditNewContactsState(mode) {
var isHide = (typeof window.shouldHideContacts === 'function') && window.shouldHideContacts();
if (!isHide) return; // обычным юзерам нечего переключать — PHP отрендерил всё открытым
var $email = $('#addEditNew [name="email"]');
var $phone = $('#addEditNew #phoneClient');
var $natPh = $('#addEditNew [name="national_phone"]');
var $warning = $('#hideContactsWarning');
var disabledTitle = 'Контакты клиента скрыты для вашей учётной записи';
if (mode === 'add') {
$email.prop('disabled', false).removeAttr('title');
$phone.prop('disabled', false).removeAttr('title');
$natPh.prop('disabled', false);
$warning.hide();
// навешиваем jQuery-маску +7 для ручного ввода в новой форме
if (!$phone.hasClass('phoneMask')) {
$phone.addClass('phoneMask');
}
try { $phone.unmask(); } catch (e) {}
$phone.mask('+79999999999');
} else { // mode === 'edit'
$email.prop('disabled', true).attr('title', disabledTitle);
$phone.prop('disabled', true).attr('title', disabledTitle);
$natPh.prop('disabled', true);
$warning.show();
// снимаем маску, чтобы серверное "+X *** *** ** **" не превратилось в "+7"
try { $phone.unmask(); } catch (e) {}
$phone.removeClass('phoneMask');
}
}
function add_client_new(object, add_user, req_id, client_name) {
setAddEditNewContactsState('add');
if (req_id) {
if (typeof req_form == 'object') {
$('#req_form').css('zIndex', '99987');
@ -12051,9 +12201,9 @@ const defaultParams = {
hasFocusClass: 'has-focus',
isDisabledClass: 'is-disabled',
isOpenClass: 'is-open',
};
};
function builder(el, builderParams) {
function builder(el, builderParams) {
const containerClass = 'customSelect';
let isOpen = false;
let uId = '';
@ -12608,9 +12758,9 @@ const defaultParams = {
// Returns the plugin instance, with the public exposed methods and properties
return container.customSelect;
}
}
function customSelect(element, customParams) {
function customSelect(element, customParams) {
// Overrides the default options with the ones provided by the user
const nodeList = [];
const selects = [];
@ -12647,9 +12797,9 @@ const defaultParams = {
// Returns all plugin instances
return selects;
}());
}
}
function change_promo(object_id, val){
function change_promo(object_id, val){
console.log(object_id, val);
var params = {};

60
js/deals_client_vue.js Normal file
View File

@ -0,0 +1,60 @@
Vue.config.devtools = true;
var deals_client = new Vue({
el: '#deals_list_client',
name: 'deals_list_client',
data() {
return {
dealsList: [],
isDealsClient: 0,
clientId: 0,
}
},
methods: {
getList() {
if (this.clientId > 0) {
this.dealsList = [];
this.isDealsClient = 0;
axios.post('/ajax/ajax_vue_requisitions.php', {
request: 'get_deal_by_client',
client_id: this.clientId,
})
.then((response) => {
var res = response.data;
if (res.deals && res.deals.length > 0) {
var promises = res.deals.map(dealId => {
return axios.get('/api/deals/' + dealId).then(r => r.data.data).catch(() => null);
});
Promise.all(promises).then(results => {
deals_client.dealsList = results.filter(d => d !== null);
deals_client.isDealsClient = deals_client.dealsList.length > 0 ? 1 : 2;
});
} else {
this.isDealsClient = 2;
}
})
.catch(function(error) {
console.log(error);
deals_client.isDealsClient = 2;
});
}
},
openDealFromClient(dealId) {
if (typeof window.editDeal === 'function') {
window.editDeal(0, dealId);
} else {
console.error('editDeal not available');
}
},
isEmpty(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) return false;
}
return true;
}
},
mounted() {
// Автоматически загружаем список при монтировании
}
});

View File

@ -1787,6 +1787,16 @@ $user_id = $_SESSION['id'];
<i class="tabs-li__icon">6</i>
<span>Для документов</span>
</li>
<?php
$allowed_users_deal = [18388];
if (in_array((int)$_SESSION['id'], $allowed_users_deal)) {
?>
<li id="liDeals" data-page="7">
<i class="tabs-li__icon">7</i>
<span>Сделки</span>
</li>
<?php } ?>
</ul>
<div>
<div class="tabs__content">
@ -3209,6 +3219,45 @@ $user_id = $_SESSION['id'];
по эл. почте Вашему клиенту или просто вывести на печать.</span>
<div class="clear"></div>
</div>
<!-- Сделки -->
<div class="tabs__content">
<div id="deals_list_client">
<div v-show="isDealsClient===0" style="text-align:center; margin-top: 25px;">
<img src="../images/rocket-spinner.svg" width="96px">
</div>
<div v-show="isDealsClient===1" class="clients">
<div v-for="(item, index) in dealsList">
<div class="client">
<div class="client-inner">
<div class="additional-info">
<div class="type" style="font-weight:600;">{{ item.title }}</div>
<div class="stagebar-title" style="color:#666;font-size:13px;">
{{ item.side_one }} / {{ item.side_two }}
</div>
<div v-if="item.deal_date" style="font-size:12px;color:#999;">Дата: {{ item.deal_date }}</div>
<div v-if="item.contract_price" style="font-size:12px;color:#999;">Сумма: {{ Number(item.contract_price).toLocaleString('ru-RU') }} руб.</div>
</div>
<div class="client-name">
<h2><a href="javascript:{}" @click="openDealFromClient(item.id)" style="cursor:pointer;">{{ item.title }}</a></h2>
</div>
<div class="actions">
<a href="javascript:{};" class="no-border" title="Редактировать сделку" @click="openDealFromClient(item.id)">
<span class="simple-button settings"><i class="jw-action-icon-settings"></i></span>
</a>
</div>
</div>
</div>
</div>
</div>
<div v-show="isDealsClient===2" class="empty-block">
<h1><i class="ti-na"></i></h1>
<h2>Сделок не найдено</h2>
</div>
</div>
<div class="clear"></div>
</div>
</div>
</div>
</div>
@ -5340,9 +5389,8 @@ $user_id = $_SESSION['id'];
</template>
<template v-else>
<div class="label">Сделка</div>
<div style="display: flex; justify-content: center;">
<button
style="color: #fff; padding: 10px 20px; background: linear-gradient(to bottom, rgba(67, 160, 71, 1) 0%, rgba(56, 142, 60, 1) 100%); border: none; font-family: 'Lato', sans-serif; font-size: 15px; border-radius: 3px; cursor: pointer;"
style="color: #fff; padding: 10px 20px; background: linear-gradient(to bottom, rgba(67, 160, 71, 1) 0%, rgba(56, 142, 60, 1) 100%); border: none; font-family: 'Lato', sans-serif; font-size: 15px;"
type="button"
id="createDealButton"
@click="initCreateDeal(reqId)">
@ -6071,6 +6119,7 @@ require($_SERVER['DOCUMENT_ROOT']."/templates/files_list.php");
<script type="text/javascript" src="/js/crm_vue.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/crm_vue.js')?>"></script>
<?php } ?>
<script type="text/javascript" src="/js/requisitions_client_vue.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/requisitions_client_vue.js')?>"></script>
<script type="text/javascript" src="/js/deals_client_vue.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/deals_client_vue.js')?>"></script>
<script type="text/javascript" src="/js/requisitions_vue.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/requisitions_vue.js')?>"></script>
<script type="text/javascript" src="/js/autosearch_vue.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/autosearch_vue.js')?>"></script>
<script type="text/javascript" src="/js/requisitions.js?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/js/requisitions.js')?>"></script>