7 мин

Модель данных для GST-инвойсов: HSN, заказы и админка

Модель данных для GST-инвойсов: минимальные сущности и экраны админки для HSN/SAC, ставок налогов, инвойсов и сверки, чтобы финансы не страдали.

Модель данных для GST-инвойсов: HSN, заказы и админка

Задача простыми словами: инвойсы и сверка без боли

Бухгалтерии и аудиту обычно нужны две вещи: чтобы каждый GST invoice был собран по правилам и чтобы его можно было без ручной магии сопоставить с заказом, отгрузкой, оплатой и возвратами. Если хотя бы одно звено не фиксируется, начинаются уточнения, правки и вечное «почему цифры не сходятся».

Проблемы чаще появляются не из-за сложных налоговых формул, а из-за дыр в данных заказа. В заказе забыли место поставки, не сохранили ставку налога, не зафиксировали HSN/SAC на момент продажи, смешали товары с разными ставками в одну строку, потеряли валюту или условия скидки. Пока заказ «живой», кажется, что можно исправить позже. Но когда пришло время выпускать юридический документ, «позже» превращается в ручную сверку и риск ошибок.

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

«Налоговая готовность» записи заказа - это когда в заказе уже есть все, чтобы без догадок выпустить корректный GST-инвойс и потом быстро сверить его с деньгами. Система должна отвечать на вопросы «что продали, где поставили, по какой ставке, кому, когда, и чем это подтверждено» без поиска в переписке и правок задним числом.

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

Минимальный набор сущностей: что хранить, а что вычислять

Если вы строите модель данных для GST-инвойсов, принцип простой: все, что влияет на сумму налога и юридическую валидность счета, должно быть либо надежным справочником, либо фиксироваться «снимком» в момент выставления.

Минимальный набор сущностей обычно укладывается в пять блоков:

  • Каталог позиций: товары и услуги как единая сущность (type), с HSN/SAC, единицами измерения и привязкой к налоговой категории.
  • Контрагенты и роли: покупатель, продавец, адреса, а также place of supply как отдельная запись/поле, чтобы не путать «куда выставляем» и «куда везем».
  • Заказ: источник правды о договоренности с клиентом - строки, цены, скидки, доставка, валюта, статус, версия условий.
  • Инвойс: юридический документ с итоговыми значениями, которые не пересчитываются задним числом.
  • Платежи/возвраты: отдельные сущности для сверки по движениям денег.

Что лучше хранить, а что вычислять на лету? Храните идентификаторы, даты, статусы, суммы по строкам и итогам, а также реквизиты, которые могут поменяться в справочниках (название товара, адрес, GSTIN, ставка). Вычисляйте удобные поля для UI и отчетов: «итого по заказу», «остаток к оплате», «сумма возвратов», «процент оплаты». Если вычисляемое влияет на налог, зафиксируйте это в инвойсе как итоговую цифру.

Практический пример: в каталоге обновили товар (изменили ставку GST или HSN). Заказы прошлых месяцев должны остаться как были, а инвойсы - тем более. Поэтому в заказе можно хранить ссылку на товар, но в инвойсе важно сохранить «снимок строки»: наименование, HSN/SAC, ставку, базу, налог и итог.

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

Товары, услуги и HSN/SAC: поля, без которых не обойтись

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

Сущность Product/Service (карточка позиции)

В карточке товара или услуги обычно достаточно таких полей:

  • Название (как печатается) и тип: товар или услуга
  • HSN (для товаров) или SAC (для услуг)
  • Единица измерения (UOM)
  • Ссылка на налоговую категорию (TaxCategoryId) и период действия (active_from/active_to)
  • Признаки для цены: «цена включает налог» и, при необходимости, дефолтная скидка

TaxCategory удобнее держать отдельной сущностью: ставка GST (или набор CGST/SGST/IGST), правила применения и даты действия. При смене ставки создается новая категория с новой датой, старая остается для истории.

Цена и сборы: где хранить числа

