Class: Stock::Movement
- Inherits:
-
Ohm::Model
- Object
- Ohm::Model
- Stock::Movement
- Includes:
- Ohm::Locking, Ohm::Looseref, Ohm::Struct
- Defined in:
- lib/porp/stock/movement.rb
Overview
The StockMovement class represents movements of physical stock by moving quantities and cost value from one MovementTarget to another.
Class Method Summary (collapse)
-
+ (Object) move(*args)
Create a new movement and commit it.
-
+ (Object) move_no_cleanup(*args)
Create a new movement and commit it.
Instance Method Summary (collapse)
-
- (Movement) initialize(attrs = {})
constructor
Set defaults for attributes.
-
- (Object) move(*args)
Commit the movement.
-
- (Object) move_no_cleanup(*args)
Commit the movement.
-
- (Object) validate
Validation.
Constructor Details
- (Movement) initialize(attrs = {})
Set defaults for attributes
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/porp/stock/movement.rb', line 52 def initialize(attrs = {}) super(attrs) self.creation_time ||= Time.now.to_f # Source quantity and unit cost default to one and zero if not supplied. It is # expected that unit cost will be determined by the source # (may want to raise here in future if zero qty passed in) self.source_amount ||= Amount.new(1, 0) # Destination stock entity is assumed to be the same as the source # stock entity if not supplied self.dest_entity_id ||= self.source_entity_id # Destination quantity is assumed to be the same as the source # quantity if not supplied. # Destination unit cost is always calculated such that # source_amount.value == dest_amount.value self.dest_amount ||= Amount.new(self.source_amount.qty, 0) self.dest_amount.ucost = Rational(source_amount.value) / Integer(dest_amount.qty) end |
Class Method Details
+ (Object) move(*args)
Create a new movement and commit it
37 38 39 40 41 |
# File 'lib/porp/stock/movement.rb', line 37 def self.move(*args) movement = create(*args) movement.move() movement end |
+ (Object) move_no_cleanup(*args)
Create a new movement and commit it. If the commit fails, the uncommitted movement remains in the database.
45 46 47 48 49 |
# File 'lib/porp/stock/movement.rb', line 45 def self.move_no_cleanup(*args) # movement = create(*args) movement = new(*args) movement.move_no_cleanup() end |
Instance Method Details
- (Object) move(*args)
Commit the movement. If the movement fails, destroy the inconsistent movement and reraise. Wraps move_no_cleanup
87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/porp/stock/movement.rb', line 87 def move(*args) begin move_no_cleanup(*args) rescue Orp::MovementAlreadyComplete warn "Attempted to complete already completed movement" nil rescue delete raise end end |
- (Object) move_no_cleanup(*args)
Commit the movement. If the movement fails, the uncommitted movement will be left in the database for inspection
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/porp/stock/movement.rb', line 101 def move_no_cleanup(*args) # Abort if the movement is already complete raise Orp::MovementAlreadyComplete if completed raise Orp::MovementValidationError if !self.valid? self.update(*args) if args.length > 0 # This mutex prevents against two attempts to complete the same move # simultaneously. The movement remains editable throughout. # # BUT: Do we even need this? If the movement is only created just in time # then nothing else is going to have that instance # mutex(0.01) do # Commit the issue self.issue_committed = self.source_target.issue(self) ? true : false # Commit the receipt if the issue succeeded if self.issue_committed self.issue_time = Time.now.to_f # Recalculate the destination unit cost in case the source # overrided the source unit cost self.dest_amount.ucost = Rational(self.source_amount.value / self.dest_amount.qty) self.rcpt_committed = self.dest_target.receive(self) ? true : false else raise Orp::MovementIssueError end # Reverse the issue if the receipt failed if self.rcpt_committed self.rcpt_time = Time.now.to_f else self.issue_committed = self.source_target.reverse_issue(self) ? false : true if !self.issue_committed # If issue_committed is still true, then the issue reversal also # failed! raise Orp::MovementReverseIssueError else self.issue_time = nil raise Orp::MovementRcptError end end # Movement is now done self.completed = true self.completion_time = Time.now.to_f # end self.save end |
- (Object) validate
Validation. A movement must specify the following:
* Source target
* Destination target
* Source stock entity
77 78 79 80 81 82 83 |
# File 'lib/porp/stock/movement.rb', line 77 def validate assert_present :source_target_id assert_present :source_target_class assert_present :dest_target_id assert_present :dest_target_class assert_present :source_entity_id end |