如何计算每种货币的总金额

时间:2016-06-06 13:08:19

标签: ruby-on-rails

我想计算每种货币的总金额。

我的应用图片如下:

event1    USD 1.23
event2    EUR 2.34
event3    JPY 100
event4    USD 4.56

Total
USD 5.79
EUR 2.34
JPY 100

虽然无论货币如何,我都可以使用sum(:amount)计算金额,但我想知道如何计算和显示每种货币。

时间表\ show.html.erb

<%= render @schedules %>

安排\ _schedules.html.erb

<% schedule.rooms.each_with_index do |a, idx| %>
  <% a.events.each do |e| %>
     ...
     <%= l(e.start_at) %>&nbsp;-&nbsp;<%= l(e.end_at) %>
     ...

    <%= e.title %>
    <%= e.ccy %> <%= e.amount %>
  <% end %>

  <%= a.events.sum(:amount) %>   # 108.13 regardless of currency (1.23 + 2.34 + 100 + 4.56)

我的模特

class Schedule
  has_many :rooms, inverse_of: :schedule, dependent: :destroy
end

class Room    
  belongs_to :schedule, inverse_of: :rooms
  has_many :events, inverse_of: :room, dependent: :destroy
end

class Event
  belongs_to :room, inverse_of: :events
  has_one :schedule, autosave: false, through: :room
end

schedules_controller.rb

def show
  @schedules = Schedule.find(params[:id])
end

schema.rb

create_table "rooms", force: :cascade do |t|
  t.integer  "schedule_id"
  t.integer  "day",                  default: 1
end

create_table "events", force: :cascade do |t|
  t.time     "start_at"
  t.time     "end_at"
  ...
  t.string   "title"
  t.integer  "room_id"
  t.string   "ccy"
  t.decimal  "amount"
end

如果你能给我任何建议,我将不胜感激。

更新

当我尝试添加<%= room.events.group(:currency).count(:amount) %> @TheCha͢mp回答时,出现以下错误。 (变量的名称是原始的。我稍后会重命名。)

ActiveRecord::StatementInvalid in SchedulesController#show

PG::GroupingError: ERROR: column "events.start_at" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: ...dation_id" = $1 GROUP BY "events"."ccy" ORDER BY "events"."... ^ : SELECT COUNT("events"."amount") AS count_amount, ccy AS ccy FROM "events" WHERE "events"."room_id" = $1 GROUP BY "events"."ccy" ORDER BY "events"."start_at" ASC

我更新了_schedules.html.erbschema.rb

解决

使用unscoped。 我在这篇文章https://makandracards.com/bitcrowd/32883-pg-groupingerror-error-column-must-appear-in-the-group-by-clause-or-be-used-in-an-aggregate-function-in-rails-while-trying-group中找到了它。

我添加了以下代码。

<% a.events.unscoped.where(room_id: a.id).group(:ccy).sum(:amount).each do |ccy, amount| %>

2 个答案:

答案 0 :(得分:1)

首先,您的命名很糟糕:为什么要为房间a和事件e命名。到底是什么ccy?我不得不谷歌那个......这让其他开发者难以阅读。我在我的解决方案中重命名了变量。

关于您的问题,您可以在数据库级别轻松执行此操作:

Event.group(:currency).count(:amount)
# => {usd: 145, eur: 2289, ...}

因此,在您看来,您可以使用

<% schedule.rooms.each_with_index do |room, index| %>
  <% room.events.each do |e| %>
    <%= event.title %>
    <%= event.ccy %> <%= event.amount %>
  <% end %>

  <%= room.events.group(:currency).count(:amount) %>
<% end %>

但是这会打到每个房间对象的数据库,效率很低。如果要阻止此操作,请将事件加入控制器中的日程表中。这是一项完全不同的任务,但我相信你可以搞清楚。

答案 1 :(得分:0)

我建议编写一个执行处理的方法,并将其放在Ruby源文件而不是视图模板中。然后只需在模板中调用该方法。这是一个示例脚本,说明了应该起作用的方法:

#!/usr/bin/env ruby

require 'awesome_print'


def totals_by_currency(input_lines)
  totals = Hash.new { |h, k| h[k] = 0.0 } # initialize new values to 0.0
  input_lines.each_with_object(totals) do |line, totals|
    _, currency, amount_as_string = line.split
    totals[currency] += amount_as_string.to_f
    totals[currency] = totals[currency].round(2)
  end
end


input =
"event1    USD 1.23
event2    EUR 2.34
event3    JPY 100
event4    USD 4.56
"

ap totals_by_currency(input.split("\n"))

=begin
Outputs:

{
    "USD" => 5.79,
    "EUR" => 2.34,
    "JPY" => 100.0
}

=end

此外,如果您想确保2个小数位,那么您可以使用''%。2f'将数字转换为字符串%:

'%.2f' % 1 # => "1.00"

可以进入视图,因为它只是如何向用户显示数据的说明。