cats_core 1.4.21 → 1.4.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/cats/core/dispatch_authorizations_controller.rb +35 -0
  3. data/app/controllers/cats/core/dispatch_plan_items_controller.rb +2 -2
  4. data/app/controllers/cats/core/dispatch_transactions_controller.rb +4 -4
  5. data/app/controllers/cats/core/dispatches_controller.rb +7 -2
  6. data/app/controllers/cats/core/lost_commodities_controller.rb +2 -2
  7. data/app/controllers/cats/core/receipt_authorizations_controller.rb +27 -0
  8. data/app/controllers/cats/core/receipt_transactions_controller.rb +2 -20
  9. data/app/controllers/cats/core/receipts_controller.rb +2 -28
  10. data/app/models/cats/core/authorization.rb +77 -0
  11. data/app/models/cats/core/dispatch.rb +30 -48
  12. data/app/models/cats/core/dispatch_authorization.rb +13 -0
  13. data/app/models/cats/core/dispatch_plan_item.rb +1 -0
  14. data/app/models/cats/core/dispatch_transaction.rb +39 -2
  15. data/app/models/cats/core/lost_commodity.rb +2 -3
  16. data/app/models/cats/core/receipt.rb +3 -37
  17. data/app/models/cats/core/receipt_authorization.rb +22 -0
  18. data/app/models/cats/core/receipt_transaction.rb +17 -14
  19. data/app/models/cats/core/transaction.rb +0 -30
  20. data/app/serializers/cats/core/dispatch_authorization_serializer.rb +8 -0
  21. data/app/serializers/cats/core/dispatch_plan_item_serializer.rb +1 -1
  22. data/app/serializers/cats/core/dispatch_transaction_serializer.rb +1 -2
  23. data/app/serializers/cats/core/lost_commodity_serializer.rb +1 -1
  24. data/app/serializers/cats/core/receipt_authorization_serializer.rb +8 -0
  25. data/app/serializers/cats/core/receipt_serializer.rb +1 -2
  26. data/app/serializers/cats/core/receipt_transaction_serializer.rb +1 -5
  27. data/app/services/cats/core/authorization_service.rb +25 -0
  28. data/config/routes.rb +26 -9
  29. data/db/migrate/20210718043401_create_cats_core_dispatch_plan_items.rb +1 -0
  30. data/db/migrate/20210718045516_create_cats_core_dispatches.rb +4 -0
  31. data/db/migrate/20210718055414_create_cats_core_dispatch_authorizations.rb +22 -0
  32. data/db/migrate/20210718202957_create_cats_core_dispatch_transactions.rb +9 -3
  33. data/db/migrate/20210727074646_create_cats_core_receipt_authorizations.rb +24 -0
  34. data/db/migrate/20210727105834_create_cats_core_receipts.rb +15 -0
  35. data/db/migrate/20210728041505_create_cats_core_lost_commodities.rb +3 -4
  36. data/db/migrate/20210814160628_create_cats_core_receipt_transactions.rb +3 -3
  37. data/lib/cats/core/version.rb +1 -1
  38. data/spec/factories/cats/core/dispatch_authorizations.rb +26 -0
  39. data/spec/factories/cats/core/dispatch_plan_items.rb +1 -0
  40. data/spec/factories/cats/core/dispatch_transactions.rb +2 -7
  41. data/spec/factories/cats/core/dispatches.rb +38 -3
  42. data/spec/factories/cats/core/lost_commodities.rb +1 -2
  43. data/spec/factories/cats/core/receipt_authorizations.rb +25 -0
  44. data/spec/factories/cats/core/receipt_transactions.rb +2 -22
  45. data/spec/factories/cats/core/receipts.rb +2 -9
  46. data/spec/factories/cats/core/stacks.rb +1 -1
  47. metadata +15 -4
  48. data/app/services/cats/core/receipt_service.rb +0 -48
  49. data/db/migrate/20210727074646_create_cats_core_receipts.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88b37ea0943ecb3db77fd873889665d4a58f990cb712a75ca258897cc448bc6c
