cats_core 1.4.0 → 1.4.3
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.
- checksums.yaml +4 -4
- data/app/controllers/cats/core/commodities_controller.rb +1 -1
- data/app/controllers/cats/core/commodity_categories_controller.rb +5 -0
- data/app/controllers/cats/core/monthly_plans_controller.rb +3 -5
- data/app/controllers/cats/core/stacks_controller.rb +7 -0
- data/app/controllers/cats/core/unit_conversions_controller.rb +13 -0
- data/app/models/cats/core/commodity.rb +0 -21
- data/app/models/cats/core/dispatch_plan.rb +27 -0
- data/app/models/cats/core/gift_certificate.rb +5 -2
- data/app/models/cats/core/location.rb +3 -1
- data/app/models/cats/core/monthly_plan.rb +24 -2
- data/app/models/cats/core/plan.rb +1 -0
- data/app/models/cats/core/rhn_request.rb +13 -1
- data/app/models/cats/core/route.rb +9 -1
- data/app/models/cats/core/stack.rb +31 -4
- data/app/models/cats/core/transport_bid.rb +5 -1
- data/app/models/cats/core/transport_bid_item.rb +1 -0
- data/app/models/cats/core/transport_offer.rb +1 -0
- data/app/models/cats/core/unit_conversion.rb +25 -0
- data/app/models/concerns/cats/core/dispatchable.rb +2 -2
- data/app/serializers/cats/core/commodity_serializer.rb +1 -1
- data/app/serializers/cats/core/unit_conversion_serializer.rb +7 -0
- data/app/services/cats/core/dispatch_plan_service.rb +1 -1
- data/app/services/cats/core/dispatch_service.rb +16 -9
- data/app/services/cats/core/stack_service.rb +29 -32
- data/config/routes.rb +3 -0
- data/db/migrate/20210717031810_create_cats_core_plans.rb +3 -0
- data/db/migrate/20210717032602_create_cats_core_gift_certificates.rb +6 -1
- data/db/migrate/20210717033223_create_cats_core_commodities.rb +1 -0
- data/db/migrate/20211215121151_create_cats_core_transport_bids.rb +3 -2
- data/db/migrate/20211215124452_create_cats_core_transport_bid_items.rb +4 -0
- data/db/migrate/20211229160125_create_cats_core_transport_offers.rb +2 -0
- data/db/migrate/20220416143416_create_cats_core_unit_conversions.rb +19 -0
- data/lib/cats/core/version.rb +1 -1
- data/spec/factories/cats/core/commodities.rb +1 -0
- data/spec/factories/cats/core/dispatch_plans.rb +14 -1
- data/spec/factories/cats/core/gift_certificates.rb +2 -0
- data/spec/factories/cats/core/plans.rb +2 -0
- data/spec/factories/cats/core/stacking_rules.rb +3 -3
- data/spec/factories/cats/core/transport_bid_items.rb +1 -0
- data/spec/factories/cats/core/transport_bids.rb +3 -2
- data/spec/factories/cats/core/unit_conversions.rb +7 -0
- metadata +8 -9
- data/app/models/cats/core/allocation.rb +0 -78
- data/app/models/cats/core/allocation_item.rb +0 -42
- data/db/migrate/20210718042823_create_cats_core_allocations.rb +0 -25
- data/db/migrate/20210718043204_create_cats_core_allocation_items.rb +0 -19
- data/spec/factories/cats/core/allocation_items.rb +0 -9
- data/spec/factories/cats/core/allocations.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2624794cc9a3787caefb212037b9f33f8964e9d5512fcc40a7dbb31fa129009
|
4
|
+
data.tar.gz: 0f2d9b94ca4dc8794fe89928449b6c26cac67a78e2ffffadfa5be8d49baef52c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0f3fc9431f4b011ca5015f9bf288627b51b547d79323e48d1f0221baae4781766b63ab521c23652feda8cd2fefb7f17a2242166ace64875cf4418138dc37c54
|
7
|
+
data.tar.gz: c9b0ec89851d48c0fcc9338dd749364d4a1a66e0a4291085c59f70b73605f5b47b4831cbfd03d01683f0b1ed829309247069f0878b6bc199cf2791936e416978
|
@@ -34,7 +34,7 @@ module Cats
|
|
34
34
|
def model_params
|
35
35
|
params.require(:payload).permit(:batch_no, :description, :unit_of_measure_id, :source_id,
|
36
36
|
:source_type, :quantity, :best_use_before, :volume_per_metric_ton,
|
37
|
-
:arrival_status)
|
37
|
+
:arrival_status, :shipping_reference)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -9,6 +9,11 @@ module Cats
|
|
9
9
|
render json: { success: true, data: data }
|
10
10
|
end
|
11
11
|
|
12
|
+
def all
|
13
|
+
data = Cats::Core::CommodityCategory.all
|
14
|
+
render json: { success: true, data: data }
|
15
|
+
end
|
16
|
+
|
12
17
|
def root
|
13
18
|
data = Cats::Core::CommodityCategory.roots
|
14
19
|
render json: { success: true, data: serialize(data) }
|
@@ -17,12 +17,10 @@ module Cats
|
|
17
17
|
|
18
18
|
def approve
|
19
19
|
plan = Cats::Core::MonthlyPlan.find(params[:id])
|
20
|
-
|
21
|
-
render json: { success: false, error: 'Empty plan cannot be approved.' }, status: :unprocessable_entity
|
22
|
-
return
|
23
|
-
end
|
24
|
-
plan.update(status: Cats::Core::Plan::APPROVED)
|
20
|
+
plan.approve
|
25
21
|
render json: { success: true, data: serialize(plan) }
|
22
|
+
rescue StandardError => e
|
23
|
+
render json: { success: false, error: e.message }, status: :unprocessable_entity
|
26
24
|
end
|
27
25
|
|
28
26
|
def generate
|
@@ -19,6 +19,13 @@ module Cats
|
|
19
19
|
render json: { success: true, data: serialize(commodities) }
|
20
20
|
end
|
21
21
|
|
22
|
+
def dispatch_stacks
|
23
|
+
service = StackService.new
|
24
|
+
dispatch = Dispatch.find(params[:id])
|
25
|
+
stacks = service.dispatch_stacks(dispatch)
|
26
|
+
render json: { success: true, data: serialize(stacks) }
|
27
|
+
end
|
28
|
+
|
22
29
|
def receipt_stacks
|
23
30
|
service = StackService.new
|
24
31
|
receipt = Receipt.find(params[:id])
|
@@ -31,8 +31,6 @@ module Cats
|
|
31
31
|
belongs_to :unit_of_measure
|
32
32
|
belongs_to :source, polymorphic: true
|
33
33
|
|
34
|
-
has_many :allocations
|
35
|
-
|
36
34
|
validates :best_use_before, presence: true
|
37
35
|
validates :batch_no, presence: true, uniqueness: true
|
38
36
|
validates :quantity, presence: true, numericality: { greater_than: 0 }
|
@@ -74,25 +72,6 @@ module Cats
|
|
74
72
|
end
|
75
73
|
result
|
76
74
|
end
|
77
|
-
|
78
|
-
def allocate(ignore_errors: true)
|
79
|
-
statuses = Allocation.where(commodity_id: id).map(&:allocation_status).uniq
|
80
|
-
unless statuses.count == 1 && statuses[0] == APPROVED
|
81
|
-
return if ignore_errors
|
82
|
-
|
83
|
-
raise(StandardError, 'There are allocations in "Draft" state.')
|
84
|
-
end
|
85
|
-
|
86
|
-
quantity = allocations.sum(:quantity)
|
87
|
-
unless quantity == self.quantity
|
88
|
-
return if ignore_errors
|
89
|
-
|
90
|
-
raise(StandardError, 'Total allocations quantity is not the same as commodity quantity.')
|
91
|
-
end
|
92
|
-
|
93
|
-
self.status = ALLOCATED
|
94
|
-
save!
|
95
|
-
end
|
96
75
|
end
|
97
76
|
end
|
98
77
|
end
|
@@ -14,6 +14,7 @@ module Cats
|
|
14
14
|
|
15
15
|
validates :reference_no, presence: true, uniqueness: true
|
16
16
|
validates :status, inclusion: { in: STATUSES }
|
17
|
+
validate :validate_dispatchable, on: :create
|
17
18
|
|
18
19
|
delegate(:batch_no, :name, :quantity, to: :commodity, prefix: true)
|
19
20
|
delegate(:request_reference, to: :dispatchable, allow_nil: true)
|
@@ -23,14 +24,40 @@ module Cats
|
|
23
24
|
plan.upstream = !plan.dispatchable.nil? && plan.dispatchable_type == RhnRequest.name
|
24
25
|
end
|
25
26
|
|
27
|
+
after_save do |plan|
|
28
|
+
plan.dispatchable&.reserve if status == DRAFT
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_dispatchable
|
32
|
+
# Check if dispatchable is already reserved and raise error if so.
|
33
|
+
return unless dispatchable
|
34
|
+
|
35
|
+
return if dispatchable.status == RhnRequest::APPROVED
|
36
|
+
|
37
|
+
errors.add(:dispatchable, 'is already reserved.') if id.nil?
|
38
|
+
end
|
39
|
+
|
26
40
|
def approve
|
27
41
|
raise(StandardError, 'Dispatch plan already approved.') if status == APPROVED
|
28
42
|
|
29
43
|
raise(StandardError, 'Dispatch plan is empty.') if dispatch_plan_items.count.zero?
|
30
44
|
|
45
|
+
# Raise error if the total dispatch plan quantity is not equal to the
|
46
|
+
# dispatchable quantity
|
47
|
+
if dispatchable.instance_of?(RhnRequest) && dispatchable.quantity != quantity
|
48
|
+
raise(StandardError, 'Requested quantity and plan quantity do not match.')
|
49
|
+
end
|
50
|
+
|
31
51
|
self.status = APPROVED
|
52
|
+
dispatchable&.allocate if dispatchable.instance_of?(RhnRequest)
|
32
53
|
save!
|
33
54
|
end
|
55
|
+
|
56
|
+
# A method which returns the total quantity in the dispatch plan
|
57
|
+
# based on dispatch plan item quantities.
|
58
|
+
def quantity
|
59
|
+
dispatch_plan_items.sum(:quantity)
|
60
|
+
end
|
34
61
|
end
|
35
62
|
end
|
36
63
|
end
|
@@ -4,21 +4,24 @@ module Cats
|
|
4
4
|
belongs_to :donation, optional: true
|
5
5
|
belongs_to :commodity_category
|
6
6
|
belongs_to :unit_of_measure
|
7
|
+
belongs_to :currency
|
7
8
|
belongs_to :destination_warehouse, class_name: 'Cats::Core::Location'
|
8
9
|
|
9
10
|
validates :reference_no, presence: true, uniqueness: true
|
10
|
-
validates :gift_date, :quantity, :requested_by, :
|
11
|
+
validates :gift_date, :quantity, :requested_by, :customs_office, presence: true
|
11
12
|
validates :quantity, numericality: { greater_than: 0 }
|
12
13
|
|
13
14
|
delegate(:name, to: :commodity_category, prefix: true)
|
14
15
|
delegate(:name, to: :destination_warehouse, prefix: true)
|
15
16
|
delegate(:abbreviation, to: :unit_of_measure, prefix: true)
|
17
|
+
delegate(:code, to: :currency, prefix: true)
|
16
18
|
|
17
19
|
before_validation :set_commodity_category
|
18
20
|
after_create :update_donation
|
19
21
|
|
20
22
|
def update_donation
|
21
|
-
|
23
|
+
return unless donation
|
24
|
+
|
22
25
|
return if donation.active
|
23
26
|
|
24
27
|
donation.active = true
|
@@ -4,11 +4,12 @@ module Cats
|
|
4
4
|
REGION = 'Region'.freeze
|
5
5
|
ZONE = 'Zone'.freeze
|
6
6
|
WOREDA = 'Woreda'.freeze
|
7
|
+
FDP = 'Fdp'.freeze
|
7
8
|
KEBELE = 'Kebele'.freeze
|
8
9
|
HUB = 'Hub'.freeze
|
9
10
|
WAREHOUSE = 'Warehouse'.freeze
|
10
11
|
|
11
|
-
LOCATION_TYPES = [REGION, ZONE, WOREDA, KEBELE, HUB, WAREHOUSE].freeze
|
12
|
+
LOCATION_TYPES = [REGION, ZONE, WOREDA, KEBELE, HUB, WAREHOUSE, FDP].freeze
|
12
13
|
|
13
14
|
has_ancestry
|
14
15
|
|
@@ -23,6 +24,7 @@ module Cats
|
|
23
24
|
ZONE => [REGION],
|
24
25
|
WOREDA => [REGION, ZONE],
|
25
26
|
KEBELE => [REGION, ZONE, WOREDA],
|
27
|
+
FDP => [REGION, ZONE, WOREDA],
|
26
28
|
HUB => [REGION, ZONE, WOREDA, KEBELE],
|
27
29
|
WAREHOUSE => [REGION, ZONE, WOREDA, KEBELE, HUB]
|
28
30
|
}
|
@@ -3,6 +3,13 @@ module Cats
|
|
3
3
|
class MonthlyPlan < ApplicationRecord
|
4
4
|
include Dispatchable
|
5
5
|
|
6
|
+
DRAFT = 'Draft'.freeze
|
7
|
+
APPROVED = 'Approved'.freeze
|
8
|
+
RESERVED = 'Reserved'.freeze
|
9
|
+
NEEDS_APPROVED = 'Needs Approved'.freeze
|
10
|
+
|
11
|
+
STATUSES = [DRAFT, APPROVED, RESERVED, NEEDS_APPROVED].freeze
|
12
|
+
|
6
13
|
belongs_to :plan
|
7
14
|
belongs_to :region, class_name: 'Cats::Core::Location'
|
8
15
|
|
@@ -11,7 +18,7 @@ module Cats
|
|
11
18
|
has_many :monthly_rations
|
12
19
|
|
13
20
|
validates :reference_no, presence: true, uniqueness: true
|
14
|
-
validates :status, presence: true, inclusion: { in:
|
21
|
+
validates :status, presence: true, inclusion: { in: MonthlyPlan::STATUSES }
|
15
22
|
validates :no_of_days, :month, presence: true, numericality: { greater_than: 0 }
|
16
23
|
validate :validate_month, :validate_region
|
17
24
|
|
@@ -26,6 +33,21 @@ module Cats
|
|
26
33
|
nil
|
27
34
|
end
|
28
35
|
|
36
|
+
def approve
|
37
|
+
raise(StandardError, 'Plan is not in draft state.') unless status == DRAFT
|
38
|
+
|
39
|
+
raise(StandardError, 'Empty plan cannot be approved.') if monthly_plan_items.count.zero?
|
40
|
+
|
41
|
+
update!(status: APPROVED)
|
42
|
+
end
|
43
|
+
|
44
|
+
def reserve
|
45
|
+
raise(StandardError, 'Plan is not approved.') unless status == APPROVED
|
46
|
+
|
47
|
+
self.status = RESERVED
|
48
|
+
save!
|
49
|
+
end
|
50
|
+
|
29
51
|
def validate_month
|
30
52
|
return unless month
|
31
53
|
|
@@ -35,7 +57,7 @@ module Cats
|
|
35
57
|
def validate_region
|
36
58
|
return unless region
|
37
59
|
|
38
|
-
errors.add(:region, 'is not valid.') unless region.location_type ==
|
60
|
+
errors.add(:region, 'is not valid.') unless region.location_type == Location::REGION
|
39
61
|
end
|
40
62
|
end
|
41
63
|
end
|
@@ -18,6 +18,7 @@ module Cats
|
|
18
18
|
has_many :plan_item_details, through: :plan_items
|
19
19
|
|
20
20
|
validates :reference_no, :year, :status, presence: true
|
21
|
+
validates :total_days, :rounds, presence: true, numericality: { greater_than: 0 }
|
21
22
|
validates :season, presence: true, inclusion: { in: SEASONS }
|
22
23
|
validates :reference_no, uniqueness: true
|
23
24
|
validates :status, inclusion: { in: STATUSES }
|
@@ -5,8 +5,9 @@ module Cats
|
|
5
5
|
|
6
6
|
DRAFT = 'Draft'.freeze
|
7
7
|
APPROVED = 'Approved'.freeze
|
8
|
+
RESERVED = 'Reserved'.freeze
|
8
9
|
ALLOCATED = 'Allocated'.freeze
|
9
|
-
STATUSES = [DRAFT, APPROVED, ALLOCATED].freeze
|
10
|
+
STATUSES = [DRAFT, APPROVED, RESERVED, ALLOCATED].freeze
|
10
11
|
|
11
12
|
belongs_to :commodity
|
12
13
|
|
@@ -43,11 +44,22 @@ module Cats
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def approve
|
47
|
+
raise(StandardError, 'Request is not in draft state.') unless status == DRAFT
|
48
|
+
|
46
49
|
self.status = APPROVED
|
47
50
|
save!
|
48
51
|
end
|
49
52
|
|
53
|
+
def reserve
|
54
|
+
raise(StandardError, 'Request is not approved.') unless status == APPROVED
|
55
|
+
|
56
|
+
self.status = RESERVED
|
57
|
+
save!
|
58
|
+
end
|
59
|
+
|
50
60
|
def allocate
|
61
|
+
raise(StandardError, 'Request is not reserved.') unless status == RESERVED
|
62
|
+
|
51
63
|
self.status = ALLOCATED
|
52
64
|
save!
|
53
65
|
end
|
@@ -8,7 +8,7 @@ module Cats
|
|
8
8
|
belongs_to :destination, class_name: 'Cats::Core::Location'
|
9
9
|
|
10
10
|
validates :source_id, uniqueness: { scope: :destination_id }
|
11
|
-
validate :validate_region, :validate_source, :validate_destination
|
11
|
+
validate :validate_region, :validate_source, :validate_destination, :validate_route
|
12
12
|
|
13
13
|
delegate(:name, to: :region, prefix: true)
|
14
14
|
|
@@ -35,6 +35,14 @@ module Cats
|
|
35
35
|
|
36
36
|
errors.add(:destination, 'cannot be a region.') if destination.location_type == Location::REGION
|
37
37
|
end
|
38
|
+
|
39
|
+
def validate_route
|
40
|
+
return unless source
|
41
|
+
|
42
|
+
return unless destination
|
43
|
+
|
44
|
+
errors.add(:base, 'Source and destination cannot be the same') if source == destination
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
end
|
@@ -20,7 +20,8 @@ module Cats
|
|
20
20
|
validates :commodity_status, inclusion: { in: Cats::Core::Commodity::COMMODITY_STATUSES }
|
21
21
|
validates :stack_status, inclusion: { in: STACK_STATUSES }
|
22
22
|
validate :validate_coordinates, :validate_dimensions, :validate_distance_from_wall, :validate_overlap,
|
23
|
-
:validate_space_between_stack,
|
23
|
+
:validate_space_between_stack, :validate_max_height, :validate_max_length, :validate_max_width,
|
24
|
+
:validate_distance_from_ceiling, unless: -> { store && store.code == 'SUP-STORE' }
|
24
25
|
|
25
26
|
delegate :batch_no, to: :commodity, prefix: true
|
26
27
|
|
@@ -48,9 +49,9 @@ module Cats
|
|
48
49
|
space_between_stack: 1,
|
49
50
|
distance_from_gangway: 2,
|
50
51
|
distance_from_ceiling: 1,
|
51
|
-
maximum_height:
|
52
|
-
maximum_length:
|
53
|
-
maximum_width:
|
52
|
+
maximum_height: 7,
|
53
|
+
maximum_length: 12,
|
54
|
+
maximum_width: 12,
|
54
55
|
distance_from_wall: 1
|
55
56
|
)
|
56
57
|
rule
|
@@ -92,6 +93,32 @@ module Cats
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
96
|
+
def validate_max_height
|
97
|
+
return unless height
|
98
|
+
|
99
|
+
errors.add(:height, 'exceeds stacking rule height.') if height > stacking_rules.maximum_height
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_max_length
|
103
|
+
return unless length
|
104
|
+
|
105
|
+
errors.add(:length, 'exceeds stacking rule length.') if length > stacking_rules.maximum_length
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_max_width
|
109
|
+
return unless width
|
110
|
+
|
111
|
+
errors.add(:width, 'exceeds stacking rule width.') if width > stacking_rules.maximum_width
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_distance_from_ceiling
|
115
|
+
return unless height && store
|
116
|
+
|
117
|
+
return unless store.height - height < stacking_rules.distance_from_ceiling
|
118
|
+
|
119
|
+
errors.add(:height, 'of stack is close to the ceiling.')
|
120
|
+
end
|
121
|
+
|
95
122
|
# A method that checks if an overlap exists b/n two stacks.
|
96
123
|
# An overlap b/n stacks does not occur if one of the stacks is completely above the other stack or
|
97
124
|
# if one of the stacks is on the left side of the other stack.
|
@@ -16,7 +16,7 @@ module Cats
|
|
16
16
|
belongs_to :transport_plan
|
17
17
|
|
18
18
|
validates :reference_no, presence: true, uniqueness: true
|
19
|
-
validates :start_date, :end_date, :status, :bid_bond_amount, presence: true
|
19
|
+
validates :start_date, :end_date, :opening_date, :status, :bid_bond_amount, presence: true
|
20
20
|
validates :bid_bond_amount, numericality: { greater_than_or_equal_to: 0 }
|
21
21
|
validates :status, inclusion: { in: STATUSES }
|
22
22
|
validate :validate_start_date_against_end_date
|
@@ -46,6 +46,10 @@ module Cats
|
|
46
46
|
save!
|
47
47
|
self
|
48
48
|
end
|
49
|
+
|
50
|
+
def quantity
|
51
|
+
transport_bid_items.sum(:quantity)
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
@@ -7,6 +7,7 @@ module Cats
|
|
7
7
|
|
8
8
|
validates :offer_date, :bid_bond_amount, presence: true
|
9
9
|
validates :bid_bond_amount, numericality: { greater_than_or_equal_to: 0 }
|
10
|
+
validates :transport_bid_id, uniqueness: { scope: :transporter_id }
|
10
11
|
|
11
12
|
delegate(:name, to: :transporter, prefix: true)
|
12
13
|
delegate(:reference_no, to: :transport_bid, prefix: 'bid')
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cats
|
2
|
+
module Core
|
3
|
+
class UnitConversion < ApplicationRecord
|
4
|
+
belongs_to :from, class_name: 'Cats::Core::UnitOfMeasure'
|
5
|
+
belongs_to :to, class_name: 'Cats::Core::UnitOfMeasure'
|
6
|
+
|
7
|
+
validates :factor, presence: true
|
8
|
+
validates :from_id, uniqueness: { scope: :to_id }
|
9
|
+
validates :factor, numericality: { greater_than: 0 }
|
10
|
+
|
11
|
+
delegate(:abbreviation, to: :from, prefix: true)
|
12
|
+
delegate(:abbreviation, to: :to, prefix: true)
|
13
|
+
|
14
|
+
def self.convert(from, to, value)
|
15
|
+
return value if from == to
|
16
|
+
|
17
|
+
conversion = find_by(from: from, to: to)
|
18
|
+
error = "Conversion factor from #{from.abbreviation} to #{to.abbreviation} not found."
|
19
|
+
raise(StandardError, error) unless conversion
|
20
|
+
|
21
|
+
value * conversion.factor
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# A concern to represent entities that lead to dispatch plan.
|
2
|
-
# All classes including this concern must implement `request_reference()
|
3
|
-
# and `request_quantity()` methods, which will be used to generate
|
2
|
+
# All classes including this concern must implement `request_reference()`,
|
3
|
+
# `reserve()`, and `request_quantity()` methods, which will be used to generate
|
4
4
|
# reference information in dispatch plan serializer.
|
5
5
|
module Cats
|
6
6
|
module Core
|
@@ -3,7 +3,7 @@ module Cats
|
|
3
3
|
class CommoditySerializer < ActiveModel::Serializer
|
4
4
|
attributes :id, :name, :batch_no, :description, :unit_of_measure_id, :unit_of_measure_abbreviation, :source_id,
|
5
5
|
:source_type, :source_reference_no, :quantity, :best_use_before, :volume_per_metric_ton,
|
6
|
-
:arrival_status, :status
|
6
|
+
:arrival_status, :status, :shipping_reference
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -23,7 +23,6 @@ module Cats
|
|
23
23
|
end
|
24
24
|
HubAuthorization.insert_all(authorizations)
|
25
25
|
plan.dispatch_plan_items.update_all(status: DispatchPlanItem::SOURCE_AUTHORIZED)
|
26
|
-
plan.dispatchable.allocate
|
27
26
|
end
|
28
27
|
send_notification(plan)
|
29
28
|
plan
|
@@ -33,6 +32,7 @@ module Cats
|
|
33
32
|
plan = Cats::Core::DispatchPlan.new(params)
|
34
33
|
plan.prepared_by = user
|
35
34
|
plan.save!
|
35
|
+
|
36
36
|
plan
|
37
37
|
rescue ActiveRecord::RecordInvalid
|
38
38
|
raise(StandardError, plan.errors.full_messages[0])
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Cats
|
2
2
|
module Core
|
3
3
|
class DispatchService
|
4
|
-
def search(user, status)
|
4
|
+
def search(user, status, authorized: false)
|
5
5
|
details = user.details
|
6
6
|
|
7
7
|
unless details['stores'] || details['warehouse'] || details['hub']
|
@@ -10,18 +10,25 @@ module Cats
|
|
10
10
|
|
11
11
|
# Get user's hub
|
12
12
|
hub = if details['stores']
|
13
|
-
|
13
|
+
Store.find(details['stores'][0]).warehouse.parent
|
14
14
|
elsif details['warehouse']
|
15
|
-
|
15
|
+
Location.find(details['warehouse']).parent
|
16
16
|
else
|
17
|
-
|
17
|
+
Location.find(details['hub'])
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
if authorized
|
21
|
+
return Dispatch.joins(:dispatch_plan_item)
|
22
|
+
.where(
|
23
|
+
dispatch_plan_item: { destination: hub, status: DispatchPlanItem::AUTHORIZED },
|
24
|
+
dispatch_status: status
|
25
|
+
)
|
26
|
+
end
|
27
|
+
Dispatch.joins(:dispatch_plan_item)
|
28
|
+
.where(
|
29
|
+
dispatch_plan_item: { destination: hub },
|
30
|
+
dispatch_status: status
|
31
|
+
)
|
25
32
|
end
|
26
33
|
|
27
34
|
def start(dispatch)
|
@@ -7,41 +7,29 @@ module Cats
|
|
7
7
|
key_available = details.keys.any? { |key| %w[stores warehouse hub].include?(key) }
|
8
8
|
raise(StandardError, 'User does not have associated location.') unless key_available
|
9
9
|
|
10
|
-
if details['hub']
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
hub = warehouse.parent
|
23
|
-
end
|
24
|
-
allocation_items =
|
25
|
-
Cats::Core::AllocationItem
|
26
|
-
.joins(allocation: :commodity)
|
27
|
-
.where(destination: hub, allocation: { allocation_status: Cats::Core::Allocation::APPROVED })
|
28
|
-
.or(
|
29
|
-
Cats::Core::AllocationItem
|
30
|
-
.joins(allocation: :commodity)
|
31
|
-
.where(destination: warehouse, allocation: { allocation_status: Cats::Core::Allocation::APPROVED })
|
32
|
-
)
|
33
|
-
end
|
10
|
+
hub = if details['hub']
|
11
|
+
Location.find(details['hub'])
|
12
|
+
elsif details['warehouse']
|
13
|
+
Location.find(details['warehouse']).parent
|
14
|
+
else
|
15
|
+
Store.find(details['stores'][0]).warehouse.parent
|
16
|
+
end
|
17
|
+
|
18
|
+
allocation_items =
|
19
|
+
DispatchPlanItem
|
20
|
+
.joins(dispatch_plan: :commodity)
|
21
|
+
.where(destination: hub, dispatch_plan: { status: DispatchPlan::APPROVED })
|
34
22
|
|
35
23
|
allocation_items.map do |item|
|
36
24
|
{
|
37
|
-
reference_no: item.
|
38
|
-
source: item.
|
25
|
+
reference_no: item.dispatch_plan.reference_no,
|
26
|
+
source: item.source.name,
|
39
27
|
destination: item.destination.name,
|
40
28
|
quantity: item.quantity,
|
41
|
-
batch_no: item.
|
42
|
-
commodity_id: item.
|
43
|
-
commodity_name: item.
|
44
|
-
unit: item.
|
29
|
+
batch_no: item.dispatch_plan.commodity.batch_no,
|
30
|
+
commodity_id: item.dispatch_plan.commodity_id,
|
31
|
+
commodity_name: item.dispatch_plan.commodity.name,
|
32
|
+
unit: item.dispatch_plan.commodity.unit_of_measure.abbreviation
|
45
33
|
}
|
46
34
|
end
|
47
35
|
end
|
@@ -49,10 +37,19 @@ module Cats
|
|
49
37
|
def receipt_stacks(receipt)
|
50
38
|
location = receipt.dispatch.dispatch_plan_item.destination
|
51
39
|
warehouses = location.children
|
52
|
-
stores =
|
40
|
+
stores = Store.where(warehouse: warehouses)
|
53
41
|
|
54
42
|
commodity = receipt.dispatch.dispatch_plan_item.dispatch_plan.commodity
|
55
|
-
|
43
|
+
Stack.where(commodity: commodity, store: stores)
|
44
|
+
end
|
45
|
+
|
46
|
+
def dispatch_stacks(dispatch)
|
47
|
+
location = dispatch.dispatch_plan_item.source
|
48
|
+
warehouses = location.children
|
49
|
+
stores = Store.where(warehouse: warehouses)
|
50
|
+
|
51
|
+
commodity = dispatch.dispatch_plan_item.dispatch_plan.commodity
|
52
|
+
Stack.where(commodity: commodity, store: stores)
|
56
53
|
end
|
57
54
|
end
|
58
55
|
end
|
data/config/routes.rb
CHANGED
@@ -60,6 +60,7 @@ Cats::Core::Engine.routes.draw do
|
|
60
60
|
defaults: { level: Cats::Core::SpaceService::STORE }
|
61
61
|
|
62
62
|
get '/commodity_categories/roots', to: 'commodity_categories#root', as: :commodity_category_roots
|
63
|
+
get '/commodity_categories/all', to: 'commodity_categories#all', as: :commodity_category_all
|
63
64
|
resources :commodity_categories, except: %i[destroy] do
|
64
65
|
member do
|
65
66
|
get 'children'
|
@@ -73,6 +74,7 @@ Cats::Core::Engine.routes.draw do
|
|
73
74
|
end
|
74
75
|
resources :currencies, except: %i[destroy]
|
75
76
|
resources :unit_of_measures, except: %i[destroy]
|
77
|
+
resources :unit_conversions, except: %i[destroy]
|
76
78
|
|
77
79
|
post '/transporters/filter', controller: :transporters, action: :filter
|
78
80
|
resources :transporters, except: %i[destroy]
|
@@ -137,5 +139,6 @@ Cats::Core::Engine.routes.draw do
|
|
137
139
|
post '/stores/:id/stacks', controller: :stacks, action: :filter
|
138
140
|
get '/stacks/commodity_for_location', controller: :stacks, action: :commodity_for_location, as: :commodity_for_location
|
139
141
|
get '/receipts/:id/stacks', controller: :stacks, action: :receipt_stacks, as: :receipt_stacks
|
142
|
+
get '/dispatches/:id/stacks', controller: :stacks, action: :dispatch_stacks, as: :dispatch_stacks
|
140
143
|
resources :stacks, only: %i[show index create update]
|
141
144
|
end
|
@@ -5,6 +5,9 @@ class CreateCatsCorePlans < ActiveRecord::Migration[6.1]
|
|
5
5
|
t.integer :year, null: false
|
6
6
|
t.string :season, null: false
|
7
7
|
t.string :status, null: false, default: 'Draft'
|
8
|
+
t.date :start_date
|
9
|
+
t.integer :total_days, null: false, default: 180
|
10
|
+
t.integer :rounds, null: false
|
8
11
|
|
9
12
|
t.references :program,
|
10
13
|
null: false,
|
@@ -30,9 +30,14 @@ class CreateCatsCoreGiftCertificates < ActiveRecord::Migration[6.1]
|
|
30
30
|
foreign_key: { to_table: :cats_core_locations }
|
31
31
|
t.float :estimated_price
|
32
32
|
t.float :estimated_tax
|
33
|
+
t.references :currency,
|
34
|
+
null: false,
|
35
|
+
index: { name: 'currency_on_gc_indx' },
|
36
|
+
foreign_key: { to_table: :cats_core_currencies }
|
33
37
|
t.string :registration_no
|
34
38
|
t.string :requested_by, null: false
|
35
|
-
t.string :request_reference
|
39
|
+
t.string :request_reference
|
40
|
+
t.string :customs_office, null: false
|
36
41
|
|
37
42
|
t.timestamps
|
38
43
|
end
|
@@ -13,6 +13,7 @@ class CreateCatsCoreCommodities < ActiveRecord::Migration[6.1]
|
|
13
13
|
t.float :volume_per_metric_ton
|
14
14
|
t.string :arrival_status, null: false, default: 'At Source'
|
15
15
|
t.boolean :approved, null: false, default: false
|
16
|
+
t.string :shipping_reference
|
16
17
|
|
17
18
|
t.timestamps
|
18
19
|
end
|
@@ -3,8 +3,9 @@ class CreateCatsCoreTransportBids < ActiveRecord::Migration[6.1]
|
|
3
3
|
create_table :cats_core_transport_bids do |t|
|
4
4
|
t.string :reference_no, unique: true
|
5
5
|
t.string :description
|
6
|
-
t.
|
7
|
-
t.
|
6
|
+
t.datetime :start_date, null: false
|
7
|
+
t.datetime :end_date, null: false
|
8
|
+
t.datetime :opening_date, null: false
|
8
9
|
t.string :status, null: false, default: 'New'
|
9
10
|
t.float :bid_bond_amount, null: false, default: 0
|
10
11
|
t.references :transport_plan,
|
@@ -10,6 +10,10 @@ class CreateCatsCoreTransportBidItems < ActiveRecord::Migration[6.1]
|
|
10
10
|
index: { name: 'tpi_on_tbi_indx' },
|
11
11
|
foreign_key: { to_table: :cats_core_transport_plan_items }
|
12
12
|
t.float :quantity, null: false
|
13
|
+
t.references :unit,
|
14
|
+
null: false,
|
15
|
+
index: { name: 'unit_on_tbi_indx' },
|
16
|
+
foreign_key: { to_table: :cats_core_unit_of_measures }
|
13
17
|
|
14
18
|
t.timestamps
|
15
19
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateCatsCoreUnitConversions < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :cats_core_unit_conversions do |t|
|
4
|
+
t.references :from,
|
5
|
+
null: false,
|
6
|
+
index: { name: 'from_on_uc_indx' },
|
7
|
+
foreign_key: { to_table: :cats_core_unit_of_measures }
|
8
|
+
t.references :to,
|
9
|
+
null: false,
|
10
|
+
index: { name: 'to_on_uc_indx' },
|
11
|
+
foreign_key: { to_table: :cats_core_unit_of_measures }
|
12
|
+
t.float :factor, null: false
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
add_index(:cats_core_unit_conversions, [:from_id, :to_id], unique: true)
|
18
|
+
end
|
19
|
+
end
|
data/lib/cats/core/version.rb
CHANGED
@@ -9,7 +9,20 @@ FactoryBot.define do
|
|
9
9
|
approved_by { nil }
|
10
10
|
|
11
11
|
trait :with_rhn do
|
12
|
-
dispatchable
|
12
|
+
dispatchable do
|
13
|
+
disp = create(:rhn_request)
|
14
|
+
disp.approve
|
15
|
+
disp
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :with_monthly_plan do
|
20
|
+
dispatchable do
|
21
|
+
disp = create(:monthly_plan)
|
22
|
+
3.times { create(:monthly_plan_item, monthly_plan: disp) }
|
23
|
+
disp.approve
|
24
|
+
disp
|
25
|
+
end
|
13
26
|
end
|
14
27
|
end
|
15
28
|
end
|
@@ -15,8 +15,10 @@ FactoryBot.define do
|
|
15
15
|
quantity { 1.5 }
|
16
16
|
estimated_price { 1.5 }
|
17
17
|
estimated_tax { 1.5 }
|
18
|
+
currency
|
18
19
|
registration_no { FFaker::Name.name }
|
19
20
|
requested_by { FFaker::Name.name }
|
20
21
|
request_reference { FFaker::Name.name }
|
22
|
+
customs_office { FFaker::Name.name }
|
21
23
|
end
|
22
24
|
end
|
@@ -3,9 +3,9 @@ FactoryBot.define do
|
|
3
3
|
space_between_stack { 1 }
|
4
4
|
distance_from_gangway { 1.5 }
|
5
5
|
distance_from_ceiling { 1 }
|
6
|
-
maximum_height {
|
7
|
-
maximum_length {
|
8
|
-
maximum_width {
|
6
|
+
maximum_height { 7 }
|
7
|
+
maximum_length { 12 }
|
8
|
+
maximum_width { 12 }
|
9
9
|
distance_from_wall { 1 }
|
10
10
|
end
|
11
11
|
end
|
@@ -2,8 +2,9 @@ FactoryBot.define do
|
|
2
2
|
factory :transport_bid, class: 'Cats::Core::TransportBid' do
|
3
3
|
reference_no { FFaker::Name.name }
|
4
4
|
description { FFaker::Name.name }
|
5
|
-
start_date {
|
6
|
-
end_date {
|
5
|
+
start_date { DateTime.now - 1.week }
|
6
|
+
end_date { DateTime.now }
|
7
|
+
opening_date { DateTime.now + 1.week }
|
7
8
|
bid_bond_amount { 100 }
|
8
9
|
transport_plan
|
9
10
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cats_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henock L.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_model_serializers
|
@@ -251,12 +251,11 @@ files:
|
|
251
251
|
- app/controllers/cats/core/spaces_controller.rb
|
252
252
|
- app/controllers/cats/core/stacks_controller.rb
|
253
253
|
- app/controllers/cats/core/transporters_controller.rb
|
254
|
+
- app/controllers/cats/core/unit_conversions_controller.rb
|
254
255
|
- app/controllers/cats/core/unit_of_measures_controller.rb
|
255
256
|
- app/controllers/cats/core/users_controller.rb
|
256
257
|
- app/controllers/concerns/cats/core/common.rb
|
257
258
|
- app/jobs/cats/core/application_job.rb
|
258
|
-
- app/models/cats/core/allocation.rb
|
259
|
-
- app/models/cats/core/allocation_item.rb
|
260
259
|
- app/models/cats/core/application_module.rb
|
261
260
|
- app/models/cats/core/application_record.rb
|
262
261
|
- app/models/cats/core/commodity.rb
|
@@ -311,6 +310,7 @@ files:
|
|
311
310
|
- app/models/cats/core/transport_plan.rb
|
312
311
|
- app/models/cats/core/transport_plan_item.rb
|
313
312
|
- app/models/cats/core/transporter.rb
|
313
|
+
- app/models/cats/core/unit_conversion.rb
|
314
314
|
- app/models/cats/core/unit_of_measure.rb
|
315
315
|
- app/models/cats/core/user.rb
|
316
316
|
- app/models/concerns/cats/core/dispatchable.rb
|
@@ -334,6 +334,7 @@ files:
|
|
334
334
|
- app/serializers/cats/core/route_serializer.rb
|
335
335
|
- app/serializers/cats/core/stack_serializer.rb
|
336
336
|
- app/serializers/cats/core/transporter_serializer.rb
|
337
|
+
- app/serializers/cats/core/unit_conversion_serializer.rb
|
337
338
|
- app/serializers/cats/core/unit_of_measure_serializer.rb
|
338
339
|
- app/serializers/cats/core/user_serializer.rb
|
339
340
|
- app/services/cats/core/dispatch_plan_service.rb
|
@@ -374,8 +375,6 @@ files:
|
|
374
375
|
- db/migrate/20210718040129_create_cats_core_routes.rb
|
375
376
|
- db/migrate/20210718042749_create_cats_core_transporters.rb
|
376
377
|
- db/migrate/20210718042755_create_cats_core_rhn_requests.rb
|
377
|
-
- db/migrate/20210718042823_create_cats_core_allocations.rb
|
378
|
-
- db/migrate/20210718043204_create_cats_core_allocation_items.rb
|
379
378
|
- db/migrate/20210718043328_create_cats_core_dispatch_plans.rb
|
380
379
|
- db/migrate/20210718043401_create_cats_core_dispatch_plan_items.rb
|
381
380
|
- db/migrate/20210718045516_create_cats_core_dispatches.rb
|
@@ -404,13 +403,12 @@ files:
|
|
404
403
|
- db/migrate/20220107125025_create_cats_core_monthly_plan_items.rb
|
405
404
|
- db/migrate/20220107132433_create_cats_core_monthly_plan_item_details.rb
|
406
405
|
- db/migrate/20220209083928_create_cats_core_hub_authorizations.rb
|
406
|
+
- db/migrate/20220416143416_create_cats_core_unit_conversions.rb
|
407
407
|
- lib/cats/core.rb
|
408
408
|
- lib/cats/core/engine.rb
|
409
409
|
- lib/cats/core/version.rb
|
410
410
|
- lib/cats_core.rb
|
411
411
|
- lib/tasks/cats_core_tasks.rake
|
412
|
-
- spec/factories/cats/core/allocation_items.rb
|
413
|
-
- spec/factories/cats/core/allocations.rb
|
414
412
|
- spec/factories/cats/core/application_modules.rb
|
415
413
|
- spec/factories/cats/core/commodities.rb
|
416
414
|
- spec/factories/cats/core/commodity_categories.rb
|
@@ -463,6 +461,7 @@ files:
|
|
463
461
|
- spec/factories/cats/core/transport_plan_items.rb
|
464
462
|
- spec/factories/cats/core/transport_plans.rb
|
465
463
|
- spec/factories/cats/core/transporters.rb
|
464
|
+
- spec/factories/cats/core/unit_conversions.rb
|
466
465
|
- spec/factories/cats/core/unit_of_measures.rb
|
467
466
|
- spec/factories/cats/core/users.rb
|
468
467
|
homepage: http://cats.ndrmcapps.org
|
@@ -488,7 +487,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
488
487
|
- !ruby/object:Gem::Version
|
489
488
|
version: '0'
|
490
489
|
requirements: []
|
491
|
-
rubygems_version: 3.3.
|
490
|
+
rubygems_version: 3.3.7
|
492
491
|
signing_key:
|
493
492
|
specification_version: 4
|
494
493
|
summary: Core module for CATS applications
|
@@ -1,78 +0,0 @@
|
|
1
|
-
module Cats
|
2
|
-
module Core
|
3
|
-
class Allocation < ApplicationRecord
|
4
|
-
DRAFT = 'Draft'.freeze
|
5
|
-
APPROVED = 'Approved'.freeze
|
6
|
-
ALLOCATION_STATUSES = [DRAFT, APPROVED].freeze
|
7
|
-
|
8
|
-
belongs_to :rhn_request, optional: true
|
9
|
-
belongs_to :commodity
|
10
|
-
belongs_to :source, class_name: 'Cats::Core::Location'
|
11
|
-
has_many :allocation_items
|
12
|
-
|
13
|
-
validates :commodity_status, :allocation_status, presence: true
|
14
|
-
validates :reference_no, presence: true, uniqueness: true
|
15
|
-
validates :quantity, presence: true, numericality: { greater_than: 0 }
|
16
|
-
validates :commodity_status, inclusion: { in: Commodity::COMMODITY_STATUSES }
|
17
|
-
validates :allocation_status, inclusion: { in: ALLOCATION_STATUSES }
|
18
|
-
validate :validate_quantity_against_items, :validate_quantity_against_commodity,
|
19
|
-
:validate_quantity_against_request, :validate_request_status, :validate_commodity_status
|
20
|
-
|
21
|
-
delegate(:reference_no, to: :rhn_request, prefix: :rhn, allow_nil: true)
|
22
|
-
delegate(:batch_no, to: :commodity, prefix: true)
|
23
|
-
delegate(:name, to: :source, prefix: true)
|
24
|
-
delegate(:location_type, to: :source, prefix: true)
|
25
|
-
|
26
|
-
def validate_quantity_against_items
|
27
|
-
return unless quantity
|
28
|
-
|
29
|
-
return unless quantity_changed?
|
30
|
-
|
31
|
-
allocated = allocation_items.sum(:quantity)
|
32
|
-
errors.add(:quantity, "is below items quantity. Minimum allowed is #{allocated}") if quantity < allocated
|
33
|
-
end
|
34
|
-
|
35
|
-
def validate_quantity_against_commodity
|
36
|
-
return unless quantity && commodity
|
37
|
-
|
38
|
-
allocated = Allocation.where(commodity: commodity).sum(:quantity)
|
39
|
-
remaining = commodity.quantity - allocated
|
40
|
-
|
41
|
-
remaining += quantity_was if quantity_was
|
42
|
-
errors.add(:quantity, "exceeds commodity quantity. Maximum allowed is #{remaining}") if quantity > remaining
|
43
|
-
end
|
44
|
-
|
45
|
-
def validate_quantity_against_request
|
46
|
-
return unless quantity && rhn_request
|
47
|
-
|
48
|
-
errors.add(:quantity, 'is not equal to requested quantity.') if quantity != rhn_request.quantity
|
49
|
-
end
|
50
|
-
|
51
|
-
def validate_request_status
|
52
|
-
return unless rhn_request
|
53
|
-
|
54
|
-
errors.add(:rhn_request, 'should be approved first.') if rhn_request.status != RhnRequest::APPROVED
|
55
|
-
end
|
56
|
-
|
57
|
-
def validate_commodity_status
|
58
|
-
return unless commodity
|
59
|
-
|
60
|
-
errors.add(:commodity, 'should be approved first.') unless commodity.status == Commodity::APPROVED
|
61
|
-
end
|
62
|
-
|
63
|
-
def approve
|
64
|
-
raise(StandardError, 'Allocation already approved.') if allocation_status == Allocation::APPROVED
|
65
|
-
|
66
|
-
item_quantity = allocation_items.sum(:quantity)
|
67
|
-
raise(StandardError, 'Allocation quantity and items quantity do not match.') if quantity != item_quantity
|
68
|
-
|
69
|
-
self.allocation_status = Allocation::APPROVED
|
70
|
-
save!
|
71
|
-
|
72
|
-
# If the commodity is all allocated, then we should
|
73
|
-
# set its status to allocated.
|
74
|
-
commodity.allocate
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module Cats
|
2
|
-
module Core
|
3
|
-
class AllocationItem < ApplicationRecord
|
4
|
-
belongs_to :allocation
|
5
|
-
belongs_to :destination, class_name: 'Cats::Core::Location'
|
6
|
-
|
7
|
-
validates :from, :to, presence: true
|
8
|
-
validates :quantity, presence: true, numericality: { greater_than: 0 }
|
9
|
-
validate :validate_source_and_destination, :validate_quantity
|
10
|
-
validate :validate_allocation_status, on: %i[create update]
|
11
|
-
|
12
|
-
def validate_source_and_destination
|
13
|
-
return unless allocation.present? && destination.present?
|
14
|
-
|
15
|
-
errors.add(:base, 'source and destination cannot be the same') if allocation.source == destination
|
16
|
-
end
|
17
|
-
|
18
|
-
def validate_quantity
|
19
|
-
return unless quantity && allocation
|
20
|
-
|
21
|
-
return unless quantity_changed?
|
22
|
-
|
23
|
-
allocated = AllocationItem.where(allocation: allocation).sum(:quantity)
|
24
|
-
remaining = allocation.quantity - allocated
|
25
|
-
remaining += quantity_was if quantity_was
|
26
|
-
errors.add(:quantity, "exceeds allocated quantity. Maximum allowed is #{remaining}") if quantity > remaining
|
27
|
-
end
|
28
|
-
|
29
|
-
def validate_allocation_status
|
30
|
-
return unless allocation
|
31
|
-
|
32
|
-
return unless allocation.allocation_status == Cats::Core::Allocation::APPROVED
|
33
|
-
|
34
|
-
errors.add(:allocation, 'is alreay approved. Changes are not allowed at this point.')
|
35
|
-
end
|
36
|
-
|
37
|
-
delegate(:name, to: :destination, prefix: true)
|
38
|
-
delegate(:reference_no, to: :allocation, prefix: true)
|
39
|
-
delegate(:location_type, to: :destination, prefix: true)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
class CreateCatsCoreAllocations < ActiveRecord::Migration[6.1]
|
2
|
-
def change
|
3
|
-
create_table :cats_core_allocations do |t|
|
4
|
-
t.string :reference_no, unique: true
|
5
|
-
t.references :rhn_request,
|
6
|
-
null: true,
|
7
|
-
index: { name: 'rr_on_allocation_indx' },
|
8
|
-
foreign_key: { to_table: :cats_core_rhn_requests }
|
9
|
-
t.references :commodity,
|
10
|
-
null: false,
|
11
|
-
index: { name: 'ca_on_commodity_indx' },
|
12
|
-
foreign_key: { to_table: :cats_core_commodities }
|
13
|
-
t.references :source,
|
14
|
-
null: false,
|
15
|
-
index: { name: 'ca_on_source_indx' },
|
16
|
-
foreign_key: { to_table: :cats_core_locations }
|
17
|
-
t.float :quantity, null: false
|
18
|
-
t.string :commodity_status, null: false, default: 'Good'
|
19
|
-
t.string :allocation_status, null: false, default: 'Draft'
|
20
|
-
t.string :remark
|
21
|
-
|
22
|
-
t.timestamps
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class CreateCatsCoreAllocationItems < ActiveRecord::Migration[6.1]
|
2
|
-
def change
|
3
|
-
create_table :cats_core_allocation_items do |t|
|
4
|
-
t.references :allocation,
|
5
|
-
null: false,
|
6
|
-
index: { name: 'allocation_on_ai_indx' },
|
7
|
-
foreign_key: { to_table: :cats_core_allocations }
|
8
|
-
t.references :destination,
|
9
|
-
null: false,
|
10
|
-
index: { name: 'destination_on_ai_indx'},
|
11
|
-
foreign_key: { to_table: :cats_core_locations }
|
12
|
-
t.float :quantity, null: false
|
13
|
-
t.date :from, null: false
|
14
|
-
t.date :to, null: false
|
15
|
-
|
16
|
-
t.timestamps
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
FactoryBot.define do
|
2
|
-
factory :allocation, class: 'Cats::Core::Allocation' do
|
3
|
-
reference_no { FFaker::Name.name }
|
4
|
-
rhn_request { nil }
|
5
|
-
commodity do
|
6
|
-
c = create(:commodity)
|
7
|
-
c.approve
|
8
|
-
c
|
9
|
-
end
|
10
|
-
source factory: :location
|
11
|
-
quantity { 50 }
|
12
|
-
commodity_status { Cats::Core::Commodity::GOOD }
|
13
|
-
allocation_status { Cats::Core::Allocation::DRAFT }
|
14
|
-
remark { FFaker::Name.name }
|
15
|
-
end
|
16
|
-
end
|