Чтобы сверка с платежами не превращалась в расследование, фиксируйте суммы на уровне строки. На строке заказа (а затем на строке инвойса) храните: базовую цену, скидку, доставку (если распределяете по строкам), прочие сборы, налогооблагаемую базу и итог.

Для налогов держите два набора данных: рассчитанные суммы по строке и агрегаты по документу (tax_total, grand_total). Даже если вы умеете пересчитать, сохраненные итоги помогают разбирать расхождения из-за округлений и частичных возвратов.

Версионность решается просто: при выпуске инвойса копируйте в InvoiceLine «снимок» критичных полей (название, тип, HSN/SAC, ставка/TaxCategory, UOM). Если позже в каталоге поменяли HSN или ставку, старые инвойсы не трогаются.

Контрагенты и место поставки: чтобы выбрать правильный налог

Чтобы правильно выбрать IGST или связку CGST+SGST, нужны несколько полей, которые однозначно отвечают на вопрос: кто продает, кто покупает и где считается место поставки.

Карточка продавца (ваша организация) должна хранить данные как юридический факт: GSTIN, юридическое название, полный адрес и штат (state code). Эти поля лучше версионировать или фиксировать снимком в момент выставления инвойса, чтобы не ломать историю при смене адреса.

У покупателя минимально нужны: тип клиента (B2B или B2C), GSTIN (если есть), bill-to адрес и штат. Даже для B2C адрес и штат важны, потому что от них зависит место поставки и тип налога.

Частая ситуация: bill-to и ship-to разные. В заказе храните оба адреса явно и сохраняйте place of supply отдельным полем (код штата), а не «вычислим потом». Так проще объяснять логику при проверках и делать сверку.

Правило выбора налога лучше закрепить на уровне заказа и затем зафиксировать в инвойсе: если штат продавца равен place of supply, применяются CGST+SGST; если разные - IGST. Не полагайтесь на «адрес доставки как место поставки» без явного флага: в реальности бывают исключения.

На уровне админки часто хватает двух разделов: «Контрагенты» и «Адреса/места поставки», плюс блок в заказе для выбора bill-to, ship-to и place of supply. Добавьте короткие проверки, чтобы не выпускать некорректные документы:

  • валидация формата GSTIN и запрет пустого GSTIN для B2B
  • обязательность штата для всех адресов
  • предупреждение, если ship-to и bill-to в разных штатах, а place of supply выбран третьим
  • запрет менять GSTIN/адрес в уже инвойсированных заказах (только через корректировку)
  • явное поле «тип клиента» вместо попыток угадать его по GSTIN

Запись заказа, готовая к налогам: структура и статусы

Чтобы структура счета-фактуры GST и сверка не превращались в ручную работу, заказ должен быть не просто «корзиной». Это учетная запись, которая фиксирует, что именно было продано, на каких условиях, и что уже можно инвойсить.

Минимальная структура обычно состоит из трех слоев. Order хранит «шапку»: номер и дату, валюту, канал продаж, ссылку на покупателя, а также bill-to/ship-to/place of supply (они влияют на выбор IGST vs CGST/SGST). OrderLine хранит строки: товар/услуга, количество, цена, скидка и HSN/SAC, зафиксированный на момент продажи.

Третий слой - TaxBreakdown. Его удобно хранить и по строкам, и итогом по документу: ставки и суммы CGST/SGST/IGST, а при необходимости cess. Да, это можно вычислять, но сохраненные итоги вместе с исходными параметрами сильно помогают в спорных случаях и при сверке платежей и инвойсов.

По статусам лучше держать короткую цепочку и не смешивать доставку с оплатой:

  • Draft
  • Confirmed
  • Fulfillable
  • Partially Fulfilled
  • Closed/Cancelled

Для частичных отгрузок нужен InvoiceLink: один заказ может породить несколько инвойсов, и каждая строка заказа должна понимать, какая часть уже вошла в конкретный инвойс.