4
- data.tar.gz: c4f897f6a36c0a5ce076d5ed144d342b82ef57ac8f3e226df6de84efa4f71daa
3
+ metadata.gz: 3dd00b147a4dfa4ab46e10d7961b4291fbdb39ed68e49a0766e69a4a4d22704a
4
+ data.tar.gz: 1c37f11f41f76263cb197cdd881b820475a38e8addf1424fce593287ab1e30ad
5
5
  SHA512:
6
- metadata.gz: 3c706230ac9911c572bdb611f18a65f7e61afaf01f94c4a901298391c7f54b7637be9b9b54b02a63e817e621916931fffe7eca14481ecca649fc21af0f1f553a
7
- data.tar.gz: c90b1ec96db1ccc7f20fae099366a4f1f5660905234a287ee7a6ccf7de16602e21c8dc653df35d1cfc5b1b722e8d2deace2374685a2c42b08263d3b113790c48
6
+ metadata.gz: ce78191aa4dde60ba521f32f95a87088f7e9380fcba3e3a05f2cb570c4fd722b6b1962ddff54ab76ea0278552b1640f3a72b0e96571e0160b096bc49b00abd56
7
+ data.tar.gz: f5e36206d35c62b26cd16bdcaeea9f11c711d90c2c68b6f5c5eeb4e174621c4ad6e8ef28f0082ad74cb505e82bbc44876cd67da9be92b045beebfd0f22c1b269
@@ -0,0 +1,35 @@
1
+ module Cats
2
+ module Core
3
+ class DispatchAuthorizationsController < ApplicationController
4
+ include Common
5
+
6
+ def index
7
+ super do
8
+ DispatchAuthorization.where(dispatch_id: params[:id])
9
+ end
10
+ end
11
+
12
+ def confirm
13
+ service = AuthorizationService.new
14
+ authorization = service.confirm(params[:id])
15
+ render json: { success: true, data: serialize(authorization) }
16
+ rescue StandardError => e
17
+ render json: { success: false, error: e.message }
18
+ end
19
+
20
+ def storekeeper_authorizations
21
+ storekeeper = User.find(params[:id])
22
+ stores = storekeeper.stores
23
+ authorizations = DispatchAuthorization.joins(:dispatch)
24
+ .where(store: stores, dispatch: { dispatch_status: Dispatch::APPROVED })
25
+ render json: { success: true, data: serialize(authorizations) }
26
+ end
27
+
28
+ private
29
+
30
+ def model_params
31
+ params.require(:payload).permit(:dispatch_id, :store_id, :quantity, :status, :authorized_by_id)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -17,8 +17,8 @@ module Cats
17
17
  private
18
18
 
19
19
  def model_params
20
- params.require(:payload).permit(:dispatch_plan_id, :source_id, :destination_id, :quantity, :commodity_status,
21
- :status, :commodity_id)
20
+ params.require(:payload).permit(:reference_no, :dispatch_plan_id, :source_id, :destination_id, :quantity,
21
+ :commodity_status, :status, :commodity_id)
22
22
  end
23
23
  end
24
24
  end
@@ -5,7 +5,7 @@ module Cats
5
5
 
6
6
  def index
7
7
  super do
8
- DispatchTransaction.where(dispatch_id: params[:id])
8
+ DispatchTransaction.where(dispatch_authorization_id: params[:id])
9
9
  end
10
10
  end
11
11
 
@@ -14,8 +14,8 @@ module Cats
14
14
  # stack is a dummy one and we need to use that without waiting for the
15
15
  # user to give us a source.
16
16
  def create_allocation
