在 Rails 中实施租赁商店:如何跟踪一段时间内的库存状态?

发布于 2024-08-10 22:30:17 字数 422 浏览 2 评论 0原文

假设您正在为一家滑雪板租赁商店实施 Rails 应用程序。

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

  1. 外出维修
  2. 可在商店 X
  3. 借给客户 Y

公司需要能够查看

  • 的特定滑雪板
  • 特定客户

的租赁历史记录租赁历史记录需要包括时态数据 (例如,Sally于2009年12月1日至2009年12月3日租用了滑雪板0123)。

你会如何设计你的模型?您是否有一个包含 4 列(id、state、customer、store)的滑雪板表,并在每次状态更改时将此表中的行以及时间戳复制到 Snowboard_history 表中?

谢谢!

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

Let's say you're implementing rails app for a snowboard rental store.

A given snowboard can be in one of 3 states:

  1. away for maintenance
  2. available at store X
  3. on loan to customer Y

The company needs to be able to view a rental history for

  • a particular snowboard
  • a particular customer

The rental history needs to include temporal data (e.g. Sally rented snowboard 0123 from Dec. 1, 2009 to Dec. 3 2009).

How would you design your model? Would you have a snowboard table with 4 columns (id, state, customer, store), and copy rows from this table, along with a timestamp, to a snowboard_history table every time the state changes?

Thanks!

(Note: I'm not actually trying to implement a rental store; this was just the simplest analogue I could think of.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

樱娆 2024-08-17 22:30:17

我会使用一对插件来完成工作。这将使用四个模型。滑雪板、商店、用户和审计。

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

I would use a pair of plugins to get the job done. Which would use four models. Snowboard, Store, User and Audit.

acts_as_state_machine and acts_as_audited

AASM simplifies the state transitions. While auditing creates the history you want.

The code for Store and User is trivial and acts_as_audited will handle the audits model.

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    

Assuming your customer is the current_user during the controller action when state changes that's all you need.

To get a snowboard history:

@snowboard.audits

To get a customer's rental history:

@customer.full_history

You might want to create a helper method to shape a customer's history into something more useful. Maybe something like his:

 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
星星的軌跡 2024-08-17 22:30:17

首先,我将为滑雪板、客户和商店生成单独的模型。

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 表示退货,1 表示维护,2 表示租赁...)
  • snowboard_id (不为空)
  • customer_id
  • store_id

例如,

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

然后我设置SnowboardEventSnowboardCustomerStore之间的所有关系。 Snowboard 可以具有 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 属性。我不认为使用观察者是必要的或相关的。

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

First I would generate separate models for Snowboard, Customer and Store.

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

(rails automatically generates id and created_at, modified_at dates)

To preserve the history, I wouldn't copy rows/values from those tables, unless it is necessary (for example if you'd like to track the price customer rented it).

Instead, I would create SnowboardEvent model (you could call it SnowboardHistory if you like, but personally it feels strange to make new history) with the similiar properties you described:

  • ev_type (ie. 0 for RETURN, 1 for MAINTENANCE, 2 for RENT...)
  • snowboard_id (not null)
  • customer_id
  • store_id

For example,

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

Then I'd set all the relations between SnowboardEvent, Snowboard, Customer and Store. Snowboard could have functions like current_state, current_store implemented as

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

And Customer would have same has_many :snowboard_events.

Checking the snowboard or customer history, would be just a matter of looping through the records with Snowboard.snowboard_events or Customer.snowboard_events. The "temporal data" would be the created_at property of those events. I don't think using Observer is necessary or related.

NOTE: the above code is not tested and by no means perfect, but just to get the idea :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文