diff --git a/app/controllers/repository_stock_values_controller.rb b/app/controllers/repository_stock_values_controller.rb index aa5b6c072..de53aa9ca 100644 --- a/app/controllers/repository_stock_values_controller.rb +++ b/app/controllers/repository_stock_values_controller.rb @@ -44,19 +44,12 @@ class RepositoryStockValuesController < ApplicationController ) end - render json: @repository_stock_vlaue + render json: @repository_stock_value end private def update! - @repository_stock_value.update_stock_with_ledger!( - repository_stock_value_params[:amount], - @repository, - repository_stock_value_params[:comment].presence - ) - @repository_stock_value.repository_stock_unit_item = - @repository_column.repository_stock_unit_items.find(repository_stock_value_params[:unit_item_id]) @repository_stock_value.update_data!(repository_stock_value_params, current_user) end @@ -66,9 +59,7 @@ class RepositoryStockValuesController < ApplicationController repository_stock_value_params, repository_cell: repository_cell, created_by: current_user, - last_modified_by: current_user, - repository_stock_unit_item: @repository_column.repository_stock_unit_items - .find(repository_stock_value_params[:unit_item_id]) + last_modified_by: current_user ) @repository_stock_value.save! @repository_stock_value.update_stock_with_ledger!( diff --git a/app/models/repository_cell.rb b/app/models/repository_cell.rb index 9f2c27e0b..34a7cafe1 100644 --- a/app/models/repository_cell.rb +++ b/app/models/repository_cell.rb @@ -34,6 +34,8 @@ class RepositoryCell < ApplicationRecord has_many :hidden_repository_cell_reminders, dependent: :destroy + before_destroy :prevent_stock_cell_destroy + validates :repository_column, inclusion: { in: (lambda do |repository_cell| repository_cell.repository_row&.repository&.repository_columns || [] @@ -58,6 +60,12 @@ class RepositoryCell < ApplicationRecord last_modified_by: user) cell.value = value value.save! + + if column.data_type == 'RepositoryStockValue' + value.update_stock_with_ledger!(value.amount, + value.repository_cell.repository_column.repository, + '') + end end cell end @@ -84,4 +92,8 @@ class RepositoryCell < ApplicationRecord errors.add(:value_type, 'must match column data type') end end + + def prevent_stock_cell_destroy + raise NotImplementedError if value.class.name == 'RepositoryStockValue' + end end diff --git a/app/models/repository_stock_value.rb b/app/models/repository_stock_value.rb index 85d96721a..d87812290 100644 --- a/app/models/repository_stock_value.rb +++ b/app/models/repository_stock_value.rb @@ -83,14 +83,24 @@ class RepositoryStockValue < ApplicationRecord end end - def data_changed?(new_data) - BigDecimal(new_data.to_s) != data + def data_different?(new_data) + BigDecimal(new_data[:amount].to_s) != amount || + (new_data[:unit_item_id].present? && new_data[:unit_item_id] != repository_stock_unit_item.id) end def update_data!(new_data, user) - self.amount = BigDecimal(new_data[:amount].to_s) - self.low_stock_threshold = new_data[:low_stock_threshold] + self.low_stock_threshold = new_data[:low_stock_threshold] if new_data[:low_stock_threshold].present? + self.repository_stock_unit_item = repository_cell + .repository_column + .repository_stock_unit_items + .find(new_data[:unit_item_id]) self.last_modified_by = user + + update_stock_with_ledger!(new_data[:amount], + repository_cell.repository_column.repository, + new_data[:comment].presence) + + self.amount = BigDecimal(new_data[:amount].to_s) save! end @@ -116,10 +126,18 @@ class RepositoryStockValue < ApplicationRecord end def self.new_with_payload(payload, attributes) - value = new(attributes) - value.amount = payload[:amount] - value.low_stock_threshold = payload[:low_stock_threshold] - value + if payload[:amount].present? + value = new(attributes) + value.amount = payload[:amount] + value.low_stock_threshold = payload[:low_stock_threshold] + value.repository_stock_unit_item = value.repository_cell + .repository_column + .repository_stock_unit_items + .find(payload['unit_item_id']) + value + else + raise ActiveRecord::RecordInvalid, 'Missing amount value' + end end def self.import_from_text(text, attributes, _options = {}) diff --git a/spec/requests/api/v1/inventory_cells_controller_spec.rb b/spec/requests/api/v1/inventory_cells_controller_spec.rb index aca76209d..bc2015139 100644 --- a/spec/requests/api/v1/inventory_cells_controller_spec.rb +++ b/spec/requests/api/v1/inventory_cells_controller_spec.rb @@ -42,6 +42,11 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do @date_time_range_column = create(:repository_column, repository: @valid_inventory, data_type: :RepositoryDateTimeRangeValue) @number_column = create(:repository_column, repository: @valid_inventory, data_type: :RepositoryNumberValue) + @stock_column = create(:repository_column, name: Faker::Name.unique.name, + data_type: :RepositoryStockValue, repository: @valid_inventory) + @repository_stock_unit_item = create( :repository_stock_unit_item, created_by: @user, + last_modified_by: @user, + repository_column: @stock_column) @valid_item = create(:repository_row, repository: @valid_inventory) @@ -80,6 +85,10 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do create(:repository_number_value, data: 1234.5678, repository_cell_attributes: { repository_row: @valid_item, repository_column: @number_column }) + create(:repository_stock_value, + amount: 10, + repository_stock_unit_item: @repository_stock_unit_item, + repository_cell_attributes: { repository_row: @valid_item, repository_column: @stock_column }) @valid_headers = { 'Authorization': 'Bearer ' + generate_token(@user.id), @@ -214,6 +223,18 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do } } } + @valid_stock_body = { + data: { + type: 'inventory_cells', + attributes: { + column_id: @stock_column.id, + value: { + amount: 19, + unit_item_id: @repository_stock_unit_item.id + } + } + } + } @update_text_body = { data: { id: @valid_item.repository_cells.where(repository_column: @text_column).first.id, @@ -356,6 +377,19 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do } } } + @update_stock_body = { + data: { + id: @valid_item.repository_cells.where(repository_column: @stock_column).first.id, + type: 'inventory_cells', + attributes: { + column_id: @stock_column.id, + value: { + amount: 20, + unit_item_id: @repository_stock_unit_item.id + } + } + } + } end describe 'GET inventory_cells, #index' do @@ -696,6 +730,55 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do ) end + it 'Response with correct inventory cell, stock cell' do + hash_body = nil + empty_item = create(:repository_row, repository: @valid_inventory) + post api_v1_team_inventory_item_cells_path( + team_id: @team.id, + inventory_id: @valid_inventory.id, + item_id: empty_item.id + ), params: @valid_stock_body.to_json, headers: @valid_headers + expect(response).to have_http_status 201 + expect { hash_body = json }.not_to raise_exception + expect(hash_body[:data]).to match( + JSON.parse( + ActiveModelSerializers::SerializableResource + .new(RepositoryCell.last, serializer: Api::V1::InventoryCellSerializer) + .to_json + )['data'] + ) + end + + it 'Response with inventory cell, stock cell, missing stock unit' do + hash_body = nil + empty_item = create(:repository_row, repository: @valid_inventory) + invalid_file_body = @valid_stock_body.deep_dup + invalid_file_body[:data][:attributes][:value].delete(:unit_item_id) + post api_v1_team_inventory_item_cells_path( + team_id: @team.id, + inventory_id: @valid_inventory.id, + item_id: empty_item.id + ), params: invalid_file_body.to_json, headers: @valid_headers + expect(response).to have_http_status 404 + expect { hash_body = json }.not_to raise_exception + expect(hash_body['errors'][0]).to include('status': 404) + end + + it 'Response with inventory cell, stock cell, missing amount' do + hash_body = nil + empty_item = create(:repository_row, repository: @valid_inventory) + invalid_file_body = @valid_stock_body.deep_dup + invalid_file_body[:data][:attributes][:value].delete(:amount) + post api_v1_team_inventory_item_cells_path( + team_id: @team.id, + inventory_id: @valid_inventory.id, + item_id: empty_item.id + ), params: invalid_file_body.to_json, headers: @valid_headers + expect(response).to have_http_status 400 + expect { hash_body = json }.not_to raise_exception + expect(hash_body['errors'][0]).to include('status': 400) + end + it 'When invalid request, payload mismatches column type' do hash_body = nil invalid_file_body = @valid_file_body.dup @@ -1010,6 +1093,26 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do ) end + it 'Response with correct inventory cell, stock cell' do + hash_body = nil + patch api_v1_team_inventory_item_cell_path( + team_id: @team.id, + inventory_id: @valid_inventory.id, + item_id: @valid_item.id, + id: @valid_item.repository_cells.where(repository_column: @stock_column).first.id + ), params: @update_stock_body.to_json, headers: @valid_headers + expect(response).to have_http_status 200 + expect { hash_body = json }.not_to raise_exception + expect(hash_body[:data]).to match( + JSON.parse( + ActiveModelSerializers::SerializableResource + .new(@valid_item.repository_cells.where(repository_column: @stock_column).first, + serializer: Api::V1::InventoryCellSerializer) + .to_json + )['data'] + ) + end + it 'When invalid request, payload mismatches column type' do hash_body = nil invalid_file_body = @valid_file_body.dup @@ -1067,7 +1170,7 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do describe 'DELETE inventory_cells, #destroy' do it 'Destroys inventory cell' do - deleted_id = @valid_item.repository_cells.last.id + deleted_id = @valid_item.repository_cells.where(repository_column: @number_column).first.id delete api_v1_team_inventory_item_cell_path( id: deleted_id, team_id: @team.id, @@ -1078,6 +1181,18 @@ RSpec.describe 'Api::V1::InventoryCellsController', type: :request do expect(RepositoryCell.where(id: deleted_id)).to_not exist end + it 'Destroys stock inventory cell' do + deleted_id = @valid_item.repository_cells.where(repository_column: @stock_column).first.id + delete api_v1_team_inventory_item_cell_path( + id: deleted_id, + team_id: @team.id, + inventory_id: @valid_inventory.id, + item_id: @valid_item.id + ), headers: @valid_headers + expect(response).to have_http_status(400) + expect(RepositoryCell.where(id: deleted_id)).to exist + end + it 'Invalid request, non existing inventory item' do deleted_id = RepositoryCell.last.id + 1 delete api_v1_team_inventory_item_cell_path(