Class: Stock::Holding
- Inherits:
-
MovementTarget
- Object
- Ohm::Model
- MovementTarget
- Stock::Holding
- Defined in:
- lib/porp/stock/holding.rb
Overview
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 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.
HoldingEntries can be effectively merged by performing Movements from the original HoldingEntries to a new HoldingEntry, and can likewise be split in similar fashion.
Class Method Summary (collapse)
Instance Method Summary (collapse)
-
- (Holding) initialize(attrs)
constructor
A new instance of Holding.
-
- (Object) issue(movement)
Issue stock from this holding.
-
- (Integer) qty
Calculates the total quantity of stock held in this holding from the holding entries.
-
- (Integer) quantity
Calculates the total quantity of stock held in this holding from the holding entries.
-
- (Object) receive(movement)
Receive stock on to this holding.
-
- (Object) reverse_issue(movement)
Can't do this yet.
-
- (String) to_s
String representation of the holding.
-
- (Rational) value
Calculates the total value of stock held in this holding from the holding entries.
Methods inherited from MovementTarget
Constructor Details
- (Holding) initialize(attrs)
A new instance of Holding
33 34 35 36 |
# File 'lib/porp/stock/holding.rb', line 33 def initialize(attrs) super(attrs) self.name ||= self.holder.name + "_" + self.status.name end |
Class Method Details
+ (Object) const_missing(name)
129 130 131 |
# File 'lib/porp/stock/holding.rb', line 129 def self.const_missing(name) Stock.const_get(name) end |
Instance Method Details
- (Object) issue(movement)
Issue stock from this holding. The issue proceeds as follows:
1) Issue stock from first entry. If there is sufficient stock, finish. 2) If there is insufficient stock on the first entry, continue issue
from the second entry, and so on until the issue is complete
3) If there is insufficient stock on all entries, create a negative
stock entry for the remainder
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/porp/stock/holding.rb', line 78 def issue(movement) movement_amount_remaining = movement.source_amount issued_value = 0 while movement_amount_remaining.int_qty > 0 do unless entries.empty? current_entry = entries.first if current_entry.amount_remaining.int_qty >= movement_amount_remaining.int_qty current_entry.amount_out.int_qty += movement_amount_remaining.int_qty issued_value += movement_amount_remaining.value current_entry.save #defunct_entries << entries.shift if current_entry.eol? # The below is equivalent to the above but avoids instantiating the # intermediate entry defunct_entries.key.rpush(entries.key.lpop) if current_entry.eol? break else movement_amount_remaining.int_qty -= current_entry.amount_remaining.int_qty issued_value += current_entry.amount_remaining.value current_entry.amount_out = current_entry.amount_in current_entry.save defunct_entries.key.rpush(entries.key.lpop) end else # Negative stock entry territory # We'll have a go at this later warn("Can't handle negatives!") break end end # Recalculate source amount unit cost movement.source_amount.ucost = issued_value / movement.source_amount.qty # Return qty issued movement.source_amount.qty end |
- (Integer) qty
Calculates the total quantity of stock held in this holding from the holding entries
50 51 52 |
# File 'lib/porp/stock/holding.rb', line 50 def qty quantity end |
- (Integer) quantity
Calculates the total quantity of stock held in this holding from the holding entries
43 44 45 46 47 |
# File 'lib/porp/stock/holding.rb', line 43 def quantity entries.all.inject(0) do |sum, entry| sum += entry.amount_remaining.int_qty end end |
- (Object) receive(movement)
Receive stock on to this holding. For the moment, we simply create a new entry each time - this may change to allow modifying entries in place
123 124 125 126 127 |
# File 'lib/porp/stock/holding.rb', line 123 def receive(movement) entry = HoldingEntry.create(amount_in: movement.dest_amount, stock_holding_id: id) entries << entry entry.amount_in.qty end |
- (Object) reverse_issue(movement)
Can't do this yet. Your receipts better not fail :-)
117 118 119 |
# File 'lib/porp/stock/holding.rb', line 117 def reverse_issue(movement) false end |
- (String) to_s
String representation of the holding
65 66 67 68 69 |
# File 'lib/porp/stock/holding.rb', line 65 def to_s "Holding: %s; H: %s; S: %s; N: %s; Qty: %s; Value: %s" % [ id, holder_id, status_id, name, quantity, value ] end |
- (Rational) value
Calculates the total value of stock held in this holding from the holding entries
58 59 60 61 62 |
# File 'lib/porp/stock/holding.rb', line 58 def value entries.all.inject(Rational(0)) do |sum, entry| sum += entry.amount_remaining.value end end |