Skip to content

Commit

Permalink
First implementations of #24 and #10
Browse files Browse the repository at this point in the history
  • Loading branch information
lentschi committed Nov 19, 2023
1 parent 700f9a5 commit 56acdcb
Show file tree
Hide file tree
Showing 48 changed files with 1,954 additions and 82 deletions.
4 changes: 4 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ $(function() {
// Handle ajax errors
// render json: {error: "can't except this!"}, status: :unprocessable_entity
$(document).ajaxError(function(ev, xhr, settings, exception) {
if (xhr.statusText === 'abort') {
return;
}

try {
msg = xhr.responseJSON.error;
} catch(err) {
Expand Down
8 changes: 8 additions & 0 deletions app/assets/stylesheets/bootstrap_and_overrides.css.less
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ body {
@articleUnavailColor: #999;
@articleUpdatedColor: #468847;

// article units:
@unitUntranslatedColor: #999;

// dim colors by this amount when the information is less important
@nonessentialDim: 35%;

Expand Down Expand Up @@ -330,6 +333,11 @@ td.symbol, th.symbol {
}
}

// ********* Article units
tr.untranslated {
color: @unitUntranslatedColor;
}


// ********* Tweaks & fixes

Expand Down
59 changes: 59 additions & 0 deletions app/controllers/article_units_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class ArticleUnitsController < ApplicationController
before_action :load_available_units, only: [:search, :create, :destroy]

def index; end

def search
@query = params[:q].blank? ? nil : params[:q].downcase

existing_article_units = ArticleUnit.all.to_a
@article_units = @available_units
.to_h do |key, value|
[key, value.merge({ code: key, record: existing_article_units.find { |existing_unit| existing_unit.unit == key } })]
end

unless @query.nil?
@article_units = @article_units.select do |_key, value|
value[:name].downcase.include?(@query) || value[:symbol]&.downcase&.include?(@query)
end
end

@article_units = @article_units
.sort { |a, b| sort_by_unit_name(@query, a, b) }
.map { |_key, value| value }

@article_units = @article_units.take(100) unless @query.nil?
@article_units = @article_units.reject { |unit| unit[:record].nil? } if @query.nil?
end

def create
@article_unit = ArticleUnit.create(unit: params[:unit])
end

def destroy
@article_unit = ArticleUnit.find(params[:id])
@article_unit.destroy
end

private

def load_available_units
@available_units = ArticleUnitsLib.units
end

def article_unit_params
params.require(:article_unit).permit(:unit)
end

def sort_by_unit_name(query, a_unit, b_unit)
a_name = a_unit[1][:name].downcase
b_name = b_unit[1][:name].downcase

unless query.nil?
return -1 if a_name.starts_with?(query) && !b_name.starts_with?(query)
return 1 if !a_name.starts_with?(query) && b_name.starts_with?(query)
end

a_name <=> b_name
end
end
8 changes: 7 additions & 1 deletion app/controllers/articles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,13 @@ def load_article
end

def load_article_units
@article_units = ArticleUnits.as_options
@article_units = ArticleUnit.as_options(additional_units: current_article_units)
end

def current_article_units
return [] if @article.nil?

[@article.supplier_order_unit, @article.group_order_unit, @article.billing_unit, @article.price_unit].compact
end

def new_empty_article_ratio
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/order_articles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def new
@order_article = @order.order_articles.build(params[:order_article])
end

def edit; end

def create
# The article may be ordered with zero units - in that case do not complain.
# If order_article is ordered and a new order_article is created, an error message will be
Expand All @@ -25,8 +27,6 @@ def create
render action: :new
end

def edit; end

def update
# begin
version_params = params.require(:article_version).permit(:id, :unit, :supplier_order_unit, :minimum_order_quantity, :billing_unit, :group_order_granularity, :group_order_unit, :price, :price_unit, :tax, :deposit, article_unit_ratios_attributes: [:id, :sort, :quantity, :unit, :_destroy])
Expand Down Expand Up @@ -69,7 +69,7 @@ def load_order_article
end

def load_article_units
@article_units = ArticleUnits.as_options
@article_units = ArticleUnit.as_options
end

def new_empty_article_ratio
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/stockit_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def articles_search
private

def load_article_units
@article_units = ArticleUnits.as_options
@article_units = ArticleUnit.as_options
end

def new_empty_article_ratio
Expand Down
4 changes: 2 additions & 2 deletions app/helpers/articles_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ def row_classes(article)
end

def format_supplier_article_unit(article)
return ArticleUnits.as_options.invert[article.supplier_order_unit] unless article.supplier_order_unit.nil?
return ArticleUnit.as_hash[article.supplier_order_unit][:name] unless article.supplier_order_unit.nil?

article.unit
end

def format_group_order_unit(article)
return ArticleUnits.as_options.invert[article.group_order_unit] unless article.group_order_unit.nil?
return ArticleUnit.as_hash[article.group_order_unit][:name] unless article.group_order_unit.nil?

article.unit
end
Expand Down
8 changes: 4 additions & 4 deletions app/helpers/orders_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ def ordered_quantities_different_from_group_orders?(order_article, ordered_mark
price = order_article.price
group_orders_sum_quantity = order_article.group_orders_sum[:quantity]
if !order_article.units_received.nil?
(price.convert_quantity(order_article.units_received, price.supplier_order_unit, price.billing_unit).round(3)) == group_orders_sum_quantity ? false : received_mark
price.convert_quantity(order_article.units_received, price.supplier_order_unit, price.billing_unit).round(3) == group_orders_sum_quantity ? false : received_mark
elsif !order_article.units_billed.nil?
order_article.units_billed == group_orders_sum_quantity ? false : billed_mark
elsif !order_article.units_to_order.nil?
(price.convert_quantity(order_article.units_to_order, price.supplier_order_unit, price.billing_unit).round(3)) == group_orders_sum_quantity ? false : ordered_mark
price.convert_quantity(order_article.units_to_order, price.supplier_order_unit, price.billing_unit).round(3) == group_orders_sum_quantity ? false : ordered_mark
end
end

Expand All @@ -66,9 +66,9 @@ def pkg_helper(article, options = {})
first_ratio = article&.article_unit_ratios&.first
return '' if first_ratio.nil? || first_ratio.quantity == 1

uq_text = #{first_ratio.quantity} #{ArticleUnits.as_options.invert[first_ratio.unit]}"
uq_text = #{first_ratio.quantity} #{ArticleUnit.as_options.invert[first_ratio.unit]}"
else
uq_text = ArticleUnits.as_options.invert[options[:unit]]
uq_text = ArticleUnit.as_options.invert[options[:unit]]
end
uq_text = content_tag(:span, uq_text, class: 'hidden-phone') if options[:soft_uq]
if options[:plain]
Expand Down
32 changes: 32 additions & 0 deletions app/models/article_unit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class ArticleUnit < ApplicationRecord
self.primary_key = :unit

def self.all_cached
return @all_cached unless @all_cached.nil?

@all_cached = all.load
end

def self.clear_cache
@all_cached = nil
end

def self.as_hash
available_units = all_cached.map(&:unit)
ArticleUnitsLib.units.to_h { |code, unit| [code, unit.merge({ visible: available_units.include?(code) })] }
end

def self.as_options(config)
additional_units = config&.dig(:additional_units) || []
options = {}

available_units = all_cached.map(&:unit)
ArticleUnitsLib.units.each do |code, unit|
next unless available_units.include?(code) || additional_units.include?(code)

options[unit[:name]] = code
end

options
end
end
2 changes: 1 addition & 1 deletion app/models/article_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def self.latest_outer_join_sql(article_field_name)
end

def supplier_order_unit_is_si_convertible
!ArticleUnits.units[self.supplier_order_unit]&.dig(:conversionFactor).nil?
!ArticleUnit.as_hash[self.supplier_order_unit]&.dig(:conversionFactor).nil?
end

# TODO: Maybe use the nilify blanks gem instead of the following six methods?:
Expand Down
6 changes: 3 additions & 3 deletions app/models/concerns/price_calculation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def get_unit_ratio_quantity(unit)
ratio = self.article_unit_ratios.find_by_unit(unit)
return ratio.quantity unless ratio.nil?

related_ratio = self.article_unit_ratios.detect { |current_ratio| ArticleUnits.units[current_ratio.unit][:baseUnit] == ArticleUnits.units[unit][:baseUnit] }
related_ratio = self.article_unit_ratios.detect { |current_ratio| ArticleUnit.as_hash[current_ratio.unit][:baseUnit] == ArticleUnit.as_hash[unit][:baseUnit] }
unless related_ratio.nil?
return related_ratio.quantity / ArticleUnits.units[unit][:conversionFactor] * ArticleUnits.units[related_ratio.unit][:conversionFactor]
return related_ratio.quantity / ArticleUnit.as_hash[unit][:conversionFactor] * ArticleUnit.as_hash[related_ratio.unit][:conversionFactor]
end

ArticleUnits.units[self.supplier_order_unit][:conversionFactor] / ArticleUnits.units[unit][:conversionFactor]
ArticleUnit.as_hash[self.supplier_order_unit][:conversionFactor] / ArticleUnit.as_hash[unit][:conversionFactor]
end

def convert_quantity(quantity, input_unit, output_unit)
Expand Down
3 changes: 3 additions & 0 deletions app/serializers/article_unit_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ArticleUnitSerializer < ActiveModel::Serializer
attributes :id, :unit
end
1 change: 1 addition & 0 deletions app/views/article_units/_create_link.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
= link_to t('.add'), article_units_path(unit: unit), class: 'btn btn-mini', remote: true, method: :post, data: {'for-unit': unit}
1 change: 1 addition & 0 deletions app/views/article_units/_destroy_link.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
=link_to t('.delete'), article_unit, method: :delete, class: 'btn btn-mini btn-danger', remote: true, data: { confirm: t('.confirm'), 'for-unit': article_unit.unit }
16 changes: 16 additions & 0 deletions app/views/article_units/_rows.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- @article_units.each do |article_unit|
%tr{class: ("untranslated" if article_unit[:untranslated])}
%td= article_unit[:code]
%td= highlight(article_unit[:symbol], @query)
%td
= highlight(article_unit[:name], @query)
- if article_unit[:untranslated]
%a{title: t('.request_translation'), href: "https://github.com/foodcoops/foodsoft/issues/new?title=Add%20translations%20for%20article%20unit%20%27#{article_unit[:code]}%27", target: '_blank'}
%i.icon-mail-forward
%td= article_unit[:description]
%td
- if article_unit[:record].nil?
= render partial: 'create_link', locals: {unit: article_unit[:code]}
- else
= render partial: 'destroy_link', locals: {article_unit: article_unit[:record]}

2 changes: 2 additions & 0 deletions app/views/article_units/create.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$('div.container-fluid').prepend('#{j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.success', name: @available_units[@article_unit.unit][:name])}))}')
$('a[data-for-unit="#{@article_unit.unit}"]').replaceWith('#{j(render(partial: "destroy_link", locals: {article_unit: @article_unit}))}')
2 changes: 2 additions & 0 deletions app/views/article_units/destroy.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$('div.container-fluid').prepend('#{j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.success', name: @available_units[@article_unit.unit][:name])}))}')
$('a[data-for-unit="#{@article_unit.unit}"]').replaceWith('#{j(render(partial: "create_link", locals: {unit: @article_unit.unit}))}')
57 changes: 57 additions & 0 deletions app/views/article_units/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
=form_with url: search_article_units_path, method: :get, id: :article_unit_search_form, html: {'data-remote': true} do |form|
= form.text_field :q, id: :article_unit_search, placeholder: t('.search')

