В практике разработки на платформе 1С:Предприятие 8 часто встречается задача организации учета взаиморасчетов, которые должны вестись не просто по контрагентам, а в разрезе конкретных сделок или проектов. При этом система должна автоматически определять, является ли платеж погашением долга или авансом, и корректно распределять средства. Рассмотрим подробно, как реализовать такую схему, опираясь на принципы, заложенные в экзаменационные задачи «1С:Специалист по платформе».
Прежде всего, проанализируем условия задачи. Нам необходимо обеспечить ведение задолженности по проектам, а авансов — в целом по контрагенту. Это означает, что в структуре данных должна быть гибкость, позволяющая хранить информацию как с привязкой к проекту, так и без нее. Выясним, какие объекты метаданных нам понадобятся.
1. Справочник Проекты. Поскольку учет ведется в разрезе проектов, нам необходим справочник. В нем обязательно должен быть реквизит ДатаОплаты (тип Дата). Именно по этому полю система будет определять очередность погашения задолженности в документе оплаты.
2. Регистр накопления Взаиморасчеты. Это ключевой объект. Мы выберем тип регистра Остатки. Почему не обороты? Потому что для принятия решения о зачете аванса или признании долга нам нужно знать актуальное состояние расчетов на момент проведения документа. Обороты не позволяют получить остаток без сканирования всей истории движений.
Структура регистра Взаиморасчеты:
Контрагент (тип СправочникСсылка.Контрагенты), Проект (тип СправочникСсылка.Проекты).Сумма (тип Число).Важный нюанс: мы будем использовать один ресурс Сумма. Если сумма положительная — это долг контрагента перед нами. Если мы записываем аванс, то будем использовать этот же регистр, но оставлять измерение Проект пустым. Это классический и наиболее эффективный прием в 1С.
Рассмотрим ситуацию отгрузки товара. При проведении Расходной накладной нам нужно проверить, нет ли у контрагента авансов. Если аванс есть, его нужно «зачесть», то есть уменьшить сумму аванса и уменьшить сумму планируемого долга по новому проекту.
Разберем алгоритм работы в модуле проведения:
Взаиморасчеты, где Контрагент = НашКонтрагент и Проект = ПустаяСсылка (это и есть наш аванс).Посмотрим на пример кода для Расходной накладной:
// Установка блокировки
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Взаиморасчеты");
ЭлементБлокировки.УстановитьПараметр("Контрагент", Контрагент);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Получение остатка аванса (пустой проект)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВзаиморасчетыОстатки.СуммаОстаток КАК Аванс
|ИЗ
| РегистрНакопления.Взаиморасчеты.Остатки(&МоментВремени, Контрагент = &Контрагент И Проект = ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка)) КАК ВзаиморасчетыОстатки";
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("Контрагент", Контрагент);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
ОстатокАванса = 0;
Если Выборка.Следующий() Тогда
ОстатокАванса = Выборка.Аванс;
КонецЕсли;
СуммаКПогашению = СуммаДокумента;
СуммаЗачета = Мин(ОстатокАванса, СуммаКПогашению);
// 1. Уменьшаем аванс (Расход по пустому проекту)
Если СуммаЗачета > 0 Тогда
Движение = Движения.Взаиморасчеты.ДобавитьРасход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Проект = Справочники.Проекты.ПустаяСсылка();
Движение.Сумма = СуммаЗачета;
СуммаКПогашению = СуммаКПогашению - СуммаЗачета;
КонецЕсли;
// 2. Оставшуюся часть пишем в долг по проекту (Приход по конкретному проекту)
Если СуммаКПогашению > 0 Тогда
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Проект = Проект; // Проект из шапки документа
Движение.Сумма = СуммаКПогашению;
КонецЕсли;
Ситуация с поступлением оплаты сложнее, так как нам нужно гасить долги по проектам в порядке их дат оплаты (метод FIFO для проектов). Проанализируем, как построить запрос в Приходе денег.
Нам нужно выбрать все остатки по контрагенту, где проект не является пустым, и обязательно соединить их со справочником Проекты, чтобы получить дату оплаты для сортировки.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВзаиморасчетыОстатки.Проект КАК Проект,
| ВзаиморасчетыОстатки.СуммаОстаток КАК Долг,
| ВзаиморасчетыОстатки.Проект.ДатаОплаты КАК ДатаОплаты
|ИЗ
| РегистрНакопления.Взаиморасчеты.Остатки(&МоментВремени, Контрагент = &Контрагент И Проект <> ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка)) КАК ВзаиморасчетыОстатки
|УПОРЯДОЧИТЬ ПО
| ДатаОплаты ВОЗР";
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Выборка = Запрос.Выполнить.Выбрать();
ОстатокПлатежа = СуммаДокумента;
Пока Выборка.Следующий() И ОстатокПлатежа > 0 Цикл
СуммаПогашения = Мин(Выборка.Долг, ОстатокПлатежа);
Движение = Движения.Взаиморасчеты.ДобавитьРасход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Проект = Выборка.Проект;
Движение.Сумма = СуммаПогашения;
ОстатокПлатежа = ОстатокПлатежа - СуммаПогашения;
КонецЦикла;
// Если деньги остались - это аванс
Если ОстатокПлатежа > 0 Тогда
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Проект = Справочники.Проекты.ПустаяСсылка();
Движение.Сумма = ОстатокПлатежа;
КонецЕсли;
Использование пустой ссылки. Мы сознательно используем ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка). В системе 1С это значение является вполне определенным и хранится в базе данных. Это позволяет нам в одном регистре и в одном ресурсе хранить разные сущности: «Долг по проекту» и «Общий аванс».
Контроль отрицательных остатков. Хотя в учебных задачах иногда говорят «учет остатков не ведется», во взаиморасчетах это недопустимо. Мы не должны позволить «Приходу денег» погасить долг в 10 000 рублей, если по регистру числится всего 5 000. Наша логика с использованием Мин(ОстатокПлатежа, Долг) в коде выше как раз обеспечивает этот контроль.
Сортировка в запросе. В документе оплаты критически важно использовать УПОРЯДОЧИТЬ ПО Проект.ДатаОплаты ВОЗР. Если мы этого не сделаем, 1С выдаст остатки в произвольном порядке (как они лежат в таблице итогов), что нарушит бизнес-логику «гасим сначала старые долги».
Отчетность. Для формирования отчета «Ведомость по взаиморасчетам» лучше использовать виртуальную таблицу ОстаткиИОбороты. Чтобы пользователю было понятно, что пустой проект — это аванс, в схеме компоновки данных (СКД) можно использовать выражение представления для поля Проект:
ВЫБОР КОГДА Проект = ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка) ТОГДА "--- АВАНС ---" ИНАЧЕ Проект КОНЕЦ
Мы рассмотрели полноценный механизм учета взаиморасчетов в разрезе проектов. Главный вывод: использование одного регистра остатков с измерением «Проект» позволяет элегантно решить задачу зачета авансов. Ключевыми точками стабильности такой системы являются управляемые блокировки для предотвращения коллизий при одновременной работе пользователей и строгий порядок сортировки по датам оплаты в запросах на получение остатков.