Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Progress with stock creation; some adjustments to improve documentation

  • Loading branch information...
commit 71ca27281bbd29f43b1fa6122e945199eb8f0b68 1 parent b50239c
@LichP authored
View
1  .yardopts
@@ -0,0 +1 @@
+--title "Porp"
View
3  README
@@ -1,7 +1,8 @@
Porp: The Prototype Open Retail Platform
-Phil Stewart, 2010-10, 2011-01
+Phil Stewart, 2010, 2011
201101: Started a new iteration, using Ohm for for ORM
+201109: Resumed work after 3 month hiatus
Licensed under the MIT License - see LICENSE.
View
9 irb/porp-pry.rb
@@ -66,13 +66,20 @@ def write
end
end
+#class Stock::Entity
+# def self.const_missing(name)
+# binding.pry
+# Stock.const_get(name)
+# end
+#end
+
stock_options = {
:holders => [:shop, :newsagent, :ho],
:statuses => [:futurestock, :instock, :reserved, :archived]
}
#test_stke_array = []
-1.upto(100) do |i|
+1.upto(3) do |i|
# StockEntity.find(description: "Test stock entity #{i}").first ||
Stock.create("Test stock entity #{i}", stock_options)
end
View
49 lib/porp/stock.rb
@@ -6,8 +6,6 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
require File.join(File.dirname(__FILE__), 'stock', 'director')
require File.join(File.dirname(__FILE__), 'stock', 'entity')
require File.join(File.dirname(__FILE__), 'stock', 'holder')
@@ -17,34 +15,31 @@
require File.join(File.dirname(__FILE__), 'stock', 'sourcedest')
require File.join(File.dirname(__FILE__), 'stock', 'status')
-=begin
-The Stock class provides the high level interface for interacting with stock.
-=end
- class Stock
- attr_reader :entity
+# The Stock class provides the high level interface for interacting with stock.
+class Stock
+ attr_reader :entity
- # Return a Stock instance for the stock entity identified by id
- def self.[](id)
- entity = Entity[id]
- if !entity.nil?
- self.new(Entity[id])
- else
- nil
- end
+ # Return a Stock instance for the stock entity identified by id
+ def self.[](id)
+ entity = Entity[id]
+ if !entity.nil?
+ self.new(Entity[id])
+ else
+ nil
end
+ end
- # Creates a new item of stock, including the entity and all holdings,
- # ensuring all dependencies are met i.e. all targets, holders, and statuses
- def self.create(description = "", opts = {})
- raise Orp::NoHoldersSpecified if opts[:holders].nil?
- raise Orp::NoStatusesSpecified if opts[:statuses].nil?
+ # Creates a new item of stock, including the entity and all holdings,
+ # ensuring all dependencies are met i.e. all targets, holders, and statuses
+ def self.create(description = "", opts = {})
+ raise Orp::NoHoldersSpecified if opts[:holders].nil?
+ raise Orp::NoStatusesSpecified if opts[:statuses].nil?
- self.new(Director.build_from_options(description, opts)
- end
+ self.new(Director.build_from_options(description, opts))
+ end
- def initialize(entity = nil)
- @entity = entity
- end
+ def initialize(entity = nil)
+ @entity = entity
end
-#end
-
+end
+
View
45 lib/porp/stock/director.rb
@@ -6,8 +6,7 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockDirector class mediates the complex interactions between the various
stock related classes, in particular the StockHolding class, which represents
@@ -33,22 +32,50 @@
user configuration and apply it on a product group, stock entity, and default
basis as appropriate.
=end
-class Stock
class Director
- def self.ensure_holders(holders)
- raise Orp::NoHoldersSpecified if holders.nil?
+ def self.ensure_holders(holder_names)
+ raise Orp::NoHoldersSpecified if holder_names.nil?
+ Holder.ensure_extant(holder_names)
end
- def self.ensure_statuses(statuses)
- raise Orp::NoStatusesSpecified if holders.nil?
+ def self.ensure_statuses(status_names)
+ raise Orp::NoStatusesSpecified if status_names.nil?
+ Status.ensure_extant(status_names)
end
# Build a stock entity and dependencies from passed options
+ #
+ # @param [String] description: A description for the entity
+ # @param [Hash] opts: Options specifying names of holders and statuses
+ # to create holdings for
+ # @returns The newly created entity
def self.build_from_options(description, opts)
-
+
+ # Ensure holders and statuses exist
+ self.ensure_holders(opts[:holders])
+ self.ensure_statuses(opts[:statuses])
+
+ # Create the entity
+ entity = Entity.create(description: description)
+
+ # Create holdings for every permutation of entity, holder, and status
+ holding_permutations(entity, opts[:holders], opts[:statuses]) do |entity, holder, status|
+ Holding.create(entity: entity, holder: holder, status: status)
+ end
+ end
+
+ protected
+
+ def self.holding_permutations(entity, holder_names, status_names)
+ holders = Holder.find_union(name: holder_names)
+ statuses = Status.find_union(name: status_names)
+ holders.each do |holder|
+ statuses.each do |status|
+ yield entity, holder, status
+ end
+ end
end
end
end
-#end
View
23 lib/porp/stock/entity.rb
@@ -6,39 +6,36 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockEntity class represents physical stock. Quantities of StockEntities
are represented by StockHoldings.
=end
-class Stock
class Entity < Ohm::Model
attribute :description
index :description
#set :sale_entities, SaleEntity
- collection :stock_holdings, StockHolding, :entity
- list :stock_movements, StockMovement
+ collection :holdings, Holding, :entity
+ list :movements, Movement
def create_holdings
end
- def holding(name)
- stock_holdings.find(:name => name).first || StockHolding.create(name: name, entity: self)
- end
-
def move(source_target, dest_target, qty, ucost)
movement = StockMovement.move_no_cleanup(source_target: source_target,
- source_stke_id: id,
+ source_entity_id: id,
source_amount: Amount.new(qty, ucost),
dest_target: dest_target,
- dest_stke_id: id)
+ dest_entity_id: id)
# Return the completion status of the movement
stock_movements << movement
movement.completed
end
- end
+
+ def self.const_missing(name)
+ Stock.const_get(name)
+ end
+ end
end
-#end
View
26 lib/porp/stock/holder.rb
@@ -6,18 +6,32 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockHolder class represents holders of stock, such as outlets, stock rooms,
warehouses, etc.
=end
-class Stock
class Holder < Ohm::Model
+ include Ohm::FindAdditions
+
+ attribute :name
+ index :name
attribute :description
- index :description
- collection :stock_holdings, StockHolding, :holder
+ collection :holdings, ->(id) {Holding[id]}, :holder
+
+ def validate
+ assert_unique :name
+ end
+
+ # Ensure that a holder object corresponding to each passed string or symbol
+ # exists, creating it if not
+ #
+ # @param holders Collection of strings or symbols corresponding
+ # to names of holders
+ def self.ensure_extant(holders)
+ holders = [holders] unless holders.kind_of?(Array)
+ holders.each {|name| find_or_create(name: name)}
+ end
end
end
-#end
View
31 lib/porp/stock/holding.rb
@@ -6,29 +6,27 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
-The StockHolding class represents a collection of holdings of physical stock.
-A StockHolding is a queue of StockHoldingEntries, each of which represents a
-quantity of StockEntities. StockHoldingEntries are created and altered by
-StockMovements, and are never destroyed. The lifetime of a StockHoldingEntry
+The Holding class represents a collection of holdings of physical stock.
+A Holding is a queue of HoldingEntries, each of which represents a
+quantity of Entities. HoldingEntries are created and altered by
+Movements, and are never destroyed. The lifetime of a HoldingEntry
will start with a receipt of stock (e.g. a GRN against a PO), and over time
-the stock represented by the StockHoldingEntry will be reduced by issues (e.g.
+the stock represented by the HoldingEntry will be reduced by issues (e.g.
sales). Once a holding reaches zero it will normally be archived as part of
the stock movement audit trail.
-StockHoldingEntries can be effectively merged by performing StockMovements from
-the original StockHoldingEntries to a new StockHoldingEntry, and can likewise
+HoldingEntries can be effectively merged by performing Movements from
+the original HoldingEntries to a new HoldingEntry, and can likewise
be split in similar fashion.
=end
-class Stock
class Holding < MovementTarget
- reference :entity, StockEntity
- reference :holder, StockHolder
- reference :status, StockStatus
- list :entries, StockHoldingEntry
- list :defunct_entries, StockHoldingEntry
+ reference :entity, ->(id) {Entity[id]}
+ reference :holder, ->(id) {Holder[id]}
+ reference :status, ->(id) {Status[id]}
+ list :entries, ->(id) {HoldingEntry[id]}
+ list :defunct_entries, ->(id) {HoldingEntry[id]}
attribute :name
index :name
@@ -91,7 +89,7 @@ def receive(movement)
end
end
- class StockHoldingEntry < Ohm::Model
+ class HoldingEntry < Ohm::Model
include Ohm::Callbacks
include Ohm::Struct
@@ -130,5 +128,4 @@ def mtime_update
end
end
end
-#end
View
13 lib/porp/stock/movement.rb
@@ -6,24 +6,22 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockMovement class represents movements of physical stock by moving
quantities and cost value from one MovementTarget to another.
=end
-class Stock
class Movement < Ohm::Model
include Ohm::Looseref
include Ohm::Struct
include Ohm::Locking
looseref :source_target, MovementTarget
- reference :source_stke, StockEntity
+ reference :source_entity, Entity
struct :source_amount, Amount
looseref :dest_target, MovementTarget
- reference :dest_stke, StockEntity
+ reference :dest_entity, Entity
struct :dest_amount, Amount
attribute :creation_time
@@ -62,7 +60,7 @@ def initialize(attrs = {})
# Destination stock entity is assumed to be the same as the source
# stock entity if not supplied
- self.dest_stke_id ||= self.source_stke_id
+ self.dest_entity_id ||= self.source_entity_id
# Destination quantity is assumed to be the same as the source
# quantity if not supplied.
@@ -81,7 +79,7 @@ def validate
assert_present :source_target_klass
assert_present :dest_target_id
assert_present :dest_target_klass
- assert_present :source_stke_id
+ assert_present :source_entity_id
end
# Commit the movement. If the movement fails, destroy the inconsistent
@@ -150,5 +148,4 @@ def move_no_cleanup(*args)
end
end
end
-#end
View
13 lib/porp/stock/movementtarget.rb
@@ -6,15 +6,13 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
-The MovementTarget class represents everything that can be the target of a
-stock movement. It is subclassed by StockSourceDest, which represents
-targets which create and destroy stock, and by StockHolding, which
-represents physical stock holdings.
+ The MovementTarget class represents everything that can be the target of a
+ stock movement. It is subclassed by StockSourceDest, which represents
+ targets which create and destroy stock, and by StockHolding, which
+ represents physical stock holdings.
=end
-class Stock
class MovementTarget < Ohm::Model
# If inherited ensure the child gets attributes defined in higher classes
@@ -44,5 +42,4 @@ def reload_attributes
end
end
end
-#end
View
5 lib/porp/stock/sourcedest.rb
@@ -6,14 +6,12 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockSourceDest class represents movement targets which create or
destroy stock. Class implements a default source/destination issue and receipt
behaviour
=end
-class Stock
class SourceDest < MovementTarget
include Ohm::Locking
@@ -105,4 +103,3 @@ def receive(movement)
end
end
end
-#end
View
27 lib/porp/stock/status.rb
@@ -6,18 +6,31 @@
#
# License: MIT (see LICENSE file)
-#module Porp
-
+class Stock
=begin
The StockStatus class represents different states can hold, such as 'in stock',
'on order', etc
=end
-class Stock
class Status < Ohm::Model
+ include Ohm::FindAdditions
+
+ attribute :name
+ index :name
attribute :description
- index :description
- collection :stock_holdings, StockHolding, :status
+ collection :holdings, Holding, :status
+
+ def validate
+ assert_unique :name
+ end
+
+ # Ensure that a status object corresponding to each passed string or symbol
+ # exists, creating it if not
+ #
+ # @param statuses Collection of strings or symbols corresponding to names
+ # of statuses
+ def self.ensure_extant(statuses)
+ statuses = [statuses] unless statuses.kind_of?(Array)
+ statuses.each {|name| find_or_create(name: name.to_s)}
+ end
end
end
-#end
-
Please sign in to comment.
Something went wrong with that request. Please try again.