cats_core 1.1.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebf6bb336bcf7b1fa89d734c18444e8f41b586c480a24b675fb62d5a8e3d5528
4
- data.tar.gz: 6a5adfade3042c981cc482f336393cd51265a636e56b2ae91b5f4c1d8193840a
3
+ metadata.gz: 63d1bc4e2e97985bc79c49007a2d826b43caec3b8fc31eca51fa4f588d22e10f
4
+ data.tar.gz: d4d08836b88121d75bd29fb711975726285b9ab5f7b64285ed48349cff51e2e3
5
5
  SHA512:
6
- metadata.gz: 5e3075ca71388f92b600ebfeb4d951e003b200987683a5de62d7138107d14efbd6c94a11425c1770e650ecf5facdcefd8ca90de71bb97d5b1685a9d787ef7ead
7
- data.tar.gz: ecd7c5c357ea9e8a05c06523cd821a8c10fd21611e5cbc27390fefc8cec5aa54ea840175eb5c8419f484654bdb5a7918d2d551c36de242599e9b41dfa32377d8
6
+ metadata.gz: 6bc9fdab7012e63bcda95512f54f0fe8586c225a8eec25c88dcc3ed6b0634aa21a6a62ba5e6091f21b718f10d9df93eeabdf0a88a62a78891f244dc2c14d3bac
7
+ data.tar.gz: 9ced66786a2d6303d5ea944366cf9357ff9d3a9ab8e4406d2a6c490d8969e0c6431b2265e321a965a2ab10c503bc51e4cbdd40766581f8af6ccf844405f58527
@@ -3,7 +3,8 @@ module Cats
3
3
  class DispatchesController < ApplicationController
4
4
  include Common
5
5
 
6
- before_action :set_service, only: %i[create_receipt_authorization approve start]
6
+ before_action :set_service, only: %i[create_receipt_authorization approve start search confirm]
7
+ before_action :set_dispatch, only: %i[approve start confirm]
7
8
 
8
9
  def index
9
10
  dispatches = Cats::Core::Dispatch.where(allocation_item_id: params[:id])
@@ -12,6 +13,7 @@ module Cats
12
13
 
13
14
  def create
14
15
  dispatch = Cats::Core::Dispatch.new(model_params)
16
+ dispatch.dispatch_status = Cats::Core::Dispatch::DRAFT
15
17
  dispatch.prepared_by = current_user
16
18
  if dispatch.save
17
19
  render json: { success: true, data: serialize(dispatch) }, status: :created
@@ -29,30 +31,44 @@ module Cats
29
31
  end
30
32
 
31
33
  def approve
32
- dispatch = Cats::Core::Dispatch.find(params[:id])
33
- dispatch = @service.approve(dispatch)
34
+ dispatch = @service.approve(@dispatch)
34
35
  render json: { success: true, data: serialize(dispatch) }
35
36
  rescue StandardError => e
36
37
  render json: { success: false, error: e.message }
37
38
  end
38
39
 
39
40
  def start
40
- dispatch = Cats::Core::Dispatch.find(params[:id])
41
- dispatch = @service.start(dispatch)
41
+ dispatch = @service.start(@dispatch)
42
42
  render json: { success: true, data: serialize(dispatch) }
43
43
  rescue StandardError => e
44
44
  render json: { success: false, error: e.message }
45
45
  end
46
46
 
47
+ def confirm
48
+ dispatch = @service.confirm(@dispatch)
49
+ render json: { success: true, data: serialize(dispatch) }
50
+ rescue StandardError => e
51
+ render json: { success: false, error: e.message }
52
+ end
53
+
54
+ def search
55
+ data = @service.search(current_user, params[:status])
56
+ render json: { success: true, data: serialize(data) }
57
+ end
58
+
47
59
  private
48
60
 
49
61
  def set_service
50
62
  @service = DispatchService.new
51
63
  end
52
64
 
65
+ def set_dispatch
66
+ @dispatch = Cats::Core::Dispatch.find(params[:id])
67
+ end
68
+
53
69
  def model_params
