cats_core 1.4.20 → 1.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -47
  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: d78b456ef9b9082680246207509f81b33ba4c9584136bafc00ac1dd9085e77cb
4
- data.tar.gz: f1138cb24c4f94d7c42dd84b15916762c1f27c321531b1057f2b9e96a2da5c75
3
+ metadata.gz: 3c58aa13420d9d6df2e5954237e8d8fbfef4db3d620636a49bba9ba2c6d30eb2
4
+ data.tar.gz: 6ff8e9bc22538cef900ed1e4ef1d80620699f5b94df2bbf8771f37cf4b0367b1
5
5
  SHA512:
6
- metadata.gz: 74b5f1fc12c9851dd07224c07b6a00912d44012f2347f87b3264cad20882f5950b9d27d9820c3c0b1526e26f32dfb83ee16263d77b4bcf2efdcd8f5ca30aaef9
7
- data.tar.gz: 349b6c6a81040bf195ea723e9920f97711be1dce7c4257a5c7f03459d4b0cebd3d59ae650265ed952791c85330891df00d51c621c81c4948c85819f30ce07903
6
+ metadata.gz: caf7a1b4209a0405e22dbf86736424ce82f577ded281348911faed27a1cbc9e232c35eea14cdb768d2655aaa8dcce9fda5362ac3c60ec088b13522a191eeabbc
7
+ data.tar.gz: 2992ab176cd20d2e82a56ac9fc178d5aa06cc8870aa67c5c545f42804c9d31dc082203ef5cdebae968b69aee147689056d06401cffe35f4d566d0c5026bf470c
@@ -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,43 +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 :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
19
23
 
20
24
  validates :reference_no, :plate_no, :driver_name, :driver_phone, :quantity, :commodity_status, presence: true
21
25
  validates :dispatch_status, presence: true, inclusion: { in: DISPATCH_STATUSES }
22
26
  validates :reference_no, uniqueness: true
23
27
  validates :quantity, numericality: { greater_than_or_equal_to: 0 }
24
28
  validates :commodity_status, inclusion: { in: Cats::Core::Commodity::COMMODITY_STATUSES }
25
- validate :validate_quantity, :validate_dispatch_plan_status
29
+ validate :validate_dispatch_plan_status
26
30
 
27
31
  delegate(:name, to: :transporter, prefix: true)
28
32
  delegate(:email, to: :prepared_by, prefix: true)
29
33
 
30
- def total_quantity
31
- dispatch_transactions.sum(:quantity)
32
- end
33
-
34
- def validate_quantity
35
- return unless quantity && dispatch_plan_item
36
-
37
- return unless quantity_changed?
38
-
39
- dispatched = dispatch_plan_item.dispatches.sum(:quantity)
40
- remaining = dispatch_plan_item.quantity - dispatched
41
- remaining += quantity_was if quantity_was
42
- errors.add(:quantity, "exceeds allocated quantity. Maximum allowed is #{remaining}") if quantity > remaining
34
+ def authorized_quantity
35
+ dispatch_authorizations.sum(:quantity)
43
36
  end
44
37
 
45
38
  def validate_dispatch_plan_status
@@ -52,42 +45,32 @@ module Cats
52
45
  def approve
53
46
  raise(StandardError, 'Dispatch has to be in draft state.') unless dispatch_status == Dispatch::DRAFT
54
47
 
55
- # Check if dispatch has transactions
56
- 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?
57
50
 
58
- Dispatch.transaction do
59
- # Commit transactions
60
- dispatch_transactions.each(&:commit)
61
- self.quantity = total_quantity
62
- self.dispatch_status = APPROVED
63
- 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
64
55
  end
65
- end
66
-
67
- def start
68
- 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?
69
59
 
70
- self.dispatch_status = STARTED
60
+ self.dispatch_status = APPROVED
71
61
  save!
72
62
  end
73
63
 
74
- def confirm
75
- 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
76
67
 
77
- unless total == quantity
78
- diff = (quantity - total).abs
79
- raise(
80
- StandardError,
81
- "There is an amount of #{diff} in the dispatch which is unaccounted for."
82
- )
83
- end
68
+ false
69
+ end
84
70
 
85
- Dispatch.transaction do
86
- self.dispatch_status = RECEIVED
87
- receipts.each { |r| r.status = Receipt::CONFIRMED }
88
- save!
89
- receipts.each(&:save!)
90
- end
71
+ def start
72
+ self.dispatch_status = STARTED
73
+ save!
91
74
  end
92
75
 
93
76
  def self.search_commodity(batch_no)
@@ -101,7 +84,7 @@ module Cats
101
84
  {
102
85
  batch_no: batch_no,
103
86
  commodity_name: commodity.name,
104
- quantity: dispatch.total_quantity,
87
+ quantity: dispatch.quantity,
105
88
  unit: commodity.unit_abbreviation,
106
89
  location: dispatch.transporter.name,
107
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