Implementando a loja de aluguel em trilhos: como rastrear um estado de inventário ao longo do tempo?

StackOverflow https://stackoverflow.com/questions/1741034

Pergunta

Digamos que você esteja implementando o aplicativo Rails para uma loja de aluguel de snowboard.

Um determinado snowboard pode estar em um dos 3 estados:

  1. longe para manutenção
  2. Disponível na loja x
  3. emprestado ao cliente y

A empresa precisa ser capaz de ver um histórico de aluguel para

  • um snowboard específico
  • um determinado cliente

O histórico de aluguel precisa incluir dados temporais (por exemplo, snowboard alugado 0123 de 1 de dezembro de 2009 a 3 de dezembro de 2009).

Como você criaria seu modelo? Você teria uma mesa de snowboard com 4 colunas (ID, estado, cliente, loja) e linhas de copiar desta tabela, juntamente com um registro de data e hora, para uma mesa de snowboard_history toda vez que o estado mudar?

Obrigado!

(Nota: na verdade não estou tentando implementar uma loja de aluguel; esse era apenas o análogo mais simples que eu conseguia pensar.)

Foi útil?

Solução

Eu usaria um par de plugins para fazer o trabalho. Que usaria quatro modelos. Snowboard, loja, usuário e auditoria.

Atos_as_state_machine e Atos_As_Audited

AASM simplifica as transições do estado. Enquanto a auditoria cria a história que você deseja.

O código para armazenamento e usuário é trivial e acts_as_audited lidará com o modelo de auditorias.

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    

Supondo que seu cliente seja o current_user durante a ação do controlador quando o estado muda, é tudo o que você precisa.

Para obter uma história de snowboard:

@snowboard.audits

Para obter o histórico de aluguel de um cliente:

@customer.full_history

Você pode criar um método auxiliar para moldar o histórico de um cliente em algo mais útil. Talvez algo como o dele:

 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

Outras dicas

Primeiro, eu geraria modelos separados para snowboard, cliente e loja.

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

(Rails gera automaticamente id e created_at, modified_at datas)

Para preservar a história, eu não copia linhas/valores dessas tabelas, a menos que seja necessário (por exemplo, se você quiser rastrear o preço do cliente a alugou).

Em vez disso, eu criaria um modelo de snowboardEvent (você poderia chamá -lo SnowboardHistory Se quiser, mas pessoalmente é estranho fazer uma nova história) com as propriedades semelhantes que você descreveu:

  • ev_type (ou seja, 0 para devolução, 1 para manutenção, 2 para alugar ...)
  • snowboard_id (não nulo)
  • customer_id
  • store_id

Por exemplo,

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

Então eu definiria todas as relações entre SnowboardEvent, Snowboard, Customer e Store. O snowboard pode ter funções como current_state, current_store implementado como

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

E o cliente teria o mesmo has_many :snowboard_events.

Verificar o snowboard ou o histórico do cliente seria apenas uma questão de loop nos registros com Snowboard.snowboard_events ou Customer.snowboard_events. Os "dados temporais" seriam o created_at propriedade desses eventos. Não acho que o uso do observador seja necessário ou relacionado.

Nota: O código acima não é testado e de modo algum perfeito, mas apenas para obter a ideia :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top