54
70
  params.require(:payload).permit(:reference_no, :allocation_item_id, :transporter_id, :plate_no, :driver_name,
55
- :driver_phone, :quantity, :remark, :dispatch_status)
71
+ :driver_phone, :quantity, :remark)
56
72
  end
57
73
  end
58
74
  end
@@ -1,50 +1,20 @@
1
1
  module Cats
2
2
  module Core
3
3
  class LocationsController < ApplicationController
4
- before_action :set_location, only: %i[show update]
4
+ include Common
5
5
 
6
6
  def index
7
7
  locations = Cats::Core::Location.where(location_type: params[:location_type])
8
- data = ActiveModelSerializers::SerializableResource.new(locations)
9
- render json: { success: true, data: data }
10
- end
11
-
12
- def show
13
- data = ActiveModelSerializers::SerializableResource.new(@location)
14
- render json: { success: true, data: data }
8
+ render json: { success: true, data: serialize(locations) }
15
9
  end
16
10
 
17
11
  def children
18
12
  parent = Cats::Core::Location.find(params[:id])
19
- data = ActiveModelSerializers::SerializableResource.new(parent.children)
20
- render json: { success: true, data: data }
21
- end
22
-
23
- def create
24
- obj = Cats::Core::Location.new(model_params)
25
- if obj.save
26
- data = ActiveModelSerializers::SerializableResource.new(obj)
27
- render json: { success: true, data: data }, status: :created
28
- else
29
- render json: { success: false, error: obj.errors.full_messages[0] }, status: :unprocessable_entity
30
- end
31
- end
32
-
33
- def update
34
- if @location.update(model_params)
35
- data = ActiveModelSerializers::SerializableResource.new(@location)
36
- render json: { success: true, data: data }
37
- else
38
- render json: { success: false, error: @location.errors.full_messages[0] }, status: :unprocessable_entity
39
- end
13
+ render json: { success: true, data: serialize(parent.children) }
40
14
  end
41
15
 
42
16
  private
43
17
 
44
- def set_location
45
- @location = Cats::Core::Location.find(params[:id])
46
- end
47
-
48
18
  def model_params
49
19
  params.require(:payload).permit(:code, :name, :location_type, :description, :parent_id)
50
20
  end
@@ -0,0 +1,13 @@
1
+ module Cats
2
+ module Core
3
+ class ReceiptTransactionsController < ApplicationController
4
+ include Common
5
+
6
+ private
7
+
8
+ def model_params
9
+ params.require(:payload).permit(:source_id, :destination_id, :transaction_date, :quantity)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ module Cats
2
+ module Core
3
+ class ReceiptsController < ApplicationController
4
+ include Common
5
+
6
+ def index
7
+ receipts = Cats::Core::Receipt.where(dispatch_id: params[:id])
8
+ render json: { success: true, data: serialize(receipts) }
9
+ end
10
+
11
+ def start_stacking
12
+ receipt = Cats::Core::Receipt.find(params[:id])
13
+ service = ReceiptService.new
14
+ result = service.start_stacking(receipt)
15
+ render json: { success: true, data: serialize(result) }
16
+ rescue StandardError => e
17
+ render json: { success: false, error: e.message }
18
+ end
19
+
20
+ def stack
21
+ receipt = Cats::Core::Receipt.find(params[:id])
22
+ service = ReceiptService.new
23
+ result = service.stack(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
+ private
30
+
31
+ def model_params
32
+ params.require(:payload).permit(:dispatch_id, :quantity, :commodity_status, :status, :remark, :prepared_by_id)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -13,6 +13,7 @@ module Cats
13
13
  belongs_to :prepared_by, class_name: 'Cats::Core::User'
14
14
  belongs_to :transporter
15
15
  belongs_to :allocation_item
16
+ has_many :receipts
16
17
 
17
18
  validates :reference_no, :plate_no, :driver_name, :driver_phone, :quantity, :commodity_status, presence: true
18
19
  validates :dispatch_status, presence: true, inclusion: { in: DISPATCH_STATUSES }
@@ -3,17 +3,48 @@ module Cats
3
3
  class Receipt < ApplicationRecord
4
4
  # Receipt status
5
5
  DRAFT = 'Draft'.freeze
6
- ACCEPTED = 'Accepted'.freeze
7
- DECLINED = 'Declined'.freeze
8
- RECEIPT_STATUSES = [DRAFT, ACCEPTED, DECLINED].freeze
6
+ CONFIRMED = 'Confirmed'.freeze
7
+ STACKING = 'Stacking'.freeze
8
+ STACKED = 'Stacked'.freeze
9
+ RECEIPT_STATUSES = [DRAFT, CONFIRMED, STACKING, STACKED].freeze
9
10
 
10
11
  belongs_to :dispatch
11
12
  belongs_to :prepared_by, class_name: 'Cats::Core::User'
13
+ has_many :receipt_transactions, foreign_key: :source_id
12
14
 
13
15
  validates :quantity, :commodity_status, :status, presence: true
14
16
  validates :quantity, numericality: { greater_than: 0 }
15
17
  validates :status, inclusion: { in: RECEIPT_STATUSES }
16
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)
37
+ diff = dispatch.quantity - received
38
+ if new_record?
39
+ return if quantity <= diff
40
+
41
+ errors.add(:quantity, "total is higher than dispatch quantity (Max = #{diff}).")
42
+ else
43
+ return unless diff.negative?
44
+
45
+ errors.add(:quantity, "total is higher than dispatch quantity (Max = #{diff.abs}).")
46
+ end
47
+ end
17
48
  end
