Копейки в счетах ра...
 
Уведомления
Очистить всё

Копейки в счетах расходятся: НДС считаем во Float. Как правильно?

5 Сообщения
4 Пользователи
13 Реакции
211 Просмотры
lena_bogatyreva
Сообщения: 27
Автор темы
Читает чужие PR бэкенд-разработчица
Присоединился: 3 года назад
[#91]

Я в прошлой жизни была бухгалтером, поэтому, когда сверка показала расхождение в 1 рубль 37 копеек за месяц, меня это задело лично) Rails 8.0, PostgreSQL. Цены в базе decimal(12, 2), но кто-то когда-то написал to_f, и всё считается во Float:

class Invoice < ApplicationRecord
  has_many :lines, class_name: "InvoiceLine"

  def total_with_vat
    lines.sum { |l| l.price.to_f * l.quantity * 1.2 }.round(2)
  end

  def vat_amount
    (total_with_vat / 1.2 * 0.2).round(2)
  end
end

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


4 ответа
kostya.sokurov
Сообщения: 73
Держит прод бэкенд-разработчик, senior
Присоединился: 4 года назад

Во Float копейки и не сойдутся: 0.1 + 0.2 там честно даёт 0.30000000000000004. Из decimal-колонки Active Record и так отдаёт BigDecimal, достаточно убрать to_f и не смешивать с Float-литералами, 1.2 в коде тоже Float. А как округлять - вопрос не к Ruby, а к вашему акту. Если НДС в акте по каждой строке, то и считать по строкам, а итог суммой уже округлённых:

class InvoiceLine < ApplicationRecord
  VAT_RATE = BigDecimal("0.2")

  def amount = price * quantity
  def vat = (amount * VAT_RATE).round(2, :half_up)
end

class Invoice < ApplicationRecord
  has_many :lines, class_name: "InvoiceLine"

  def vat_amount = lines.sum(&:vat)
  def total_with_vat = lines.sum(&:amount) + vat_amount
end

Если же НДС считается от итога, копейка выйдет другая, и это нормально. Главное, чтобы код считал так же, как документ.


Ответ
GalinaKorneva
Сообщения: 30
Читает чужие PR бэкенд-разработчица
Присоединился: 4 года назад

Можно ещё хранить суммы в копейках целым числом, в платёжках их так и передают. Но для счетов с НДС BigDecimal хватает


Ответ
Валерий40
Сообщения: 33
Читает чужие PR фулстек-разработчик
Присоединился: 4 года назад

В PHP я бы тут полез в bcmath, а в Ruby хотя бы BigDecimal из коробки. Проверь ещё формы: из параметров приходит строка, и to_f по привычке пишут прямо в контроллере. Если цена идёт через атрибут модели с decimal-колонкой, Rails сам приведёт её к BigDecimal.


Ответ
lena_bogatyreva
Сообщения: 27
Автор темы
Читает чужие PR бэкенд-разработчица
Присоединился: 3 года назад

Убрала to_f и Float-литералы, НДС теперь по строкам с round(2, :half_up), как в наших актах. Пересчитала сентябрьские счета в консоли на копии базы: все 312 сошлись с актами до копейки. Добавила тест на тот счёт, где вылезало расхождение. Тимлид сказал, что раз я это нашла, то теперь смотрю на ревью всё, где есть деньги. Сама напросилась)


Ответ