Compare commits
54 commits
demo
...
automatic_
Author | SHA1 | Date | |
---|---|---|---|
|
6e80f81c54 | ||
|
9a34f174a6 | ||
|
3b5b057eed | ||
|
817e409a2b | ||
|
e80ec9c1ce | ||
|
7f23b4784c | ||
|
b07653b34f | ||
|
c442327275 | ||
|
33034e66b8 | ||
|
45e2668cea | ||
|
5f2130ca44 | ||
|
913136bb72 | ||
|
4ac5bcae06 | ||
|
37b3b4523a | ||
|
a1682932ac | ||
|
026c3a6285 | ||
|
a8b2f387db | ||
|
2151835afb | ||
|
20dc8b8b82 | ||
|
e4f91ef67a | ||
|
c50ba6eda5 | ||
|
075f3cfa1a | ||
|
64b99038e6 | ||
|
7fe5fb4592 | ||
8b0e03ff60 | |||
|
4bfa87d258 | ||
|
20a67becf5 | ||
|
91e07ab660 | ||
|
285441cb4b | ||
|
fb2b4d8a8a | ||
|
f6fb804bbe | ||
|
a7775f5a98 | ||
b06656ba80 | |||
|
6e721db654 | ||
45ae192891 | |||
808baa5a98 | |||
|
5cbe8dd968 | ||
|
34e238466f | ||
5fb10ec686 | |||
50bf879fbf | |||
ea248a5f28 | |||
4ff44aed4c | |||
3d81dd6b57 | |||
|
c67e9b5be8 | ||
|
8604e27fe9 | ||
|
f2d5936cf0 | ||
|
c01c16ecdb | ||
|
67d0492ac4 | ||
|
5f00a39841 | ||
|
8420323c92 | ||
|
e0f63eebdc | ||
|
a7a0830d43 | ||
|
503ed6c379 | ||
|
debce2a635 |
439 changed files with 7547 additions and 7853 deletions
145
.drone.yml
145
.drone.yml
|
@ -1,145 +0,0 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: build and test
|
||||
|
||||
steps:
|
||||
- name: rubocop
|
||||
image: circleci/ruby:2.7-bullseye-node-browsers-legacy
|
||||
commands:
|
||||
- sudo apt install --no-install-recommends -y libmagic-dev
|
||||
- sudo -E bundle install
|
||||
- sudo -E bundle exec rubocop
|
||||
volumes:
|
||||
- name: gem-cache
|
||||
path: /bundle
|
||||
- name: tmp
|
||||
path: /drone/src/tmp
|
||||
failure: ignore
|
||||
|
||||
|
||||
- name: build_test
|
||||
image: circleci/ruby:2.7-bullseye-node-browsers-legacy
|
||||
commands:
|
||||
- sudo apt install --no-install-recommends -y libmagic-dev
|
||||
- echo 'Wait for db container'; sleep 30
|
||||
- bundle config set path '/bundle'
|
||||
- bundle config set without 'production'
|
||||
- sudo -E bundle install
|
||||
- sudo -E bundle exec rake foodsoft:setup_development_docker || true
|
||||
- sudo -E bundle exec rake rspec-rerun:spec
|
||||
volumes:
|
||||
- name: gem-cache
|
||||
path: /bundle
|
||||
- name: tmp
|
||||
path: /drone/src/tmp
|
||||
environment:
|
||||
RAILS_LOG_TO_STDOUT: true
|
||||
RAILS_ENV: test
|
||||
COVERAGE: lcov
|
||||
DATABASE_URL: mysql2://user:password@mariadb/test?encoding=utf8mb4
|
||||
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true
|
||||
PARALLEL_TEST_PROCESSORS: 60
|
||||
|
||||
services:
|
||||
- name: mariadb
|
||||
image: mariadb
|
||||
environment:
|
||||
MYSQL_USER: user
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
|
||||
volumes:
|
||||
- name: gem-cache
|
||||
host:
|
||||
path: /tmp/cache
|
||||
- name: tmp
|
||||
temp: {}
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker build and deploy
|
||||
steps:
|
||||
- name: build and publish docker image
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: git.local-it.org
|
||||
repo: git.local-it.org/foodsoft/foodsoft
|
||||
username: philipp
|
||||
password:
|
||||
from_secret: docker_registry
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_BRANCH}
|
||||
- ${DRONE_COMMIT:0:8}
|
||||
cache_from:
|
||||
- "git.local-it.org/foodsoft/foodsoft:latest"
|
||||
- "git.local-it.org/foodsoft/foodsoft:${DRONE_BRANCH}"
|
||||
- name: deployment
|
||||
image: git.local-it.org/philipp/stack-ssh-deply:latest
|
||||
settings:
|
||||
stack: "foodsoft_${DRONE_BRANCH}"
|
||||
compose: "deployment/compose.yml"
|
||||
deploy_key:
|
||||
from_secret: drone_deploy_key
|
||||
host: "dev.local-it.cloud"
|
||||
user: "root"
|
||||
port: 22
|
||||
reg_user: philipp
|
||||
reg_pass:
|
||||
from_secret: docker_registry
|
||||
reg_url: git.local-it.org
|
||||
image: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
|
||||
generate_secrets: true
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
IMAGE: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
|
||||
STACK_NAME: "foodsoft_${DRONE_BRANCH}"
|
||||
DOMAIN: "foodsoft.dev.local-it.cloud"
|
||||
LETS_ENCRYPT_ENV: production
|
||||
FOODCOOP_MULTI_INSTALL: true
|
||||
FOODCOOP_NAME: Einkaufskooperative Foobar
|
||||
FOODCOOP_CITY: Berlin
|
||||
FOODCOOP_COUNTRY: Deutschland
|
||||
FOODCOOP_EMAIL: foodsoft@local-it.org
|
||||
FOODCOOP_PHONE: 123456789
|
||||
FOODCOOP_STREET: Einkaufsstraße 5
|
||||
FOODCOOP_ZIP_CODE: 12345
|
||||
FOODCOOP_HOMEPAGE: https://foodsoft.local-it.org
|
||||
FOODCOOP_HELP_URL: https://git.local-it.org/foodsoft/foodsoft
|
||||
FOODCOOP_TIME_ZONE: Berlin
|
||||
FOODCOOP_USE_NICK: true
|
||||
FOODCOOP_LANGUAGE: de
|
||||
FOODCOOP_FOOTER: '<a href="https://foodsoft.local-it.org/">Foodsoft</a> hosted by <a href="https://local-it.org">local-it e,V,</a>.'
|
||||
USE_APPLE_POINTS: false
|
||||
STOP_ORDERING_UNDER: 75
|
||||
MINIMUM_BALANCE: 0
|
||||
MYSQL_DB: foodsoft
|
||||
MYSQL_HOST: db
|
||||
MYSQL_PORT: 3306
|
||||
MYSQL_USER: foodsoft
|
||||
EMAIL_SENDER: demo@local-it.org
|
||||
EMAIL_ERROR: flip@yksflip.de
|
||||
SMTP_ADDRESS: mail.local-it.org
|
||||
SMTP_AUTHENTICATION: login
|
||||
SMTP_DOMAIN: mail.local-it.org
|
||||
SMTP_ENABLE_STARTTLS_AUTO: true
|
||||
SMTP_PORT: 587
|
||||
SMTP_USER_NAME: demo@local-it.org
|
||||
EMAIL_REPLY_DOMAIN:
|
||||
SMTP_SERVER_HOST: 0.0.0.0
|
||||
SMTP_SERVER_PORT: 2525
|
||||
SECRET_DB_PASSWORD_VERSION: v1
|
||||
SECRET_DB_ROOT_PASSWORD_VERSION: v1
|
||||
SECRET_SHARED_LISTS_DB_PASSWORD_VERSION: v1
|
||||
SECRET_SMTP_PASSWORD_VERSION: v1
|
||||
SECRET_SECRET_KEY_BASE_VERSION: v1
|
||||
APP_CONFIG_VERSION: v1
|
||||
DB_CONFIG_VERSION: v1
|
||||
ENTRYPOINT_VERSION: v1
|
||||
PRODUCTION_ENV_VERSION: v1
|
||||
trigger:
|
||||
branch:
|
||||
- demo
|
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Fixes line endings for Windows (Docker) environment, which are by default converted to crlf
|
||||
* text=auto
|
||||
*.sh text eol=lf
|
||||
proc-start text eol=lf
|
||||
Rakefile text eol=lf
|
2
.github/workflows/ruby.yml
vendored
2
.github/workflows/ruby.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
MYSQL_DATABASE: test
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping"
|
||||
--health-cmd "mariadb-admin ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
|
1832
.rubocop_todo.yml
1832
.rubocop_todo.yml
File diff suppressed because it is too large
Load diff
|
@ -1 +1 @@
|
|||
2.7.2
|
||||
2.7.8
|
||||
|
|
11
Dockerfile
11
Dockerfile
|
@ -15,9 +15,7 @@ ENV PORT=3000 \
|
|||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
COPY plugins/ ./plugins
|
||||
COPY config/ ./config
|
||||
COPY . ./
|
||||
|
||||
# install dependencies and generate crontab
|
||||
RUN buildDeps='libmagic-dev' && \
|
||||
|
@ -32,8 +30,6 @@ RUN buildDeps='libmagic-dev' && \
|
|||
\
|
||||
bundle exec whenever >crontab
|
||||
|
||||
COPY . ./
|
||||
|
||||
# compile assets with temporary mysql server
|
||||
RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
|
||||
export SECRET_KEY_BASE=thisisnotimportantnow && \
|
||||
|
@ -53,10 +49,9 @@ RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
|
|||
rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
# Make relevant dirs and files writable for app user
|
||||
RUN mkdir -p tmp storage && \
|
||||
RUN mkdir -p tmp && \
|
||||
chown nobody config/app_config.yml && \
|
||||
chown nobody tmp && \
|
||||
chown nobody storage
|
||||
chown nobody tmp
|
||||
|
||||
# Run app as unprivileged user
|
||||
USER nobody
|
||||
|
|
98
Gemfile
98
Gemfile
|
@ -1,74 +1,73 @@
|
|||
# A sample Gemfile
|
||||
source "https://rubygems.org"
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "rails", '~> 7.0'
|
||||
gem 'mail', '~> 2.7.1' # bug with mail 2.8.0 https://github.com/mikel/mail/issues/1489
|
||||
gem 'rails', '~> 7.0'
|
||||
|
||||
|
||||
gem 'sassc-rails'
|
||||
gem 'less-rails'
|
||||
gem 'sassc-rails'
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
gem 'therubyracer', platforms: :ruby
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'select2-rails'
|
||||
gem 'rails_tokeninput'
|
||||
gem 'bootsnap', require: false
|
||||
gem 'bootstrap-datepicker-rails'
|
||||
gem 'date_time_attribute'
|
||||
gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update
|
||||
gem 'i18n-js', '~> 3.0.0.rc8'
|
||||
gem 'jquery-rails'
|
||||
gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update
|
||||
gem 'rails-i18n'
|
||||
gem 'bootsnap', require: false
|
||||
gem 'rails_tokeninput'
|
||||
gem 'select2-rails'
|
||||
|
||||
gem 'mysql2'
|
||||
gem 'prawn'
|
||||
gem 'prawn-table'
|
||||
gem 'haml', '~> 5.0'
|
||||
gem 'haml-rails'
|
||||
gem 'kaminari'
|
||||
gem 'simple_form'
|
||||
gem 'inherited_resources'
|
||||
gem 'active_model_serializers', '~> 0.10.0'
|
||||
gem 'acts_as_tree'
|
||||
gem 'attribute_normalizer'
|
||||
gem 'daemons'
|
||||
gem 'doorkeeper'
|
||||
gem 'doorkeeper-i18n'
|
||||
gem 'haml', '~> 5.0'
|
||||
gem 'haml-rails'
|
||||
gem 'ice_cube'
|
||||
gem 'inherited_resources'
|
||||
gem 'kaminari'
|
||||
gem 'mysql2'
|
||||
gem 'prawn'
|
||||
gem 'prawn-table'
|
||||
gem 'puma'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
gem 'active_model_serializers', '~> 0.10.0'
|
||||
gem 'twitter-bootstrap-rails', '~> 2.2.8'
|
||||
gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73
|
||||
gem 'ransack'
|
||||
gem 'resque'
|
||||
gem 'ruby-units'
|
||||
gem 'sd_notify'
|
||||
gem 'simple_form'
|
||||
gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap
|
||||
gem 'simple-navigation-bootstrap'
|
||||
gem 'sprockets', '< 4'
|
||||
gem 'ransack'
|
||||
gem 'acts_as_tree'
|
||||
gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73
|
||||
gem 'resque'
|
||||
gem 'puma'
|
||||
gem 'sd_notify'
|
||||
gem 'twitter-bootstrap-rails', '~> 2.2.8'
|
||||
gem 'whenever', require: false # For defining cronjobs, see config/schedule.rb
|
||||
gem 'ruby-units'
|
||||
gem 'attribute_normalizer'
|
||||
gem 'ice_cube'
|
||||
# At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152
|
||||
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
|
||||
gem 'foodsoft_article_import', git: 'https://git.local-it.org/Foodsoft/foodsoft_article_import', tag: 'v1.0'
|
||||
gem 'roo'
|
||||
gem 'roo-xls'
|
||||
gem 'spreadsheet'
|
||||
gem 'exception_notification'
|
||||
gem 'gaffe'
|
||||
gem 'ruby-filemagic'
|
||||
gem 'mime-types'
|
||||
gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
|
||||
gem 'midi-smtp-server'
|
||||
gem 'mime-types'
|
||||
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
|
||||
gem 'roo'
|
||||
gem 'roo-xls'
|
||||
gem 'rswag-api'
|
||||
gem 'rswag-ui'
|
||||
gem 'ruby-filemagic'
|
||||
gem 'spreadsheet'
|
||||
|
||||
# we use the git version of acts_as_versioned, and need to include it in this Gemfile
|
||||
gem 'acts_as_versioned', git: 'https://github.com/technoweenie/acts_as_versioned.git'
|
||||
gem 'foodsoft_wiki', path: 'plugins/wiki'
|
||||
gem 'foodsoft_messages', path: 'plugins/messages'
|
||||
gem 'foodsoft_documents', path: 'plugins/documents'
|
||||
gem 'foodsoft_discourse', path: 'plugins/discourse'
|
||||
gem 'foodsoft_documents', path: 'plugins/documents'
|
||||
gem 'foodsoft_links', path: 'plugins/links'
|
||||
gem 'foodsoft_messages', path: 'plugins/messages'
|
||||
gem 'foodsoft_polls', path: 'plugins/polls'
|
||||
gem 'foodsoft_wiki', path: 'plugins/wiki'
|
||||
gem "foodsoft_group_order_invoice", :path => "plugins/group_order_invoice"
|
||||
|
||||
# plugins not enabled by default
|
||||
# gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
||||
|
@ -76,10 +75,10 @@ gem 'foodsoft_polls', path: 'plugins/polls'
|
|||
# gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
||||
|
||||
group :development do
|
||||
gem 'sqlite3', '~> 1.3.6'
|
||||
gem 'mailcatcher'
|
||||
gem 'web-console'
|
||||
gem 'listen'
|
||||
gem 'mailcatcher'
|
||||
gem 'sqlite3', '~> 1.3.6'
|
||||
gem 'web-console'
|
||||
|
||||
# Better error output
|
||||
gem 'better_errors'
|
||||
|
@ -107,26 +106,23 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem 'rspec-rails'
|
||||
gem 'apparition' # Capybara javascript driver
|
||||
gem 'capybara'
|
||||
gem 'connection_pool'
|
||||
gem 'database_cleaner'
|
||||
gem 'factory_bot_rails'
|
||||
gem 'faker'
|
||||
gem 'capybara'
|
||||
gem 'apparition' # Capybara javascript driver
|
||||
gem 'database_cleaner'
|
||||
gem 'connection_pool'
|
||||
gem 'rspec-rails'
|
||||
# need to include rspec components before i18n-spec or rake fails in test environment
|
||||
gem 'i18n-spec'
|
||||
gem 'rspec-core'
|
||||
gem 'rspec-rerun'
|
||||
gem 'i18n-spec'
|
||||
gem 'rails-controller-testing'
|
||||
# code coverage
|
||||
gem 'simplecov', require: false
|
||||
gem 'simplecov-lcov', require: false
|
||||
# api
|
||||
gem 'rswag-specs'
|
||||
gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
|
||||
end
|
||||
|
||||
gem "importmap-rails", "~> 1.1"
|
||||
gem "image_processing", "~> 1.12"
|
||||
gem "terser", "~> 1.1"
|
||||
|
|
333
Gemfile.lock
333
Gemfile.lock
|
@ -1,16 +1,8 @@
|
|||
GIT
|
||||
remote: https://git.local-it.org/Foodsoft/foodsoft_article_import
|
||||
revision: 49a0c1ddb3bb67a357c692c63af0cda2db7c45b0
|
||||
tag: v1.0
|
||||
specs:
|
||||
foodsoft_article_import (1.0.0)
|
||||
roo (~> 2.9.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/gregschmit/recurring_select
|
||||
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
|
||||
revision: 89af1439687a619765631a3e0c61eb3713be0cf9
|
||||
specs:
|
||||
recurring_select (3.0.0)
|
||||
recurring_select (3.0.1)
|
||||
coffee-rails (>= 3.1)
|
||||
ice_cube (>= 0.11)
|
||||
jquery-rails (>= 3.0)
|
||||
|
@ -39,6 +31,13 @@ PATH
|
|||
rails
|
||||
ruby-filemagic
|
||||
|
||||
PATH
|
||||
remote: plugins/group_order_invoice
|
||||
specs:
|
||||
foodsoft_group_order_invoice (0.1.2)
|
||||
deface (~> 1.0)
|
||||
rails (>= 7.0.4)
|
||||
|
||||
PATH
|
||||
remote: plugins/links
|
||||
specs:
|
||||
|
@ -78,47 +77,47 @@ PATH
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
actioncable (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activestorage (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
actionmailbox (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
activejob (= 7.0.6)
|
||||
activerecord (= 7.0.6)
|
||||
activestorage (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
actionview (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
actionmailer (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
actionview (= 7.0.6)
|
||||
activejob (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.4)
|
||||
actionview (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
rack (~> 2.0, >= 2.2.0)
|
||||
actionpack (7.0.6)
|
||||
actionview (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activestorage (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
actiontext (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
activerecord (= 7.0.6)
|
||||
activestorage (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
actionview (7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -128,29 +127,29 @@ GEM
|
|||
activemodel (>= 4.1, < 7.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
activejob (7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
activerecord (7.0.4)
|
||||
activemodel (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
activestorage (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
activemodel (7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
activerecord (7.0.6)
|
||||
activemodel (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
activestorage (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
activejob (= 7.0.6)
|
||||
activerecord (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.4)
|
||||
activesupport (7.0.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
acts_as_tree (2.9.1)
|
||||
activerecord (>= 3.0.0)
|
||||
addressable (2.8.1)
|
||||
addressable (2.8.4)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
apparition (0.6.0)
|
||||
capybara (~> 3.13, < 4)
|
||||
|
@ -158,22 +157,22 @@ GEM
|
|||
ast (2.4.2)
|
||||
attribute_normalizer (1.2.0)
|
||||
base32 (0.3.4)
|
||||
better_errors (2.9.1)
|
||||
coderay (>= 1.0.0)
|
||||
better_errors (2.10.1)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
rouge (>= 1.0.0)
|
||||
bindex (0.8.1)
|
||||
binding_of_caller (1.0.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootsnap (1.15.0)
|
||||
bootsnap (1.16.0)
|
||||
msgpack (~> 1.2)
|
||||
bootstrap-datepicker-rails (1.9.0.1)
|
||||
bootstrap-datepicker-rails (1.10.0.1)
|
||||
railties (>= 3.0)
|
||||
builder (3.2.4)
|
||||
bullet (7.0.7)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
capybara (3.38.0)
|
||||
capybara (3.39.2)
|
||||
addressable
|
||||
matrix
|
||||
mini_mime (>= 0.1.3)
|
||||
|
@ -194,14 +193,14 @@ GEM
|
|||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
commonjs (0.2.7)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.3.0)
|
||||
concurrent-ruby (1.2.2)
|
||||
connection_pool (2.4.1)
|
||||
content_for_in_controllers (0.0.2)
|
||||
crass (1.0.6)
|
||||
daemons (1.4.1)
|
||||
database_cleaner (2.0.1)
|
||||
database_cleaner-active_record (~> 2.0.0)
|
||||
database_cleaner-active_record (2.0.1)
|
||||
database_cleaner (2.0.2)
|
||||
database_cleaner-active_record (>= 2, < 3)
|
||||
database_cleaner-active_record (2.1.0)
|
||||
activerecord (>= 5.a)
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
|
@ -218,7 +217,7 @@ GEM
|
|||
diff-lcs (1.5.0)
|
||||
diffy (3.4.2)
|
||||
docile (1.4.0)
|
||||
doorkeeper (5.6.2)
|
||||
doorkeeper (5.6.6)
|
||||
railties (>= 5)
|
||||
doorkeeper-i18n (5.2.6)
|
||||
doorkeeper (>= 5.2)
|
||||
|
@ -235,12 +234,12 @@ GEM
|
|||
factory_bot_rails (6.2.0)
|
||||
factory_bot (~> 6.2.0)
|
||||
railties (>= 5.0.0)
|
||||
faker (3.1.0)
|
||||
faker (3.2.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
ffi (1.15.5)
|
||||
gaffe (1.2.0)
|
||||
rails (>= 4.0.0)
|
||||
globalid (1.0.0)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
haml (5.2.2)
|
||||
temple (>= 0.8.0)
|
||||
|
@ -250,22 +249,19 @@ GEM
|
|||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
has_scope (0.8.0)
|
||||
has_scope (0.8.1)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
hashie (3.4.6)
|
||||
htmlentities (4.3.4)
|
||||
i18n (1.12.0)
|
||||
i18n (1.14.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.0.11)
|
||||
i18n (>= 0.6.6, < 2)
|
||||
i18n-spec (0.6.0)
|
||||
iso
|
||||
ice_cube (0.16.4)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
importmap-rails (1.1.5)
|
||||
importmap-rails (1.2.1)
|
||||
actionpack (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
inherited_resources (1.13.1)
|
||||
|
@ -276,7 +272,7 @@ GEM
|
|||
interception (0.5)
|
||||
iso (0.4.0)
|
||||
i18n
|
||||
jquery-rails (4.5.1)
|
||||
jquery-rails (4.6.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
|
@ -296,6 +292,7 @@ GEM
|
|||
activerecord
|
||||
kaminari-core (= 1.2.2)
|
||||
kaminari-core (1.2.2)
|
||||
language_server-protocol (3.17.0.3)
|
||||
less (2.6.0)
|
||||
commonjs (~> 0.2.7)
|
||||
less-rails (5.0.0)
|
||||
|
@ -303,14 +300,17 @@ GEM
|
|||
less (~> 2.6.0)
|
||||
sprockets (~> 3.0)
|
||||
libv8 (3.16.14.19-x86_64-linux)
|
||||
listen (3.7.1)
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.19.1)
|
||||
loofah (2.21.3)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
mailcatcher (0.2.4)
|
||||
eventmachine
|
||||
haml
|
||||
|
@ -324,20 +324,19 @@ GEM
|
|||
marcel (1.0.2)
|
||||
matrix (0.4.2)
|
||||
method_source (1.0.0)
|
||||
midi-smtp-server (3.0.3)
|
||||
midi-smtp-server (3.1.2)
|
||||
mime-types (3.4.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2022.0105)
|
||||
mini_magick (4.12.0)
|
||||
mime-types-data (3.2023.0218.1)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.17.0)
|
||||
mono_logger (1.1.1)
|
||||
msgpack (1.6.0)
|
||||
minitest (5.18.1)
|
||||
mono_logger (1.1.2)
|
||||
msgpack (1.7.2)
|
||||
multi_json (1.15.0)
|
||||
mustermann (3.0.0)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
mysql2 (0.5.4)
|
||||
net-imap (0.3.4)
|
||||
mysql2 (0.5.5)
|
||||
net-imap (0.3.6)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
|
@ -346,12 +345,13 @@ GEM
|
|||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.10-x86_64-linux)
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.3-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
parallel (1.22.1)
|
||||
parser (3.2.0.0)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.3)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
pdf-core (0.9.0)
|
||||
polyglot (0.3.5)
|
||||
prawn (2.4.0)
|
||||
|
@ -359,7 +359,7 @@ GEM
|
|||
ttfunk (~> 1.7)
|
||||
prawn-table (0.2.2)
|
||||
prawn (>= 1.3.0, < 3.0.0)
|
||||
pry (0.14.1)
|
||||
pry (0.14.2)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-rescue (1.5.2)
|
||||
|
@ -368,147 +368,151 @@ GEM
|
|||
pry-stack_explorer (0.6.1)
|
||||
binding_of_caller (~> 1.0)
|
||||
pry (~> 0.13)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.0.2)
|
||||
public_suffix (5.0.3)
|
||||
puma (6.3.0)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.6.2)
|
||||
rack (2.2.5)
|
||||
rack-cors (1.1.1)
|
||||
racc (1.7.1)
|
||||
rack (2.2.7)
|
||||
rack-cors (2.0.1)
|
||||
rack (>= 2.0.0)
|
||||
rack-protection (3.0.5)
|
||||
rack-protection (3.0.6)
|
||||
rack
|
||||
rack-test (2.0.2)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rails (7.0.4)
|
||||
actioncable (= 7.0.4)
|
||||
actionmailbox (= 7.0.4)
|
||||
actionmailer (= 7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
actiontext (= 7.0.4)
|
||||
actionview (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activemodel (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activestorage (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
rails (7.0.6)
|
||||
actioncable (= 7.0.6)
|
||||
actionmailbox (= 7.0.6)
|
||||
actionmailer (= 7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
actiontext (= 7.0.6)
|
||||
actionview (= 7.0.6)
|
||||
activejob (= 7.0.6)
|
||||
activemodel (= 7.0.6)
|
||||
activerecord (= 7.0.6)
|
||||
activestorage (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.4)
|
||||
railties (= 7.0.6)
|
||||
rails-assets-listjs (0.2.0.beta.4)
|
||||
railties (>= 3.1)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
activesupport (>= 5.0.1.rc1)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
rails-dom-testing (2.1.1)
|
||||
activesupport (>= 5.0.0)
|
||||
minitest
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.4.4)
|
||||
loofah (~> 2.19, >= 2.19.1)
|
||||
rails-i18n (7.0.6)
|
||||
rails-html-sanitizer (1.6.0)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
rails-i18n (7.0.7)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
rails-settings-cached (0.4.3)
|
||||
rails (>= 4.2.0)
|
||||
rails_tokeninput (1.7.0)
|
||||
railties (>= 3.1.0)
|
||||
railties (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
railties (7.0.6)
|
||||
actionpack (= 7.0.6)
|
||||
activesupport (= 7.0.6)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
ransack (3.2.1)
|
||||
ransack (4.0.0)
|
||||
activerecord (>= 6.1.5)
|
||||
activesupport (>= 6.1.5)
|
||||
i18n
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
redis (5.0.5)
|
||||
redis (5.0.6)
|
||||
redis-client (>= 0.9.0)
|
||||
redis-client (0.11.2)
|
||||
redis-client (0.14.1)
|
||||
connection_pool
|
||||
redis-namespace (1.10.0)
|
||||
redis-namespace (1.11.0)
|
||||
redis (>= 4)
|
||||
ref (2.0.0)
|
||||
regexp_parser (2.6.1)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
resque (2.4.0)
|
||||
regexp_parser (2.8.1)
|
||||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
resque (2.5.0)
|
||||
mono_logger (~> 1.0)
|
||||
multi_json (~> 1.0)
|
||||
redis-namespace (~> 1.6)
|
||||
sinatra (>= 0.9.2)
|
||||
rexml (3.2.5)
|
||||
roo (2.9.0)
|
||||
roo (2.10.0)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (>= 1.3.0, < 3.0.0)
|
||||
roo-xls (1.2.0)
|
||||
nokogiri
|
||||
roo (>= 2.0.0, < 3)
|
||||
spreadsheet (> 0.9.0)
|
||||
rouge (4.1.2)
|
||||
rspec (3.12.0)
|
||||
rspec-core (~> 3.12.0)
|
||||
rspec-expectations (~> 3.12.0)
|
||||
rspec-mocks (~> 3.12.0)
|
||||
rspec-core (3.12.0)
|
||||
rspec-core (3.12.2)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-expectations (3.12.1)
|
||||
rspec-expectations (3.12.3)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-mocks (3.12.1)
|
||||
rspec-mocks (3.12.6)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.0.1)
|
||||
rspec-rails (6.0.3)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
rspec-core (~> 3.11)
|
||||
rspec-expectations (~> 3.11)
|
||||
rspec-mocks (~> 3.11)
|
||||
rspec-support (~> 3.11)
|
||||
rspec-core (~> 3.12)
|
||||
rspec-expectations (~> 3.12)
|
||||
rspec-mocks (~> 3.12)
|
||||
rspec-support (~> 3.12)
|
||||
rspec-rerun (1.1.0)
|
||||
rspec (~> 3.0)
|
||||
rspec-support (3.12.0)
|
||||
rswag-api (2.7.0)
|
||||
rspec-support (3.12.1)
|
||||
rswag-api (2.10.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rswag-specs (2.7.0)
|
||||
rswag-specs (2.10.1)
|
||||
activesupport (>= 3.1, < 7.1)
|
||||
json-schema (>= 2.2, < 4.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rspec-core (>= 2.14)
|
||||
rswag-ui (2.7.0)
|
||||
rswag-ui (2.10.1)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.43.0)
|
||||
rubocop (1.54.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.0.0)
|
||||
parser (>= 3.2.2.3)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.24.1, < 2.0)
|
||||
rubocop-ast (>= 1.28.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.24.1)
|
||||
parser (>= 3.1.1.0)
|
||||
rubocop-rails (2.17.4)
|
||||
rubocop-ast (1.29.0)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-capybara (2.18.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-factory_bot (2.23.1)
|
||||
rubocop (~> 1.33)
|
||||
rubocop-rails (2.20.2)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop-rspec (2.16.0)
|
||||
rubocop-rspec (2.22.0)
|
||||
rubocop (~> 1.33)
|
||||
rubocop-capybara (~> 2.17)
|
||||
rubocop-factory_bot (~> 2.22)
|
||||
ruby-filemagic (0.7.3)
|
||||
ruby-ole (1.2.12.2)
|
||||
ruby-prof (1.4.5)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-prof (1.6.3)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-units (3.0.0)
|
||||
ruby-vips (2.1.4)
|
||||
ffi (~> 1.12)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
sass-rails (6.0.0)
|
||||
|
@ -528,7 +532,7 @@ GEM
|
|||
simple-navigation-bootstrap (1.0.2)
|
||||
railties (>= 3.1)
|
||||
simple-navigation (>= 3.7.0, < 4.0.0)
|
||||
simple_form (5.1.0)
|
||||
simple_form (5.2.0)
|
||||
actionpack (>= 5.2)
|
||||
activemodel (>= 5.2)
|
||||
simplecov (0.22.0)
|
||||
|
@ -538,10 +542,10 @@ GEM
|
|||
simplecov-html (0.12.3)
|
||||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
sinatra (3.0.5)
|
||||
sinatra (3.0.6)
|
||||
mustermann (~> 3.0)
|
||||
rack (~> 2.2, >= 2.2.4)
|
||||
rack-protection (= 3.0.5)
|
||||
rack-protection (= 3.0.6)
|
||||
tilt (~> 2.0)
|
||||
skinny (0.2.4)
|
||||
eventmachine (~> 1.0.0)
|
||||
|
@ -559,8 +563,8 @@ GEM
|
|||
sqlite3-ruby (1.3.3)
|
||||
sqlite3 (>= 1.3.3)
|
||||
table_print (1.5.7)
|
||||
temple (0.9.1)
|
||||
terser (1.1.13)
|
||||
temple (0.10.2)
|
||||
terser (1.1.17)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
therubyracer (0.12.3)
|
||||
libv8 (~> 3.16.14.15)
|
||||
|
@ -569,9 +573,9 @@ GEM
|
|||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thor (1.2.1)
|
||||
tilt (2.0.11)
|
||||
timeout (0.3.1)
|
||||
thor (1.2.2)
|
||||
tilt (2.2.0)
|
||||
timeout (0.4.0)
|
||||
ttfunk (1.7.0)
|
||||
twitter-bootstrap-rails (2.2.8)
|
||||
actionpack (>= 3.1)
|
||||
|
@ -580,7 +584,7 @@ GEM
|
|||
railties (>= 3.1)
|
||||
twitter-text (1.14.7)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (2.0.5)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
|
@ -605,7 +609,7 @@ GEM
|
|||
twitter-text
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.6)
|
||||
zeitwerk (2.6.8)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
@ -631,9 +635,9 @@ DEPENDENCIES
|
|||
exception_notification
|
||||
factory_bot_rails
|
||||
faker
|
||||
foodsoft_article_import!
|
||||
foodsoft_discourse!
|
||||
foodsoft_documents!
|
||||
foodsoft_group_order_invoice!
|
||||
foodsoft_links!
|
||||
foodsoft_messages!
|
||||
foodsoft_polls!
|
||||
|
@ -645,14 +649,12 @@ DEPENDENCIES
|
|||
i18n-js (~> 3.0.0.rc8)
|
||||
i18n-spec
|
||||
ice_cube
|
||||
image_processing (~> 1.12)
|
||||
importmap-rails (~> 1.1)
|
||||
inherited_resources
|
||||
jquery-rails
|
||||
kaminari
|
||||
less-rails
|
||||
listen
|
||||
mail (~> 2.7.1)
|
||||
mailcatcher
|
||||
midi-smtp-server
|
||||
mime-types
|
||||
|
@ -665,7 +667,6 @@ DEPENDENCIES
|
|||
rack-cors
|
||||
rails (~> 7.0)
|
||||
rails-assets-listjs (= 0.2.0.beta.4)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rails-settings-cached (= 0.4.3)
|
||||
rails_tokeninput
|
||||
|
@ -705,4 +706,4 @@ DEPENDENCIES
|
|||
whenever
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.5
|
||||
2.4.16
|
||||
|
|
147
README.md
147
README.md
|
@ -1,124 +1,71 @@
|
|||
Foodsoft
|
||||
=========
|
||||
|
||||
[Website](https://foodsoft.local-it.org)
|
||||
[Prototypefund](https://prototypefund.de/project/weiterentwicklung-von-foodsoft/)
|
||||
[![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions)
|
||||
[![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master)
|
||||
[![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft)
|
||||
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.svg)](https://codeclimate.com/github/foodcoops/foodsoft)
|
||||
[![Docker Status](https://img.shields.io/docker/cloud/build/foodcoops/foodsoft.svg)](https://hub.docker.com/r/foodcoops/foodsoft)
|
||||
[![Documentation](https://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/foodcoops/foodsoft)
|
||||
|
||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
||||
|
||||
Foodsoft ist ein Tool für [Lebensmittelkooperativen](https://de.wikipedia.org/wiki/Lebensmittelkooperative), welches selbstorganisierte gemeinsame Bestellungen in Großmengen von regionalen und ökologischen Produkten vereinfacht und transparent gestaltet.
|
||||
A food cooperative is a group of people that buy food from suppliers of their own choosing. A collective do-it-yourself supermarket. Members order their products online and collect them on a specified day. And all put in a bit of work to make that possible. Foodsoft facilitates the process.
|
||||
|
||||
Foodsoft wurde ursprünglich entwickelt und betrieben von [foodcoops.net](https://foodcoops.net/)
|
||||
If you're a food coop considering to use foodsoft, please have a look at the [wiki page for foodcoops](https://github.com/foodcoops/foodsoft/wiki/For-foodcoops). When you'd like to experiment with or develop foodsoft, you can read [how to set it up](https://github.com/foodcoops/foodsoft/blob/master/doc/SETUP_DEVELOPMENT.md) on your own computer.
|
||||
|
||||
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
|
||||
|
||||
#### Zielgruppe
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
Unsere Zielgruppen sind Bürger:innen, Gruppen und Vereine, die eine Einkauskooperative aufbauen wollen und eine Software, die die Bestellung, Verteilung und Abrechnung erleichtert, benötigen.
|
||||
If you'd like to see what is currently bring prioritised for development, check [our roadmap](https://github.com/orgs/foodcoops/projects/1). If you'd like to influence the roadmap, please join our [monthly community call](https://forum.foodcoops.net/t/foodsoft-monthly-community-call/573/6). As of March 2023, Foodsoft has limited development capacity but we are trying to build this up once more. For now, we try to prioritise what we work on, in order to focus our efforts. If your proposed changes are waiting for some time without review, please join the community call to discuss.
|
||||
|
||||
#### Vorhaben
|
||||
Developing
|
||||
----------
|
||||
|
||||
* ✅ Technische Schuld reduzieren
|
||||
* ✅ Ruby on Rails Upgrade
|
||||
* ✅ Artikel Import verbessern
|
||||
(Großhandelschnitstelle)
|
||||
* ✅ Userexperience Verbessern
|
||||
> Foodsoft development needs your help! If you want to hack/triage/organise to improve the software, please consider joining our monthly community calls which are announced on [this forum thread](https://forum.foodcoops.net/t/foodsoft-monthly-community-call/573/6). In these calls, we check in with each other, discuss what to prioritise and try to make progress with development and community issues together.
|
||||
|
||||
#### Was ist eine Einkaufskooperative?
|
||||
Get foodsoft [running locally](doc/SETUP_DEVELOPMENT.md),
|
||||
then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines)
|
||||
page on the wiki.
|
||||
|
||||
![Wie funktioniert eine Einkauskooperative?](./doc/foodcoop-explained.jpg)
|
||||
Get a foodsoft dev-environment running in the browser with Gitpod
|
||||
|
||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/foodcoops/foodsoft)
|
||||
|
||||
Follow these [instructions](doc/SETUP_DEVELOPMENT_GITPOD.md) to complete setup from within the Gitpod workspace.
|
||||
|
||||
State of this Fork
|
||||
------------------
|
||||
Deploying
|
||||
---------
|
||||
|
||||
#### Increase Test Coverage
|
||||
Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing
|
||||
[hosting platform](https://foodcoops.net/foodsoft-hosting/).
|
||||
|
||||
1. integration and model tests
|
||||
* [x] fork
|
||||
* [x] upstream [#966](https://github.com/foodcoops/foodsoft/pull/966)
|
||||
1. Controller tests
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/8_increase_test_coverage_controllers)
|
||||
* [ ] upstream [#970](https://github.com/foodcoops/foodsoft/pull/970)
|
||||
License
|
||||
-------
|
||||
|
||||
#### Upgrade
|
||||
Foodsoft is licensed under the [AGPL](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
license (version 3 or later). Practically this means that you are free to use,
|
||||
adapt and redistribute the software, as long as you publish any changes you
|
||||
make to the code.
|
||||
|
||||
1. Migrate to RSwag API Tests
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/28_introduce_rswag)
|
||||
* [x] upstream [#969](https://github.com/foodcoops/foodsoft/pull/969)
|
||||
1. Rails v7
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/9_rails_v_7)
|
||||
* [x] upstream [#979](https://github.com/foodcoops/foodsoft/pull/979)
|
||||
disussion [#956](https://github.com/foodcoops/foodsoft/issues/956)
|
||||
1. Javascript Importmap
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/9_rails_v_7_js_importmap)
|
||||
* [x] upstream
|
||||
For private use, there are no restrictions, but if you give others access to
|
||||
Foodsoft (like running it open to the internet), you must also make your
|
||||
changes available under the same license. This can be as easy as
|
||||
[forking](https://github.com/foodcoops/foodsoft/fork) the project on Github and
|
||||
pushing your changes. You are not required to integrate your changes back into
|
||||
the main Foodsoft version (but if you're up for it that would be very welcome).
|
||||
|
||||
#### Article Order Import/Export
|
||||
|
||||
Updating Articles from large resellers and exporting orders is now much easier!
|
||||
|
||||
1. adds bnn fileformat that is used from large german resellers e.g. naturkost nord
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/11_bnn_import_article_update)
|
||||
[gem](https://git.local-it.org/Foodsoft/foodsoft_article_import)
|
||||
* [ ] upstream
|
||||
1. Import category field
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/56_add_update_of_article_category_to_file_import)
|
||||
* [ ] upstream
|
||||
1. Export order as a custom csv file
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/12_generate_custom_csv_file)
|
||||
* [ ] upstream
|
||||
1. Naturkostnord Plugin
|
||||
* [ ] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/12_nkn_file_plugin)
|
||||
* [ ] upstream
|
||||
|
||||
#### Improve User Experience
|
||||
|
||||
1. Richtext editor for messages. Also allows sending attachements.
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/16_html_message_templates)
|
||||
* [x] upstream
|
||||
1. Show the sum of all order group balances
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/47_finance_ordergroup_sums)
|
||||
* [x] upstream
|
||||
1. UI improvements for group order view
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/uxui_group_order)
|
||||
* [ ] upstream
|
||||
1. Favorites
|
||||
* [ ] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/20_favourites)
|
||||
* [ ] upstream
|
||||
1. Show the per kilo / litre price
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/11_include_kilo_litre_price)
|
||||
* [ ] upstream
|
||||
|
||||
#### Other
|
||||
|
||||
1. Fix broken plugin mechanism
|
||||
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/downgrade-haml)
|
||||
* [x] upstream
|
||||
|
||||
#### Screenshots
|
||||
|
||||
![rswag](./doc/screenshots/rswag.png)
|
||||
|
||||
---
|
||||
|
||||
![bnn upload](./doc/screenshots/bnn_upload.png)
|
||||
|
||||
---
|
||||
|
||||
![message formatting](./doc/screenshots/message_formatting.png)
|
||||
|
||||
---
|
||||
|
||||
![balance sum](./doc/screenshots/balance_sum.png)
|
||||
|
||||
---
|
||||
|
||||
![custom csv export](./doc/screenshots/custom_csv_export.png)
|
||||
csv export
|
||||
|
||||
---
|
||||
|
||||
![order](./doc/screenshots/order.png)
|
||||
To make it a little easier, configuration files are exempt, so you can just
|
||||
install and configure Foodsoft without having to publish your changes. These
|
||||
files are marked as public domain in the file header.
|
||||
|
||||
If you have any remaining questions, please
|
||||
[open an issue](https://github.com/foodcoops/foodsoft/issues/new) or open a new
|
||||
topic at the [forum](https://forum.foodcoops.net).
|
||||
|
||||
Please see [LICENSE](LICENSE.md) for the full and authoritative text. Some
|
||||
bundled third-party components have [other licenses](vendor/README.md).
|
||||
|
||||
Thanks to [Icons8](http://icons8.com/) for letting us use their icons.
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env rake
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
require File.expand_path('config/application', __dir__)
|
||||
require 'rake'
|
||||
require 'rspec-rerun/tasks' if defined?(RSpec) # http://stackoverflow.com/a/16853615/2866660
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.es
|
||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl
|
||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.fr
|
||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.tr
|
||||
//= require list
|
||||
//= require list.unlist
|
||||
//= require list.delay
|
||||
|
|
|
@ -179,13 +179,17 @@ function updateBalance() {
|
|||
var balance = groupBalance - total;
|
||||
$('#new_balance').html(I18n.l("currency", balance));
|
||||
$('#total_balance').val(I18n.l("currency", balance));
|
||||
// determine bgcolor and submit button state according to balance
|
||||
var bgcolor = '';
|
||||
if (balance < minimumBalance) {
|
||||
bgcolor = '#FF0000';
|
||||
$('#submit_button').attr('disabled', 'disabled')
|
||||
$('#balance-alert').css('display', 'block')
|
||||
|
||||
} else {
|
||||
$('#submit_button').removeAttr('disabled')
|
||||
$('#balance-alert').css('display', 'none')
|
||||
}
|
||||
// update bgcolor
|
||||
for (i in itemTotal) {
|
||||
$('#td_price_' + i).css('background-color', bgcolor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
|
||||
* the trix-editor content (whether displayed or under editing). Feel free to incorporate this
|
||||
* inclusion directly in any other asset bundle and remove this file.
|
||||
*
|
||||
*= require trix
|
||||
*/
|
||||
|
||||
/*
|
||||
* We need to override trix.css’s image gallery styles to accommodate the
|
||||
* <action-text-attachment> element we wrap around attachments. Otherwise,
|
||||
* images in galleries will be squished by the max-width: 33%; rule.
|
||||
*/
|
||||
.trix-content .attachment-gallery > action-text-attachment,
|
||||
.trix-content .attachment-gallery > .attachment {
|
||||
flex: 1 0 33%;
|
||||
padding: 0 0.5em;
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
|
||||
.trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
|
||||
flex-basis: 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.trix-content action-text-attachment .attachment {
|
||||
padding: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
|
@ -7,5 +7,4 @@
|
|||
*= require list.unlist
|
||||
*= require list.missing
|
||||
*= require recurring_select
|
||||
*= require actiontext
|
||||
*/
|
||||
|
|
|
@ -230,7 +230,7 @@ table {
|
|||
margin: .5em 0;
|
||||
|
||||
input:disabled {
|
||||
background-color: gray; }
|
||||
background-color: red; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,14 +278,13 @@ tr.order-article .article-info {
|
|||
display: none;
|
||||
}
|
||||
|
||||
tr.order-article:focus{
|
||||
background-color: #E4EED6;
|
||||
}
|
||||
tr.order-article:focus .article-info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
tr.order-article:focus {
|
||||
background-color: #E9E9E9;
|
||||
}
|
||||
|
||||
// ********* Articles
|
||||
|
||||
tr.just-updated {
|
||||
|
|
|
@ -1,23 +1,11 @@
|
|||
.missing-many td {
|
||||
background-color: #ffc590aa;
|
||||
.list .missing-many td, .list .missing-many:hover td {
|
||||
background-color: #ebbebe;
|
||||
}
|
||||
|
||||
.missing-many:hover td, .missing-many:focus td {
|
||||
background-color: #ffc590;
|
||||
.list .missing-few td, .list .missing-few:hover td {
|
||||
background-color: #ffee75;
|
||||
}
|
||||
|
||||
.missing-few td {
|
||||
background-color: #fcf488aa;
|
||||
}
|
||||
|
||||
.missing-few:hover td, .missing-few:focus td {
|
||||
background-color: #fcf488;
|
||||
}
|
||||
|
||||
.missing-none td {
|
||||
background-color: #d0f6ffaa;
|
||||
}
|
||||
|
||||
.missing-none:hover td, .missing-none:focus td {
|
||||
background-color: #d0f6ff;
|
||||
.list .missing-none td, .list .missing-none:hover td {
|
||||
background-color: #E4EED6;
|
||||
}
|
||||
|
|
|
@ -3,39 +3,39 @@ class Admin::BankAccountsController < Admin::BaseController
|
|||
|
||||
def new
|
||||
@bank_account = BankAccount.new(params[:bank_account])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@bank_account = BankAccount.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@bank_account = BankAccount.new(params[:bank_account])
|
||||
if @bank_account.valid? && @bank_account.save
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
redirect_to update_bank_accounts_admin_finances_url, status: :see_other
|
||||
else
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@bank_account = BankAccount.find(params[:id])
|
||||
render :action => 'new', :layout => false
|
||||
end
|
||||
|
||||
def update
|
||||
@bank_account = BankAccount.find(params[:id])
|
||||
|
||||
if @bank_account.update(params[:bank_account])
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
redirect_to update_bank_accounts_admin_finances_url, status: :see_other
|
||||
else
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@bank_account = BankAccount.find(params[:id])
|
||||
@bank_account.destroy
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
redirect_to update_bank_accounts_admin_finances_url, status: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
render template: 'shared/alert'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,11 @@ class Admin::BankGatewaysController < Admin::BaseController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@bank_gateway = BankGateway.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@bank_gateway = BankGateway.new(params[:bank_gateway])
|
||||
if @bank_gateway.valid? && @bank_gateway.save
|
||||
|
@ -15,11 +20,6 @@ class Admin::BankGatewaysController < Admin::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@bank_gateway = BankGateway.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def update
|
||||
@bank_gateway = BankGateway.find(params[:id])
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Admin::ConfigsController < Admin::BaseController
|
||||
before_action :get_tabs, only: [:show, :list]
|
||||
before_action :get_tabs, only: %i[show list]
|
||||
|
||||
def show
|
||||
@current_tab = @tabs.include?(params[:tab]) ? params[:tab] : @tabs.first
|
||||
|
@ -16,7 +16,7 @@ class Admin::ConfigsController < Admin::BaseController
|
|||
def update
|
||||
parse_recurring_selects! params[:config][:order_schedule]
|
||||
ActiveRecord::Base.transaction do
|
||||
# TODO support nested configuration keys
|
||||
# TODO: support nested configuration keys
|
||||
params[:config].each do |key, val|
|
||||
FoodsoftConfig[key] = convert_config_value val
|
||||
end
|
||||
|
@ -29,7 +29,7 @@ class Admin::ConfigsController < Admin::BaseController
|
|||
|
||||
# Set configuration tab names as `@tabs`
|
||||
def get_tabs
|
||||
@tabs = %w(foodcoop payment tasks messages layout language security others)
|
||||
@tabs = %w[foodcoop payment tasks messages layout language security others]
|
||||
# allow engines to modify this list
|
||||
engines = Rails::Engine.subclasses.map(&:instance).select { |e| e.respond_to?(:configuration) }
|
||||
engines.each { |e| e.configuration(@tabs, self) }
|
||||
|
@ -38,16 +38,16 @@ class Admin::ConfigsController < Admin::BaseController
|
|||
|
||||
# turn recurring rules into something palatable
|
||||
def parse_recurring_selects!(config)
|
||||
if config
|
||||
for k in [:pickup, :boxfill, :ends] do
|
||||
if config[k]
|
||||
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
||||
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
||||
config[k][:recurr] = ActiveSupport::JSON.decode(config[k][:recurr])
|
||||
config[k][:recurr] = FoodsoftDateUtil.rule_from(config[k][:recurr]).to_ical if config[k][:recurr]
|
||||
else
|
||||
config[k] = nil
|
||||
end
|
||||
return unless config
|
||||
|
||||
for k in %i[pickup boxfill ends] do
|
||||
if config[k]
|
||||
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
||||
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
||||
config[k][:recurr] = ActiveSupport::JSON.decode(config[k][:recurr])
|
||||
config[k][:recurr] = FoodsoftDateUtil.rule_from(config[k][:recurr]).to_ical if config[k][:recurr]
|
||||
else
|
||||
config[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,21 +10,21 @@ class Admin::FinancesController < Admin::BaseController
|
|||
|
||||
def update_bank_accounts
|
||||
@bank_accounts = BankAccount.order('name')
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_bank_gateways
|
||||
@bank_gateways = BankGateway.order('name')
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_transaction_types
|
||||
@financial_transaction_classes = FinancialTransactionClass.includes(:financial_transaction_types).order('name ASC')
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_supplier_categories
|
||||
@supplier_categories = SupplierCategory.order('name')
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,25 +6,25 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
|
||||
if @financial_transaction_class.save
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
|
||||
if @financial_transaction_class.save
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||
|
||||
if @financial_transaction_class.update(params[:financial_transaction_class])
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
@ -33,9 +33,9 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController
|
|||
def destroy
|
||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||
@financial_transaction_class.destroy!
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
render template: 'shared/alert'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,25 +7,25 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
|
||||
if @financial_transaction_type.save
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
|
||||
if @financial_transaction_type.save
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||
|
||||
if @financial_transaction_type.update(params[:financial_transaction_type])
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
@ -34,9 +34,9 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
|
|||
def destroy
|
||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||
@financial_transaction_type.destroy!
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
render template: 'shared/alert'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,28 +3,28 @@ class Admin::MailDeliveryStatusController < Admin::BaseController
|
|||
|
||||
def index
|
||||
@maildeliverystatus = MailDeliveryStatus.order(created_at: :desc)
|
||||
@maildeliverystatus = @maildeliverystatus.where(email: params[:email]) unless params[:email].blank?
|
||||
@maildeliverystatus = @maildeliverystatus.where(email: params[:email]) if params[:email].present?
|
||||
@maildeliverystatus = @maildeliverystatus.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
||||
def show
|
||||
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
||||
filename = "maildeliverystatus_#{params[:id]}.#{MIME::Types[@maildeliverystatus.attachment_mime].first.preferred_extension}"
|
||||
send_data(@maildeliverystatus.attachment_data, :filename => filename, :type => @maildeliverystatus.attachment_mime)
|
||||
send_data(@maildeliverystatus.attachment_data, filename: filename, type: @maildeliverystatus.attachment_mime)
|
||||
end
|
||||
|
||||
def destroy_all
|
||||
@maildeliverystatus = MailDeliveryStatus.delete_all
|
||||
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
||||
rescue => error
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
||||
@maildeliverystatus.destroy
|
||||
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
||||
rescue => error
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,16 +2,15 @@ class Admin::OrdergroupsController < Admin::BaseController
|
|||
inherit_resources
|
||||
|
||||
def index
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"])
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params['sort'])
|
||||
|
||||
if request.format.csv?
|
||||
send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv', type: 'text/csv'
|
||||
send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv',
|
||||
type: 'text/csv'
|
||||
end
|
||||
|
||||
# if somebody uses the search field:
|
||||
unless params[:query].blank?
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%")
|
||||
end
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present?
|
||||
|
||||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -19,8 +18,8 @@ class Admin::OrdergroupsController < Admin::BaseController
|
|||
def destroy
|
||||
@ordergroup = Ordergroup.find(params[:id])
|
||||
@ordergroup.mark_as_deleted
|
||||
redirect_to admin_ordergroups_url, notice: t('admin.ordergroups.destroy.notice')
|
||||
rescue => error
|
||||
redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error')
|
||||
redirect_to admin_ordergroups_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_ordergroups_url, alert: t('.error')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,11 @@ class Admin::SupplierCategoriesController < Admin::BaseController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@supplier_category = SupplierCategory.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@supplier_category = SupplierCategory.new(params[:supplier_category])
|
||||
if @supplier_category.valid? && @supplier_category.save
|
||||
|
@ -15,11 +20,6 @@ class Admin::SupplierCategoriesController < Admin::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@supplier_category = SupplierCategory.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def update
|
||||
@supplier_category = SupplierCategory.find(params[:id])
|
||||
|
||||
|
|
|
@ -3,16 +3,14 @@ class Admin::UsersController < Admin::BaseController
|
|||
|
||||
def index
|
||||
@users = params[:show_deleted] ? User.deleted : User.undeleted
|
||||
@users = @users.sort_by_param(params["sort"])
|
||||
@users = @users.sort_by_param(params['sort'])
|
||||
|
||||
@users = @users.includes(:mail_delivery_status)
|
||||
|
||||
if request.format.csv?
|
||||
send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv'
|
||||
end
|
||||
send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv' if request.format.csv?
|
||||
|
||||
# if somebody uses the search field:
|
||||
@users = @users.natural_search(params[:user_name]) unless params[:user_name].blank?
|
||||
@users = @users.natural_search(params[:user_name]) if params[:user_name].present?
|
||||
|
||||
@users = @users.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -20,17 +18,17 @@ class Admin::UsersController < Admin::BaseController
|
|||
def destroy
|
||||
@user = User.find(params[:id])
|
||||
@user.mark_as_deleted
|
||||
redirect_to admin_users_url, notice: t('admin.users.destroy.notice')
|
||||
rescue => error
|
||||
redirect_to admin_users_url, alert: t('admin.users.destroy.error', error: error.message)
|
||||
redirect_to admin_users_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
||||
end
|
||||
|
||||
def restore
|
||||
@user = User.find(params[:id])
|
||||
@user.restore
|
||||
redirect_to admin_users_url, notice: t('admin.users.restore.notice')
|
||||
rescue => error
|
||||
redirect_to admin_users_url, alert: t('admin.users.restore.error', error: error.message)
|
||||
redirect_to admin_users_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
||||
end
|
||||
|
||||
def sudo
|
||||
|
|
|
@ -4,7 +4,7 @@ class Admin::WorkgroupsController < Admin::BaseController
|
|||
def index
|
||||
@workgroups = Workgroup.order('name ASC')
|
||||
# if somebody uses the search field:
|
||||
@workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].blank?
|
||||
@workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present?
|
||||
|
||||
@workgroups = @workgroups.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -12,8 +12,8 @@ class Admin::WorkgroupsController < Admin::BaseController
|
|||
def destroy
|
||||
@workgroup = Workgroup.find(params[:id])
|
||||
@workgroup.destroy
|
||||
redirect_to admin_workgroups_url, notice: t('admin.workgroups.destroy.notice')
|
||||
rescue => error
|
||||
redirect_to admin_workgroups_url, alert: t('admin.workgroups.destroy.error', error: error.message)
|
||||
redirect_to admin_workgroups_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_workgroups_url, alert: t('.error', error: e.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,29 +20,30 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
def require_ordergroup
|
||||
authenticate
|
||||
unless current_ordergroup.present?
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup')
|
||||
end
|
||||
return if current_ordergroup.present?
|
||||
|
||||
raise Api::Errors::PermissionRequired, 'Forbidden, must be in an ordergroup'
|
||||
end
|
||||
|
||||
def require_minimum_balance
|
||||
minimum_balance = FoodsoftConfig[:minimum_balance] or return
|
||||
if current_ordergroup.account_balance < minimum_balance
|
||||
raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance))
|
||||
end
|
||||
return unless current_ordergroup.account_balance < minimum_balance
|
||||
|
||||
raise Api::Errors::PermissionRequired, t('application.controller.error_minimum_balance', min: minimum_balance)
|
||||
end
|
||||
|
||||
def require_enough_apples
|
||||
if current_ordergroup.not_enough_apples?
|
||||
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||
raise Api::Errors::PermissionRequired.new(s)
|
||||
end
|
||||
return unless current_ordergroup.not_enough_apples?
|
||||
|
||||
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples,
|
||||
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||
raise Api::Errors::PermissionRequired, s
|
||||
end
|
||||
|
||||
def require_config_enabled(config)
|
||||
unless FoodsoftConfig[config]
|
||||
raise Api::Errors::PermissionRequired.new(t('application.controller.error_not_enabled', config: config))
|
||||
end
|
||||
return if FoodsoftConfig[config]
|
||||
|
||||
raise Api::Errors::PermissionRequired, t('application.controller.error_not_enabled', config: config)
|
||||
end
|
||||
|
||||
def skip_session
|
||||
|
@ -52,12 +53,12 @@ class Api::V1::BaseController < ApplicationController
|
|||
def not_found_handler(e)
|
||||
# remove where-clauses from error message (not suitable for end-users)
|
||||
msg = e.message.try { |m| m.sub(/\s*\[.*?\]\s*$/, '') } || 'Not found'
|
||||
render status: 404, json: { error: 'not_found', error_description: msg }
|
||||
render status: :not_found, json: { error: 'not_found', error_description: msg }
|
||||
end
|
||||
|
||||
def not_acceptable_handler(e)
|
||||
msg = e.message || 'Data not acceptable'
|
||||
render status: 422, json: { error: 'not_acceptable', error_description: msg }
|
||||
render status: :unprocessable_entity, json: { error: 'not_acceptable', error_description: msg }
|
||||
end
|
||||
|
||||
def doorkeeper_unauthorized_render_options(error:)
|
||||
|
@ -70,11 +71,11 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
def permission_required_handler(e)
|
||||
msg = e.message || 'Forbidden, user has no access'
|
||||
render status: 403, json: { error: 'forbidden', error_description: msg }
|
||||
render status: :forbidden, json: { error: 'forbidden', error_description: msg }
|
||||
end
|
||||
|
||||
# @todo something with ApplicationHelper#show_user
|
||||
def show_user(user = current_user, **options)
|
||||
def show_user(user = current_user, **_options)
|
||||
user.display
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,8 @@ class Api::V1::User::FinancialTransactionsController < Api::V1::BaseController
|
|||
|
||||
def create
|
||||
transaction_type = FinancialTransactionType.find(create_params[:financial_transaction_type_id])
|
||||
ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user, transaction_type)
|
||||
ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user,
|
||||
transaction_type)
|
||||
render json: ft
|
||||
end
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController
|
|||
before_action -> { doorkeeper_authorize! 'group_orders:user' }
|
||||
|
||||
before_action :require_ordergroup
|
||||
before_action :require_minimum_balance, only: [:create, :update] # destroy is ok
|
||||
before_action :require_enough_apples, only: [:create, :update] # destroy is ok
|
||||
before_action :require_minimum_balance, only: %i[create update] # destroy is ok
|
||||
before_action :require_enough_apples, only: %i[create update] # destroy is ok
|
||||
# @todo allow decreasing amounts when minimum balance isn't met
|
||||
|
||||
def index
|
||||
|
@ -35,7 +35,8 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController
|
|||
goa = nil
|
||||
GroupOrderArticle.transaction do
|
||||
goa = scope_for_update.includes(:group_order_article_quantities).find(params.require(:id))
|
||||
goa.update_quantities((update_params[:quantity] || goa.quantity).to_i, (update_params[:tolerance] || goa.tolerance).to_i)
|
||||
goa.update_quantities((update_params[:quantity] || goa.quantity).to_i,
|
||||
(update_params[:tolerance] || goa.tolerance).to_i)
|
||||
goa.order_article.update_results!
|
||||
goa.group_order.update_price!
|
||||
goa.group_order.update!(updated_by: current_user)
|
||||
|
|
|
@ -8,13 +8,13 @@ class Api::V1::User::OrdergroupController < Api::V1::BaseController
|
|||
financial_overview: {
|
||||
account_balance: ordergroup.account_balance.to_f,
|
||||
available_funds: ordergroup.get_available_funds.to_f,
|
||||
financial_transaction_class_sums: FinancialTransactionClass.sorted.map { |c|
|
||||
financial_transaction_class_sums: FinancialTransactionClass.sorted.map do |c|
|
||||
{
|
||||
id: c.id,
|
||||
name: c.display,
|
||||
amount: ordergroup["sum_of_class_#{c.id}"].to_f
|
||||
}
|
||||
}
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -19,10 +19,10 @@ class ApplicationController < ActionController::Base
|
|||
private
|
||||
|
||||
def set_user_last_activity
|
||||
if current_user && (session[:last_activity] == nil || session[:last_activity] < 1.minutes.ago)
|
||||
current_user.update_attribute(:last_activity, Time.now)
|
||||
session[:last_activity] = Time.now
|
||||
end
|
||||
return unless current_user && (session[:last_activity].nil? || session[:last_activity] < 1.minute.ago)
|
||||
|
||||
current_user.update_attribute(:last_activity, Time.now)
|
||||
session[:last_activity] = Time.now
|
||||
end
|
||||
|
||||
# Many plugins can be turned on and off on the fly with a `use_` configuration option.
|
||||
|
@ -64,11 +64,11 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def items_per_page
|
||||
if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
|
||||
@per_page = params[:per_page].to_i
|
||||
else
|
||||
@per_page = 20
|
||||
end
|
||||
@per_page = if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
|
||||
params[:per_page].to_i
|
||||
else
|
||||
20
|
||||
end
|
||||
end
|
||||
|
||||
# Set timezone according to foodcoop preference.
|
||||
|
|
|
@ -4,17 +4,17 @@ class ArticleCategoriesController < ApplicationController
|
|||
before_action :authenticate_article_meta
|
||||
|
||||
def create
|
||||
create!(:notice => I18n.t('article_categories.create.notice')) { article_categories_path }
|
||||
create!(notice: I18n.t('article_categories.create.notice')) { article_categories_path }
|
||||
end
|
||||
|
||||
def update
|
||||
update!(:notice => I18n.t('article_categories.update.notice')) { article_categories_path }
|
||||
update!(notice: I18n.t('article_categories.update.notice')) { article_categories_path }
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy!
|
||||
rescue => error
|
||||
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: e.message)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -2,24 +2,24 @@ 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"
|
||||
sort = if params['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
|
||||
else
|
||||
'article_categories.name, articles.name'
|
||||
end
|
||||
|
||||
@articles = Article.undeleted.where(supplier_id: @supplier, :type => nil).includes(:article_category).order(sort)
|
||||
@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'
|
||||
|
@ -32,31 +32,31 @@ class ArticlesController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.js { render :layout => false }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
@article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default])
|
||||
render :layout => false
|
||||
@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
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@article = Article.find(params[:id])
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@article = Article.new(params[:article])
|
||||
if @article.valid? && @article.save
|
||||
render :layout => false
|
||||
render layout: false
|
||||
else
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,9 +65,9 @@ class ArticlesController < ApplicationController
|
|||
@article = Article.find(params[:id])
|
||||
|
||||
if @article.update(params[:article])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
else
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,7 +75,7 @@ class ArticlesController < ApplicationController
|
|||
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
|
||||
render layout: false
|
||||
end
|
||||
|
||||
# Renders a form for editing all articles from a supplier
|
||||
|
@ -87,19 +87,17 @@ class ArticlesController < ApplicationController
|
|||
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
|
||||
Article.transaction do
|
||||
if params[:articles].present?
|
||||
# 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 # Remember that there are validation errors
|
||||
end
|
||||
|
||||
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||
end
|
||||
|
||||
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -134,32 +132,31 @@ class ArticlesController < ApplicationController
|
|||
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)
|
||||
redirect_to supplier_articles_url(@supplier, per_page: params[:per_page])
|
||||
rescue StandardError => e
|
||||
redirect_to supplier_articles_url(@supplier, per_page: params[:per_page]),
|
||||
alert: I18n.t('errors.general_msg', msg: e)
|
||||
end
|
||||
|
||||
# lets start with parsing articles from uploaded file, yeah
|
||||
# Renders the upload form
|
||||
def upload
|
||||
end
|
||||
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
|
||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile,
|
||||
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')
|
||||
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)
|
||||
rescue StandardError => e
|
||||
redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
# sync all articles with the external database
|
||||
|
@ -167,13 +164,14 @@ class ArticlesController < ApplicationController
|
|||
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)
|
||||
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
|
||||
return unless @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||
|
||||
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.sync.notice')
|
||||
end
|
||||
|
||||
# Updates, deletes articles when upload or sync form is submitted
|
||||
|
@ -188,7 +186,7 @@ class ArticlesController < ApplicationController
|
|||
# delete articles
|
||||
begin
|
||||
@outlisted_articles.each(&:mark_as_deleted)
|
||||
rescue
|
||||
rescue StandardError
|
||||
# raises an exception when used in current order
|
||||
has_error = true
|
||||
end
|
||||
|
@ -200,15 +198,15 @@ class ArticlesController < ApplicationController
|
|||
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
|
||||
if has_error
|
||||
@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
|
||||
else
|
||||
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -220,18 +218,18 @@ class ArticlesController < ApplicationController
|
|||
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
|
||||
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
|
||||
@article.article_category_id = params[:article_category_id] if params[:article_category_id].present?
|
||||
if params[:direct] && params[:article_category_id].present? && @article.valid? && @article.save
|
||||
render action: 'create', layout: false
|
||||
else
|
||||
render :action => 'new', :layout => false
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,15 +9,19 @@ module Concerns::Auth
|
|||
|
||||
def current_user
|
||||
# check if there is a valid session and return the logged-in user (its object)
|
||||
if session[:user_id] && params[:foodcoop]
|
||||
# for shared-host installations. check if the cookie-subdomain fits to request.
|
||||
@current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
|
||||
end
|
||||
return unless session[:user_id] && params[:foodcoop]
|
||||
|
||||
# for shared-host installations. check if the cookie-subdomain fits to request.
|
||||
@current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
|
||||
end
|
||||
|
||||
def deny_access
|
||||
session[:return_to] = request.original_url
|
||||
redirect_to root_url, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path))
|
||||
redirect_to root_url,
|
||||
alert: I18n.t('application.controller.error_denied',
|
||||
sign_in: ActionController::Base.helpers.link_to(
|
||||
t('application.controller.error_denied_sign_in'), login_path
|
||||
))
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -47,12 +51,7 @@ module Concerns::Auth
|
|||
|
||||
def authenticate(role = 'any')
|
||||
# Attempt to retrieve authenticated user from controller instance or session...
|
||||
if !current_user
|
||||
# No user at all: redirect to login page.
|
||||
logout
|
||||
session[:return_to] = request.original_url
|
||||
redirect_to_login :alert => I18n.t('application.controller.error_authn')
|
||||
else
|
||||
if current_user
|
||||
# We have an authenticated user, now check role...
|
||||
# Roles gets the user through his memberships.
|
||||
hasRole = case role
|
||||
|
@ -73,6 +72,11 @@ module Concerns::Auth
|
|||
else
|
||||
deny_access
|
||||
end
|
||||
else
|
||||
# No user at all: redirect to login page.
|
||||
logout
|
||||
session[:return_to] = request.original_url
|
||||
redirect_to_login alert: I18n.t('application.controller.error_authn')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -116,13 +120,13 @@ module Concerns::Auth
|
|||
# if fails the user will redirected to startpage
|
||||
def authenticate_membership_or_admin(group_id = params[:id])
|
||||
@group = Group.find(group_id)
|
||||
unless @group.member?(@current_user) || @current_user.role_admin?
|
||||
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
||||
end
|
||||
return if @group.member?(@current_user) || @current_user.role_admin?
|
||||
|
||||
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
||||
end
|
||||
|
||||
def authenticate_or_token(prefix, role = 'any')
|
||||
if not params[:token].blank?
|
||||
if params[:token].present?
|
||||
begin
|
||||
TokenVerifier.new(prefix).verify(params[:token])
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
|
|
|
@ -36,9 +36,9 @@ module Concerns::AuthApi
|
|||
# Make sure that at least one the given OAuth scopes is valid for the current user's permissions.
|
||||
# @raise Api::Errors::PermissionsRequired
|
||||
def doorkeeper_authorize_roles!(*scopes)
|
||||
unless scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, no permission')
|
||||
end
|
||||
return if scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
||||
|
||||
raise Api::Errors::PermissionRequired, 'Forbidden, no permission'
|
||||
end
|
||||
|
||||
# Check whether a given OAuth scope is permitted for the current user.
|
||||
|
@ -48,9 +48,7 @@ module Concerns::AuthApi
|
|||
def doorkeeper_scope_permitted?(scope)
|
||||
scope_parts = scope.split(':')
|
||||
# user sub-scopes like +config:user+ are always permitted
|
||||
if scope_parts.last == 'user'
|
||||
return true
|
||||
end
|
||||
return true if scope_parts.last == 'user'
|
||||
|
||||
case scope_parts.first
|
||||
when 'user' then return true # access to the current user's own profile
|
||||
|
@ -64,8 +62,8 @@ module Concerns::AuthApi
|
|||
end
|
||||
|
||||
case scope
|
||||
when 'orders:read' then return true
|
||||
when 'orders:write' then return current_user.role_orders?
|
||||
when 'orders:read' then true
|
||||
when 'orders:write' then current_user.role_orders?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,12 +24,12 @@ module Concerns::FoodcoopScope
|
|||
elsif FoodsoftConfig.allowed_foodcoop? foodcoop
|
||||
FoodsoftConfig.select_foodcoop foodcoop
|
||||
else
|
||||
raise ActionController::RoutingError.new 'Foodcoop Not Found'
|
||||
raise ActionController::RoutingError, 'Foodcoop Not Found'
|
||||
end
|
||||
end
|
||||
|
||||
# Always stay in foodcoop url scope
|
||||
def default_url_options(options = {})
|
||||
def default_url_options(_options = {})
|
||||
super().merge({ foodcoop: FoodsoftConfig.scope })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module Concerns::Locale
|
|||
end
|
||||
|
||||
def browser_language
|
||||
request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil
|
||||
request.env['HTTP_ACCEPT_LANGUAGE']&.scan(/^[a-z]{2}/)&.first
|
||||
end
|
||||
|
||||
def default_language
|
||||
|
@ -30,7 +30,7 @@ module Concerns::Locale
|
|||
def select_language_according_to_priority
|
||||
language = explicitly_requested_language || session_language || user_settings_language
|
||||
language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale]
|
||||
language.presence&.to_sym unless language.blank?
|
||||
language.presence&.to_sym if language.present?
|
||||
end
|
||||
|
||||
def available_locales
|
||||
|
@ -38,11 +38,11 @@ module Concerns::Locale
|
|||
end
|
||||
|
||||
def set_locale
|
||||
if available_locales.include?(select_language_according_to_priority)
|
||||
::I18n.locale = select_language_according_to_priority
|
||||
else
|
||||
::I18n.locale = default_language
|
||||
end
|
||||
::I18n.locale = if available_locales.include?(select_language_according_to_priority)
|
||||
select_language_according_to_priority
|
||||
else
|
||||
default_language
|
||||
end
|
||||
|
||||
locale = session[:locale] = ::I18n.locale
|
||||
logger.info("Set locale to #{locale}")
|
||||
|
|
|
@ -3,7 +3,7 @@ module Concerns::SendOrderPdf
|
|||
|
||||
protected
|
||||
|
||||
def send_order_pdf order, document
|
||||
def send_order_pdf(order, document)
|
||||
klass = case document
|
||||
when 'groups' then OrderByGroups
|
||||
when 'articles' then OrderByArticles
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class DeliveriesController < ApplicationController
|
||||
before_action :find_supplier, :exclude => :fill_new_stock_article_form
|
||||
before_action :find_supplier, exclude: :fill_new_stock_article_form
|
||||
|
||||
def index
|
||||
@deliveries = @supplier.deliveries.order('date DESC')
|
||||
|
@ -15,6 +15,10 @@ class DeliveriesController < ApplicationController
|
|||
@delivery.date = Date.today # TODO: move to model/database
|
||||
end
|
||||
|
||||
def edit
|
||||
@delivery = Delivery.find(params[:id])
|
||||
end
|
||||
|
||||
def create
|
||||
@delivery = Delivery.new(params[:delivery])
|
||||
|
||||
|
@ -22,14 +26,10 @@ class DeliveriesController < ApplicationController
|
|||
flash[:notice] = I18n.t('deliveries.create.notice')
|
||||
redirect_to [@supplier, @delivery]
|
||||
else
|
||||
render :action => "new"
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@delivery = Delivery.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@delivery = Delivery.find(params[:id])
|
||||
|
||||
|
@ -37,7 +37,7 @@ class DeliveriesController < ApplicationController
|
|||
flash[:notice] = I18n.t('deliveries.update.notice')
|
||||
redirect_to [@supplier, @delivery]
|
||||
else
|
||||
render :action => "edit"
|
||||
render action: 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -52,18 +52,18 @@ class DeliveriesController < ApplicationController
|
|||
def add_stock_change
|
||||
@stock_change = StockChange.new
|
||||
@stock_change.stock_article = StockArticle.find(params[:stock_article_id])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def form_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def form_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
class FeedbackController < ApplicationController
|
||||
def new
|
||||
end
|
||||
def new; end
|
||||
|
||||
def create
|
||||
if params[:message].present?
|
||||
Mailer.feedback(current_user, params[:message]).deliver_now
|
||||
redirect_to root_url, notice: t('feedback.create.notice')
|
||||
redirect_to root_url, notice: t('.notice')
|
||||
else
|
||||
render :action => 'new'
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController
|
|||
|
||||
def new
|
||||
@order = Order.find(params[:order_id])
|
||||
flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
|
||||
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? && flash[:alert].blank?
|
||||
@comments = @order.comments
|
||||
|
||||
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
||||
|
@ -13,13 +13,13 @@ class Finance::BalancingController < Finance::BaseController
|
|||
|
||||
sort_param = params['sort'] || 'name'
|
||||
@articles = case sort_param
|
||||
when 'name' then
|
||||
when 'name'
|
||||
@articles.order('articles.name ASC')
|
||||
when 'name_reverse' then
|
||||
when 'name_reverse'
|
||||
@articles.order('articles.name DESC')
|
||||
when 'order_number' then
|
||||
when 'order_number'
|
||||
@articles.order('articles.order_number ASC')
|
||||
when 'order_number_reverse' then
|
||||
when 'order_number_reverse'
|
||||
@articles.order('articles.order_number DESC')
|
||||
else
|
||||
@articles
|
||||
|
@ -31,13 +31,13 @@ class Finance::BalancingController < Finance::BaseController
|
|||
def new_on_order_article_create # See publish/subscribe design pattern in /doc.
|
||||
@order_article = OrderArticle.find(params[:order_article_id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def new_on_order_article_update # See publish/subscribe design pattern in /doc.
|
||||
@order_article = OrderArticle.find(params[:order_article_id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_summary
|
||||
|
@ -46,29 +46,29 @@ class Finance::BalancingController < Finance::BaseController
|
|||
|
||||
def edit_note
|
||||
@order = Order.find(params[:id])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_note
|
||||
@order = Order.find(params[:id])
|
||||
if @order.update(params[:order])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
else
|
||||
render :action => :edit_note, :layout => false
|
||||
render action: :edit_note, layout: false
|
||||
end
|
||||
end
|
||||
|
||||
def edit_transport
|
||||
@order = Order.find(params[:id])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def update_transport
|
||||
@order = Order.find(params[:id])
|
||||
@order.update!(params[:order])
|
||||
redirect_to new_finance_order_path(order_id: @order.id)
|
||||
rescue => error
|
||||
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
# before the order will booked, a view lists all Ordergroups and its order_prices
|
||||
|
@ -81,18 +81,33 @@ class Finance::BalancingController < Finance::BaseController
|
|||
@order = Order.find(params[:id])
|
||||
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
||||
@order.close!(@current_user, @type)
|
||||
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
|
||||
note = t('finance.balancing.close.notice')
|
||||
if @order.closed?
|
||||
alert = t('finance.balancing.close.alert')
|
||||
if FoodsoftConfig[:group_order_invoices]&.[](:use_automatic_invoices)
|
||||
@order.group_orders.each do |go|
|
||||
alert = t('finance.balancing.close.settings_not_set')
|
||||
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
||||
if goi.save!
|
||||
NotifyGroupOrderInvoiceJob.perform_later(goi)
|
||||
note = t('finance.balancing.close.notice_mail')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
alert ||= t('finance.balancing.close.alert')
|
||||
redirect_to finance_order_index_url, notice: note
|
||||
rescue => error
|
||||
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
|
||||
redirect_to new_finance_order_url(order_id: @order.id), notice: note, alert: alert, msg: error.message
|
||||
end
|
||||
|
||||
# Close the order directly, without automaticly updating ordergroups account balances
|
||||
def close_direct
|
||||
@order = Order.find(params[:id])
|
||||
@order.close_direct!(@current_user)
|
||||
redirect_to finance_order_index_url, notice: t('finance.balancing.close_direct.notice')
|
||||
rescue => error
|
||||
redirect_to finance_order_index_url, alert: t('finance.balancing.close_direct.alert', message: error.message)
|
||||
redirect_to finance_order_index_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to finance_order_index_url, alert: t('.alert', message: e.message)
|
||||
end
|
||||
|
||||
def close_all_direct_with_invoice
|
||||
|
@ -103,8 +118,8 @@ class Finance::BalancingController < Finance::BaseController
|
|||
count += 1
|
||||
end
|
||||
end
|
||||
redirect_to finance_order_index_url, notice: t('finance.balancing.close_all_direct_with_invoice.notice', count: count)
|
||||
rescue => error
|
||||
redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: error.message)
|
||||
redirect_to finance_order_index_url, notice: t('.notice', count: count)
|
||||
rescue StandardError => e
|
||||
redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,8 +8,8 @@ class Finance::BankAccountsController < Finance::BaseController
|
|||
@bank_account = BankAccount.find(params[:id])
|
||||
count = @bank_account.assign_unlinked_transactions
|
||||
redirect_to finance_bank_account_transactions_url(@bank_account), notice: t('.notice', count: count)
|
||||
rescue => error
|
||||
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
def import
|
||||
|
@ -33,8 +33,8 @@ class Finance::BankAccountsController < Finance::BaseController
|
|||
end
|
||||
|
||||
needs_redirect = ok
|
||||
rescue => error
|
||||
flash.alert = t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
flash.alert = t('errors.general_msg', msg: e.message)
|
||||
needs_redirect = true
|
||||
ensure
|
||||
return unless needs_redirect
|
||||
|
|
|
@ -3,26 +3,30 @@ class Finance::BankTransactionsController < ApplicationController
|
|||
inherit_resources
|
||||
|
||||
def index
|
||||
if params["sort"]
|
||||
sort = case params["sort"]
|
||||
when "date" then "date"
|
||||
when "amount" then "amount"
|
||||
when "financial_link" then "financial_link_id"
|
||||
when "date_reverse" then "date DESC"
|
||||
when "amount_reverse" then "amount DESC"
|
||||
when "financial_link_reverse" then "financial_link_id DESC"
|
||||
sort = if params['sort']
|
||||
case params['sort']
|
||||
when 'date' then 'date'
|
||||
when 'amount' then 'amount'
|
||||
when 'financial_link' then 'financial_link_id'
|
||||
when 'date_reverse' then 'date DESC'
|
||||
when 'amount_reverse' then 'amount DESC'
|
||||
when 'financial_link_reverse' then 'financial_link_id DESC'
|
||||
end
|
||||
else
|
||||
sort = "date DESC"
|
||||
end
|
||||
else
|
||||
'date DESC'
|
||||
end
|
||||
|
||||
@bank_account = BankAccount.find(params[:bank_account_id])
|
||||
@bank_transactions_all = @bank_account.bank_transactions.order(sort).includes(:financial_link)
|
||||
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%", "%#{params[:query]}%") unless params[:query].nil?
|
||||
unless params[:query].nil?
|
||||
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%",
|
||||
"%#{params[:query]}%")
|
||||
end
|
||||
@bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page)
|
||||
|
||||
respond_to do |format|
|
||||
format.js; format.html { render }
|
||||
format.js
|
||||
format.html { render }
|
||||
format.csv do
|
||||
send_data BankTransactionsCsv.new(@bank_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv'
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Finance::FinancialLinksController < Finance::BaseController
|
||||
before_action :find_financial_link, except: [:create, :incomplete]
|
||||
before_action :find_financial_link, except: %i[create incomplete]
|
||||
|
||||
def show
|
||||
@items = @financial_link.bank_transactions.map do |bt|
|
||||
|
@ -37,7 +37,7 @@ class Finance::FinancialLinksController < Finance::BaseController
|
|||
|
||||
def create
|
||||
@financial_link = FinancialLink.first_unused_or_create
|
||||
if params[:bank_transaction] then
|
||||
if params[:bank_transaction]
|
||||
bank_transaction = BankTransaction.find(params[:bank_transaction])
|
||||
bank_transaction.update_attribute :financial_link, @financial_link
|
||||
end
|
||||
|
@ -72,14 +72,16 @@ class Finance::FinancialLinksController < Finance::BaseController
|
|||
|
||||
def create_financial_transaction
|
||||
financial_transaction = FinancialTransaction.new(financial_transaction_params)
|
||||
financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount, financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link
|
||||
financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount,
|
||||
financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link
|
||||
redirect_to finance_link_url(@financial_link), notice: t('.notice')
|
||||
rescue => error
|
||||
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: error)
|
||||
rescue StandardError => e
|
||||
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: e)
|
||||
end
|
||||
|
||||
def index_financial_transaction
|
||||
@financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type, :ordergroup)
|
||||
@financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type,
|
||||
:ordergroup)
|
||||
end
|
||||
|
||||
def add_financial_transaction
|
||||
|
@ -123,7 +125,7 @@ class Finance::FinancialLinksController < Finance::BaseController
|
|||
end
|
||||
|
||||
def find_best_fitting_ordergroup_id_for_financial_link(financial_link_id)
|
||||
FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pluck(:ordergroup_id).first
|
||||
FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pick(:ordergroup_id)
|
||||
JOIN bank_transactions a ON financial_transactions.financial_link_id = a.financial_link_id
|
||||
JOIN bank_transactions b ON a.iban = b.iban AND b.financial_link_id = #{financial_link_id.to_i}
|
||||
SQL
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
class Finance::FinancialTransactionsController < ApplicationController
|
||||
before_action :authenticate_finance
|
||||
before_action :find_ordergroup, :except => [:new_collection, :create_collection, :index_collection]
|
||||
before_action :find_ordergroup, except: %i[new_collection create_collection index_collection]
|
||||
inherit_resources
|
||||
# belongs_to :ordergroup
|
||||
|
||||
def index
|
||||
if params['sort']
|
||||
sort = case params['sort']
|
||||
when "date" then "created_on"
|
||||
when "note" then "note"
|
||||
when "amount" then "amount"
|
||||
when "date_reverse" then "created_on DESC"
|
||||
when "note_reverse" then "note DESC"
|
||||
when "amount_reverse" then "amount DESC"
|
||||
sort = if params['sort']
|
||||
case params['sort']
|
||||
when 'date' then 'created_on'
|
||||
when 'note' then 'note'
|
||||
when 'amount' then 'amount'
|
||||
when 'date_reverse' then 'created_on DESC'
|
||||
when 'note_reverse' then 'note DESC'
|
||||
when 'amount_reverse' then 'amount DESC'
|
||||
end
|
||||
else
|
||||
sort = "created_on DESC"
|
||||
end
|
||||
else
|
||||
'created_on DESC'
|
||||
end
|
||||
|
||||
@q = FinancialTransaction.ransack(params[:q])
|
||||
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
|
||||
|
@ -26,9 +26,11 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
@financial_transactions = @financial_transactions_all.page(params[:page]).per(@per_page)
|
||||
|
||||
respond_to do |format|
|
||||
format.js; format.html { render }
|
||||
format.js
|
||||
format.html { render }
|
||||
format.csv do
|
||||
send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv'
|
||||
send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv',
|
||||
type: 'text/csv'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -38,11 +40,11 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
if @ordergroup
|
||||
@financial_transaction = @ordergroup.financial_transactions.build
|
||||
else
|
||||
@financial_transaction = FinancialTransaction.new
|
||||
end
|
||||
@financial_transaction = if @ordergroup
|
||||
@ordergroup.financial_transactions.build
|
||||
else
|
||||
FinancialTransaction.new
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -53,16 +55,18 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
else
|
||||
@financial_transaction.save!
|
||||
end
|
||||
redirect_to finance_group_transactions_path(@ordergroup), notice: I18n.t('finance.financial_transactions.controller.create.notice')
|
||||
rescue ActiveRecord::RecordInvalid => error
|
||||
flash.now[:alert] = error.message
|
||||
render :action => :new
|
||||
redirect_to finance_group_transactions_path(@ordergroup),
|
||||
notice: I18n.t('finance.financial_transactions.controller.create.notice')
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
flash.now[:alert] = e.message
|
||||
render action: :new
|
||||
end
|
||||
|
||||
def destroy
|
||||
transaction = FinancialTransaction.find(params[:id])
|
||||
transaction.revert!(current_user)
|
||||
redirect_to finance_group_transactions_path(transaction.ordergroup), notice: t('finance.financial_transactions.controller.destroy.notice')
|
||||
redirect_to finance_group_transactions_path(transaction.ordergroup),
|
||||
notice: t('finance.financial_transactions.controller.destroy.notice')
|
||||
end
|
||||
|
||||
def new_collection
|
||||
|
@ -88,17 +92,17 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
|
||||
params[:financial_transactions].each do |trans|
|
||||
# ignore empty amount fields ...
|
||||
unless trans[:amount].blank?
|
||||
amount = LocalizeInput.parse(trans[:amount]).to_f
|
||||
note = params[:note]
|
||||
ordergroup = Ordergroup.find(trans[:ordergroup_id])
|
||||
if params[:set_balance]
|
||||
note += " (#{amount})"
|
||||
amount -= ordergroup.financial_transaction_class_balance(type.financial_transaction_class)
|
||||
end
|
||||
ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link)
|
||||
foodcoop_amount -= amount
|
||||
next if trans[:amount].blank?
|
||||
|
||||
amount = LocalizeInput.parse(trans[:amount]).to_f
|
||||
note = params[:note]
|
||||
ordergroup = Ordergroup.find(trans[:ordergroup_id])
|
||||
if params[:set_balance]
|
||||
note += " (#{amount})"
|
||||
amount -= ordergroup.financial_transaction_class_balance(type.financial_transaction_class)
|
||||
end
|
||||
ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link)
|
||||
foodcoop_amount -= amount
|
||||
end
|
||||
|
||||
if params[:create_foodcoop_transaction]
|
||||
|
@ -107,7 +111,7 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
user: @current_user,
|
||||
amount: foodcoop_amount,
|
||||
note: params[:note],
|
||||
financial_link: financial_link,
|
||||
financial_link: financial_link
|
||||
})
|
||||
ft.save!
|
||||
end
|
||||
|
@ -117,8 +121,8 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
|
||||
url = financial_link ? finance_link_url(financial_link.id) : finance_ordergroups_url
|
||||
redirect_to url, notice: I18n.t('finance.financial_transactions.controller.create_collection.notice')
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
render action: :new_collection
|
||||
end
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
class Finance::InvoicesController < ApplicationController
|
||||
before_action :authenticate_finance_or_invoices
|
||||
|
||||
before_action :find_invoice, only: [:show, :edit, :update, :destroy]
|
||||
before_action :ensure_can_edit, only: [:edit, :update, :destroy]
|
||||
before_action :find_invoice, only: %i[show edit update destroy]
|
||||
before_action :ensure_can_edit, only: %i[edit update destroy]
|
||||
|
||||
def index
|
||||
@invoices_all = Invoice.includes(:supplier, :deliveries, :orders).order('date DESC')
|
||||
@invoices = @invoices_all.page(params[:page]).per(@per_page)
|
||||
|
||||
respond_to do |format|
|
||||
format.js; format.html { render }
|
||||
format.js
|
||||
format.html { render }
|
||||
format.csv do
|
||||
send_data InvoicesCsv.new(@invoices_all).to_csv, filename: 'invoices.csv', type: 'text/csv'
|
||||
end
|
||||
|
@ -20,11 +21,10 @@ class Finance::InvoicesController < ApplicationController
|
|||
@suppliers = Supplier.includes(:invoices).where('invoices.paid_on IS NULL').references(:invoices)
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def new
|
||||
@invoice = Invoice.new :supplier_id => params[:supplier_id]
|
||||
@invoice = Invoice.new supplier_id: params[:supplier_id]
|
||||
@invoice.deliveries << Delivery.find_by_id(params[:delivery_id]) if params[:delivery_id]
|
||||
@invoice.orders << Order.find_by_id(params[:order_id]) if params[:order_id]
|
||||
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id
|
||||
|
@ -36,12 +36,14 @@ class Finance::InvoicesController < ApplicationController
|
|||
|
||||
def form_on_supplier_id_change
|
||||
fill_deliveries_and_orders_collection params[:invoice_id], params[:supplier_id]
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def fill_deliveries_and_orders_collection(invoice_id, supplier_id)
|
||||
@deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(date: :desc).limit(25)
|
||||
@orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(ends: :desc).limit(25)
|
||||
@deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id,
|
||||
supplier_id).order(date: :desc).limit(25)
|
||||
@orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id,
|
||||
supplier_id).order(ends: :desc).limit(25)
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -58,7 +60,7 @@ class Finance::InvoicesController < ApplicationController
|
|||
end
|
||||
else
|
||||
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id
|
||||
render :action => "new"
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -81,7 +83,7 @@ class Finance::InvoicesController < ApplicationController
|
|||
@invoice = Invoice.find(params[:invoice_id])
|
||||
type = MIME::Types[@invoice.attachment_mime].first
|
||||
filename = "invoice_#{@invoice.id}_attachment.#{type.preferred_extension}"
|
||||
send_data(@invoice.attachment_data, :filename => filename, :type => type)
|
||||
send_data(@invoice.attachment_data, filename: filename, type: type)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -92,8 +94,8 @@ class Finance::InvoicesController < ApplicationController
|
|||
|
||||
# Returns true if @current_user can edit the invoice..
|
||||
def ensure_can_edit
|
||||
unless @invoice.user_can_edit?(current_user)
|
||||
deny_access
|
||||
end
|
||||
return if @invoice.user_can_edit?(current_user)
|
||||
|
||||
deny_access
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
class Finance::OrdergroupsController < Finance::BaseController
|
||||
def index
|
||||
m = /^(?<col>name|sum_of_class_\d+)(?<reverse>_reverse)?$/.match params["sort"]
|
||||
m = /^(?<col>name|sum_of_class_\d+)(?<reverse>_reverse)?$/.match params['sort']
|
||||
if m
|
||||
sort = m[:col]
|
||||
sort += ' DESC' if m[:reverse]
|
||||
else
|
||||
sort = "name"
|
||||
sort = 'name'
|
||||
end
|
||||
|
||||
@ordergroups = Ordergroup.undeleted.order(sort)
|
||||
|
@ -14,7 +14,7 @@ class Finance::OrdergroupsController < Finance::BaseController
|
|||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||
|
||||
@total_balances = FinancialTransactionClass.sorted.each_with_object({}) do |c, tmp|
|
||||
tmp[c.id] = c.financial_transactions.reduce(0) { | sum, t | sum + t.amount }
|
||||
tmp[c.id] = c.financial_transactions.reduce(0) { |sum, t| sum + t.amount }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
class Foodcoop::OrdergroupsController < ApplicationController
|
||||
def index
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"])
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params['sort'])
|
||||
|
||||
unless params[:name].blank? # Search by name
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%")
|
||||
end
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%") if params[:name].present? # Search by name
|
||||
|
||||
if params[:only_active] # Select only active groups
|
||||
@ordergroups = @ordergroups.active
|
||||
end
|
||||
@ordergroups = @ordergroups.active if params[:only_active] # Select only active groups
|
||||
|
||||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.js { render :layout => false }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
class Foodcoop::UsersController < ApplicationController
|
||||
before_action -> { require_config_disabled :disable_members_overview }
|
||||
|
||||
def index
|
||||
@users = User.undeleted.sort_by_param(params["sort"])
|
||||
@users = User.undeleted.sort_by_param(params['sort'])
|
||||
|
||||
# if somebody uses the search field:
|
||||
@users = @users.natural_search(params[:user_name]) unless params[:user_name].blank?
|
||||
@users = @users.natural_search(params[:user_name]) if params[:user_name].present?
|
||||
|
||||
if params[:ordergroup_name]
|
||||
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", "%#{params[:ordergroup_name]}%")
|
||||
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?",
|
||||
"%#{params[:ordergroup_name]}%")
|
||||
end
|
||||
|
||||
@users = @users.page(params[:page]).per(@per_page)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.js { render :layout => false } # index.js.erb
|
||||
format.js { render layout: false } # index.js.erb
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class Foodcoop::WorkgroupsController < ApplicationController
|
||||
before_action :authenticate_membership_or_admin,
|
||||
:except => [:index]
|
||||
except: [:index]
|
||||
|
||||
def index
|
||||
@workgroups = Workgroup.order("name")
|
||||
@workgroups = Workgroup.order('name')
|
||||
end
|
||||
|
||||
def edit
|
||||
|
@ -13,9 +13,9 @@ class Foodcoop::WorkgroupsController < ApplicationController
|
|||
def update
|
||||
@workgroup = Workgroup.find(params[:id])
|
||||
if @workgroup.update(params[:workgroup])
|
||||
redirect_to foodcoop_workgroups_url, :notice => I18n.t('workgroups.update.notice')
|
||||
redirect_to foodcoop_workgroups_url, notice: I18n.t('workgroups.update.notice')
|
||||
else
|
||||
render :action => 'edit'
|
||||
render action: 'edit'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class GroupOrderArticlesController < ApplicationController
|
||||
before_action :authenticate_finance
|
||||
before_action :find_group_order_article, except: [:new, :create]
|
||||
before_action :find_group_order_article, except: %i[new create]
|
||||
|
||||
layout false # We only use this controller to server js snippets, no need for layout rendering
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
class GroupOrdersController < ApplicationController
|
||||
# Security
|
||||
before_action :ensure_ordergroup_member
|
||||
before_action :ensure_open_order, :only => [:new, :create, :edit, :update, :order, :stock_order, :saveOrder]
|
||||
before_action :ensure_my_group_order, only: [:show, :edit, :update]
|
||||
before_action :enough_apples?, only: [:new, :create]
|
||||
before_action :ensure_open_order, only: %i[new create edit update order stock_order saveOrder]
|
||||
before_action :ensure_my_group_order, only: %i[show edit update]
|
||||
before_action :enough_apples?, only: %i[new create]
|
||||
|
||||
# Index page.
|
||||
def index
|
||||
|
@ -13,9 +13,17 @@ class GroupOrdersController < ApplicationController
|
|||
@finished_not_closed_orders_including_group_order = Order.finished_not_closed.ordergroup_group_orders_map(@ordergroup)
|
||||
end
|
||||
|
||||
def show
|
||||
@order = @group_order.order
|
||||
end
|
||||
|
||||
def new
|
||||
ordergroup = params[:stock_order] ? nil : @ordergroup
|
||||
@group_order = @order.group_orders.build(:ordergroup => ordergroup, :updated_by => current_user)
|
||||
@group_order = @order.group_orders.build(ordergroup: ordergroup, updated_by: current_user)
|
||||
@ordering_data = @group_order.load_data
|
||||
end
|
||||
|
||||
def edit
|
||||
@ordering_data = @group_order.load_data
|
||||
end
|
||||
|
||||
|
@ -23,34 +31,26 @@ class GroupOrdersController < ApplicationController
|
|||
@group_order = GroupOrder.new(params[:group_order])
|
||||
begin
|
||||
@group_order.save_ordering!
|
||||
redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.create.notice')
|
||||
redirect_to group_order_url(@group_order), notice: I18n.t('group_orders.create.notice')
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_stale')
|
||||
rescue => exception
|
||||
logger.error('Failed to update order: ' + exception.message)
|
||||
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_general')
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_stale')
|
||||
rescue StandardError => e
|
||||
logger.error('Failed to update order: ' + e.message)
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_general')
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@order = @group_order.order
|
||||
end
|
||||
|
||||
def edit
|
||||
@ordering_data = @group_order.load_data
|
||||
end
|
||||
|
||||
def update
|
||||
@group_order.attributes = params[:group_order]
|
||||
@group_order.updated_by = current_user
|
||||
begin
|
||||
@group_order.save_ordering!
|
||||
redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.update.notice')
|
||||
redirect_to group_order_url(@group_order), notice: I18n.t('group_orders.update.notice')
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_stale')
|
||||
rescue => exception
|
||||
logger.error('Failed to update order: ' + exception.message)
|
||||
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_general')
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_stale')
|
||||
rescue StandardError => e
|
||||
logger.error('Failed to update order: ' + e.message)
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_general')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -74,16 +74,16 @@ class GroupOrdersController < ApplicationController
|
|||
# Used as a :before_action by OrdersController.
|
||||
def ensure_ordergroup_member
|
||||
@ordergroup = @current_user.ordergroup
|
||||
if @ordergroup.nil?
|
||||
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
|
||||
end
|
||||
return unless @ordergroup.nil?
|
||||
|
||||
redirect_to root_url, alert: I18n.t('group_orders.errors.no_member')
|
||||
end
|
||||
|
||||
def ensure_open_order
|
||||
@order = Order.includes([:supplier, :order_articles]).find(order_id_param)
|
||||
@order = Order.includes(%i[supplier order_articles]).find(order_id_param)
|
||||
unless @order.open?
|
||||
flash[:notice] = I18n.t('group_orders.errors.closed')
|
||||
redirect_to :action => 'index'
|
||||
redirect_to action: 'index'
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||
|
@ -91,17 +91,17 @@ class GroupOrdersController < ApplicationController
|
|||
|
||||
def ensure_my_group_order
|
||||
@group_order = GroupOrder.find(params[:id])
|
||||
if @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?)
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||
end
|
||||
return unless @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?)
|
||||
|
||||
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||
end
|
||||
|
||||
def enough_apples?
|
||||
if @ordergroup.not_enough_apples?
|
||||
redirect_to group_orders_url,
|
||||
alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples,
|
||||
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||
end
|
||||
return unless @ordergroup.not_enough_apples?
|
||||
|
||||
redirect_to group_orders_url,
|
||||
alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples,
|
||||
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||
end
|
||||
|
||||
def order_id_param
|
||||
|
|
|
@ -9,8 +9,7 @@ class HomeController < ApplicationController
|
|||
@unassigned_tasks = Task.order(:due_date).next_unassigned_tasks_for(current_user)
|
||||
end
|
||||
|
||||
def profile
|
||||
end
|
||||
def profile; end
|
||||
|
||||
def reference_calculator
|
||||
if current_user.ordergroup
|
||||
|
@ -18,7 +17,7 @@ class HomeController < ApplicationController
|
|||
@bank_accounts = @types.includes(:bank_account).map(&:bank_account).uniq.compact
|
||||
@bank_accounts = [BankAccount.last] if @bank_accounts.empty?
|
||||
else
|
||||
redirect_to root_path, alert: I18n.t('group_orders.errors.no_member')
|
||||
redirect_to root_url, alert: I18n.t('group_orders.errors.no_member')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,7 +25,7 @@ class HomeController < ApplicationController
|
|||
if @current_user.update(user_params)
|
||||
@current_user.ordergroup.update(ordergroup_params) if ordergroup_params
|
||||
session[:locale] = @current_user.locale
|
||||
redirect_to my_profile_path, notice: I18n.t('home.changes_saved')
|
||||
redirect_to my_profile_url, notice: I18n.t('home.changes_saved')
|
||||
else
|
||||
render :profile
|
||||
end
|
||||
|
@ -36,40 +35,43 @@ class HomeController < ApplicationController
|
|||
@user = @current_user
|
||||
@ordergroup = @user.ordergroup
|
||||
|
||||
unless @ordergroup.nil?
|
||||
if @ordergroup.nil?
|
||||
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
|
||||
else
|
||||
|
||||
@ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id)
|
||||
|
||||
if params['sort']
|
||||
sort = case params['sort']
|
||||
when "date" then "created_on"
|
||||
when "note" then "note"
|
||||
when "amount" then "amount"
|
||||
when "date_reverse" then "created_on DESC"
|
||||
when "note_reverse" then "note DESC"
|
||||
when "amount_reverse" then "amount DESC"
|
||||
sort = if params['sort']
|
||||
case params['sort']
|
||||
when 'date' then 'created_on'
|
||||
when 'note' then 'note'
|
||||
when 'amount' then 'amount'
|
||||
when 'date_reverse' then 'created_on DESC'
|
||||
when 'note_reverse' then 'note DESC'
|
||||
when 'amount_reverse' then 'amount DESC'
|
||||
end
|
||||
else
|
||||
sort = "created_on DESC"
|
||||
end
|
||||
else
|
||||
'created_on DESC'
|
||||
end
|
||||
|
||||
@financial_transactions = @ordergroup.financial_transactions.visible.page(params[:page]).per(@per_page).order(sort)
|
||||
@financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?', "%#{params[:query]}%") if params[:query].present?
|
||||
if params[:query].present?
|
||||
@financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?',
|
||||
"%#{params[:query]}%")
|
||||
end
|
||||
|
||||
else
|
||||
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
|
||||
end
|
||||
end
|
||||
|
||||
# cancel personal memberships direct from the myProfile-page
|
||||
def cancel_membership
|
||||
if params[:membership_id]
|
||||
membership = @current_user.memberships.find(params[:membership_id])
|
||||
else
|
||||
membership = @current_user.memberships.find_by_group_id!(params[:group_id])
|
||||
end
|
||||
membership = if params[:membership_id]
|
||||
@current_user.memberships.find(params[:membership_id])
|
||||
else
|
||||
@current_user.memberships.find_by_group_id!(params[:group_id])
|
||||
end
|
||||
membership.destroy
|
||||
redirect_to my_profile_path, notice: I18n.t('home.ordergroup_cancelled', :group => membership.group.name)
|
||||
redirect_to my_profile_path, notice: I18n.t('home.ordergroup_cancelled', group: membership.group.name)
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -82,8 +84,8 @@ class HomeController < ApplicationController
|
|||
end
|
||||
|
||||
def ordergroup_params
|
||||
if params[:user][:ordergroup]
|
||||
params.require(:user).require(:ordergroup).permit(:contact_address)
|
||||
end
|
||||
return unless params[:user][:ordergroup]
|
||||
|
||||
params.require(:user).require(:ordergroup).permit(:contact_address)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ class InvitesController < ApplicationController
|
|||
before_action -> { require_config_disabled :disable_invite }
|
||||
|
||||
def new
|
||||
@invite = Invite.new(:user => @current_user, :group => @group)
|
||||
@invite = Invite.new(user: @current_user, group: @group)
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -27,6 +27,10 @@ class InvitesController < ApplicationController
|
|||
protected
|
||||
|
||||
def authenticate_membership_or_admin_for_invites
|
||||
authenticate_membership_or_admin((params[:invite][:group_id] rescue params[:id]))
|
||||
authenticate_membership_or_admin(begin
|
||||
params[:invite][:group_id]
|
||||
rescue StandardError
|
||||
params[:id]
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class LoginController < ApplicationController
|
||||
skip_before_action :authenticate # no authentication since this is the login page
|
||||
before_action :validate_token, :only => [:new_password, :update_password]
|
||||
before_action :validate_token, only: %i[new_password update_password]
|
||||
|
||||
# Display the form to enter an email address requesting a token to set a new password.
|
||||
def forgot_password
|
||||
|
@ -9,20 +9,17 @@ class LoginController < ApplicationController
|
|||
|
||||
# Sends an email to a user with the token that allows setting a new password through action "password".
|
||||
def reset_password
|
||||
if request.get? || params[:user].nil? # Catch for get request and give better error message.
|
||||
redirect_to forgot_password_url, alert: I18n.t('errors.general_again') and return
|
||||
end
|
||||
redirect_to forgot_password_url, alert: I18n.t('errors.general_again') and return if request.get? || params[:user].nil? # Catch for get request and give better error message.
|
||||
|
||||
if (user = User.undeleted.find_by_email(params[:user][:email]))
|
||||
user.request_password_reset!
|
||||
end
|
||||
redirect_to login_url, :notice => I18n.t('login.controller.reset_password.notice')
|
||||
redirect_to login_url, notice: I18n.t('login.controller.reset_password.notice')
|
||||
end
|
||||
|
||||
# Set a new password with a token from the password reminder email.
|
||||
# Called with params :id => User.id and :token => User.reset_password_token to specify a new password.
|
||||
def new_password
|
||||
end
|
||||
def new_password; end
|
||||
|
||||
# Sets a new password.
|
||||
# Called with params :id => User.id and :token => User.reset_password_token to specify a new password.
|
||||
|
@ -32,7 +29,7 @@ class LoginController < ApplicationController
|
|||
@user.reset_password_token = nil
|
||||
@user.reset_password_expires = nil
|
||||
@user.save
|
||||
redirect_to login_url, :notice => I18n.t('login.controller.update_password.notice')
|
||||
redirect_to login_url, notice: I18n.t('login.controller.update_password.notice')
|
||||
else
|
||||
render :new_password
|
||||
end
|
||||
|
@ -50,14 +47,14 @@ class LoginController < ApplicationController
|
|||
@user = User.new(params[:user])
|
||||
@user.email = @invite.email
|
||||
if @user.save
|
||||
Membership.new(:user => @user, :group => @invite.group).save!
|
||||
Membership.new(user: @user, group: @invite.group).save!
|
||||
@invite.destroy
|
||||
session[:locale] = @user.locale
|
||||
redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice')
|
||||
end
|
||||
end
|
||||
else
|
||||
@user = User.new(:email => @invite.email)
|
||||
@user = User.new(email: @invite.email)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,8 +62,8 @@ class LoginController < ApplicationController
|
|||
|
||||
def validate_token
|
||||
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
||||
if (@user.nil? || @user.reset_password_expires < Time.now)
|
||||
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||
end
|
||||
return unless @user.nil? || @user.reset_password_expires < Time.now
|
||||
|
||||
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class OrderArticlesController < ApplicationController
|
||||
before_action :fetch_order, except: :destroy
|
||||
before_action :authenticate_finance_or_invoices, except: [:new, :create]
|
||||
before_action :authenticate_finance_orders_or_pickup, except: [:edit, :update, :destroy]
|
||||
before_action :authenticate_finance_or_invoices, except: %i[new create]
|
||||
before_action :authenticate_finance_orders_or_pickup, except: %i[edit update destroy]
|
||||
|
||||
layout false # We only use this controller to serve js snippets, no need for layout rendering
|
||||
|
||||
|
@ -9,28 +9,26 @@ class OrderArticlesController < ApplicationController
|
|||
@order_article = @order.order_articles.build(params[:order_article])
|
||||
end
|
||||
|
||||
def edit
|
||||
@order_article = OrderArticle.find(params[:id])
|
||||
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
|
||||
# given mentioning that the article already exists, which is desired.
|
||||
@order_article = @order.order_articles.where(:article_id => params[:order_article][:article_id]).first
|
||||
unless @order_article && @order_article.units_to_order == 0
|
||||
@order_article = @order.order_articles.build(params[:order_article])
|
||||
end
|
||||
@order_article = @order.order_articles.where(article_id: params[:order_article][:article_id]).first
|
||||
@order_article = @order.order_articles.build(params[:order_article]) unless @order_article && @order_article.units_to_order == 0
|
||||
@order_article.save!
|
||||
rescue
|
||||
rescue StandardError
|
||||
render action: :new
|
||||
end
|
||||
|
||||
def edit
|
||||
@order_article = OrderArticle.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@order_article = OrderArticle.find(params[:id])
|
||||
begin
|
||||
@order_article.update_article_and_price!(params[:order_article], params[:article], params[:article_price])
|
||||
rescue
|
||||
rescue StandardError
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
class OrderCommentsController < ApplicationController
|
||||
def new
|
||||
@order = Order.find(params[:order_id])
|
||||
@order_comment = @order.comments.build(:user => current_user)
|
||||
@order_comment = @order.comments.build(user: current_user)
|
||||
end
|
||||
|
||||
def create
|
||||
@order_comment = OrderComment.new(params[:order_comment])
|
||||
if @order_comment.save
|
||||
render :layout => false
|
||||
render layout: false
|
||||
else
|
||||
render :action => :new, :layout => false
|
||||
render action: :new, layout: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,25 +5,26 @@ class OrdersController < ApplicationController
|
|||
include Concerns::SendOrderPdf
|
||||
|
||||
before_action :authenticate_pickups_or_orders
|
||||
before_action :authenticate_orders, except: [:receive, :receive_on_order_article_create, :receive_on_order_article_update, :show]
|
||||
before_action :remove_empty_article, only: [:create, :update]
|
||||
before_action :authenticate_orders,
|
||||
except: %i[receive receive_on_order_article_create receive_on_order_article_update show]
|
||||
before_action :remove_empty_article, only: %i[create update]
|
||||
|
||||
# List orders
|
||||
def index
|
||||
@open_orders = Order.open.includes(:supplier)
|
||||
@finished_orders = Order.finished_not_closed.includes(:supplier)
|
||||
@per_page = 15
|
||||
if params['sort']
|
||||
sort = case params['sort']
|
||||
when "supplier" then "suppliers.name, ends DESC"
|
||||
when "pickup" then "pickup DESC"
|
||||
when "ends" then "ends DESC"
|
||||
when "supplier_reverse" then "suppliers.name DESC"
|
||||
when "ends_reverse" then "ends"
|
||||
sort = if params['sort']
|
||||
case params['sort']
|
||||
when 'supplier' then 'suppliers.name, ends DESC'
|
||||
when 'pickup' then 'pickup DESC'
|
||||
when 'ends' then 'ends DESC'
|
||||
when 'supplier_reverse' then 'suppliers.name DESC'
|
||||
when 'ends_reverse' then 'ends'
|
||||
end
|
||||
else
|
||||
sort = "ends DESC"
|
||||
end
|
||||
else
|
||||
'ends DESC'
|
||||
end
|
||||
@suppliers = Supplier.having_articles.order('suppliers.name')
|
||||
@orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -43,13 +44,13 @@ class OrdersController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.html
|
||||
format.js do
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
format.pdf do
|
||||
send_order_pdf @order, params[:document]
|
||||
end
|
||||
format.csv do
|
||||
send_data OrderCsv.new(@order, options= {custom_csv: params[:custom_csv]}).to_csv, filename: @order.name + '.csv', type: 'text/csv'
|
||||
send_data OrderCsv.new(@order).to_csv, filename: @order.name + '.csv', type: 'text/csv'
|
||||
end
|
||||
format.text do
|
||||
send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain'
|
||||
|
@ -57,19 +58,6 @@ class OrdersController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def custom_csv
|
||||
@order = Order.find(params[:id])
|
||||
@view = (params[:view] || 'default').gsub(/[^-_a-zA-Z0-9]/, '')
|
||||
@partial = case @view
|
||||
when 'default' then 'articles'
|
||||
when 'groups' then 'shared/articles_by/groups'
|
||||
when 'articles' then 'shared/articles_by/articles'
|
||||
else 'articles'
|
||||
end
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# Page to create a new order.
|
||||
def new
|
||||
if params[:order_id]
|
||||
|
@ -79,8 +67,14 @@ class OrdersController < ApplicationController
|
|||
else
|
||||
@order = Order.new(supplier_id: params[:supplier_id]).init_dates
|
||||
end
|
||||
rescue => error
|
||||
redirect_to orders_url, alert: t('errors.general_msg', msg: error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to orders_url, alert: t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
# Page to edit an exsiting order.
|
||||
# editing finished orders is done in FinanceController
|
||||
def edit
|
||||
@order = Order.includes(:articles).find(params[:id])
|
||||
end
|
||||
|
||||
# Save a new order.
|
||||
|
@ -94,31 +88,25 @@ class OrdersController < ApplicationController
|
|||
redirect_to @order
|
||||
else
|
||||
logger.debug "[debug] order errors: #{@order.errors.messages}"
|
||||
render :action => 'new'
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
# Page to edit an exsiting order.
|
||||
# editing finished orders is done in FinanceController
|
||||
def edit
|
||||
@order = Order.includes(:articles).find(params[:id])
|
||||
end
|
||||
|
||||
# Update an existing order.
|
||||
def update
|
||||
@order = Order.find params[:id]
|
||||
if @order.update(params[:order].merge(updated_by: current_user))
|
||||
flash[:notice] = I18n.t('orders.update.notice')
|
||||
redirect_to :action => 'show', :id => @order
|
||||
redirect_to action: 'show', id: @order
|
||||
else
|
||||
render :action => 'edit'
|
||||
render action: 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
# Delete an order.
|
||||
def destroy
|
||||
Order.find(params[:id]).destroy
|
||||
redirect_to :action => 'index'
|
||||
redirect_to action: 'index'
|
||||
end
|
||||
|
||||
# Finish a current order.
|
||||
|
@ -126,8 +114,8 @@ class OrdersController < ApplicationController
|
|||
order = Order.find(params[:id])
|
||||
order.finish!(@current_user)
|
||||
redirect_to order, notice: I18n.t('orders.finish.notice')
|
||||
rescue => error
|
||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
# Send a order to the supplier.
|
||||
|
@ -135,20 +123,18 @@ class OrdersController < ApplicationController
|
|||
order = Order.find(params[:id])
|
||||
order.send_to_supplier!(@current_user)
|
||||
redirect_to order, notice: I18n.t('orders.send_to_supplier.notice')
|
||||
rescue => error
|
||||
redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||
rescue StandardError => e
|
||||
redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
end
|
||||
|
||||
def receive
|
||||
@order = Order.find(params[:id])
|
||||
unless request.post?
|
||||
@order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name')
|
||||
else
|
||||
if request.post?
|
||||
Order.transaction do
|
||||
s = update_order_amounts
|
||||
@order.update_attribute(:state, 'received') if @order.state != 'received'
|
||||
|
||||
flash[:notice] = (s ? I18n.t('orders.receive.notice', :msg => s) : I18n.t('orders.receive.notice_none'))
|
||||
flash[:notice] = (s ? I18n.t('orders.receive.notice', msg: s) : I18n.t('orders.receive.notice_none'))
|
||||
end
|
||||
NotifyReceivedOrderJob.perform_later(@order)
|
||||
if current_user.role_orders? || current_user.role_finance?
|
||||
|
@ -158,23 +144,25 @@ class OrdersController < ApplicationController
|
|||
else
|
||||
redirect_to receive_order_path(@order)
|
||||
end
|
||||
else
|
||||
@order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name')
|
||||
end
|
||||
end
|
||||
|
||||
def receive_on_order_article_create # See publish/subscribe design pattern in /doc.
|
||||
@order_article = OrderArticle.find(params[:order_article_id])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def receive_on_order_article_update # See publish/subscribe design pattern in /doc.
|
||||
@order_article = OrderArticle.find(params[:order_article_id])
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def update_order_amounts
|
||||
return if not params[:order_articles]
|
||||
return unless params[:order_articles]
|
||||
|
||||
# where to leave remainder during redistribution
|
||||
rest_to = []
|
||||
|
@ -189,35 +177,42 @@ class OrdersController < ApplicationController
|
|||
# "MySQL lock timeout exceeded" errors. It's ok to do
|
||||
# this article-by-article anway.
|
||||
params[:order_articles].each do |oa_id, oa_params|
|
||||
unless oa_params.blank?
|
||||
oa = OrderArticle.find(oa_id)
|
||||
# update attributes; don't use update_attribute because it calls save
|
||||
# which makes received_changed? not work anymore
|
||||
oa.attributes = oa_params
|
||||
if oa.units_received_changed?
|
||||
counts[0] += 1
|
||||
unless oa.units_received.blank?
|
||||
cunits[0] += oa.units_received * oa.article.unit_quantity
|
||||
oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to
|
||||
oacounts.each_with_index { |c, i| cunits[i + 1] += c; counts[i + 1] += 1 if c > 0 }
|
||||
next if oa_params.blank?
|
||||
|
||||
oa = OrderArticle.find(oa_id)
|
||||
# update attributes; don't use update_attribute because it calls save
|
||||
# which makes received_changed? not work anymore
|
||||
oa.attributes = oa_params
|
||||
if oa.units_received_changed?
|
||||
counts[0] += 1
|
||||
if oa.units_received.present?
|
||||
cunits[0] += oa.units_received * oa.article.unit_quantity
|
||||
oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to
|
||||
oacounts.each_with_index do |c, i|
|
||||
cunits[i + 1] += c
|
||||
counts[i + 1] += 1 if c > 0
|
||||
end
|
||||
end
|
||||
oa.save!
|
||||
end
|
||||
oa.save!
|
||||
end
|
||||
return nil if counts[0] == 0
|
||||
|
||||
notice = []
|
||||
notice << I18n.t('orders.update_order_amounts.msg1', count: counts[0], units: cunits[0])
|
||||
notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1], units: cunits[1]) if params[:rest_to_tolerance]
|
||||
if params[:rest_to_tolerance]
|
||||
notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1],
|
||||
units: cunits[1])
|
||||
end
|
||||
notice << I18n.t('orders.update_order_amounts.msg3', count: counts[2], units: cunits[2]) if params[:rest_to_stock]
|
||||
if counts[3] > 0 || cunits[3] > 0
|
||||
notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3], units: cunits[3])
|
||||
notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3],
|
||||
units: cunits[3])
|
||||
end
|
||||
notice.join(', ')
|
||||
end
|
||||
|
||||
def remove_empty_article
|
||||
params[:order][:article_ids].reject!(&:blank?) if params[:order] && params[:order][:article_ids]
|
||||
params[:order][:article_ids].compact_blank! if params[:order] && params[:order][:article_ids]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,16 +12,20 @@ class SessionsController < ApplicationController
|
|||
user = User.authenticate(params[:nick], params[:password])
|
||||
if user
|
||||
user.update_attribute(:last_login, Time.now)
|
||||
login_and_redirect_to_return_to user, :notice => I18n.t('sessions.logged_in')
|
||||
login_and_redirect_to_return_to user, notice: I18n.t('sessions.logged_in')
|
||||
else
|
||||
flash.now.alert = I18n.t(FoodsoftConfig[:use_nick] ? 'sessions.login_invalid_nick' : 'sessions.login_invalid_email')
|
||||
render "new"
|
||||
render 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
logout
|
||||
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
||||
if FoodsoftConfig[:logout_redirect_url].present?
|
||||
redirect_to FoodsoftConfig[:logout_redirect_url]
|
||||
else
|
||||
redirect_to login_url, notice: I18n.t('sessions.logged_out')
|
||||
end
|
||||
end
|
||||
|
||||
# redirect to root, going to default foodcoop when none given
|
||||
|
|
|
@ -7,21 +7,21 @@ class StockTakingsController < ApplicationController
|
|||
|
||||
def new
|
||||
@stock_taking = StockTaking.new
|
||||
StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(:stock_article => a) }
|
||||
StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(stock_article: a) }
|
||||
end
|
||||
|
||||
def new_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||
stock_article = StockArticle.find(params[:stock_article_id])
|
||||
@stock_change = StockChange.new(:stock_article => stock_article)
|
||||
@stock_change = StockChange.new(stock_article: stock_article)
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
create!(:notice => I18n.t('stock_takings.create.notice'))
|
||||
create!(notice: I18n.t('stock_takings.create.notice'))
|
||||
end
|
||||
|
||||
def update
|
||||
update!(:notice => I18n.t('stock_takings.update.notice'))
|
||||
update!(notice: I18n.t('stock_takings.update.notice'))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,57 +7,13 @@ class StockitController < ApplicationController
|
|||
def index_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def index_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# three possibilites to fill a new_stock_article form
|
||||
# (1) start from blank or use params
|
||||
def new
|
||||
@stock_article = StockArticle.new(params[:stock_article])
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# (2) StockArticle as template
|
||||
def copy
|
||||
@stock_article = StockArticle.find(params[:stock_article_id]).dup
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# (3) non-stock Article as template
|
||||
def derive
|
||||
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def create
|
||||
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
|
||||
@stock_article.save!
|
||||
render :layout => false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render :action => 'new', :layout => false
|
||||
end
|
||||
|
||||
def edit
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def update
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
@stock_article.update!(params[:stock_article])
|
||||
render :layout => false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render :action => 'edit', :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -65,24 +21,68 @@ class StockitController < ApplicationController
|
|||
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
|
||||
end
|
||||
|
||||
# three possibilites to fill a new_stock_article form
|
||||
# (1) start from blank or use params
|
||||
def new
|
||||
@stock_article = StockArticle.new(params[:stock_article])
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
# (2) StockArticle as template
|
||||
def copy
|
||||
@stock_article = StockArticle.find(params[:stock_article_id]).dup
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
# (3) non-stock Article as template
|
||||
def derive
|
||||
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def create
|
||||
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
|
||||
@stock_article.save!
|
||||
render layout: false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render action: 'new', layout: false
|
||||
end
|
||||
|
||||
def update
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
@stock_article.update!(params[:stock_article])
|
||||
render layout: false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render action: 'edit', layout: false
|
||||
end
|
||||
|
||||
def show_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
|
||||
render :layout => false
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def destroy
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
@stock_article.mark_as_deleted
|
||||
render :layout => false
|
||||
rescue => error
|
||||
render :partial => "destroy_fail", :layout => false,
|
||||
:locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.message) }
|
||||
render layout: false
|
||||
rescue StandardError => e
|
||||
render partial: 'destroy_fail', layout: false,
|
||||
locals: { fail_msg: I18n.t('errors.general_msg', msg: e.message) }
|
||||
end
|
||||
|
||||
# TODO: Fix this!!
|
||||
def articles_search
|
||||
@articles = Article.not_in_stock.limit(8).where('name LIKE ?', "%#{params[:term]}%")
|
||||
render :json => @articles.map(&:name)
|
||||
render json: @articles.map(&:name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class StylesController < ApplicationController
|
|||
def foodcoop
|
||||
css = FoodsoftConfig[:custom_css]
|
||||
if css.blank?
|
||||
render body: nil, content_type: 'text/css', status: 404
|
||||
render body: nil, content_type: 'text/css', status: :not_found
|
||||
else
|
||||
expires_in 1.week, public: true if params[:md5].present?
|
||||
render body: css, content_type: 'text/css'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class SuppliersController < ApplicationController
|
||||
before_action :authenticate_suppliers, :except => [:index, :list]
|
||||
before_action :authenticate_suppliers, except: %i[index list]
|
||||
helper :deliveries
|
||||
|
||||
def index
|
||||
|
@ -24,6 +24,10 @@ class SuppliersController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@supplier = Supplier.find(params[:id])
|
||||
end
|
||||
|
||||
def create
|
||||
@supplier = Supplier.new(supplier_params)
|
||||
@supplier.supplier_category ||= SupplierCategory.first
|
||||
|
@ -31,21 +35,17 @@ class SuppliersController < ApplicationController
|
|||
flash[:notice] = I18n.t('suppliers.create.notice')
|
||||
redirect_to suppliers_path
|
||||
else
|
||||
render :action => 'new'
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@supplier = Supplier.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@supplier = Supplier.find(params[:id])
|
||||
if @supplier.update(supplier_params)
|
||||
flash[:notice] = I18n.t('suppliers.update.notice')
|
||||
redirect_to @supplier
|
||||
else
|
||||
render :action => 'edit'
|
||||
render action: 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -54,8 +54,8 @@ class SuppliersController < ApplicationController
|
|||
@supplier.mark_as_deleted
|
||||
flash[:notice] = I18n.t('suppliers.destroy.notice')
|
||||
redirect_to suppliers_path
|
||||
rescue => e
|
||||
flash[:error] = I18n.t('errors.general_msg', :msg => e.message)
|
||||
rescue StandardError => e
|
||||
flash[:error] = I18n.t('errors.general_msg', msg: e.message)
|
||||
redirect_to @supplier
|
||||
end
|
||||
|
||||
|
|
|
@ -11,35 +11,33 @@ class TasksController < ApplicationController
|
|||
@accepted_tasks = Task.accepted_tasks_for(current_user)
|
||||
end
|
||||
|
||||
def new
|
||||
@task = Task.new(current_user_id: current_user.id)
|
||||
end
|
||||
|
||||
def create
|
||||
@task = Task.new(current_user_id: current_user.id)
|
||||
@task.created_by = current_user
|
||||
@task.attributes = (task_params)
|
||||
if params[:periodic]
|
||||
@task.periodic_task_group = PeriodicTaskGroup.new
|
||||
end
|
||||
if @task.save
|
||||
@task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic]
|
||||
redirect_to tasks_url, :notice => I18n.t('tasks.create.notice')
|
||||
else
|
||||
render :template => "tasks/new"
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@task = Task.find(params[:id])
|
||||
end
|
||||
|
||||
def new
|
||||
@task = Task.new(current_user_id: current_user.id)
|
||||
end
|
||||
|
||||
def edit
|
||||
@task = Task.find(params[:id])
|
||||
@periodic = !!params[:periodic]
|
||||
@task.current_user_id = current_user.id
|
||||
end
|
||||
|
||||
def create
|
||||
@task = Task.new(current_user_id: current_user.id)
|
||||
@task.created_by = current_user
|
||||
@task.attributes = (task_params)
|
||||
@task.periodic_task_group = PeriodicTaskGroup.new if params[:periodic]
|
||||
if @task.save
|
||||
@task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic]
|
||||
redirect_to tasks_url, notice: I18n.t('tasks.create.notice')
|
||||
else
|
||||
render template: 'tasks/new'
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@task = Task.find(params[:id])
|
||||
task_group = @task.periodic_task_group
|
||||
|
@ -50,16 +48,14 @@ class TasksController < ApplicationController
|
|||
if @task.errors.empty? && @task.save
|
||||
task_group.update_tasks_including(@task, prev_due_date) if params[:periodic]
|
||||
flash[:notice] = I18n.t('tasks.update.notice')
|
||||
if was_periodic && !@task.periodic?
|
||||
flash[:notice] = I18n.t('tasks.update.notice_converted')
|
||||
end
|
||||
flash[:notice] = I18n.t('tasks.update.notice_converted') if was_periodic && !@task.periodic?
|
||||
if @task.workgroup
|
||||
redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id)
|
||||
else
|
||||
redirect_to tasks_url
|
||||
end
|
||||
else
|
||||
render :template => "tasks/edit"
|
||||
render template: 'tasks/edit'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,7 +71,7 @@ class TasksController < ApplicationController
|
|||
end
|
||||
task.update_ordergroup_stats(user_ids)
|
||||
|
||||
redirect_to tasks_url, :notice => I18n.t('tasks.destroy.notice')
|
||||
redirect_to tasks_url, notice: I18n.t('tasks.destroy.notice')
|
||||
end
|
||||
|
||||
# assign current_user to the task and set the assignment to "accepted"
|
||||
|
@ -85,20 +81,20 @@ class TasksController < ApplicationController
|
|||
if ass = task.is_assigned?(current_user)
|
||||
ass.update_attribute(:accepted, true)
|
||||
else
|
||||
task.assignments.create(:user => current_user, :accepted => true)
|
||||
task.assignments.create(user: current_user, accepted: true)
|
||||
end
|
||||
redirect_to user_tasks_path, :notice => I18n.t('tasks.accept.notice')
|
||||
redirect_to user_tasks_path, notice: I18n.t('tasks.accept.notice')
|
||||
end
|
||||
|
||||
# deletes assignment between current_user and given taskcurrent_user_id: current_user.id
|
||||
def reject
|
||||
Task.find(params[:id]).users.delete(current_user)
|
||||
redirect_to :action => "index"
|
||||
redirect_to action: 'index'
|
||||
end
|
||||
|
||||
def set_done
|
||||
Task.find(params[:id]).update_attribute :done, true
|
||||
redirect_to tasks_url, :notice => I18n.t('tasks.set_done.notice')
|
||||
redirect_to tasks_url, notice: I18n.t('tasks.set_done.notice')
|
||||
end
|
||||
|
||||
# Shows all tasks, which are already done
|
||||
|
@ -109,9 +105,9 @@ class TasksController < ApplicationController
|
|||
# shows workgroup (normal group) to edit weekly_tasks_template
|
||||
def workgroup
|
||||
@group = Group.find(params[:workgroup_id])
|
||||
if @group.is_a? Ordergroup
|
||||
redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found')
|
||||
end
|
||||
return unless @group.is_a? Ordergroup
|
||||
|
||||
redirect_to tasks_url, alert: I18n.t('tasks.error_not_found')
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,7 +3,7 @@ class UsersController < ApplicationController
|
|||
def index
|
||||
@users = User.undeleted.natural_search(params[:q])
|
||||
respond_to do |format|
|
||||
format.json { render :json => @users.map(&:token_attributes).to_json }
|
||||
format.json { render json: @users.map(&:token_attributes).to_json }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
class OrderByArticles < OrderPdf
|
||||
def filename
|
||||
I18n.t('documents.order_by_articles.filename', :name => order.name, :date => order.ends.to_date) + '.pdf'
|
||||
I18n.t('documents.order_by_articles.filename', name: order.name, date: order.ends.to_date) + '.pdf'
|
||||
end
|
||||
|
||||
def title
|
||||
I18n.t('documents.order_by_articles.title', :name => order.name,
|
||||
:date => order.ends.strftime(I18n.t('date.formats.default')))
|
||||
I18n.t('documents.order_by_articles.title', name: order.name,
|
||||
date: order.ends.strftime(I18n.t('date.formats.default')))
|
||||
end
|
||||
|
||||
def body
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
class OrderByGroups < OrderPdf
|
||||
def filename
|
||||
I18n.t('documents.order_by_groups.filename', :name => order.name, :date => order.ends.to_date) + '.pdf'
|
||||
I18n.t('documents.order_by_groups.filename', name: order.name, date: order.ends.to_date) + '.pdf'
|
||||
end
|
||||
|
||||
def title
|
||||
I18n.t('documents.order_by_groups.title', :name => order.name,
|
||||
:date => order.ends.strftime(I18n.t('date.formats.default')))
|
||||
I18n.t('documents.order_by_groups.title', name: order.name,
|
||||
date: order.ends.strftime(I18n.t('date.formats.default')))
|
||||
end
|
||||
|
||||
def body
|
||||
|
|
|
@ -2,7 +2,7 @@ class OrderFax < OrderPdf
|
|||
BATCH_SIZE = 250
|
||||
|
||||
def filename
|
||||
I18n.t('documents.order_fax.filename', :name => order.name, :date => order.ends.to_date) + '.pdf'
|
||||
I18n.t('documents.order_fax.filename', name: order.name, date: order.ends.to_date) + '.pdf'
|
||||
end
|
||||
|
||||
def title
|
||||
|
@ -20,16 +20,18 @@ class OrderFax < OrderPdf
|
|||
move_down 5
|
||||
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
unless order.supplier.try(:customer_number).blank?
|
||||
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", size: fontsize(9), align: :right
|
||||
if order.supplier.try(:customer_number).present?
|
||||
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}",
|
||||
size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
end
|
||||
unless contact[:phone].blank?
|
||||
if contact[:phone].present?
|
||||
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
end
|
||||
unless contact[:email].blank?
|
||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :right
|
||||
if contact[:email].present?
|
||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9),
|
||||
align: :right
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,7 +40,7 @@ class OrderFax < OrderPdf
|
|||
text order.name
|
||||
move_down 5
|
||||
text order.supplier.try(:address).to_s
|
||||
unless order.supplier.try(:fax).blank?
|
||||
if order.supplier.try(:fax).present?
|
||||
move_down 5
|
||||
text "#{Supplier.human_attribute_name :fax}: #{order.supplier[:fax]}"
|
||||
end
|
||||
|
@ -50,7 +52,7 @@ class OrderFax < OrderPdf
|
|||
move_down 10
|
||||
text "#{Delivery.human_attribute_name :date}:"
|
||||
move_down 10
|
||||
unless order.supplier.try(:contact_person).blank?
|
||||
if order.supplier.try(:contact_person).present?
|
||||
text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
|
||||
move_down 10
|
||||
end
|
||||
|
@ -78,8 +80,8 @@ class OrderFax < OrderPdf
|
|||
table.row(0).border_bottom_width = 2
|
||||
table.columns(1).align = :right
|
||||
table.columns(3..6).align = :right
|
||||
table.row(data.length - 1).columns(0..5).borders = [:top, :bottom]
|
||||
table.row(data.length - 1).columns(0).borders = [:top, :bottom, :left]
|
||||
table.row(data.length - 1).columns(0..5).borders = %i[top bottom]
|
||||
table.row(data.length - 1).columns(0).borders = %i[top bottom left]
|
||||
table.row(data.length - 1).border_top_width = 2
|
||||
end
|
||||
# font_size: fontsize(8),
|
||||
|
@ -98,7 +100,7 @@ class OrderFax < OrderPdf
|
|||
.preload(:article, :article_price)
|
||||
end
|
||||
|
||||
def each_order_article
|
||||
order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa }
|
||||
def each_order_article(&block)
|
||||
order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,12 +3,12 @@ class OrderMatrix < OrderPdf
|
|||
PLACEHOLDER_CHAR = 'X'
|
||||
|
||||
def filename
|
||||
I18n.t('documents.order_matrix.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf'
|
||||
I18n.t('documents.order_matrix.filename', name: @order.name, date: @order.ends.to_date) + '.pdf'
|
||||
end
|
||||
|
||||
def title
|
||||
I18n.t('documents.order_matrix.title', :name => @order.name,
|
||||
:date => @order.ends.strftime(I18n.t('date.formats.default')))
|
||||
I18n.t('documents.order_matrix.title', name: @order.name,
|
||||
date: @order.ends.strftime(I18n.t('date.formats.default')))
|
||||
end
|
||||
|
||||
def body
|
||||
|
@ -87,7 +87,7 @@ class OrderMatrix < OrderPdf
|
|||
table.cells.border_width = 0.5
|
||||
table.cells.border_color = '666666'
|
||||
|
||||
table.row(0).borders = [:bottom, :left]
|
||||
table.row(0).borders = %i[bottom left]
|
||||
table.row(0).padding = [2, 0, 2, 0]
|
||||
table.row(1..-1).height = row_height_1
|
||||
table.column(0..1).borders = []
|
||||
|
@ -106,7 +106,7 @@ class OrderMatrix < OrderPdf
|
|||
table.column(2 + idx).border_width = 2
|
||||
end
|
||||
|
||||
table.row_colors = ['dddddd', 'ffffff']
|
||||
table.row_colors = %w[dddddd ffffff]
|
||||
end
|
||||
|
||||
first_page = false
|
||||
|
|
|
@ -28,7 +28,11 @@ module Admin::ConfigsHelper
|
|||
options[:default] = options[:input_html].delete(:value)
|
||||
return form.input key, options, &block
|
||||
end
|
||||
block ||= proc { config_input_field form, key, options.merge(options[:input_html]) } if options[:as] == :select_recurring
|
||||
if options[:as] == :select_recurring
|
||||
block ||= proc {
|
||||
config_input_field form, key, options.merge(options[:input_html])
|
||||
}
|
||||
end
|
||||
form.input key, options, &block
|
||||
end
|
||||
|
||||
|
@ -57,11 +61,12 @@ module Admin::ConfigsHelper
|
|||
unchecked_value = options.delete(:unchecked_value) || 'false'
|
||||
options[:checked] = 'checked' if v = options.delete(:value) && v != 'false'
|
||||
# different key for hidden field so that allow clocking on label focuses the control
|
||||
form.hidden_field(key, id: "#{key}_", value: unchecked_value, as: :hidden) + form.check_box(key, options, checked_value, false)
|
||||
form.hidden_field(key, id: "#{key}_", value: unchecked_value,
|
||||
as: :hidden) + form.check_box(key, options, checked_value, false)
|
||||
elsif options[:as] == :select_recurring
|
||||
options[:value] = FoodsoftDateUtil.rule_from(options[:value])
|
||||
options[:rules] ||= []
|
||||
options[:rules].unshift options[:value] unless options[:value].blank?
|
||||
options[:rules].unshift options[:value] if options[:value].present?
|
||||
options[:rules].push [I18n.t('recurring_select.not_recurring'), '{}'] if options.delete(:allow_blank) # blank after current value
|
||||
form.select_recurring key, options.delete(:rules).uniq, options
|
||||
else
|
||||
|
@ -73,7 +78,7 @@ module Admin::ConfigsHelper
|
|||
# @param form [ActionView::Helpers::FormBuilder] Form object.
|
||||
# @param key [Symbol, String] Configuration key of a boolean (e.g. +use_messages+).
|
||||
# @option options [String] :label Label to show
|
||||
def config_use_heading(form, key, options = {})
|
||||
def config_use_heading(form, key, options = {}, &block)
|
||||
head = content_tag :label do
|
||||
lbl = options[:label] || config_input_label(form, key)
|
||||
field = config_input_field(form, key, as: :boolean, boolean_style: :inline,
|
||||
|
@ -83,9 +88,7 @@ module Admin::ConfigsHelper
|
|||
content_tag :span, (lbl + field).html_safe, config_input_tooltip_options(form, key, {})
|
||||
end
|
||||
end
|
||||
fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}") do
|
||||
yield
|
||||
end
|
||||
fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}", &block)
|
||||
head + fields
|
||||
end
|
||||
|
||||
|
@ -127,7 +130,7 @@ module Admin::ConfigsHelper
|
|||
# tooltip with help info to the right
|
||||
cfg_path = form.lookup_model_names[1..-1] + [key]
|
||||
tooltip = I18n.t("config.hints.#{cfg_path.map(&:to_s).join('.')}", default: '')
|
||||
unless tooltip.blank?
|
||||
if tooltip.present?
|
||||
options[:data] ||= {}
|
||||
options[:data][:toggle] ||= 'tooltip'
|
||||
options[:data][:placement] ||= 'right'
|
||||
|
|
|
@ -2,9 +2,7 @@ module Admin::OrdergroupsHelper
|
|||
def ordergroup_members_title(ordergroup)
|
||||
s = ''
|
||||
s += ordergroup.users.map(&:name).join(', ') if ordergroup.users.any?
|
||||
if ordergroup.contact_person.present?
|
||||
s += "\n" + Ordergroup.human_attribute_name(:contact) + ": " + ordergroup.contact_person
|
||||
end
|
||||
s += "\n" + Ordergroup.human_attribute_name(:contact) + ': ' + ordergroup.contact_person if ordergroup.contact_person.present?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ module ApplicationHelper
|
|||
include PathHelper
|
||||
|
||||
def format_time(time = Time.now)
|
||||
I18n.l(time, :format => "%d.%m.%Y %H:%M") unless time.nil?
|
||||
I18n.l(time, format: :foodsoft_datetime) unless time.nil?
|
||||
end
|
||||
|
||||
def format_date(time = Time.now)
|
||||
|
@ -16,7 +16,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def format_datetime_timespec(time, format)
|
||||
I18n.l(time, :format => format) unless (time.nil? || format.nil?)
|
||||
I18n.l(time, format: format) unless time.nil? || format.nil?
|
||||
end
|
||||
|
||||
def format_currency(amount)
|
||||
|
@ -26,28 +26,28 @@ module ApplicationHelper
|
|||
|
||||
# Splits an IBAN into groups of 4 digits displayed with margins in between
|
||||
def format_iban(iban)
|
||||
iban && iban.scan(/..?.?.?/).map { |item| content_tag(:span, item, style: "margin-right: 0.5em;") }.join.html_safe
|
||||
iban && iban.scan(/..?.?.?/).map { |item| content_tag(:span, item, style: 'margin-right: 0.5em;') }.join.html_safe
|
||||
end
|
||||
|
||||
# Creates ajax-controlled-links for pagination
|
||||
def pagination_links_remote(collection, options = {})
|
||||
per_page = options[:per_page] || @per_page
|
||||
params = options[:params] || {}
|
||||
params = params.merge({ :per_page => per_page })
|
||||
paginate collection, :params => params, :remote => true
|
||||
params = params.merge({ per_page: per_page })
|
||||
paginate collection, params: params, remote: true
|
||||
end
|
||||
|
||||
# Link-collection for per_page-options when using the pagination-plugin
|
||||
def items_per_page(options = {})
|
||||
per_page_options = options[:per_page_options] || [20, 50, 100, 500]
|
||||
current = options[:current] || @per_page
|
||||
params = params || {}
|
||||
params ||= {}
|
||||
|
||||
links = per_page_options.map do |per_page|
|
||||
params.merge!({ :per_page => per_page })
|
||||
params.merge!({ per_page: per_page })
|
||||
link_class = 'btn'
|
||||
link_class << ' disabled' if per_page == current
|
||||
link_to(per_page, params, :remote => true, class: link_class)
|
||||
link_to(per_page, params, remote: true, class: link_class)
|
||||
end
|
||||
|
||||
if options[:wrap] == false
|
||||
|
@ -63,21 +63,19 @@ module ApplicationHelper
|
|||
# Hmtl options
|
||||
remote = options[:remote].nil? ? true : options[:remote]
|
||||
class_name = case params[:sort]
|
||||
when key then
|
||||
when key
|
||||
'sortup'
|
||||
when key + '_reverse' then
|
||||
when key + '_reverse'
|
||||
'sortdown'
|
||||
else
|
||||
nil
|
||||
end
|
||||
html_options = {
|
||||
:title => I18n.t('helpers.application.sort_by', text: text),
|
||||
:remote => remote,
|
||||
:class => class_name
|
||||
title: I18n.t('helpers.application.sort_by', text: text),
|
||||
remote: remote,
|
||||
class: class_name
|
||||
}
|
||||
|
||||
# Url options
|
||||
key += "_reverse" if params[:sort] == key
|
||||
key += '_reverse' if params[:sort] == key
|
||||
per_page = options[:per_page] || @per_page
|
||||
url_options = params.merge(per_page: per_page, sort: key)
|
||||
url_options.merge!({ page: params[:page] }) if params[:page]
|
||||
|
@ -95,14 +93,16 @@ module ApplicationHelper
|
|||
# be overridden by the option 'desc'.
|
||||
# Other options are passed through to I18n.
|
||||
def heading_helper(model, attribute, options = {})
|
||||
i18nopts = { count: 2 }.merge(options.select { |a| !['short', 'desc'].include?(a) })
|
||||
i18nopts = { count: 2 }.merge(options.select { |a| !%w[short desc].include?(a) })
|
||||
s = model.human_attribute_name(attribute, i18nopts)
|
||||
if options[:short]
|
||||
desc = options[:desc]
|
||||
desc ||= model.human_attribute_name("#{attribute}_desc".to_sym, options.merge({ fallback: true, default: '', count: 2 }))
|
||||
desc ||= model.human_attribute_name("#{attribute}_desc".to_sym,
|
||||
options.merge({ fallback: true, default: '', count: 2 }))
|
||||
desc.blank? && desc = s
|
||||
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({ fallback: true, default: '', count: 2 }))
|
||||
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" unless sshort.blank?
|
||||
sshort = model.human_attribute_name("#{attribute}_short".to_sym,
|
||||
options.merge({ fallback: true, default: '', count: 2 }))
|
||||
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" if sshort.present?
|
||||
end
|
||||
s
|
||||
end
|
||||
|
@ -117,7 +117,7 @@ module ApplicationHelper
|
|||
# Returns the weekday. 0 is sunday, 1 is monday and so on
|
||||
def weekday(dayNumber)
|
||||
weekdays = I18n.t('date.day_names')
|
||||
return weekdays[dayNumber]
|
||||
weekdays[dayNumber]
|
||||
end
|
||||
|
||||
# to set a title for both the h1-tag and the title in the header
|
||||
|
@ -136,13 +136,13 @@ module ApplicationHelper
|
|||
|
||||
def icon(name, options = {})
|
||||
icons = {
|
||||
:delete => { :file => 'b_drop.png', :alt => I18n.t('ui.delete') },
|
||||
:edit => { :file => 'b_edit.png', :alt => I18n.t('ui.edit') },
|
||||
:members => { :file => 'b_users.png', :alt => I18n.t('helpers.application.edit_user') }
|
||||
delete: { file: 'b_drop.png', alt: I18n.t('ui.delete') },
|
||||
edit: { file: 'b_edit.png', alt: I18n.t('ui.edit') },
|
||||
members: { file: 'b_users.png', alt: I18n.t('helpers.application.edit_user') }
|
||||
}
|
||||
options[:alt] ||= icons[name][:alt]
|
||||
options[:title] ||= icons[name][:title]
|
||||
options.merge!({ :size => '16x16', :border => "0" })
|
||||
options.merge!({ size: '16x16', border: '0' })
|
||||
|
||||
image_tag icons[name][:file], options
|
||||
end
|
||||
|
@ -150,27 +150,29 @@ module ApplicationHelper
|
|||
# Remote links with default 'loader'.gif during request
|
||||
def remote_link_to(text, options = {})
|
||||
remote_options = {
|
||||
:before => "Element.show('loader')",
|
||||
:success => "Element.hide('loader')",
|
||||
:method => :get
|
||||
before: "Element.show('loader')",
|
||||
success: "Element.hide('loader')",
|
||||
method: :get
|
||||
}
|
||||
link_to(text, options[:url], remote_options.merge(options))
|
||||
end
|
||||
|
||||
def format_roles(record, icon = false)
|
||||
roles = %w(suppliers article_meta orders pickups finance invoices admin)
|
||||
roles = %w[suppliers article_meta orders pickups finance invoices admin]
|
||||
roles.select! { |role| record.send "role_#{role}?" }
|
||||
names = Hash[roles.map { |r| [r, I18n.t("helpers.application.role_#{r}")] }]
|
||||
names = roles.index_with { |r| I18n.t("helpers.application.role_#{r}") }
|
||||
if icon
|
||||
roles.map { |r| image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) }.join(' ').html_safe
|
||||
roles.map do |r|
|
||||
image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r])
|
||||
end.join(' ').html_safe
|
||||
else
|
||||
roles.map { |r| names[r] }.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def link_to_gmaps(address)
|
||||
link_to h(address), "http://maps.google.com/?q=#{h(address)}", :title => I18n.t('helpers.application.show_google_maps'),
|
||||
:target => "_blank"
|
||||
link_to h(address), "http://maps.google.com/?q=#{h(address)}", title: I18n.t('helpers.application.show_google_maps'),
|
||||
target: '_blank', rel: 'noopener'
|
||||
end
|
||||
|
||||
# Returns flash messages html.
|
||||
|
@ -186,8 +188,8 @@ module ApplicationHelper
|
|||
type = :success if type == 'notice'
|
||||
type = :error if type == 'alert'
|
||||
text = content_tag(:div,
|
||||
content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => "close", "data-dismiss" => "alert") +
|
||||
message, :class => "alert fade in alert-#{type}")
|
||||
content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => 'close', 'data-dismiss' => 'alert') +
|
||||
message, class: "alert fade in alert-#{type}")
|
||||
flash_messages << text if message
|
||||
end
|
||||
flash_messages.join("\n").html_safe
|
||||
|
@ -195,17 +197,17 @@ module ApplicationHelper
|
|||
|
||||
# render base errors in a form after failed validation
|
||||
# http://railsapps.github.io/twitter-bootstrap-rails.html
|
||||
def base_errors resource
|
||||
def base_errors(resource)
|
||||
return '' if resource.errors.empty? || resource.errors[:base].empty?
|
||||
|
||||
messages = resource.errors[:base].map { |msg| content_tag(:li, msg) }.join
|
||||
render :partial => 'shared/base_errors', :locals => { :error_messages => messages }
|
||||
render partial: 'shared/base_errors', locals: { error_messages: messages }
|
||||
end
|
||||
|
||||
# show a user, depending on settings
|
||||
def show_user(user = @current_user, options = {})
|
||||
if user.nil?
|
||||
"?"
|
||||
'?'
|
||||
elsif FoodsoftConfig[:use_nick]
|
||||
if options[:full] && options[:markup]
|
||||
raw "<b>#{h user.nick}</b> (#{h user.first_name} #{h user.last_name})"
|
||||
|
@ -216,7 +218,7 @@ module ApplicationHelper
|
|||
user.nick.nil? ? I18n.t('helpers.application.nick_fallback') : user.nick
|
||||
end
|
||||
else
|
||||
"#{user.first_name} #{user.last_name}" + (options[:unique] ? " (\##{user.id})" : '')
|
||||
"#{user.first_name} #{user.last_name}" + (options[:unique] ? " (##{user.id})" : '')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -258,9 +260,9 @@ module ApplicationHelper
|
|||
|
||||
# @return [String] stylesheet tag for foodcoop CSS style (+custom_css+ foodcoop config)
|
||||
# @see #foodcoop_css_path
|
||||
def foodcoop_css_tag(options = {})
|
||||
unless FoodsoftConfig[:custom_css].blank?
|
||||
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
||||
end
|
||||
def foodcoop_css_tag(_options = {})
|
||||
return if FoodsoftConfig[:custom_css].blank?
|
||||
|
||||
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,13 +3,13 @@ module ArticlesHelper
|
|||
def highlight_new(unequal_attributes, attribute)
|
||||
return unless unequal_attributes
|
||||
|
||||
unequal_attributes.has_key?(attribute) ? "background-color: yellow" : ""
|
||||
unequal_attributes.has_key?(attribute) ? 'background-color: yellow' : ''
|
||||
end
|
||||
|
||||
def row_classes(article)
|
||||
classes = []
|
||||
classes << "unavailable" if !article.availability
|
||||
classes << "just-updated" if article.recently_updated && article.availability
|
||||
classes.join(" ")
|
||||
classes << 'unavailable' unless article.availability
|
||||
classes << 'just-updated' if article.recently_updated && article.availability
|
||||
classes.join(' ')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,11 +11,11 @@ module DeliveriesHelper
|
|||
|
||||
def articles_for_select2(articles, except = [], &block)
|
||||
articles = articles.reorder('articles.name ASC')
|
||||
articles = articles.reject { |a| not except.index(a.id).nil? } if except
|
||||
block_given? or block = Proc.new { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
|
||||
articles = articles.reject { |a| !except.index(a.id).nil? } if except
|
||||
block_given? or block = proc { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
|
||||
articles.map do |a|
|
||||
{ :id => a.id, :text => block.call(a) }
|
||||
end.unshift({ :id => '', :text => '' })
|
||||
{ id: a.id, text: block.call(a) }
|
||||
end.unshift({ id: '', text: '' })
|
||||
end
|
||||
|
||||
def articles_for_table(articles)
|
||||
|
@ -23,10 +23,14 @@ module DeliveriesHelper
|
|||
end
|
||||
|
||||
def stock_change_remove_link(stock_change_form)
|
||||
return link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'remove_new_stock_change btn btn-small' if stock_change_form.object.new_record?
|
||||
if stock_change_form.object.new_record?
|
||||
return link_to t('deliveries.stock_change_fields.remove_article'), '#',
|
||||
class: 'remove_new_stock_change btn btn-small'
|
||||
end
|
||||
|
||||
output = stock_change_form.hidden_field :_destroy
|
||||
output += link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'destroy_stock_change btn btn-small'
|
||||
return output.html_safe
|
||||
output += link_to t('deliveries.stock_change_fields.remove_article'), '#',
|
||||
class: 'destroy_stock_change btn btn-small'
|
||||
output.html_safe
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,11 +2,11 @@ module Finance::BalancingHelper
|
|||
def balancing_view_partial
|
||||
view = params[:view] || 'edit_results'
|
||||
case view
|
||||
when 'edit_results' then
|
||||
when 'edit_results'
|
||||
'edit_results_by_articles'
|
||||
when 'groups_overview' then
|
||||
when 'groups_overview'
|
||||
'shared/articles_by/groups'
|
||||
when 'articles_overview' then
|
||||
when 'articles_overview'
|
||||
'shared/articles_by/articles'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
module Finance::InvoicesHelper
|
||||
def format_delivery_item delivery
|
||||
def format_delivery_item(delivery)
|
||||
format_date(delivery.date)
|
||||
end
|
||||
|
||||
def format_order_item order
|
||||
def format_order_item(order)
|
||||
"#{format_date(order.ends)} (#{number_to_currency(order.sum)})"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,12 +2,12 @@ module GroupOrderArticlesHelper
|
|||
# return an edit field for a GroupOrderArticle result
|
||||
def group_order_article_edit_result(goa)
|
||||
result = number_with_precision goa.result, strip_insignificant_zeros: true
|
||||
unless goa.group_order.order.finished? && current_user.role_finance?
|
||||
result
|
||||
else
|
||||
if goa.group_order.order.finished? && current_user.role_finance?
|
||||
simple_form_for goa, remote: true, html: { 'data-submit-onchange' => 'changed', class: 'delta-input' } do |f|
|
||||
f.input_field :result, as: :delta, class: 'input-nano', data: { min: 0 }, id: "r_#{goa.id}", value: result
|
||||
end
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
module GroupOrdersHelper
|
||||
def data_to_js(ordering_data)
|
||||
ordering_data[:order_articles].map { |id, data|
|
||||
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], data[:used_quantity], data[:quantity_available]]
|
||||
}.map { |row|
|
||||
ordering_data[:order_articles].map do |id, data|
|
||||
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance],
|
||||
data[:used_quantity], data[:quantity_available]]
|
||||
end.map do |row|
|
||||
"addData(#{row.join(', ')});"
|
||||
}.join("\n")
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
# Returns a link to the page where a group_order can be edited.
|
||||
|
@ -14,9 +15,9 @@ module GroupOrdersHelper
|
|||
path = if options[:show] && group_order
|
||||
group_order_path(group_order)
|
||||
elsif group_order
|
||||
edit_group_order_path(group_order, :order_id => order.id)
|
||||
edit_group_order_path(group_order, order_id: order.id)
|
||||
else
|
||||
new_group_order_path(:order_id => order.id)
|
||||
new_group_order_path(order_id: order.id)
|
||||
end
|
||||
options.delete(:show)
|
||||
name = block_given? ? capture(&block) : order.name
|
||||
|
@ -26,7 +27,7 @@ module GroupOrdersHelper
|
|||
# Return css class names for order result table
|
||||
|
||||
def order_article_class_name(quantity, tolerance, result)
|
||||
if (quantity + tolerance > 0)
|
||||
if quantity + tolerance > 0
|
||||
result > 0 ? 'success' : 'failed'
|
||||
else
|
||||
'ignored'
|
||||
|
@ -45,20 +46,12 @@ module GroupOrdersHelper
|
|||
end
|
||||
|
||||
def get_missing_units_css_class(quantity_missing)
|
||||
if (quantity_missing == 1)
|
||||
return 'missing-few';
|
||||
elsif (quantity_missing == 0)
|
||||
return ''
|
||||
if quantity_missing == 1
|
||||
'missing-few'
|
||||
elsif quantity_missing == 0
|
||||
''
|
||||
else
|
||||
return 'missing-many'
|
||||
'missing-many'
|
||||
end
|
||||
end
|
||||
|
||||
def price_per_base_unit(article:, price:)
|
||||
quantity_unit = QuantityUnit.parse(article.unit)
|
||||
return nil unless quantity_unit.present?
|
||||
|
||||
scaled_price, base_unit = quantity_unit.scale_price_to_base_unit(price)
|
||||
"#{number_to_currency(scaled_price)}/#{base_unit}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module OrderArticlesHelper
|
||||
def article_label_with_unit(article)
|
||||
pkg_info = pkg_helper(article, plain: true)
|
||||
"#{article.name} (#{[article.unit, pkg_info].reject(&:blank?).join(' ')})"
|
||||
"#{article.name} (#{[article.unit, pkg_info].compact_blank.join(' ')})"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module OrdersHelper
|
|||
|
||||
def options_for_suppliers_to_select
|
||||
options = [[I18n.t('helpers.orders.option_choose')]]
|
||||
options += Supplier.map { |s| [s.name, url_for(action: "new", supplier_id: s.id)] }
|
||||
options += Supplier.map { |s| [s.name, url_for(action: 'new', supplier_id: s.id)] }
|
||||
options += [[I18n.t('helpers.orders.option_stock'), url_for(action: 'new', supplier_id: nil)]]
|
||||
options_for_select(options)
|
||||
end
|
||||
|
@ -29,13 +29,13 @@ module OrdersHelper
|
|||
nil
|
||||
else
|
||||
units_info = []
|
||||
[:units_to_order, :units_billed, :units_received].map do |unit|
|
||||
if n = order_article.send(unit)
|
||||
line = n.to_s + ' '
|
||||
line += pkg_helper(order_article.price, options) + ' ' unless n == 0
|
||||
line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
|
||||
units_info << line
|
||||
end
|
||||
%i[units_to_order units_billed units_received].map do |unit|
|
||||
next unless n = order_article.send(unit)
|
||||
|
||||
line = n.to_s + ' '
|
||||
line += pkg_helper(order_article.price, options) + ' ' unless n == 0
|
||||
line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
|
||||
units_info << line
|
||||
end
|
||||
units_info.join(', ').html_safe
|
||||
end
|
||||
|
@ -67,8 +67,8 @@ module OrdersHelper
|
|||
def pkg_helper_icon(c = nil, options = {})
|
||||
options = { tag: 'i', class: '' }.merge(options)
|
||||
if c.nil?
|
||||
c = " ".html_safe
|
||||
options[:class] += " icon-only"
|
||||
c = ' '.html_safe
|
||||
options[:class] += ' icon-only'
|
||||
end
|
||||
content_tag(options[:tag], c, class: "package #{options[:class]}").html_safe
|
||||
end
|
||||
|
@ -94,11 +94,12 @@ module OrdersHelper
|
|||
autocomplete: 'off'
|
||||
|
||||
if order_article.result_manually_changed?
|
||||
input_html = content_tag(:span, class: 'input-prepend intable', title: t('orders.edit_amount.field_locked_title', default: '')) {
|
||||
input_html = content_tag(:span, class: 'input-prepend intable',
|
||||
title: t('orders.edit_amount.field_locked_title', default: '')) do
|
||||
button_tag(nil, type: :button, class: 'btn unlocker') {
|
||||
content_tag(:i, nil, class: 'icon icon-unlock')
|
||||
} + input_html
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
input_html.html_safe
|
||||
|
@ -109,18 +110,16 @@ module OrdersHelper
|
|||
def ordergroup_count(order)
|
||||
group_orders = order.group_orders.includes(:ordergroup)
|
||||
txt = "#{group_orders.count} #{Ordergroup.model_name.human count: group_orders.count}"
|
||||
if group_orders.count == 0
|
||||
return txt
|
||||
else
|
||||
desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ')
|
||||
content_tag(:abbr, txt, title: desc).html_safe
|
||||
end
|
||||
return txt if group_orders.count == 0
|
||||
|
||||
desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ')
|
||||
content_tag(:abbr, txt, title: desc).html_safe
|
||||
end
|
||||
|
||||
# @param order_or_supplier [Order, Supplier] Order or supplier to link to
|
||||
# @return [String] Link to order or supplier, showing its name.
|
||||
def supplier_link(order_or_supplier)
|
||||
if order_or_supplier.kind_of?(Order) && order_or_supplier.stockit?
|
||||
if order_or_supplier.is_a?(Order) && order_or_supplier.stockit?
|
||||
link_to(order_or_supplier.name, stock_articles_path).html_safe
|
||||
else
|
||||
link_to(@order.supplier.name, supplier_path(@order.supplier)).html_safe
|
||||
|
@ -152,19 +151,8 @@ module OrdersHelper
|
|||
if order.stockit?
|
||||
content_tag :div, t('orders.index.action_receive'), class: "btn disabled #{options[:class]}"
|
||||
else
|
||||
link_to t('orders.index.action_receive'), receive_order_path(order), class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
||||
link_to t('orders.index.action_receive'), receive_order_path(order),
|
||||
class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
||||
end
|
||||
end
|
||||
|
||||
def custom_csv_collection
|
||||
[
|
||||
OrderArticle.human_attribute_name(:units_to_order),
|
||||
Article.human_attribute_name(:order_number),
|
||||
Article.human_attribute_name(:name),
|
||||
Article.human_attribute_name(:unit),
|
||||
Article.human_attribute_name(:unit_quantity_short),
|
||||
ArticlePrice.human_attribute_name(:price),
|
||||
OrderArticle.human_attribute_name(:total_price)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
module StockitHelper
|
||||
def stock_article_classes(article)
|
||||
class_names = []
|
||||
class_names << "unavailable" if article.quantity_available <= 0
|
||||
class_names.join(" ")
|
||||
class_names << 'unavailable' if article.quantity_available <= 0
|
||||
class_names.join(' ')
|
||||
end
|
||||
|
||||
def link_to_stock_change_reason(stock_change)
|
||||
|
@ -17,8 +17,8 @@ module StockitHelper
|
|||
|
||||
def stock_article_price_hint(stock_article)
|
||||
t('simple_form.hints.stock_article.edit_stock_article.price',
|
||||
:stock_article_copy_link => link_to(t('stockit.form.copy_stock_article'),
|
||||
stock_article_copy_path(stock_article),
|
||||
:remote => true))
|
||||
stock_article_copy_link: link_to(t('stockit.form.copy_stock_article'),
|
||||
stock_article_copy_path(stock_article),
|
||||
remote: true))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
module TasksHelper
|
||||
def task_assignments(task)
|
||||
task.assignments.map do |ass|
|
||||
content_tag :span, show_user(ass.user), :class => (ass.accepted? ? 'accepted' : 'unaccepted')
|
||||
end.join(", ").html_safe
|
||||
content_tag :span, show_user(ass.user), class: (ass.accepted? ? 'accepted' : 'unaccepted')
|
||||
end.join(', ').html_safe
|
||||
end
|
||||
|
||||
# generate colored number of still required users
|
||||
def highlighted_required_users(task)
|
||||
unless task.enough_users_assigned?
|
||||
content_tag :span, task.still_required_users, class: 'badge badge-important',
|
||||
title: I18n.t('helpers.tasks.required_users', :count => task.still_required_users)
|
||||
end
|
||||
return if task.enough_users_assigned?
|
||||
|
||||
content_tag :span, task.still_required_users, class: 'badge badge-important',
|
||||
title: I18n.t('helpers.tasks.required_users', count: task.still_required_users)
|
||||
end
|
||||
|
||||
def task_title(task)
|
||||
|
|
|
@ -6,7 +6,7 @@ class DeltaInput < SimpleForm::Inputs::StringInput
|
|||
options[:data] ||= {}
|
||||
options[:data][:delta] ||= 1
|
||||
options[:autocomplete] ||= 'off'
|
||||
# TODO get generated id, don't know how yet - `add_default_name_and_id_for_value` might be an option
|
||||
# TODO: get generated id, don't know how yet - `add_default_name_and_id_for_value` might be an option
|
||||
|
||||
template.content_tag :div, class: 'delta-input input-prepend input-append' do
|
||||
delta_button(content_tag(:i, nil, class: 'icon icon-minus'), -1, options) +
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||
import "trix"
|
||||
import "@rails/actiontext"
|
||||
|
|
|
@ -29,7 +29,7 @@ class AppleBar
|
|||
|
||||
def mean_order_amount_per_job
|
||||
(1 / @global_avg).round
|
||||
rescue
|
||||
rescue StandardError
|
||||
0
|
||||
end
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class ArticlesCsv < RenderCsv
|
|||
def data
|
||||
@object.each do |o|
|
||||
yield [
|
||||
'',
|
||||
o.availability ? I18n.t('simple_form.yes') : I18n.t('simple_form.no'),
|
||||
o.order_number,
|
||||
o.name,
|
||||
o.note,
|
||||
|
|
|
@ -41,14 +41,14 @@ class BankAccountConnector
|
|||
end
|
||||
end
|
||||
|
||||
@@registered_classes = Set.new
|
||||
@registered_classes = Set.new
|
||||
|
||||
def self.register(klass)
|
||||
@@registered_classes.add klass
|
||||
@registered_classes.add klass
|
||||
end
|
||||
|
||||
def self.find(iban)
|
||||
@@registered_classes.each do |klass|
|
||||
@registered_classes.each do |klass|
|
||||
return klass if klass.handles(iban)
|
||||
end
|
||||
nil
|
||||
|
|
|
@ -17,16 +17,16 @@ class BankAccountInformationImporter
|
|||
ret = 0
|
||||
booked.each do |t|
|
||||
amount = parse_account_information_amount t[:transactionAmount]
|
||||
entityName = amount < 0 ? t[:creditorName] : t[:debtorName]
|
||||
entityAccount = amount < 0 ? t[:creditorAccount] : t[:debtorAccount]
|
||||
entity_name = amount < 0 ? t[:creditorName] : t[:debtorName]
|
||||
entity_account = amount < 0 ? t[:creditorAccount] : t[:debtorAccount]
|
||||
reference = [t[:endToEndId], t[:remittanceInformationUnstructured]].join("\n").strip
|
||||
|
||||
@bank_account.bank_transactions.where(external_id: t[:transactionId]).first_or_create.update({
|
||||
date: t[:bookingDate],
|
||||
amount: amount,
|
||||
iban: entityAccount && entityAccount[:iban],
|
||||
iban: entity_account && entity_account[:iban],
|
||||
reference: reference,
|
||||
text: entityName,
|
||||
text: entity_name,
|
||||
receipt: t[:additionalInformation]
|
||||
})
|
||||
ret += 1
|
||||
|
@ -34,7 +34,7 @@ class BankAccountInformationImporter
|
|||
|
||||
balances = (data[:balances] ? data[:balances].map { |b| [b[:balanceType], b[:balanceAmount]] } : []).to_h
|
||||
balance = balances.values.first
|
||||
%w(closingBooked expected authorised openingBooked interimAvailable forwardAvailable nonInvoiced).each do |type|
|
||||
%w[closingBooked expected authorised openingBooked interimAvailable forwardAvailable nonInvoiced].each do |type|
|
||||
value = balances[type]
|
||||
if value
|
||||
balance = value
|
||||
|
|
|
@ -10,66 +10,68 @@ module DateTimeAttributeValidate
|
|||
super
|
||||
|
||||
attributes.each do |attribute|
|
||||
validate -> { self.send("#{attribute}_datetime_value_valid") }
|
||||
validate -> { send("#{attribute}_datetime_value_valid") }
|
||||
|
||||
# allow resetting the field to nil
|
||||
before_validation do
|
||||
if self.instance_variable_get("@#{attribute}_is_set")
|
||||
date = self.instance_variable_get("@#{attribute}_date_value")
|
||||
time = self.instance_variable_get("@#{attribute}_time_value")
|
||||
if date.blank? && time.blank?
|
||||
self.send("#{attribute}=", nil)
|
||||
end
|
||||
if instance_variable_get("@#{attribute}_is_set")
|
||||
date = instance_variable_get("@#{attribute}_date_value")
|
||||
time = instance_variable_get("@#{attribute}_time_value")
|
||||
send("#{attribute}=", nil) if date.blank? && time.blank?
|
||||
end
|
||||
end
|
||||
|
||||
# remember old date and time values
|
||||
define_method("#{attribute}_date_value=") do |val|
|
||||
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||
self.instance_variable_set("@#{attribute}_date_value", val)
|
||||
instance_variable_set("@#{attribute}_is_set", true)
|
||||
instance_variable_set("@#{attribute}_date_value", val)
|
||||
begin
|
||||
self.send("#{attribute}_date=", val)
|
||||
rescue
|
||||
send("#{attribute}_date=", val)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
end
|
||||
define_method("#{attribute}_time_value=") do |val|
|
||||
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||
self.instance_variable_set("@#{attribute}_time_value", val)
|
||||
instance_variable_set("@#{attribute}_is_set", true)
|
||||
instance_variable_set("@#{attribute}_time_value", val)
|
||||
begin
|
||||
self.send("#{attribute}_time=", val)
|
||||
rescue
|
||||
send("#{attribute}_time=", val)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# fallback to field when values are not set
|
||||
define_method("#{attribute}_date_value") do
|
||||
self.instance_variable_get("@#{attribute}_date_value") || self.send("#{attribute}_date").try { |e| e.strftime('%Y-%m-%d') }
|
||||
instance_variable_get("@#{attribute}_date_value") || send("#{attribute}_date").try do |e|
|
||||
e.strftime('%Y-%m-%d')
|
||||
end
|
||||
end
|
||||
define_method("#{attribute}_time_value") do
|
||||
self.instance_variable_get("@#{attribute}_time_value") || self.send("#{attribute}_time").try { |e| e.strftime('%H:%M') }
|
||||
instance_variable_get("@#{attribute}_time_value") || send("#{attribute}_time").try do |e|
|
||||
e.strftime('%H:%M')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# validate date and time
|
||||
define_method("#{attribute}_datetime_value_valid") do
|
||||
date = self.instance_variable_get("@#{attribute}_date_value")
|
||||
date = instance_variable_get("@#{attribute}_date_value")
|
||||
unless date.blank? || begin
|
||||
Date.parse(date)
|
||||
rescue
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
errors.add(attribute, "is not a valid date") # @todo I18n
|
||||
errors.add(attribute, 'is not a valid date') # @todo I18n
|
||||
end
|
||||
time = self.instance_variable_get("@#{attribute}_time_value")
|
||||
time = instance_variable_get("@#{attribute}_time_value")
|
||||
unless time.blank? || begin
|
||||
Time.parse(time)
|
||||
rescue
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
errors.add(attribute, "is not a valid time") # @todo I18n
|
||||
errors.add(attribute, 'is not a valid time') # @todo I18n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ module Foodsoft
|
|||
cattr_accessor :variables
|
||||
|
||||
# Hash of variables. Note that keys are Strings.
|
||||
@@variables = {
|
||||
@variables = {
|
||||
'scope' => -> { FoodsoftConfig.scope },
|
||||
'name' => -> { FoodsoftConfig[:name] },
|
||||
'contact.street' => -> { FoodsoftConfig[:contact][:street] },
|
||||
|
@ -39,13 +39,13 @@ module Foodsoft
|
|||
'supplier_count' => -> { Supplier.undeleted.count },
|
||||
'active_supplier_count' => -> { active_supplier_count },
|
||||
'active_suppliers' => -> { active_suppliers },
|
||||
'first_order_date' => -> { I18n.l Order.first.try { |o| o.starts.to_date } }
|
||||
'first_order_date' => -> { I18n.l(Order.first.try { |o| o.starts.to_date }) }
|
||||
}
|
||||
|
||||
# Return expanded variable
|
||||
# @return [String] Expanded variable
|
||||
def self.get(var)
|
||||
s = @@variables[var.to_s]
|
||||
s = @variables[var.to_s]
|
||||
s.respond_to?(:call) ? s.call : s.to_s
|
||||
end
|
||||
|
||||
|
@ -55,7 +55,7 @@ module Foodsoft
|
|||
# @return [String] Expanded string
|
||||
def self.expand(str, options = {})
|
||||
str.gsub(/{{([._a-zA-Z0-9]+)}}/) do
|
||||
options[::Regexp.last_match(1)] || self.get(::Regexp.last_match(1))
|
||||
options[::Regexp.last_match(1)] || get(::Regexp.last_match(1))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class FoodsoftConfig
|
|||
# Load initial config from development or production
|
||||
set_config Rails.env
|
||||
# Overwrite scope to have a better namescope than 'production'
|
||||
self.scope = config[:default_scope] or raise "No default_scope is set"
|
||||
self.scope = config[:default_scope] or raise 'No default_scope is set'
|
||||
# Set defaults for backward-compatibility
|
||||
set_missing
|
||||
# Make sure relevant configuration is applied, also in single coops mode,
|
||||
|
@ -79,7 +79,7 @@ class FoodsoftConfig
|
|||
end
|
||||
|
||||
def init_mailing
|
||||
[:protocol, :host, :port, :script_name].each do |k|
|
||||
%i[protocol host port script_name].each do |k|
|
||||
ActionMailer::Base.default_url_options[k] = self[k] if self[k]
|
||||
end
|
||||
end
|
||||
|
@ -117,7 +117,7 @@ class FoodsoftConfig
|
|||
# @return [Object] Value of the key.
|
||||
def [](key)
|
||||
if RailsSettings::CachedSettings.table_exists? && allowed_key?(key)
|
||||
value = RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"]
|
||||
value = RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"]
|
||||
value = config[key] if value.nil?
|
||||
value
|
||||
else
|
||||
|
@ -139,20 +139,20 @@ class FoodsoftConfig
|
|||
if config[key] == value || (config[key].nil? && value == false)
|
||||
# delete (ok if it was already deleted)
|
||||
begin
|
||||
RailsSettings::CachedSettings.destroy "foodcoop.#{self.scope}.#{key}"
|
||||
RailsSettings::CachedSettings.destroy "foodcoop.#{scope}.#{key}"
|
||||
rescue RailsSettings::Settings::SettingNotFound
|
||||
end
|
||||
else
|
||||
# or store
|
||||
RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"] = value
|
||||
RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"] = value
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# @return [Array<String>] Configuration keys that are set (either in +app_config.yml+ or database).
|
||||
def keys
|
||||
keys = RailsSettings::CachedSettings.get_all("foodcoop.#{self.scope}.").try(:keys) || []
|
||||
keys.map! { |k| k.gsub(/^foodcoop\.#{self.scope}\./, '') }
|
||||
keys = RailsSettings::CachedSettings.get_all("foodcoop.#{scope}.").try(:keys) || []
|
||||
keys.map! { |k| k.gsub(/^foodcoop\.#{scope}\./, '') }
|
||||
keys += config.keys
|
||||
keys.map(&:to_s).uniq
|
||||
end
|
||||
|
@ -181,10 +181,10 @@ class FoodsoftConfig
|
|||
# @return [Boolean] Whether this key may be set in the database
|
||||
def allowed_key?(key)
|
||||
# fast check for keys without nesting
|
||||
if self.config[:protected].include? key
|
||||
!self.config[:protected][key]
|
||||
if config[:protected].include? key
|
||||
!config[:protected][key]
|
||||
else
|
||||
!self.config[:protected][:all]
|
||||
!config[:protected][:all]
|
||||
end
|
||||
# @todo allow to check nested keys as well
|
||||
end
|
||||
|
@ -287,7 +287,9 @@ class FoodsoftConfig
|
|||
def normalize_value(value)
|
||||
value = value.map { |v| normalize_value(v) } if value.is_a? Array
|
||||
if value.is_a? Hash
|
||||
value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map { |a| [a[0], normalize_value(a[1])] }]
|
||||
value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map do |a|
|
||||
[a[0], normalize_value(a[1])]
|
||||
end]
|
||||
end
|
||||
case value
|
||||
when 'true' then true
|
||||
|
|
|
@ -8,26 +8,24 @@ module FoodsoftDateUtil
|
|||
# @todo handle ical parse errors
|
||||
occ = begin
|
||||
schedule.next_occurrence(from).to_time
|
||||
rescue
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
end
|
||||
if options && options[:time] && occ
|
||||
occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight)
|
||||
end
|
||||
occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight) if options && options[:time] && occ
|
||||
occ
|
||||
end
|
||||
|
||||
# @param p [String, Symbol, Hash, IceCube::Rule] What to return a rule from.
|
||||
# @param rule [String, Symbol, Hash, IceCube::Rule] What to return a rule from.
|
||||
# @return [IceCube::Rule] Recurring rule
|
||||
def self.rule_from(p)
|
||||
case p
|
||||
def self.rule_from(rule)
|
||||
case rule
|
||||
when String
|
||||
IceCube::Rule.from_ical(p)
|
||||
IceCube::Rule.from_ical(rule)
|
||||
when Hash
|
||||
IceCube::Rule.from_hash(p)
|
||||
IceCube::Rule.from_hash(rule)
|
||||
else
|
||||
p
|
||||
rule
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
25
app/lib/foodsoft_file.rb
Normal file
25
app/lib/foodsoft_file.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Foodsoft-file import
|
||||
class FoodsoftFile
|
||||
# parses a string from a foodsoft-file
|
||||
# returns two arrays with articles and outlisted_articles
|
||||
# the parsed article is a simple hash
|
||||
def self.parse(file, options = {})
|
||||
SpreadsheetFile.parse file, options do |row, row_index|
|
||||
next if row[2].blank?
|
||||
|
||||
article = { order_number: row[1],
|
||||
name: row[2],
|
||||
note: row[3],
|
||||
manufacturer: row[4],
|
||||
origin: row[5],
|
||||
unit: row[6],
|
||||
price: row[7],
|
||||
tax: row[8],
|
||||
deposit: (row[9].nil? ? '0' : row[9]),
|
||||
unit_quantity: row[10],
|
||||
article_category: row[13] }
|
||||
status = row[0] && row[0].strip.downcase == 'x' ? :outlisted : nil
|
||||
yield status, article, row_index
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,8 +23,8 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
|||
recipient = rcpt_to.gsub(/^\s*<\s*(.*)\s*>\s*$/, '\1')
|
||||
@handlers << self.class.find_handler(recipient)
|
||||
rcpt_to
|
||||
rescue => error
|
||||
logger.info("Can not accept mail for '#{rcpt_to}': #{error}")
|
||||
rescue StandardError => e
|
||||
logger.info("Can not accept mail for '#{rcpt_to}': #{e}")
|
||||
raise MidiSmtpServer::Smtpd550Exception
|
||||
end
|
||||
|
||||
|
@ -32,16 +32,16 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
|||
@handlers.each do |handler|
|
||||
handler.call(ctx[:message][:data])
|
||||
end
|
||||
rescue => error
|
||||
ExceptionNotifier.notify_exception(error, data: ctx)
|
||||
raise error
|
||||
rescue StandardError => e
|
||||
ExceptionNotifier.notify_exception(e, data: ctx)
|
||||
raise e
|
||||
ensure
|
||||
@handlers.clear
|
||||
end
|
||||
|
||||
def self.find_handler(recipient)
|
||||
m = /(?<foodcoop>[^@.]+)\.(?<address>[^@]+)(@(?<hostname>[^@]+))?/.match recipient
|
||||
raise "recipient is missing or has an invalid format" if m.nil?
|
||||
raise 'recipient is missing or has an invalid format' if m.nil?
|
||||
raise "Foodcoop '#{m[:foodcoop]}' could not be found" unless FoodsoftConfig.allowed_foodcoop? m[:foodcoop]
|
||||
|
||||
FoodsoftConfig.select_multifoodcoop m[:foodcoop]
|
||||
|
@ -53,6 +53,6 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
|||
end
|
||||
end
|
||||
|
||||
raise "invalid format for recipient"
|
||||
raise 'invalid format for recipient'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,60 +2,28 @@ require 'csv'
|
|||
|
||||
class OrderCsv < RenderCsv
|
||||
def header
|
||||
params = @options[:custom_csv]
|
||||
arr = if params.nil?
|
||||
[
|
||||
OrderArticle.human_attribute_name(:units_to_order),
|
||||
Article.human_attribute_name(:order_number),
|
||||
Article.human_attribute_name(:name),
|
||||
Article.human_attribute_name(:unit),
|
||||
Article.human_attribute_name(:unit_quantity_short),
|
||||
ArticlePrice.human_attribute_name(:price),
|
||||
OrderArticle.human_attribute_name(:total_price)
|
||||
]
|
||||
else
|
||||
[
|
||||
params[:first],
|
||||
params[:second],
|
||||
params[:third],
|
||||
params[:fourth],
|
||||
params[:fifth],
|
||||
params[:sixth],
|
||||
params[:seventh]
|
||||
]
|
||||
end
|
||||
[
|
||||
OrderArticle.human_attribute_name(:units_to_order),
|
||||
Article.human_attribute_name(:order_number),
|
||||
Article.human_attribute_name(:name),
|
||||
Article.human_attribute_name(:unit),
|
||||
Article.human_attribute_name(:unit_quantity_short),
|
||||
ArticlePrice.human_attribute_name(:price),
|
||||
OrderArticle.human_attribute_name(:total_price)
|
||||
]
|
||||
end
|
||||
|
||||
def data
|
||||
@object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa|
|
||||
@object.order_articles.ordered.includes(%i[article article_price]).all.map do |oa|
|
||||
yield [
|
||||
match_params(oa, header[0]),
|
||||
match_params(oa, header[1]),
|
||||
match_params(oa, header[2]),
|
||||
match_params(oa, header[3]),
|
||||
match_params(oa, header[4]),
|
||||
match_params(oa, header[5]),
|
||||
match_params(oa, header[6])
|
||||
oa.units_to_order,
|
||||
oa.article.order_number,
|
||||
oa.article.name,
|
||||
oa.article.unit,
|
||||
oa.price.unit_quantity > 1 ? oa.price.unit_quantity : nil,
|
||||
number_to_currency(oa.price.price * oa.price.unit_quantity),
|
||||
number_to_currency(oa.total_price)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def match_params(object, attribute)
|
||||
case attribute
|
||||
when OrderArticle.human_attribute_name(:units_to_order)
|
||||
object.units_to_order
|
||||
when Article.human_attribute_name(:order_number)
|
||||
object.article.order_number
|
||||
when Article.human_attribute_name(:name)
|
||||
object.article.name
|
||||
when Article.human_attribute_name(:unit)
|
||||
object.article.unit
|
||||
when Article.human_attribute_name(:unit_quantity_short)
|
||||
object.price.unit_quantity > 1 ? object.price.unit_quantity : nil
|
||||
when ArticlePrice.human_attribute_name(:price)
|
||||
number_to_currency(object.price.price * object.price.unit_quantity)
|
||||
when OrderArticle.human_attribute_name(:total_price)
|
||||
number_to_currency(object.total_price)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ class OrderPdf < RenderPdf
|
|||
end
|
||||
|
||||
def group_order_article_quantity_with_tolerance(goa)
|
||||
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : "#{goa.quantity}"
|
||||
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : goa.quantity.to_s
|
||||
end
|
||||
|
||||
def group_order_article_result(goa)
|
||||
|
@ -88,7 +88,7 @@ class OrderPdf < RenderPdf
|
|||
.pluck('groups.name', 'SUM(group_orders.price)', 'ordergroup_id', 'SUM(group_orders.transport)')
|
||||
|
||||
result.map do |item|
|
||||
[item.first || stock_ordergroup_name] + item[1..-1]
|
||||
[item.first || stock_ordergroup_name] + item[1..]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,7 +103,7 @@ class OrderPdf < RenderPdf
|
|||
def each_ordergroup_batch(batch_size)
|
||||
offset = 0
|
||||
|
||||
while true
|
||||
loop do
|
||||
go_records = ordergroups(offset, batch_size)
|
||||
|
||||
break unless go_records.any?
|
||||
|
@ -113,7 +113,7 @@ class OrderPdf < RenderPdf
|
|||
# get quantity for each article and ordergroup
|
||||
goa_records = group_order_articles(group_ids)
|
||||
.group('group_order_articles.order_article_id, group_orders.ordergroup_id')
|
||||
.pluck('group_order_articles.order_article_id', 'group_orders.ordergroup_id', 'SUM(COALESCE(group_order_articles.result, group_order_articles.quantity))')
|
||||
.pluck('group_order_articles.order_article_id', 'group_orders.ordergroup_id', Arel.sql('SUM(COALESCE(group_order_articles.result, group_order_articles.quantity))'))
|
||||
|
||||
# transform the flat list of results in a hash (with the article as key), which contains an array for all ordergroups
|
||||
results = goa_records.group_by(&:first).transform_values do |value|
|
||||
|
@ -136,7 +136,7 @@ class OrderPdf < RenderPdf
|
|||
group_order_articles(ordergroup)
|
||||
.includes(order_article: { article: [:supplier] })
|
||||
.order('suppliers.name, articles.name')
|
||||
.preload(order_article: [:article_price, :order])
|
||||
.preload(order_article: %i[article_price order])
|
||||
.each(&block)
|
||||
end
|
||||
|
||||
|
|
|
@ -8,23 +8,19 @@ class OrderTxt
|
|||
def to_txt
|
||||
supplier = @order.supplier
|
||||
contact = FoodsoftConfig[:contact].symbolize_keys
|
||||
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
|
||||
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
|
||||
text = I18n.t('orders.fax.heading', name: FoodsoftConfig[:name])
|
||||
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" if supplier.customer_number.present?
|
||||
text += "\n" + I18n.t('orders.fax.delivery_day')
|
||||
text += "\n\n#{supplier.name}\n#{supplier.address}\n#{Supplier.human_attribute_name(:fax)}: #{supplier.fax}\n\n"
|
||||
text += "****** " + I18n.t('orders.fax.to_address') + "\n\n"
|
||||
text += '****** ' + I18n.t('orders.fax.to_address') + "\n\n"
|
||||
text += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
|
||||
text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
|
||||
text += format("%8s %8s %s\n", I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'), I18n.t('orders.fax.name'))
|
||||
text += '****** ' + I18n.t('orders.fax.articles') + "\n\n"
|
||||
text += format("%8s %8s %s\n", I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'),
|
||||
I18n.t('orders.fax.name'))
|
||||
# now display all ordered articles
|
||||
@order.order_articles.ordered.includes([:article, :article_price]).each do |oa|
|
||||
@order.order_articles.ordered.includes(%i[article article_price]).each do |oa|
|
||||
text += format("%8s %8d %s\n", oa.article.order_number, oa.units_to_order.to_i, oa.article.name)
|
||||
end
|
||||
text
|
||||
end
|
||||
|
||||
# Helper method to test pdf via rails console: OrderTxt.new(order).save_tmp
|
||||
def save_tmp
|
||||
File.write("#{Rails.root}/tmp/#{self.class.to_s.underscore}.txt", to_csv.force_encoding("UTF-8"))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
class QuantityUnit
|
||||
def initialize(quantity, unit)
|
||||
@quantity = quantity
|
||||
@unit = unit
|
||||
end
|
||||
|
||||
def self.parse(number_with_unit)
|
||||
# remove whitespace
|
||||
number_with_unit = number_with_unit.gsub(/\s+/, '')
|
||||
# to lowercase
|
||||
number_with_unit = number_with_unit.downcase
|
||||
# remove numerical part
|
||||
number = number_with_unit.gsub(/[^0-9.,]/, '')
|
||||
# remove unit part
|
||||
unit = number_with_unit.gsub(/[^a-zA-Z]/, '')
|
||||
# convert comma to dot
|
||||
number = number.gsub(',', '.')
|
||||
# convert to float
|
||||
number = number.to_f
|
||||
|
||||
return nil unless unit.in?(%w[g kg l ml])
|
||||
|
||||
QuantityUnit.new(number, unit)
|
||||
end
|
||||
|
||||
def scale_price_to_base_unit(price)
|
||||
return nil unless price.is_a?(Numeric)
|
||||
|
||||
factor = if @unit == 'kg' || @unit == 'l'
|
||||
1
|
||||
elsif @unit == 'g' || @unit == 'ml'
|
||||
1000
|
||||
end
|
||||
|
||||
scaled_price = price / @quantity * factor
|
||||
scaled_price.round(2)
|
||||
|
||||
base_unit = if @unit == 'kg' || @unit == 'g'
|
||||
'kg'
|
||||
elsif @unit == 'l' || @unit == 'ml'
|
||||
'L'
|
||||
end
|
||||
|
||||
[scaled_price, base_unit]
|
||||
end
|
||||
|
||||
|
||||
def to_s
|
||||
"#{@quantity} #{@unit}"
|
||||
end
|
||||
|
||||
def quantity
|
||||
@quantity
|
||||
end
|
||||
|
||||
def unit
|
||||
@unit
|
||||
end
|
||||
end
|
|
@ -13,14 +13,13 @@ class RenderCsv
|
|||
end
|
||||
|
||||
def to_csv
|
||||
options = @options.select { |k| %w(col_sep row_sep).include? k.to_s }
|
||||
options = @options.select { |k| %w[col_sep row_sep].include? k.to_s }
|
||||
ret = CSV.generate options do |csv|
|
||||
if h = header
|
||||
csv << h
|
||||
end
|
||||
data { |d| csv << d }
|
||||
end
|
||||
ret << I18n.t('.orders.articles.prices_sum') << ";" << "#{number_to_currency(@object.sum(:gross))}/#{number_to_currency(@object.sum(:net))}" if @options[:custom_csv]
|
||||
ret.encode(@options[:encoding], invalid: :replace, undef: :replace)
|
||||
end
|
||||
|
||||
|
@ -32,12 +31,6 @@ class RenderCsv
|
|||
yield []
|
||||
end
|
||||
|
||||
# Helper method to test pdf via rails console: OrderCsv.new(order).save_tmp
|
||||
def save_tmp
|
||||
encoding = @options[:encoding] || 'UTF-8'
|
||||
File.write("#{Rails.root}/tmp/#{self.class.to_s.underscore}.csv", to_csv.force_encoding(encoding))
|
||||
end
|
||||
|
||||
# XXX disable unit to avoid encoding problems, both in unit and whitespace. Also allows computations in spreadsheet.
|
||||
def number_to_currency(number, options = {})
|
||||
super(number, options.merge({ unit: '' }))
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue