1 # -*- coding: utf-8 -*-
2 def parse_date_select params, name
3 return nil unless params and not params['%s(1i)' % name].blank?
4 args = (1..3).map {|i| params["%s(%di)" % [name, i]]}.reject(&:blank?).map(&:to_i)
5 args << 1 if args.length < 3
14 TotalizerBase.instance_eval { include AddCSVDownload }
17 class Totalizer < TotalizerBase
22 'member_male' => "男性\n(会員)",
23 'member_female' => "女性\n(会員)",
24 'guest_male' => "男性\n(非会員)",
25 'guest_female' => "女性\n(非会員)",
27 'product_code' => '商品番号',
28 'product_name' => '商品名',
32 'sale_start_at' => '販売開始期間',
45 'payment_total' => '支払い合計',
49 'use_point' => '使用ポイント',
51 WHERE_CLAUSE = "orders.received_at between :date_from and :date_to and orders.retailer_id = :retailer_id "
53 attr_accessor :columns, :title, :total, :links, :default_type
55 def Totalizer.get_instance type
56 name = "#{type}_totalizer".classify
57 Object.const_defined?(name) and Object.const_get(name).new
61 columns.map { |i| COLUMN_NAMES[i] }
64 def get_records(params)
65 conditions = get_conditions params
66 return nil unless conditions[:date_from] and conditions[:date_to]
67 @records = search(conditions)
70 def get_conditions params
72 if params[:search][:by_date]
73 date_from = parse_date_select(params[:search], 'date_from')
74 date_to = parse_date_select(params[:search], 'date_to')
76 date_from = parse_date_select(params[:search], 'month')
77 date_to = Time.local(date_from.year, date_from.month, 1) + 1.month - 1.day if date_from
79 date_to &&= Time.local(date_to.year, date_to.month, date_to.day, 23, 59, 59)
80 { :date_from => date_from, :date_to => date_to, :retailer_id => params[:search][:retailer_id] }
83 def self.list_for_csv(params)
84 @records = self.new.get_records(params)
95 g.font = Pathname.new(RAILS_ROOT).join('lib', 'sazanami-gothic.ttf').to_s
99 def self.get_csv_settings(columns=nil)
100 [self.new.columns, self.new.labels.map{|c|c.sub("\n", '')}]
103 def self.csv_output_setting_name
109 class TermTotalizer < Totalizer
113 @columns = %w(term count male female subtotal discount charge deliv_fee total average)
114 @links = %w(日別 day 月別 month 年別 year 曜日別 wday 時間別 hour)
115 @default_type = 'day'
118 def get_conditions params
120 @helper = Helper.new(params[:type])
125 @records or return nil
126 return nil if @records == []
127 g = init_graph(Gruff::Line)
129 g.data('価格', @records.map{|r| r['total']})
132 (0...@records.size).step([@records.size/3, 1].max)[0...-1].each do |i|
133 labels[i] = @records[i]['term']
136 labels[@records.size-1] = @records.last['term']
141 def search conditions
142 records = OrderDelivery.find_by_sql([<<-EOS, conditions])
144 #{@helper.columns 'received_at'},
148 sum(female) as female,
149 sum(member_male) as member_male,
150 sum(member_female) as member_female,
151 sum(guest_male) as guest_male,
152 sum(guest_female) as guest_female,
154 sum(subtotal) as subtotal,
155 sum(payment_total) as payment_total,
156 sum(discount) as discount,
157 sum(deliv_fee) as deliv_fee,
158 sum(charge) as charge,
159 sum(use_point) as use_point,
160 round(avg(total)) as average
163 case when (customers.sex = #{System::MALE} or (customers.sex is null and order_deliveries.sex = #{System::MALE})) then 1 else 0 end as male,
164 case when (customers.sex = #{System::FEMALE} or (customers.sex is null and order_deliveries.sex = #{System::FEMALE})) then 1 else 0 end as female,
165 case when customers.sex = #{System::MALE} then 1 else 0 end as member_male,
166 case when customers.sex = #{System::FEMALE} then 1 else 0 end as member_female,
167 case when customers.sex is null and order_deliveries.sex = #{System::MALE} then 1 else 0 end as guest_male,
168 case when customers.sex is null and order_deliveries.sex = #{System::FEMALE} then 1 else 0 end as guest_female,
169 subtotal, total, payment_total,
170 discount, deliv_fee, charge, use_point,
172 from order_deliveries
173 join orders on orders.id = order_deliveries.order_id
174 left outer join customers on customers.id = orders.customer_id
175 where #{WHERE_CLAUSE}
177 group by term, #{@helper.group_by_clause}
178 order by term, #{@helper.group_by_clause}
181 records.each do | record |
182 record.term = @helper.term_of record
185 all_term = @helper.all_term conditions[:date_from], conditions[:date_to]
186 record_hash = records.index_by(&:term)
187 # term を回して records に無ければ、全部 0 の Hash で埋める
188 records = all_term.map do | term |
192 r = Hash.new(0) # デフォルト値0
200 records.each do | record |
201 columns.each do | key |
203 total[key] += record[key].to_i
206 @total['term'] = '合計'
207 if @total['count'] != 0
208 @total['average'] = @total['total'] / @total['count']
210 @total['average'] = 0
217 @type = type || 'day'
220 @fields = ['year', 'month', 'day', 'dow']
221 @format = '%04d/%02d/%02d(%s)'
223 @fields = ['year', 'month']
224 @format = '%02d/%02d月'
241 def columns date_column_name
242 #offset = "%d" % Time.zone.utc_offset
243 dow_column_interval = "%s" % [date_column_name]#+ #{MergeAdapterUtil.interval_second(offset)}" % [date_column_name]
244 dow_column = "#{MergeAdapterUtil.day_of_week(dow_column_interval)} as dow"
247 "extract(%s from %s ) as %s" % [f, date_column_name, f]
255 @format % @fields.map do | f |
257 f == 'dow' ? %w(日 月 火 水 木 金 土)[v.to_i] : v
262 {'year'=>d.year,'month'=>d.month,'day'=>d.day,'dow'=>d.wday}
265 def all_term date_from, date_to
270 (( ( date_to - date_from ) / 1.day).ceil ).times do
271 terms << term_of(date_to_record(d))
272 d = d.advance(:days=>1)
279 while d.year < date_to.year || d.month <= date_to.month
280 terms << term_of(date_to_record(d))
281 d = Time.local(d.year, d.month + 1)
287 while d.year <= date_to.year
288 terms << term_of(date_to_record(d))
289 d = Time.local(d.year + 1)
293 (0..6).map{ |dow| term_of({'dow'=>dow}) }
295 (0..23).map{ |hour| term_of({'hour'=>hour}) }
301 class ProductTotalizer < Totalizer
305 @columns = %w(position product_code product_name count items unit_price price sale_start_at)
306 @links = %w(全体 all 会員 member 非会員 nomember)
307 @default_type = 'all'
309 def get_conditions params
314 [Customer::KARITOUROKU, Customer::TOUROKU, Customer::TEISHI]
318 [Customer::KARITOUROKU, Customer::TOUROKU, Customer::TEISHI]
320 @member = params[:type]
321 conds[:sale_start_from] = parse_date_select(params[:search], 'sale_start_from')
322 conds[:sale_start_to] = parse_date_select(params[:search], 'sale_start_to')
326 @records or return nil
327 g = init_graph(Gruff::Pie)
329 g.legend_box_size = g.legend_font_size = 14
330 g.zero_degree = -90.0
333 @records.each_with_index do |r, i|
335 g.data(r['product_name'], r['price'].to_i)
337 others += r['price'].to_i
341 g.data('その他', others)
345 def search conditions
346 records = OrderDetail.find_by_sql([<<-EOS, conditions])
353 sum(quantity) as items,
354 order_details.price as unit_price,
355 sum(order_details.price*quantity) as price,
356 products.sale_start_at as sale_start_at
358 join order_deliveries on order_deliveries.id = order_details.order_delivery_id
359 join orders on orders.id = order_deliveries.order_id
360 left outer join customers on customers.id = orders.customer_id
361 join product_styles on product_styles.id = order_details.product_style_id
362 join products on products.id = product_styles.product_id
363 where #{WHERE_CLAUSE}
364 #{if @member == 'member'
365 " and (customers.activate in (:activate)) "
366 elsif @member == 'nomember'
367 " and (customers.activate is null) "
369 " and (customers.activate in (:activate) or customers.activate is null) "
371 and ((:sale_start_from is null or :sale_start_to is null)
372 or products.sale_start_at between :sale_start_from and :sale_start_to)
373 group by order_details.id, product_code, product_name, unit_price, products.sale_start_at
376 # position の振り直し & 販売開始日を Date に
377 records.zip((1..records.size).to_a) do | r, i |
379 r.sale_start_at = Date.parse(r.sale_start_at)
385 class AgeTotalizer < Totalizer
401 @columns = %w(age count subtotal discount charge deliv_fee total average)
402 #@links = %w(全体 all 会員 member 非会員 nomember)
403 @links = %w(全体 all) # 今のところ非会員購入はできないので
404 @default_type = 'all'
406 def get_conditions params
411 @records or return nil
412 g = init_graph(Gruff::SideBar)
416 g.data(r['age'], r['payment_total'].to_i)
421 def search conditions
422 age_when_else = AGES.map do | v, label |
426 "when ((customers.birthday is null and customers.id is not null) or (customers.id is null and order_deliveries.birthday is null)) then '%s'" % label
428 "when extract(year from #{MergeAdapterUtil.age('customers.birthday')}) < %d then '%s'" % [v, label]
429 "when extract(year from #{MergeAdapterUtil.age('order_deliveries.birthday')}) < %d then '%s'" % [v, label]
432 records = OrderDetail.find_by_sql([<<-EOS, conditions])
437 sum(subtotal) as subtotal,
438 sum(payment_total) as payment_total,
439 sum(discount) as discount,
440 sum(deliv_fee) as deliv_fee,
441 sum(charge) as charge,
442 sum(use_point) as use_point,
443 round(avg(total)) as average
456 from order_deliveries
457 join orders on orders.id = order_deliveries.order_id
458 left outer join customers on customers.id = orders.customer_id
459 where #{WHERE_CLAUSE}
465 record_hash = records.index_by(&:age)
466 AGES.map{|_, age| record_hash[age] || Hash.new(0).merge('age'=>age) }
470 class JobTotalizer < Totalizer
474 @columns = %w(position job count subtotal discount charge deliv_fee total average)
476 @default_type = 'all'
478 def get_conditions params
482 @records or return nil
483 g = init_graph(Gruff::Pie)
485 @records.reject{|r| r['payment_total'].to_i.zero?}.each do |r|
486 g.data(r['job'], r['payment_total'].to_i)
490 def search conditions
491 records = OrderDetail.find_by_sql([<<-EOS, conditions])
494 occupations.name as job,
497 sum(subtotal) as subtotal,
498 sum(payment_total) as payment_total,
499 sum(discount) as discount,
500 sum(deliv_fee) as deliv_fee,
501 sum(charge) as charge,
502 sum(use_point) as use_point,
503 round(avg(total)) as average
507 when customers.occupation_id is not null then customers.occupation_id
508 when customers.occupation_id is null then order_deliveries.occupation_id
509 end as occupation_id,
511 subtotal as subtotal,
512 payment_total as payment_total,
513 discount as discount,
514 deliv_fee as deliv_fee,
516 use_point as use_point
517 from order_deliveries
518 join orders on orders.id = order_deliveries.order_id
519 left outer join customers on customers.id = orders.customer_id
520 left join occupations on occupations.id = customers.occupation_id
521 where #{WHERE_CLAUSE}
523 left join occupations on occupations.id = t1.occupation_id
524 group by occupations.name
527 record_hash = records.index_by(&:job)
528 records = Occupation.find(:all).map do | occupation |
529 job = occupation.name
530 if record_hash.has_key?(job)
537 end.sort_by do |record|
538 -record['total'].to_i
540 records.zip((1..records.size).to_a) do | r, i |
547 class MemberTotalizer < Totalizer
551 @columns = %w(kind count subtotal discount charge deliv_fee total average)
553 def get_conditions params
557 @records or return nil
558 g = init_graph(Gruff::Pie)
560 @records.reject{|r| r['payment_total'].to_i.zero?}.each do |r|
561 g.data(r['kind'], r['payment_total'].to_i)
565 def search conditions
566 records = OrderDetail.find_by_sql([<<-EOS, conditions])
568 case when customers.sex = #{System::MALE} then '会員男性'
569 when customers.sex = #{System::FEMALE} then '会員女性'
570 when customers.sex is null and order_deliveries.sex = #{System::MALE} then '非会員男性'
571 when customers.sex is null and order_deliveries.sex = #{System::FEMALE} then '非会員女性'
575 sum(subtotal) as subtotal,
576 sum(payment_total) as payment_total,
577 sum(discount) as discount,
578 sum(deliv_fee) as deliv_fee,
579 sum(charge) as charge,
580 sum(use_point) as use_point,
581 round(avg(total)) as average
582 from order_deliveries
583 join orders on orders.id = order_deliveries.order_id
584 left outer join customers on customers.id = orders.customer_id
585 where #{WHERE_CLAUSE}
589 record_hash = records.index_by(&:kind)
591 # TODO: ここの即値はなんとかしたい
592 records = %w(会員男性 会員女性 非会員男性 非会員女性).map do |kind|
593 if record_hash.has_key?(kind)
600 end.sort_by do |record|
601 -record['total'].to_i