1. Чому проведення документа — це найскладніша частина ERP

В інтернет-магазині або CRM-системі збереження замовлення зазвичай зводиться до простого INSERT чи UPDATE одного рядка. В обліковій системі все інакше.

Один документ «Реалізація товарів» повинен одночасно:

  • Перевірити наявність вільного залишку номенклатури на складі;
  • Списати партії товару за методом FIFO (або за середньою собівартістю);
  • Збільшити дебіторську заборгованість покупця за договором;
  • Сформувати бухгалтерські проводки подвійного запису за Національними стандартами бухгалтерського обліку (НП(С)БО);
  • Зафіксувати дані для податкового обліку (ПДВ-зобов'язання).

2. Атомарність через TransactionScope

Якщо під час списання третьої позиції виявиться, що товару не вистачає на складі, жоден запис не повинен потрапити в базу. У Ruta це забезпечується патерном Unit of Work та транзакційним скоупом СУБД:

Документ або проводиться повністю в усіх регістрах одночасно, або жодна зміна не фіксується. Система повертає зрозуміле людино-читабельне повідомлення: «Недостатньо залишку товару: Ноутбук Dell (потрібно 5 шт., в наявності 3 шт. на складі Основний)».

3. Подвійний запис проти Регістрів накопичення

Часто виникає дискусія: що краще — класичні бухгалтерські проводки (Дебет / Кредит) чи регістри залишків?

Досвід показує, що для швидкодійної системи потрібні обидва механізми:

  • Регістри накопичення (баланс залишків): зберігають агреговані підсумки по складах, партіях та контрагентах. Завдяки цьому звіт про залишки на складі на 100 000 позицій генерується за мілісекунди без перерахунку всіх проводок з початку року.
  • Журнал проводок (План рахунків): формує юридично обов'язковий бухгалтерський баланс, головну книгу та оборотні відомості для податкової та фінансової звітності.

4. Партионний облік у режимі високого навантаження

Коли одночасно десятки менеджерів проводять реалізації з одного складу, виникає ризик транзакційних взаємних блокувань (Deadlocks). У Ruta алгоритм списання партій оптимізовано:

  • Рядки номенклатури перед блокуванням завжди сортуються за унікальним ID, що запобігає виникненню взаємних блокувань;
  • Використовуються точкові блокування рядків залишку (Row-level locking SELECT ... FOR UPDATE) замість блокування всієї таблиці чи складу;
  • Розрахунок собівартості відокремлений у чисту функцію без побічних ефектів, що полегшує тестування.