Оперативный учет - Задача 1.1

Компания занимается оптовой торговлей. Поступление товаров отражается документом «Приходная накладная», продажа – «Расходная накладная». Помимо, продажи товара, могут оказываться дополнительные услуги, например по доставке. И услуги и товары указываются в одной табличной части.

Складной учет не ведется.

При проведении расходной накладной при нехватке товара необходимо выдавать соответствующее предупреждение с указанием количества нехватки и не позволять проводить документ.

Списание себестоимости должно быть организованно по партиям, в Зависимости от значения принятого на этот год в учетной политике Метода списания себестоимости (FIFO или LIFO). Еще раз подчеркивается – Учетная политика действует год. На следующий год метод списания может смениться.

Необходимо построить отчет по продажам товаров за период и остаткам товаров на указанную дату. Ориентировочный вид обоих отчетов приведен ниже:

Отчет «Продажи»
Отчет «Продажи»
Отчет «Остатки товара»
Отчет «Остатки товаров»

Решение …

Шаг № 1 Переводим режим управления блокировкой данных каркасной конфигурации в значение Управляемый. Находим в дереве конфигурации элемент Общие | Подсистемы, создаем подчиненную подсистему ОперативныйУчет с одноименной картинкой, а затем две вложенные в нее подсистемы Документы и Справочники. Для последних задавать картинки не будем. Включим справочники Номенклатура и Контрагенты в подсистему Справочники, а документы ПриходнаяНакладная и РасходнаяНакладная в подсистему Документы.

Шаг № 2 В справочнике Номенклатура создаем новый реквизит Услуга типа Булево. Конечно, в каркасной конфигурации есть подходящее перечисление ВидыНоменклатуры, но использовать его менее удобно. Например, придется потратить драгоценные минуты для того, чтобы в режиме 1С:Предприятия пробежаться по списку элементов справочника, и каждому проставить вид ВидыНоменклатуры.Товар. Для типа Булево всем автоматом проставится значение Услуга = Ложь.

Для хранения текущего значения принятого на этот год в учетной политике метода списания себестоимости создадим регистр сведений УчетнаяПолитика. Периодичность - В пределах года, т.к. в задаче не сказано, что изменение учетной политики должно фиксироваться соответствующим документом, то оставляем Режим записи регистра без изменения (Независимый). Добавим регистр в подсистему ОперативныйУчет, а на закладке «Данные» создадим ресурс МетодСписания типа ПеречислениеСсылка.УчетнаяПолитика.

Сохраним изменения в конфигурации, проигнорировав список предупреждений вида «Объект не включен ни в одну подсистему, отображаемую в командном интерфейсе». В режиме 1С:Предприятия добавим в справочник Номенклатура новую группу Услуги, а в нее элемент - Доставка. Для элемента установим реквизит Услуга в значение Истина. Далее, перейдем по ссылке Учетная политика, создадим в списке новый элемент, в котором укажем метод списания себестоимости Фифо.

Шаг № 3 Вернемся в режим Конфигуратора. Посмотрим внимательно на форму первого отчета и зададим себе вопрос о том, откуда будут браться данные для его формирования. Т.к. продажи - это по своей сути оборотная информация, то решим, что данные будут браться из соответствующего оборотного регистра накопления (назовем его Продажи), причем форма отчета диктует нам структуру этого регистра.

Структура регистра «Продажи»
Структура регистра «Продажи»

Шаг № 4 Аналогично, посмотрим внимательно на форму второго отчета и зададим себе вопрос о том, откуда будут браться данные для его формирования. Решим, что данные будут браться из соответствующего регистра накопления вида Остатки (назовем его ОстаткиНоменклатуры). В каркасной конфигурации уже присутствует регистр с таким именем, поэтому внесем требуемые изменения прямо в него.

Структура регистра «ОстаткиНоменклатуры»
Структура регистра «ОстаткиНоменклатуры»

В регистре ОстаткиНоменклатуры измерение Партия будет иметь тип ДокументСсылка.ПриходнаяНакладная. Другими словами, мы собираемся хранить остатки номенклатуры в разрезе конкретных документов ПриходнаяНакладная, по которым эта номенклатура к нам поступала. Например, на основе данных второго отчета можно сделать вывод, что приход номенклатуры «Big (капилярная)» происходил по документам «Приходная накладная 000000001» и «Приходная накладная 000000002», по каждому из них мы собираемся хранить текущий остаток номенклатуры и стоимость. Это и есть партионный учет, требуемый в задаче!

