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

Бухгалтерии и аудиту обычно нужны две вещи: чтобы каждый GST invoice был собран по правилам и чтобы его можно было без ручной магии сопоставить с заказом, отгрузкой, оплатой и возвратами. Если хотя бы одно звено не фиксируется, начинаются уточнения, правки и вечное «почему цифры не сходятся».
Проблемы чаще появляются не из-за сложных налоговых формул, а из-за дыр в данных заказа. В заказе забыли место поставки, не сохранили ставку налога, не зафиксировали HSN/SAC на момент продажи, смешали товары с разными ставками в одну строку, потеряли валюту или условия скидки. Пока заказ «живой», кажется, что можно исправить позже. Но когда пришло время выпускать юридический документ, «позже» превращается в ручную сверку и риск ошибок.
Полезно разделять данные «для печати инвойса» и данные «для учета». Для печати нужны реквизиты, номера, даты, суммы и строки. Для учета нужны опорные поля, которые редко видны на PDF: версии ставок, признаки налогообложения, связи с оплатами, события (отгрузка, возврат), а также неизменяемые снимки ключевых атрибутов.
«Налоговая готовность» записи заказа - это когда в заказе уже есть все, чтобы без догадок выпустить корректный GST-инвойс и потом быстро сверить его с деньгами. Система должна отвечать на вопросы «что продали, где поставили, по какой ставке, кому, когда, и чем это подтверждено» без поиска в переписке и правок задним числом.
Хорошее правило: если поле влияет на налог или на итоговую сумму, его нужно сохранять в момент подтверждения заказа, а не пересчитывать через месяц по обновившимся справочникам.
Если вы строите модель данных для GST-инвойсов, принцип простой: все, что влияет на сумму налога и юридическую валидность счета, должно быть либо надежным справочником, либо фиксироваться «снимком» в момент выставления.
Минимальный набор сущностей обычно укладывается в пять блоков:
Что лучше хранить, а что вычислять на лету? Храните идентификаторы, даты, статусы, суммы по строкам и итогам, а также реквизиты, которые могут поменяться в справочниках (название товара, адрес, GSTIN, ставка). Вычисляйте удобные поля для UI и отчетов: «итого по заказу», «остаток к оплате», «сумма возвратов», «процент оплаты». Если вычисляемое влияет на налог, зафиксируйте это в инвойсе как итоговую цифру.
Практический пример: в каталоге обновили товар (изменили ставку GST или HSN). Заказы прошлых месяцев должны остаться как были, а инвойсы - тем более. Поэтому в заказе можно хранить ссылку на товар, но в инвойсе важно сохранить «снимок строки»: наименование, HSN/SAC, ставку, базу, налог и итог.
Платежи и возвраты не стоит «впихивать» в заказ одной суммой. Один заказ может быть оплачен частями, а возврат может прийти отдельным платежом на другой день. Раздельные сущности дают чистую сверку: инвойсы отвечают за начисления, платежи - за деньги.
Каталог может меняться, но инвойс обязан оставаться неизменным. Поэтому в справочниках храним «как сейчас», а в строках заказа и инвойса фиксируем «как было на момент продажи».
В карточке товара или услуги обычно достаточно таких полей:
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. Добавьте короткие проверки, чтобы не выпускать некорректные документы:
Чтобы структура счета-фактуры GST и сверка не превращались в ручную работу, заказ должен быть не просто «корзиной». Это учетная запись, которая фиксирует, что именно было продано, на каких условиях, и что уже можно инвойсить.
Минимальная структура обычно состоит из трех слоев. Order хранит «шапку»: номер и дату, валюту, канал продаж, ссылку на покупателя, а также bill-to/ship-to/place of supply (они влияют на выбор IGST vs CGST/SGST). OrderLine хранит строки: товар/услуга, количество, цена, скидка и HSN/SAC, зафиксированный на момент продажи.
Третий слой - TaxBreakdown. Его удобно хранить и по строкам, и итогом по документу: ставки и суммы CGST/SGST/IGST, а при необходимости cess. Да, это можно вычислять, но сохраненные итоги вместе с исходными параметрами сильно помогают в спорных случаях и при сверке платежей и инвойсов.
По статусам лучше держать короткую цепочку и не смешивать доставку с оплатой:
Для частичных отгрузок нужен InvoiceLink: один заказ может породить несколько инвойсов, и каждая строка заказа должна понимать, какая часть уже вошла в конкретный инвойс.
И то, что быстро окупается: аудит изменений. Кто изменил заказ, когда, и почему (комментарий или причина из списка). Это спасает, когда финансы находят расхождение через месяц.
Инвойс для GST - это не «отчет из системы», а юридический документ. После выпуска он не редактируется. Любые изменения делаются только через новые документы (например, credit note), иначе ломаются сверка, аудит и история.
На уровне заголовка (invoice header) фиксируйте минимум, который однозначно описывает документ:
По строкам (InvoiceLine) важно хранить не ссылку на товар «как сейчас», а то, что реально попало в документ:
Округление - частый источник расхождений. Заранее выберите правило (например, округление на уровне строки или только итогов) и сохраняйте его следы: сумму до округления, величину округления и финальную total invoice value. Тогда при сверке и при повторной печати цифры не «поплывут».
Печатная форма (PDF) должна быть воспроизводимой. Практичный подход: хранить версию шаблона и снимок рассчитанных итогов, а также результат генерации (идентификатор файла и контрольную сумму). Это помогает доказать, что документ не менялся.
Если после выпуска меняются цена, количество, возврат или налоговый статус, не правьте инвойс. Делайте отдельный credit note/debit note, привязанный к исходному инвойсу, с собственной нумерацией и датой. Так бухгалтерия увидит цепочку: было начислено, затем скорректировано, и почему.
Если цель - выпускать корректные GST-инвойсы и быстро сводить оплату, админка должна помогать фиксировать справочники и ловить ошибки до генерации счета. Лучше меньше экранов, но с четкими проверками и историей изменений.
Минимальный набор обычно укладывается в пять разделов:
Чтобы сверка не ломалась, добавьте две вещи: на заказе - блок «платежи и ссылки» (payment reference, сумма, дата, статус сверки), а на инвойсе - неизменяемый снимок ключевых данных (ставки, адреса, HSN/SAC, суммы).
Частая причина проблем - когда счет собирается из «живых» данных, которые успели измениться после продажи. Процесс должен быть коротким, но с жесткой фиксацией ключевых полей.
Перед расчетами убедитесь, что заказ можно инвойсить. Минимальный набор:
Определите тип поставки по штатам: если штаты продавца и места поставки разные - обычно применяется IGST, если одинаковые - CGST+SGST. Дальше рассчитайте налоги по каждой строке и итогам. Сохраняйте не только суммы, но и базу, ставку и результат округления.
Перед выпуском назначьте номер и серию (по финансовому году/типу документа) и зафиксируйте снимок данных: продавец, покупатель, адреса, GSTIN, HSN/SAC, ставки, суммы, условия. После этого счет не должен зависеть от справочников.
Соберите PDF/HTML по шаблону, сохраните файл и контрольную сумму (хеш) в хранилище, а в базе - идентификатор результата, версию шаблона и метаданные генерации.
После статуса Issued заблокируйте критичные поля (адреса, ставки, HSN/SAC, суммы, номер). Исправления - только через credit/debit note или через «Cancel + Reissue» с полной аудиторией.
Самая частая причина проблем в GST - не «неправильная ставка», а то, что данные меняются задним числом. Финансы пытаются свести платежи, отгрузки и налоги, а система каждый раз показывает другие цифры.
Типовые ошибки:
Быстрая проверка, что модель данных для GST-инвойсов не подведет: HSN/SAC и название фиксируются в строке инвойса при выпуске; налоги хранятся по строкам (ставка, база, сумма, тип налога); bill-to, ship-to и place of supply разделены; выпущенный инвойс неизменяем; округление записывается как факт.
Перед релизом важнее всего не «красота инвойса», а предсказуемость данных. Прогоните несколько реальных сценариев: скидка, доставка, частичная отгрузка, возврат. Хороший знак - когда бухгалтерия может восстановить любую цифру без вопросов к разработчикам.
Проверьте:
Интернет-магазин в Махараштре продает товар покупателю в Карнатаке. Доставка в другой штат значит, что налог обычно считается как 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, контрагенты и адреса, заказы, отгрузки/возвраты, инвойсы, платежи и сверка. Цель не «все функции», а чтобы по любой цифре в инвойсе можно было быстро найти источник.
Рабочий план короткий:
Практичный тест: возьмите один день продаж и попробуйте закрыть его «как бухгалтер» без ручных таблиц. Если хоть один шаг требует копировать данные из экрана в экран, это место лучше чинить до запуска.
Если нужно быстро собрать прототип админки, базы, ролей и статусов, такой модуль удобно набросать на TakProsto (takprosto.ai) и проверить логику на реальных заказах до того, как вы начнете «полировать» интерфейс и печатные формы.
Это заказ, в котором уже зафиксированы все данные, влияющие на налог и итоговую сумму, так что инвойс можно выпустить без догадок и ручных правок.
Минимум: place of supply (код штата), bill-to/ship-to, HSN/SAC по каждой строке, ставка/категория налога с версией, валюта/курс (если нужно), правило округления и рассчитанные суммы по строкам и итогам.
Фиксируйте все, что может поменяться в справочниках и при этом влияет на юридическую валидность:
Ссылки на справочники можно хранить, но печать и сверка должны опираться на «снимок», а не на «как сейчас в каталоге».
Не меняйте старые заказы и инвойсы. Правильная схема такая:
Так вы избегаете ситуации, когда «вчерашний» инвойс внезапно показывает другой HSN или налог из‑за обновления справочника.
Базовое правило: сравнивайте штат продавца и place of supply.
Важно: не пытайтесь всегда вычислять place of supply «по адресу доставки». Храните его отдельным полем в заказе и фиксируйте в инвойсе, чтобы логику можно было объяснить и повторить при проверках.
Лучше хранить оба уровня:
Даже если вы умеете пересчитать, сохраненные значения помогают разбирать расхождения из‑за округлений, частичных отгрузок и возвратов, а также дают понятный «след расчета» для аудита.
Выберите одно правило и применяйте его всегда, затем сохраняйте результат как факт.
Практичный минимум:
Так при повторной печати и при сверке платежей цифры не будут «плавать» из‑за другого порядка вычислений.
Делайте несколько инвойсов, а не один «резиновый» документ:
Это делает прозрачными сценарии частичной отгрузки: каждый инвойс отражает только то, что реально поставили/оказали, и потом корректно сводится с оплатой и возвратами.
Не храните «оплачено по заказу одной цифрой». Делайте отдельные сущности и связи:
Тогда один платеж можно разнести на несколько инвойсов, а отчеты становятся простыми: остаток долга считается по инвойсам с учетом распределений, а не «на глаз по заказу».
Выпущенный инвойс не редактируют. Изменения оформляют отдельными документами:
Кредит/дебет‑нота должна ссылаться на исходный инвойс и содержать те же критичные налоговые атрибуты по корректируемым строкам (HSN/SAC, ставка, база, суммы налогов).
Обычно хватает 5–6 разделов, чтобы ловить ошибки до выпуска документа:
Если делаете прототип в TakProsto, полезно сразу заложить неизменяемые поля после статуса Issued и журнал изменений — это быстрее всего снижает ручную сверку.