Compare commits
35 commits
automatic_
...
demo
Author | SHA1 | Date | |
---|---|---|---|
|
eb6cf00f94 | ||
c76c148f99 | |||
ce7b4d7ce4 | |||
|
eb719057c4 | ||
|
2614f095cb | ||
b94ca21022 | |||
|
8cb86b2f88 | ||
3d71d266e3 | |||
ee03a2a9af | |||
|
237ef5d38b | ||
|
dfe8beae2c | ||
|
75bb400d0d | ||
6f2a3b4f5f | |||
d81ae10dc8 | |||
4b5775e107 | |||
|
936c1ba878 | ||
|
b3571515b0 | ||
|
28c851823a | ||
|
25d4efa71a | ||
|
49a04b226c | ||
|
69c80eba3e | ||
|
e6e2cdc2c6 | ||
4bb724495d | |||
0bd04fba41 | |||
46e3794a4e | |||
5c04a43f61 | |||
|
78da4feafe | ||
|
666e7934a6 | ||
|
82d4ff0284 | ||
|
c487f0368a | ||
a7747c9e84 | |||
fb8ccfea4a | |||
|
d7591d46b9 | ||
|
d16aa19300 | ||
|
3f114af193 |
495 changed files with 7760 additions and 13558 deletions
145
.drone.yml
Normal file
145
.drone.yml
Normal file
|
@ -0,0 +1,145 @@
|
|||
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
5
.gitattributes
vendored
|
@ -1,5 +0,0 @@
|
|||
# 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
|
6
.github/workflows/ruby.yml
vendored
6
.github/workflows/ruby.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
MYSQL_DATABASE: test
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
options: >-
|
||||
--health-cmd "mariadb-admin ping"
|
||||
--health-cmd "mysqladmin ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
@ -35,9 +35,7 @@ jobs:
|
|||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup chromedriver
|
||||
uses: nanasess/setup-chromedriver@v2
|
||||
with:
|
||||
chromedriver-version: '115.0.5790.170' # https://github.com/nanasess/setup-chromedriver/issues/200
|
||||
uses: nanasess/setup-chromedriver@v1.0.1
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
|
|
1821
.rubocop_todo.yml
1821
.rubocop_todo.yml
File diff suppressed because it is too large
Load diff
|
@ -1 +1 @@
|
|||
2.7.8
|
||||
2.7.2
|
||||
|
|
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -1,36 +1,3 @@
|
|||
# Foodsoft 4.8.0
|
||||
|
||||
* feat: Show total sums for ordergroup finances [#1017](https://github.com/foodcoops/foodsoft/pull/1017)
|
||||
* feat: Richtext Messages and Attachments with Actiontext [#918](https://github.com/foodcoops/foodsoft/issues/918)
|
||||
* feat: Make date configurable via locales [#997](https://github.com/foodcoops/foodsoft/pull/997)
|
||||
* feat: Turkish language support added [#995](https://github.com/foodcoops/foodsoft/pull/995)
|
||||
* feat: Disable member list via configuration [#990](https://github.com/foodcoops/foodsoft/pull/990)
|
||||
* feat: Specify an URL to redirect after logout via settings #989
|
||||
* feat: introduce importmaps [#983](https://github.com/foodcoops/foodsoft/pull/983)
|
||||
* feat: ruby 2.7.2 and rails 7 upgrade [#979](https://github.com/foodcoops/foodsoft/pull/979)
|
||||
* feat: Add home controller test [#972](https://github.com/foodcoops/foodsoft/pull/972)
|
||||
* feat: Replace apivore with rswag for api tests [#969](https://github.com/foodcoops/foodsoft/pull/969)
|
||||
* feat: increase test coverage [#966](https://github.com/foodcoops/foodsoft/pull/966)
|
||||
* feat: Show order note as tooltip [#965](https://github.com/foodcoops/foodsoft/pull/965)
|
||||
* feat: Add sd_notify [#961](https://github.com/foodcoops/foodsoft/pull/961)
|
||||
* feat: Show instance name at login screen [#957](https://github.com/foodcoops/foodsoft/pull/957)
|
||||
* feat: Enabled systemd socket activation [#942](https://github.com/foodcoops/foodsoft/pull/942)
|
||||
* feat: Add table_print gem for debugging ActiveRecord queries in the console [#935](https://github.com/foodcoops/foodsoft/pull/935)
|
||||
* feat: Add admin UI for SupplierCategories (supplier_categories) [#930](https://github.com/foodcoops/foodsoft/pull/930)
|
||||
|
||||
* fix: add null checks for articles convert_units [33034e6](https://github.com/foodcoops/foodsoft/commit/33034e66b88968dedc5289425e1eff847ee67e12)
|
||||
* fix: downgrade haml to make deface work [#1003](https://github.com/foodcoops/foodsoft/pull/1003)
|
||||
* fix: dutch translation errors [#954](https://github.com/foodcoops/foodsoft/pull/954)
|
||||
* fix: Fixe filtering of active ordergroups [#934](https://github.com/foodcoops/foodsoft/pull/934)
|
||||
* fix: Change password validation to allow longer passwords [#923](https://github.com/foodcoops/foodsoft/pull/923)
|
||||
* fix: Invoice: change label "delivery" to "stock delivery" [#922](https://github.com/foodcoops/foodsoft/pull/922)
|
||||
* fix: Allow decimal numbers in transaction collections [#921](https://github.com/foodcoops/foodsoft/pull/921)
|
||||
* fix: Add validation of more article fields [#917](https://github.com/foodcoops/foodsoft/pull/917/files)
|
||||
* fix: Add default time_zone [#912](https://github.com/foodcoops/foodsoft/pull/912)
|
||||
* fix: Rename Piwik to Matomo [#911](https://github.com/foodcoops/foodsoft/pull/911/files)
|
||||
* fix: Change instructions to rbenv [#910](https://github.com/foodcoops/foodsoft/pull/910/files)
|
||||
|
||||
|
||||
# Foodsoft 4.7.1
|
||||
(31 December 2020)
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ ENV PORT=3000 \
|
|||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY . ./
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
COPY plugins/ ./plugins
|
||||
COPY config/ ./config
|
||||
|
||||
# install dependencies and generate crontab
|
||||
RUN buildDeps='libmagic-dev' && \
|
||||
|
@ -30,6 +32,8 @@ 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 && \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM ruby:2.7.8
|
||||
FROM ruby:2.7
|
||||
|
||||
# Install dependencies
|
||||
RUN deps='libmagic-dev chromium nodejs' && \
|
||||
|
@ -19,7 +19,6 @@ ENV PORT=3000 \
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
RUN gem update --system
|
||||
RUN gem install bundler
|
||||
RUN bundle config build.nokogiri "--use-system-libraries"
|
||||
|
||||
|
|
107
Gemfile
107
Gemfile
|
@ -1,77 +1,74 @@
|
|||
# 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', '>=7.0.4.1'
|
||||
|
||||
gem 'less-rails'
|
||||
gem 'sassc-rails'
|
||||
gem 'less-rails'
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
gem 'therubyracer', platforms: :ruby
|
||||
|
||||
gem 'bootsnap', require: false
|
||||
gem 'jquery-rails'
|
||||
gem 'select2-rails'
|
||||
gem 'rails_tokeninput'
|
||||
gem 'bootstrap-datepicker-rails'
|
||||
gem 'date_time_attribute'
|
||||
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 'i18n-js', '~> 3.0.0.rc8'
|
||||
gem 'rails-i18n'
|
||||
gem 'rails_tokeninput'
|
||||
gem 'select2-rails'
|
||||
gem 'bootsnap', require: false
|
||||
|
||||
gem 'active_model_serializers', '~> 0.10.0'
|
||||
gem 'acts_as_tree'
|
||||
gem 'attribute_normalizer'
|
||||
gem 'mysql2'
|
||||
gem 'prawn'
|
||||
gem 'prawn-table'
|
||||
gem 'haml', '~> 5.0'
|
||||
gem 'haml-rails'
|
||||
gem 'kaminari'
|
||||
gem 'simple_form'
|
||||
gem 'inherited_resources'
|
||||
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 'net-ftp'
|
||||
gem 'net-http'
|
||||
gem 'prawn'
|
||||
gem 'prawn-table'
|
||||
gem 'puma'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
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 'simple_form'
|
||||
gem 'active_model_serializers', '~> 0.10.0'
|
||||
gem 'twitter-bootstrap-rails', '~> 2.2.8'
|
||||
gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap
|
||||
gem 'simple-navigation-bootstrap'
|
||||
gem 'sprockets', '< 4'
|
||||
gem 'twitter-bootstrap-rails', '~> 2.2.8'
|
||||
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 '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 'exception_notification'
|
||||
gem 'gaffe'
|
||||
gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
|
||||
gem "image_processing", "~> 1.12"
|
||||
gem "importmap-rails", "~> 1.1"
|
||||
gem 'midi-smtp-server'
|
||||
gem 'mime-types'
|
||||
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 'midi-smtp-server'
|
||||
gem 'rswag-api'
|
||||
gem 'rswag-ui'
|
||||
gem 'ruby-filemagic'
|
||||
gem 'spreadsheet'
|
||||
gem "terser", "~> 1.1"
|
||||
|
||||
# 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_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_automatic_invoices', path: 'plugins/automatic_invoices'
|
||||
gem 'foodsoft_messages', path: 'plugins/messages'
|
||||
gem 'foodsoft_documents', path: 'plugins/documents'
|
||||
gem 'foodsoft_discourse', path: 'plugins/discourse'
|
||||
gem 'foodsoft_links', path: 'plugins/links'
|
||||
gem 'foodsoft_polls', path: 'plugins/polls'
|
||||
|
||||
# plugins not enabled by default
|
||||
# gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
||||
|
@ -79,10 +76,10 @@ gem 'foodsoft_automatic_invoices', path: 'plugins/automatic_invoices'
|
|||
# gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
||||
|
||||
group :development do
|
||||
gem 'listen'
|
||||
gem 'mailcatcher'
|
||||
gem 'sqlite3', '~> 1.3.6'
|
||||
gem 'mailcatcher'
|
||||
gem 'web-console'
|
||||
gem 'listen'
|
||||
|
||||
# Better error output
|
||||
gem 'better_errors'
|
||||
|
@ -110,20 +107,26 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem 'apparition' # Capybara javascript driver
|
||||
gem 'capybara'
|
||||
gem 'connection_pool'
|
||||
gem 'database_cleaner'
|
||||
gem 'rspec-rails'
|
||||
gem 'factory_bot_rails'
|
||||
gem 'faker'
|
||||
gem 'rspec-rails'
|
||||
gem 'capybara'
|
||||
gem 'apparition' # Capybara javascript driver
|
||||
gem 'database_cleaner'
|
||||
gem 'connection_pool'
|
||||
# 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"
|
||||
|
|
205
Gemfile.lock
205
Gemfile.lock
|
@ -1,3 +1,11 @@
|
|||
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
|
||||
|
@ -16,13 +24,6 @@ GIT
|
|||
acts_as_versioned (0.6.0)
|
||||
activerecord (>= 3.0.9)
|
||||
|
||||
PATH
|
||||
remote: plugins/automatic_invoices
|
||||
specs:
|
||||
foodsoft_automatic_invoices (0.0.1)
|
||||
deface (~> 1.9)
|
||||
rails
|
||||
|
||||
PATH
|
||||
remote: plugins/discourse
|
||||
specs:
|
||||
|
@ -77,47 +78,47 @@ PATH
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actioncable (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailbox (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activestorage (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailer (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
actionview (= 7.0.4)
|
||||
activejob (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
actionpack (7.0.4)
|
||||
actionview (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
rack (~> 2.0, >= 2.2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actiontext (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activerecord (= 7.0.4)
|
||||
activestorage (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -127,22 +128,22 @@ GEM
|
|||
activemodel (>= 4.1, < 7.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activejob (7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activestorage (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
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)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.0.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
@ -193,7 +194,7 @@ GEM
|
|||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
commonjs (0.2.7)
|
||||
concurrent-ruby (1.2.2)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.3.0)
|
||||
content_for_in_controllers (0.0.2)
|
||||
crass (1.0.6)
|
||||
|
@ -217,7 +218,7 @@ GEM
|
|||
diff-lcs (1.5.0)
|
||||
diffy (3.4.2)
|
||||
docile (1.4.0)
|
||||
doorkeeper (5.6.6)
|
||||
doorkeeper (5.6.2)
|
||||
railties (>= 5)
|
||||
doorkeeper-i18n (5.2.6)
|
||||
doorkeeper (>= 5.2)
|
||||
|
@ -239,7 +240,7 @@ GEM
|
|||
ffi (1.15.5)
|
||||
gaffe (1.2.0)
|
||||
rails (>= 4.0.0)
|
||||
globalid (1.0.1)
|
||||
globalid (1.0.0)
|
||||
activesupport (>= 5.0)
|
||||
haml (5.2.2)
|
||||
temple (>= 0.8.0)
|
||||
|
@ -254,7 +255,7 @@ GEM
|
|||
activesupport (>= 5.2)
|
||||
hashie (3.4.6)
|
||||
htmlentities (4.3.4)
|
||||
i18n (1.14.1)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.0.11)
|
||||
i18n (>= 0.6.6, < 2)
|
||||
|
@ -305,14 +306,11 @@ GEM
|
|||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.21.3)
|
||||
loofah (2.19.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
mailcatcher (0.2.4)
|
||||
eventmachine
|
||||
haml
|
||||
|
@ -332,18 +330,13 @@ GEM
|
|||
mime-types-data (3.2022.0105)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.18.0)
|
||||
minitest (5.17.0)
|
||||
mono_logger (1.1.1)
|
||||
msgpack (1.6.0)
|
||||
multi_json (1.15.0)
|
||||
mustermann (3.0.0)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
mysql2 (0.5.4)
|
||||
net-ftp (0.2.0)
|
||||
net-protocol
|
||||
time
|
||||
net-http (0.3.2)
|
||||
uri
|
||||
net-imap (0.3.4)
|
||||
date
|
||||
net-protocol
|
||||
|
@ -353,11 +346,11 @@ GEM
|
|||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.2-x86_64-linux)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.10-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.1)
|
||||
parallel (1.22.1)
|
||||
parser (3.2.0.0)
|
||||
ast (~> 2.4.1)
|
||||
pdf-core (0.9.0)
|
||||
polyglot (0.3.5)
|
||||
|
@ -376,38 +369,41 @@ GEM
|
|||
binding_of_caller (~> 1.0)
|
||||
pry (~> 0.13)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.3.1)
|
||||
puma (6.0.2)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.7.0)
|
||||
rack (2.2.7)
|
||||
racc (1.6.2)
|
||||
rack (2.2.5)
|
||||
rack-cors (1.1.1)
|
||||
rack (>= 2.0.0)
|
||||
rack-protection (3.0.5)
|
||||
rack
|
||||
rack-test (2.1.0)
|
||||
rack-test (2.0.2)
|
||||
rack (>= 1.3)
|
||||
rails (7.0.8)
|
||||
actioncable (= 7.0.8)
|
||||
actionmailbox (= 7.0.8)
|
||||
actionmailer (= 7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actiontext (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
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)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.8)
|
||||
railties (= 7.0.4)
|
||||
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)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.6.0)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
rails-html-sanitizer (1.4.4)
|
||||
loofah (~> 2.19, >= 2.19.1)
|
||||
rails-i18n (7.0.6)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
|
@ -415,9 +411,9 @@ GEM
|
|||
rails (>= 4.2.0)
|
||||
rails_tokeninput (1.7.0)
|
||||
railties (>= 3.1.0)
|
||||
railties (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
railties (7.0.4)
|
||||
actionpack (= 7.0.4)
|
||||
activesupport (= 7.0.4)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
|
@ -438,7 +434,7 @@ GEM
|
|||
redis-namespace (1.10.0)
|
||||
redis (>= 4)
|
||||
ref (2.0.0)
|
||||
regexp_parser (2.8.0)
|
||||
regexp_parser (2.6.1)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
|
@ -480,7 +476,7 @@ GEM
|
|||
rspec-support (3.12.0)
|
||||
rswag-api (2.7.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rswag-specs (2.9.0)
|
||||
rswag-specs (2.7.0)
|
||||
activesupport (>= 3.1, < 7.1)
|
||||
json-schema (>= 2.2, < 4.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
|
@ -488,18 +484,18 @@ GEM
|
|||
rswag-ui (2.7.0)
|
||||
actionpack (>= 3.1, < 7.1)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.50.2)
|
||||
rubocop (1.43.0)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.0, < 2.0)
|
||||
rubocop-ast (>= 1.24.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.28.1)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-ast (1.24.1)
|
||||
parser (>= 3.1.1.0)
|
||||
rubocop-rails (2.17.4)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
|
@ -509,7 +505,7 @@ GEM
|
|||
ruby-filemagic (0.7.3)
|
||||
ruby-ole (1.2.12.2)
|
||||
ruby-prof (1.4.5)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-units (3.0.0)
|
||||
ruby-vips (2.1.4)
|
||||
ffi (~> 1.12)
|
||||
|
@ -525,6 +521,7 @@ GEM
|
|||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
sd_notify (0.1.1)
|
||||
select2-rails (4.0.13)
|
||||
simple-navigation (3.14.0)
|
||||
activesupport (>= 2.3.2)
|
||||
|
@ -572,10 +569,8 @@ GEM
|
|||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thor (1.2.2)
|
||||
thor (1.2.1)
|
||||
tilt (2.0.11)
|
||||
time (0.2.2)
|
||||
date
|
||||
timeout (0.3.1)
|
||||
ttfunk (1.7.0)
|
||||
twitter-bootstrap-rails (2.2.8)
|
||||
|
@ -585,14 +580,13 @@ GEM
|
|||
railties (>= 3.1)
|
||||
twitter-text (1.14.7)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (2.0.6)
|
||||
tzinfo (2.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (2.4.2)
|
||||
uniform_notifier (1.16.0)
|
||||
uri (0.10.0.2)
|
||||
web-console (4.2.0)
|
||||
actionview (>= 6.0.0)
|
||||
activemodel (>= 6.0.0)
|
||||
|
@ -611,7 +605,7 @@ GEM
|
|||
twitter-text
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.8)
|
||||
zeitwerk (2.6.6)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
@ -637,7 +631,7 @@ DEPENDENCIES
|
|||
exception_notification
|
||||
factory_bot_rails
|
||||
faker
|
||||
foodsoft_automatic_invoices!
|
||||
foodsoft_article_import!
|
||||
foodsoft_discourse!
|
||||
foodsoft_documents!
|
||||
foodsoft_links!
|
||||
|
@ -658,20 +652,20 @@ DEPENDENCIES
|
|||
kaminari
|
||||
less-rails
|
||||
listen
|
||||
mail (~> 2.7.1)
|
||||
mailcatcher
|
||||
midi-smtp-server
|
||||
mime-types
|
||||
mysql2
|
||||
net-ftp
|
||||
net-http
|
||||
prawn
|
||||
prawn-table
|
||||
pry-rescue
|
||||
pry-stack_explorer
|
||||
puma
|
||||
rack-cors
|
||||
rails (~> 7.0, >= 7.0.4.1)
|
||||
rails (~> 7.0)
|
||||
rails-assets-listjs (= 0.2.0.beta.4)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rails-settings-cached (= 0.4.3)
|
||||
rails_tokeninput
|
||||
|
@ -693,6 +687,7 @@ DEPENDENCIES
|
|||
ruby-prof
|
||||
ruby-units
|
||||
sassc-rails
|
||||
sd_notify
|
||||
select2-rails
|
||||
simple-navigation (~> 3.14.0)
|
||||
simple-navigation-bootstrap
|
||||
|
|
147
README.md
147
README.md
|
@ -1,71 +1,124 @@
|
|||
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)
|
||||
[Website](https://foodsoft.local-it.org)
|
||||
[Prototypefund](https://prototypefund.de/project/weiterentwicklung-von-foodsoft/)
|
||||
|
||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
||||
|
||||
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 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.
|
||||
|
||||
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.
|
||||
Foodsoft wurde ursprünglich entwickelt und betrieben von [foodcoops.net](https://foodcoops.net/)
|
||||
|
||||
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
#### Zielgruppe
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Developing
|
||||
----------
|
||||
#### Vorhaben
|
||||
|
||||
> 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.
|
||||
* ✅ Technische Schuld reduzieren
|
||||
* ✅ Ruby on Rails Upgrade
|
||||
* ✅ Artikel Import verbessern
|
||||
(Großhandelschnitstelle)
|
||||
* ✅ Userexperience Verbessern
|
||||
|
||||
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.
|
||||
#### Was ist eine Einkaufskooperative?
|
||||
|
||||
Get a foodsoft dev-environment running in the browser with Gitpod
|
||||
![Wie funktioniert eine Einkauskooperative?](./doc/foodcoop-explained.jpg)
|
||||
|
||||
[![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.
|
||||
|
||||
Deploying
|
||||
---------
|
||||
State of this Fork
|
||||
------------------
|
||||
|
||||
Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing
|
||||
[hosting platform](https://foodcoops.net/foodsoft-hosting/).
|
||||
#### Increase Test Coverage
|
||||
|
||||
License
|
||||
-------
|
||||
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)
|
||||
|
||||
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.
|
||||
#### Upgrade
|
||||
|
||||
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).
|
||||
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
|
||||
|
||||
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.
|
||||
#### 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)
|
||||
|
||||
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', __dir__)
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
require 'rake'
|
||||
require 'rspec-rerun/tasks' if defined?(RSpec) # http://stackoverflow.com/a/16853615/2866660
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
4.8.99
|
||||
4.7.99
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
//= 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,17 +179,13 @@ 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')
|
||||
}
|
||||
// update bgcolor
|
||||
for (i in itemTotal) {
|
||||
$('#td_price_' + i).css('background-color', bgcolor);
|
||||
$('#balance-alert').css('display', 'none')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ table {
|
|||
margin: .5em 0;
|
||||
|
||||
input:disabled {
|
||||
background-color: red; }
|
||||
background-color: gray; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,6 +241,9 @@ table {
|
|||
tr.order-article:hover .article-info {
|
||||
display: none;
|
||||
}
|
||||
tr.order-article:focus .article-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#order-footer {
|
||||
|
@ -275,10 +278,13 @@ tr.order-article .article-info {
|
|||
display: none;
|
||||
}
|
||||
|
||||
tr.order-article:hover .article-info {
|
||||
tr.order-article:focus .article-info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
tr.order-article:focus {
|
||||
background-color: #E9E9E9;
|
||||
}
|
||||
|
||||
// ********* Articles
|
||||
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
.list .missing-many td, .list .missing-many:hover td {
|
||||
background-color: #ebbebe;
|
||||
.missing-many td {
|
||||
background-color: #ffc590aa;
|
||||
}
|
||||
|
||||
.list .missing-few td, .list .missing-few:hover td {
|
||||
background-color: #ffee75;
|
||||
.missing-many:hover td, .missing-many:focus td {
|
||||
background-color: #ffc590;
|
||||
}
|
||||
|
||||
.list .missing-none td, .list .missing-none:hover td {
|
||||
background-color: #E4EED6;
|
||||
.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;
|
||||
}
|
||||
|
|
|
@ -3,39 +3,39 @@ class Admin::BankAccountsController < Admin::BaseController
|
|||
|
||||
def new
|
||||
@bank_account = BankAccount.new(params[:bank_account])
|
||||
render layout: false
|
||||
end
|
||||
|
||||
def edit
|
||||
@bank_account = BankAccount.find(params[:id])
|
||||
render action: 'new', layout: false
|
||||
render :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: :see_other
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
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: :see_other
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
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: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
render template: 'shared/alert'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,11 +6,6 @@ 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
|
||||
|
@ -20,6 +15,11 @@ 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: %i[show list]
|
||||
before_action :get_tabs, only: [: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)
|
||||
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
|
||||
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
|
||||
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 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
|
||||
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 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: :see_other
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
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: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
render template: 'shared/alert'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,25 +7,25 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
|
|||
render layout: false
|
||||
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
|
||||
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 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: :see_other
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
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: :see_other
|
||||
rescue StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||
rescue => error
|
||||
flash.now[:alert] = error.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]) if params[:email].present?
|
||||
@maildeliverystatus = @maildeliverystatus.where(email: params[:email]) unless params[:email].blank?
|
||||
@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 StandardError => e
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
||||
@maildeliverystatus.destroy
|
||||
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,15 +2,16 @@ 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:
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present?
|
||||
unless params[:query].blank?
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%")
|
||||
end
|
||||
|
||||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -18,8 +19,8 @@ class Admin::OrdergroupsController < Admin::BaseController
|
|||
def destroy
|
||||
@ordergroup = Ordergroup.find(params[:id])
|
||||
@ordergroup.mark_as_deleted
|
||||
redirect_to admin_ordergroups_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_ordergroups_url, alert: t('.error')
|
||||
redirect_to admin_ordergroups_url, notice: t('admin.ordergroups.destroy.notice')
|
||||
rescue => error
|
||||
redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,11 +6,6 @@ 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
|
||||
|
@ -20,6 +15,11 @@ 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,14 +3,16 @@ 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)
|
||||
|
||||
send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv' if request.format.csv?
|
||||
if request.format.csv?
|
||||
send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv'
|
||||
end
|
||||
|
||||
# if somebody uses the search field:
|
||||
@users = @users.natural_search(params[:user_name]) if params[:user_name].present?
|
||||
@users = @users.natural_search(params[:user_name]) unless params[:user_name].blank?
|
||||
|
||||
@users = @users.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -18,17 +20,17 @@ class Admin::UsersController < Admin::BaseController
|
|||
def destroy
|
||||
@user = User.find(params[:id])
|
||||
@user.mark_as_deleted
|
||||
redirect_to admin_users_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
||||
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)
|
||||
end
|
||||
|
||||
def restore
|
||||
@user = User.find(params[:id])
|
||||
@user.restore
|
||||
redirect_to admin_users_url, notice: t('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
||||
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)
|
||||
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]}%") if params[:query].present?
|
||||
@workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].blank?
|
||||
|
||||
@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('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to admin_workgroups_url, alert: t('.error', error: e.message)
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,30 +20,29 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
def require_ordergroup
|
||||
authenticate
|
||||
return if current_ordergroup.present?
|
||||
|
||||
raise Api::Errors::PermissionRequired, 'Forbidden, must be in an ordergroup'
|
||||
unless current_ordergroup.present?
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup')
|
||||
end
|
||||
end
|
||||
|
||||
def require_minimum_balance
|
||||
minimum_balance = FoodsoftConfig[:minimum_balance] or return
|
||||
return unless current_ordergroup.account_balance < minimum_balance
|
||||
|
||||
raise Api::Errors::PermissionRequired, t('application.controller.error_minimum_balance', min: minimum_balance)
|
||||
if current_ordergroup.account_balance < minimum_balance
|
||||
raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance))
|
||||
end
|
||||
end
|
||||
|
||||
def require_enough_apples
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
def require_config_enabled(config)
|
||||
return if FoodsoftConfig[config]
|
||||
|
||||
raise Api::Errors::PermissionRequired, t('application.controller.error_not_enabled', config: config)
|
||||
unless FoodsoftConfig[config]
|
||||
raise Api::Errors::PermissionRequired.new(t('application.controller.error_not_enabled', config: config))
|
||||
end
|
||||
end
|
||||
|
||||
def skip_session
|
||||
|
@ -53,12 +52,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: :not_found, json: { error: 'not_found', error_description: msg }
|
||||
render status: 404, json: { error: 'not_found', error_description: msg }
|
||||
end
|
||||
|
||||
def not_acceptable_handler(e)
|
||||
msg = e.message || 'Data not acceptable'
|
||||
render status: :unprocessable_entity, json: { error: 'not_acceptable', error_description: msg }
|
||||
render status: 422, json: { error: 'not_acceptable', error_description: msg }
|
||||
end
|
||||
|
||||
def doorkeeper_unauthorized_render_options(error:)
|
||||
|
@ -71,11 +70,11 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
def permission_required_handler(e)
|
||||
msg = e.message || 'Forbidden, user has no access'
|
||||
render status: :forbidden, json: { error: 'forbidden', error_description: msg }
|
||||
render status: 403, 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,8 +16,7 @@ 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: %i[create update] # destroy is ok
|
||||
before_action :require_enough_apples, only: %i[create update] # destroy is ok
|
||||
before_action :require_minimum_balance, only: [:create, :update] # destroy is ok
|
||||
before_action :require_enough_apples, only: [:create, :update] # destroy is ok
|
||||
# @todo allow decreasing amounts when minimum balance isn't met
|
||||
|
||||
def index
|
||||
|
@ -35,8 +35,7 @@ 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 do |c|
|
||||
financial_transaction_class_sums: FinancialTransactionClass.sorted.map { |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
|
||||
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
|
||||
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
|
||||
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
|
||||
@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
|
||||
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
|
||||
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 StandardError => e
|
||||
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: e.message)
|
||||
rescue => error
|
||||
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -2,24 +2,24 @@ class ArticlesController < ApplicationController
|
|||
before_action :authenticate_article_meta, :find_supplier
|
||||
|
||||
def index
|
||||
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'
|
||||
if params['sort']
|
||||
sort = case params['sort']
|
||||
when "name" then "articles.name"
|
||||
when "unit" then "articles.unit"
|
||||
when "article_category" then "article_categories.name"
|
||||
when "note" then "articles.note"
|
||||
when "availability" then "articles.availability"
|
||||
when "name_reverse" then "articles.name DESC"
|
||||
when "unit_reverse" then "articles.unit DESC"
|
||||
when "article_category_reverse" then "article_categories.name DESC"
|
||||
when "note_reverse" then "articles.note DESC"
|
||||
when "availability_reverse" then "articles.availability DESC"
|
||||
end
|
||||
else
|
||||
'article_categories.name, articles.name'
|
||||
end
|
||||
else
|
||||
sort = "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,17 +87,19 @@ class ArticlesController < ApplicationController
|
|||
def update_all
|
||||
invalid_articles = false
|
||||
|
||||
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
|
||||
begin
|
||||
Article.transaction do
|
||||
unless params[:articles].blank?
|
||||
# Update other article attributes...
|
||||
@articles = Article.find(params[:articles].keys)
|
||||
@articles.each do |article|
|
||||
unless article.update(params[:articles][article.id.to_s])
|
||||
invalid_articles = true unless invalid_articles # Remember that there are validation errors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -132,31 +134,32 @@ class ArticlesController < ApplicationController
|
|||
end
|
||||
end
|
||||
# action succeded
|
||||
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)
|
||||
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page])
|
||||
rescue => error
|
||||
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]),
|
||||
:alert => I18n.t('errors.general_msg', :msg => error)
|
||||
end
|
||||
|
||||
# lets start with parsing articles from uploaded file, yeah
|
||||
# Renders the upload form
|
||||
def upload; end
|
||||
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')
|
||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile,
|
||||
options
|
||||
options[:update_category] = (params[:articles]['update_category'] == '1')
|
||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, type, options
|
||||
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||
redirect_to supplier_articles_path(@supplier),
|
||||
notice: I18n.t('articles.controller.parse_upload.notice')
|
||||
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice')
|
||||
end
|
||||
@ignored_article_count = 0
|
||||
rescue StandardError => e
|
||||
redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message)
|
||||
end
|
||||
|
||||
# sync all articles with the external database
|
||||
|
@ -164,14 +167,13 @@ 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
|
||||
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')
|
||||
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
|
||||
end
|
||||
end
|
||||
|
||||
# Updates, deletes articles when upload or sync form is submitted
|
||||
|
@ -186,7 +188,7 @@ class ArticlesController < ApplicationController
|
|||
# delete articles
|
||||
begin
|
||||
@outlisted_articles.each(&:mark_as_deleted)
|
||||
rescue StandardError
|
||||
rescue
|
||||
# raises an exception when used in current order
|
||||
has_error = true
|
||||
end
|
||||
|
@ -198,15 +200,15 @@ class ArticlesController < ApplicationController
|
|||
raise ActiveRecord::Rollback if has_error
|
||||
end
|
||||
|
||||
if has_error
|
||||
if !has_error
|
||||
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice')
|
||||
else
|
||||
@updated_article_pairs = @updated_articles.map do |article|
|
||||
orig_article = Article.find(article.id)
|
||||
[article, orig_article.unequal_attributes(article)]
|
||||
end
|
||||
flash.now.alert = I18n.t('articles.controller.error_invalid')
|
||||
render params[:from_action] == 'sync' ? :sync : :parse_upload
|
||||
else
|
||||
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -218,18 +220,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] if params[:article_category_id].present?
|
||||
if params[:direct] && params[:article_category_id].present? && @article.valid? && @article.save
|
||||
render action: 'create', layout: false
|
||||
@article.article_category_id = params[:article_category_id] unless params[:article_category_id].blank?
|
||||
if params[:direct] && !params[:article_category_id].blank? && @article.valid? && @article.save
|
||||
render :action => 'create', :layout => false
|
||||
else
|
||||
render action: 'new', layout: false
|
||||
render :action => 'new', :layout => false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,19 +9,15 @@ module Concerns::Auth
|
|||
|
||||
def current_user
|
||||
# check if there is a valid session and return the logged-in user (its object)
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -51,7 +47,12 @@ module Concerns::Auth
|
|||
|
||||
def authenticate(role = 'any')
|
||||
# Attempt to retrieve authenticated user from controller instance or session...
|
||||
if current_user
|
||||
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
|
||||
# We have an authenticated user, now check role...
|
||||
# Roles gets the user through his memberships.
|
||||
hasRole = case role
|
||||
|
@ -72,11 +73,6 @@ 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
|
||||
|
||||
|
@ -120,13 +116,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)
|
||||
return if @group.member?(@current_user) || @current_user.role_admin?
|
||||
|
||||
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
||||
unless @group.member?(@current_user) || @current_user.role_admin?
|
||||
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate_or_token(prefix, role = 'any')
|
||||
if params[:token].present?
|
||||
if not params[:token].blank?
|
||||
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)
|
||||
return if scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
||||
|
||||
raise Api::Errors::PermissionRequired, 'Forbidden, no permission'
|
||||
unless scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, no permission')
|
||||
end
|
||||
end
|
||||
|
||||
# Check whether a given OAuth scope is permitted for the current user.
|
||||
|
@ -48,7 +48,9 @@ module Concerns::AuthApi
|
|||
def doorkeeper_scope_permitted?(scope)
|
||||
scope_parts = scope.split(':')
|
||||
# user sub-scopes like +config:user+ are always permitted
|
||||
return true if scope_parts.last == 'user'
|
||||
if scope_parts.last == 'user'
|
||||
return true
|
||||
end
|
||||
|
||||
case scope_parts.first
|
||||
when 'user' then return true # access to the current user's own profile
|
||||
|
@ -62,8 +64,8 @@ module Concerns::AuthApi
|
|||
end
|
||||
|
||||
case scope
|
||||
when 'orders:read' then true
|
||||
when 'orders:write' then current_user.role_orders?
|
||||
when 'orders:read' then return true
|
||||
when 'orders:write' then return 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, 'Foodcoop Not Found'
|
||||
raise ActionController::RoutingError.new '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']&.scan(/^[a-z]{2}/)&.first
|
||||
request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil
|
||||
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 if language.present?
|
||||
language.presence&.to_sym unless language.blank?
|
||||
end
|
||||
|
||||
def available_locales
|
||||
|
@ -38,11 +38,11 @@ module Concerns::Locale
|
|||
end
|
||||
|
||||
def set_locale
|
||||
::I18n.locale = if available_locales.include?(select_language_according_to_priority)
|
||||
select_language_according_to_priority
|
||||
else
|
||||
default_language
|
||||
end
|
||||
if available_locales.include?(select_language_according_to_priority)
|
||||
::I18n.locale = select_language_according_to_priority
|
||||
else
|
||||
::I18n.locale = 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,10 +15,6 @@ 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])
|
||||
|
||||
|
@ -26,10 +22,14 @@ 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,12 +1,13 @@
|
|||
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('.notice')
|
||||
redirect_to root_url, notice: t('feedback.create.notice')
|
||||
else
|
||||
render action: 'new'
|
||||
render :action => 'new'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,39 +5,39 @@ class Finance::BalancingController < Finance::BaseController
|
|||
|
||||
def new
|
||||
@order = Order.find(params[:order_id])
|
||||
flash.now.alert = t('.alert') if @order.closed?
|
||||
flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
|
||||
@comments = @order.comments
|
||||
|
||||
|
||||
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
||||
group_order_articles: { group_order: :ordergroup })
|
||||
|
||||
sort_param = params['sort'] || 'name'
|
||||
@articles = case sort_param
|
||||
when 'name'
|
||||
when 'name' then
|
||||
@articles.order('articles.name ASC')
|
||||
when 'name_reverse'
|
||||
when 'name_reverse' then
|
||||
@articles.order('articles.name DESC')
|
||||
when 'order_number'
|
||||
when 'order_number' then
|
||||
@articles.order('articles.order_number ASC')
|
||||
when 'order_number_reverse'
|
||||
when 'order_number_reverse' then
|
||||
@articles.order('articles.order_number DESC')
|
||||
else
|
||||
@articles
|
||||
end
|
||||
|
||||
render layout: false if request.xhr?
|
||||
end
|
||||
|
||||
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 StandardError => e
|
||||
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: error.message)
|
||||
end
|
||||
|
||||
# before the order will booked, a view lists all Ordergroups and its order_prices
|
||||
|
@ -81,18 +81,18 @@ 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('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.message)
|
||||
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
|
||||
rescue => error
|
||||
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: 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('.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to finance_order_index_url, alert: t('.alert', message: e.message)
|
||||
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)
|
||||
end
|
||||
|
||||
def close_all_direct_with_invoice
|
||||
|
@ -103,8 +103,8 @@ class Finance::BalancingController < Finance::BaseController
|
|||
count += 1
|
||||
end
|
||||
end
|
||||
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)
|
||||
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)
|
||||
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 StandardError => e
|
||||
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: error.message)
|
||||
end
|
||||
|
||||
def import
|
||||
|
@ -33,8 +33,8 @@ class Finance::BankAccountsController < Finance::BaseController
|
|||
end
|
||||
|
||||
needs_redirect = ok
|
||||
rescue StandardError => e
|
||||
flash.alert = t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
flash.alert = t('errors.general_msg', msg: error.message)
|
||||
needs_redirect = true
|
||||
ensure
|
||||
return unless needs_redirect
|
||||
|
|
|
@ -3,30 +3,26 @@ class Finance::BankTransactionsController < ApplicationController
|
|||
inherit_resources
|
||||
|
||||
def index
|
||||
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'
|
||||
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"
|
||||
end
|
||||
else
|
||||
'date DESC'
|
||||
end
|
||||
else
|
||||
sort = "date DESC"
|
||||
end
|
||||
|
||||
@bank_account = BankAccount.find(params[:bank_account_id])
|
||||
@bank_transactions_all = @bank_account.bank_transactions.order(sort).includes(:financial_link)
|
||||
unless params[:query].nil?
|
||||
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%",
|
||||
"%#{params[:query]}%")
|
||||
end
|
||||
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%", "%#{params[:query]}%") unless params[:query].nil?
|
||||
@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: %i[create incomplete]
|
||||
before_action :find_financial_link, except: [: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]
|
||||
if params[:bank_transaction] then
|
||||
bank_transaction = BankTransaction.find(params[:bank_transaction])
|
||||
bank_transaction.update_attribute :financial_link, @financial_link
|
||||
end
|
||||
|
@ -72,16 +72,14 @@ 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 StandardError => e
|
||||
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: e)
|
||||
rescue => error
|
||||
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: error)
|
||||
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
|
||||
|
@ -125,7 +123,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).pick(:ordergroup_id)
|
||||
FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pluck(:ordergroup_id).first
|
||||
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: %i[new_collection create_collection index_collection]
|
||||
before_action :find_ordergroup, :except => [:new_collection, :create_collection, :index_collection]
|
||||
inherit_resources
|
||||
# belongs_to :ordergroup
|
||||
|
||||
def index
|
||||
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'
|
||||
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"
|
||||
end
|
||||
else
|
||||
'created_on DESC'
|
||||
end
|
||||
else
|
||||
sort = "created_on DESC"
|
||||
end
|
||||
|
||||
@q = FinancialTransaction.ransack(params[:q])
|
||||
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
|
||||
|
@ -26,11 +26,9 @@ 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
|
||||
|
@ -40,11 +38,11 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@financial_transaction = if @ordergroup
|
||||
@ordergroup.financial_transactions.build
|
||||
else
|
||||
FinancialTransaction.new
|
||||
end
|
||||
if @ordergroup
|
||||
@financial_transaction = @ordergroup.financial_transactions.build
|
||||
else
|
||||
@financial_transaction = FinancialTransaction.new
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -55,18 +53,16 @@ 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 => e
|
||||
flash.now[:alert] = e.message
|
||||
render action: :new
|
||||
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
|
||||
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
|
||||
|
@ -92,17 +88,17 @@ class Finance::FinancialTransactionsController < ApplicationController
|
|||
|
||||
params[:financial_transactions].each do |trans|
|
||||
# ignore empty amount fields ...
|
||||
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)
|
||||
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
|
||||
end
|
||||
ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link)
|
||||
foodcoop_amount -= amount
|
||||
end
|
||||
|
||||
if params[:create_foodcoop_transaction]
|
||||
|
@ -111,7 +107,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
|
||||
|
@ -121,8 +117,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 StandardError => e
|
||||
flash.now[:alert] = e.message
|
||||
rescue => error
|
||||
flash.now[:alert] = error.message
|
||||
render action: :new_collection
|
||||
end
|
||||
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
class Finance::InvoicesController < ApplicationController
|
||||
before_action :authenticate_finance_or_invoices
|
||||
|
||||
before_action :find_invoice, only: %i[show edit update destroy]
|
||||
before_action :ensure_can_edit, only: %i[edit update destroy]
|
||||
before_action :find_invoice, only: [:show, :edit, :update, :destroy]
|
||||
before_action :ensure_can_edit, only: [: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
|
||||
|
@ -21,10 +20,11 @@ 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,14 +36,12 @@ 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
|
||||
|
@ -60,7 +58,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
|
||||
|
||||
|
@ -83,7 +81,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
|
||||
|
@ -94,8 +92,8 @@ class Finance::InvoicesController < ApplicationController
|
|||
|
||||
# Returns true if @current_user can edit the invoice..
|
||||
def ensure_can_edit
|
||||
return if @invoice.user_can_edit?(current_user)
|
||||
|
||||
deny_access
|
||||
unless @invoice.user_can_edit?(current_user)
|
||||
deny_access
|
||||
end
|
||||
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,16 +1,20 @@
|
|||
class Foodcoop::OrdergroupsController < ApplicationController
|
||||
def index
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params['sort'])
|
||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"])
|
||||
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%") if params[:name].present? # Search by name
|
||||
unless params[:name].blank? # Search by name
|
||||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%")
|
||||
end
|
||||
|
||||
@ordergroups = @ordergroups.active if params[:only_active] # Select only active groups
|
||||
if params[:only_active] # Select only active groups
|
||||
@ordergroups = @ordergroups.active
|
||||
end
|
||||
|
||||
@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,22 +1,19 @@
|
|||
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]) if params[:user_name].present?
|
||||
@users = @users.natural_search(params[:user_name]) unless params[:user_name].blank?
|
||||
|
||||
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: %i[new create]
|
||||
before_action :find_group_order_article, except: [: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: %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]
|
||||
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]
|
||||
|
||||
# Index page.
|
||||
def index
|
||||
|
@ -13,17 +13,9 @@ 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)
|
||||
@ordering_data = @group_order.load_data
|
||||
end
|
||||
|
||||
def edit
|
||||
@group_order = @order.group_orders.build(:ordergroup => ordergroup, :updated_by => current_user)
|
||||
@ordering_data = @group_order.load_data
|
||||
end
|
||||
|
||||
|
@ -31,26 +23,34 @@ 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 StandardError => e
|
||||
logger.error('Failed to update order: ' + e.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 => exception
|
||||
logger.error('Failed to update order: ' + exception.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 StandardError => e
|
||||
logger.error('Failed to update order: ' + e.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 => exception
|
||||
logger.error('Failed to update order: ' + exception.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
|
||||
return unless @ordergroup.nil?
|
||||
|
||||
redirect_to root_url, alert: I18n.t('group_orders.errors.no_member')
|
||||
if @ordergroup.nil?
|
||||
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_open_order
|
||||
@order = Order.includes(%i[supplier order_articles]).find(order_id_param)
|
||||
@order = Order.includes([: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])
|
||||
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')
|
||||
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
|
||||
end
|
||||
|
||||
def enough_apples?
|
||||
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])
|
||||
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
|
||||
end
|
||||
|
||||
def order_id_param
|
||||
|
|
|
@ -9,7 +9,8 @@ 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
|
||||
|
@ -17,7 +18,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_url, alert: I18n.t('group_orders.errors.no_member')
|
||||
redirect_to root_path, alert: I18n.t('group_orders.errors.no_member')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -25,7 +26,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_url, notice: I18n.t('home.changes_saved')
|
||||
redirect_to my_profile_path, notice: I18n.t('home.changes_saved')
|
||||
else
|
||||
render :profile
|
||||
end
|
||||
|
@ -35,43 +36,40 @@ class HomeController < ApplicationController
|
|||
@user = @current_user
|
||||
@ordergroup = @user.ordergroup
|
||||
|
||||
if @ordergroup.nil?
|
||||
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
|
||||
else
|
||||
unless @ordergroup.nil?
|
||||
|
||||
@ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id)
|
||||
|
||||
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'
|
||||
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"
|
||||
end
|
||||
else
|
||||
'created_on DESC'
|
||||
end
|
||||
|
||||
@financial_transactions = @ordergroup.financial_transactions.visible.page(params[:page]).per(@per_page).order(sort)
|
||||
if params[:query].present?
|
||||
@financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?',
|
||||
"%#{params[:query]}%")
|
||||
else
|
||||
sort = "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?
|
||||
|
||||
else
|
||||
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
|
||||
end
|
||||
end
|
||||
|
||||
# cancel personal memberships direct from the myProfile-page
|
||||
def cancel_membership
|
||||
membership = if params[:membership_id]
|
||||
@current_user.memberships.find(params[:membership_id])
|
||||
else
|
||||
@current_user.memberships.find_by_group_id!(params[:group_id])
|
||||
end
|
||||
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.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
|
||||
|
@ -84,8 +82,8 @@ class HomeController < ApplicationController
|
|||
end
|
||||
|
||||
def ordergroup_params
|
||||
return unless params[:user][:ordergroup]
|
||||
|
||||
params.require(:user).require(:ordergroup).permit(:contact_address)
|
||||
if params[:user][:ordergroup]
|
||||
params.require(:user).require(:ordergroup).permit(:contact_address)
|
||||
end
|
||||
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,10 +27,6 @@ class InvitesController < ApplicationController
|
|||
protected
|
||||
|
||||
def authenticate_membership_or_admin_for_invites
|
||||
authenticate_membership_or_admin(begin
|
||||
params[:invite][:group_id]
|
||||
rescue StandardError
|
||||
params[:id]
|
||||
end)
|
||||
authenticate_membership_or_admin((params[:invite][:group_id] rescue params[:id]))
|
||||
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: %i[new_password update_password]
|
||||
before_action :validate_token, :only => [:new_password, :update_password]
|
||||
|
||||
# Display the form to enter an email address requesting a token to set a new password.
|
||||
def forgot_password
|
||||
|
@ -9,17 +9,20 @@ class LoginController < ApplicationController
|
|||
|
||||
# Sends an email to a user with the token that allows setting a new password through action "password".
|
||||
def reset_password
|
||||
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 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
|
||||
|
||||
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.
|
||||
|
@ -29,7 +32,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
|
||||
|
@ -47,14 +50,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
|
||||
|
||||
|
@ -62,8 +65,8 @@ class LoginController < ApplicationController
|
|||
|
||||
def validate_token
|
||||
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
||||
return unless @user.nil? || @user.reset_password_expires < Time.now
|
||||
|
||||
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||
if (@user.nil? || @user.reset_password_expires < Time.now)
|
||||
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class OrderArticlesController < ApplicationController
|
||||
before_action :fetch_order, except: :destroy
|
||||
before_action :authenticate_finance_or_invoices, except: %i[new create]
|
||||
before_action :authenticate_finance_orders_or_pickup, except: %i[edit update destroy]
|
||||
before_action :authenticate_finance_or_invoices, except: [:new, :create]
|
||||
before_action :authenticate_finance_orders_or_pickup, except: [:edit, :update, :destroy]
|
||||
|
||||
layout false # We only use this controller to serve js snippets, no need for layout rendering
|
||||
|
||||
|
@ -9,26 +9,28 @@ 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
|
||||
@order_article = @order.order_articles.build(params[:order_article]) unless @order_article && @order_article.units_to_order == 0
|
||||
@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.save!
|
||||
rescue StandardError
|
||||
rescue
|
||||
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 StandardError
|
||||
rescue
|
||||
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,26 +5,25 @@ class OrdersController < ApplicationController
|
|||
include Concerns::SendOrderPdf
|
||||
|
||||
before_action :authenticate_pickups_or_orders
|
||||
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]
|
||||
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]
|
||||
|
||||
# List orders
|
||||
def index
|
||||
@open_orders = Order.open.includes(:supplier)
|
||||
@finished_orders = Order.finished_not_closed.includes(:supplier)
|
||||
@per_page = 15
|
||||
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'
|
||||
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"
|
||||
end
|
||||
else
|
||||
'ends DESC'
|
||||
end
|
||||
else
|
||||
sort = "ends DESC"
|
||||
end
|
||||
@suppliers = Supplier.having_articles.order('suppliers.name')
|
||||
@orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
@ -44,13 +43,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).to_csv, filename: @order.name + '.csv', type: 'text/csv'
|
||||
send_data OrderCsv.new(@order, options= {custom_csv: params[:custom_csv]}).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'
|
||||
|
@ -58,6 +57,19 @@ 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]
|
||||
|
@ -67,14 +79,8 @@ class OrdersController < ApplicationController
|
|||
else
|
||||
@order = Order.new(supplier_id: params[:supplier_id]).init_dates
|
||||
end
|
||||
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])
|
||||
rescue => error
|
||||
redirect_to orders_url, alert: t('errors.general_msg', msg: error.message)
|
||||
end
|
||||
|
||||
# Save a new order.
|
||||
|
@ -88,25 +94,31 @@ 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.
|
||||
|
@ -114,8 +126,8 @@ class OrdersController < ApplicationController
|
|||
order = Order.find(params[:id])
|
||||
order.finish!(@current_user)
|
||||
redirect_to order, notice: I18n.t('orders.finish.notice')
|
||||
rescue StandardError => e
|
||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||
end
|
||||
|
||||
# Send a order to the supplier.
|
||||
|
@ -123,18 +135,20 @@ 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 StandardError => e
|
||||
redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => error
|
||||
redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||
end
|
||||
|
||||
def receive
|
||||
@order = Order.find(params[:id])
|
||||
if request.post?
|
||||
unless request.post?
|
||||
@order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name')
|
||||
else
|
||||
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?
|
||||
|
@ -144,25 +158,23 @@ 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 unless params[:order_articles]
|
||||
return if not params[:order_articles]
|
||||
|
||||
# where to leave remainder during redistribution
|
||||
rest_to = []
|
||||
|
@ -177,42 +189,35 @@ 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|
|
||||
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
|
||||
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 }
|
||||
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])
|
||||
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.msg2', count: counts[1], units: cunits[1]) if params[:rest_to_tolerance]
|
||||
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].compact_blank! if params[:order] && params[:order][:article_ids]
|
||||
params[:order][:article_ids].reject!(&:blank?) if params[:order] && params[:order][:article_ids]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,20 +12,16 @@ 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
|
||||
if FoodsoftConfig[:logout_redirect_url].present?
|
||||
redirect_to FoodsoftConfig[:logout_redirect_url], allow_other_host: true
|
||||
else
|
||||
redirect_to login_url, notice: I18n.t('sessions.logged_out')
|
||||
end
|
||||
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
||||
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,18 +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
|
||||
|
||||
def show
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# three possibilites to fill a new_stock_article form
|
||||
|
@ -26,63 +21,68 @@ class StockitController < ApplicationController
|
|||
def new
|
||||
@stock_article = StockArticle.new(params[:stock_article])
|
||||
|
||||
render layout: false
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
# (2) StockArticle as template
|
||||
def copy
|
||||
@stock_article = StockArticle.find(params[:stock_article_id]).dup
|
||||
|
||||
render layout: false
|
||||
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
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def create
|
||||
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
|
||||
@stock_article.save!
|
||||
render layout: false
|
||||
render :layout => false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render action: 'new', layout: false
|
||||
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
|
||||
render :layout => false
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render action: 'edit', layout: false
|
||||
render :action => 'edit', :layout => false
|
||||
end
|
||||
|
||||
def show
|
||||
@stock_article = StockArticle.find(params[:id])
|
||||
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
|
||||
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 StandardError => e
|
||||
render partial: 'destroy_fail', layout: false,
|
||||
locals: { fail_msg: I18n.t('errors.general_msg', msg: e.message) }
|
||||
render :layout => false
|
||||
rescue => error
|
||||
render :partial => "destroy_fail", :layout => false,
|
||||
:locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.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: :not_found
|
||||
render body: nil, content_type: 'text/css', status: 404
|
||||
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: %i[index list]
|
||||
before_action :authenticate_suppliers, :except => [:index, :list]
|
||||
helper :deliveries
|
||||
|
||||
def index
|
||||
|
@ -24,10 +24,6 @@ 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
|
||||
|
@ -35,17 +31,21 @@ 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 StandardError => e
|
||||
flash[:error] = I18n.t('errors.general_msg', msg: e.message)
|
||||
rescue => e
|
||||
flash[:error] = I18n.t('errors.general_msg', :msg => e.message)
|
||||
redirect_to @supplier
|
||||
end
|
||||
|
||||
|
|
|
@ -11,33 +11,35 @@ class TasksController < ApplicationController
|
|||
@accepted_tasks = Task.accepted_tasks_for(current_user)
|
||||
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 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')
|
||||
redirect_to tasks_url, :notice => I18n.t('tasks.create.notice')
|
||||
else
|
||||
render template: 'tasks/new'
|
||||
render :template => "tasks/new"
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@task = Task.find(params[:id])
|
||||
end
|
||||
|
||||
def edit
|
||||
@task = Task.find(params[:id])
|
||||
@periodic = !!params[:periodic]
|
||||
@task.current_user_id = current_user.id
|
||||
end
|
||||
|
||||
def update
|
||||
@task = Task.find(params[:id])
|
||||
task_group = @task.periodic_task_group
|
||||
|
@ -48,14 +50,16 @@ 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')
|
||||
flash[:notice] = I18n.t('tasks.update.notice_converted') if was_periodic && !@task.periodic?
|
||||
if was_periodic && !@task.periodic?
|
||||
flash[:notice] = I18n.t('tasks.update.notice_converted')
|
||||
end
|
||||
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
|
||||
|
||||
|
@ -71,7 +75,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"
|
||||
|
@ -81,20 +85,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
|
||||
|
@ -105,9 +109,9 @@ class TasksController < ApplicationController
|
|||
# shows workgroup (normal group) to edit weekly_tasks_template
|
||||
def workgroup
|
||||
@group = Group.find(params[:workgroup_id])
|
||||
return unless @group.is_a? Ordergroup
|
||||
|
||||
redirect_to tasks_url, alert: I18n.t('tasks.error_not_found')
|
||||
if @group.is_a? Ordergroup
|
||||
redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found')
|
||||
end
|
||||
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,18 +20,16 @@ class OrderFax < OrderPdf
|
|||
move_down 5
|
||||
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
if order.supplier.try(:customer_number).present?
|
||||
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}",
|
||||
size: fontsize(9), align: :right
|
||||
unless order.supplier.try(:customer_number).blank?
|
||||
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
end
|
||||
if contact[:phone].present?
|
||||
unless contact[:phone].blank?
|
||||
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :right
|
||||
move_down 5
|
||||
end
|
||||
if contact[:email].present?
|
||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9),
|
||||
align: :right
|
||||
unless contact[:email].blank?
|
||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :right
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,7 +38,7 @@ class OrderFax < OrderPdf
|
|||
text order.name
|
||||
move_down 5
|
||||
text order.supplier.try(:address).to_s
|
||||
if order.supplier.try(:fax).present?
|
||||
unless order.supplier.try(:fax).blank?
|
||||
move_down 5
|
||||
text "#{Supplier.human_attribute_name :fax}: #{order.supplier[:fax]}"
|
||||
end
|
||||
|
@ -52,7 +50,7 @@ class OrderFax < OrderPdf
|
|||
move_down 10
|
||||
text "#{Delivery.human_attribute_name :date}:"
|
||||
move_down 10
|
||||
if order.supplier.try(:contact_person).present?
|
||||
unless order.supplier.try(:contact_person).blank?
|
||||
text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
|
||||
move_down 10
|
||||
end
|
||||
|
@ -80,8 +78,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 = %i[top bottom]
|
||||
table.row(data.length - 1).columns(0).borders = %i[top bottom left]
|
||||
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).border_top_width = 2
|
||||
end
|
||||
# font_size: fontsize(8),
|
||||
|
@ -100,7 +98,7 @@ class OrderFax < OrderPdf
|
|||
.preload(:article, :article_price)
|
||||
end
|
||||
|
||||
def each_order_article(&block)
|
||||
order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block)
|
||||
def each_order_article
|
||||
order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa }
|
||||
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 = %i[bottom left]
|
||||
table.row(0).borders = [: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 = %w[dddddd ffffff]
|
||||
table.row_colors = ['dddddd', 'ffffff']
|
||||
end
|
||||
|
||||
first_page = false
|
||||
|
|
|
@ -28,11 +28,7 @@ module Admin::ConfigsHelper
|
|||
options[:default] = options[:input_html].delete(:value)
|
||||
return form.input key, options, &block
|
||||
end
|
||||
if options[:as] == :select_recurring
|
||||
block ||= proc {
|
||||
config_input_field form, key, options.merge(options[:input_html])
|
||||
}
|
||||
end
|
||||
block ||= proc { config_input_field form, key, options.merge(options[:input_html]) } if options[:as] == :select_recurring
|
||||
form.input key, options, &block
|
||||
end
|
||||
|
||||
|
@ -61,12 +57,11 @@ 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] if options[:value].present?
|
||||
options[:rules].unshift options[:value] unless options[:value].blank?
|
||||
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
|
||||
|
@ -78,7 +73,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 = {}, &block)
|
||||
def config_use_heading(form, key, options = {})
|
||||
head = content_tag :label do
|
||||
lbl = options[:label] || config_input_label(form, key)
|
||||
field = config_input_field(form, key, as: :boolean, boolean_style: :inline,
|
||||
|
@ -88,7 +83,9 @@ 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]}", &block)
|
||||
fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}") do
|
||||
yield
|
||||
end
|
||||
head + fields
|
||||
end
|
||||
|
||||
|
@ -130,7 +127,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: '')
|
||||
if tooltip.present?
|
||||
unless tooltip.blank?
|
||||
options[:data] ||= {}
|
||||
options[:data][:toggle] ||= 'tooltip'
|
||||
options[:data][:placement] ||= 'right'
|
||||
|
|
|
@ -2,7 +2,9 @@ module Admin::OrdergroupsHelper
|
|||
def ordergroup_members_title(ordergroup)
|
||||
s = ''
|
||||
s += ordergroup.users.map(&:name).join(', ') if ordergroup.users.any?
|
||||
s += "\n" + Ordergroup.human_attribute_name(:contact) + ': ' + ordergroup.contact_person if ordergroup.contact_person.present?
|
||||
if ordergroup.contact_person.present?
|
||||
s += "\n" + Ordergroup.human_attribute_name(:contact) + ": " + ordergroup.contact_person
|
||||
end
|
||||
s
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ module ApplicationHelper
|
|||
include PathHelper
|
||||
|
||||
def format_time(time = Time.now)
|
||||
I18n.l(time, format: :foodsoft_datetime) unless time.nil?
|
||||
I18n.l(time, :format => "%d.%m.%Y %H:%M") 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,19 +63,21 @@ module ApplicationHelper
|
|||
# Hmtl options
|
||||
remote = options[:remote].nil? ? true : options[:remote]
|
||||
class_name = case params[:sort]
|
||||
when key
|
||||
when key then
|
||||
'sortup'
|
||||
when key + '_reverse'
|
||||
when key + '_reverse' then
|
||||
'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]
|
||||
|
@ -93,16 +95,14 @@ 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| !%w[short desc].include?(a) })
|
||||
i18nopts = { count: 2 }.merge(options.select { |a| !['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>" if sshort.present?
|
||||
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?
|
||||
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')
|
||||
weekdays[dayNumber]
|
||||
return 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,29 +150,27 @@ 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 = roles.index_with { |r| I18n.t("helpers.application.role_#{r}") }
|
||||
names = Hash[roles.map { |r| [r, I18n.t("helpers.application.role_#{r}")] }]
|
||||
if icon
|
||||
roles.map do |r|
|
||||
image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r])
|
||||
end.join(' ').html_safe
|
||||
roles.map { |r| image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) }.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', rel: 'noopener'
|
||||
link_to h(address), "http://maps.google.com/?q=#{h(address)}", :title => I18n.t('helpers.application.show_google_maps'),
|
||||
:target => "_blank"
|
||||
end
|
||||
|
||||
# Returns flash messages html.
|
||||
|
@ -188,8 +186,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
|
||||
|
@ -197,17 +195,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})"
|
||||
|
@ -218,7 +216,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
|
||||
|
||||
|
@ -260,9 +258,9 @@ module ApplicationHelper
|
|||
|
||||
# @return [String] stylesheet tag for foodcoop CSS style (+custom_css+ foodcoop config)
|
||||
# @see #foodcoop_css_path
|
||||
def foodcoop_css_tag(_options = {})
|
||||
return if FoodsoftConfig[:custom_css].blank?
|
||||
|
||||
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
||||
def foodcoop_css_tag(options = {})
|
||||
unless FoodsoftConfig[:custom_css].blank?
|
||||
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
||||
end
|
||||
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' unless article.availability
|
||||
classes << 'just-updated' if article.recently_updated && article.availability
|
||||
classes.join(' ')
|
||||
classes << "unavailable" if !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| !except.index(a.id).nil? } if except
|
||||
block_given? or block = proc { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
|
||||
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.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,14 +23,10 @@ module DeliveriesHelper
|
|||
end
|
||||
|
||||
def stock_change_remove_link(stock_change_form)
|
||||
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
|
||||
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?
|
||||
|
||||
output = stock_change_form.hidden_field :_destroy
|
||||
output += link_to t('deliveries.stock_change_fields.remove_article'), '#',
|
||||
class: 'destroy_stock_change btn btn-small'
|
||||
output.html_safe
|
||||
output += link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'destroy_stock_change btn btn-small'
|
||||
return 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'
|
||||
when 'edit_results' then
|
||||
'edit_results_by_articles'
|
||||
when 'groups_overview'
|
||||
when 'groups_overview' then
|
||||
'shared/articles_by/groups'
|
||||
when 'articles_overview'
|
||||
when 'articles_overview' then
|
||||
'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
|
||||
if goa.group_order.order.finished? && current_user.role_finance?
|
||||
unless goa.group_order.order.finished? && current_user.role_finance?
|
||||
result
|
||||
else
|
||||
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,11 +1,10 @@
|
|||
module GroupOrdersHelper
|
||||
def data_to_js(ordering_data)
|
||||
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|
|
||||
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|
|
||||
"addData(#{row.join(', ')});"
|
||||
end.join("\n")
|
||||
}.join("\n")
|
||||
end
|
||||
|
||||
# Returns a link to the page where a group_order can be edited.
|
||||
|
@ -15,9 +14,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
|
||||
|
@ -27,7 +26,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'
|
||||
|
@ -46,12 +45,20 @@ module GroupOrdersHelper
|
|||
end
|
||||
|
||||
def get_missing_units_css_class(quantity_missing)
|
||||
if quantity_missing == 1
|
||||
'missing-few'
|
||||
elsif quantity_missing == 0
|
||||
''
|
||||
if (quantity_missing == 1)
|
||||
return 'missing-few';
|
||||
elsif (quantity_missing == 0)
|
||||
return ''
|
||||
else
|
||||
'missing-many'
|
||||
return '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].compact_blank.join(' ')})"
|
||||
"#{article.name} (#{[article.unit, pkg_info].reject(&: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 = []
|
||||
%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
|
||||
[: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
|
||||
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,12 +94,11 @@ 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: '')) do
|
||||
input_html = content_tag(:span, class: 'input-prepend intable', title: t('orders.edit_amount.field_locked_title', default: '')) {
|
||||
button_tag(nil, type: :button, class: 'btn unlocker') {
|
||||
content_tag(:i, nil, class: 'icon icon-unlock')
|
||||
} + input_html
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
input_html.html_safe
|
||||
|
@ -110,16 +109,18 @@ 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}"
|
||||
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
|
||||
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
|
||||
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.is_a?(Order) && order_or_supplier.stockit?
|
||||
if order_or_supplier.kind_of?(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
|
||||
|
@ -151,8 +152,19 @@ 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)
|
||||
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)
|
||||
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
|
||||
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,4 +1,3 @@
|
|||
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||
import "trix"
|
||||
import "@rails/actiontext"
|
||||
import "trix-editor-overrides"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
// app/javascript/trix-editor-overrides.js
|
||||
window.addEventListener("trix-file-accept", function(event) {
|
||||
if (event.file.size > 1024 * 1024 * 512) {
|
||||
event.preventDefault()
|
||||
alert(I18n.t('js.trix_editor.file_size_alert'))
|
||||
}
|
||||
})
|
|
@ -29,7 +29,7 @@ class AppleBar
|
|||
|
||||
def mean_order_amount_per_job
|
||||
(1 / @global_avg).round
|
||||
rescue StandardError
|
||||
rescue
|
||||
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]
|
||||
entity_name = amount < 0 ? t[:creditorName] : t[:debtorName]
|
||||
entity_account = amount < 0 ? t[:creditorAccount] : t[:debtorAccount]
|
||||
entityName = amount < 0 ? t[:creditorName] : t[:debtorName]
|
||||
entityAccount = 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: entity_account && entity_account[:iban],
|
||||
iban: entityAccount && entityAccount[:iban],
|
||||
reference: reference,
|
||||
text: entity_name,
|
||||
text: entityName,
|
||||
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,68 +10,66 @@ module DateTimeAttributeValidate
|
|||
super
|
||||
|
||||
attributes.each do |attribute|
|
||||
validate -> { send("#{attribute}_datetime_value_valid") }
|
||||
validate -> { self.send("#{attribute}_datetime_value_valid") }
|
||||
|
||||
# allow resetting the field to nil
|
||||
before_validation do
|
||||
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?
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
# remember old date and time values
|
||||
define_method("#{attribute}_date_value=") do |val|
|
||||
instance_variable_set("@#{attribute}_is_set", true)
|
||||
instance_variable_set("@#{attribute}_date_value", val)
|
||||
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||
self.instance_variable_set("@#{attribute}_date_value", val)
|
||||
begin
|
||||
send("#{attribute}_date=", val)
|
||||
rescue StandardError
|
||||
self.send("#{attribute}_date=", val)
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
define_method("#{attribute}_time_value=") do |val|
|
||||
instance_variable_set("@#{attribute}_is_set", true)
|
||||
instance_variable_set("@#{attribute}_time_value", val)
|
||||
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||
self.instance_variable_set("@#{attribute}_time_value", val)
|
||||
begin
|
||||
send("#{attribute}_time=", val)
|
||||
rescue StandardError
|
||||
self.send("#{attribute}_time=", val)
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# fallback to field when values are not set
|
||||
define_method("#{attribute}_date_value") do
|
||||
instance_variable_get("@#{attribute}_date_value") || send("#{attribute}_date").try do |e|
|
||||
e.strftime('%Y-%m-%d')
|
||||
end
|
||||
self.instance_variable_get("@#{attribute}_date_value") || self.send("#{attribute}_date").try { |e| e.strftime('%Y-%m-%d') }
|
||||
end
|
||||
define_method("#{attribute}_time_value") do
|
||||
instance_variable_get("@#{attribute}_time_value") || send("#{attribute}_time").try do |e|
|
||||
e.strftime('%H:%M')
|
||||
end
|
||||
self.instance_variable_get("@#{attribute}_time_value") || self.send("#{attribute}_time").try { |e| e.strftime('%H:%M') }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# validate date and time
|
||||
define_method("#{attribute}_datetime_value_valid") do
|
||||
date = instance_variable_get("@#{attribute}_date_value")
|
||||
date = self.instance_variable_get("@#{attribute}_date_value")
|
||||
unless date.blank? || begin
|
||||
Date.parse(date)
|
||||
rescue StandardError
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
errors.add(attribute, 'is not a valid date') # @todo I18n
|
||||
errors.add(attribute, "is not a valid date") # @todo I18n
|
||||
end
|
||||
time = instance_variable_get("@#{attribute}_time_value")
|
||||
time = self.instance_variable_get("@#{attribute}_time_value")
|
||||
unless time.blank? || begin
|
||||
Time.parse(time)
|
||||
rescue StandardError
|
||||
rescue
|
||||
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)] || get(::Regexp.last_match(1))
|
||||
options[::Regexp.last_match(1)] || self.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
|
||||
%i[protocol host port script_name].each do |k|
|
||||
[: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.#{scope}.#{key}"]
|
||||
value = RailsSettings::CachedSettings["foodcoop.#{self.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.#{scope}.#{key}"
|
||||
RailsSettings::CachedSettings.destroy "foodcoop.#{self.scope}.#{key}"
|
||||
rescue RailsSettings::Settings::SettingNotFound
|
||||
end
|
||||
else
|
||||
# or store
|
||||
RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"] = value
|
||||
RailsSettings::CachedSettings["foodcoop.#{self.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.#{scope}.").try(:keys) || []
|
||||
keys.map! { |k| k.gsub(/^foodcoop\.#{scope}\./, '') }
|
||||
keys = RailsSettings::CachedSettings.get_all("foodcoop.#{self.scope}.").try(:keys) || []
|
||||
keys.map! { |k| k.gsub(/^foodcoop\.#{self.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 config[:protected].include? key
|
||||
!config[:protected][key]
|
||||
if self.config[:protected].include? key
|
||||
!self.config[:protected][key]
|
||||
else
|
||||
!config[:protected][:all]
|
||||
!self.config[:protected][:all]
|
||||
end
|
||||
# @todo allow to check nested keys as well
|
||||
end
|
||||
|
@ -287,9 +287,7 @@ 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 do |a|
|
||||
[a[0], normalize_value(a[1])]
|
||||
end]
|
||||
value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map { |a| [a[0], normalize_value(a[1])] }]
|
||||
end
|
||||
case value
|
||||
when 'true' then true
|
||||
|
|
|
@ -8,24 +8,26 @@ module FoodsoftDateUtil
|
|||
# @todo handle ical parse errors
|
||||
occ = begin
|
||||
schedule.next_occurrence(from).to_time
|
||||
rescue StandardError
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight) if options && options[:time] && occ
|
||||
if options && options[:time] && occ
|
||||
occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight)
|
||||
end
|
||||
occ
|
||||
end
|
||||
|
||||
# @param rule [String, Symbol, Hash, IceCube::Rule] What to return a rule from.
|
||||
# @param p [String, Symbol, Hash, IceCube::Rule] What to return a rule from.
|
||||
# @return [IceCube::Rule] Recurring rule
|
||||
def self.rule_from(rule)
|
||||
case rule
|
||||
def self.rule_from(p)
|
||||
case p
|
||||
when String
|
||||
IceCube::Rule.from_ical(rule)
|
||||
IceCube::Rule.from_ical(p)
|
||||
when Hash
|
||||
IceCube::Rule.from_hash(rule)
|
||||
IceCube::Rule.from_hash(p)
|
||||
else
|
||||
rule
|
||||
p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# 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 StandardError => e
|
||||
logger.info("Can not accept mail for '#{rcpt_to}': #{e}")
|
||||
rescue => error
|
||||
logger.info("Can not accept mail for '#{rcpt_to}': #{error}")
|
||||
raise MidiSmtpServer::Smtpd550Exception
|
||||
end
|
||||
|
||||
|
@ -32,16 +32,16 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
|||
@handlers.each do |handler|
|
||||
handler.call(ctx[:message][:data])
|
||||
end
|
||||
rescue StandardError => e
|
||||
ExceptionNotifier.notify_exception(e, data: ctx)
|
||||
raise e
|
||||
rescue => error
|
||||
ExceptionNotifier.notify_exception(error, data: ctx)
|
||||
raise error
|
||||
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,28 +2,60 @@ require 'csv'
|
|||
|
||||
class OrderCsv < RenderCsv
|
||||
def header
|
||||
[
|
||||
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)
|
||||
]
|
||||
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
|
||||
end
|
||||
|
||||
def data
|
||||
@object.order_articles.ordered.includes(%i[article article_price]).all.map do |oa|
|
||||
@object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa|
|
||||
yield [
|
||||
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)
|
||||
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])
|
||||
]
|
||||
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.to_s
|
||||
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : "#{goa.quantity}"
|
||||
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..]
|
||||
[item.first || stock_ordergroup_name] + item[1..-1]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,7 +103,7 @@ class OrderPdf < RenderPdf
|
|||
def each_ordergroup_batch(batch_size)
|
||||
offset = 0
|
||||
|
||||
loop do
|
||||
while true
|
||||
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', Arel.sql('SUM(COALESCE(group_order_articles.result, group_order_articles.quantity))'))
|
||||
.pluck('group_order_articles.order_article_id', 'group_orders.ordergroup_id', '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: %i[article_price order])
|
||||
.preload(order_article: [:article_price, :order])
|
||||
.each(&block)
|
||||
end
|
||||
|
||||
|
|
|
@ -8,19 +8,23 @@ 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}" if supplier.customer_number.present?
|
||||
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 += "\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(%i[article article_price]).each do |oa|
|
||||
@order.order_articles.ordered.includes([: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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue