foodsoft/app/controllers/articles_controller.rb
viehlieb 6ce659e78a include foodsoft-article-import
use filetypes for manual uploading bnn, odin, foodsoft file

use opts in .parse

adapt specs to include file format

add specs for odin, bnn, foodsoft files

adapt localize input to remove ',' separator and replace with '.'

remove depr foodsoftfile.rb and spreadsheet.rb

remove todo
2023-02-24 13:05:22 +01:00

249 lines
9.1 KiB
Ruby

class ArticlesController < ApplicationController
before_action :authenticate_article_meta, :find_supplier
def index
if params['sort']
sort = case params['sort']
when "name" then "articles.name"
when "unit" then "articles.unit"
when "article_category" then "article_categories.name"
when "note" then "articles.note"
when "availability" then "articles.availability"
when "name_reverse" then "articles.name DESC"
when "unit_reverse" then "articles.unit DESC"
when "article_category_reverse" then "article_categories.name DESC"
when "note_reverse" then "articles.note DESC"
when "availability_reverse" then "articles.availability DESC"
end
else
sort = "article_categories.name, articles.name"
end
@articles = Article.undeleted.where(supplier_id: @supplier, :type => nil).includes(:article_category).order(sort)
if request.format.csv?
send_data ArticlesCsv.new(@articles, encoding: 'utf-8').to_csv, filename: 'articles.csv', type: 'text/csv'
return
end
@articles = @articles.where('articles.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
@articles = @articles.page(params[:page]).per(@per_page)
respond_to do |format|
format.html
format.js { render :layout => false }
end
end
def new
@article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default])
render :layout => false
end
def copy
@article = @supplier.articles.find(params[:article_id]).dup
render :layout => false
end
def edit
@article = Article.find(params[:id])
render :action => 'new', :layout => false
end
def create
@article = Article.new(params[:article])
if @article.valid? && @article.save
render :layout => false
else
render :action => 'new', :layout => false
end
end
# Updates one Article and highlights the line if succeded
def update
@article = Article.find(params[:id])
if @article.update(params[:article])
render :layout => false
else
render :action => 'new', :layout => false
end
end
# Deletes article from database. send error msg, if article is used in a current order
def destroy
@article = Article.find(params[:id])
@article.mark_as_deleted unless @order = @article.in_open_order # If article is in an active Order, the Order will be returned
render :layout => false
end
# Renders a form for editing all articles from a supplier
def edit_all
@articles = @supplier.articles.undeleted
end
# Updates all article of specific supplier
def update_all
invalid_articles = false
begin
Article.transaction do
unless params[:articles].blank?
# Update other article attributes...
@articles = Article.find(params[:articles].keys)
@articles.each do |article|
unless article.update(params[:articles][article.id.to_s])
invalid_articles = true unless invalid_articles # Remember that there are validation errors
end
end
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
end
end
end
if invalid_articles
# An error has occurred, transaction has been rolled back.
flash.now.alert = I18n.t('articles.controller.error_invalid')
render :edit_all
else
# Successfully done.
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_all.notice')
end
end
# makes different actions on selected articles
def update_selected
raise I18n.t('articles.controller.error_nosel') if params[:selected_articles].nil?
articles = Article.find(params[:selected_articles])
Article.transaction do
case params[:selected_action]
when 'destroy'
articles.each(&:mark_as_deleted)
flash[:notice] = I18n.t('articles.controller.update_sel.notice_destroy')
when 'setNotAvailable'
articles.each { |a| a.update_attribute(:availability, false) }
flash[:notice] = I18n.t('articles.controller.update_sel.notice_unavail')
when 'setAvailable'
articles.each { |a| a.update_attribute(:availability, true) }
flash[:notice] = I18n.t('articles.controller.update_sel.notice_avail')
else
flash[:alert] = I18n.t('articles.controller.update_sel.notice_noaction')
end
end
# action succeded
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page])
rescue => error
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]),
:alert => I18n.t('errors.general_msg', :msg => error)
end
# lets start with parsing articles from uploaded file, yeah
# Renders the upload form
def upload
end
# Update articles from a spreadsheet
def parse_upload
uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file')
type = params[:articles]['type']
options = { filename: uploaded_file.original_filename }
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
options[:convert_units] = (params[:articles]['convert_units'] == '1')
options[:update_category] = (params[:articles]['update_category'] == '1')
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, type, options
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice')
end
@ignored_article_count = 0
rescue => error
redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message)
end
# sync all articles with the external database
# renders a form with articles, which should be updated
def sync
# check if there is an shared_supplier
unless @supplier.shared_supplier
redirect_to supplier_articles_url(@supplier), :alert => I18n.t('articles.controller.sync.shared_alert', :supplier => @supplier.name)
end
# sync articles against external database
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_all
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
end
end
# Updates, deletes articles when upload or sync form is submitted
def update_synchronized
@outlisted_articles = Article.find(params[:outlisted_articles].try(:keys) || [])
@updated_articles = Article.find(params[:articles].try(:keys) || [])
@updated_articles.map { |a| a.assign_attributes(params[:articles][a.id.to_s]) }
@new_articles = (params[:new_articles] || []).map { |a| @supplier.articles.build(a) }
has_error = false
Article.transaction do
# delete articles
begin
@outlisted_articles.each(&:mark_as_deleted)
rescue
# raises an exception when used in current order
has_error = true
end
# Update articles
@updated_articles.each { |a| a.save or has_error = true }
# Add new articles
@new_articles.each { |a| a.save or has_error = true }
raise ActiveRecord::Rollback if has_error
end
if !has_error
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice')
else
@updated_article_pairs = @updated_articles.map do |article|
orig_article = Article.find(article.id)
[article, orig_article.unequal_attributes(article)]
end
flash.now.alert = I18n.t('articles.controller.error_invalid')
render params[:from_action] == 'sync' ? :sync : :parse_upload
end
end
# renders a view to import articles in local database
#
def shared
# build array of keywords, required for ransack _all suffix
q = params.fetch(:q, {})
q[:name_cont_all] = params.fetch(:name_cont_all_joined, '').split(' ')
search = @supplier.shared_supplier.shared_articles.ransack(q)
@articles = search.result.page(params[:page]).per(10)
render :layout => false
end
# fills a form whith values of the selected shared_article
# when the direct parameter is set and the article is valid, it is imported directly
def import
@article = SharedArticle.find(params[:shared_article_id]).build_new_article(@supplier)
@article.article_category_id = params[:article_category_id] unless params[:article_category_id].blank?
if params[:direct] && !params[:article_category_id].blank? && @article.valid? && @article.save
render :action => 'create', :layout => false
else
render :action => 'new', :layout => false
end
end
private
# @return [Number] Number of articles not taken into account when syncing (having no number)
def ignored_article_count
if action_name == 'sync' || params[:from_action] == 'sync'
@ignored_article_count ||= @supplier.articles.undeleted.where(order_number: [nil, '']).count
else
0
end
end
helper_method :ignored_article_count
end