17
- dispatch = Dispatch.find(model_params[:dispatch_id])
18
- commodity = dispatch.dispatch_plan_item.commodity
17
+ authorization = DispatchAuthorization.find(model_params[:dispatch_authorization_id])
18
+ commodity = authorization.dispatch.dispatch_plan_item.commodity
19
19
  transaction = DispatchTransaction.new(model_params)
20
20
 
21
21
  # Fetch supplier stack by commodity
@@ -32,7 +32,7 @@ module Cats
32
32
  private
33
33
 
34
34
  def model_params
35
- params.require(:payload).permit(:source_id, :dispatch_id, :transaction_date, :quantity)
35
+ params.require(:payload).permit(:source_id, :dispatch_authorization_id, :transaction_date, :quantity)
36
36
  end
37
37
  end
38
38
  end
@@ -43,6 +43,11 @@ module Cats
43
43
  render json: { success: false, error: e.message }
44
44
  end
45
45
 
46
+ def filter
47
+ query = Dispatch.ransack(params[:q])
48
+ render json: { success: true, data: serialize(query.result) }
49
+ end
50
+
46
51
  def search
47
52
  data = @service.search(current_user, params[:status])
48
53
  render json: { success: true, data: serialize(data) }
@@ -60,8 +65,8 @@ module Cats
60
65
  end
61
66
 
62
67
  def model_params
63
- params.require(:payload).permit(:reference_no, :dispatch_plan_item_id, :transporter_id, :plate_no,
64
- :driver_name, :driver_phone, :quantity, :remark)
68
+ params.require(:payload).permit(:reference_no, :dispatch_plan_item_id, :transporter_id, :plate_no, :unit_id,
69
+ :driver_name, :driver_phone, :remark)
65
70
  end
66
71
  end
67
72
  end
@@ -5,12 +5,12 @@ module Cats
5
5
 
6
6
  def index
7
7
  super do
8
- LostCommodity.where(dispatch_id: params[:id])
8
+ LostCommodity.where(receipt_authorization_id: params[:id])
9
9
  end
10
10
  end
11
11
 
12
12
  def model_params
13
- params.require(:payload).permit(:dispatch_id, :quantity, :commodity_status, :remark)
13
+ params.require(:payload).permit(:receipt_authorization_id, :quantity, :remark)
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,27 @@
1
+ module Cats
2
+ module Core
3
+ class ReceiptAuthorizationsController < ApplicationController
4
+ include Common
5
+
6
+ def index
7
+ super do
8
+ ReceiptAuthorization.where(dispatch_id: params[:id])
9
+ end
10
+ end
11
+
12
+ def confirm
13
+ authorization = ReceiptAuthorization.find(params[:id])
14
+ authorization.confirm
15
+ render json: { success: true, data: serialize(authorization) }
16
+ rescue StandardError => e
17
+ render json: { success: false, error: e.message }
18
+ end
19
+
20
+ private
21
+
22
+ def model_params
23
+ params.require(:payload).permit(:dispatch_id, :store_id, :quantity, :remark, :status, :authorized_by_id)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -5,32 +5,14 @@ module Cats
5
5
 
6
6
  def index
7
7
  super do
8
- ReceiptTransaction.where(receipt_id: params[:id])
9
- end
10
- end
11
-
12
- def create
13
- super do
14
- p = model_params
15
-
16
- # Look for a transaction with the same destination as incoming
17
- transaction = ReceiptTransaction.find_by(
18
- receipt_id: p[:receipt_id],
19
- destination_id: p[:destination_id]
20
- )
21
- if transaction
22
- transaction.quantity += p[:quantity]
23
- else
24
- transaction = ReceiptTransaction.new(p)
25
- end
26
- transaction
8
+ ReceiptTransaction.joins(receipt_authorization: :dispatch).where(receipt_authorization_id: params[:id])
27
9
  end
28
10
  end
29
11
 
30
12
  private
31
13
 
32
14
  def model_params
33
- params.require(:payload).permit(:receipt_id, :destination_id, :transaction_date, :quantity)
15
+ params.require(:payload).permit(:receipt_authorization_id, :destination_id, :transaction_date, :quantity)
34
16
  end
