Как реализовать учет взаиморасчетов по методу ФИФО и работу с авансами в 1С?

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С часто возникает задача автоматического распределения платежей по документам отгрузки. Метод ФИФО (First In, First Out) подразумевает, что первая поступившая оплата должна закрывать самую раннюю дебиторскую задолженность. В этой статье мы подробно разберем, как спроектировать регистр накопления, настроить типы данных и написать программный код для автоматического закрытия долгов и учета авансов.

Проектирование регистра «Расчеты с покупателями»

Для реализации задачи нам понадобится регистр накопления остатков (например, РасчетыСПокупателями). Ключевым моментом здесь является правильный выбор измерений. Рассмотрим структуру, которая позволит нам отслеживать «партии» задолженности:

  1. Контрагент (тип СправочникСсылка.Контрагенты).
  2. ДокументРасчетов — это самое важное измерение. В нем будет храниться ссылка на документ, который породил задолженность или аванс.

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

Реализация логики в документе «Расходная накладная»

Когда мы проводим документ реализации, наша задача — проверить, нет ли у этого контрагента авансов (переплат). Если авансы есть, накладная должна их «погасить». Если авансов нет или их недостаточно, остаток суммы должен записаться как приход в регистр с указанием самой этой накладной в качестве документа расчетов.

Рассмотрим пример кода в процедуре ОбработкаПроведения. Проанализируем, как получить ссылку на текущий документ и записать движение:


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

Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("Контрагент", Контрагент);

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

ОстатокСуммыПоДокументу = СуммаДокумента;

Пока Выборка.Следующий() И ОстатокСуммыПоДокументу > 0 Цикл
    // Логика погашения аванса по ФИФО
    СуммаПогашения = Мин(ОстатокСуммыПоДокументу, -Выборка.СуммаОстаток);
    
    Движение = Движения.РасчетыСПокупателями.ДобавитьПриход();
    Движение.Период = Дата;
    Движение.Контрагент = Контрагент;
    Движение.ДокументРасчетов = Выборка.ДокументРасчетов; // Погашаем конкретный аванс
    Движение.Сумма = СуммаПогашения;
    
    ОстатокСуммыПоДокументу = ОстатокСуммыПоДокументу - СуммаПогашения;
КонецЦикла;

// Если после погашения авансов осталась сумма — записываем её как новый долг
Если ОстатокСуммыПоДокументу > 0 Тогда
    Движение = Движения.РасчетыСПокупателями.ДобавитьПриход();
    Движение.Период = Дата;
    Движение.Контрагент = Контрагент;
    Движение.ДокументРасчетов = Ссылка; // Ссылка на текущую накладную
    Движение.Сумма = ОстатокСуммыПоДокументу;
КонецЕсли;

Обратите внимание на использование ЭтотОбъект.Ссылка (или просто Ссылка). Это позволяет системе «запомнить», что долг возник именно по этой накладной.

Реализация логики в документе «Оплата от покупателя»

В платежном документе логика зеркальная: мы ищем неоплаченные накладные (дебиторскую задолженность) и закрываем их в порядке возрастания даты. Выясним причину, почему важно использовать УПОРЯДОЧИТЬ ПО ДокументРасчетов.Дата в запросе: это и есть техническое воплощение принципа ФИФО.

Рассмотрим по шагам алгоритм распределения платежа:

  1. Получаем остатки по регистру с фильтром по контрагенту и только положительные остатки (долги).
  2. Сортируем их по дате документа расчетов.
  3. В цикле уменьшаем задолженность.
  4. Если деньги еще остались, регистрируем аванс, где документом расчетов будет выступать сама «Оплата от покупателя».

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

Запрос.УстановитьПараметр("Момент", МоментВремени());
Запрос.УстановитьПараметр("Контрагент", Контрагент);

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

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

// Если остались лишние деньги — это аванс
Если СуммаКРаспределению > 0 Тогда
    Движение = Движения.РасчетыСПокупателями.ДобавитьРасход(); // Уменьшаем долг (уходим в минус = аванс)
    Движение.Период = Дата;
    Движение.Контрагент = Контрагент;
    Движение.ДокументРасчетов = Ссылка; // Сами становимся "партией" аванса
    Движение.Сумма = СуммаКРаспределению;
КонецЕсли;

Профессиональные рекомендации и «подводные камни»

Рассмотрим подробнее важные аспекты, которые часто упускают начинающие разработчики:

1. Момент времени вместо Даты. Проанализируем ситуацию: если за один день проведено несколько документов, использование просто Дата в параметрах запроса может привести к непредсказуемым результатам. Всегда используйте МоментВремени() или Граница(МоментВремени(), ВидГраницы.Исключая) для получения актуальных остатков в момент проведения.

2. Блокировки данных. Выясним причину возможных ошибок в многопользовательском режиме. Если два пользователя одновременно проводят оплаты по одному контрагенту, они могут прочитать одни и те же остатки и «закрыть» одну и ту же накладную дважды. Для предотвращения этого перед запросом остатков необходимо устанавливать Управляемую блокировку по комбинации Контрагента в регистре.

3. Перепроведение «задним числом». Если пользователь изменит сумму старой накладной, вся цепочка последующих оплат по ФИФО может стать неверной. В крупных системах для этого настраивают Последовательности документов, которые сигнализируют о необходимости перепроведения документов при нарушении хронологии.

4. Использование индексации. Убедитесь, что для измерений Контрагент и ДокументРасчетов в свойствах регистра включен признак Индексировать. Это критически важно для быстродействия запросов к таблице остатков, когда база вырастет в объемах.

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

← На главную