И то, что быстро окупается: аудит изменений. Кто изменил заказ, когда, и почему (комментарий или причина из списка). Это спасает, когда финансы находят расхождение через месяц.

Инвойс как юридический документ: что фиксировать навсегда

Спроектировать процесс без переделок
Зафиксируйте статусы, блокировки и корректировки в planning mode, прежде чем писать логику.

Инвойс для GST - это не «отчет из системы», а юридический документ. После выпуска он не редактируется. Любые изменения делаются только через новые документы (например, credit note), иначе ломаются сверка, аудит и история.

На уровне заголовка (invoice header) фиксируйте минимум, который однозначно описывает документ:

  • номер и серия (или префикс) + уникальность в рамках серии
  • дата выпуска (и, если нужно, дата поставки/отгрузки отдельным полем)
  • тип: B2B/B2C/экспорт
  • валюта и курс (если экспорт), а также признак reverse charge, если применимо
  • снимок данных контрагента и места поставки на момент выпуска (название, адрес, GSTIN и т.д.)

По строкам (InvoiceLine) важно хранить не ссылку на товар «как сейчас», а то, что реально попало в документ:

  • описание позиции (как в печатной форме)
  • HSN/SAC
  • количество, единица измерения, цена, скидка (если есть)
  • taxable value (база)
  • ставки и суммы налогов (CGST/SGST/IGST/cess, если применимо)

Округление - частый источник расхождений. Заранее выберите правило (например, округление на уровне строки или только итогов) и сохраняйте его следы: сумму до округления, величину округления и финальную total invoice value. Тогда при сверке и при повторной печати цифры не «поплывут».

Печатная форма (PDF) должна быть воспроизводимой. Практичный подход: хранить версию шаблона и снимок рассчитанных итогов, а также результат генерации (идентификатор файла и контрольную сумму). Это помогает доказать, что документ не менялся.

Если после выпуска меняются цена, количество, возврат или налоговый статус, не правьте инвойс. Делайте отдельный credit note/debit note, привязанный к исходному инвойсу, с собственной нумерацией и датой. Так бухгалтерия увидит цепочку: было начислено, затем скорректировано, и почему.

Минимальные экраны админки: без лишних кнопок

Если цель - выпускать корректные GST-инвойсы и быстро сводить оплату, админка должна помогать фиксировать справочники и ловить ошибки до генерации счета. Лучше меньше экранов, но с четкими проверками и историей изменений.

Минимальный набор обычно укладывается в пять разделов:

  • Товары и услуги: HSN/SAC, единицы измерения, признак taxable/zero-rated/exempt, поиск и журнал изменений.
  • Контрагенты: тип B2B/B2C, GSTIN (если есть), адреса и штат, дефолтный place of supply.
  • Налоги и правила: ставки с датами действия, правила выбора (по категории и inter/intra-state), причины освобождения.
  • Заказы: фильтры по статусам и отдельный фильтр «налоговые проблемы», индикатор «готовность к инвойсу».
  • Инвойсы: создание из заказа, повторная печать/экспорт, корректировки через credit/debit note, заметки и поле «причина» для ручных действий.

Чтобы сверка не ломалась, добавьте две вещи: на заказе - блок «платежи и ссылки» (payment reference, сумма, дата, статус сверки), а на инвойсе - неизменяемый снимок ключевых данных (ставки, адреса, HSN/SAC, суммы).

Процесс: от заказа до выпущенного GST-инвойса

Прототип с локальными данными
Держите данные в России: TakProsto работает на российских серверах и не отправляет данные за рубеж.

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

1) Валидация заказа

Перед расчетами убедитесь, что заказ можно инвойсить. Минимальный набор:

  • заполнены и валидны штаты продавца и места поставки
  • для B2B указан GSTIN (по вашим правилам)
  • каждая строка имеет HSN (товары) или SAC (услуги), единицу измерения, количество и цену
  • для позиции известна ставка GST или признак освобождения
  • правила валюты и округления едины