35
17
  end
36
18
  end
@@ -5,40 +5,14 @@ module Cats
5
5
 
6
6
  def index
7
7
  super do
8
- Receipt.where(dispatch_id: params[:id])
8
+ Receipt.where(receipt_authorization_id: params[:id])
9
9
  end
10
10
  end
11
11
 
12
- def create
13
- super do
14
- service = ReceiptService.new
15
- receipt = service.init(model_params)
16
- receipt
17
- end
18
- end
19
-
20
- def start_stacking
21
- receipt = Receipt.find(params[:id])
22
- service = ReceiptService.new
23
- result = service.start_stacking(receipt)
24
- render json: { success: true, data: serialize(result) }
25
- rescue StandardError => e
26
- render json: { success: false, error: e.message }
27
- end
28
-
29
- def finish_stacking
30
- receipt = Receipt.find(params[:id])
31
- service = ReceiptService.new
32
- result = service.finish_stacking(receipt)
33
- render json: { success: true, data: serialize(result) }
34
- rescue StandardError => e
35
- render json: { success: false, error: e.message }
36
- end
37
-
38
12
  private
39
13
 
40
14
  def model_params
41
- params.require(:payload).permit(:dispatch_id, :quantity, :commodity_status, :status, :remark, :prepared_by_id)
15
+ params.require(:payload).permit(:receipt_authorization_id, :commodity_status, :quantity, :remark)
42
16
  end
43
17
  end
44
18
  end
@@ -0,0 +1,77 @@
1
+ module Cats
2
+ module Core
3
+ class Authorization < ApplicationRecord
4
+ self.abstract_class = true
5
+ after_initialize :set_status
6
+
7
+ # Authorization statuses
8
+ AUTHORIZED = 'Authorized'.freeze
9
+ CONFIRMED = 'Confirmed'.freeze
10
+ STATUSES = [AUTHORIZED, CONFIRMED].freeze
11
+
12
+ belongs_to :dispatch
13
+ belongs_to :store
14
+ belongs_to :authorized_by, class_name: 'Cats::Core::User'
15
+
16
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
17
+ validates :status, presence: true, inclusion: { in: STATUSES }
18
+ validate :validate_dispatch_status, if: -> { status == AUTHORIZED }
19
+
20
+ delegate(:full_name, to: :authorized_by, prefix: :authorizer)
21
+ delegate(:name, to: :store, prefix: true)
22
+ delegate(:reference_no, to: :dispatch, prefix: true)
23
+ delegate(:dispatch_status, to: :dispatch)
24
+ delegate(:plate_no, to: :dispatch)
25
+ delegate(:driver_name, to: :dispatch)
26
+
27
+ def validate_dispatch_status
28
+ return unless dispatch
29
+
30
+ if instance_of?(DispatchAuthorization)
31
+ errors.add(:dispatch, 'is not in draft state.') unless dispatch.dispatch_status == Dispatch::DRAFT
32
+ else
33
+ return if dispatch.dispatch_status == Cats::Core::Dispatch::STARTED
34
+
35
+ errors.add(:dispatch, 'should be in "Started" state.')
36
+ end
37
+ end
38
+
39
+ # A convenience method to return dispatch or receipt transactions based on the
40
+ # authorization type.
41
+ def transactions
42
+ return dispatch_transactions if instance_of?(DispatchAuthorization)
43
+
44
+ return receipt_transactions if instance_of?(ReceiptAuthorization)
45
+ end
46
+
47
+ def confirm
48
+ if instance_of?(DispatchAuthorization)
49
+ raise(StandardError, 'Authorization does not have transactions.') if transactions.blank?
50
+
51
+ transactions.each(&:commit)
52
+ # count = transactions.where(status: Transaction::DRAFT).count
53
+ # raise(StandardError, 'There are uncommitted transactions.') if count.positive?
54
+ elsif instance_of?(ReceiptAuthorization)
55
+ raise(StandardError, 'Authorization does not have receipts.') unless receipts.count.positive?
56
+
57
+ total_received = receipts.sum(:quantity)
58
+ total_lost = lost_commodities.sum(:quantity)
59
+ diff = quantity - (total_received + total_lost)
60
+ raise(StandardError, "A quantity of #{diff} is unaccounted for.") unless diff.zero?
61
+
62
+ self.received_quantity = total_received
63
+ end
64
+ self.status = CONFIRMED
65
+ save!
66
+ end
67
+
68
+ private
69
+
70
+ def set_status
71
+ return unless new_record?
72
+
73
+ self.status = AUTHORIZED
74
+ end
75
+ end
76
+ end
77
+ end
@@ -3,44 +3,36 @@ module Cats
3
3
  class Dispatch < ApplicationRecord
