Task stock consumption modal[SCI-6444][SCI-6445] (#3798)

Add main logic for consumption on task [SCI-6444]

Co-authored-by: Anton <anton@scinote.net>
This commit is contained in:
aignatov-bio 2022-01-28 11:10:56 +01:00 committed by GitHub
parent 371d5f0636
commit 2c88843924
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 267 additions and 13 deletions

View file

@ -191,6 +191,10 @@ var MyModuleRepositories = (function() {
});
}
function reloadSimpleTable() {
SIMPLE_TABLE.ajax.reload(null, false);
}
function renderFullViewTable(tableContainer, options = {}) {
if (FULL_VIEW_TABLE) FULL_VIEW_TABLE.destroy();
SELECTED_ROWS = {};
@ -741,6 +745,9 @@ var MyModuleRepositories = (function() {
initRepoistoryAssignView();
initSelectAllCheckbox();
initExportAssignedRows();
},
reloadSimpletable: () => {
reloadSimpleTable();
}
};
}());

View file

@ -0,0 +1,68 @@
/* global SmartAnnotation I18n MyModuleRepositories */
var MyModuleStockConsumption = (function() {
const CONSUMPTION_MODAL = '#consumeRepositoryStockValueModal';
const WARNING_MODAL = '#consumeRepositoryStockValueModalWarning';
function initManageAction() {
$('.task-section').on('click', '.manage-repository-consumed-stock-value-link', function(e) {
e.preventDefault();
$.ajax({
url: $(this).attr('href'),
type: 'GET',
dataType: 'json',
success: (result) => {
var $manageModal = $(CONSUMPTION_MODAL);
$manageModal.find('.modal-content').html(result.html);
$manageModal.modal('show');
SmartAnnotation.init($(CONSUMPTION_MODAL + ' #comment')[0]);
$('#stock_consumption').on('change', function() {
let initialValue = parseFloat($(this).data('initial-value'));
let initialStock = parseFloat($(this).data('initial-stock'));
let finalValue = initialValue - ($(this).val() || 0) + initialStock;
$('.stock-final-container .value').text(finalValue);
$('.stock-final-container').toggleClass('error', finalValue <= 0);
$('.update-consumption-button').attr('disabled', $(this).val() === '');
});
$(CONSUMPTION_MODAL + ' form').on('ajax:success', function() {
MyModuleRepositories.reloadSimpletable();
$manageModal.modal('hide');
$(WARNING_MODAL).modal('hide');
});
$('.update-consumption-button').on('click', function(event, skipValidation) {
if (parseFloat($('.stock-final-container .value').text()) < 0 && !skipValidation) {
event.preventDefault();
$manageModal.modal('hide');
$(WARNING_MODAL).modal('show');
let units = $(CONSUMPTION_MODAL).find('.consumption-container .units').text();
let value = $('#stock_consumption').val();
$(WARNING_MODAL).find('.modal-body p').text(
I18n.t('my_modules.repository.stock_warning_modal.description', { value: `${value} ${units}` })
);
}
});
}
});
});
}
function initWarningModal() {
$(WARNING_MODAL).on('click', '.cancel-consumption', function() {
$(WARNING_MODAL).modal('hide');
$(CONSUMPTION_MODAL).modal('show');
}).on('click', '.confirm-consumption-button', function() {
$('.update-consumption-button').trigger('click', [true]);
});
}
return {
init: () => {
initManageAction();
initWarningModal();
}
};
}());
MyModuleStockConsumption.init();

View file