2) Расчет налогов и проверка IGST vs CGST/SGST

Определите тип поставки по штатам: если штаты продавца и места поставки разные - обычно применяется IGST, если одинаковые - CGST+SGST. Дальше рассчитайте налоги по каждой строке и итогам. Сохраняйте не только суммы, но и базу, ставку и результат округления.

3) Присвоение номера и фиксация снимка

Перед выпуском назначьте номер и серию (по финансовому году/типу документа) и зафиксируйте снимок данных: продавец, покупатель, адреса, GSTIN, HSN/SAC, ставки, суммы, условия. После этого счет не должен зависеть от справочников.

4) Генерация шаблона и сохранение результата

Соберите PDF/HTML по шаблону, сохраните файл и контрольную сумму (хеш) в хранилище, а в базе - идентификатор результата, версию шаблона и метаданные генерации.

5) Блокировка и корректировки

После статуса Issued заблокируйте критичные поля (адреса, ставки, HSN/SAC, суммы, номер). Исправления - только через credit/debit note или через «Cancel + Reissue» с полной аудиторией.

Частые ошибки, из-за которых ломается соответствие и сверка

Самая частая причина проблем в GST - не «неправильная ставка», а то, что данные меняются задним числом. Финансы пытаются свести платежи, отгрузки и налоги, а система каждый раз показывает другие цифры.

Типовые ошибки:

  • Поменяли HSN/SAC в карточке товара и ждут, что старые документы «подтянутся». Инвойс должен отражать состояние на момент выставления, поэтому код и описание нужно копировать в строку инвойса и больше не трогать.
  • Хранят только общий налог по инвойсу. Для GST нужна детализация по строкам, ставкам и типу налога, иначе возвраты и частичные отгрузки превращаются в ручной Excel.
  • Путают bill-to и ship-to или не сохраняют place of supply. В итоге выбирается не тот тип налога.
  • Разрешают редактировать выпущенный инвойс. Правильный путь - credit note/debit note с понятной связью с исходным документом.
  • Не фиксируют округление. Мелочь на экране, но в сверке расхождения в 0.01-0.05 быстро накапливаются.

Быстрая проверка, что модель данных для GST-инвойсов не подведет: HSN/SAC и название фиксируются в строке инвойса при выпуске; налоги хранятся по строкам (ставка, база, сумма, тип налога); bill-to, ship-to и place of supply разделены; выпущенный инвойс неизменяем; округление записывается как факт.

Чек-лист перед запуском в прод

Перед релизом важнее всего не «красота инвойса», а предсказуемость данных. Прогоните несколько реальных сценариев: скидка, доставка, частичная отгрузка, возврат. Хороший знак - когда бухгалтерия может восстановить любую цифру без вопросов к разработчикам.

Проверьте:

  • У каждой позиции заполнены HSN или SAC, единица измерения и налоговая категория.
  • В каждом заказе зафиксированы штат продавца и штат поставки (именно в заказе, а не только в профиле компании).
  • По каждой строке сохранены не только проценты, но и рассчитанные суммы: база, CGST/SGST или IGST, итог.
  • Инвойс имеет уникальный номер и дату, которые нельзя отредактировать после выпуска, и хранит снимок ключевых данных.
  • Платежи, возвраты и корректировки связаны с конкретными инвойсами, видно распределение и остаток.

Реалистичный пример: продажа, частичная отгрузка и возврат

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

Интернет-магазин в Махараштре продает товар покупателю в Карнатаке. Доставка в другой штат значит, что налог обычно считается как IGST (а не CGST+SGST). На таких кейсах быстро видно, держит ли удар ваша модель данных для GST-инвойсов.

Заказ на 3 позиции: два одинаковых товара (HSN 8517, 18%) и одна услуга установки (SAC 9987, 18%). Покупатель оплатил всю сумму сразу. На складе в наличии только один товар, поэтому отгружают частично.

