cats_core 1.1.0 → 1.1.4

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 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