Шаг № 5 Перейдем к реализации логики проведения документов ПриходнаяНакладная и РасходнаяНакладная. Начнем с первого, для этого откроем модуль объекта документа ПриходнаяНакладная и добавим в него следующий код:

Процедура ОбработкаПроведения(Отказ, Режим)

    Движения.ОстаткиНоменклатуры.Записывать = Истина;

    // Получаем данные по Товарам документа и одновременно избавляемся от дублей строк
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |   ПриходнаяНакладнаяСписокНоменклатуры.Номенклатура,
        |   СУММА(ПриходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество,
        |   СУММА(ПриходнаяНакладнаяСписокНоменклатуры.Сумма) КАК Сумма
        |ИЗ
        |   Документ.ПриходнаяНакладная.СписокНоменклатуры КАК ПриходнаяНакладнаяСписокНоменклатуры
        |ГДЕ
        |   ПриходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
        |   И (НЕ ПриходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга)
        |
        |СГРУППИРОВАТЬ ПО
        |   ПриходнаяНакладнаяСписокНоменклатуры.Номенклатура";

    Запрос.УстановитьПараметр("Ссылка", Ссылка);

    Результат = Запрос.Выполнить();

    ВыборкаДетальныеЗаписи = Результат.Выбрать();

    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл

        // регистр ОстаткиНоменклатуры Приход
        Движение = Движения.ОстаткиНоменклатуры.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
        Движение.Период = Дата;
        Движение.Партия = Ссылка;
        Движение.Номенклатура = ВыборкаДетальныеЗаписи.Номенклатура;
        Движение.Количество = ВыборкаДетальныеЗаписи.Количество;
        Движение.Стоимость = ВыборкаДетальныеЗаписи.Сумма;
    КонецЦикла;

КонецПроцедуры

Можно заметить, что логика проведения довольно прямолинейна, поэтому не требует дополнительных пояснений. Проведение документа РасходнаяНакладная немногим сложнее, но прежде чем переходить к нему, разберемся, как именно оно должно выглядеть, согласно требованиям задачи.

РасходнаяНакладная должна уменьшать остатки номенклатуры в регистре ОстаткиНоменклатуры, а также создавать движения в регистре Продажи. Причем, остатки номенклатуры у нас хранятся в разрезе партий (документов ПриходнаяНакладная), поэтому перед списанием номенклатуры необходимо будет получить из регистра список партий, доступных для списания. В соответствии с установленным значением на этот год в учетной политике методом списания себестоимости, будем сортировать этот список партий или по возрастанию дат документов (ФИФО - первый пришел, первый ушел), или по убыванию дат документов (ЛИФО - последний пришел, первый ушел).

Кроме того, при проведении документа РасходнаяНакладная требуется отслеживать нехватку товара. Для того чтобы понять хватает ли товара для проведения документа, неэффективно бегать по его списку партий. Лучшим решением будет озаботиться тем, чтобы у нас всегда были бы под рукой данные об общем количестве доступных единиц товара.

Наконец, для исключения коллизий, связанных с параллельной работой других пользователей, реализуем блокировку необходимых данных. Дело в том, что возможна такая ситуация, когда разные транзакции одновременно читают доступное количество некоего товара, а потом каждая транзакция списывает его. В результате чего контроль остатков будет работать неверно, а наша обязанность этого избежать. Поэтому, прежде чем читать что-то, что мы собираемся изменять, будем запрещать чтение этих данных другими транзакциями до тех пор, пока мы не закончим свои изменения (или пока не откажемся от проведения документа).

Процедура ОбработкаПроведения(Отказ, Режим)

    // Чтение метода списания себестоимости
    МетодСписания = РегистрыСведений.УчетнаяПолитика.ПолучитьПоследнее(Дата).МетодСписания;

    Если МетодСписания.Пустая() Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Учетная политика не определена!";
        Сообщение.Сообщить();

        Отказ = Истина;
        Возврат;
    КонецЕсли;

    СортировкаПартий = ?(МетодСписания = Перечисления.УчетнаяПолитика.ФИФО, "", " УБЫВ");
    
    Движения.Продажи.Записывать = Истина;
    Движения.ОстаткиНоменклатуры.Записывать = Истина;

    // Удаление предыдущих движений документа
    Движения.ОстаткиНоменклатуры.Записать();

    // Блокировка данных
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");

    Блокировка.Заблокировать();

    // Чтение партий, доступных для списания. Сортировка согласно метода списания себестоимости
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |   РасходнаяНакладнаяСписокНоменклатуры.Номенклатура,
        |   РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга КАК Услуга,
        |   СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество,
        |   СУММА(РасходнаяНакладнаяСписокНоменклатуры.Сумма) КАК Сумма
        |ПОМЕСТИТЬ РасходнаяНакладная
        |ИЗ
        |   Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
        |ГДЕ
        |   РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
        |
        |СГРУППИРОВАТЬ ПО
        |   РасходнаяНакладнаяСписокНоменклатуры.Номенклатура,
        |   РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        |   РасходнаяНакладная.Номенклатура КАК Номенклатура,
        |   РасходнаяНакладная.Услуга КАК Услуга,
        |   РасходнаяНакладная.Количество КАК Количество,
        |   РасходнаяНакладная.Сумма,
        |   ОстаткиНоменклатурыОстатки.Партия КАК Партия,
        |   ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
        |   ЕСТЬNULL(ОстаткиНоменклатурыОстатки.СтоимостьОстаток, 0) КАК СтоимостьОстаток
        |ИЗ
        |   РасходнаяНакладная КАК РасходнаяНакладная
        |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
        |               &МоментВремени,
        |               Номенклатура В
        |                   (ВЫБРАТЬ
        |                       РасходнаяНакладная.Номенклатура
        |                   ИЗ
        |                       РасходнаяНакладная)) КАК ОстаткиНоменклатурыОстатки
        |       ПО РасходнаяНакладная.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура
        |
        |УПОРЯДОЧИТЬ ПО
        |   ОстаткиНоменклатурыОстатки.Партия.МоментВремени" + СортировкаПартий + "
        |ИТОГИ
        |   МАКСИМУМ(Услуга),
        |   МАКСИМУМ(Количество),
        |   МАКСИМУМ(Сумма),
        |   СУММА(КоличествоОстаток)
        |ПО
        |   Номенклатура";

    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

    Результат = Запрос.Выполнить();
    ВыборкаНоменклатура = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

    Пока ВыборкаНоменклатура.Следующий() Цикл

        // Контроль остатков
        Если (НЕ ВыборкаНоменклатура.Услуга) 
              И (ВыборкаНоменклатура.Количество > ВыборкаНоменклатура.КоличествоОстаток) Тогда
              
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = "Не хватает "+ (ВыборкаНоменклатура.Количество-ВыборкаНоменклатура.КоличествоОстаток) +
                              " позиций номенклатуры " + ВыборкаНоменклатура.Номенклатура + ".";
            Сообщение.Сообщить();

            Отказ = Истина;
        КонецЕсли;

        Если Отказ Тогда
            Продолжить;
        КонецЕсли;

        // Создание движений
        ТекСебестоимость = 0;

        Если не ВыборкаНоменклатура.Услуга Тогда

            ОсталосьСписать = ВыборкаНоменклатура.Количество;
            ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();

            Пока (ОсталосьСписать > 0) И ВыборкаДетальныеЗаписи.Следующий() Цикл
                Списываем = Мин(ОсталосьСписать,  ВыборкаДетальныеЗаписи.КоличествоОстаток);

                // регистр ОстаткиНоменклатуры Расход
                Движение = Движения.ОстаткиНоменклатуры.Добавить();
                Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
                Движение.Период = Дата;
                Движение.Номенклатура = ВыборкаДетальныеЗаписи.Номенклатура;
                Движение.Партия = ВыборкаДетальныеЗаписи.Партия;
                Движение.Количество = Списываем;
                Движение.Стоимость = (Списываем * ВыборкаДетальныеЗаписи.СтоимостьОстаток) /
                                                            ВыборкаДетальныеЗаписи.КоличествоОстаток;

                ТекСебестоимость = ТекСебестоимость + Движение.Стоимость;
                ОсталосьСписать = ОсталосьСписать - Списываем;
            КонецЦикла;
        КонецЕсли;

        // регистр Продажи
        Движение = Движения.Продажи.Добавить();
        Движение.Период = Дата;
        Движение.Номенклатура = ВыборкаНоменклатура.Номенклатура;
        Движение.Количество = ВыборкаНоменклатура.Количество;
        Движение.Себестоимость = ТекСебестоимость;
        Движение.Выручка = ВыборкаНоменклатура.Сумма;
    КонецЦикла;