18
49
  end
19
50
  end
@@ -3,6 +3,8 @@ module Cats
3
3
  class ReceiptTransaction < Transaction
4
4
  belongs_to :source, class_name: 'Cats::Core::Receipt'
5
5
  belongs_to :destination, class_name: 'Cats::Core::Stack'
6
+
7
+ delegate(:code, to: :destination, prefix: true)
6
8
  end
7
9
  end
8
10
  end
@@ -12,12 +12,14 @@ module Cats
12
12
  validates :transaction_date, :quantity, :status, presence: true
13
13
  validates :quantity, numericality: { greater_than: 0 }
14
14
  validates :status, inclusion: { in: STATUSES }
15
- validate :validate_quantity
15
+ validate :validate_quantity, unless: :skip_quantity_validation
16
16
 
17
17
  def validate_quantity
18
18
  return unless quantity.present? && source.present?
19
19
 
20
- errors.add(:quantity, 'cannot be more than source quantity') if quantity > source.quantity
20
+ total = self.class.where(source: source).sum(:quantity)
21
+ diff = source.quantity - total
22
+ errors.add(:quantity, "total is higher than source quantity (Max = #{diff}).") if quantity > diff
21
23
  end
22
24
 
23
25
  def commit
@@ -27,10 +29,23 @@ module Cats
27
29
  source.save
28
30
  destination.save
29
31
  self.status = COMMITTED
30
- save
32
+ save!
31
33
  end
32
34
  end
33
35
 
36
+ def skip_quantity_validation
37
+ # Quantity validation should be skipped if we are commiting transactions.
38
+ (
39
+ instance_of?(Cats::Core::DispatchTransaction) &&
40
+ destination &&
41
+ destination.dispatch_status == Cats::Core::Dispatch::STARTED
42
+ ) || (
43
+ instance_of?(Cats::Core::ReceiptTransaction) &&
44
+ source &&
45
+ source.status == Cats::Core::Receipt::STACKING
46
+ )
47
+ end
48
+
34
49
  def set_status
35
50
  return unless new_record?
36
51
 
@@ -0,0 +1,4 @@
1
+ class ReceiptSerializer < ActiveModel::Serializer
2
+ attributes :id, :dispatch_id, :dispatch_reference_no, :commodity_status, :quantity, :status, :remark,
3
+ :prepared_by_id, :prepared_by_email
4
+ end
@@ -0,0 +1,8 @@
1
+ class ReceiptTransactionSerializer < ActiveModel::Serializer
2
+ attributes :id, :source_id, :dispatch_reference, :destination_id, :destination_code, :quantity, :transaction_date,
3
+ :status
4
+
5
+ def dispatch_reference
6
+ object.source.dispatch.reference_no
7
+ end
8
+ end
@@ -40,6 +40,53 @@ module Cats
40
40
  dispatch
