在Rails中实施租赁商店:如何跟踪库存状态?

时间:2009-11-16 09:25:00

标签: ruby-on-rails database model tracking temporal

假设您正在为滑雪板租赁商店实施rails app。

给定的滑雪板可以处于以下三种状态之一:

  1. 离开维修
  2. 可在商店X
  3. 获得
  4. 贷款给客户Y
  5. 公司需要能够查看

    的租赁历史记录
    • 特定的滑雪板
    • 特定客户

    租赁历史记录需要包含时间数据(例如,Sally从2009年12月1日到2009年12月3日租用滑雪板0123)。

    您将如何设计模型?你有一个4列(id,state,customer,store)的滑雪板表,并且每次状态发生变化时,都会将此表中的行和时间戳复制到snowboard_history表中吗?

    谢谢!

    (注意:我实际上并没有尝试实施租赁店;这只是我能想到的最简单的类比。)

2 个答案:

答案 0 :(得分:9)

我会使用一对插件来完成工作。哪个会使用四个型号。滑雪板,商店,用户和审核。

acts_as_state_machineacts_as_audited

AASM简化了状态转换。审计创建了您想要的历史记录。

Store和User的代码很简单,acts_as_audited将处理审计模型。

class Snowboard < ActiveRecord::Base

  include AASM
  belongs_to :store


  aasm_initial_state :unread
  acts_as_audited :only => :state

  aasm_state :maintenance
  aasm_state :available
  aasm_state :rented

  aasm_event :send_for_repairs do
    transitions :to => :maintenance, :from => [:available]
  end

  aasm_event :return_from_repairs do
    transitions :to => :available, :from => [:maintenance]
  end

  aasm_event :rent_to_customer do
   transitions :to => :rented, :from => [:available]
  end

  aasm_event :returned_by_customer do
    transitions :to => :available, :from => [:rented]
  end
end

class User < ActiveRecord::Base
  has_many :full_history, :class_name => 'Audit', :as => :user,
   :conditions => {:auditable_type => "Snowboard"}
end    

假设您的客户是控制器操作期间的current_user,当状态发生变化时,您只需要这样做。

获取滑雪板历史记录:

@snowboard.audits

获取客户的租赁历史记录:

@customer.full_history

您可能想要创建一个帮助方法,将客户的历史记录塑造成更有用的东西。也许像他这样:

 def rental_history
    history = []
    outstanding_rentals = {}
    full_history.each do |item|
      id = item.auditable_id
      if rented_at = outstanding_rentals.keys.delete(id)
        history << { 
          :snowboard_id => id, 
          :rental_start => rented_at,
          :rental_end => item.created_at
        }   
      else
        outstanding_rentals[:id] = item.created_at
      end
    end
    history << oustanding_rentals.collect{|key, value| {:snowboard_id => key,  
      :rental_start => value}
  end
end

答案 1 :(得分:2)

首先,我会为Snowboard,Customer和Store生成单独的模型。

script/generate model Snowboard name:string price:integer ...
script/generate model Customer name:string ...
script/generate model Store name:string ...

(rails会自动生成idcreated_atmodified_at日期)

为了保留历史记录,我不会复制这些表格中的行/值,除非有必要(例如,如果您想跟踪客户租用它的价格)。

相反,我会创建SnowboardEvent模型(如果您愿意,可以将其称为SnowboardHistory,但就创建新历史而言,使用您所描述的类似属性会感到奇怪:

  • ev_type(即0为RETURN,1为维护,2为RENT ......)
  • snowboard_id(非空)
  • customer_id
  • store_id

例如,

script/generate model SnowboardEvent ev_type:integer snowboard_id:integer \
    customer_id:integer store_id:integer

然后我会设置SnowboardEventSnowboardCustomerStore之间的所有关系。滑雪板可以具有current_statecurrent_store等功能,实现为

class Snowboard < ActiveRecord::Base
  has_many :snowboard_events
  validates_presence_of :name

  def initialize(store)
    ev = SnowboardEvent.new(
         {:ev_type => RETURN,
          :store_id => store.id,
          :snowboard_id = id,
          :customer_id => nil})
    ev.save
  end

  def current_state
    ev = snowboard_events.last
    ev.ev_type          
  end

  def current_store
    ev = snowboard_events.last
    if ev.ev_type == RETURN
      return ev.store_id
    end
    nil
  end

  def rent(customer)
    last = snowboard_events.last
    if last.ev_type == RETURN
      ev = SnowboardEvent.new(
           {:ev_type => RENT,
            :snowboard_id => id,
            :customer_id => customer.id
            :store_id => nil })
      ev.save
    end
  end

  def return_to(store)
    last = snowboard_events.last
    if last.ev_type != RETURN
      # Force customer to be same as last one
      ev = SnowboardEvent.new(
           {:ev_type => RETURN,
            :snowboard_id => id,
            :customer_id => last.customer.id
            :store_id => store.id})
      ev.save
    end
  end
end

客户将拥有相同的has_many :snowboard_events

检查滑雪板或客户历史记录只需使用Snowboard.snowboard_eventsCustomer.snowboard_events循环播放记录。 “时态数据”将是这些事件的created_at属性。我不认为使用Observer是必要的或相关的。

注意:上面的代码没有经过测试,也不是完美的,只是为了得到这个想法:)