4
4
  DRAFT = 'Draft'.freeze
5
5
  APPROVED = 'Approved'.freeze
6
+ READY_TO_START = 'Ready to Start'.freeze
6
7
  STARTED = 'Started'.freeze
7
8
  ARRIVED = 'Arrived'.freeze
8
9
  UNLOADED = 'Unloaded'.freeze
9
10
  RECEIVED = 'Received'.freeze
10
11
 
11
- DISPATCH_STATUSES = [DRAFT, APPROVED, STARTED, ARRIVED, UNLOADED, RECEIVED].freeze
12
+ DISPATCH_STATUSES = [DRAFT, APPROVED, READY_TO_START, STARTED, ARRIVED, UNLOADED, RECEIVED].freeze
12
13
 
13
14
  belongs_to :prepared_by, class_name: 'Cats::Core::User'
14
15
  belongs_to :transporter
15
16
  belongs_to :dispatch_plan_item
16
- has_many :receipts
17
- has_many :dispatch_transactions
18
- has_many :receipt_transactions, through: :receipts
19
- has_many :lost_commodities
17
+ belongs_to :unit, class_name: 'Cats::Core::UnitOfMeasure'
18
+
19
+ has_many :dispatch_authorizations
20
+ has_many :receipt_authorizations
21
+ has_many :dispatch_transactions, through: :dispatch_authorizations
22
+ has_many :receipt_transactions, through: :receipt_authorizations
20
23
 
21
24
  validates :reference_no, :plate_no, :driver_name, :driver_phone, :quantity, :commodity_status, presence: true
22
25
  validates :dispatch_status, presence: true, inclusion: { in: DISPATCH_STATUSES }
23
26
  validates :reference_no, uniqueness: true
24
27
  validates :quantity, numericality: { greater_than_or_equal_to: 0 }
25
28
  validates :commodity_status, inclusion: { in: Cats::Core::Commodity::COMMODITY_STATUSES }
26
- validate :validate_quantity, :validate_dispatch_plan_status
29
+ validate :validate_dispatch_plan_status
27
30
 
28
31
  delegate(:name, to: :transporter, prefix: true)
29
32
  delegate(:email, to: :prepared_by, prefix: true)
30
33
 
31
- def total_quantity
32
- dispatch_transactions.sum(:quantity)
33
- end
34
-
35
- def validate_quantity
36
- return unless quantity && dispatch_plan_item
37
-
38
- return unless quantity_changed?
39
-
40
- dispatched = dispatch_plan_item.dispatches.sum(:quantity)
41
- remaining = dispatch_plan_item.quantity - dispatched
42
- remaining += quantity_was if quantity_was
43
- errors.add(:quantity, "exceeds allocated quantity. Maximum allowed is #{remaining}") if quantity > remaining
34
+ def authorized_quantity
35
+ dispatch_authorizations.sum(:quantity)
44
36
  end
45
37
 
46
38
  def validate_dispatch_plan_status