%table.table.table-striped
%thead
%tr
%th= heading_helper ArticleUnit, :code, short: true
%th= heading_helper ArticleUnit, :symbol
%th= heading_helper ArticleUnit, :name
%th= heading_helper ArticleUnit, :description
%th
%th

%tbody#article_units_search_results
%br


- content_for :javascript do
:javascript
let timer;
function debounce(timeout, func){
return (...args) => {
if (timer !== undefined) {
clearTimeout(timer);
timer = undefined;
}
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
}

$(document).ready(function () {
$('#article_unit_search').on('input', debounce(250, () => {
$('#article_unit_search_form').submit();
}));
});

let currentXhr;

$(document).on('ajax:send', function(_event, xhr) {
if (timer !== undefined) {
clearTimeout(timer);
timer = undefined;
}
if (currentXhr) {
currentXhr.abort();
}
currentXhr = xhr;
return true;
});

$(document).on('ajax:complete', function(_event, _xhr, _status) {
currentXhr = null;
});

$('#article_unit_search_form').submit();
1 change: 1 addition & 0 deletions app/views/article_units/search.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$('#article_units_search_results').html('#{j(render("rows"))}');
2 changes: 1 addition & 1 deletion app/views/articles/_edit_all_table.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
:javascript
const units = #{raw(ArticleUnits.units.to_json)};
const units = #{raw(ArticleUnit.as_hash.to_json)};
- price_markup = FoodsoftConfig[:price_markup].to_f
%table.table
%thead
Expand Down
2 changes: 1 addition & 1 deletion app/views/articles/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
= simple_form_for [@supplier, @article.latest_article_version], :url => url, :validate => true, :remote => true do |f|
:javascript
const articleUnitRatioTemplate$ = $($.parseHTML("#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: f, article_unit_ratio_counter: -1}))}"));
const units = #{raw(ArticleUnits.units.to_json)};
const units = #{raw(ArticleUnit.as_hash.to_json)};
new ArticleForm(articleUnitRatioTemplate$, $('.article-form').parents('form'), units, #{FoodsoftConfig[:price_markup].to_f});

= render 'shared/js_templates/unit_conversion_popover_template'
Expand Down
2 changes: 1 addition & 1 deletion app/views/articles/_sync.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- content_for :javascript do
:javascript
const units = #{raw(ArticleUnits.units.to_json)};
const units = #{raw(ArticleUnit.as_hash.to_json)};
let articleUnitRatioTemplate$ = undefined;
- if @outlisted_articles.any?
%h2= t '.outlist.title'
Expand Down
10 changes: 5 additions & 5 deletions app/views/articles/_sync_table.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
%td= article.note
%td= article.manufacturer
%td= article.origin
%td= ArticleUnits.get_translated_name_for_code(article.supplier_order_unit)
%td= ArticleUnitsLib.get_translated_name_for_code(article.supplier_order_unit)
%td= number_to_currency article.price
%td= number_to_percentage article.tax
%td= number_to_currency article.deposit
Expand Down Expand Up @@ -67,9 +67,9 @@
%ul
- article.article_unit_ratios.each do |ratio|
%li
= "#{ratio.quantity} x #{ArticleUnits.get_translated_name_for_code(ratio.unit)}"
= "#{ratio.quantity} x #{ArticleUnitsLib.get_translated_name_for_code(ratio.unit)}"
= t 'per'
= ArticleUnits.get_translated_name_for_code(article.supplier_order_unit)
= ArticleUnitsLib.get_translated_name_for_code(article.supplier_order_unit)
.fold-line
= form.input :minimum_order_quantity, label: "Mininum order quantity" do
.input-append
Expand All @@ -78,9 +78,9 @@
- unless changed_article.new_record?
%p.help-block{style: 'color: grey;'}=article.minimum_order_quantity.to_s
.fold-line
= form.input :billing_unit, hint: changed_article.new_record? ? nil : ArticleUnits.get_translated_name_for_code(article.billing_unit || article.supplier_order_unit), hint_html: {style: 'color: grey;'}, as: :select, collection: [], input_html: {'data-initial-value': changed_article.billing_unit, class: 'input-medium', style: highlight_new(attrs, :billing_unit)}, include_blank: false
= form.input :billing_unit, hint: changed_article.new_record? ? nil : ArticleUnitsLib.get_translated_name_for_code(article.billing_unit || article.supplier_order_unit), hint_html: {style: 'color: grey;'}, as: :select, collection: [], input_html: {'data-initial-value': changed_article.billing_unit, class: 'input-medium', style: highlight_new(attrs, :billing_unit)}, include_blank: false
.fold-line
= form.input :group_order_granularity, hint: changed_article.new_record? ? nil : "#{article.group_order_granularity} x #{ArticleUnits.get_translated_name_for_code(article.group_order_unit)}", hint_html: {style: 'color: grey;'}, label: "Allow orders per", input_html: {class: 'input-mini', style: highlight_new(attrs, :group_order_granularity), title: "steps in which ordergroups can order this article"}
= form.input :group_order_granularity, hint: changed_article.new_record? ? nil : "#{article.group_order_granularity} x #{ArticleUnitsLib.get_translated_name_for_code(article.group_order_unit)}", hint_html: {style: 'color: grey;'}, label: "Allow orders per", input_html: {class: 'input-mini', style: highlight_new(attrs, :group_order_granularity), title: "steps in which ordergroups can order this article"}
= form.input :group_order_unit, as: :select, collection: [], input_html: {'data-initial-value': changed_article.group_order_unit, class: 'input-medium', style: highlight_new(attrs, :group_order_unit)}, label: '&times;'.html_safe, include_blank: false
%td{:style => highlight_new(attrs, :price)}
.d-flex.gap-1
Expand Down
Loading

0 comments on commit 56acdcb

Please sign in to comment.