Перед тем, как в принципе начать решение, а еще лучше до раздачи билетов, необходимо произвести некоторую настройку каркасной базы.
1. В свойствах конфигурации режим управления блокировками намеренно установлен в автоматический, хотя нужно управляемый. Это необходимо исправить сразу, т.к. по мере решения вы можете насоздавать объектов с неверно установленным режимом управления блокировками, чем позже вы это заметите, тем больше потом исправлять. А если не заметите вовсе, могут "штрафануть".
2. Запустите хотя бы раз синтакс помощник. Компы там не шибко мощные, и если СП ни разу не запускался с момента установки/обновления 1С, его индексация занимает время.
3. В настройках конфигуратора заранее зайдите в Сервис - Параметры и поставьте там "Управляемое приложение и обычное приложение". Это позволит быстро, когда это необходимо, запускать на исполнение конфигурацию в обычном режиме. В обычном режиме доступна консоль запросов. К этому пункту можно так же добавить, что в сети валяется управляемая консоль запросов, которую можно найти обычным поиском и перетащить себе на комп.
4. Создайте сразу подсистемы, этим вы сделаете часть работы которую делать нужно все равно обязательно, однако наглядный интерфейс даже во время разработки упростит вам тестирование. Причем для оптимизации времени, как вариант, можно делать так: создаете подсистему ОперативныйУчет, далее в ней три подсистемы Документы, СправочникиИПланы, Регистры. После этого подсистему ОперативныйУчет раскопируйте еще на две (подчиненные тоже скопируются) и переименуйте в БухгалтерскийУчет и Расчет.
5. Если вы пришли раньше (а я так понял, это все делают) то сидеть ждать, пока препод засунет ключ с лицензиями чтобы запускалась 1С, тоже не следует, откройте блокнот, и создайте себе несколько заготовок кода, который в любом случае понадобится.
ОПЕРАТИВНЫЙ УЧЕТ
6. Новая методика проведения документов. Используется в небольшом проценте задач, однако если попадется, не перемудрите с блокировками, тут ее надо устанавливать при помощи свойства БлокироватьДляИзменения, так же важно включить движения самого документа в запрос по отлову минусов. Пример ниже:
Для каждого Строка Из СписокНоменклатуры Цикл Запись = Движения.ТоварыНаСкладах.ДобавитьРасход(); Запись.Период = Дата; Запись.Номенклатура = Строка.Номенклатура; Запись.Склад = Склад; Запись.Количество = Строка.Количество;
КонецЦикла; Движения.ТоварыНаСкладах.БлокироватьДляИзменения = Истина; // НАБОР БУДЕТ ЗАБЛОКИРОВАН СРАЗУ ПОСЛЕ ЗАПИСИ Движения.ТоварыНаСкладах.Записать(); Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Ссылка", Ссылка); Запрос.УстановитьПараметр("МоментВремени", Новый Граница(МоментВремени(),ВидГраницы.Включая)); // ВКЛЮЧАЯ ДВИЖЕНИЯ САМОГО ДОКУМЕНТА
Запрос.УстановитьПараметр("Склад", Склад); Запрос.Текст = "ВЫБРАТЬ | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура, | СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК КоличествоДокумент |ПОМЕСТИТЬ втДанныеДокумента |ИЗ | Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
|ГДЕ
|РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура |; | |//////////////////////////////////////////////////////////////////////////////// |ВЫБРАТЬ | ТоварыНаСкладахОстатки.Номенклатура, | ТоварыНаСкладахОстатки.КоличествоОстаток |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки( | &МоментВремени, | Номенклатура В | (ВЫБРАТЬ | втДанныеДокумента.Номенклатура | ИЗ | втДанныеДокумента КАК втДанныеДокумента) | И Склад = &Склад) КАК ТоварыНаСкладахОстатки |ГДЕ | ТоварыНаСкладахОстатки.КоличествоОстаток < 0"; Выборка = Запрос.Выполнить().Выбрать(); Если Выборка.Количество() Тогда
Отказ = Истина; КонецЕсли;
7. Правильный порядок расположения операндов при расчете себестоимости. Проблема копеек. Есть мнение, что правильно делать так:
НоваяЗапись.Сумма = Выборка.КоличествоДокумент * Выборка.СуммаОстаток / Выборка.КоличествоОстаток;
То есть, умножение идет перед делением, что обеспечит в случае 3 * 10 / 3 число 10 а не число 9.99999999(9), как может быть при совсем неправильном порядке, однако методически такой способ не считается верным (это слова преподавателя из 1С), во-первых, деление по методике должно идти сначала, но деление не суммы на остаток количества, а количества к списанию на количество остатка, во-вторых, при равенстве количеств, лучше просто взять сумму, поэтому делая так, как ниже, проблем быть не должно:
НоваяЗапись.Сумма = ?(Выборка.КоличествоДокумент = Выборка.КоличествоОстаток, Выборка.СуммаОстаток,
Окр(Выборка.КоличествоДокумент / Выборка.КоличествоОстаток,2) * Выборка.СуммаОстаток);
8. Правильная установка момента времени в качестве параметра запроса при проведении документа. Если документ по вашему решению может проводиться оперативно, то установку параметра для запроса оптимальнее делать так:
Запрос.УстановитьПараметр("МоментВремени", ?(РежимПроведения=РежимПроведенияДокумента.Оперативный,
Неопределено, МоментВремени()));
9. Списание себестоимости по партиям, наиболее часто в том или ином варианте встречается при решении задач оперативного учета, бухгалерского тоже. Управляемая блокировка должна располагаться до получения данных запросом. Саму блокировку приведу ниже. Важный момент, сортировка при получении выборки, должна производиться по моменту времени, не по дате, и тем более не по ссылке.
Движения.ОстаткиНоменклатуры.Записать(); Движения.Продажи.Записать(); Движения.ОстаткиНоменклатуры.Записывать = Истина; Движения.Продажи.Записывать = Истина; // ТУТ НЕОБХОДИМА УПРАВЛЯЕМАЯ БЛОКИРОВКА Запрос = Новый Запрос; Запрос.УстановитьПараметр("Ссылка", Ссылка); Запрос.УстановитьПараметр("Момент", ?(РежимПроведения=РежимПроведенияДокумента.Оперативный, Неопределено, МоментВремени())); Запрос.Текст = "ВЫБРАТЬ |РасходнаяНакладнаяСписокНоменклатуры.Номенклатура, |СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество, |СУММА(РасходнаяНакладнаяСписокНоменклатуры.Сумма) КАК Сумма |ПОМЕСТИТЬ втТаблицаДокумента |ИЗ |Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры |ГДЕ |РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка | |СГРУППИРОВАТЬ ПО |РасходнаяНакладнаяСписокНоменклатуры.Номенклатура |; | |//////////////////////////////////////////////////////////////////////////////// |ВЫБРАТЬ |втТаблицаДокумента.Номенклатура КАК Номенклатура, |ОстаткиНоменклатурыОстатки.Партия, |ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток, |ЕСТЬNULL(ОстаткиНоменклатурыОстатки.СуммаОстаток, 0) КАК СуммаОстаток, |втТаблицаДокумента.Количество КАК КоличествоДокумент, |втТаблицаДокумента.Сумма КАК СуммаДокумент, |втТаблицаДокумента.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры |ИЗ |втТаблицаДокумента КАК втТаблицаДокумента |ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки( |&Момент, |Номенклатура В |(ВЫБРАТЬ |втТаблицаДокумента.Номенклатура |ИЗ |втТаблицаДокумента КАК втТаблицаДокумента)) КАК ОстаткиНоменклатурыОстатки |ПО втТаблицаДокумента.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура | |УПОРЯДОЧИТЬ ПО |ОстаткиНоменклатурыОстатки.Партия.МоментВремени |ИТОГИ |СУММА(КоличествоОстаток), |СУММА(СуммаОстаток), |МАКСИМУМ(КоличествоДокумент), |МАКСИМУМ(СуммаДокумент) |ПО |Номенклатура"; Если РегистрыСведений.НастройкаУчетнойПолитики.ПолучитьПоследнее(Дата).УчетнаяПолитика = Перечисления.УчетнаяПолитика.ЛИФО Тогда Запрос.Текст = СтрЗаменить(Запрос.Текст,"ОстаткиНоменклатурыОстатки.Партия.МоментВремени",
"ОстаткиНоменклатурыОстатки.Партия.МоментВремени УБЫВ"); КонецЕсли; ВыборкаНоменклатура = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам,"Номенклатура"); Пока ВыборкаНоменклатура.Следующий() Цикл Себестоимость = 0; Если ВыборкаНоменклатура.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Товар Тогда КоличествоСписать = ВыборкаНоменклатура.КоличествоДокумент; Если ВыборкаНоменклатура.КоличествоДокумент > ВыборкаНоменклатура.КоличествоОстаток Тогда Отказ = Истина; // Оповестить о нехватке. Продолжить; КонецЕсли; ВыборкаПартии = ВыборкаНоменклатура.Выбрать(); Пока КоличествоСписать > 0 И ВыборкаПартии.Следующий() Цикл НоваяЗапись = Движения.ОстаткиНоменклатуры.ДобавитьРасход(); НоваяЗапись.Период = Дата; НоваяЗапись.Номенклатура = ВыборкаНоменклатура.Номенклатура; НоваяЗапись.Партия = ВыборкаПартии.Партия; НоваяЗапись.Количество = Мин(КоличествоСписать, ВыборкаПартии.КоличествоОстаток); НоваяЗапись.Сумма = ?(ВыборкаПартии.КоличествоОстаток=НоваяЗапись.Количество,ВыборкаПартии.СуммаОстаток,
Окр(НоваяЗапись.Количество/ВыборкаПартии.КоличествоОстаток,2)*ВыборкаПартии.СуммаОстаток); КоличествоСписать = КоличествоСписать - НоваяЗапись.Количество; Себестоимость = Себестоимость + НоваяЗапись.Сумма; КонецЦикла; КонецЕсли; НоваяЗапись = Движения.Продажи.Добавить(); НоваяЗапись.Период = Дата; НоваяЗапись.Номенклатура = ВыборкаНоменклатура.Номенклатура; НоваяЗапись.Количество = ВыборкаНоменклатура.КоличествоДокумент; НоваяЗапись.Сумма = ВыборкаНоменклатура.СуммаДокумент; НоваяЗапись.Себестоимость = Себестоимость; КонецЦикла;
10. Управляемая транзакционная блокировка регистра накопления. Приводится в действие сразу после вызова метода Заблокировать() и действует до окончания транзакции. Должна применяться всегда, когда на считываемые сначала данные опирается логика работы дальнейшего алгоритма.
Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры"); ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры; ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура"); Блокировка.Заблокировать();
БУХГАЛТЕРСКИЙ УЧЕТ
11. Правильная организация процедуры ПередЗаписью документа РучнаяОперация, который необходимо реализовывать в любой задаче по бухгалтерскому учету. Процедура, разумеется, модуля объекта документа. Так же стоит помнить, что документу необходимо запретить проведение.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения) Проводки = Движения.Управленческий; Проводки.Записывать = Истина; Если НЕ Проводки.Модифицированность() И НЕ ЭтоНовый() Тогда Проводки.Прочитать(); КонецЕсли; Для каждого Проводка Из Проводки Цикл Проводка.Период = Дата; Если НЕ ЭтоНовый() И ПометкаУдаления <> Ссылка.ПометкаУдаления Тогда Проводка.Активность = НЕ ПометкаУдаления; КонецЕсли; КонецЦикла; КонецПроцедуры
12. Управляемая транзакционная блокировка регистра бухгалтерии.
Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить("РегистрБухгалтерии.Управленческий"); ЭлементБлокировки.УстановитьЗначение("Счет", ПланыСчетов.Управленческий.Товары); ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры; ЭлементБлокировки.ИспользоватьИзИсточникаДанных(ПланыВидовХарактеристик.ВидыСубконто.Номенклатура, "Номенклатура"); Блокировка.Заблокировать();
13. Т.к. в разрабатываемой вами конфигурации присутствие документа РучнаяОперация является обязательным,это накладывает неявное, но обязательное требование - во всех запросах и отчетах, которые вы делаете для решения задачи, накладывать максимальное количество уточняющих условий на счета. К примеру, если в условии вашей задачи упомянуто, что документ приходная делает проводку Дт Товары - Кт Поставщики, а расходная - Дт ПрибылиУбытки Кт Товары то анализируя потом для какой-либо цели (зависит от задачи) всю эту ситуацию, удобно использовать таблицу Обороты, задавая условие счета равное счету Товары,а на выходе дебетовый оборот будет со счетом Поставщики, а кредитовый - ПрибылиУбытки. Но в условии виртуальной таблицы обязательно нужно указать условие, приведенное ниже, и тогда РучнаяОперация, какая бы корреспонденция счетов там ни была введена, не сможет нарушить работу вашего запроса:
| КорСчет В (Значение(ПланСчетов.Управленческий.Поставщики), Значение(ПланСчетов.Управленческий.ПрибылиУбытки))
ПЕРИОДИЧЕСКИЕ РАСЧЕТЫ
14. Самое главное, это правильная организация процедуры расчета записей. Во-первых, желательно ей быть в общем модуле. И не только потому, что он выполняется на сервере, а потому, что это более универсально, чем в модуле конкретного документа. Во-вторых, желательно найти "золотую середину" при получении данных для расчета, с одной стороны неправильно получить одним махом все все данные для всего набора записей, и дальше в одном цикле делать расчет, с другой стороны - получать для каждой записи набора - избыточно. Вариант такой золотой середины ниже, данные получаются по принципу "один запрос на каждый вид расчета":
Процедура ПроизвестиРасчетНабора(НаборЗаписей) Экспорт Регистратор = НаборЗаписей.Отбор.Регистратор.Значение; Измерения = Новый Массив; Измерения.Добавить("Сотрудник"); Измерения.Добавить("Подразделение"); Запрос = Новый Запрос; Запрос.УстановитьПараметр("Регистратор", Регистратор); Запрос.УстановитьПараметр("Измерения", Измерения); /////////////////////////////// // ПОВТОРЯЕМЫЙ БЛОК ВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад; Если НаборЗаписей.ПроверитьНаличиеВидаРасчета(ВидРасчета) Тогда Запрос.УстановитьПараметр("ВидРасчета", ВидРасчета); Запрос.Текст = ""; // ТЕКСТ ЗАПРОСА ДЛЯ КОНКРЕТНОГО ВИДА РАСЧЕТА Выборка = Запрос.Выполнить().Выбрать(); Для каждого Запись Из НаборЗаписей Цикл Если Запись.ВидРасчета <> ВидРасчета Тогда Продолжить; КонецЕсли; Выборка.Сбросить(); Если Выборка.НайтиСледующий(Запись.НомерСтроки, "НомерСтроки") Тогда Запись.Результат = 0; // ФОРМУЛА ДЛЯ РАСЧЕТА КонецЕсли; КонецЦикла; КонецЕсли; /////////////////////////////// НаборЗаписей.Записать(, Истина); // ПОВТОРЯЕМЫЙ БЛОК НаборЗаписей.Записать(, Истина); // ПОВТОРЯЕМЫЙ БЛОК НаборЗаписей.Записать(, Истина); КонецПроцедуры
Функцию "ПроверитьНаличиеВидаРасчета" имеет смысл разместить в каждом регистре в модуле набора записей. Тогда повторяемые блоки будут универсальными. Сама функция имеет следующий вид:
Функция ПроверитьНаличиеВидаРасчета(ВидРасчета) Экспорт Возврат ?(ЭтотОбъект.ВыгрузитьКолонку("ВидРасчета").Найти(ВидРасчета)=Неопределено,Ложь,Истина); КонецФункции
15. Заполнение сторно записей для набора записей регистра расчета.
ТаблицаДополнений = Движения.ОсновныеНачисления.ПолучитьДополнение(); Для каждого ЗаписьСторно Из ТаблицаДополнений Цикл Движение = Движения.ОсновныеНачисления.Добавить(); ЗаполнитьЗначенияСвойств(Движение, ЗаписьСторно); Движение.ПериодРегистрации = ЗаписьСторно.ПериодРегистрацииСторно; Движение.ПериодДействияНачало = ЗаписьСторно.ПериодДействияНачалоСторно; Движение.ПериодДействияКонец = ЗаписьСторно.ПериодДействияКонецСторно; Движение.Сторно = Истина; КонецЦикла;
16. Разбитие оклада при многократном изменении его значения внутри периода расчета. Вариант получения разбивки прямо в запросе.
Запрос = Новый Запрос; Запрос.УстановитьПараметр("Ссылка", Ссылка); Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ПериодРегистрации)); Запрос.УстановитьПараметр("ДатаОкончания", КонецМесяца(ПериодРегистрации)); Запрос.Текст = "ВЫБРАТЬ | ВложенныйЗапрос.Период, | ВложенныйЗапрос.Сотрудник, | ВложенныйЗапрос.Подразделение, | ВложенныйЗапрос.Оклад |ПОМЕСТИТЬ втИзмененияОклада |ИЗ | (ВЫБРАТЬ | СведенияОСотрудникахСрезПоследних.Период КАК Период, | СведенияОСотрудникахСрезПоследних.Сотрудник КАК Сотрудник, | СведенияОСотрудникахСрезПоследних.Подразделение КАК Подразделение, | СведенияОСотрудникахСрезПоследних.Оклад КАК Оклад | ИЗ | РегистрСведений.СведенияОСотрудниках.СрезПоследних(&ДатаНачала, ) КАК СведенияОСотрудникахСрезПоследних | | ОБЪЕДИНИТЬ ВСЕ | | ВЫБРАТЬ | СведенияОСотрудниках.Период, | СведенияОСотрудниках.Сотрудник, | СведенияОСотрудниках.Подразделение, | СведенияОСотрудниках.Оклад | ИЗ | РегистрСведений.СведенияОСотрудниках КАК СведенияОСотрудниках | ГДЕ | СведенияОСотрудниках.Период МЕЖДУ &ДатаНачала И &ДатаОкончания) КАК ВложенныйЗапрос |; | |//////////////////////////////////////////////////////////////////////////////// |ВЫБРАТЬ | ВложенныйЗапрос.ПериодРегистрации, | ВложенныйЗапрос.Сотрудник КАК Сотрудник, | ВложенныйЗапрос.Подразделение КАК Подразделение, | ВложенныйЗапрос.ВидРасчета, | ВложенныйЗапрос.Оклад, | ВЫБОР | КОГДА ВложенныйЗапрос.Период1 ЕСТЬ NULL | ИЛИ ВложенныйЗапрос.Период1 < ВложенныйЗапрос.ДатаНачала | ТОГДА ВложенныйЗапрос.ДатаНачала | ИНАЧЕ ВложенныйЗапрос.Период1 | КОНЕЦ КАК ПериодДействияНачало, | ВЫБОР | КОГДА ВложенныйЗапрос.Период2 ЕСТЬ NULL | ТОГДА КОНЕЦПЕРИОДА(ВложенныйЗапрос.ДатаОкончания, ДЕНЬ) | ИНАЧЕ ДОБАВИТЬКДАТЕ(ВложенныйЗапрос.Период2, СЕКУНДА, -1) | КОНЕЦ КАК ПериодДействияКонец |ИЗ | (ВЫБРАТЬ | НачислениеЗарплатыОсновныеНачисления.Ссылка.ПериодРегистрации КАК ПериодРегистрации, | НачислениеЗарплатыОсновныеНачисления.Сотрудник КАК Сотрудник, | НачислениеЗарплатыОсновныеНачисления.Подразделение КАК Подразделение, | НачислениеЗарплатыОсновныеНачисления.ВидРасчета КАК ВидРасчета, | втИзмененияОклада1.Оклад КАК Оклад, | НачислениеЗарплатыОсновныеНачисления.ДатаНачала КАК ДатаНачала, | НачислениеЗарплатыОсновныеНачисления.ДатаОкончания КАК ДатаОкончания, | втИзмененияОклада1.Период КАК Период1, | МИНИМУМ(втИзмененияОклада2.Период) КАК Период2 | ИЗ | Документ.НачислениеЗарплаты.ОсновныеНачисления КАК НачислениеЗарплатыОсновныеНачисления | ЛЕВОЕ СОЕДИНЕНИЕ втИзмененияОклада КАК втИзмененияОклада1 | ЛЕВОЕ СОЕДИНЕНИЕ втИзмененияОклада КАК втИзмененияОклада2 | ПО втИзмененияОклада1.Сотрудник = втИзмененияОклада2.Сотрудник | И втИзмененияОклада1.Подразделение = втИзмененияОклада2.Подразделение | И втИзмененияОклада1.Период < втИзмененияОклада2.Период | ПО НачислениеЗарплатыОсновныеНачисления.Сотрудник = втИзмененияОклада1.Сотрудник | И НачислениеЗарплатыОсновныеНачисления.Подразделение = втИзмененияОклада1.Подразделение | И (НачислениеЗарплатыОсновныеНачисления.ВидРасчета = ЗНАЧЕНИЕ(ПланВидовРасчета.Основныеначисления.Оклад)) | ГДЕ | НачислениеЗарплатыОсновныеНачисления.Ссылка = &Ссылка | | СГРУППИРОВАТЬ ПО | НачислениеЗарплатыОсновныеНачисления.Ссылка.ПериодРегистрации, | НачислениеЗарплатыОсновныеНачисления.Сотрудник, | НачислениеЗарплатыОсновныеНачисления.Подразделение, | НачислениеЗарплатыОсновныеНачисления.ВидРасчета, | втИзмененияОклада1.Оклад, | НачислениеЗарплатыОсновныеНачисления.ДатаНачала, | НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, | втИзмененияОклада1.Период) КАК ВложенныйЗапрос | |УПОРЯДОЧИТЬ ПО | Сотрудник, | Подразделение, | ПериодДействияНачало"; Выборка = Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл НоваяЗапись = Движения.ОсновныеНачисления.Добавить(); ЗаполнитьЗначенияСвойств(НоваяЗапись, Выборка); КонецЦикла;
К данному способу нужно добавить установку параметров виртуальной таблицы среза последних, ограничить только имеющимися в документе сотрудниками. Так же нужно ограничить возможность указания периода действия записей месяцем, отличным от периода регистрации (ну или не ограничивать, тогда получать изменения оклада за более длительный интервал времени).
17. Разбитие на периоды на границе месяца при еженедельных начислениях, прямо в запросе. Часто встречаются задачи с еженедельными начислениями, одна из особенностей в этом случае - переход через месяц в пределах одной недели. В этом случае нужно получить две записи регистра расчета, соответствуюх одной записи документа.
Запрос = Новый Запрос; Запрос.УстановитьПараметр("Ссылка", Ссылка); Запрос.Текст = "ВЫБРАТЬ | ВложенныйЗапрос.ПериодРегистрации, | ВложенныйЗапрос.Сотрудник КАК Сотрудник, | ВложенныйЗапрос.Подразделение КАК Подразделение, | ВложенныйЗапрос.ВидРасчета КАК ВидРасчета, | ВложенныйЗапрос.Размер, | ВложенныйЗапрос.ПериодДействияНачало КАК ПериодДействияНачало, | ВложенныйЗапрос.ПериодДействияКонец |ИЗ | (ВЫБРАТЬ | НачислениеЗарплатыОсновныеНачисления.Ссылка.ПериодРегистрации КАК ПериодРегистрации, | НачислениеЗарплатыОсновныеНачисления.Сотрудник КАК Сотрудник, | НачислениеЗарплатыОсновныеНачисления.Подразделение КАК Подразделение, | НачислениеЗарплатыОсновныеНачисления.ВидРасчета КАК ВидРасчета, | НачислениеЗарплатыОсновныеНачисления.Размер КАК Размер, | НачислениеЗарплатыОсновныеНачисления.ДатаНачала КАК ПериодДействияНачало, | ВЫБОР | КОГДА НАЧАЛОПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаНачала, МЕСЯЦ) <> НАЧАЛОПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, МЕСЯЦ) | ТОГДА КОНЕЦПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаНачала, МЕСЯЦ) | ИНАЧЕ КОНЕЦПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, ДЕНЬ) | КОНЕЦ КАК ПериодДействияКонец, | НачислениеЗарплатыОсновныеНачисления.НомерСтроки КАК НомерСтроки | ИЗ | Документ.НачислениеЗарплаты.ОсновныеНачисления КАК НачислениеЗарплатыОсновныеНачисления | ГДЕ | НачислениеЗарплатыОсновныеНачисления.Ссылка = &Ссылка | | ОБЪЕДИНИТЬ ВСЕ | | ВЫБРАТЬ | НачислениеЗарплатыОсновныеНачисления.Ссылка.ПериодРегистрации, | НачислениеЗарплатыОсновныеНачисления.Сотрудник, | НачислениеЗарплатыОсновныеНачисления.Подразделение, | НачислениеЗарплатыОсновныеНачисления.ВидРасчета, | НачислениеЗарплатыОсновныеНачисления.Размер, | НАЧАЛОПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, МЕСЯЦ), | КОНЕЦПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, ДЕНЬ), | НачислениеЗарплатыОсновныеНачисления.НомерСтроки | ИЗ | Документ.НачислениеЗарплаты.ОсновныеНачисления КАК НачислениеЗарплатыОсновныеНачисления | ГДЕ | НачислениеЗарплатыОсновныеНачисления.Ссылка = &Ссылка | И НАЧАЛОПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаНачала, МЕСЯЦ) <>
|НАЧАЛОПЕРИОДА(НачислениеЗарплатыОсновныеНачисления.ДатаОкончания, МЕСЯЦ)) КАК ВложенныйЗапрос | |УПОРЯДОЧИТЬ ПО | ВложенныйЗапрос.НомерСтроки, | ПериодДействияНачало"; Пока Выборка.Следующий() Цикл НоваяЗапись = Движения.ОсновныеНачисления.Добавить(); ЗаполнитьЗначенияСвойств(НоваяЗапись, Выборка); КонецЦикла;
18. "Обеспечить пользователю возможность редактировать результат расчета прямо в документе" - такое условие тоже встречается нередко. В этом случае в форме документа размещается кнопка "Расчет", которая и производит весь расчет. Вариант процедуры модуля формы ниже:
ОбъектСервер = РеквизитФормыВЗначение("Объект"); ОбъектСервер.СформироватьДвиженияДокумента(); Расчет.ПроизвестиРасчетНабора(ОбъектСервер.Движения.ОсновныеНачисления); Расчет.ПроизвестиРасчетНабора(ОбъектСервер.Движения.ДополнительныеНачисления); ОбъектСервер.ОсновныеНачисления.Загрузить(ОбъектСервер.Движения.ОсновныеНачисления.Выгрузить()); ОбъектСервер.ДополнительныеНачисления.Загрузить(ОбъектСервер.Движения.ДополнительныеНачисления.Выгрузить()); ОбъектСервер.Движения.ОсновныеНачисления.Очистить(); ОбъектСервер.Движения.ОсновныеНачисления.Записать(); ОбъектСервер.Движения.ДополнительныеНачисления.Очистить(); ОбъектСервер.Движения.ДополнительныеНачисления.Записать(); ЗначениеВРеквизитФормы(ОбъектСервер,"Объект");
Процедура СформироватьДвиженияДокумента располагается в модуле объекта документа и просто записывает содержимое табличных частей в регистр, в неизменном виде, включая колонку "Результат". Вызывается она как в модуле формы, так и при проведении документа. Процедура ПроизвестиРасчетНабора была описана выше.
19. Перерасчет только тех записей набора, которые требуют перерасчета. Такое условие есть в нескольких задачах и чтобы его обработать, необходимо процедуру расчета (в моих примерах это ПроизвестиРасчетНабора) модифицировать, чтобы она учитывала переданный второй параметр, НомерСтроки. В случае, если номер строки отличается от Неопределено, процедура должна перерасчитывать только ту запись, номер которой в нее попал. Запрос для связки записей перерасчета с записями регистра, для получения номера строки, ниже:
Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ОсновныеНачисления.НомерСтроки, | ""Основные"" КАК Регистр, | Перерасчет1.ОбъектПерерасчета |ИЗ | РегистрРасчета.ОсновныеНачисления КАК ОсновныеНачисления | ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрРасчета.ОсновныеНачисления.Перерасчет1 КАК Перерасчет1 | ПО ОсновныеНачисления.ВидРасчета = Перерасчет1.ВидРасчета | И ОсновныеНачисления.Регистратор = Перерасчет1.ОбъектПерерасчета | И ОсновныеНачисления.Сотрудник = Перерасчет1.Сотрудник | И ОсновныеНачисления.Подразделение = Перерасчет1.Подразделение | |ОБЪЕДИНИТЬ ВСЕ | |ВЫБРАТЬ | ДополнительныеНачисления.НомерСтроки, | ""Дополнительные"", | Перерасчет1.ОбъектПерерасчета |ИЗ | РегистрРасчета.ДополнительныеНачисления КАК ДополнительныеНачисления | ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрРасчета.ДополнительныеНачисления.Перерасчет1 КАК Перерасчет1 | ПО ДополнительныеНачисления.ВидРасчета = Перерасчет1.ВидРасчета | И ДополнительныеНачисления.Регистратор = Перерасчет1.ОбъектПерерасчета | И ДополнительныеНачисления.Сотрудник = Перерасчет1.Сотрудник | И ДополнительныеНачисления.Подразделение = Перерасчет1.Подразделение | |ОБЪЕДИНИТЬ ВСЕ | |ВЫБРАТЬ | Удержания.НомерСтроки, | ""Удержания"", | Перерасчет1.ОбъектПерерасчета |ИЗ | РегистрРасчета.Удержания КАК Удержания | ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрРасчета.Удержания.Перерасчет1 КАК Перерасчет1 | ПО Удержания.ВидРасчета = Перерасчет1.ВидРасчета | И Удержания.Регистратор = Перерасчет1.ОбъектПерерасчета | И Удержания.Сотрудник = Перерасчет1.Сотрудник | И Удержания.Подразделение = Перерасчет1.Подразделение"; Выборка = Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл Если Выборка.Регистр = "Основные" Тогда Набор = РегистрыРасчета.ОсновныеНачисления.СоздатьНаборЗаписей(); ИначеЕсли Выборка.Регистр = "Дополнительные" Тогда Набор = РегистрыРасчета.ДополнительныеНачисления.СоздатьНаборЗаписей(); ИначеЕсли Выборка.Регистр = "Удержания" Тогда Набор = РегистрыРасчета.Удержания.СоздатьНаборЗаписей(); КонецЕсли; Набор.Отбор.Регистратор.Установить(Выборка.ОбъектПерерасчета); Набор.Прочитать(); Набор.Записать(); Расчет.ПроизвестиРасчетНабора(Набор, Выборка.НомерСтроки); КонецЦикла;
20. Заполнение диаграммы Ганта.
// ЗАПРОС ПО ФАКТИЧЕСКОМУ ПЕРИОДУ ДЕЙСТВИЯ Диаграмма.Обновление = Ложь; Диаграмма.Очистить(); Пока Выборка.Следующий() Цикл Серия = Диаграмма.УстановитьСерию(Выборка.Сотрудник); Точка = Диаграмма.УстановитьТочку(Выборка.ВидРасчета); ТекущееЗначение = Диаграмма.ПолучитьЗначение(Точка,Серия); Интервал = ТекущееЗначение.Добавить(); Интервал.Начало = Выборка.ПериодДействияНачало; Интервал.Конец = Выборка.ПериодДействияКонец; КонецЦикла;
Диаграмма.Обновление = Истина;
20. И последнее, но тоже важное. Наиболее оптимальное по соотношению "затраченное время - результат" изменение обработки ЗаполнениеГрафика. В реальной жизни так делать было бы ошибкой, но т.к. вы на экзамене, и перезаполнять данные разных годов, сохраняя предыдущие значения, вам врядли понадобится, имейте ввиду три действия ниже:
Процедура ЗаполнитьГрафик(ДатаНачала, ДатаОкончания, ВыходныеДни, ГрафикРаботы) Экспорт Набор = РегистрыСведений.ГрафикиРаботы.СоздатьНаборЗаписей(); Набор.Отбор.ГрафикРаботы.Установить(ГрафикРаботы); // 1. ДОБАВЛЯЕМ ОТБОР ПО ГРАФИКУ //Набор.Прочитать(); // 2. КОММЕНТИРУЕМ ЧТЕНИЕ НАБОРА ЧислоСекундВСутках = 86400; Дат = ДатаНачала; Для к = 0 По Набор.Количество()-1 Цикл Запись = Набор[к]; Если Запись.Дата < ДатаНачала Тогда Продолжить; ИначеЕсли Запись.Дата =Дат Тогда Если Найти(ВыходныеДни, Строка(ДеньНедели(Дат))) Тогда Запись.Значение = 0; Иначе Запись.Значение = 8; КонецЕсли; Дат = Дат + ЧислоСекундВСутках; Иначе Пока Дат < Мин(Запись.Дата, ДатаОкончания) Цикл НоваяЗапись = Набор.Добавить(); НоваяЗапись.Дата = Дат; Если Найти(ВыходныеДни, Строка(ДеньНедели(Дат))) Тогда НоваяЗапись.Значение = 0; Иначе НоваяЗапись.Значение = 8; КонецЕсли; Дат = Дат + ЧислоСекундВСутках; КонецЦикла; Если Запись.Дата > ДатаОкончания Тогда Прервать; Иначе Если Найти(ВыходныеДни, Строка(ДеньНедели(Дат))) Тогда Запись.Значение = 0; Иначе Запись.Значение = 8; КонецЕсли; КонецЕсли; Дат = Дат + ЧислоСекундВСутках; КонецЕсли; КонецЦикла; Набор.Записать(); Пока Дат <= ДатаОкончания Цикл Запись = Набор.Добавить(); Запись.ГрафикРаботы = ГрафикРаботы; // 3. ВСТАВЛЯЕМ НУЖНЫЙ ГРАФИК Запись.Дата = Дат; Если Найти(ВыходныеДни, Строка(ДеньНедели(Дат))) Тогда Запись.Значение = 0; Иначе Запись.Значение = 8; КонецЕсли; Дат = Дат + ЧислоСекундВСутках; КонецЦикла; Набор.Записать(); КонецПроцедуры