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

Задача простыми словами: инвойсы и сверка без боли
Бухгалтерии и аудиту обычно нужны две вещи: чтобы каждый 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: один заказ может породить несколько инвойсов, и каждая строка заказа должна понимать, какая часть уже вошла в конкретный инвойс.
И то, что быстро окупается: аудит изменений. Кто изменил заказ, когда, и почему (комментарий или причина из списка). Это спасает, когда финансы находят расхождение через месяц.
Инвойс как юридический документ: что фиксировать навсегда
Инвойс для 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-инвойса
Частая причина проблем - когда счет собирается из «живых» данных, которые успели измениться после продажи. Процесс должен быть коротким, но с жесткой фиксацией ключевых полей.
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 и журнал изменений — это быстрее всего снижает ручную сверку.