Implementando a loja de aluguel em trilhos: como rastrear um estado de inventário ao longo do tempo?
-
20-09-2019 - |
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:
- longe para manutenção
- Disponível na loja x
- 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.)
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 :)