@ -210,16 +210,16 @@ $.fn.dataTable.render.RepositoryConsumedStockValue = function(data) {
let canManage = $('.repository-table').data('stock-consumption-editable');
if (data && data.value.consumed_stock_formatted) {
if (canManage) {
return `<a class="manage-repository-stock-value-link stock-value-view-render">
${data.value.consumed_stock_formatted}
return `<a href="${data.updateStockConsumptionUrl}" class="manage-repository-consumed-stock-value-link stock-value-view-render">
${data.value.consumed_stock_formatted} ${data.value.unit}
</a>`;
}
return `<span class="stock-value-view-render">
${data.value.consumed_stock_formatted}
${data.value.consumed_stock_formatted} ${data.value.unit}
</span>`;
}
if (canManage && data && data.stock_present) {
return `<a class="manage-repository-consumed-stock-value-link">
return `<a href="${data.updateStockConsumptionUrl}" class="manage-repository-consumed-stock-value-link">
<i class="fas fa-vial"></i>
${I18n.t('libraries.manange_modal_column.stock_type.add_stock_consumption')}
</a>`;

View file

@ -0,0 +1,16 @@
#consumeRepositoryStockValueModal {
.consumption-container {
align-items: center;
display: grid;
grid-template-columns: 1fr 1fr;
.units {
margin: 1.25em 0 0 .5em;
}
}
.comments-container {
margin-bottom: 1em;
}
}

View file

@ -1,7 +1,8 @@
// scss-lint:disable SelectorDepth
// scss-lint:disable NestingDepth
.repository-stock-modal {
.repository-stock-modal,
#consumeRepositoryStockValueModal {
.stock-update-view {
align-items: center;
display: grid;
@ -28,6 +29,12 @@
.units {
@include font-small;
}
&.error {
.value {
color: $brand-danger;
}
}
}
.stock-arrow {

View file

@ -5,8 +5,9 @@ class MyModuleRepositoriesController < ApplicationController
before_action :load_my_module
before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html)
before_action :check_my_module_view_permissions, except: :update
before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption)
before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html)
before_action :check_repository_row_consumption_permissions, only: %i(consume_modal update_consumption)
before_action :check_assign_repository_records_permissions, only: :update
def index_dt
@ -140,6 +141,31 @@ class MyModuleRepositoriesController < ApplicationController
end
end
def consume_modal
@repository_row = @repository.repository_rows.find(params[:row_id])
@module_repository_row = @my_module.my_module_repository_rows.find_by(repository_row: @repository_row)
@stock_value = @module_repository_row.repository_row.repository_stock_value
render json: {
html: render_to_string(
partial: 'my_modules/repositories/consume_stock_modal_content.html.erb'
)
}
end
def update_consumption
module_repository_row = @my_module.my_module_repository_rows.find_by(id: params[:module_row_id])
module_repository_row.with_lock do
module_repository_row.assign_attributes(
stock_consumption: params[:stock_consumption],
last_modified_by: current_user,
comment: params[:comment]
)
module_repository_row.save!
end
render json: {}, status: :ok
end
private
def load_my_module
@ -164,6 +190,10 @@ class MyModuleRepositoriesController < ApplicationController
render_403 unless can_assign_my_module_repository_rows?(@my_module)
end
def check_repository_row_consumption_permissions
render_403 unless can_update_my_module_stock_consumption?(@my_module)
end
def update_flash_message(service)
assigned_count = service.assigned_rows_count
unassigned_count = service.unassigned_rows_count

View file

@ -50,7 +50,7 @@ module RepositoryDatatableHelper
end
end
def prepare_simple_view_row_columns(repository_rows)
def prepare_simple_view_row_columns(repository_rows, my_module)
repository_rows.map do |record|
row = {
DT_RowId: record.id,
@ -66,8 +66,14 @@ module RepositoryDatatableHelper
end
row['2'] = {
stock_present: record.repository_stock_cell.present?,
updateStockConsumptionUrl: Rails.application.routes.url_helpers.consume_modal_my_module_repository_path(
my_module,
record.repository,
row_id: record.id
),
value: {
consumed_stock_formatted: record.consumed_stock
consumed_stock_formatted: record.consumed_stock,
unit: record.repository_stock_value.repository_stock_unit_item&.data
}
}
end

View file

@ -1,4 +1,7 @@
class MyModuleRepositoryRow < ApplicationRecord
attr_accessor :last_modified_by
attr_accessor :comment
belongs_to :assigned_by,
foreign_key: 'assigned_by_id',
class_name: 'User',
@ -13,17 +16,28 @@ class MyModuleRepositoryRow < ApplicationRecord
around_save :deduct_stock_balance, if: :stock_consumption_changed?
before_save :nulify_stock_consumption, if: :stock_consumption_changed?
private
def nulify_stock_consumption
self.stock_consumption = nil if stock_consumption.zero?
end
def deduct_stock_balance
stock_value = repository_row.repository_stock_value
delta = stock_consumption_was.to_d - stock_consumption.to_d
lock!
delta = stock_consumption.to_d - stock_consumption_was.to_d
stock_value.lock!
stock_value.amount = stock_value.amount - delta
yield
stock_value.save!
stock_value.repository_ledger_records.create!(user: last_modified_by, amount: delta, balance: stock_value.amount)
stock_value.repository_ledger_records.create!(
reference: self,
user: last_modified_by,
amount: delta,
balance: stock_value.amount,
comment: comment
)
save!
end
end

View file

@ -34,7 +34,8 @@ class RepositoryRow < ApplicationRecord
repository_date: 'RepositoryDateValue',
repository_date_time_range: 'RepositoryDateTimeRangeValue',
repository_time_range: 'RepositoryTimeRangeValue',
repository_date_range: 'RepositoryDateRangeValue'
repository_date_range: 'RepositoryDateRangeValue',
repository_stock: 'RepositoryStockValue'
}.each do |relation, class_name|
has_many "#{relation}_cells".to_sym, -> { where(value_type: class_name) }, class_name: 'RepositoryCell',
inverse_of: :repository_row

View file

@ -157,6 +157,8 @@
<!-- Delete file modal -->
<%= render partial: 'assets/asset_delete_modal.html.erb' %>
<%= render partial: 'my_modules/repositories/consume_stock_modal.html.erb'%>
<%= stylesheet_link_tag 'datatables' %>
<%= javascript_include_tag("my_modules/protocols") %>
<%= javascript_include_tag("my_modules/status_flow") %>

View file

@ -0,0 +1,39 @@
<div class="modal small"
id="consumeRepositoryStockValueModal"
tabindex="-1"
role="dialog"
aria-labelledby="consumeRepositoryStockValueLabel">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
</div>
</div>
</div>
<div class="modal small"
id="consumeRepositoryStockValueModalWarning"
tabindex="-1"
role="dialog">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
<%= t('my_modules.repository.stock_warning_modal.title') %>
</h4>
</div>
<div class="modal-body">
<p></p>
<div class="modal-footer">
<button type="button"
id="cancel"
class="btn btn-default cancel-consumption"><%=t('general.cancel') %></button>
<%= submit_tag t('my_modules.repository.stock_warning_modal.consume_anyway'), class: "btn btn-primary confirm-consumption-button"%>
</div>
</div>
</div>
</div>
<%= javascript_include_tag 'my_modules/stock' %>

View file

@ -0,0 +1,50 @@
<%= form_with url: update_consumption_my_module_repository_path(@my_module, @repository, module_row_id: @module_repository_row), method: :post, remote: true do |f| %>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
<%= t('my_modules.repository.stock_modal.title', name: @repository_row.name)%>
</h4>
</div>
<div class="modal-body">
<p><%= t('my_modules.repository.stock_modal.description') %></p>
<div class="consumption-container">
<div class="sci-input-container">
<%= f.label :stock_consumption, t('my_modules.repository.stock_modal.amount') %>
<%= f.number_field :stock_consumption, value: @module_repository_row.stock_consumption , placeholder: t('my_modules.repository.stock_modal.consumed') , class: 'sci-input-field', data: {initial_value: (@module_repository_row.stock_consumption || 0), initial_stock: @stock_value.amount } %>
</div>
<span class="units"> <%= @stock_value.repository_stock_unit_item&.data %></span>
</div>
<div class="sci-input-container comments-container">
<%= f.label :comment, t('my_modules.repository.stock_modal.comment') %>
<%= f.text_field :comment, placeholder: t('my_modules.repository.stock_modal.enter_comment') , class: 'sci-input-field' %>
</div>
<div class="row mt-3">
<div class="col-sm-12">
<div class="stock-update-view">
<div class="stock-initial-container">
<span class="subtitle"><%= t('repository_stock_values.manage_modal.current_stock') %></span>
<span class="value"><%= @stock_value.amount %></span>
<span class="units"><%= @stock_value.repository_stock_unit_item&.data %></span>
</div>
<div class="stock-arrow">
<i class="fas fa-arrow-right"></i>
</div>
<div class="stock-final-container">
<span class="subtitle"><%= t('repository_stock_values.manage_modal.new_stock') %></span>
<span class="value">-</span>
<span class="units"><%= @stock_value.repository_stock_unit_item&.data %></span>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button"
id="cancel"
class="btn btn-default"
data-dismiss="modal"><%=t('general.cancel') %></button>
<%= submit_tag t('general.save'), class: "btn btn-primary update-consumption-button", disabled: @module_repository_row.stock_consumption.nil? %>
</div>
<% end %>

View file

@ -2,7 +2,7 @@
json.draw @draw
json.data do
json.array! prepare_simple_view_row_columns(@repository_rows)
json.array! prepare_simple_view_row_columns(@repository_rows, @my_module)
end
json.recordsFiltered @repository_rows.first ? @repository_rows.first.filtered_count : 0
json.recordsTotal @all_rows_count

View file

@ -33,6 +33,7 @@ Rails.application.config.assets.precompile += %w(my_modules/repositories.js)
Rails.application.config.assets.precompile += %w(my_modules/status_flow.js)
Rails.application.config.assets.precompile += %w(my_modules/protocols/protocol_status_bar.js)
Rails.application.config.assets.precompile += %w(my_modules/results.js)
Rails.application.config.assets.precompile += %w(my_modules/stock.js)
Rails.application.config.assets.precompile += %w(my_modules/archived.js)
Rails.application.config.assets.precompile += %w(my_modules/pwa_mobile_app.js)
Rails.application.config.assets.precompile += %w(assets/wopi/create_wopi_file.js)

View file

@ -995,6 +995,17 @@ en:
set_default_button: 'Set as default view'
created_by: 'by %{full_name}'
provisioning: 'Provisioning'
stock_modal:
title: 'Consume %{name}'
description: 'Enter the total amount consumed in this task.'
amount: 'Amount'
consumed: 'Consumed'
comment: 'Comment (optional)'
enter_comment: 'Enter a comment'
stock_warning_modal:
title: 'Out of stock!'
description: 'By consuming %{value} you will use more than you currently have in stock. This will result in negative stock.'
consume_anyway: 'Consume anyway'
flash:
assign_to_task_html: "Successfully assigned <strong>%{assigned_items}</strong> item(s) to the task."
assign_to_task_and_downstream_html: "Successfully assigned <strong>%{assigned_items}</strong> item(s) to the task and downstream tasks."

View file

@ -388,6 +388,8 @@ Rails.application.routes.draw do
post :export_repository
get :assign_repository_records_modal, as: :assign_modal
get :update_repository_records_modal, as: :update_modal
get :consume_modal
post :update_consumption
end
end