КонецПроцедуры

Шаг № 6 Займемся отчетами… Создадим отчет Продажи, добавим его в подсистему Оперативный учет, а затем на вкладке «Основные» нажмем кнопку «Открыть схему компоновки данных». В схему добавим набор данных вида «Запрос». Текст запроса легко можно создать визуальными средствами, но мы просто укажем его следующим образом:

ВЫБРАТЬ
	ПродажиОбороты.Номенклатура,
	ПродажиОбороты.КоличествоОборот КАК Количество,
	ПродажиОбороты.СебестоимостьОборот КАК Себестоимость,
	ПродажиОбороты.ВыручкаОборот КАК Продажа,
	ПродажиОбороты.ВыручкаОборот - ПродажиОбороты.СебестоимостьОборот КАК Прибыль
ИЗ
	РегистрНакопления.Продажи.Обороты КАК ПродажиОбороты

Перейдем на закладку «Параметры». Установим параметры отчета согласно рисунка.

Параметры отчета «Продажи»
Параметры отчета «Продажи»

Перейдем на закладку «Настройки». Добавим в структуру нашего отчета новую группировку (клавиша Ins), но поле группировки указывать не будем (клавиша Enter). На вкладке «Выбранные поля» укажем список полей отчета, согласно требуемой формы.