Первая отгрузка создает Инвойс №1 только на то, что реально отправили: 1 товар и, по вашей политике, установку сейчас или позже. Инвойс №1 фиксирует налоговые поля навсегда: место поставки, тип налога (IGST), ставки, суммы по строкам, валюту, округления, серию и номер.

Через неделю отправляют второй товар и выпускают Инвойс №2 на оставшуюся часть. Оба инвойса ссылаются на один заказ, но живут как отдельные юридические документы. В сверке один платеж от покупателя распределяется по двум инвойсам, а дебиторка становится нулевой, если оплата покрыла оба.

Потом покупатель возвращает второй товар. Это не «минус строка в инвойсе», а отдельный документ: credit note, который ссылается на Инвойс №2 и содержит те же налоговые атрибуты по возвращаемой строке. В сверке появятся возврат денег (или зачет), а налоговые итоги скорректируются credit note.

Финансам обычно нужны простые отчеты: продажи и налоги по периодам (invoice и credit note отдельно), дебиторка по инвойсам с учетом оплат, сверка платежей (payment -> allocation -> invoice/credit note), возвраты (причины, суммы, налоги, статус возврата средств).

Поля, которые часто забывают (и это выясняется именно на таком сценарии): place of supply и state code; связки order_id -> invoice_id и invoice_id -> credit_note_id; разбиение налогов по строкам; единицы измерения и taxable value; статусы shipped/returned/refunded и даты (для периода учета).

Следующие шаги: как быстро собрать рабочий модуль и не переделывать

Начните не с шаблонов счета, а с того, что сложнее всего менять: минимальная модель данных и правила, когда запись можно править, а когда уже нельзя. Если статус «инвойс выпущен» или «отгружено» позволяет редактировать ключевые поля, расхождения при сверке почти неизбежны.

Дальше нарисуйте простую схему экранов админки и покажите ее финансам до разработки. Часто достаточно 5-6 экранов: справочник товаров и услуг с HSN/SAC, контрагенты и адреса, заказы, отгрузки/возвраты, инвойсы, платежи и сверка. Цель не «все функции», а чтобы по любой цифре в инвойсе можно было быстро найти источник.

Рабочий план короткий:

  • Зафиксируйте статусы и переходы: черновик заказа -> подтвержден -> отгружено/оказано -> инвойс выпущен -> оплачен -> закрыт, плюс ветки для возвратов и кредит-нот.
  • Включите проверки на вводе: HSN/SAC обязателен, ставка налога не пустая, место поставки заполнено, суммы по строкам сходятся с итогами.
  • Добавьте журнал изменений: кто и когда менял адрес, налоговый статус, цены, скидки, ставки, HSN/SAC.
  • Определите «неизменяемые поля» после выпуска инвойса (состав строк, ставки, адреса), а корректировки делайте отдельными документами.
  • Прогоните 20-30 реальных заказов и сверьте итоги: инвойсы, платежи, задолженность, налоги к уплате.

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

Если нужно быстро собрать прототип админки, базы, ролей и статусов, такой модуль удобно набросать на TakProsto (takprosto.ai) и проверить логику на реальных заказах до того, как вы начнете «полировать» интерфейс и печатные формы.

FAQ

Что значит «заказ готов к налогам» (tax-ready) для GST-инвойса?

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

Минимум: place of supply (код штата), bill-to/ship-to, HSN/SAC по каждой строке, ставка/категория налога с версией, валюта/курс (если нужно), правило округления и рассчитанные суммы по строкам и итогам.

Какие поля обязательно «снимать» в инвойс, а не тянуть из справочников?

Фиксируйте все, что может поменяться в справочниках и при этом влияет на юридическую валидность:

  • реквизиты продавца/покупателя (название, адрес, GSTIN, штат)
  • place of supply и выбранный тип налога (IGST или CGST+SGST)
  • по строкам: описание, HSN/SAC, UOM, ставка, taxable value, суммы налогов
  • итоги документа: tax_total, grand_total, округление

