Личный опыт Гения 1С борьбы с типовыми конфигурациями 1С8
Оглавление
Технологии сопровождения типовых конфигураций
Обновление типовых конфигураций
Альтернатива предопределенным элементам
Особенности программирования в типовых конфигурациях
Особенности добавления новых объектов в типовую конфигурацию
Особенности отладки в типовых конфигурациях
Права доступа на внешние обработки
Дополнительные права доступа и RLS
Отсутствие функции по получению булева права доступа (УТ 10)
Очистка кэша дополнительных права доступа
Права доступа на внешние обработки
Как запретить запускать и редактировать внешние обработки (УТ 10)
Некоторые конкретные рецепты по правам доступа
Право только на ввод физических лиц (ЗУП 2.5)
Обмен с учетом даты запрета редактирования
Автоматическое продвижение даты запрета редактирования
Программно получить пользователей, у которых включена настройка
Удобство работы с движениями документов
Добавление своих полей в регистр Продаж (УТ 10.3)
Как сделать в своем документе поддержку типовых печатных форм
Связь между организацией и контрагентами
Отчет по стоимостной оценке склада
Вывод усредненной себестоимости в отчет (УТ 10.3)
Проверка записанности документа-основания при приеме/отправке товара по ордерной схеме (Розница 1.0)
Контроль остатков при перемещениях по ордерной схеме
Коды учета серий в документах (УТ11)
Контрагенты, для которых не нужно делать скидку УТ 10.3.7.9
Отсутствие грузоотправителя и грузополучателя в счет-фактурах на услуги
Для чего используется регистр списанные товары
Проверка, что себестоимость правильно списывается в разрезе характеристик (УТ 10.3)
Чтобы себестоимость комиссии была равна себестоимости продаж УТ 10.3
Чтобы остатки по партиям соответствовали остаткам по складам УТ10
Нулевая версия при версионировании УПП
Выбор заранее составленных настроек для пользователей
При открытии универсального отчета показывать быстрые отборы
Взаимодействие пользователя с программой
Работа с задачами пользователей
Создание извещений пользователей через задачи пользователей УТ 10.3
Избавление от навязчивости списка задач УТ 10.3
Настройки торгового оборудования для терминального режима RDP УТ 10.3
Настройки рабочего места кассира
Использование настройки РМК по умолчанию
Расположение процедуры поиска по штрих-коду
Поиск не только по регистру штрих-кодов
Добавление в форму поддержку сканера штрих-кода (УТ10, РТ1)
Общие свойства типовых обменов
Внутренняя механика работы обменов
Размещение временных файлов обмена и блокировки
Типовые обмены данными по правилам
Проведение отложенных документов
Права доступа для проведения отложенных документов
Изменение счета после загрузки банковских документов в БП из УТ
Интеграция: Адресные классификаторы
Нюанс обновления адресных классификаторов ВСЕ
Ошибки в процедуре закрытия кассовых смен
Несущественная ошибка отмены транзакции (УТ 10.3, Розница 1.0)
Опыт борьбы с типовыми конфигурациями Гения 1С.
Иногда обновление конфигурации не происходит, при этом при каждом запуске программа сообщает об этом:
НомерВерсии = Константы.НомерВерсииКонфигурации.Получить();
ПервыйЗапуск = (НомерВерсии = "");
Если НЕ ПустаяСтрока(Метаданные.Версия)
И НомерВерсии <> Метаданные.Версия Тогда
Если Не ПервыйЗапуск Тогда
Предупреждение("Изменился номер версии конфигурации. Будет выполнено обновление информационной базы.");
КонецЕслИ;
Иначе
Возврат;
КонецЕсли;
Чтобы избавиться от проблемы, нужно выполнить в консоли кода такой код:
Константы.НомерВерсииКонфигурации.Установить("");
По сути, он указывает программе, что произошел первый запуск программы.
Правильный подход – добавлять предопределенные элементы в справочнике для предопределенных объектов. Но если изменения в конфигурацию вносить нельзя, можно добавлять обычные элементы и искать их по определенному коду. Ведь если вносится новый предопределенный элемент, конфигурацию уже нельзя обновить динамически.
После добавления нового объекта в конфигурацию нужно запустить 1С с параметром запуска ЗапуститьОбновлениеИнформационнойБазы (удобно через отладчик). Иначе будет ошибка вида:
Ошибка при выполнении функции ОбщегоНазначения.ИдентификаторОбъектаМетаданных
Во многих типовых есть фукнкция ПоказатьВременнуюТаблицу. Она бывает в разных модулях, но она есть. Поэтому отлаживать результат запроса можно и без внесения изменений в код.
Хотя есть способ просмотра временных таблиц через ввод в табло имени внешней обработки, но чаще проще использовать готовую функцию.
Функция работает если у запроса установлен менеджер временных таблиц.
Пример из УТ, в табло ввести функцию:
ОбщегоНазначенияУТ.ПоказатьВременнуюТаблицу(Запрос.МенеджерВременныхТаблиц, "ОстаткиУпаковокПоВетвям")
Файл установки элемента управления «1CBarCode.exe» находится на диске ИТС в каталоге «1CITS\EXE\TradeWare\1C\1CBarCode»
В типиках есть возможность назначать дополнительные права пользователям. Можно создавать свои дополнительные права.
В типовых конфигурациях нельзя назначить права доступа на каждую обработку. Вариант решения – создать дополнительное право пользователя и в самой обработке проверять, доступно это право или нет.
Если есть частая потребность, чтобы объекты могли редактировать/просматривать пользователи, обладающие каким-либо дополнительным правом, можно использовать следующий поход.
Допустим, нужно дать доступ только избранным пользователям к регистру «Премии».
Нужно завести дополнительное право с идентификатором «ПросмотрПремий», параметр сеанса «ПравоПросмотрПремий».
Параметры сеанса такого типа заполняются автоматически кодом, который нужно вставить в процедуру ПередНачаломРаботыПриложения:
//Осипов - устанавливаем права доступа
З = Новый Запрос(
"ВЫБРАТЬ
| ПраваПользователей.Ссылка
|ИЗ
| ПланВидовХарактеристик.ПраваПользователей КАК ПраваПользователей
|ГДЕ
| ПраваПользователей.Предопределенный");
МассивПрав = З.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Для Каждого Право ИЗ МассивПрав Цикл
ИмяПрава = ПланыВидовХарактеристик.ПраваПользователей.ПолучитьИмяПредопределенного(Право);
ИмяПараметраСеанса = "Право" + ИмяПрава;
Если Метаданные.ПараметрыСеанса.Найти(ИмяПараметраСеанса) <> Неопределено Тогда
ПараметрыСеанса[ИмяПараметраСеанса] = _Функции.ЕстьДополнительноеПраво(ПланыВидовХарактеристик.ПраваПользователей[ИмяПрава], ложь);
КонецЕсли;
КонецЦикла;
Далее, в RLS на чтение и/или изменение на нужно добавить простое условие для набора полей «Дополнительные поля», в нашем примере «ГДЕ &ПравоПросмотрПремий». К сожалению, платформа не позволяет назначить одинаковые RLS сразу на все роли.
Теперь можно управлять RLS-ом непосредственно из дополнительных прав пользователей.
Почему-то отсутствует функция для проверки, имеет пользователь дополнительное право или нет. Проверка прав везде делается одним и тем же кодом из 8 строчек, почему для этого кода не сделана отдельная функция, не понятно:
Разрешено = УправлениеПользователями.ПолучитьЗначениеПраваДляТекущегоПользователя(ПланыВидовХарактеристик.ПраваПользователей.НайтиПоНаименованию("Разрешено использовать реестр "), Ложь);
Если Разрешено.Количество() = 0 Тогда
Разрешено = Ложь;
ИначеЕсли Разрешено.Количество() > 1 Тогда
Разрешено = Истина;
Иначе
Разрешено = Разрешено[0].Значение;
КонецЕсли;
Рекомендую вместо этих строк использовать функцию:
Функция ЕстьДополнительноеПраво(Право, ЗначениеПоУмолчанию = ложь) Экспорт
Разрешено = УправлениеПользователями.ПолучитьЗначениеПраваДляТекущегоПользователя(Право, ЗначениеПоУмолчанию);
Если Разрешено.Количество() = 0 Тогда
Разрешено = Ложь;
ИначеЕсли Разрешено.Количество() > 1 Тогда
Разрешено = Истина;
Иначе
Разрешено = Разрешено[0].Значение;
КонецЕсли;
Возврат Разрешено;
КонецФункции
Если администратор меняет пользователю дополнительные права доступа, пользователю нужно перезайти в 1с. Это неудобно. Проще дать ему команду для очистки сохраненных в кэше прав доступа:
Кэш = глЗначениеПеременной("ЗначенияДополнительныхПравПользователя");
Если Кэш <> Неопределено Тогда
Кэш.Очистить();
КонецЕсли;
Для того, чтобы в типовых запретить доступ к типовым обработкам, нужно добавить дополнительное право и поставить заглушки.
В код запуска обработки при щелчке в форме списка в процедуру СправочникСписокВыбор добавить в начало код:
ДП = ПланыВидовХарактеристик.ПраваПользователей.РазрешитьПрямуюРаботуСВнешнимиОбработками;
Если НЕ УправлениеПользователями.ПолучитьБулевоЗначениеПраваПользователя(ДП, Ложь) Тогда
Предупреждение("Запускать непосредственно внешние обработки может только пользователь с ДП """ + ДП + """!");
СтандартнаяОбработка = ложь;
Возврат;
КонецЕсли;
В процедуре ПриОткрытии формы элемента:
ДП = ПланыВидовХарактеристик.ПраваПользователей.РазрешитьПрямуюРаботуСВнешнимиОбработками;
Если НЕ УправлениеПользователями.ПолучитьБулевоЗначениеПраваПользователя(ДП, Ложь) Тогда
Предупреждение("Редактировать непосредственно внешние обработки может только пользователь с ДП """ + ДП + """!");
Закрыть(ложь);
Возврат;
КонецЕсли;
Процедура ПриОткрытии используется, т.к. события ПередОткрытием нет.
Назначаем пользователю основную роль «МенеджерПоНаборуПерсонала» и минимальную роль «Пользователь». Создаем дополнительную новую пустую роль «_ВводФЛ» и добавляем ее пользователю. Назначаем интерфейс «Набор персонала».
Создаем новую подписку на все объекты метаданных, заканчивающиеся на Объект на общее для всех событие ПриЗаписи. В коде обработчика прописываем:
Процедура _ВводФЛПриЗаписи(Источник, Отказ) Экспорт
Если РольДоступна("_ВводФизЛиц") И ТипЗнч(Источник) <> Тип("СправочникОбъект.ФизическиеЛица") Тогда
ВызватьИсключение "Вашей роли можно записывать только физических лиц";
КонецЕсли;
КонецПроцедуры
В результате при записи будет выдаваться сообщение вида:
Если требуется не только вводить физлиц, но и вносить сопутстующую информацию, заполняя связанные справочники, пригодится такой код проверки:
Процедура _ВводФЛПриЗаписи(Источник, Отказ) Экспорт
Если РольДоступна("_ВводФизЛиц") И
НЕ (
ТипЗнч(Источник) = Тип("СправочникОбъект.ФизическиеЛица") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.Военкоматы") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.КлассификаторСтранМира") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.ЗваниеГражданскогоВоинскогоУчета") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.СоставыВоеннослужащих") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.УчебныеЗаведения") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.ВидыОбразованияФизЛиц") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.КлассификаторСпециальностейПоОбразованию") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.ПрофессииРабочих") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.УченыеСтепени") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.УченыеЗвания") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.ВидыСтажа") ИЛИ ТипЗнч(Источник) = Тип("СправочникОбъект.КлассификаторСпециальностейПоОбразованию") ИЛИ
ТипЗнч(Источник) = Тип("СправочникОбъект.Награды")
) Тогда
ВызватьИсключение "Вашей роли можно записывать только физических лиц";
КонецЕсли;
КонецПроцедуры
Также в роль «_ВводФЛ» нужно право на чтение, изменение и добавление справочника «Виды стажа».
Дата запрета редактирования может застопорить обмен. Поэтому пользователю, под которым происходит обмен, нужно или ставить максимально раннюю дату, или добавлять в конфигурации в начало методов ПередЗаписьюДокументаДатаЗапретаРедактированияПередЗаписью и ПередЗаписьюРегистраДатаЗапретаРедактированияПередЗаписью код:
Если Источник.ОбменДанными.Загрузка = истина Тогда
Возврат;
КонецЕсли;
Можно написать регламентное задание, которое автоматически продвигает дату запрета редактирования.
МЗ = РегистрыСведений.ГраницыЗапретаИзмененияДанных.Выбрать();
ПОКА МЗ.Следующий() Цикл
//По умолчанию можно редактировать с 1 числа прошлого месяца
НоваяДата = НачалоМесяца(ТекущаяДата());
НоваяДата = НачалоМесяца(НоваяДата -1);
Если МЗ.Пользователь.Имя = "Иванов Алексей Юрьевич" Тогда
НоваяДата = '20140101';
ИначеЕсли МЗ.Пользователь.Имя = "Петров Максим Александрович" Тогда
НоваяДата = '20150101';
ИначеЕсли МЗ.Пользователь.Имя = "Робот" Тогда
НоваяДата = ТекущаяДата() - 180*3600*24; //За полгода робот может
ИначеЕсли Не ЗначениеЗаполнено(МЗ.Пользователь) Тогда
НоваяДата = ТекущаяДата() - 7*3600*24; //За неделю, остальное регулируется более тонкой настройкой
КонецЕсли;
Если МЗ.ГраницаЗапретаИзменений <> НоваяДата Тогда
МЗ.ГраницаЗапретаИзменений = НоваяДата;
КонецЕсли;
Сообщить("Пользователь: " + МЗ.Пользователь + " дата: " + МЗ.ГраницаЗапретаИзменений);
КонецЦикла;
Чтобы получить пользователей с заданным значением настройки, можно использовать функцию:
Функция ПользователиСУказаннойНастройкой(Настройка, Значение) Экспорт
//Возвращает массив пользователей, у которых включена настройка
З = Новый Запрос(
"ВЫБРАТЬ
| НастройкиПользователей.Пользователь
|ИЗ
| РегистрСведений.НастройкиПользователей КАК НастройкиПользователей
|ГДЕ
| НастройкиПользователей.Настройка = &Настройка
| И НастройкиПользователей.Значение = &Значение");
З.УстановитьПараметр("Настройка", Настройка);
З.УстановитьПараметр("Значение", Значение);
Возврат З.Выполнить().Выгрузить().ВыгрузитьКолонку("Пользователь");
КонецФункции
В типиках достаточно сложно создавать новые виды документов, которые комбинировали бы типовые операции по взаиморасчетам, товарам, деньгам и т.п.
В коде много привязок к конкретному виду документа, к виду операции документа. Виды операций не формализованы и не изолированы. Поэтому если вы хотите создать документ, который бы делал, скажем, перемещение с последующим списанием, лучше сделать пачку документов – основной документ и привязанные к нему списание и перемещение, т.к. повторить такой функционал в новом документе обойдется дорого во времени.
Кроме того, формы документов содержат много copy-paste кода, одинаковые участки не формализированы, можно создать форму копированием существующей, но при обновлении типовой конфигурации с работой формы могут возникнуть проблемы.
В типиках для большинства регистров не сделаны формы списков. А типовое поведение платформы подразумевает, что по щелку на строке списка движений открывается регистратор, а не значение, содержащееся в ячейке. Это не удобно, поэтому имеет смысл сделать доработки.
Чтобы добавить свои поля в движения по регистру продаж, их нужно добавить в регистр и табличную часть документа и прописать эти поля в процедуру «ПодготовитьТаблицыДокумента» модуля документа.
Процедура ОсновныеДействияФормыПечать(Кнопка)
УниверсальныеМеханизмы.ОткрытьФормуВыбораПечатныхФормОбъекта(ЭтотОбъект, ЭтаФорма);
// Установить печатную форму по умолчанию.
РаботаСДиалогами.УстановитьКнопкуПечати(ЭтотОбъект, ЭтаФорма);
КонецПроцедуры
Процедура ОсновныеДействияФормыДействиеПечать(Кнопка)
УниверсальныеМеханизмы.НапечататьДокументПоУмолчанию(ЭтотОбъект);
КонецПроцедуры // ОсновныеДействияФормыДействиеПечать()
Процедура ПриОткрытии()
РаботаСДиалогами.УстановитьКнопкуПечати(ЭтотОбъект, ЭтаФорма);
КонецПроцедуры
Теперь остается только добавить внешние печатные формы.
Иногда достаточно сделать одну печатную форму, которая печатает несколько вариантов.
Соответственно, в списке выбора будет несколько печатных форм, но вызываться будет только одна обработка с разными параметрами.
В реквизиты обработки нужно добавить реквизит "ДополнительныеПараметры" с типом "Произвольный".
В модуле Печать обработки печатной формы можно анализировать примерно таким кодом (на примере параметра Штрихкод:
Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
Если ДополнительныеПараметры.Свойство("ШтрихКод") Тогда
флСоШтрихКодом = ЗначениеЗаполнено(ДополнительныеПараметры.ШтрихКод);
КонецЕсли;
КонецЕсли;
Соответственно, в параметрах обработки нужно добавить необходимый параметр:
Между организацией и контрагентом не всегда есть связь. Дежурный способ – по ИНН организации найти контрагента и наоборот:
Грузоотправитель = Справочники.КОнтрагенты.НайтиПоРеквизиту("ИНН", Организация.ИНН);
В отчете «Стоимостная оценка склада» в неоперативном режиме не выводится себестоимость за единицу. Чтобы она выводилась, нужно:
Добавляем показатель:
ИначеЕсли Режим = 1 Тогда // Неоперативный режим
…
УниверсальныйОтчет.ДобавитьПоказатель("СебестоимостьЦена",
"Цена себестоимости", Истина, "ЧЦ=15; ЧДЦ=2");
Меняем текст запроса:
ИначеЕсли Режим = 1 Тогда // Неоперативный режим
…
| ВЫБОР КОГДА КоличествоОстаток = 0 ТОГДА 0 ИНАЧЕ
СтоимостьОстаток / КоличествоОстаток КОНЕЦ КАК СебестоимостьЦена,
|{ВЫБРАТЬ
…
| СебестоимостьЦена,
|ИТОГИ
| СРЕДНЕЕ(СебестоимостьЦена),
Вставляем в структуру полей (вроде бы не обязательно):
УниверсальныйОтчет.мСтруктураПредставлениеПолей.Вставить("СебестоимостьЦена", "СебестоимостьЦена");
Удобно проверять то, что документ-основание записан в базу, пользователи часто обращаются с этим вопросом. В процедуре ПередОткрытием добавить:
Если НЕ
ЗначениеЗаполнено(ДокументОбъект.Ссылка) Тогда
Предупреждение("Не записан в базу
документ-основание, запишите и повторите вызов обработки!");
Отказ = истина;
Возврат;
КонецЕсли;
Почему-то в типиках при перемещениях по ордерной схеме не контролируются остатки на складе-отправителе. В результате операторы могут ошибиться и заметить ошибку только когда уже выписывают расходный ордер.
Для того, чтобы отменить это поведение, достаточно заменить строку в модуле проведения документа «Перемещение товаров»:
//Если ЗначениеЗаполнено(СтруктураШапкиДокумента.СкладОтправитель) И Не РеализацияПоОрдернойСхеме Тогда
//Проверяем остатки всегда, даже при проведении по ордерной схеме...
Если ЗначениеЗаполнено(СтруктураШапкиДокумента.СкладОтправитель) Тогда
Почему-то вместо перечисления используются числовые коды, их можно найти поиском по строке " статусы указания серий ":
//Возможные статусы указания серий:
//
// 0 - серии указывать не требуется
// нечетные статусы - количество по сериям не совпадает с количеством товаров
// четные статусы - количество по сериям совпадает с количеством товаров
// 1,2 - серии указываются справочно
// 3,4 - по сериям учитываются остатки, серии указываются по факту отбора
// 5,6 - по сериям учитываются остатки, серии указываются при планировании отбора,
// заполняются по FEFO (используются только в документах отгрузки товаров)
// 7,8 - по сериям учитываются остатки, серии указываются при планировании отбора
// 9,10 - по сериям учитываются остатки, серии указываются при планировании отгрузки,
// по сериям формируются движения по регистру СвободныеОстатки (как при приходе, так и при расходе)
// 11 - серии не указаны, указание не обязательно (используется заказах и ордерах в начальном статусе)
// 12 - серии в заказе указывать не требуется, но нужно сделать движения по пустой серии по РезервыСерий
// используется в заказах в строках с вариантом обеспечения "Обособленно", "Из заказов" и пустым вариантом
// 13,14 - по сериям учитывается себестоимость
// 15 - по сериям учитывается себестоимость, серии не указаны, указание не обязательно (используется заказах и ордерах в начальном статусе)
// 16 - по сериям учитывается себестоимость, серии в заказе указывать не требуется, но нужно сделать движения по пустой серии по РезервыСерий
// используется в заказах в строках с вариантом обеспечения "Обособленно", "Из заказов" и пустым вариантом
Менеджеров часто интересует вопрос – кто из их коллег зарезервировал товары на складе? Для анализа можно использовать отчет «Анализ доступности товаров на складах», добавив группировку «Документ-основание: Ответственный». Но есть один нюанс – по умолчанию менеджеры не могут видеть поля документа «Заказ покупателей», поэтому в роль «Менеджер по продажам» нужно в список доступных для чтения полей RLS добавить поле «Ответственный».
Можно создать кнопочку, чтобы менеджерам добавлялось/удалялось поле группировки самостоятельно, например, так:
Процедура КоманднаяПанельФормыПоРезервам(Кнопка)
//Осипов для учета резервов
ЭлементыФормы.КоманднаяПанельФормы.Кнопки.ПоРезервам.Пометка = НЕ ЭлементыФормы.КоманднаяПанельФормы.Кнопки.ПоРезервам.Пометка;
НужнаРасшифровкаРезервов = ЭлементыФормы.КоманднаяПанельФормы.Кнопки.ПоРезервам.Пометка;
ИскИзмерение = НайтиИзмерениеСтрокиПостроителяПоПутиКДанным(УниверсальныйОтчет.ПостроительОтчета.ИзмеренияСтроки, "ДокументОснование.Ответственный");
Если НужнаРасшифровкаРезервов Тогда
Если ИскИзмерение = Неопределено Тогда
УниверсальныйОтчет.ПостроительОтчета.ИзмеренияСтроки.Добавить("ДокументОснование.Ответственный");
КонецЕсли;
Иначе
Если ИскИзмерение <> Неопределено Тогда
УниверсальныйОтчет.ПостроительОтчета.ИзмеренияСтроки.Удалить(ИскИзмерение);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция ВыбратьНастройкуУниверсальногоОтчета(ФормаОтчета, Приставка, ЭлементФормы)
Перем ИмяОбъекта;
ИмяОбъекта = Строка(ФормаОтчета.ЭтотОбъект);
СтруктураНастройки = Новый Структура;
СтруктураНастройки.Вставить("Пользователь", глЗначениеПеременной("глТекущийПользователь"));
СтруктураНастройки.Вставить("ИмяОбъекта", ИмяОбъекта);
СтруктураНастройки.Вставить("НаименованиеНастройки", Неопределено);
З = Новый Запрос(
"ВЫБРАТЬ
| СохраненныеНастройки.ИмяОбъекта,
| СохраненныеНастройки.НаименованиеНастройки,
| СохраненныеНастройки.СохраненнаяНастройка,
| СохраненныеНастройки.Пользователь
|ИЗ
| РегистрСведений.СохраненныеНастройки КАК СохраненныеНастройки
|ГДЕ
| СохраненныеНастройки.ИмяОбъекта = &ИмяОбъекта");
З.УстановитьПараметр("ИмяОбъекта", ИмяОбъекта);
РезНаборНастроек = Новый СписокЗначений();
ТЗ = З.Выполнить().Выгрузить();
Для Каждого Стр ИЗ ТЗ Цикл
Если Лев(Стр.НаименованиеНастройки, СтрДлина(Приставка)) = Приставка Тогда
РезНаборНастроек.Добавить(Стр.СохраненнаяНастройка, Стр.НаименованиеНастройки);
КонецЕсли;
КонецЦикла;
Выбор = Неопределено;
Если РезНаборНастроек.Количество() > 0 Тогда
Выбор = ФормаОтчета.ВыбратьИзСписка(РезНаборНастроек, ЭлементФормы);
КонецЕсли;
Если Выбор <> Неопределено Тогда
ИскСтрокаТЗ = ТЗ.Найти(Выбор.Значение, "СохраненнаяНастройка");
СтруктураНастройки.Вставить("НаименованиеНастройки", Выбор.Представление);
СтруктураНастройки.Вставить("СохраненнаяНастройка", Выбор.Значение);
//Чтобы можно было подгружать настройку другого пользователя
СтруктураНастройки.Вставить("Пользователь", ИскСтрокаТЗ.Пользователь);
УниверсальныеМеханизмы.ПолучитьНастройку(СтруктураНастройки);
Результат = СтруктураНастройки;
Отчет = ФормаОтчета.ЭтотОбъект;
Объект = ФормаОтчета.ЭтотОбъект.УниверсальныйОтчет;
Объект.мТекущаяНастройка = Результат;
Отчет.ВосстановитьНастройкиИзСтруктуры(Результат.СохраненнаяНастройка);
Объект.ВосстановитьПараметрыПечати(Объект, Отчет, ФормаОтчета, Результат);
КонецЕсли;
КонецФункции
Часто в типиках реквизиты Грузополучатель и Грузоотправитель не связаны с Организацией и Покупателем. А ведь на практике Грузоотправителем чаще всего является Организация, а Грузополучателем – Покупатель. Связка реализовывается через обработчик событий изменения Организации и Покупателя. При открытии нового документа также надо принудительно вызывать эти обработчики – они сами по себе не вызываются для новых документов.
В типиках есть автоматические скидки при продаже. Код реализован в процедурах ЗапросПоСкидкам и РассчитатьСкидкиПриПродаже модуля ОбработкаТабличныхЧастей.
Пользователи попросили для некоторых контрагентов не начислять скидку. Я предпочел косметическое вмешательство, а не глобальную перекройку задачи.
В функции, где строится массив получателей скидки (в том числе и пустой получатель – по всем контрагентам), я просто исключаю контрагента, если по нему не нужно делать скидок. Чтобы пометить такого контрагента, в его комментарий добавляю строку «-СКИДКИ;». Можно было бы конечно, использовать свойства контрагентов, но это более трудный путь. Все замечательно работает.
Функция ПолучитьМассивПолучателейСкидки(ВидРеализации, ДокументОбъект) Экспорт
МассивПолучателей = Новый Массив;
Если ВидРеализации = Перечисления.ВидыСкидок.Розничная Тогда
МассивПолучателей.Добавить(ДокументОбъект.Склад);
МассивПолучателей.Добавить(Справочники.Склады.ПустаяСсылка());
Иначе
//Осипов 20091216
Если Найти(ДокументОбъект.Контрагент.Комментарий, "-СКИДКИ;") <> 0 Тогда
Возврат МассивПолучателей;
КонецЕсли;
МассивПолучателей.Добавить(ДокументОбъект.Контрагент);
МассивПолучателей.Добавить(ДокументОбъект.ДоговорКонтрагента);
МассивПолучателей.Добавить(Справочники.ДоговорыКонтрагентов.ПустаяСсылка());
КонецЕсли;
Возврат МассивПолучателей;
КонецФункции // ПолучитьМассивПолучателейСкидки()
В типиках если счет-фактура выдана на услуги, т.е. нет груза, то не печатаются грузоотправитель и грузополучатель. Однако некоторые организации просят всегда их печатать. Было бы логично для универсальности сделать константу «Печатать грузоотправителя и грузополучателя в счет-фактурах на услуги». Но пока мы дождемся этого от 1С, нужно комментировать код в модуле печати счет-фактуры:
Если Не ЕстьТовары Тогда
//ДанныеДляПечати.Грузоотправитель = "";
//ДанныеДляПечати.Грузополучатель = "";
//ДанныеДляПечати.АдресДоставки = "";
КонецЕсли;
Регистр списанные товары используется, чтобы партионное списание не лазило в табличную часть документа, а узнавало, что нужно списывать сразу из этого регистра. Поэтому документы должны заполнять этот регистр.
Недостатком архитектуры регистра является то, что у него только одно измерение – номер строки. Поэтому одна строка документа может сделать только одно движение (приход, расход, перемещение) по партиям. Проблема решается путем добавления строк с номерами на 100 000, 200 000, 300 000 больше, чем исходный номер строки. В документах 1с не может быть больше 100 000 строк, поэтому использование виртуального номера строки правомерно.
Допустим, нужно проверить, учитывает ли программа разрез по характеристикам при списании себестоимости.
Рассмотрим ситуацию для методики оценки себестоимости «по средней» без учета склада и только для характеристик.
Заведем два прихода товара «Тест товара»:
1 марта 2011 года – 10 штук по цене 100 с характеристикой 111
10 марта 2011 года – 20 штук по цене 110 с характеристикой 222
Регистр партий выглядит так:
Производим продажу 15 штук товара 20 марта 2011 года, из них 10 штук с характеристикой 111, 5 штук – с характеристикой 222.
Производим продажу 5 штук товара 25 марта 2011 года с характеристикой 222.
Регистр партий выглядит так:
Т.е. программа использовала для списания товаров не среднюю себестоимость всех товаров (1000 + 2200) / 30 = 107 рублей, а по каждой характеристике свою сумму.
Мне понадобилось, чтобы товар, который поступал на комиссию, при продаже формировал такую же себестоимость, как и купленный товар. Просто в базе велся учет себестоимости по марже, а тип договора нужно было сохранить, чтобы выгружать его в бухгалтерию правильно.
Пришлось поправить код процедуры УправлениеЗапасамиПартионныйУчет.СформироватьДвиженияСписанияНаСебестоимостьПродажУпр:
Если ИСТИНА ИЛИ НЕ Строка.СтатусПартии = Перечисления.СтатусыПартийТоваров.НаКомиссию Тогда
Движение.Стоимость = КоэффициентСторно*Строка.Стоимость;
Иначе
Просто убрать это условие (помечено желтым).
В 1С принято, что если товара не хватает, то остатки по складам списываются в минус, а остатки по партиям списываются в ноль. В результате между остатками по складам и партиям накапливается расхождение. Чтобы этого избежать, нужно списывать количество по нулевой стоимости. Для этого нужно вставить заплатку всего в одном месте, в процедуре УправлениеЗапасамиПартионныйУчетСписаниеПартий:
Если (КоличествоОсталосьПогасить > 0) Тогда
СообщитьОНехваткеПартии(СтрокаДокумента, СтруктураПараметров, РегистрУчета, КоличествоОсталосьПогасить);
//Осипов, чтобы списывалось адекватно, добавляем новое движение...
Движение = ДобавитьДвижениеВСтруктуруПараметров(ИмяРегистра, СтруктураПараметров);
// Свойства
Движение.Период = СтрокаДокумента.Период;
Движение.Регистратор = СтрокаДокумента.Регистратор;
Движение.Активность = Истина;
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
// Измерения
Движение.Номенклатура = СтрокаДокумента.Номенклатура; //Важно, СтрокаДокумента, а не СтрокаПартии
// Ресурсы
Движение.Количество = КоличествоОсталосьПогасить;
Движение.Стоимость = 0;
// Реквизиты
Движение.КодОперации = СтрокаДокумента.КодОперацииПартииТоваров;
//Важно - строки партии нет, поэтому никаких зависимых движения
//// Заполнение полей, специфических для учета
//ЗаполнитьПоляЗаписиСписания(Движение, СтрокаПартии, СтрокаДокумента, СтруктураПараметров, РегистрУчета, КоэффСписания, КоэффПоступления, КоэффСписанияВалютный);
//Важно - эти движения оставляем, но добавляю коэффициенты поступления и списания по одному.
// Обработка движений, связанных со списанием определенных партий по документу (специфика разных видов учета)
КоэффСписания = 1;
КоэффПоступления = 1;
ВыполнитьСвязанныеСоСписаниемДвижения(СтрокаДокумента, СтруктураПараметров, Движение, КоэффСписания, КоэффПоступления);
// Обработка поступления
КорректировкаСтоимости.ВыполнитьКорДвижение(РегистрУчета, СтрокаДокумента, СтруктураПараметров, Движение);
Иначе
При этом код по добавлению движения нужно добавить из кода чуть выше этого места.
Альтернативный вариант еще проще – если возникает недостаток количества, добавлять его в последнюю партию. Но он не универсальный, т.к. следует помнить, что иногда, еслидвижения по партиям и метод не сработает.
В версионировании лучше сделать, чтобы сохранялась нулевая версия, начальная версия нового объекта, а не только последующие. Это помогает более точно контролировать изменения. Список доработок в листинге:
Процедура МеханизмВерсионированияОбъектов_ПередЗаписьюДокумента
//Осипов - для новых - всегда
Если Источник.ЭтоНовый() ИЛИ МеханизмВерсионированияОбъектов_ПроверкаИзменений(Источник.Ссылка, Источник) Тогда
ПодготовитьЗаписьОбъекта (Источник, РежимЗаписи);
КонецЕсли;
КонецПроцедуры
Процедура ПодготовитьЗаписьОбъекта
ВерсионироватьОбъект = Истина;
Если ВариантВерсионирования = Перечисления.ВариантыВерсионированияОбъектов.НеВерсионировать Тогда
ВерсионироватьОбъект = Ложь;
ИначеЕсли ВариантВерсионирования = Перечисления.ВариантыВерсионированияОбъектов.ВерсионироватьПриПроведении Тогда
Если ЧислоВерсийОбъекта = 0 И РежимЗаписи <> РежимЗаписиДокумента.Проведение Тогда
//Осипов - версионируем всегда, убрал...
//ВерсионироватьОбъект = Ложь;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура МеханизмВерсионированияОбъектов_ПриЗаписиСправочникаДокумента
Если ЧислоВерсийОбъекта = 0 Тогда
Если НЕ ПараметрыВерсионирования.Свойство("ЭтоНовыйОбъектВерсионирования") Тогда
//Пишем старую версию объекта
//Пишем новую версию объекта
Иначе
//Пишем новую версию объекта
КонецПроцедуры
Удобно сделать какие-то настройки, которые должны быть у всех пользователей и вызывать их одной кнопкой. Таким настройкам можно дать имя, начинающееся с «!». Пользователь нажимает кнопку выбора и ему открывается список настроек, начинающихся на «!». Он может быстро выбрать настроенные пользователям программистом настройки.
Процедура ВыбратьНастройку(Кнопка)
ВыбратьНастройкуУниверсальногоОтчета(ЭтаФорма, "!", ЭлементыФормы.КоманднаяПанельФормы);
// Выбираем все настройки, начинающиеся на ! знак
КонецПроцедуры
Функция ВыбратьНастройкуУниверсальногоОтчета(ФормаОтчета, Приставка, ЭлементФормы)
Перем ИмяОбъекта;
ИмяОбъекта = Строка(ФормаОтчета.ЭтотОбъект);
СтруктураНастройки = Новый Структура;
СтруктураНастройки.Вставить("Пользователь", глЗначениеПеременной("глТекущийПользователь"));
СтруктураНастройки.Вставить("ИмяОбъекта", ИмяОбъекта);
СтруктураНастройки.Вставить("НаименованиеНастройки", Неопределено);
З = Новый Запрос(
"ВЫБРАТЬ
| СохраненныеНастройки.ИмяОбъекта,
| СохраненныеНастройки.НаименованиеНастройки,
| СохраненныеНастройки.СохраненнаяНастройка
|ИЗ
| РегистрСведений.СохраненныеНастройки КАК СохраненныеНастройки
|ГДЕ
| СохраненныеНастройки.ИмяОбъекта = &ИмяОбъекта");
З.УстановитьПараметр("ИмяОбъекта", ИмяОбъекта);
РезНаборНастроек = Новый СписокЗначений();
ТЗ = З.Выполнить().Выгрузить();
Для Каждого Стр ИЗ ТЗ Цикл
Если Лев(Стр.НаименованиеНастройки, СтрДлина(Приставка)) = Приставка Тогда
РезНаборНастроек.Добавить(Стр.СохраненнаяНастройка, Стр.НаименованиеНастройки);
КонецЕсли;
КонецЦикла;
Выбор = Неопределено;
Если РезНаборНастроек.Количество() > 0 Тогда
Выбор = ФормаОтчета.ВыбратьИзСписка(РезНаборНастроек, ЭлементФормы);
КонецЕсли;
Если Выбор <> Неопределено Тогда
СтруктураНастройки.Вставить("НаименованиеНастройки", Выбор.Представление);
СтруктураНастройки.Вставить("СохраненнаяНастройка", Выбор.Значение);
УниверсальныеМеханизмы.ПолучитьНастройку(СтруктураНастройки);
Результат = СтруктураНастройки;
Отчет = ФормаОтчета.ЭтотОбъект;
Объект = ФормаОтчета.ЭтотОбъект.УниверсальныйОтчет;
Объект.мТекущаяНастройка = Результат;
Отчет.ВосстановитьНастройкиИзСтруктуры(Результат.СохраненнаяНастройка);
Объект.ВосстановитьПараметрыПечати(Объект, Отчет, ФормаОтчета, Результат.СохраненнаяНастройка);
КонецЕсли;
КонецФункции
Часто пользователи хотят такого поведения универсального отчета. Реализуется просто. В модуле отчета «Универсальный отчет» в самом конце добавляем строку:
БыстрыйОтборРазвернут = истина; //Для удобства пользователей
При грамотном проектировании конфигурации можно было бы добиться того, что любую печатную форму можно было бы легко превратить во внешнюю печатную форму, но этого увы, не сделано.
Самый простой способ – скопировать модуль объекта целиком во внешнюю обработку и назначить обработке основной объект того типа, который распечатывается. Тогда практически ничего переделывать не надо.
Если нужно сформировать извещения некоторым пользователям, можно создать булеву настройку пользователей, которую назначать тем пользователям, которые должны получать извещения. Далее, получить список этих пользователей и поставить им задачу с оповещением текущим времен.
Пользователи = ПользователиСУказаннойНастройкой(ПланыВидовХарактеристик.НастройкиПользователей.ПолучатьИзвещенияОНочномПерепроведении, истина);
Для Каждого Пользователь Из Пользователи Цикл
//Формируем извещение
Задача = Задачи.ЗадачиПользователя.СоздатьЗадачу();
Задача.Дата = ТекущаяДата();
Задача.Исполнитель = Пользователь;
Задача.Оповещение = истина;
Задача.СрокОповещения = ТекущаяДата();
Задача.Наименование = "Состояние ночного перепроведения";
Задача.Описание =
"Последний раз перепроведение работало: " + Константы.ДатаПоследнегоЗапускаПроведенияДокументов.Получить() + Символы.ПС +
"Перепроведение дошло до даты: " + Константы.ДатаПоследнегоЗапускаПроведенияДокументов.Получить() + Символы.ПС +
"Список ошибок по документам: " + Символы.ПС +
Константы.ОшибкиПриПроведенииДокументов.Получить() + Символы.ПС;
Задача.Записать();
КонецЦикла;
Окошко напоминания о задачах довольно навязчивое. Каждые N секунд оно активизируется, показывая текущие задачи пользователя. В идеале было бы, чтобы оно активизировалось только, если появляются новые задачи. Но можно поступить проще – если окошко открыто, то не активизировать его.
Для этого нужно в процедуре УправлениеКонтактами.ПроверитьНапоминанияПользователя::ПроверитьНапоминанияПользователя использовать такой код (жирным отмечены мои правки):
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
ИзначальноФормаОповещенияЗадачОткрыта = ФормаОповещенияЗадачОткрыта; //Осипов
Если НЕ ФормаОповещенияЗадачОткрыта Тогда
ФормаОповещения.Открыть();
ФормаОповещенияЗадачОткрыта = Истина;
КонецЕсли;
ВыгрузкаДляСверки = РезультатЗапроса.Выгрузить();
Если НЕ ИзначальноФормаОповещенияЗадачОткрыта Тогда //Осипов
ФормаОповещения.АктивизироватьФорму(ВыгрузкаДляСверки);
КонецЕсли; //Осипов
Нужно, чтобы к имени терминала добавлялось имя компьютера, иначе настройки у всех пользователей терминала будут одинаковыми.
Корректировать процедуру в модуле ТОСервер.
// Функция возвращает строку c именем компьютера для нужд торгового оборудования.
//
// Возвращаемое значение:
// Строка - имя компьютера для торгового оборудования.
//
Функция ПолучитьИмяКомпьютераТО() Экспорт
Если мИмяКомпьютера = Неопределено Тогда
мИмяКомпьютера = ВРег(ИмяКомпьютера());
Попытка
WshShell = Новый COMОбъект("WScript.Shell");
ИмяКомпьютераКлиента = СокрЛП(WshShell.expandEnvironmentStrings("%CLIENTNAME%"));
Если ИмяКомпьютераКлиента <> "" Тогда
мИмяКомпьютера = мИмяКомпьютера + "_" + ИмяКомпьютераКлиента;
КонецЕсли;
Исключение
КонецПопытки;
КонецЕсли;
Возврат мИмяКомпьютера;
КонецФункции // ПолучитьИмяКомпьютераТО()
Часто на предприятиях используется только одна настройка РМК, общая для всех компьютеров. Это делается, чтобы продавцы привыкли к интерфейсу и не испытывали проблем при переходе из магазина в магазин сети.
При запуске нового магазина приходится везде прописывать основную настройку РМК в регистр сведений «Настройки РМК».
Избежать этого можно вставкой подобного кода в процедуру «ПолучитьНастройкуРМККомпьютера» модуля «РМК»:
//Основная настройка РМК, вставка
ПоУмолчанию = Справочники.ДопКонстанты.ОсновнаяНастройкаРМК.Значение;
Если ЗначениеЗаполнено(ПоУмолчанию) Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Возврат Результат; //Это типовой код
Печать чека происходит в процедуре «ПечатьЧека» обработки «ТОСервер».
Шапка печатается средствами драйвера ФР, хотя 1С поддерживает возможность полной настройки чека, если задан шаблон чека.
В рознице процедура поиска по штрих-коду – процедура модуля РаботаСТорговымОборудованием.ОбработатьВведенныйШтрихКод. В УТ 10.3 это процедура обработки ТОСервер. ОбработатьСобытиеСШК и дублируется в процедуре ТОСервер.ОбработатьВведенныйШтрихкод.
В типовых конфигурациях поиск по штрих-коду осуществляется только по регистру сведений штрих-кодов. Но иногда достаточно неудобно заносить в этот регистр копию серийных номеров из справочников серийные номера или характеристики.
Проще доработать общую для конфигурации функцию поиска по штрих-коду РаботаСТорговымОборудованием.ОбработатьВведенныйШтрихКод, чтобы программа искала также и по наименованию этих справочников. Вот пример доработки для поиска по характеристикам (добавленный код в блоке Если):
Запрос = Новый Запрос(
"ВЫБРАТЬ
| РегШК.Владелец КАК Владелец,
| РегШК.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
| РегШК.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры
|ИЗ
| РегистрСведений.Штрихкоды КАК РегШК
|ГДЕ
| РегШК.Штрихкод = &Штрихкод");
Если ЗначениеЗаполнено(СокрЛП(ШтрихКод)) Тогда
Запрос.Текст = Запрос.Текст +
" ОБЪЕДИНИТЬ " +
"ВЫБРАТЬ
| Т.Владелец КАК Владелец,
| Т.Владелец.ЕдиницаДляОтчетов КАК ЕдиницаИзмерения,
| Т.Ссылка КАК ХарактеристикаНоменклатуры
|ИЗ
| Справочник.ХарактеристикиНоменклатуры КАК Т
|ГДЕ
| Т.Наименование = &Штрихкод";
КонецЕсли;
Используется именно «ОБЪЕДИНИТЬ», чтобы не дублировались одни и те же товары с одинаковыми единицами и штрих-кодами.
Пример из УТ доработки для поиска по наименованию серийного номера. В исходный код внедряется дополнительная функция ДополнительныйПоискШКПоХарактеристикам:
РезультатЗапроса = Запрос.Выполнить();
ДополнительныйПоискШКПоХарактеристикам(РезультатЗапроса, ШК); //Осипов
Если Не РезультатЗапроса.Пустой() Тогда
РезультатЗапроса.Следующий();
Ответ = Клиент.СШКНоменклатура(РезультатЗапроса.Владелец,
РезультатЗапроса.ХарактеристикаНоменклатуры,
РезультатЗапроса.СерияНоменклатуры,
РезультатЗапроса.Качество,
РезультатЗапроса.ЕдиницаИзмерения,
1,
СШК);
Если Ответ Тогда
Обработка.СобытиеОбработано(Объект);
Возврат Результат;
КонецЕсли;
КонецЕсли;
Эта функция делает дополнительный поиск:
Функция ДополнительныйПоискШКПоХарактеристикам(РезультатЗапроса, ШК)
//Если ничего не найдено, ищем по характеристикам товара
//20100720 осипов
Если РезультатЗапроса.Пустой() Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ЗначенияСвойствОбъектов.Объект.Владелец КАК Владелец,
| ЗначенияСвойствОбъектов.Объект.Владелец.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
| ЗначенияСвойствОбъектов.Объект КАК ХарактеристикаНоменклатуры,
| ЗНАЧЕНИЕ(Справочник.Качество.Новый) КАК Качество,
| ЗНАЧЕНИЕ(Справочник.СерииНоменклатуры.ПустаяСсылка) КАК СерияНоменклатуры
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство = &Свойство
| И ЗначенияСвойствОбъектов.Значение = &Штрихкод";
Запрос.УстановитьПараметр("Свойство",ПланыВидовХарактеристик.СвойстваОбъектов.СерийныйНомер);
Запрос.УстановитьПараметр("Штрихкод", ШК);
РезультатЗапроса = Запрос.Выполнить();
КонецЕсли;
Если РезультатЗапроса.Пустой() Тогда
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Т.Владелец КАК Владелец,
| Т.Владелец.ЕдиницаДляОтчетов КАК ЕдиницаИзмерения,
| Т.Ссылка КАК ХарактеристикаНоменклатуры,
| Неопределено КАК СерияНоменклатуры,
| Неопределено КАК Качество,
| &Штрихкод
|ИЗ
| Справочник.ХарактеристикиНоменклатуры КАК Т
|ГДЕ
| Т.Наименование = &Штрихкод";
Запрос.УстановитьПараметр("Штрихкод", ШК);
РезультатЗапроса = Запрос.Выполнить();
КонецЕсли;
КонецФункции
Чтобы форма реагировала на сканер штрих-кода, нужно добавить в нее следующий код:
Функция СШКОбработатьШтрихкод(ШК) Экспорт
//... некий код по обработке ШК
КонецФункции
Процедура ВнешнееСобытие(Источник, Событие, Данные)
Если Событие = "BarCodeValue" Тогда
ПолучитьСерверТО().ОбработатьВнешнееСобытие(Событие, Данные, ЭтаФорма);
КонецЕсли;
КонецПроцедуры
Процедура ПриОткрытии()
ПолучитьСерверТО().ПодключитьКлиента(ЭтаФорма);
КонецПроцедуры
Функция ПоддерживаетсяВидТО(ВидТО) Экспорт
Возврат ВидТО = Перечисления.ВидыТорговогоОборудования.СканерШтрихКода;
КонецФункции
Обратите внимание – выполняется некоторая инициализация при открытии формы.
В этом разделе собрана информация обо всех обменах данными.
При распаковке файла сообщения 1С размещает файлы из архива во временном каталоге. При этом файлы распаковываются в этот каталог под теми же именами, что были в архиве. Имена файлов в архиве не меняются, поэтому возможна ситуация, когда файл не получится распаковать, т.к. файл во временном каталоге с таким именем заблокирован. Почему возникает блокировка, непонятно, но это частое явление. Поэтому имеет смысл распаковывать всегда в новый временный каталог. Доработка выглядит так:
Функция РазархивироватьФайл(СтруктураНастроекОбменаДанными)
...
КаталогДляРаспаковки = КаталогВременныхФайлов(); //Исходный код
...
//При каждой распаковке использую отдельный каталог, чтобы не
было блокировок...
КаталогДляРаспаковки = РаботаСФайлами.ПолучитьИмяФайла(КаталогДляРаспаковки,
Строка(Новый УникальныйИдентификатор()));
СоздатьКаталог(КаталогДляРаспаковки);
...
КонецФункции
Причиной, почему не проводятся отложенные к проведению документы может быть то, что для регламентного задания не задан пользователь, а в конфигурации сделана проверка прав доступа, привязанная к пользователю. К сожалению, в 1С нельзя проводить документы в режиме «Обмен данными - загрузка». Поэтому нужно для регламентных заданий назначить пользователя с максимальными правами.
У нас была проблема с тем, что при выполнении фонового задания, где не определен пользователь, срабатывал запрет на проведение документа задним числом, в результате отложенное проведение работало только при запуске вручную под администратором.
В плане обмена в УТ не нужно использовать префикс, лучше использовать разные префиксы по организациям в УТ и БП. Префиксация может привести к опасному глюку, когда несколько номеров могут конвертироваться при добавке префикса в один, в результате несколько документов будут сливаться в один. Это возникает, если номер документа короткий, т.е. справа в номере есть пробелы. Лучше не рисковать и не использовать префиксацию.
Можно отключить в правилах обмена сопоставление по номеру документа для всех документов, т.к. это часто приводит к ошибкам сопоставления.
Меня попросили сделать настройку для документа «Платежный ордер на списание денежных средств», чтобы для операция по расчетно-кассовому обслуживанию в УТ эти операции ставились на нужный счет в БП, а не на счет 000 или счет взаиморасчетов с контрагентом, как это происходило автоматом.
Я написал следующий код в конец алгоритма «После выгрузки» правила «ПлатежныйОрдерСписаниеДенежныхСредств»:
//Осипов 20100428 добавил платежки
//Объект = Документы.ПлатежныйОрдерСписаниеДенежныхСредств.НайтиПоНомеру( "RZ000000026", '20100426').ПолучитьОбъект();
Если СокрЛП(Объект.СтатьяДвиженияДенежныхСредств.Наименование) = "Услуги РКО"
И Объект.ВидОперации = Перечисления.ВидыОперацийСписаниеБезналичныхДенежныхСредств.ПрочееСписаниеБезналичныхДенежныхСредств
И (НЕ ЗначениеЗаполнено(Объект.СчетУчетаРасчетовСКонтрагентом) ИЛИ Объект.СчетУчетаРасчетовСКонтрагентом.Код = "000") Тогда
Объект.СчетУчетаРасчетовСКонтрагентом = ПланыСчетов.Хозрасчетный.ПрочиеРасходы;
ЭлементДР = Справочники.ПрочиеДоходыИРасходы.НайтиПоНаименованию("Расходы на услуги банков");
Объект.СубконтоДт1 = ЭлементДР;
Объект.СчетУчетаНУ = ПланыСчетов.Налоговый.ВнереализационныеРасходы;
Объект.СубконтоДт1НУ = ЭлементДР;
Если Объект.РасшифровкаПлатежа.Количество() > 0 Тогда
ЗаполнитьЗначенияСвойств(Объект.РасшифровкаПлатежа[0], Объект, "СчетУчетаРасчетовСКонтрагентом")
КонецЕсли;
//Объект.ПолучитьФорму().Открыть();
КонецЕсли;
Этот код я отлаживал в консоли кода на конкретном документе, а потом просто вставил в правила обмена. Для возможности последующей отладки оставил закомментированный код по работе с конкретным документом.
При повторной загрузке региона, например, Московской области, старые записи не удаляются.
В результате получаем ошибки у пользователей.
Например, раньше Котельники принадлежали Люберецкому району МО. Теперь же они находятся просто как город в МО.
Если грузить в чистую базу, то видим только правильный вариант.
Если грузить повторно, то видим оба варианта.
По идее, должна была бы быть галочка "Очищать регион перед загрузкой".
Чтобы идеально загружать АК, нужно сначала очищать регион, потом загружать.
В процедуре ОбработкаЧековККМ обработки ЗакрытиеКассовойСмены есть такой код:
Если РезультатЗапроса.Пустой() Тогда
ТекстСообщения = "За кассовую смену не продано ни одного товара по выбранной кассе ККМ.";
ОбщегоНазначения.СообщитьОбОшибке(ТекстСообщения);
ОтменитьТранзакцию = Истина;
Ниже есть код:
Если ОтменитьТранзакцию Тогда
ОтменитьТранзакцию();
Но из-за особенностей расположения условий, если срабатывает первое условие, то во второй код выполнение не заходит. Поэтому транзакция так и остается не отмененной.
Дата |
Изменение |
Место |
Примечание |
|
|||
|
|
|
|
26.03.12 |
Вставка |
Нулевая версия при версионировании УПП |
|
26.03.12 |
Вставка |
Статья про списание в минус в партионном учете |
|