@@ -53,42 +45,32 @@ module Cats
53
45
  def approve
54
46
  raise(StandardError, 'Dispatch has to be in draft state.') unless dispatch_status == Dispatch::DRAFT
55
47
 
56
- # Check if dispatch has transactions
57
- raise(StandardError, 'Dispatch has no transactions.') unless dispatch_transactions.count.positive?
48
+ # Check if dispatch has authorizations
49
+ raise(StandardError, 'Dispatch has no authorizations.') unless dispatch_authorizations.count.positive?
58
50
 
59
- Dispatch.transaction do
60
- # Commit transactions
61
- dispatch_transactions.each(&:commit)
62
- self.quantity = total_quantity
63
- self.dispatch_status = APPROVED
64
- save!
51
+ dispatches = Dispatch.where(dispatch_plan_item: dispatch_plan_item)
52
+ authorized = dispatches.inject(0) do |result, dispatch|
53
+ result += dispatch.authorized_quantity
54
+ result
65
55
  end
66
- end
67
-
68
- def start
69
- raise(StandardError, 'Dispatch has to be approved first.') unless dispatch_status == Dispatch::APPROVED
56
+ diff = authorized - dispatch_plan_item.quantity
57
+ error = "Total authorized quantity exceeds allocated quantity (Extra = #{diff})."
58
+ raise(StandardError, error) if diff.positive?
70
59
 
71
- self.dispatch_status = STARTED
60
+ self.dispatch_status = APPROVED
72
61
  save!
73
62
  end
74
63
 
75
- def confirm
76
- total = receipts.sum(:quantity)
64
+ def all_authorizations_confirmed?
65
+ statuses = dispatch_authorizations.map(&:status).uniq
66
+ return true if statuses.length == 1 && statuses[0] == Authorization::CONFIRMED
77
67
 
78
- unless total == quantity
79
- diff = (quantity - total).abs
80
- raise(
81
- StandardError,
82
- "There is an amount of #{diff} in the dispatch which is unaccounted for."
83
- )
84
- end
68
+ false
69
+ end
85
70
 
86
- Dispatch.transaction do
87
- self.dispatch_status = RECEIVED
88
- receipts.each { |r| r.status = Receipt::CONFIRMED }
89
- save!
90
- receipts.each(&:save!)
91
- end
71
+ def start
72
+ self.dispatch_status = STARTED
73
+ save!
92
74
  end
93
75
 
94
76
  def self.search_commodity(batch_no)