Ссылки на справочники можно хранить, но печать и сверка должны опираться на «снимок», а не на «как сейчас в каталоге».

Что делать, если в каталоге поменяли HSN/SAC или ставку GST после продаж?

Не меняйте старые заказы и инвойсы. Правильная схема такая:

  • в каталоге меняется HSN/SAC/ставка → создается новая версия (или новая налоговая категория с датой действия)
  • в заказе/инвойсе хранится зафиксированный код и ставка на момент продажи/выпуска

Так вы избегаете ситуации, когда «вчерашний» инвойс внезапно показывает другой HSN или налог из‑за обновления справочника.

Как правильно выбирать IGST vs CGST/SGST, чтобы потом не спорить на сверке?

Базовое правило: сравнивайте штат продавца и place of supply.

  • одинаковые → обычно CGST + SGST
  • разные → обычно IGST

Важно: не пытайтесь всегда вычислять place of supply «по адресу доставки». Храните его отдельным полем в заказе и фиксируйте в инвойсе, чтобы логику можно было объяснить и повторить при проверках.

Нужно ли хранить налоги по строкам или достаточно итоговой суммы в инвойсе?

Лучше хранить оба уровня:

  • по строкам: база, ставка, суммы CGST/SGST/IGST (и cess при необходимости)
  • по документу: агрегаты tax_total и grand_total

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

Как избежать расхождений из‑за округления (0.01–0.05)?

Выберите одно правило и применяйте его всегда, затем сохраняйте результат как факт.

Практичный минимум:

  • где округляем: на строке или только в итогах
  • сумма до округления
  • величина округления
  • финальная total invoice value

Так при повторной печати и при сверке платежей цифры не будут «плавать» из‑за другого порядка вычислений.

Как поддержать частичную отгрузку, если один заказ превращается в два инвойса?

Делайте несколько инвойсов, а не один «резиновый» документ:

  • один заказ может породить несколько инвойсов
  • храните связь order → invoices и учет «сколько по каждой строке уже инвойсировано»

Это делает прозрачными сценарии частичной отгрузки: каждый инвойс отражает только то, что реально поставили/оказали, и потом корректно сводится с оплатой и возвратами.

Как правильно связывать платежи с инвойсами для чистой сверки?

Не храните «оплачено по заказу одной цифрой». Делайте отдельные сущности и связи:

  • Payment (платеж)
  • Allocation (распределение) → на какой инвойс/кредит‑ноту и в какой сумме

Тогда один платеж можно разнести на несколько инвойсов, а отчеты становятся простыми: остаток долга считается по инвойсам с учетом распределений, а не «на глаз по заказу».

Как корректировать возвраты и изменения цены без правки выпущенного инвойса?

Выпущенный инвойс не редактируют. Изменения оформляют отдельными документами:

  • credit note — уменьшение (возврат товара, скидка задним числом)
  • debit note — увеличение (доплата, корректировка вверх)

Кредит/дебет‑нота должна ссылаться на исходный инвойс и содержать те же критичные налоговые атрибуты по корректируемым строкам (HSN/SAC, ставка, база, суммы налогов).

Какие минимальные экраны админки нужны, чтобы выпускать GST-инвойсы без ручной магии?

Обычно хватает 5–6 разделов, чтобы ловить ошибки до выпуска документа:

  • товары/услуги (HSN/SAC, UOM, налоговая категория, журнал изменений)
  • контрагенты (B2B/B2C, GSTIN, адреса, штат)
  • налоги и версии ставок (даты действия)
  • заказы (статусы, «готовность к инвойсу», налоговые предупреждения)
  • инвойсы (создание из заказа, повторная генерация, блокировки, корректировки)
  • платежи и сверка (распределения, статусы)

Если делаете прототип в TakProsto, полезно сразу заложить неизменяемые поля после статуса Issued и журнал изменений — это быстрее всего снижает ручную сверку.

Похожие статьи