41
41
  end
42
42
 
43
+ def search(user, status)
44
+ details = user.details
45
+
46
+ unless details['stores'] || details['warehouse'] || details['hub']
47
+ raise(StandardError, 'User does not have associated location.')
48
+ end
49
+
50
+ if details['stores']
51
+ warehouse = Cats::Core::Store.find(details['stores'][0]).warehouse
52
+ hub = warehouse.parent
53
+
54
+ dispatches = Cats::Core::Dispatch.joins(:allocation_item)
55
+ .where(
56
+ allocation_item: { destination: hub },
57
+ dispatch_status: status
58
+ ).or(
59
+ Cats::Core::Dispatch.joins(:allocation_item)
60
+ .where(
61
+ allocation_item: { destination: warehouse },
62
+ dispatch_status: status
63
+ )
64
+ )
65
+ elsif details['warehouse']
66
+ warehouse = Cats::Core::Location.find(details['warehouse'])
67
+ hub = warehouse.parent
68
+ dispatches = Cats::Core::Dispatch.joins(:allocation_item)
69
+ .where(
70
+ allocation_item: { destination: hub },
71
+ dispatch_status: status
72
+ ).or(
73
+ Cats::Core::Dispatch.joins(:allocation_item)
74
+ .where(
75
+ allocation_item: { destination: warehouse },
76
+ dispatch_status: status
77
+ )
78
+ )
79
+ elsif details['hub']
80
+ hub = Cats::Core::Location.find(details['hub'])
81
+ dispatches = Cats::Core::Dispatch.joins(:allocation_item)
82
+ .where(
83
+ allocation_item: { destination: hub },
84
+ dispatch_status: status
85
+ )
86
+ end
87
+ dispatches
88
+ end
89
+
43
90
  def approve(dispatch)
44
91
  unless dispatch.dispatch_status == Cats::Core::Dispatch::DRAFT
45
92
  raise(StandardError, 'Dispatch has to be in draft state.')
@@ -64,6 +111,26 @@ module Cats
64
111
  transactions.each(&:commit)
65
112
  dispatch
66
113
  end
114
+
115
+ def confirm(dispatch)
116
+ total = dispatch.receipts.sum(:quantity)
117
+
118
+ unless total == dispatch.quantity
119
+ raise(
120
+ StandardError,
121
+ "There is an amount of #{dispatch.quantity - total} in the dispatch which is unaccounted for."
122
+ )
123
+ end
124
+
125
+ Cats::Core::Dispatch.transaction do
126
+ dispatch.dispatch_status = Cats::Core::Dispatch::RECEIVED
127
+ dispatch.receipts.each { |r| r.status = Cats::Core::Receipt::CONFIRMED }
128
+ dispatch.save
129
+ dispatch.receipts.each(&:save!)
130
+ end
131
+
132
+ dispatch
133
+ end
67
134
  end
68
135
  end
69
136
  end
@@ -0,0 +1,32 @@
1
+ module Cats
2
+ module Core
3
+ class ReceiptService
4
+ def start_stacking(receipt)
5
+ raise(StandardError, 'Receipt should be confirmed.') unless receipt.status == Cats::Core::Receipt::CONFIRMED
6
+
7
+ raise(StandardError, 'There are no stack assignments in receipt.') if receipt.receipt_transactions.count.zero?
8
+
9
+ receipt.status = Cats::Core::Receipt::STACKING
10
+ receipt.save!
11
+ receipt
12
+ end
13
+
14
+ def stack(receipt)
15
+ unless receipt.status == Cats::Core::Receipt::STACKING
16
+ raise(StandardError, 'Receipt should be in stacking state.')
17
+ end
18
+
19
+ receipt.status = Cats::Core::Receipt::STACKED
20
+ stacks = receipt.receipt_transactions.map(&:destination)
21
+ stacks.each { |stack| stack.stack_status = Cats::Core::Stack::ALLOCATED }
22
+
23
+ Cats::Core::Receipt.transaction do
24
+ receipt.receipt_transactions.each(&:commit)
25
+ stacks.each(&:save!)
26
+ receipt.save!
27
+ end
28
+ receipt
29
+ end
30
+ end
31
+ end
32
+ end
data/config/routes.rb CHANGED
@@ -56,11 +56,21 @@ Cats::Core::Engine.routes.draw do
56
56
  action: :create_receipt_authorization,