Выбранные поля отчета «Продажи»
Выбранные поля отчета «Продажи»

Рядом с вкладкой «Выбранные поля» расположена вкладка «Параметры». Перейдем на нее и выполним настройку параметра «Период» отчета.

Настройка параметра «Период» отчета
Настройка параметра «Период» отчета

Последний штрих - переходим на вкладку «Другие настройки». Здесь зададим заголовок отчета.

Установка заголовка отчета «Продажи»
Установка заголовка отчета «Продажи»

Шаг № 7 Создадим отчет ОстаткиТоваров, добавим его в подсистему Оперативный учет, а затем на вкладке «Основные» нажмем кнопку «Открыть схему компоновки данных». В схему добавим набор данных вида «Запрос». Укажем следующий текст запроса:

ВЫБРАТЬ
	ОстаткиНоменклатурыОстатки.Номенклатура,
	ОстаткиНоменклатурыОстатки.Партия,
	ОстаткиНоменклатурыОстатки.КоличествоОстаток КАК Количество,
	ОстаткиНоменклатурыОстатки.СтоимостьОстаток КАК Стоимость
ИЗ
	РегистрНакопления.ОстаткиНоменклатуры.Остатки КАК ОстаткиНоменклатурыОстатки

Перейдем на закладку «Ресурсы». Создадим ресурсы согласно рисунка, они нам необходимы потому, что в данном отчете присутствует группировка.

Ресурсы отчета «ОстаткиНоменклатуры»
Ресурсы отчета «ОстаткиНоменклатуры»

Перейдем на закладку «Параметры». Установим заголовок и состав даты параметра «Период».

Параметр «Период» отчета «ОстаткиНоменклатуры»
Параметр «Период» отчета «ОстаткиНоменклатуры»

Перейдем на закладку «Настройки». Добавим в структуру нашего отчета новую группировку (клавиша Ins), поле группировки - Номенклатура. Затем создадим вложенную в нее группировку, но для нее группировочное поле указывать не будем. На вкладке «Выбранные поля» укажем список полей отчета.

Выбранные поля отчета «ОстаткиНоменклатуры»
Выбранные поля отчета «ОстаткиНоменклатуры»

Рядом с вкладкой «Выбранные поля» расположена вкладка «Параметры». Перейдем на нее и выполним настройку параметра «Дата» отчета.

Настройка параметра «Дата» отчета
Настройка параметра «Дата» отчета

И последнее - переходим на вкладку «Другие настройки». Здесь зададим заголовок отчета, а также укажем прочие параметры, необходимые для формирования отчета в требуемом виде.

Установка параметров формирования отчета «ОстаткиНоменклатуры»
Установка параметров формирования отчета «ОстаткиНоменклатуры»
1Gb.ru counter Рейтинг сайтов Бийска