@@ -102,7 +84,7 @@ module Cats
102
84
  {
103
85
  batch_no: batch_no,
104
86
  commodity_name: commodity.name,
105
- quantity: dispatch.total_quantity,
87
+ quantity: dispatch.quantity,
106
88
  unit: commodity.unit_abbreviation,
107
89
  location: dispatch.transporter.name,
108
90
  location_detail: "Plate No.: #{dispatch.plate_no}, Driver: #{dispatch.driver_name}",
@@ -0,0 +1,13 @@
1
+ module Cats
2
+ module Core
3
+ class DispatchAuthorization < Authorization
4
+ has_many :dispatch_transactions
5
+
6
+ def validate_dispatch_status
7
+ return unless dispatch
8
+
9
+ errors.add(:dispatch, 'is not in draft state.') unless dispatch.dispatch_status == Dispatch::DRAFT
10
+ end
11
+ end
12
+ end
13
+ end
@@ -15,6 +15,7 @@ module Cats
15
15
  has_many :dispatches
16
16
  has_many :hub_authorizations
17
17
 
18
+ validates :reference_no, presence: true, uniqueness: true
18
19
  validates :commodity_status, presence: true, inclusion: { in: Commodity::COMMODITY_STATUSES }
19
20
  validates :quantity, presence: true, numericality: { greater_than: 0 }
20
21
  validates :status, presence: true, inclusion: { in: STATUSES }
@@ -2,10 +2,13 @@ module Cats
2
2
  module Core
3
3
  class DispatchTransaction < Transaction
4
4
  belongs_to :source, class_name: 'Cats::Core::Stack'
5
- belongs_to :dispatch
5
+ belongs_to :dispatch_authorization
6
+
7
+ validates :source_id, uniqueness: { scope: :dispatch_authorization_id }
8
+ validate :validate_dispatch
9
+ validate :validate_source_quantity, :validate_authorized_quantity, unless: :skip_quantity_validation
6
10
 
7
11
  delegate(:code, to: :source, prefix: true)
8
- delegate(:reference_no, to: :dispatch, prefix: true)
9
12
 
10
13
  def commit
11
14
  Transaction.transaction do
@@ -16,6 +19,40 @@ module Cats
16
19
  save!
17
20
  end
18
21
  end
22
+
23
+ def validate_dispatch
24
+ return unless dispatch_authorization
25
+
26
+ status = dispatch_authorization.dispatch.dispatch_status
27
+ errors.add(:base, 'Dispatch must be approved first.') if status == Dispatch::DRAFT
28
+ end
29
+
30
+ def validate_source_quantity
31
+ return unless quantity && source
32
+
33
+ total = DispatchTransaction.where(status: DRAFT, source: source).sum(:quantity)
34
+ diff = quantity - (source.quantity - total)
35
+ diff -= quantity_was if quantity_was
36
+ errors.add(:quantity, "exceeds source quantity by #{diff}") if diff.positive?
37
+ end
38
+
39
+ def validate_authorized_quantity
40
+ return unless quantity && dispatch_authorization
41
+
42
+ total = DispatchTransaction.where(dispatch_authorization: dispatch_authorization).sum(:quantity)
43
+ diff = dispatch_authorization.quantity - total
44
+ diff += quantity_was if quantity_was
45
+ errors.add(:quantity, "exceeds authorized quantity (Max. = #{diff}).") if quantity > diff
46
+ end
47
+
48
+ def skip_quantity_validation
49
+ # Quantity validation should be skipped if dispatch is already started.
50
+ return true unless dispatch_authorization
51
+
52
+ return true if status == COMMITTED
53
+
54
+ ![Dispatch::DRAFT, Dispatch::APPROVED].include?(dispatch_authorization.dispatch.dispatch_status)
55
+ end
19
56
  end
20
57
  end
21
58
  end
@@ -1,10 +1,9 @@
1
1
  module Cats
2
2
  module Core
3
3
  class LostCommodity < ApplicationRecord
4
- belongs_to :dispatch
4
+ belongs_to :receipt_authorization
5
5
 
6
- validates :quantity, presence: true
7
- validates :commodity_status, presence: true, inclusion: { in: Commodity::COMMODITY_STATUSES }
6
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
8
7
  end
9
8
  end
10
9
  end
@@ -1,44 +1,10 @@
1
1
  module Cats
2
2
  module Core
3
3
  class Receipt < ApplicationRecord
4
- # Receipt status
5
- DRAFT = 'Draft'.freeze
6
- CONFIRMED = 'Confirmed'.freeze
7
- STACKING = 'Stacking'.freeze
8
- STACKED = 'Stacked'.freeze
9
- RECEIPT_STATUSES = [DRAFT, CONFIRMED, STACKING, STACKED].freeze
4
+ belongs_to :receipt_authorization
10
5
 
11
- belongs_to :dispatch
12
- belongs_to :prepared_by, class_name: 'Cats::Core::User'
13
- has_many :receipt_transactions
14
-
15
- validates :quantity, :commodity_status, :status, presence: true
16
- validates :quantity, numericality: { greater_than: 0 }
17
- validates :status, inclusion: { in: RECEIPT_STATUSES }
18
- validates :commodity_status, inclusion: { in: Cats::Core::Commodity::COMMODITY_STATUSES }
19
- validate :validate_dispatch_status, :validate_total_quantity
20
-
21
- delegate(:reference_no, to: :dispatch, prefix: true)
22
- delegate(:email, to: :prepared_by, prefix: true)
23
-
24
- def validate_dispatch_status
25
- return unless dispatch
26
-
27
- statuses = [Cats::Core::Dispatch::STARTED, Cats::Core::Dispatch::RECEIVED]
28
- return if statuses.include?(dispatch.dispatch_status)
29
-
30
- errors.add(:dispatch, 'should be in "Started" state.')
31
- end
32
-
33
- def validate_total_quantity
34
- return unless dispatch && quantity
35
-
36
- received = dispatch.receipts.sum(:quantity) + dispatch.lost_commodities.sum(:quantity)
37
- diff = dispatch.quantity - received
38
- diff += quantity_was if quantity_was
39
-
40
- errors.add(:quantity, "total is higher than dispatch quantity (Max = #{diff}).") if quantity > diff
41
- end
6
+ validates :commodity_status, presence: true, inclusion: { in: Commodity::COMMODITY_STATUSES }
7
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
42
8
  end
43
9
  end
44
10
  end
@@ -0,0 +1,22 @@
1
+ module Cats
2
+ module Core
3
+ class ReceiptAuthorization < Authorization
4
+ has_many :receipts
5
+ has_many :lost_commodities
6
+ has_many :receipt_transactions
7
+
8
+ validates :received_quantity, presence: true, numericality: { greater_than_or_equal_to: 0 }
9
+ validate :validate_total_quantity
10
+
11
+ def validate_total_quantity
12
+ return unless dispatch && quantity
13
+
14
+ received = dispatch.receipt_authorizations.sum(:quantity)
15
+ diff = dispatch.quantity - received
16
+ diff += quantity_was if quantity_was
17
+
18
+ errors.add(:quantity, "authorized is higher than dispatch quantity (Max = #{diff}).") if quantity > diff
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,27 +1,30 @@
1
1
  module Cats
2
2
  module Core
3
3
  class ReceiptTransaction < Transaction
4
- belongs_to :receipt
4
+ belongs_to :receipt_authorization
5
5
  belongs_to :destination, class_name: 'Cats::Core::Stack'
6
6
 
7
- delegate(:code, to: :destination, prefix: true)
7
+ validates :transaction_date, presence: true
8
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
9
+ validate :validate_receipt, :validate_quantity
8
10
 
9
- def validate_quantity
10
- return unless quantity.present? && destination.present? && receipt.present?
11
+ delegate(:code, to: :destination, prefix: true)
12
+ delegate(:dispatch_reference_no, to: :receipt_authorization)
11
13
 
12
- authorized = receipt.dispatch.dispatch_plan_item.hub_authorizations.where(
13
- store: destination.store
14
- ).sum(:quantity)
15
- received = self.class.joins(:destination).where(
16
- destination: { store_id: destination.store_id }
17
- ).sum(:quantity)
14
+ def validate_receipt
15
+ return unless receipt_authorization
18
16
 
19
- received -= quantity_was if quantity_was
17
+ status = receipt_authorization.status
18
+ errors.add(:receipt_authorization, 'must be confirmed.') if status == ReceiptAuthorization::AUTHORIZED
19
+ end
20
20
 
21
- # Get quantity in hub authorization for source
22
- available = authorized - received
21
+ def validate_quantity
22
+ return unless quantity && destination && receipt_authorization
23
23
 
24
- errors.add(:quantity, "exceeds authorized quantity (Max = #{available}).") if quantity > available
24
+ total = ReceiptTransaction.where(receipt_authorization: receipt_authorization).sum(:quantity)
25
+ diff = receipt_authorization.quantity - total
26
+ diff += quantity_was if quantity_was
27
+ errors.add(:quantity, "exceeds authorized quantity (Max. = #{diff}).") if quantity > diff
25
28
  end
26
29
 
27
30
  def commit