57
57
  as: :receipt_authorization_allocation_item
58
58
 
59
+ get '/dispatches/search', controller: :dispatches, action: :search, as: :search_dispatches
59
60
  resources :dispatches, except: %i[index destroy] do
60
61
  member do
62
+ get 'receipts', controller: :receipts, action: :index
61
63
  post 'approve'
62
64
  post 'start'
65
+ post 'confirm'
63
66
  end
64
67
  end
65
68
  resources :dispatch_transactions, except: %i[new edit destroy]
69
+ resources :receipts, except: %i[index new edit destroy] do
70
+ member do
71
+ post 'start_stacking'
72
+ post 'stack'
73
+ end
74
+ end
75
+ resources :receipt_transactions, except: %i[new edit destroy]
66
76
  end
@@ -1,5 +1,5 @@
1
1
  module Cats
2
2
  module Core
3
- VERSION = '1.1.0'.freeze
3
+ VERSION = '1.1.4'.freeze
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  FactoryBot.define do
2
- factory :receipt_transaction do
2
+ factory :receipt_transaction, class: 'Cats::Core::ReceiptTransaction' do
3
3
  source factory: :receipt
4
4
  destination factory: :stack
5
5
  transaction_date { Date.today }
@@ -1,7 +1,7 @@
1
1
  FactoryBot.define do
2
2
  factory :receipt, class: 'Cats::Core::Receipt' do
3
- dispatch
4
- quantity { 1.5 }
3
+ dispatch factory: :dispatch, dispatch_status: Cats::Core::Dispatch::STARTED
4
+ quantity { 50 }
5
5
  commodity_status { Cats::Core::Commodity::GOOD }
6
6
  status { Cats::Core::Receipt::DRAFT }
7
7
  remark { FFaker::Name.name }
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.1.0
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henock L.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-23 00:00:00.000000000 Z
11
+ date: 2021-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_model_serializers
@@ -225,6 +225,8 @@ files:
225
225
  - app/controllers/cats/core/locations_controller.rb
226
226
  - app/controllers/cats/core/menus_controller.rb
227
227
  - app/controllers/cats/core/notifications_controller.rb
228
+ - app/controllers/cats/core/receipt_transactions_controller.rb
229
+ - app/controllers/cats/core/receipts_controller.rb
228
230
  - app/controllers/cats/core/roles_controller.rb
229
231
  - app/controllers/cats/core/spaces_controller.rb
230
232
  - app/controllers/cats/core/unit_of_measures_controller.rb
@@ -275,11 +277,14 @@ files:
275
277
  - app/serializers/cats/core/dispatch_serializer.rb
276
278
  - app/serializers/cats/core/dispatch_transaction_serializer.rb
277
279
  - app/serializers/cats/core/location_serializer.rb
280
+ - app/serializers/cats/core/receipt_serializer.rb
281
+ - app/serializers/cats/core/receipt_transaction_serializer.rb
278
282
  - app/serializers/cats/core/role_menu_serializer.rb
279
283
  - app/serializers/cats/core/unit_of_measure_serializer.rb
280
284
  - app/services/cats/core/dispatch_service.rb
281
285
  - app/services/cats/core/menu_service.rb
282
286
  - app/services/cats/core/notification_service.rb
287
+ - app/services/cats/core/receipt_service.rb
283
288
  - app/services/cats/core/space_service.rb
284
289
  - app/services/cats/core/token_auth_service.rb
285
290
  - config/routes.rb