Ни для кого не секрет, что давать пользователям бесконтрольный доступ к справочнику - неразумно. Однако сидеть и часть своего рабочего времени тратить на то, чтобы самостоятельно создавать записи - вообще глупо. Так что в нашей компании был разработан план, как передать данный функционал пользователям, не обременяя их правами. Пример на справочнике "Организации".
Главное понимание - надо вам это и для чего. В большой компании, где новые Контрагенты появляются по пять штук в день минимум - совсем не желательно раздавать права на создание записей во все желающие руки - начнется бардак с созданием записей с одним только названием, причем частенько даже не проверяя: есть ли уже такая запись. В противовес такому "человеческому фактору", мы создали механизм круговой поруки (которая мажет как копоть) где решение о создании или изменении записи, лежит не на специалисте, а на Верификаторе или Андерайтере, которым потом самим же и заниматься идентификацией контрагента. А для простоты работы с новым Сценарием, мы внесли функционал в рабочий маршрут.
Первое, что нам потребовалось, для реализации наших планов - создание карточки нового документа, который будет содержать в себе все необходимое для заполнения справочника. По сути, мы создали документ-заявку на создание новой записи или ее редактирование.
(Несмотря на то, что многие поля обязательные - многие пользователи не стесняясь ставят прочерки и пытаются пропустить заполнение полей, но сталкиваются с проблемами на следующем этапе - проверке, живым человеком)
Мы не будем глубоко углубляться в моменты контроля и наполнения карточки - ничего в этом сакрального нет. Хотя пару отступлений стоит сделать:
1. Для того чтобы поиск Организации работал не только по названию, но и по ИНН, в "Выборе" были сделаны следующие записи:
// До выбора
KeyRequisite = Object.Requisites("Организация")
KeyRequisiteArray = ArrayOf(ArrayOf("Наименование"; "Справочник:ОРГ"; KeyRequisite.Title; KeyRequisite.AsString; TRUE; TRUE))
LookUpRequisiteNames.Add('LongString5')
LookUpRequisiteNames.Add('ИНН')
BeforeSelectingFromRefRequisite(SelectMode; InputValue; LookUpReference; KeyRequisiteArray; FALSE)
// После выбора
LookUpRequisiteNames.DelimitedText = AfterSelectingFromRefRequisite(LookUpReference)
2. Так же, на реквизите Организации мы создали вычисления, которые в случае выбора - выбирали все необходимые данные из Справочника. А в случае удаления - зачищали поля:
if Assigned(Object.Requisites("Организация").Value)
RequisitesTabMain = 'String;String2;String3;String4;DFAWorker;Email;LongString3;Телефон;НаселенныйПункт;LongString;LongString2'
foreach ReqName in CSubString(RequisitesTabMain; ';')
Object.Requisites(ReqName).Value = null
endforeach
КодОрг = Object.Requisites("Организация").Value
Организация = References.ОРГ.GetObjectByCode(КодОрг)
Object.Requisites("String").Value = Trim(Организация.Requisites(SYSREQ_NAME).Value) // Наименование
Object.Requisites("String2").Value = Trim(Организация.Requisites("LongString5").Value) // Юридическое наименование
if Assigned(Организация.Requisites("ИНН").Value) // ИНН
Object.Requisites("String3").Value = Trim(Организация.Requisites("ИНН").Value)
endif
if Assigned(Организация.Requisites("Содержание2").Value) // Адрес
Object.Requisites("LongString").Value = Trim(Организация.Requisites("Содержание2").Value)
endif
if Assigned(Организация.Requisites("Ед_Изм").Value) // КПП
Object.Requisites("String4").Value = Trim(Организация.Requisites("Ед_Изм").Value)
endif
if Assigned(Организация.Requisites("Дополнение4").Value) // Телефоны
Object.Requisites("Телефон").Value = Trim(Организация.Requisites("Дополнение4").Value)
endif
if Assigned(Организация.Requisites("LongString4").Value) // Отрасль
Object.Requisites("LongString3").Value = Организация.Requisites("LongString4").Value
endif
if Assigned(Организация.Requisites("Email").Value) // Email
Object.Requisites("Email").Value = Trim(Организация.Requisites("Email").Value)
endif
if Assigned(Организация.Requisites("Employee").Value) // Ответственный
Object.Requisites("DFAWorker").Value = Организация.Requisites("Employee").Value
endif
if Assigned(Организация.Requisites("Город").Value) // Населенный пункт
Object.Requisites("НаселенныйПункт").Value = Организация.Requisites("Город").Value
endif
Object.SYSREQ_EDOC_NAME = Format("Архив документов по клиенту: %s на %s (Отв: %s)"; ArrayOf(Организация.SYSREQ_NAME;Сегодня();Object.Requisites("DFAWorker").DisplayText))
else
RequisitesTabMain = 'String;String2;String3;String4;DFAWorker;Email;LongString3;Телефон;НаселенныйПункт;LongString;LongString2'
foreach ReqName in CSubString(RequisitesTabMain; ';')
Object.Requisites(ReqName).Value = null
endforeach
endif
Прошу обратить внимание, что реквизит "Отрасль" в справочнике мы изменили - учитывайте это, когда будете копировать!
3. Ну, и конечно куча вычислений "Перед Сохранением", часть из которых - суть проверки сведений Данных в карточке и в записи справочника:
if Assigned(Object.Requisites("Организация").Value)
КодОрг = Object.Requisites("Организация").Value
Организация = References.ОРГ.GetObjectByCode(КодОрг)
if (Object.Requisites("String").Value <<>> Trim(Организация.Requisites(SYSREQ_NAME).DisplayText)) or
(Object.Requisites("String2").Value <<>> Trim(Организация.Requisites("LongString5").DisplayText)) or
(Object.Requisites("String3").Value <<>> Организация.Requisites("ИНН").Value) or
(Object.Requisites("String4").Value <<>> Организация.Requisites("Ед_Изм").Value) or
(Object.Requisites("Телефон").Value <<>> Организация.Requisites("Дополнение4").Value) or
(Object.Requisites("LongString3").Value <<>> Организация.Requisites("LongString4").Value) or
(Object.Requisites("Email").Value <<>> Организация.Requisites("Email").Value) or
(Object.Requisites("DFAWorker").Value <<>> Организация.Requisites("Employee").Value) or
(Object.Requisites("НаселенныйПункт").Value <<>> Организация.Requisites("Город").Value)
Object.Requisites("YesNo9").Value = YES_VALUE
endif
endif
Вот в этой части у нас всплывает реквизит "Object.Requisites("YesNo9").Value", который отмечает - стоит ли вносить изменения в запись справочника или все осталось по старому. Такой же реквизит мы активируем, когда у нас в принципе нет Организации, но есть все необходимые для ее создания данные.
Ну, и по мелочи - ищем схожие записи по ИНН, а так же проверяем ИНН в параметрах:
if not Assigned(Object.Requisites("Организация").Value)
Reference = References.ОРГ.GetComponent
View = Reference.CreateView('Главное')
AddWhere = Reference.AddWhere(Format("%0:s.%1:s like char(37) + '%2:g' + char(37)"; ArrayOf(Reference.TableName; Reference.Requisites('ИНН').FieldName; INN)))
Reference.Open
if Reference.RecordCount > 0
Code = Reference.Requisites('Код').AsString
Organiz = ReferenceRequisiteValue('ОРГ';Code;'Наименование')
Text1 = "В справочнике Организаций, уже существует запись с ИНН = "
Text2 = "Найденная организация: "
Text3 = "Выбрать в документ найденную организацию?"
Raise(СоздатьИсключение("Найден Дубликат!"; Text1 & INN & CR & Text2 & Organiz ; ecWarning ))
endif
Reference.Close
Reference.DelWhere(AddWhere)
Reference = nil
Object.Requisites("YesNo9").Value = YES_VALUE
else
OrgINN = ReferenceRequisiteValue('ОРГ';Object.Requisites("Организация").Value;'ИНН')
if INN <<>> OrgINN
text4 = "Введенный вами ИНН не совпадает с ИНН выбранной организации!" & CR & "ИНН выбранной организации: " & OrgINN
Raise(СоздатьИсключение("Несовпадение ИНН!"; text4 ; ecWarning ))
endif
endif
И до кучи проверяем ИНН:
INN = Object.Requisites("String3").Value
if (ДлинаСтр(INN) > 8) and (ДлинаСтр(INN) < 13)
//
else
TextExcept = "1. ИНН физического лица является последовательностью из 12 цифр." & CR &
"2 .ИНН индивидуального предпринимателя присваивается при регистрации физического лица." & CR &
"3. ИНН юридического лица является последовательностью из 10 цифр." & CR &
"4. ИНН иностранного юридического лица всегда начинается с цифр «9909» и далее еще 5 цифр."
Object.Requisites("String3").Value = ''
Raise(СоздатьИсключение("Ошибка формата ИНН!"; TextExcept ; ecException))
endif
Достаточно - остальное уже мелочи и суть-украшательство.
Теперь, когда у нас есть документ или архив со всеми необходимыми для редактирования или создания записи в справочнике, можно использовать его в маршруте (на ваше усмотрение), и добавить в него блоки:
Как понятно из сути - первый условный блок №33 смотрит значение переменной в Документе "YesNo9" и если надо вносить правки или создавать запись - включается ответвление.
Блок №34 "Ответственный за Согласование Карточки ОРГАНИЗАЦИИ" Событие Страт:
Params = Object.WorkflowParams
EDocInfo = Params.ValueByName("Attachment").Value // Наш Документ "Сканы юридических дел"
Doc = EDocuments.GetObjectByID(EDocInfo.id)
if Assigned(Doc.Requisites('Организация').Value)
Theme = Format("Согласование ИЗМЕНЕНИЯ карточки Контрагента: %s"; Doc.Requisites('String').Value)
TextFull = "В уже существующей карточке Контрагента были внесены изменения:" & CR &
"Наименование клиента: " & Doc.Requisites("String").Value & CR &
"Юридическое наименование: " & Doc.Requisites("String2").Value & CR &
"ИНН: " & Doc.Requisites("String3").Value & CR &
"КПП: " & Doc.Requisites("String4").Value & CR &
"Город: " & Doc.Requisites("НаселенныйПункт").DisplayText & CR &
"Вид Деятельности: " & Doc.Requisites("LongString3").Value & CR &
"Email: " & Doc.Requisites("Email").Value & CR &
"Партнер: Да" & CR &
"Телефон: " & Doc.Requisites("Телефон").Value & CR & CR &
"Принимаете ли вы данные изменения или требуется их изменение?" & CR &
"Если вы согласуете данную задачу, в справочнике обновятся данные у указанного Контрагента."
else
Theme = Format("Согласование СОЗДАНИЯ карточки Контрагента: %s"; Doc.Requisites('String').Value)
TextFull = "Требуется согласовать создание новой записи в справочник Организации:" & CR &
"Наименование клиента: " & Doc.Requisites("String").Value & CR &
"Юридическое наименование: " & Doc.Requisites("String2").Value & CR &
"ИНН: " & Doc.Requisites("String3").Value & CR &
"КПП: " & Doc.Requisites("String4").Value & CR &
"Город: " & Doc.Requisites("НаселенныйПункт").Value & CR &
"Вид Деятельности: " & Doc.Requisites("LongString3").Value & CR &
"Email: " & Doc.Requisites("Email").Value & CR &
"Партнер: Да" & CR &
"Телефон: " & Doc.Requisites("Телефон").Value & CR & CR &
"Принимаете ли вы данные изменения или требуется их изменение?" & CR &
"Если вы согласуете данную задачу, в справочнике Организации появиться новая запись с вышеуказанными данными."
endif
Object.ActiveText = TextFull
Sender.Properties.ValueByName(JOB_BLOCK_SUBJECT_PROPERTY).Value = Theme
Блок №36 - отправка инициатору на Доработку - не обсуждаем, там ничего интересного нет.
Самая соль в блоке Сценария №35 "Создание/Изменение карточки Организации", в котором у нас вложены те самые, нужные нам, Вычисления:
Params = Object.WorkflowParams
EDocInfo = Params.ValueByName("Attachment").Value
Doc = EDocuments.GetObjectByID(EDocInfo.id)
if Assigned(Doc.Requisites('Организация').Value)
OrgCode = ИзмененияСпрОрганизации(Doc.Requisites('Организация').Value;
Doc.Requisites("String").Value; Doc.Requisites("String2").Value; Doc.Requisites("String3").Value;
Doc.Requisites("String4").Value; Doc.Requisites("НаселенныйПункт").Value; Doc.Requisites("LongString3").Value;
Doc.Requisites("Email").Value; 'Да'; Doc.Requisites("Телефон").Value; Doc.Requisites("DFAWorker").Value; Doc.Requisites("LongString").Value)
Text = "Обновлена карточка Конрагента: " & OrgCode
else
OrgCode = ИзмененияСпрОрганизации(;Doc.Requisites("String").Value; Doc.Requisites("String2").Value; Doc.Requisites("String3").Value;
Doc.Requisites("String4").Value; Doc.Requisites("НаселенныйПункт").Value; Doc.Requisites("LongString3").Value;
Doc.Requisites("Email").Value; 'Да'; Doc.Requisites("Телефон").Value; Doc.Requisites("DFAWorker").Value; Doc.Requisites("LongString").Value)
Text = "Создана новая карточка Конрагента: " & OrgCode
endif
Object.ActiveText = Text
Params.ValueByName("КодКлиента").Value = OrgCode
if Assigned(OrgCode)
Doc.Requisites('YesNo9').Value = NO_VALUE
Doc.Requisites('Организация').Value = OrgCode
Doc.Save
endif
Не густо - не так ли?
Суть всего этого танца, заключена в создании Функции "ИзмененияСпрОрганизации" в которую передаются параметры.
Вот ее основа и наполнение:
А вот и само наполнение:
if Assigned(Контрагент)
try
SprOrg = References.ОРГ.GetObjectByCode(Trim(Контрагент))
except
SprOrg = References.ОРГ.CreateNew
endexcept
else
SprOrg = References.ОРГ.CreateNew
endif
SprOrg.Requisites(SYSREQ_NAME).AsString = Наименование
SprOrg.Requisites("LongString5").AsString = ЮрНаименование
SprOrg.Requisites("ИНН").AsString = ИНН
SprOrg.Requisites("Ед_Изм").AsString = КПП
SprOrg.Requisites("Город").Value = Город
SprOrg.Requisites("LongString4").Value = ВидДеятельности
SprOrg.Requisites("Партнерство").Value = Партнер
SprOrg.Requisites("Email").AsString = Email
SprOrg.Requisites("Дополнение4").AsString = Телефон
SprOrg.Requisites("Employee").Value = Ответственный
SprOrg.Requisites("Содержание2").AsString = Адрес
SprOrg.Save
Rez = SprOrg.SYSREQ_CODE
SprOrg.Close
Result = Rez
Все довольно просто, если не вдаваться в неведомые дали и защитные механизмы. Однако этот инструмент прекрасно работает, и как показала практика - устраивает всех.
На протяжении пары недель, как данный сценарий был запущен, не было создано ни одной дублирующейся записи, а так же были пресечены все попытки "забивать" поля пробелами или иными знаками для экономии времени. Таким образом, время работы специалиста больше не дергается частыми запросами на создание новой записи справочника или ее редактирования, а так же усилился контроль по данным вносимым в записи нужного в работе справочника.
По сути это все. Если вы узнали что-то новое или полезное - я писал это не зря!
Всем удачи!
Тарас, если я правильно понимаю, у вас на каждую заявку на создание/актуализацию Контрагента в системе создается документ. Назначение полей карточки и логика их обработки из материала понятны. А что с телом документа? Что туда заносится и как потом используется?
1. Дополню вопрос Алексея. Интересно что происходит с телом документа после завершения согласования и внесения корректировок в справочник Организации. Остается лежать мертвым грузом/используется для статистики/удаляется?
Также интересно:
2. Какова нагрузка на ответственного за согласование карточки Организации?
3. Как быстро новая заявка проходит согласование?
Алексей Семакин, Документ создается под архив с утверждающими документами на Партнера, где все, что является основой для работы - Юридические документы, которые как правило не меняются, разве что адреса или телефоны могут быть изменены.
В Типовом маршруте, где данный документ и Сценарий задействован у нас предусмотрены 3 линии движения:
Все эти линии нуждаются в документах основных - "Сканы юридических лиц" а так же документах добавляемых по сетке:
Изменения в данном Архиве как правило не происходит, но если и будет, то для этого сотрудники будут пользоваться Импортом в новую. версию, и изменение данных в карточке перед стартом задачи по линии Изменения данных нашего Партнера.
Евгения Сарварова, 1. После завершения согласования, все документы из сетки выше (они все принадлежат одному типу документов) принудительно привязываются к первичному документу: "Сканы юридических лиц", который в свою очередь связан со справочником Организации. Круговая порука как она есть. Связывания не происходит только в случае отказа в периоде согласования, о всех остальных случаях - связывание происходит в самом конце маршрута. Документы не входящие в сетку - не подшиваются.
А для архива и отчетности, у нас организована папка поиска, которая все эти документы собирает. С ней работает ФинМониторинг.
2. Ответственный за согласование изменения/создания карточки Организации и до этого занимался только тем, что перебирал документы из архивов во вложениях и сверял их с карточками и указанными в маршруте параметрах. Сейчас проверка происходит в 1 месте - сверка документов и единственной карточки, да и то, только если надо создавать карточку или утвердить правки. Вроде как стало только проще.
3. В виду высокой заинтересованности Контролера и ведущего Партнера менеджера, данный узел в Маршруте проходится за 30-60 минут максимум (ранее проверка отнимала время больше на другом этапе - проверка документов), а весь маршрут не согласуется более суток в любой его линии согласования.
Тарас, спасибо за развернутый ответ!
Собственно вот вся карта:
Мне кажется SprOrg.Open тут лишнее, т.к. метод GetObjectByCode и так инициирует процесс открытия записи справочника.
Зачем использовать такие конструкции:
SprOrg.Requisites("Город").Value = References.ГРД.GetObjectByCode(Город).SYSREQ_CODE SprOrg.Requisites("Employee").Value = References.РАБ.GetObjectByCode(Ответственный).SYSREQ_CODE
У вас и так в переменных Город и Ответственный содержится код записи! достаточно:
SprOrg.Requisites("Город").Value = Город
SprOrg.Requisites("Employee").Value = Ответственный
Если я не ошибаюсь то лучше не использовать для строковых реквизитов конструкции типа :
тк в случае получения null будет exception, используйте лучше:
если в реквизите будет null, то DisplayText возвращает пустую строку.
Дмитрий Тарасов, ваша правда, у меня уже привычка все лишний раз открывать...
Анатолий Придыбайло, да - я снова перестраховался.
Артем Сергеев, Ну, я учитывал невозможность Null до этого, но вы правы - лучше привыкать к лучшему.
Все предложения в коде учтены. Спасибо!
Дополню, что вместо вычислений для поиска Организации по ИНН можно выполнить аналогичную настройку, бонусом настройка станет работать для всех полей Организация во всех карточках.
Авторизуйтесь, чтобы написать комментарий