Compare commits
35 commits
1008-ideal
...
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 |
460 changed files with 7648 additions and 9086 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_DATABASE: test
|
||||||
MYSQL_ROOT_PASSWORD: password
|
MYSQL_ROOT_PASSWORD: password
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "mariadb-admin ping"
|
--health-cmd "mysqladmin ping"
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
@ -35,9 +35,7 @@ jobs:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup chromedriver
|
- name: Setup chromedriver
|
||||||
uses: nanasess/setup-chromedriver@v2
|
uses: nanasess/setup-chromedriver@v1.0.1
|
||||||
with:
|
|
||||||
chromedriver-version: '115.0.5790.170' # https://github.com/nanasess/setup-chromedriver/issues/200
|
|
||||||
- name: Setup ruby
|
- name: Setup ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
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
|
# Foodsoft 4.7.1
|
||||||
(31 December 2020)
|
(31 December 2020)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ ENV PORT=3000 \
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
COPY . ./
|
COPY Gemfile Gemfile.lock ./
|
||||||
|
COPY plugins/ ./plugins
|
||||||
|
COPY config/ ./config
|
||||||
|
|
||||||
# install dependencies and generate crontab
|
# install dependencies and generate crontab
|
||||||
RUN buildDeps='libmagic-dev' && \
|
RUN buildDeps='libmagic-dev' && \
|
||||||
|
@ -30,6 +32,8 @@ RUN buildDeps='libmagic-dev' && \
|
||||||
\
|
\
|
||||||
bundle exec whenever >crontab
|
bundle exec whenever >crontab
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
# compile assets with temporary mysql server
|
# compile assets with temporary mysql server
|
||||||
RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
|
RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
|
||||||
export SECRET_KEY_BASE=thisisnotimportantnow && \
|
export SECRET_KEY_BASE=thisisnotimportantnow && \
|
||||||
|
|
106
Gemfile
106
Gemfile
|
@ -1,87 +1,85 @@
|
||||||
# A sample Gemfile
|
# A sample Gemfile
|
||||||
source 'https://rubygems.org'
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "rails", '~> 7.0'
|
||||||
|
gem 'mail', '~> 2.7.1' # bug with mail 2.8.0 https://github.com/mikel/mail/issues/1489
|
||||||
|
|
||||||
gem 'rails', '~> 7.0'
|
|
||||||
|
|
||||||
gem 'less-rails'
|
|
||||||
gem 'sassc-rails'
|
gem 'sassc-rails'
|
||||||
|
gem 'less-rails'
|
||||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||||
gem 'therubyracer', platforms: :ruby
|
gem 'therubyracer', platforms: :ruby
|
||||||
|
|
||||||
gem 'bootsnap', require: false
|
gem 'jquery-rails'
|
||||||
|
gem 'select2-rails'
|
||||||
|
gem 'rails_tokeninput'
|
||||||
gem 'bootstrap-datepicker-rails'
|
gem 'bootstrap-datepicker-rails'
|
||||||
gem 'date_time_attribute'
|
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 '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-i18n'
|
||||||
gem 'rails_tokeninput'
|
gem 'bootsnap', require: false
|
||||||
gem 'select2-rails'
|
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10.0'
|
|
||||||
gem 'acts_as_tree'
|
|
||||||
gem 'attribute_normalizer'
|
|
||||||
gem 'daemons'
|
|
||||||
gem 'doorkeeper'
|
|
||||||
gem 'doorkeeper-i18n'
|
|
||||||
gem 'haml', '~> 5.0'
|
|
||||||
gem 'haml-rails'
|
|
||||||
gem 'ice_cube'
|
|
||||||
gem 'inherited_resources'
|
|
||||||
gem 'kaminari'
|
|
||||||
gem 'mysql2'
|
gem 'mysql2'
|
||||||
gem 'prawn'
|
gem 'prawn'
|
||||||
gem 'prawn-table'
|
gem 'prawn-table'
|
||||||
gem 'puma'
|
gem 'haml', '~> 5.0'
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'haml-rails'
|
||||||
gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73
|
gem 'kaminari'
|
||||||
gem 'ransack'
|
|
||||||
gem 'resque'
|
|
||||||
gem 'ruby-units'
|
|
||||||
gem 'sd_notify'
|
|
||||||
gem 'simple_form'
|
gem 'simple_form'
|
||||||
|
gem 'inherited_resources'
|
||||||
|
gem 'daemons'
|
||||||
|
gem 'doorkeeper'
|
||||||
|
gem 'doorkeeper-i18n'
|
||||||
|
gem 'rack-cors', require: 'rack/cors'
|
||||||
|
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', '~> 3.14.0' # 3.x for simple_navigation_bootstrap
|
||||||
gem 'simple-navigation-bootstrap'
|
gem 'simple-navigation-bootstrap'
|
||||||
gem 'sprockets', '< 4'
|
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 '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
|
# 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 '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'
|
||||||
gem 'roo-xls'
|
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-api'
|
||||||
gem 'rswag-ui'
|
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
|
# 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 '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_wiki', path: 'plugins/wiki'
|
||||||
|
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
|
# plugins not enabled by default
|
||||||
# gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
# gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
||||||
# gem 'foodsoft_printer', path: 'plugins/printer'
|
# gem 'foodsoft_printer', path: 'plugins/printer'
|
||||||
# gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
# gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
||||||
gem 'foodsoft_mollie', path: 'plugins/mollie'
|
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'listen'
|
|
||||||
gem 'mailcatcher'
|
|
||||||
gem 'sqlite3', '~> 1.3.6'
|
gem 'sqlite3', '~> 1.3.6'
|
||||||
|
gem 'mailcatcher'
|
||||||
gem 'web-console'
|
gem 'web-console'
|
||||||
|
gem 'listen'
|
||||||
|
|
||||||
# Better error output
|
# Better error output
|
||||||
gem 'better_errors'
|
gem 'better_errors'
|
||||||
|
@ -109,20 +107,26 @@ group :development, :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'apparition' # Capybara javascript driver
|
gem 'rspec-rails'
|
||||||
gem 'capybara'
|
|
||||||
gem 'connection_pool'
|
|
||||||
gem 'database_cleaner'
|
|
||||||
gem 'factory_bot_rails'
|
gem 'factory_bot_rails'
|
||||||
gem 'faker'
|
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
|
# need to include rspec components before i18n-spec or rake fails in test environment
|
||||||
gem 'i18n-spec'
|
|
||||||
gem 'rspec-core'
|
gem 'rspec-core'
|
||||||
gem 'rspec-rerun'
|
gem 'rspec-rerun'
|
||||||
|
gem 'i18n-spec'
|
||||||
|
gem 'rails-controller-testing'
|
||||||
# code coverage
|
# code coverage
|
||||||
gem 'simplecov', require: false
|
gem 'simplecov', require: false
|
||||||
gem 'simplecov-lcov', require: false
|
gem 'simplecov-lcov', require: false
|
||||||
# api
|
# api
|
||||||
gem 'rswag-specs'
|
gem 'rswag-specs'
|
||||||
|
gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
|
||||||
end
|
end
|
||||||
|
|
||||||
|
gem "importmap-rails", "~> 1.1"
|
||||||
|
gem "image_processing", "~> 1.12"
|
||||||
|
gem "terser", "~> 1.1"
|
||||||
|
|
86
Gemfile.lock
86
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
|
GIT
|
||||||
remote: https://github.com/gregschmit/recurring_select
|
remote: https://github.com/gregschmit/recurring_select
|
||||||
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
|
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
|
||||||
|
@ -48,13 +56,6 @@ PATH
|
||||||
mail
|
mail
|
||||||
rails
|
rails
|
||||||
|
|
||||||
PATH
|
|
||||||
remote: plugins/mollie
|
|
||||||
specs:
|
|
||||||
foodsoft_mollie (0.0.1)
|
|
||||||
mollie-api-ruby
|
|
||||||
rails
|
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: plugins/polls
|
remote: plugins/polls
|
||||||
specs:
|
specs:
|
||||||
|
@ -193,7 +194,7 @@ GEM
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.12.2)
|
coffee-script-source (1.12.2)
|
||||||
commonjs (0.2.7)
|
commonjs (0.2.7)
|
||||||
concurrent-ruby (1.2.2)
|
concurrent-ruby (1.1.10)
|
||||||
connection_pool (2.3.0)
|
connection_pool (2.3.0)
|
||||||
content_for_in_controllers (0.0.2)
|
content_for_in_controllers (0.0.2)
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
|
@ -217,7 +218,7 @@ GEM
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
diffy (3.4.2)
|
diffy (3.4.2)
|
||||||
docile (1.4.0)
|
docile (1.4.0)
|
||||||
doorkeeper (5.6.6)
|
doorkeeper (5.6.2)
|
||||||
railties (>= 5)
|
railties (>= 5)
|
||||||
doorkeeper-i18n (5.2.6)
|
doorkeeper-i18n (5.2.6)
|
||||||
doorkeeper (>= 5.2)
|
doorkeeper (>= 5.2)
|
||||||
|
@ -239,7 +240,7 @@ GEM
|
||||||
ffi (1.15.5)
|
ffi (1.15.5)
|
||||||
gaffe (1.2.0)
|
gaffe (1.2.0)
|
||||||
rails (>= 4.0.0)
|
rails (>= 4.0.0)
|
||||||
globalid (1.0.1)
|
globalid (1.0.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
haml (5.2.2)
|
haml (5.2.2)
|
||||||
temple (>= 0.8.0)
|
temple (>= 0.8.0)
|
||||||
|
@ -254,7 +255,7 @@ GEM
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
hashie (3.4.6)
|
hashie (3.4.6)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
i18n (1.14.1)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
i18n-js (3.0.11)
|
i18n-js (3.0.11)
|
||||||
i18n (>= 0.6.6, < 2)
|
i18n (>= 0.6.6, < 2)
|
||||||
|
@ -301,19 +302,15 @@ GEM
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
less (~> 2.6.0)
|
less (~> 2.6.0)
|
||||||
sprockets (~> 3.0)
|
sprockets (~> 3.0)
|
||||||
libv8 (3.16.14.19)
|
|
||||||
libv8 (3.16.14.19-x86_64-linux)
|
libv8 (3.16.14.19-x86_64-linux)
|
||||||
listen (3.7.1)
|
listen (3.7.1)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
loofah (2.21.3)
|
loofah (2.19.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.8.1)
|
mail (2.7.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
net-imap
|
|
||||||
net-pop
|
|
||||||
net-smtp
|
|
||||||
mailcatcher (0.2.4)
|
mailcatcher (0.2.4)
|
||||||
eventmachine
|
eventmachine
|
||||||
haml
|
haml
|
||||||
|
@ -333,9 +330,7 @@ GEM
|
||||||
mime-types-data (3.2022.0105)
|
mime-types-data (3.2022.0105)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.8.2)
|
minitest (5.17.0)
|
||||||
minitest (5.18.0)
|
|
||||||
mollie-api-ruby (4.7.1)
|
|
||||||
mono_logger (1.1.1)
|
mono_logger (1.1.1)
|
||||||
msgpack (1.6.0)
|
msgpack (1.6.0)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
|
@ -352,13 +347,10 @@ GEM
|
||||||
net-smtp (0.3.3)
|
net-smtp (0.3.3)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.15.2)
|
nokogiri (1.13.10-x86_64-linux)
|
||||||
mini_portile2 (~> 2.8.2)
|
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.2-x86_64-linux)
|
parallel (1.22.1)
|
||||||
racc (~> 1.4)
|
parser (3.2.0.0)
|
||||||
parallel (1.23.0)
|
|
||||||
parser (3.2.2.1)
|
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
pdf-core (0.9.0)
|
pdf-core (0.9.0)
|
||||||
polyglot (0.3.5)
|
polyglot (0.3.5)
|
||||||
|
@ -379,13 +371,13 @@ GEM
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
puma (6.0.2)
|
puma (6.0.2)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
racc (1.7.0)
|
racc (1.6.2)
|
||||||
rack (2.2.7)
|
rack (2.2.5)
|
||||||
rack-cors (1.1.1)
|
rack-cors (1.1.1)
|
||||||
rack (>= 2.0.0)
|
rack (>= 2.0.0)
|
||||||
rack-protection (3.0.5)
|
rack-protection (3.0.5)
|
||||||
rack
|
rack
|
||||||
rack-test (2.1.0)
|
rack-test (2.0.2)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails (7.0.4)
|
rails (7.0.4)
|
||||||
actioncable (= 7.0.4)
|
actioncable (= 7.0.4)
|
||||||
|
@ -403,12 +395,15 @@ GEM
|
||||||
railties (= 7.0.4)
|
railties (= 7.0.4)
|
||||||
rails-assets-listjs (0.2.0.beta.4)
|
rails-assets-listjs (0.2.0.beta.4)
|
||||||
railties (>= 3.1)
|
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)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.4.4)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.19, >= 2.19.1)
|
||||||
nokogiri (~> 1.14)
|
|
||||||
rails-i18n (7.0.6)
|
rails-i18n (7.0.6)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
|
@ -439,7 +434,7 @@ GEM
|
||||||
redis-namespace (1.10.0)
|
redis-namespace (1.10.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
ref (2.0.0)
|
ref (2.0.0)
|
||||||
regexp_parser (2.8.0)
|
regexp_parser (2.6.1)
|
||||||
responders (3.0.1)
|
responders (3.0.1)
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
|
@ -481,7 +476,7 @@ GEM
|
||||||
rspec-support (3.12.0)
|
rspec-support (3.12.0)
|
||||||
rswag-api (2.7.0)
|
rswag-api (2.7.0)
|
||||||
railties (>= 3.1, < 7.1)
|
railties (>= 3.1, < 7.1)
|
||||||
rswag-specs (2.9.0)
|
rswag-specs (2.7.0)
|
||||||
activesupport (>= 3.1, < 7.1)
|
activesupport (>= 3.1, < 7.1)
|
||||||
json-schema (>= 2.2, < 4.0)
|
json-schema (>= 2.2, < 4.0)
|
||||||
railties (>= 3.1, < 7.1)
|
railties (>= 3.1, < 7.1)
|
||||||
|
@ -489,18 +484,18 @@ GEM
|
||||||
rswag-ui (2.7.0)
|
rswag-ui (2.7.0)
|
||||||
actionpack (>= 3.1, < 7.1)
|
actionpack (>= 3.1, < 7.1)
|
||||||
railties (>= 3.1, < 7.1)
|
railties (>= 3.1, < 7.1)
|
||||||
rubocop (1.50.2)
|
rubocop (1.43.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.2.0.0)
|
parser (>= 3.2.0.0)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml (>= 3.2.5, < 4.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)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.28.1)
|
rubocop-ast (1.24.1)
|
||||||
parser (>= 3.2.1.0)
|
parser (>= 3.1.1.0)
|
||||||
rubocop-rails (2.17.4)
|
rubocop-rails (2.17.4)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
|
@ -510,7 +505,7 @@ GEM
|
||||||
ruby-filemagic (0.7.3)
|
ruby-filemagic (0.7.3)
|
||||||
ruby-ole (1.2.12.2)
|
ruby-ole (1.2.12.2)
|
||||||
ruby-prof (1.4.5)
|
ruby-prof (1.4.5)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.11.0)
|
||||||
ruby-units (3.0.0)
|
ruby-units (3.0.0)
|
||||||
ruby-vips (2.1.4)
|
ruby-vips (2.1.4)
|
||||||
ffi (~> 1.12)
|
ffi (~> 1.12)
|
||||||
|
@ -574,7 +569,7 @@ GEM
|
||||||
daemons (>= 1.0.9)
|
daemons (>= 1.0.9)
|
||||||
eventmachine (>= 1.0.0)
|
eventmachine (>= 1.0.0)
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
thor (1.2.2)
|
thor (1.2.1)
|
||||||
tilt (2.0.11)
|
tilt (2.0.11)
|
||||||
timeout (0.3.1)
|
timeout (0.3.1)
|
||||||
ttfunk (1.7.0)
|
ttfunk (1.7.0)
|
||||||
|
@ -585,7 +580,7 @@ GEM
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
twitter-text (1.14.7)
|
twitter-text (1.14.7)
|
||||||
unf (~> 0.1.0)
|
unf (~> 0.1.0)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
|
@ -610,10 +605,9 @@ GEM
|
||||||
twitter-text
|
twitter-text
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.6.8)
|
zeitwerk (2.6.6)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
@ -637,11 +631,11 @@ DEPENDENCIES
|
||||||
exception_notification
|
exception_notification
|
||||||
factory_bot_rails
|
factory_bot_rails
|
||||||
faker
|
faker
|
||||||
|
foodsoft_article_import!
|
||||||
foodsoft_discourse!
|
foodsoft_discourse!
|
||||||
foodsoft_documents!
|
foodsoft_documents!
|
||||||
foodsoft_links!
|
foodsoft_links!
|
||||||
foodsoft_messages!
|
foodsoft_messages!
|
||||||
foodsoft_mollie!
|
|
||||||
foodsoft_polls!
|
foodsoft_polls!
|
||||||
foodsoft_wiki!
|
foodsoft_wiki!
|
||||||
gaffe
|
gaffe
|
||||||
|
@ -658,6 +652,7 @@ DEPENDENCIES
|
||||||
kaminari
|
kaminari
|
||||||
less-rails
|
less-rails
|
||||||
listen
|
listen
|
||||||
|
mail (~> 2.7.1)
|
||||||
mailcatcher
|
mailcatcher
|
||||||
midi-smtp-server
|
midi-smtp-server
|
||||||
mime-types
|
mime-types
|
||||||
|
@ -670,6 +665,7 @@ DEPENDENCIES
|
||||||
rack-cors
|
rack-cors
|
||||||
rails (~> 7.0)
|
rails (~> 7.0)
|
||||||
rails-assets-listjs (= 0.2.0.beta.4)
|
rails-assets-listjs (= 0.2.0.beta.4)
|
||||||
|
rails-controller-testing
|
||||||
rails-i18n
|
rails-i18n
|
||||||
rails-settings-cached (= 0.4.3)
|
rails-settings-cached (= 0.4.3)
|
||||||
rails_tokeninput
|
rails_tokeninput
|
||||||
|
|
147
README.md
147
README.md
|
@ -1,71 +1,124 @@
|
||||||
Foodsoft
|
Foodsoft
|
||||||
=========
|
=========
|
||||||
|
|
||||||
[![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions)
|
[Website](https://foodsoft.local-it.org)
|
||||||
[![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master)
|
[Prototypefund](https://prototypefund.de/project/weiterentwicklung-von-foodsoft/)
|
||||||
[![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft)
|
|
||||||
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.svg)](https://codeclimate.com/github/foodcoops/foodsoft)
|
|
||||||
[![Docker Status](https://img.shields.io/docker/cloud/build/foodcoops/foodsoft.svg)](https://hub.docker.com/r/foodcoops/foodsoft)
|
|
||||||
[![Documentation](https://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/foodcoops/foodsoft)
|
|
||||||
|
|
||||||
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
|
|
||||||
|
|
||||||
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),
|
#### Was ist eine Einkaufskooperative?
|
||||||
then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines)
|
|
||||||
page on the wiki.
|
|
||||||
|
|
||||||
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
|
#### Increase Test Coverage
|
||||||
[hosting platform](https://foodcoops.net/foodsoft-hosting/).
|
|
||||||
|
|
||||||
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)
|
#### Upgrade
|
||||||
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.
|
|
||||||
|
|
||||||
For private use, there are no restrictions, but if you give others access to
|
1. Migrate to RSwag API Tests
|
||||||
Foodsoft (like running it open to the internet), you must also make your
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/28_introduce_rswag)
|
||||||
changes available under the same license. This can be as easy as
|
* [x] upstream [#969](https://github.com/foodcoops/foodsoft/pull/969)
|
||||||
[forking](https://github.com/foodcoops/foodsoft/fork) the project on Github and
|
1. Rails v7
|
||||||
pushing your changes. You are not required to integrate your changes back into
|
* [x] [fork](https://git.local-it.org/Foodsoft/foodsoft/src/branch/9_rails_v_7)
|
||||||
the main Foodsoft version (but if you're up for it that would be very welcome).
|
* [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
|
#### Article Order Import/Export
|
||||||
install and configure Foodsoft without having to publish your changes. These
|
|
||||||
files are marked as public domain in the file header.
|
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
|
#!/usr/bin/env rake
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .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 'rake'
|
||||||
require 'rspec-rerun/tasks' if defined?(RSpec) # http://stackoverflow.com/a/16853615/2866660
|
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.es
|
||||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl
|
//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl
|
||||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.fr
|
//= require bootstrap-datepicker/locales/bootstrap-datepicker.fr
|
||||||
//= require bootstrap-datepicker/locales/bootstrap-datepicker.tr
|
|
||||||
//= require list
|
//= require list
|
||||||
//= require list.unlist
|
//= require list.unlist
|
||||||
//= require list.delay
|
//= require list.delay
|
||||||
|
|
|
@ -179,13 +179,13 @@ function updateBalance() {
|
||||||
var balance = groupBalance - total;
|
var balance = groupBalance - total;
|
||||||
$('#new_balance').html(I18n.l("currency", balance));
|
$('#new_balance').html(I18n.l("currency", balance));
|
||||||
$('#total_balance').val(I18n.l("currency", balance));
|
$('#total_balance').val(I18n.l("currency", balance));
|
||||||
// determine bgcolor and submit button state according to balance
|
|
||||||
if (balance < minimumBalance) {
|
if (balance < minimumBalance) {
|
||||||
$('#submit_button').attr('disabled', 'disabled')
|
$('#submit_button').attr('disabled', 'disabled')
|
||||||
$('#available_funds_error').css('display', 'block')
|
$('#balance-alert').css('display', 'block')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$('#submit_button').removeAttr('disabled')
|
$('#submit_button').removeAttr('disabled')
|
||||||
$('#available_funds_error').css('display', 'none')
|
$('#balance-alert').css('display', 'none')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +228,4 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
updateButtons($(document));
|
updateButtons($(document));
|
||||||
|
|
||||||
$(document).ready(updateBalance);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -230,7 +230,7 @@ table {
|
||||||
margin: .5em 0;
|
margin: .5em 0;
|
||||||
|
|
||||||
input:disabled {
|
input:disabled {
|
||||||
background-color: red; }
|
background-color: gray; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,6 +241,9 @@ table {
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:hover .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
tr.order-article:focus .article-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#order-footer {
|
#order-footer {
|
||||||
|
@ -275,10 +278,13 @@ tr.order-article .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:focus .article-info {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr.order-article:focus {
|
||||||
|
background-color: #E9E9E9;
|
||||||
|
}
|
||||||
|
|
||||||
// ********* Articles
|
// ********* Articles
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
.list .missing-many td, .list .missing-many:hover td {
|
.missing-many td {
|
||||||
background-color: #ebbebe;
|
background-color: #ffc590aa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list .missing-few td, .list .missing-few:hover td {
|
.missing-many:hover td, .missing-many:focus td {
|
||||||
background-color: #ffee75;
|
background-color: #ffc590;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list .missing-none td, .list .missing-none:hover td {
|
.missing-few td {
|
||||||
background-color: #E4EED6;
|
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
|
def new
|
||||||
@bank_account = BankAccount.new(params[:bank_account])
|
@bank_account = BankAccount.new(params[:bank_account])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@bank_account = BankAccount.find(params[:id])
|
|
||||||
render action: 'new', layout: false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@bank_account = BankAccount.new(params[:bank_account])
|
@bank_account = BankAccount.new(params[:bank_account])
|
||||||
if @bank_account.valid? && @bank_account.save
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@bank_account = BankAccount.find(params[:id])
|
||||||
|
render :action => 'new', :layout => false
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@bank_account = BankAccount.find(params[:id])
|
@bank_account = BankAccount.find(params[:id])
|
||||||
|
|
||||||
if @bank_account.update(params[:bank_account])
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@bank_account = BankAccount.find(params[:id])
|
@bank_account = BankAccount.find(params[:id])
|
||||||
@bank_account.destroy
|
@bank_account.destroy
|
||||||
redirect_to update_bank_accounts_admin_finances_url, status: :see_other
|
redirect_to update_bank_accounts_admin_finances_url, :status => 303
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
flash.now[:alert] = e.message
|
flash.now[:alert] = error.message
|
||||||
render template: 'shared/alert'
|
render template: 'shared/alert'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,6 @@ class Admin::BankGatewaysController < Admin::BaseController
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@bank_gateway = BankGateway.find(params[:id])
|
|
||||||
render action: 'new', layout: false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@bank_gateway = BankGateway.new(params[:bank_gateway])
|
@bank_gateway = BankGateway.new(params[:bank_gateway])
|
||||||
if @bank_gateway.valid? && @bank_gateway.save
|
if @bank_gateway.valid? && @bank_gateway.save
|
||||||
|
@ -20,6 +15,11 @@ class Admin::BankGatewaysController < Admin::BaseController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@bank_gateway = BankGateway.find(params[:id])
|
||||||
|
render action: 'new', layout: false
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@bank_gateway = BankGateway.find(params[:id])
|
@bank_gateway = BankGateway.find(params[:id])
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Admin::ConfigsController < Admin::BaseController
|
class Admin::ConfigsController < Admin::BaseController
|
||||||
before_action :get_tabs, only: %i[show list]
|
before_action :get_tabs, only: [:show, :list]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@current_tab = @tabs.include?(params[:tab]) ? params[:tab] : @tabs.first
|
@current_tab = @tabs.include?(params[:tab]) ? params[:tab] : @tabs.first
|
||||||
|
@ -16,7 +16,7 @@ class Admin::ConfigsController < Admin::BaseController
|
||||||
def update
|
def update
|
||||||
parse_recurring_selects! params[:config][:order_schedule]
|
parse_recurring_selects! params[:config][:order_schedule]
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
# TODO: support nested configuration keys
|
# TODO support nested configuration keys
|
||||||
params[:config].each do |key, val|
|
params[:config].each do |key, val|
|
||||||
FoodsoftConfig[key] = convert_config_value val
|
FoodsoftConfig[key] = convert_config_value val
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,7 @@ class Admin::ConfigsController < Admin::BaseController
|
||||||
|
|
||||||
# Set configuration tab names as `@tabs`
|
# Set configuration tab names as `@tabs`
|
||||||
def get_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
|
# allow engines to modify this list
|
||||||
engines = Rails::Engine.subclasses.map(&:instance).select { |e| e.respond_to?(:configuration) }
|
engines = Rails::Engine.subclasses.map(&:instance).select { |e| e.respond_to?(:configuration) }
|
||||||
engines.each { |e| e.configuration(@tabs, self) }
|
engines.each { |e| e.configuration(@tabs, self) }
|
||||||
|
@ -38,16 +38,16 @@ class Admin::ConfigsController < Admin::BaseController
|
||||||
|
|
||||||
# turn recurring rules into something palatable
|
# turn recurring rules into something palatable
|
||||||
def parse_recurring_selects!(config)
|
def parse_recurring_selects!(config)
|
||||||
return unless config
|
if config
|
||||||
|
for k in [:pickup, :boxfill, :ends] do
|
||||||
for k in %i[pickup boxfill ends] do
|
if config[k]
|
||||||
if config[k]
|
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
||||||
# allow clearing it using dummy value '{}' ('' would break recurring_select)
|
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
||||||
if config[k][:recurr].present? && config[k][:recurr] != '{}'
|
config[k][:recurr] = ActiveSupport::JSON.decode(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]
|
||||||
config[k][:recurr] = FoodsoftDateUtil.rule_from(config[k][:recurr]).to_ical if config[k][:recurr]
|
else
|
||||||
else
|
config[k] = nil
|
||||||
config[k] = nil
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,21 +10,21 @@ class Admin::FinancesController < Admin::BaseController
|
||||||
|
|
||||||
def update_bank_accounts
|
def update_bank_accounts
|
||||||
@bank_accounts = BankAccount.order('name')
|
@bank_accounts = BankAccount.order('name')
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_bank_gateways
|
def update_bank_gateways
|
||||||
@bank_gateways = BankGateway.order('name')
|
@bank_gateways = BankGateway.order('name')
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_transaction_types
|
def update_transaction_types
|
||||||
@financial_transaction_classes = FinancialTransactionClass.includes(:financial_transaction_types).order('name ASC')
|
@financial_transaction_classes = FinancialTransactionClass.includes(:financial_transaction_types).order('name ASC')
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_supplier_categories
|
def update_supplier_categories
|
||||||
@supplier_categories = SupplierCategory.order('name')
|
@supplier_categories = SupplierCategory.order('name')
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,25 +6,25 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
|
||||||
render action: 'new', layout: false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
|
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
|
||||||
if @financial_transaction_class.save
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render action: 'new', layout: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||||
|
render action: 'new', layout: false
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||||
|
|
||||||
if @financial_transaction_class.update(params[:financial_transaction_class])
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render action: 'new', layout: false
|
||||||
end
|
end
|
||||||
|
@ -33,9 +33,9 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
|
||||||
@financial_transaction_class.destroy!
|
@financial_transaction_class.destroy!
|
||||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
flash.now[:alert] = e.message
|
flash.now[:alert] = error.message
|
||||||
render template: 'shared/alert'
|
render template: 'shared/alert'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,25 +7,25 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
|
||||||
render action: 'new', layout: false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
|
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
|
||||||
if @financial_transaction_type.save
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render action: 'new', layout: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||||
|
render action: 'new', layout: false
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||||
|
|
||||||
if @financial_transaction_type.update(params[:financial_transaction_type])
|
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
|
else
|
||||||
render action: 'new', layout: false
|
render action: 'new', layout: false
|
||||||
end
|
end
|
||||||
|
@ -34,9 +34,9 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
@financial_transaction_type = FinancialTransactionType.find(params[:id])
|
||||||
@financial_transaction_type.destroy!
|
@financial_transaction_type.destroy!
|
||||||
redirect_to update_transaction_types_admin_finances_url, status: :see_other
|
redirect_to update_transaction_types_admin_finances_url, status: 303
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
flash.now[:alert] = e.message
|
flash.now[:alert] = error.message
|
||||||
render template: 'shared/alert'
|
render template: 'shared/alert'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,28 +3,28 @@ class Admin::MailDeliveryStatusController < Admin::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@maildeliverystatus = MailDeliveryStatus.order(created_at: :desc)
|
@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)
|
@maildeliverystatus = @maildeliverystatus.page(params[:page]).per(@per_page)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
||||||
filename = "maildeliverystatus_#{params[:id]}.#{MIME::Types[@maildeliverystatus.attachment_mime].first.preferred_extension}"
|
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
|
end
|
||||||
|
|
||||||
def destroy_all
|
def destroy_all
|
||||||
@maildeliverystatus = MailDeliveryStatus.delete_all
|
@maildeliverystatus = MailDeliveryStatus.delete_all
|
||||||
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
@maildeliverystatus = MailDeliveryStatus.find(params[:id])
|
||||||
@maildeliverystatus.destroy
|
@maildeliverystatus.destroy
|
||||||
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
redirect_to admin_mail_delivery_status_index_path, notice: t('.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
|
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,15 +2,16 @@ class Admin::OrdergroupsController < Admin::BaseController
|
||||||
inherit_resources
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@ordergroups = Ordergroup.undeleted.sort_by_param(params['sort'])
|
@ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"])
|
||||||
|
|
||||||
if request.format.csv?
|
if request.format.csv?
|
||||||
send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv',
|
send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv', type: 'text/csv'
|
||||||
type: 'text/csv'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# if somebody uses the search field:
|
# 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)
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
end
|
end
|
||||||
|
@ -18,8 +19,8 @@ class Admin::OrdergroupsController < Admin::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@ordergroup = Ordergroup.find(params[:id])
|
@ordergroup = Ordergroup.find(params[:id])
|
||||||
@ordergroup.mark_as_deleted
|
@ordergroup.mark_as_deleted
|
||||||
redirect_to admin_ordergroups_url, notice: t('.notice')
|
redirect_to admin_ordergroups_url, notice: t('admin.ordergroups.destroy.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_ordergroups_url, alert: t('.error')
|
redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,6 @@ class Admin::SupplierCategoriesController < Admin::BaseController
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@supplier_category = SupplierCategory.find(params[:id])
|
|
||||||
render action: 'new', layout: false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@supplier_category = SupplierCategory.new(params[:supplier_category])
|
@supplier_category = SupplierCategory.new(params[:supplier_category])
|
||||||
if @supplier_category.valid? && @supplier_category.save
|
if @supplier_category.valid? && @supplier_category.save
|
||||||
|
@ -20,6 +15,11 @@ class Admin::SupplierCategoriesController < Admin::BaseController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@supplier_category = SupplierCategory.find(params[:id])
|
||||||
|
render action: 'new', layout: false
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@supplier_category = SupplierCategory.find(params[:id])
|
@supplier_category = SupplierCategory.find(params[:id])
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,16 @@ class Admin::UsersController < Admin::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@users = params[:show_deleted] ? User.deleted : User.undeleted
|
@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)
|
@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:
|
# 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)
|
@users = @users.page(params[:page]).per(@per_page)
|
||||||
end
|
end
|
||||||
|
@ -18,17 +20,17 @@ class Admin::UsersController < Admin::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@user = User.find(params[:id])
|
@user = User.find(params[:id])
|
||||||
@user.mark_as_deleted
|
@user.mark_as_deleted
|
||||||
redirect_to admin_users_url, notice: t('.notice')
|
redirect_to admin_users_url, notice: t('admin.users.destroy.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
redirect_to admin_users_url, alert: t('admin.users.destroy.error', error: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def restore
|
def restore
|
||||||
@user = User.find(params[:id])
|
@user = User.find(params[:id])
|
||||||
@user.restore
|
@user.restore
|
||||||
redirect_to admin_users_url, notice: t('.notice')
|
redirect_to admin_users_url, notice: t('admin.users.restore.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_users_url, alert: t('.error', error: e.message)
|
redirect_to admin_users_url, alert: t('admin.users.restore.error', error: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def sudo
|
def sudo
|
||||||
|
|
|
@ -4,7 +4,7 @@ class Admin::WorkgroupsController < Admin::BaseController
|
||||||
def index
|
def index
|
||||||
@workgroups = Workgroup.order('name ASC')
|
@workgroups = Workgroup.order('name ASC')
|
||||||
# if somebody uses the search field:
|
# 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)
|
@workgroups = @workgroups.page(params[:page]).per(@per_page)
|
||||||
end
|
end
|
||||||
|
@ -12,8 +12,8 @@ class Admin::WorkgroupsController < Admin::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@workgroup = Workgroup.find(params[:id])
|
@workgroup = Workgroup.find(params[:id])
|
||||||
@workgroup.destroy
|
@workgroup.destroy
|
||||||
redirect_to admin_workgroups_url, notice: t('.notice')
|
redirect_to admin_workgroups_url, notice: t('admin.workgroups.destroy.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to admin_workgroups_url, alert: t('.error', error: e.message)
|
redirect_to admin_workgroups_url, alert: t('admin.workgroups.destroy.error', error: error.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,30 +20,29 @@ class Api::V1::BaseController < ApplicationController
|
||||||
|
|
||||||
def require_ordergroup
|
def require_ordergroup
|
||||||
authenticate
|
authenticate
|
||||||
return if current_ordergroup.present?
|
unless current_ordergroup.present?
|
||||||
|
raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup')
|
||||||
raise Api::Errors::PermissionRequired, 'Forbidden, must be in an ordergroup'
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_minimum_balance
|
def require_minimum_balance
|
||||||
minimum_balance = FoodsoftConfig[:minimum_balance] or return
|
minimum_balance = FoodsoftConfig[:minimum_balance] or return
|
||||||
return unless current_ordergroup.account_balance < minimum_balance
|
if current_ordergroup.account_balance < minimum_balance
|
||||||
|
raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance))
|
||||||
raise Api::Errors::PermissionRequired, t('application.controller.error_minimum_balance', min: minimum_balance)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_enough_apples
|
def require_enough_apples
|
||||||
return unless current_ordergroup.not_enough_apples?
|
if current_ordergroup.not_enough_apples?
|
||||||
|
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||||
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples,
|
raise Api::Errors::PermissionRequired.new(s)
|
||||||
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
end
|
||||||
raise Api::Errors::PermissionRequired, s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_config_enabled(config)
|
def require_config_enabled(config)
|
||||||
return if FoodsoftConfig[config]
|
unless FoodsoftConfig[config]
|
||||||
|
raise Api::Errors::PermissionRequired.new(t('application.controller.error_not_enabled', config: config))
|
||||||
raise Api::Errors::PermissionRequired, t('application.controller.error_not_enabled', config: config)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip_session
|
def skip_session
|
||||||
|
@ -53,12 +52,12 @@ class Api::V1::BaseController < ApplicationController
|
||||||
def not_found_handler(e)
|
def not_found_handler(e)
|
||||||
# remove where-clauses from error message (not suitable for end-users)
|
# remove where-clauses from error message (not suitable for end-users)
|
||||||
msg = e.message.try { |m| m.sub(/\s*\[.*?\]\s*$/, '') } || 'Not found'
|
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
|
end
|
||||||
|
|
||||||
def not_acceptable_handler(e)
|
def not_acceptable_handler(e)
|
||||||
msg = e.message || 'Data not acceptable'
|
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
|
end
|
||||||
|
|
||||||
def doorkeeper_unauthorized_render_options(error:)
|
def doorkeeper_unauthorized_render_options(error:)
|
||||||
|
@ -71,11 +70,11 @@ class Api::V1::BaseController < ApplicationController
|
||||||
|
|
||||||
def permission_required_handler(e)
|
def permission_required_handler(e)
|
||||||
msg = e.message || 'Forbidden, user has no access'
|
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
|
end
|
||||||
|
|
||||||
# @todo something with ApplicationHelper#show_user
|
# @todo something with ApplicationHelper#show_user
|
||||||
def show_user(user = current_user, **_options)
|
def show_user(user = current_user, **options)
|
||||||
user.display
|
user.display
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,8 +16,7 @@ class Api::V1::User::FinancialTransactionsController < Api::V1::BaseController
|
||||||
|
|
||||||
def create
|
def create
|
||||||
transaction_type = FinancialTransactionType.find(create_params[:financial_transaction_type_id])
|
transaction_type = FinancialTransactionType.find(create_params[:financial_transaction_type_id])
|
||||||
ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user,
|
ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user, transaction_type)
|
||||||
transaction_type)
|
|
||||||
render json: ft
|
render json: ft
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController
|
||||||
before_action -> { doorkeeper_authorize! 'group_orders:user' }
|
before_action -> { doorkeeper_authorize! 'group_orders:user' }
|
||||||
|
|
||||||
before_action :require_ordergroup
|
before_action :require_ordergroup
|
||||||
before_action :require_minimum_balance, only: %i[create update] # destroy is ok
|
before_action :require_minimum_balance, only: [:create, :update] # destroy is ok
|
||||||
before_action :require_enough_apples, only: %i[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
|
# @todo allow decreasing amounts when minimum balance isn't met
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -35,8 +35,7 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController
|
||||||
goa = nil
|
goa = nil
|
||||||
GroupOrderArticle.transaction do
|
GroupOrderArticle.transaction do
|
||||||
goa = scope_for_update.includes(:group_order_article_quantities).find(params.require(:id))
|
goa = scope_for_update.includes(:group_order_article_quantities).find(params.require(:id))
|
||||||
goa.update_quantities((update_params[:quantity] || goa.quantity).to_i,
|
goa.update_quantities((update_params[:quantity] || goa.quantity).to_i, (update_params[:tolerance] || goa.tolerance).to_i)
|
||||||
(update_params[:tolerance] || goa.tolerance).to_i)
|
|
||||||
goa.order_article.update_results!
|
goa.order_article.update_results!
|
||||||
goa.group_order.update_price!
|
goa.group_order.update_price!
|
||||||
goa.group_order.update!(updated_by: current_user)
|
goa.group_order.update!(updated_by: current_user)
|
||||||
|
|
|
@ -8,13 +8,13 @@ class Api::V1::User::OrdergroupController < Api::V1::BaseController
|
||||||
financial_overview: {
|
financial_overview: {
|
||||||
account_balance: ordergroup.account_balance.to_f,
|
account_balance: ordergroup.account_balance.to_f,
|
||||||
available_funds: ordergroup.get_available_funds.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,
|
id: c.id,
|
||||||
name: c.display,
|
name: c.display,
|
||||||
amount: ordergroup["sum_of_class_#{c.id}"].to_f
|
amount: ordergroup["sum_of_class_#{c.id}"].to_f
|
||||||
}
|
}
|
||||||
end
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,10 +19,10 @@ class ApplicationController < ActionController::Base
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_user_last_activity
|
def set_user_last_activity
|
||||||
return unless current_user && (session[:last_activity].nil? || session[:last_activity] < 1.minute.ago)
|
if current_user && (session[:last_activity] == nil || session[:last_activity] < 1.minutes.ago)
|
||||||
|
current_user.update_attribute(:last_activity, Time.now)
|
||||||
current_user.update_attribute(:last_activity, Time.now)
|
session[:last_activity] = Time.now
|
||||||
session[:last_activity] = Time.now
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Many plugins can be turned on and off on the fly with a `use_` configuration option.
|
# 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
|
end
|
||||||
|
|
||||||
def items_per_page
|
def items_per_page
|
||||||
@per_page = if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
|
if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
|
||||||
params[:per_page].to_i
|
@per_page = params[:per_page].to_i
|
||||||
else
|
else
|
||||||
20
|
@per_page = 20
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set timezone according to foodcoop preference.
|
# Set timezone according to foodcoop preference.
|
||||||
|
|
|
@ -4,17 +4,17 @@ class ArticleCategoriesController < ApplicationController
|
||||||
before_action :authenticate_article_meta
|
before_action :authenticate_article_meta
|
||||||
|
|
||||||
def create
|
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
|
end
|
||||||
|
|
||||||
def update
|
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
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
destroy!
|
destroy!
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: e.message)
|
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -2,24 +2,24 @@ class ArticlesController < ApplicationController
|
||||||
before_action :authenticate_article_meta, :find_supplier
|
before_action :authenticate_article_meta, :find_supplier
|
||||||
|
|
||||||
def index
|
def index
|
||||||
sort = if params['sort']
|
if params['sort']
|
||||||
case params['sort']
|
sort = case params['sort']
|
||||||
when 'name' then 'articles.name'
|
when "name" then "articles.name"
|
||||||
when 'unit' then 'articles.unit'
|
when "unit" then "articles.unit"
|
||||||
when 'article_category' then 'article_categories.name'
|
when "article_category" then "article_categories.name"
|
||||||
when 'note' then 'articles.note'
|
when "note" then "articles.note"
|
||||||
when 'availability' then 'articles.availability'
|
when "availability" then "articles.availability"
|
||||||
when 'name_reverse' then 'articles.name DESC'
|
when "name_reverse" then "articles.name DESC"
|
||||||
when 'unit_reverse' then 'articles.unit DESC'
|
when "unit_reverse" then "articles.unit DESC"
|
||||||
when 'article_category_reverse' then 'article_categories.name DESC'
|
when "article_category_reverse" then "article_categories.name DESC"
|
||||||
when 'note_reverse' then 'articles.note DESC'
|
when "note_reverse" then "articles.note DESC"
|
||||||
when 'availability_reverse' then 'articles.availability DESC'
|
when "availability_reverse" then "articles.availability DESC"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
'article_categories.name, articles.name'
|
sort = "article_categories.name, articles.name"
|
||||||
end
|
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?
|
if request.format.csv?
|
||||||
send_data ArticlesCsv.new(@articles, encoding: 'utf-8').to_csv, filename: 'articles.csv', type: 'text/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|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.js { render layout: false }
|
format.js { render :layout => false }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@article = @supplier.articles.build(tax: FoodsoftConfig[:tax_default])
|
@article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy
|
def copy
|
||||||
@article = @supplier.articles.find(params[:article_id]).dup
|
@article = @supplier.articles.find(params[:article_id]).dup
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@article = Article.new(params[:article])
|
@article = Article.new(params[:article])
|
||||||
if @article.valid? && @article.save
|
if @article.valid? && @article.save
|
||||||
render layout: false
|
render :layout => false
|
||||||
else
|
else
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,9 +65,9 @@ class ArticlesController < ApplicationController
|
||||||
@article = Article.find(params[:id])
|
@article = Article.find(params[:id])
|
||||||
|
|
||||||
if @article.update(params[:article])
|
if @article.update(params[:article])
|
||||||
render layout: false
|
render :layout => false
|
||||||
else
|
else
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class ArticlesController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
@article = Article.find(params[:id])
|
@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
|
@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
|
end
|
||||||
|
|
||||||
# Renders a form for editing all articles from a supplier
|
# Renders a form for editing all articles from a supplier
|
||||||
|
@ -87,17 +87,19 @@ class ArticlesController < ApplicationController
|
||||||
def update_all
|
def update_all
|
||||||
invalid_articles = false
|
invalid_articles = false
|
||||||
|
|
||||||
Article.transaction do
|
begin
|
||||||
if params[:articles].present?
|
Article.transaction do
|
||||||
# Update other article attributes...
|
unless params[:articles].blank?
|
||||||
@articles = Article.find(params[:articles].keys)
|
# Update other article attributes...
|
||||||
@articles.each do |article|
|
@articles = Article.find(params[:articles].keys)
|
||||||
unless article.update(params[:articles][article.id.to_s])
|
@articles.each do |article|
|
||||||
invalid_articles ||= true # Remember that there are validation errors
|
unless article.update(params[:articles][article.id.to_s])
|
||||||
|
invalid_articles = true unless invalid_articles # Remember that there are validation errors
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -132,31 +134,32 @@ class ArticlesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# action succeded
|
# action succeded
|
||||||
redirect_to supplier_articles_url(@supplier, per_page: params[:per_page])
|
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page])
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to supplier_articles_url(@supplier, per_page: params[:per_page]),
|
redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]),
|
||||||
alert: I18n.t('errors.general_msg', msg: e)
|
:alert => I18n.t('errors.general_msg', :msg => error)
|
||||||
end
|
end
|
||||||
|
|
||||||
# lets start with parsing articles from uploaded file, yeah
|
# lets start with parsing articles from uploaded file, yeah
|
||||||
# Renders the upload form
|
# Renders the upload form
|
||||||
def upload; end
|
def upload
|
||||||
|
end
|
||||||
|
|
||||||
# Update articles from a spreadsheet
|
# Update articles from a spreadsheet
|
||||||
def parse_upload
|
def parse_upload
|
||||||
uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file')
|
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 = { filename: uploaded_file.original_filename }
|
||||||
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
|
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
|
||||||
options[:convert_units] = (params[:articles]['convert_units'] == '1')
|
options[:convert_units] = (params[:articles]['convert_units'] == '1')
|
||||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile,
|
options[:update_category] = (params[:articles]['update_category'] == '1')
|
||||||
options
|
@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?
|
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||||
redirect_to supplier_articles_path(@supplier),
|
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice')
|
||||||
notice: I18n.t('articles.controller.parse_upload.notice')
|
|
||||||
end
|
end
|
||||||
@ignored_article_count = 0
|
@ignored_article_count = 0
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message)
|
redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# sync all articles with the external database
|
# sync all articles with the external database
|
||||||
|
@ -164,14 +167,13 @@ class ArticlesController < ApplicationController
|
||||||
def sync
|
def sync
|
||||||
# check if there is an shared_supplier
|
# check if there is an shared_supplier
|
||||||
unless @supplier.shared_supplier
|
unless @supplier.shared_supplier
|
||||||
redirect_to supplier_articles_url(@supplier),
|
redirect_to supplier_articles_url(@supplier), :alert => I18n.t('articles.controller.sync.shared_alert', :supplier => @supplier.name)
|
||||||
alert: I18n.t('articles.controller.sync.shared_alert', supplier: @supplier.name)
|
|
||||||
end
|
end
|
||||||
# sync articles against external database
|
# sync articles against external database
|
||||||
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_all
|
@updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_all
|
||||||
return unless @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
|
||||||
|
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
|
||||||
redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.sync.notice')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates, deletes articles when upload or sync form is submitted
|
# Updates, deletes articles when upload or sync form is submitted
|
||||||
|
@ -186,7 +188,7 @@ class ArticlesController < ApplicationController
|
||||||
# delete articles
|
# delete articles
|
||||||
begin
|
begin
|
||||||
@outlisted_articles.each(&:mark_as_deleted)
|
@outlisted_articles.each(&:mark_as_deleted)
|
||||||
rescue StandardError
|
rescue
|
||||||
# raises an exception when used in current order
|
# raises an exception when used in current order
|
||||||
has_error = true
|
has_error = true
|
||||||
end
|
end
|
||||||
|
@ -198,15 +200,15 @@ class ArticlesController < ApplicationController
|
||||||
raise ActiveRecord::Rollback if has_error
|
raise ActiveRecord::Rollback if has_error
|
||||||
end
|
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|
|
@updated_article_pairs = @updated_articles.map do |article|
|
||||||
orig_article = Article.find(article.id)
|
orig_article = Article.find(article.id)
|
||||||
[article, orig_article.unequal_attributes(article)]
|
[article, orig_article.unequal_attributes(article)]
|
||||||
end
|
end
|
||||||
flash.now.alert = I18n.t('articles.controller.error_invalid')
|
flash.now.alert = I18n.t('articles.controller.error_invalid')
|
||||||
render params[:from_action] == 'sync' ? :sync : :parse_upload
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -218,18 +220,18 @@ class ArticlesController < ApplicationController
|
||||||
q[:name_cont_all] = params.fetch(:name_cont_all_joined, '').split(' ')
|
q[:name_cont_all] = params.fetch(:name_cont_all_joined, '').split(' ')
|
||||||
search = @supplier.shared_supplier.shared_articles.ransack(q)
|
search = @supplier.shared_supplier.shared_articles.ransack(q)
|
||||||
@articles = search.result.page(params[:page]).per(10)
|
@articles = search.result.page(params[:page]).per(10)
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
# fills a form whith values of the selected shared_article
|
# 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
|
# when the direct parameter is set and the article is valid, it is imported directly
|
||||||
def import
|
def import
|
||||||
@article = SharedArticle.find(params[:shared_article_id]).build_new_article(@supplier)
|
@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?
|
@article.article_category_id = params[:article_category_id] unless params[:article_category_id].blank?
|
||||||
if params[:direct] && params[:article_category_id].present? && @article.valid? && @article.save
|
if params[:direct] && !params[:article_category_id].blank? && @article.valid? && @article.save
|
||||||
render action: 'create', layout: false
|
render :action => 'create', :layout => false
|
||||||
else
|
else
|
||||||
render action: 'new', layout: false
|
render :action => 'new', :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,19 +9,15 @@ module Concerns::Auth
|
||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
# check if there is a valid session and return the logged-in user (its object)
|
# check if there is a valid session and return the logged-in user (its object)
|
||||||
return unless session[:user_id] && params[:foodcoop]
|
if session[:user_id] && params[:foodcoop]
|
||||||
|
# for shared-host installations. check if the cookie-subdomain fits to request.
|
||||||
# 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
|
||||||
@current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def deny_access
|
def deny_access
|
||||||
session[:return_to] = request.original_url
|
session[:return_to] = request.original_url
|
||||||
redirect_to root_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))
|
||||||
alert: I18n.t('application.controller.error_denied',
|
|
||||||
sign_in: ActionController::Base.helpers.link_to(
|
|
||||||
t('application.controller.error_denied_sign_in'), login_path
|
|
||||||
))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -51,7 +47,12 @@ module Concerns::Auth
|
||||||
|
|
||||||
def authenticate(role = 'any')
|
def authenticate(role = 'any')
|
||||||
# Attempt to retrieve authenticated user from controller instance or session...
|
# 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...
|
# We have an authenticated user, now check role...
|
||||||
# Roles gets the user through his memberships.
|
# Roles gets the user through his memberships.
|
||||||
hasRole = case role
|
hasRole = case role
|
||||||
|
@ -72,11 +73,6 @@ module Concerns::Auth
|
||||||
else
|
else
|
||||||
deny_access
|
deny_access
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,13 +116,13 @@ module Concerns::Auth
|
||||||
# if fails the user will redirected to startpage
|
# if fails the user will redirected to startpage
|
||||||
def authenticate_membership_or_admin(group_id = params[:id])
|
def authenticate_membership_or_admin(group_id = params[:id])
|
||||||
@group = Group.find(group_id)
|
@group = Group.find(group_id)
|
||||||
return if @group.member?(@current_user) || @current_user.role_admin?
|
unless @group.member?(@current_user) || @current_user.role_admin?
|
||||||
|
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
||||||
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_or_token(prefix, role = 'any')
|
def authenticate_or_token(prefix, role = 'any')
|
||||||
if params[:token].present?
|
if not params[:token].blank?
|
||||||
begin
|
begin
|
||||||
TokenVerifier.new(prefix).verify(params[:token])
|
TokenVerifier.new(prefix).verify(params[:token])
|
||||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
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.
|
# Make sure that at least one the given OAuth scopes is valid for the current user's permissions.
|
||||||
# @raise Api::Errors::PermissionsRequired
|
# @raise Api::Errors::PermissionsRequired
|
||||||
def doorkeeper_authorize_roles!(*scopes)
|
def doorkeeper_authorize_roles!(*scopes)
|
||||||
return if scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
unless scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
|
||||||
|
raise Api::Errors::PermissionRequired.new('Forbidden, no permission')
|
||||||
raise Api::Errors::PermissionRequired, 'Forbidden, no permission'
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check whether a given OAuth scope is permitted for the current user.
|
# Check whether a given OAuth scope is permitted for the current user.
|
||||||
|
@ -48,7 +48,9 @@ module Concerns::AuthApi
|
||||||
def doorkeeper_scope_permitted?(scope)
|
def doorkeeper_scope_permitted?(scope)
|
||||||
scope_parts = scope.split(':')
|
scope_parts = scope.split(':')
|
||||||
# user sub-scopes like +config:user+ are always permitted
|
# 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
|
case scope_parts.first
|
||||||
when 'user' then return true # access to the current user's own profile
|
when 'user' then return true # access to the current user's own profile
|
||||||
|
@ -62,8 +64,8 @@ module Concerns::AuthApi
|
||||||
end
|
end
|
||||||
|
|
||||||
case scope
|
case scope
|
||||||
when 'orders:read' then true
|
when 'orders:read' then return true
|
||||||
when 'orders:write' then current_user.role_orders?
|
when 'orders:write' then return current_user.role_orders?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,12 +24,12 @@ module Concerns::FoodcoopScope
|
||||||
elsif FoodsoftConfig.allowed_foodcoop? foodcoop
|
elsif FoodsoftConfig.allowed_foodcoop? foodcoop
|
||||||
FoodsoftConfig.select_foodcoop foodcoop
|
FoodsoftConfig.select_foodcoop foodcoop
|
||||||
else
|
else
|
||||||
raise ActionController::RoutingError, 'Foodcoop Not Found'
|
raise ActionController::RoutingError.new 'Foodcoop Not Found'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Always stay in foodcoop url scope
|
# Always stay in foodcoop url scope
|
||||||
def default_url_options(_options = {})
|
def default_url_options(options = {})
|
||||||
super().merge({ foodcoop: FoodsoftConfig.scope })
|
super().merge({ foodcoop: FoodsoftConfig.scope })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Concerns::Locale
|
||||||
end
|
end
|
||||||
|
|
||||||
def browser_language
|
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
|
end
|
||||||
|
|
||||||
def default_language
|
def default_language
|
||||||
|
@ -30,7 +30,7 @@ module Concerns::Locale
|
||||||
def select_language_according_to_priority
|
def select_language_according_to_priority
|
||||||
language = explicitly_requested_language || session_language || user_settings_language
|
language = explicitly_requested_language || session_language || user_settings_language
|
||||||
language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale]
|
language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale]
|
||||||
language.presence&.to_sym if language.present?
|
language.presence&.to_sym unless language.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def available_locales
|
def available_locales
|
||||||
|
@ -38,11 +38,11 @@ module Concerns::Locale
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_locale
|
def set_locale
|
||||||
::I18n.locale = if available_locales.include?(select_language_according_to_priority)
|
if available_locales.include?(select_language_according_to_priority)
|
||||||
select_language_according_to_priority
|
::I18n.locale = select_language_according_to_priority
|
||||||
else
|
else
|
||||||
default_language
|
::I18n.locale = default_language
|
||||||
end
|
end
|
||||||
|
|
||||||
locale = session[:locale] = ::I18n.locale
|
locale = session[:locale] = ::I18n.locale
|
||||||
logger.info("Set locale to #{locale}")
|
logger.info("Set locale to #{locale}")
|
||||||
|
|
|
@ -3,7 +3,7 @@ module Concerns::SendOrderPdf
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def send_order_pdf(order, document)
|
def send_order_pdf order, document
|
||||||
klass = case document
|
klass = case document
|
||||||
when 'groups' then OrderByGroups
|
when 'groups' then OrderByGroups
|
||||||
when 'articles' then OrderByArticles
|
when 'articles' then OrderByArticles
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class DeliveriesController < ApplicationController
|
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
|
def index
|
||||||
@deliveries = @supplier.deliveries.order('date DESC')
|
@deliveries = @supplier.deliveries.order('date DESC')
|
||||||
|
@ -15,10 +15,6 @@ class DeliveriesController < ApplicationController
|
||||||
@delivery.date = Date.today # TODO: move to model/database
|
@delivery.date = Date.today # TODO: move to model/database
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@delivery = Delivery.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@delivery = Delivery.new(params[:delivery])
|
@delivery = Delivery.new(params[:delivery])
|
||||||
|
|
||||||
|
@ -26,10 +22,14 @@ class DeliveriesController < ApplicationController
|
||||||
flash[:notice] = I18n.t('deliveries.create.notice')
|
flash[:notice] = I18n.t('deliveries.create.notice')
|
||||||
redirect_to [@supplier, @delivery]
|
redirect_to [@supplier, @delivery]
|
||||||
else
|
else
|
||||||
render action: 'new'
|
render :action => "new"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@delivery = Delivery.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@delivery = Delivery.find(params[:id])
|
@delivery = Delivery.find(params[:id])
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class DeliveriesController < ApplicationController
|
||||||
flash[:notice] = I18n.t('deliveries.update.notice')
|
flash[:notice] = I18n.t('deliveries.update.notice')
|
||||||
redirect_to [@supplier, @delivery]
|
redirect_to [@supplier, @delivery]
|
||||||
else
|
else
|
||||||
render action: 'edit'
|
render :action => "edit"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,18 +52,18 @@ class DeliveriesController < ApplicationController
|
||||||
def add_stock_change
|
def add_stock_change
|
||||||
@stock_change = StockChange.new
|
@stock_change = StockChange.new
|
||||||
@stock_change.stock_article = StockArticle.find(params[:stock_article_id])
|
@stock_change.stock_article = StockArticle.find(params[:stock_article_id])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def form_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
def form_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def form_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
def form_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
class FeedbackController < ApplicationController
|
class FeedbackController < ApplicationController
|
||||||
def new; end
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if params[:message].present?
|
if params[:message].present?
|
||||||
Mailer.feedback(current_user, params[:message]).deliver_now
|
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
|
else
|
||||||
render action: 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@order = Order.find(params[:order_id])
|
@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
|
@comments = @order.comments
|
||||||
|
|
||||||
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
||||||
|
@ -13,13 +13,13 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
|
|
||||||
sort_param = params['sort'] || 'name'
|
sort_param = params['sort'] || 'name'
|
||||||
@articles = case sort_param
|
@articles = case sort_param
|
||||||
when 'name'
|
when 'name' then
|
||||||
@articles.order('articles.name ASC')
|
@articles.order('articles.name ASC')
|
||||||
when 'name_reverse'
|
when 'name_reverse' then
|
||||||
@articles.order('articles.name DESC')
|
@articles.order('articles.name DESC')
|
||||||
when 'order_number'
|
when 'order_number' then
|
||||||
@articles.order('articles.order_number ASC')
|
@articles.order('articles.order_number ASC')
|
||||||
when 'order_number_reverse'
|
when 'order_number_reverse' then
|
||||||
@articles.order('articles.order_number DESC')
|
@articles.order('articles.order_number DESC')
|
||||||
else
|
else
|
||||||
@articles
|
@articles
|
||||||
|
@ -31,13 +31,13 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
def new_on_order_article_create # See publish/subscribe design pattern in /doc.
|
def new_on_order_article_create # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_on_order_article_update # See publish/subscribe design pattern in /doc.
|
def new_on_order_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_summary
|
def update_summary
|
||||||
|
@ -46,29 +46,29 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
|
|
||||||
def edit_note
|
def edit_note
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_note
|
def update_note
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
if @order.update(params[:order])
|
if @order.update(params[:order])
|
||||||
render layout: false
|
render :layout => false
|
||||||
else
|
else
|
||||||
render action: :edit_note, layout: false
|
render :action => :edit_note, :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit_transport
|
def edit_transport
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_transport
|
def update_transport
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
@order.update!(params[:order])
|
@order.update!(params[:order])
|
||||||
redirect_to new_finance_order_path(order_id: @order.id)
|
redirect_to new_finance_order_path(order_id: @order.id)
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: e.message)
|
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# before the order will booked, a view lists all Ordergroups and its order_prices
|
# 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])
|
@order = Order.find(params[:id])
|
||||||
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
||||||
@order.close!(@current_user, @type)
|
@order.close!(@current_user, @type)
|
||||||
redirect_to finance_order_index_url, notice: t('.notice')
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.message)
|
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the order directly, without automaticly updating ordergroups account balances
|
# Close the order directly, without automaticly updating ordergroups account balances
|
||||||
def close_direct
|
def close_direct
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
@order.close_direct!(@current_user)
|
@order.close_direct!(@current_user)
|
||||||
redirect_to finance_order_index_url, notice: t('.notice')
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close_direct.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to finance_order_index_url, alert: t('.alert', message: e.message)
|
redirect_to finance_order_index_url, alert: t('finance.balancing.close_direct.alert', message: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def close_all_direct_with_invoice
|
def close_all_direct_with_invoice
|
||||||
|
@ -103,8 +103,8 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
count += 1
|
count += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
redirect_to finance_order_index_url, notice: t('.notice', count: count)
|
redirect_to finance_order_index_url, notice: t('finance.balancing.close_all_direct_with_invoice.notice', count: count)
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: e.message)
|
redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: error.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,8 @@ class Finance::BankAccountsController < Finance::BaseController
|
||||||
@bank_account = BankAccount.find(params[:id])
|
@bank_account = BankAccount.find(params[:id])
|
||||||
count = @bank_account.assign_unlinked_transactions
|
count = @bank_account.assign_unlinked_transactions
|
||||||
redirect_to finance_bank_account_transactions_url(@bank_account), notice: t('.notice', count: count)
|
redirect_to finance_bank_account_transactions_url(@bank_account), notice: t('.notice', count: count)
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: e.message)
|
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import
|
def import
|
||||||
|
@ -33,8 +33,8 @@ class Finance::BankAccountsController < Finance::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
needs_redirect = ok
|
needs_redirect = ok
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
flash.alert = t('errors.general_msg', msg: e.message)
|
flash.alert = t('errors.general_msg', msg: error.message)
|
||||||
needs_redirect = true
|
needs_redirect = true
|
||||||
ensure
|
ensure
|
||||||
return unless needs_redirect
|
return unless needs_redirect
|
||||||
|
|
|
@ -3,30 +3,26 @@ class Finance::BankTransactionsController < ApplicationController
|
||||||
inherit_resources
|
inherit_resources
|
||||||
|
|
||||||
def index
|
def index
|
||||||
sort = if params['sort']
|
if params["sort"]
|
||||||
case params['sort']
|
sort = case params["sort"]
|
||||||
when 'date' then 'date'
|
when "date" then "date"
|
||||||
when 'amount' then 'amount'
|
when "amount" then "amount"
|
||||||
when 'financial_link' then 'financial_link_id'
|
when "financial_link" then "financial_link_id"
|
||||||
when 'date_reverse' then 'date DESC'
|
when "date_reverse" then "date DESC"
|
||||||
when 'amount_reverse' then 'amount DESC'
|
when "amount_reverse" then "amount DESC"
|
||||||
when 'financial_link_reverse' then 'financial_link_id DESC'
|
when "financial_link_reverse" then "financial_link_id DESC"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
'date DESC'
|
sort = "date DESC"
|
||||||
end
|
end
|
||||||
|
|
||||||
@bank_account = BankAccount.find(params[:bank_account_id])
|
@bank_account = BankAccount.find(params[:bank_account_id])
|
||||||
@bank_transactions_all = @bank_account.bank_transactions.order(sort).includes(:financial_link)
|
@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]}%") unless params[:query].nil?
|
||||||
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%",
|
|
||||||
"%#{params[:query]}%")
|
|
||||||
end
|
|
||||||
@bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page)
|
@bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js
|
format.js; format.html { render }
|
||||||
format.html { render }
|
|
||||||
format.csv do
|
format.csv do
|
||||||
send_data BankTransactionsCsv.new(@bank_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv'
|
send_data BankTransactionsCsv.new(@bank_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Finance::FinancialLinksController < Finance::BaseController
|
class Finance::FinancialLinksController < Finance::BaseController
|
||||||
before_action :find_financial_link, except: %i[create incomplete]
|
before_action :find_financial_link, except: [:create, :incomplete]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@items = @financial_link.bank_transactions.map do |bt|
|
@items = @financial_link.bank_transactions.map do |bt|
|
||||||
|
@ -37,7 +37,7 @@ class Finance::FinancialLinksController < Finance::BaseController
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@financial_link = FinancialLink.first_unused_or_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 = BankTransaction.find(params[:bank_transaction])
|
||||||
bank_transaction.update_attribute :financial_link, @financial_link
|
bank_transaction.update_attribute :financial_link, @financial_link
|
||||||
end
|
end
|
||||||
|
@ -72,16 +72,14 @@ class Finance::FinancialLinksController < Finance::BaseController
|
||||||
|
|
||||||
def create_financial_transaction
|
def create_financial_transaction
|
||||||
financial_transaction = FinancialTransaction.new(financial_transaction_params)
|
financial_transaction = FinancialTransaction.new(financial_transaction_params)
|
||||||
financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount,
|
financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount, financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link
|
||||||
financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link
|
|
||||||
redirect_to finance_link_url(@financial_link), notice: t('.notice')
|
redirect_to finance_link_url(@financial_link), notice: t('.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: e)
|
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: error)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index_financial_transaction
|
def index_financial_transaction
|
||||||
@financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type,
|
@financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type, :ordergroup)
|
||||||
:ordergroup)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_financial_transaction
|
def add_financial_transaction
|
||||||
|
@ -125,7 +123,7 @@ class Finance::FinancialLinksController < Finance::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_best_fitting_ordergroup_id_for_financial_link(financial_link_id)
|
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 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}
|
JOIN bank_transactions b ON a.iban = b.iban AND b.financial_link_id = #{financial_link_id.to_i}
|
||||||
SQL
|
SQL
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
class Finance::FinancialTransactionsController < ApplicationController
|
class Finance::FinancialTransactionsController < ApplicationController
|
||||||
before_action :authenticate_finance
|
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
|
inherit_resources
|
||||||
# belongs_to :ordergroup
|
# belongs_to :ordergroup
|
||||||
|
|
||||||
def index
|
def index
|
||||||
sort = if params['sort']
|
if params['sort']
|
||||||
case params['sort']
|
sort = case params['sort']
|
||||||
when 'date' then 'created_on'
|
when "date" then "created_on"
|
||||||
when 'note' then 'note'
|
when "note" then "note"
|
||||||
when 'amount' then 'amount'
|
when "amount" then "amount"
|
||||||
when 'date_reverse' then 'created_on DESC'
|
when "date_reverse" then "created_on DESC"
|
||||||
when 'note_reverse' then 'note DESC'
|
when "note_reverse" then "note DESC"
|
||||||
when 'amount_reverse' then 'amount DESC'
|
when "amount_reverse" then "amount DESC"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
'created_on DESC'
|
sort = "created_on DESC"
|
||||||
end
|
end
|
||||||
|
|
||||||
@q = FinancialTransaction.ransack(params[:q])
|
@q = FinancialTransaction.ransack(params[:q])
|
||||||
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
|
@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)
|
@financial_transactions = @financial_transactions_all.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js
|
format.js; format.html { render }
|
||||||
format.html { render }
|
|
||||||
format.csv do
|
format.csv do
|
||||||
send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv',
|
send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv'
|
||||||
type: 'text/csv'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -40,29 +38,31 @@ class Finance::FinancialTransactionsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@financial_transaction = if @ordergroup
|
if @ordergroup
|
||||||
@ordergroup.financial_transactions.build
|
@financial_transaction = @ordergroup.financial_transactions.build
|
||||||
else
|
else
|
||||||
FinancialTransaction.new
|
@financial_transaction = FinancialTransaction.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@financial_transaction = FinancialTransaction.new(params[:financial_transaction])
|
@financial_transaction = FinancialTransaction.new(params[:financial_transaction])
|
||||||
@financial_transaction.user = current_user
|
@financial_transaction.user = current_user
|
||||||
@financial_transaction.save!
|
if @financial_transaction.ordergroup
|
||||||
redirect_to finance_group_transactions_path(@ordergroup),
|
@financial_transaction.add_transaction!
|
||||||
notice: I18n.t('finance.financial_transactions.controller.create.notice')
|
else
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
@financial_transaction.save!
|
||||||
flash.now[:alert] = e.message
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
transaction = FinancialTransaction.find(params[:id])
|
transaction = FinancialTransaction.find(params[:id])
|
||||||
transaction.revert!(current_user)
|
transaction.revert!(current_user)
|
||||||
redirect_to finance_group_transactions_path(transaction.ordergroup),
|
redirect_to finance_group_transactions_path(transaction.ordergroup), notice: t('finance.financial_transactions.controller.destroy.notice')
|
||||||
notice: t('finance.financial_transactions.controller.destroy.notice')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_collection
|
def new_collection
|
||||||
|
@ -88,17 +88,17 @@ class Finance::FinancialTransactionsController < ApplicationController
|
||||||
|
|
||||||
params[:financial_transactions].each do |trans|
|
params[:financial_transactions].each do |trans|
|
||||||
# ignore empty amount fields ...
|
# ignore empty amount fields ...
|
||||||
next if trans[:amount].blank?
|
unless trans[:amount].blank?
|
||||||
|
amount = LocalizeInput.parse(trans[:amount]).to_f
|
||||||
amount = LocalizeInput.parse(trans[:amount]).to_f
|
note = params[:note]
|
||||||
note = params[:note]
|
ordergroup = Ordergroup.find(trans[:ordergroup_id])
|
||||||
ordergroup = Ordergroup.find(trans[:ordergroup_id])
|
if params[:set_balance]
|
||||||
if params[:set_balance]
|
note += " (#{amount})"
|
||||||
note += " (#{amount})"
|
amount -= ordergroup.financial_transaction_class_balance(type.financial_transaction_class)
|
||||||
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
|
end
|
||||||
ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link)
|
|
||||||
foodcoop_amount -= amount
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if params[:create_foodcoop_transaction]
|
if params[:create_foodcoop_transaction]
|
||||||
|
@ -107,7 +107,7 @@ class Finance::FinancialTransactionsController < ApplicationController
|
||||||
user: @current_user,
|
user: @current_user,
|
||||||
amount: foodcoop_amount,
|
amount: foodcoop_amount,
|
||||||
note: params[:note],
|
note: params[:note],
|
||||||
financial_link: financial_link
|
financial_link: financial_link,
|
||||||
})
|
})
|
||||||
ft.save!
|
ft.save!
|
||||||
end
|
end
|
||||||
|
@ -117,8 +117,8 @@ class Finance::FinancialTransactionsController < ApplicationController
|
||||||
|
|
||||||
url = financial_link ? finance_link_url(financial_link.id) : finance_ordergroups_url
|
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')
|
redirect_to url, notice: I18n.t('finance.financial_transactions.controller.create_collection.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
flash.now[:alert] = e.message
|
flash.now[:alert] = error.message
|
||||||
render action: :new_collection
|
render action: :new_collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
class Finance::InvoicesController < ApplicationController
|
class Finance::InvoicesController < ApplicationController
|
||||||
before_action :authenticate_finance_or_invoices
|
before_action :authenticate_finance_or_invoices
|
||||||
|
|
||||||
before_action :find_invoice, only: %i[show edit update destroy]
|
before_action :find_invoice, only: [:show, :edit, :update, :destroy]
|
||||||
before_action :ensure_can_edit, only: %i[edit update destroy]
|
before_action :ensure_can_edit, only: [:edit, :update, :destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@invoices_all = Invoice.includes(:supplier, :deliveries, :orders).order('date DESC')
|
@invoices_all = Invoice.includes(:supplier, :deliveries, :orders).order('date DESC')
|
||||||
@invoices = @invoices_all.page(params[:page]).per(@per_page)
|
@invoices = @invoices_all.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js
|
format.js; format.html { render }
|
||||||
format.html { render }
|
|
||||||
format.csv do
|
format.csv do
|
||||||
send_data InvoicesCsv.new(@invoices_all).to_csv, filename: 'invoices.csv', type: 'text/csv'
|
send_data InvoicesCsv.new(@invoices_all).to_csv, filename: 'invoices.csv', type: 'text/csv'
|
||||||
end
|
end
|
||||||
|
@ -21,10 +20,11 @@ class Finance::InvoicesController < ApplicationController
|
||||||
@suppliers = Supplier.includes(:invoices).where('invoices.paid_on IS NULL').references(:invoices)
|
@suppliers = Supplier.includes(:invoices).where('invoices.paid_on IS NULL').references(:invoices)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
def new
|
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.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]
|
@invoice.orders << Order.find_by_id(params[:order_id]) if params[:order_id]
|
||||||
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_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
|
def form_on_supplier_id_change
|
||||||
fill_deliveries_and_orders_collection params[:invoice_id], params[:supplier_id]
|
fill_deliveries_and_orders_collection params[:invoice_id], params[:supplier_id]
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_deliveries_and_orders_collection(invoice_id, supplier_id)
|
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,
|
@deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(date: :desc).limit(25)
|
||||||
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)
|
||||||
@orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id,
|
|
||||||
supplier_id).order(ends: :desc).limit(25)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -60,7 +58,7 @@ class Finance::InvoicesController < ApplicationController
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id
|
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id
|
||||||
render action: 'new'
|
render :action => "new"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,7 +81,7 @@ class Finance::InvoicesController < ApplicationController
|
||||||
@invoice = Invoice.find(params[:invoice_id])
|
@invoice = Invoice.find(params[:invoice_id])
|
||||||
type = MIME::Types[@invoice.attachment_mime].first
|
type = MIME::Types[@invoice.attachment_mime].first
|
||||||
filename = "invoice_#{@invoice.id}_attachment.#{type.preferred_extension}"
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -94,8 +92,8 @@ class Finance::InvoicesController < ApplicationController
|
||||||
|
|
||||||
# Returns true if @current_user can edit the invoice..
|
# Returns true if @current_user can edit the invoice..
|
||||||
def ensure_can_edit
|
def ensure_can_edit
|
||||||
return if @invoice.user_can_edit?(current_user)
|
unless @invoice.user_can_edit?(current_user)
|
||||||
|
deny_access
|
||||||
deny_access
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
class Finance::OrdergroupsController < Finance::BaseController
|
class Finance::OrdergroupsController < Finance::BaseController
|
||||||
def index
|
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
|
if m
|
||||||
sort = m[:col]
|
sort = m[:col]
|
||||||
sort += ' DESC' if m[:reverse]
|
sort += ' DESC' if m[:reverse]
|
||||||
else
|
else
|
||||||
sort = 'name'
|
sort = "name"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ordergroups = Ordergroup.undeleted.order(sort)
|
@ordergroups = Ordergroup.undeleted.order(sort)
|
||||||
|
@ -14,7 +14,7 @@ class Finance::OrdergroupsController < Finance::BaseController
|
||||||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
@total_balances = FinancialTransactionClass.sorted.each_with_object({}) do |c, tmp|
|
@total_balances = FinancialTransactionClass.sorted.each_with_object({}) do |c, tmp|
|
||||||
tmp[c.id] = c.financial_transactions.reduce(0) { |sum, t| sum + (t.amount || 0) }
|
tmp[c.id] = c.financial_transactions.reduce(0) { | sum, t | sum + t.amount }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
class Foodcoop::OrdergroupsController < ApplicationController
|
class Foodcoop::OrdergroupsController < ApplicationController
|
||||||
def index
|
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)
|
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
format.js { render layout: false }
|
format.js { render :layout => false }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
class Foodcoop::UsersController < ApplicationController
|
class Foodcoop::UsersController < ApplicationController
|
||||||
before_action -> { require_config_disabled :disable_members_overview }
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@users = User.undeleted.sort_by_param(params['sort'])
|
@users = User.undeleted.sort_by_param(params["sort"])
|
||||||
|
|
||||||
# if somebody uses the search field:
|
# 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]
|
if params[:ordergroup_name]
|
||||||
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?",
|
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", "%#{params[:ordergroup_name]}%")
|
||||||
"%#{params[:ordergroup_name]}%")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@users = @users.page(params[:page]).per(@per_page)
|
@users = @users.page(params[:page]).per(@per_page)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.haml
|
format.html # index.html.haml
|
||||||
format.js { render layout: false } # index.js.erb
|
format.js { render :layout => false } # index.js.erb
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
class Foodcoop::WorkgroupsController < ApplicationController
|
class Foodcoop::WorkgroupsController < ApplicationController
|
||||||
before_action :authenticate_membership_or_admin,
|
before_action :authenticate_membership_or_admin,
|
||||||
except: [:index]
|
:except => [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@workgroups = Workgroup.order('name')
|
@workgroups = Workgroup.order("name")
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
|
@ -13,9 +13,9 @@ class Foodcoop::WorkgroupsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@workgroup = Workgroup.find(params[:id])
|
@workgroup = Workgroup.find(params[:id])
|
||||||
if @workgroup.update(params[:workgroup])
|
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
|
else
|
||||||
render action: 'edit'
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class GroupOrderArticlesController < ApplicationController
|
class GroupOrderArticlesController < ApplicationController
|
||||||
before_action :authenticate_finance
|
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
|
layout false # We only use this controller to server js snippets, no need for layout rendering
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
class GroupOrdersController < ApplicationController
|
class GroupOrdersController < ApplicationController
|
||||||
# Security
|
# Security
|
||||||
before_action :ensure_ordergroup_member
|
before_action :ensure_ordergroup_member
|
||||||
before_action :ensure_open_order, only: %i[new create edit update order stock_order saveOrder]
|
before_action :ensure_open_order, :only => [:new, :create, :edit, :update, :order, :stock_order, :saveOrder]
|
||||||
before_action :ensure_my_group_order, only: %i[show edit update]
|
before_action :ensure_my_group_order, only: [:show, :edit, :update]
|
||||||
before_action :enough_apples?, only: %i[new create]
|
before_action :enough_apples?, only: [:new, :create]
|
||||||
|
|
||||||
# Index page.
|
# Index page.
|
||||||
def index
|
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)
|
@finished_not_closed_orders_including_group_order = Order.finished_not_closed.ordergroup_group_orders_map(@ordergroup)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
|
||||||
@order = @group_order.order
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
def new
|
||||||
ordergroup = params[:stock_order] ? nil : @ordergroup
|
ordergroup = params[:stock_order] ? nil : @ordergroup
|
||||||
@group_order = @order.group_orders.build(ordergroup: ordergroup, updated_by: current_user)
|
@group_order = @order.group_orders.build(:ordergroup => ordergroup, :updated_by => current_user)
|
||||||
@ordering_data = @group_order.load_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@ordering_data = @group_order.load_data
|
@ordering_data = @group_order.load_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -31,26 +23,34 @@ class GroupOrdersController < ApplicationController
|
||||||
@group_order = GroupOrder.new(params[:group_order])
|
@group_order = GroupOrder.new(params[:group_order])
|
||||||
begin
|
begin
|
||||||
@group_order.save_ordering!
|
@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
|
rescue ActiveRecord::StaleObjectError
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_stale')
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_stale')
|
||||||
rescue StandardError => e
|
rescue => exception
|
||||||
logger.error('Failed to update order: ' + e.message)
|
logger.error('Failed to update order: ' + exception.message)
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_general')
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_general')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@order = @group_order.order
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@ordering_data = @group_order.load_data
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@group_order.attributes = params[:group_order]
|
@group_order.attributes = params[:group_order]
|
||||||
@group_order.updated_by = current_user
|
@group_order.updated_by = current_user
|
||||||
begin
|
begin
|
||||||
@group_order.save_ordering!
|
@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
|
rescue ActiveRecord::StaleObjectError
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_stale')
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_stale')
|
||||||
rescue StandardError => e
|
rescue => exception
|
||||||
logger.error('Failed to update order: ' + e.message)
|
logger.error('Failed to update order: ' + exception.message)
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_general')
|
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_general')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,16 +74,16 @@ class GroupOrdersController < ApplicationController
|
||||||
# Used as a :before_action by OrdersController.
|
# Used as a :before_action by OrdersController.
|
||||||
def ensure_ordergroup_member
|
def ensure_ordergroup_member
|
||||||
@ordergroup = @current_user.ordergroup
|
@ordergroup = @current_user.ordergroup
|
||||||
return unless @ordergroup.nil?
|
if @ordergroup.nil?
|
||||||
|
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
|
||||||
redirect_to root_url, alert: I18n.t('group_orders.errors.no_member')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_open_order
|
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?
|
unless @order.open?
|
||||||
flash[:notice] = I18n.t('group_orders.errors.closed')
|
flash[:notice] = I18n.t('group_orders.errors.closed')
|
||||||
redirect_to action: 'index'
|
redirect_to :action => 'index'
|
||||||
end
|
end
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||||
|
@ -91,17 +91,17 @@ class GroupOrdersController < ApplicationController
|
||||||
|
|
||||||
def ensure_my_group_order
|
def ensure_my_group_order
|
||||||
@group_order = GroupOrder.find(params[:id])
|
@group_order = GroupOrder.find(params[:id])
|
||||||
return unless @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?)
|
if @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?)
|
||||||
|
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
||||||
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def enough_apples?
|
def enough_apples?
|
||||||
return unless @ordergroup.not_enough_apples?
|
if @ordergroup.not_enough_apples?
|
||||||
|
redirect_to group_orders_url,
|
||||||
redirect_to group_orders_url,
|
alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples,
|
||||||
alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples,
|
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
||||||
stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def order_id_param
|
def order_id_param
|
||||||
|
|
|
@ -9,7 +9,8 @@ class HomeController < ApplicationController
|
||||||
@unassigned_tasks = Task.order(:due_date).next_unassigned_tasks_for(current_user)
|
@unassigned_tasks = Task.order(:due_date).next_unassigned_tasks_for(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def profile; end
|
def profile
|
||||||
|
end
|
||||||
|
|
||||||
def reference_calculator
|
def reference_calculator
|
||||||
if current_user.ordergroup
|
if current_user.ordergroup
|
||||||
|
@ -17,7 +18,7 @@ class HomeController < ApplicationController
|
||||||
@bank_accounts = @types.includes(:bank_account).map(&:bank_account).uniq.compact
|
@bank_accounts = @types.includes(:bank_account).map(&:bank_account).uniq.compact
|
||||||
@bank_accounts = [BankAccount.last] if @bank_accounts.empty?
|
@bank_accounts = [BankAccount.last] if @bank_accounts.empty?
|
||||||
else
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ class HomeController < ApplicationController
|
||||||
if @current_user.update(user_params)
|
if @current_user.update(user_params)
|
||||||
@current_user.ordergroup.update(ordergroup_params) if ordergroup_params
|
@current_user.ordergroup.update(ordergroup_params) if ordergroup_params
|
||||||
session[:locale] = @current_user.locale
|
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
|
else
|
||||||
render :profile
|
render :profile
|
||||||
end
|
end
|
||||||
|
@ -35,43 +36,40 @@ class HomeController < ApplicationController
|
||||||
@user = @current_user
|
@user = @current_user
|
||||||
@ordergroup = @user.ordergroup
|
@ordergroup = @user.ordergroup
|
||||||
|
|
||||||
if @ordergroup.nil?
|
unless @ordergroup.nil?
|
||||||
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
|
|
||||||
else
|
|
||||||
|
|
||||||
@ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id)
|
@ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id)
|
||||||
|
|
||||||
sort = if params['sort']
|
if params['sort']
|
||||||
case params['sort']
|
sort = case params['sort']
|
||||||
when 'date' then 'created_on'
|
when "date" then "created_on"
|
||||||
when 'note' then 'note'
|
when "note" then "note"
|
||||||
when 'amount' then 'amount'
|
when "amount" then "amount"
|
||||||
when 'date_reverse' then 'created_on DESC'
|
when "date_reverse" then "created_on DESC"
|
||||||
when 'note_reverse' then 'note DESC'
|
when "note_reverse" then "note DESC"
|
||||||
when 'amount_reverse' then 'amount DESC'
|
when "amount_reverse" then "amount DESC"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
'created_on DESC'
|
sort = "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]}%")
|
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
# cancel personal memberships direct from the myProfile-page
|
# cancel personal memberships direct from the myProfile-page
|
||||||
def cancel_membership
|
def cancel_membership
|
||||||
membership = if params[:membership_id]
|
if params[:membership_id]
|
||||||
@current_user.memberships.find(params[:membership_id])
|
membership = @current_user.memberships.find(params[:membership_id])
|
||||||
else
|
else
|
||||||
@current_user.memberships.find_by_group_id!(params[:group_id])
|
membership = @current_user.memberships.find_by_group_id!(params[:group_id])
|
||||||
end
|
end
|
||||||
membership.destroy
|
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
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
@ -84,8 +82,8 @@ class HomeController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def ordergroup_params
|
def ordergroup_params
|
||||||
return unless params[:user][:ordergroup]
|
if params[:user][:ordergroup]
|
||||||
|
params.require(:user).require(:ordergroup).permit(:contact_address)
|
||||||
params.require(:user).require(:ordergroup).permit(:contact_address)
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ class InvitesController < ApplicationController
|
||||||
before_action -> { require_config_disabled :disable_invite }
|
before_action -> { require_config_disabled :disable_invite }
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@invite = Invite.new(user: @current_user, group: @group)
|
@invite = Invite.new(:user => @current_user, :group => @group)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -27,10 +27,6 @@ class InvitesController < ApplicationController
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def authenticate_membership_or_admin_for_invites
|
def authenticate_membership_or_admin_for_invites
|
||||||
authenticate_membership_or_admin(begin
|
authenticate_membership_or_admin((params[:invite][:group_id] rescue params[:id]))
|
||||||
params[:invite][:group_id]
|
|
||||||
rescue StandardError
|
|
||||||
params[:id]
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class LoginController < ApplicationController
|
class LoginController < ApplicationController
|
||||||
skip_before_action :authenticate # no authentication since this is the login page
|
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.
|
# Display the form to enter an email address requesting a token to set a new password.
|
||||||
def forgot_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".
|
# Sends an email to a user with the token that allows setting a new password through action "password".
|
||||||
def reset_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]))
|
if (user = User.undeleted.find_by_email(params[:user][:email]))
|
||||||
user.request_password_reset!
|
user.request_password_reset!
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
# Set a new password with a token from the password reminder email.
|
# 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.
|
# 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.
|
# Sets a new password.
|
||||||
# Called with params :id => User.id and :token => User.reset_password_token to specify 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_token = nil
|
||||||
@user.reset_password_expires = nil
|
@user.reset_password_expires = nil
|
||||||
@user.save
|
@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
|
else
|
||||||
render :new_password
|
render :new_password
|
||||||
end
|
end
|
||||||
|
@ -47,14 +50,14 @@ class LoginController < ApplicationController
|
||||||
@user = User.new(params[:user])
|
@user = User.new(params[:user])
|
||||||
@user.email = @invite.email
|
@user.email = @invite.email
|
||||||
if @user.save
|
if @user.save
|
||||||
Membership.new(user: @user, group: @invite.group).save!
|
Membership.new(:user => @user, :group => @invite.group).save!
|
||||||
@invite.destroy
|
@invite.destroy
|
||||||
session[:locale] = @user.locale
|
session[:locale] = @user.locale
|
||||||
redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice')
|
redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@user = User.new(email: @invite.email)
|
@user = User.new(:email => @invite.email)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,8 +65,8 @@ class LoginController < ApplicationController
|
||||||
|
|
||||||
def validate_token
|
def validate_token
|
||||||
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
@user = User.find_by_id_and_reset_password_token(params[:id], params[:token])
|
||||||
return unless @user.nil? || @user.reset_password_expires < Time.now
|
if (@user.nil? || @user.reset_password_expires < Time.now)
|
||||||
|
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
||||||
redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid')
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class OrderArticlesController < ApplicationController
|
class OrderArticlesController < ApplicationController
|
||||||
before_action :fetch_order, except: :destroy
|
before_action :fetch_order, except: :destroy
|
||||||
before_action :authenticate_finance_or_invoices, except: %i[new create]
|
before_action :authenticate_finance_or_invoices, except: [:new, :create]
|
||||||
before_action :authenticate_finance_orders_or_pickup, except: %i[edit update destroy]
|
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
|
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])
|
@order_article = @order.order_articles.build(params[:order_article])
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@order_article = OrderArticle.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
# The article may be ordered with zero units - in that case do not complain.
|
# 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
|
# 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.
|
# 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.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
|
unless @order_article && @order_article.units_to_order == 0
|
||||||
|
@order_article = @order.order_articles.build(params[:order_article])
|
||||||
|
end
|
||||||
@order_article.save!
|
@order_article.save!
|
||||||
rescue StandardError
|
rescue
|
||||||
render action: :new
|
render action: :new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@order_article = OrderArticle.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@order_article = OrderArticle.find(params[:id])
|
@order_article = OrderArticle.find(params[:id])
|
||||||
begin
|
begin
|
||||||
@order_article.update_article_and_price!(params[:order_article], params[:article], params[:article_price])
|
@order_article.update_article_and_price!(params[:order_article], params[:article], params[:article_price])
|
||||||
rescue StandardError
|
rescue
|
||||||
render action: :edit
|
render action: :edit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
class OrderCommentsController < ApplicationController
|
class OrderCommentsController < ApplicationController
|
||||||
def new
|
def new
|
||||||
@order = Order.find(params[:order_id])
|
@order = Order.find(params[:order_id])
|
||||||
@order_comment = @order.comments.build(user: current_user)
|
@order_comment = @order.comments.build(:user => current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@order_comment = OrderComment.new(params[:order_comment])
|
@order_comment = OrderComment.new(params[:order_comment])
|
||||||
if @order_comment.save
|
if @order_comment.save
|
||||||
render layout: false
|
render :layout => false
|
||||||
else
|
else
|
||||||
render action: :new, layout: false
|
render :action => :new, :layout => false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,26 +5,25 @@ class OrdersController < ApplicationController
|
||||||
include Concerns::SendOrderPdf
|
include Concerns::SendOrderPdf
|
||||||
|
|
||||||
before_action :authenticate_pickups_or_orders
|
before_action :authenticate_pickups_or_orders
|
||||||
before_action :authenticate_orders,
|
before_action :authenticate_orders, except: [:receive, :receive_on_order_article_create, :receive_on_order_article_update, :show]
|
||||||
except: %i[receive receive_on_order_article_create receive_on_order_article_update show]
|
before_action :remove_empty_article, only: [:create, :update]
|
||||||
before_action :remove_empty_article, only: %i[create update]
|
|
||||||
|
|
||||||
# List orders
|
# List orders
|
||||||
def index
|
def index
|
||||||
@open_orders = Order.open.includes(:supplier)
|
@open_orders = Order.open.includes(:supplier)
|
||||||
@finished_orders = Order.finished_not_closed.includes(:supplier)
|
@finished_orders = Order.finished_not_closed.includes(:supplier)
|
||||||
@per_page = 15
|
@per_page = 15
|
||||||
sort = if params['sort']
|
if params['sort']
|
||||||
case params['sort']
|
sort = case params['sort']
|
||||||
when 'supplier' then 'suppliers.name, ends DESC'
|
when "supplier" then "suppliers.name, ends DESC"
|
||||||
when 'pickup' then 'pickup DESC'
|
when "pickup" then "pickup DESC"
|
||||||
when 'ends' then 'ends DESC'
|
when "ends" then "ends DESC"
|
||||||
when 'supplier_reverse' then 'suppliers.name DESC'
|
when "supplier_reverse" then "suppliers.name DESC"
|
||||||
when 'ends_reverse' then 'ends'
|
when "ends_reverse" then "ends"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
'ends DESC'
|
sort = "ends DESC"
|
||||||
end
|
end
|
||||||
@suppliers = Supplier.having_articles.order('suppliers.name')
|
@suppliers = Supplier.having_articles.order('suppliers.name')
|
||||||
@orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page)
|
@orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page)
|
||||||
end
|
end
|
||||||
|
@ -44,13 +43,13 @@ class OrdersController < ApplicationController
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.js do
|
format.js do
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
format.pdf do
|
format.pdf do
|
||||||
send_order_pdf @order, params[:document]
|
send_order_pdf @order, params[:document]
|
||||||
end
|
end
|
||||||
format.csv do
|
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
|
end
|
||||||
format.text do
|
format.text do
|
||||||
send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain'
|
send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain'
|
||||||
|
@ -58,6 +57,19 @@ class OrdersController < ApplicationController
|
||||||
end
|
end
|
||||||
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.
|
# Page to create a new order.
|
||||||
def new
|
def new
|
||||||
if params[:order_id]
|
if params[:order_id]
|
||||||
|
@ -67,14 +79,8 @@ class OrdersController < ApplicationController
|
||||||
else
|
else
|
||||||
@order = Order.new(supplier_id: params[:supplier_id]).init_dates
|
@order = Order.new(supplier_id: params[:supplier_id]).init_dates
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to orders_url, alert: t('errors.general_msg', msg: e.message)
|
redirect_to orders_url, alert: t('errors.general_msg', msg: error.message)
|
||||||
end
|
|
||||||
|
|
||||||
# Page to edit an exsiting order.
|
|
||||||
# editing finished orders is done in FinanceController
|
|
||||||
def edit
|
|
||||||
@order = Order.includes(:articles).find(params[:id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Save a new order.
|
# Save a new order.
|
||||||
|
@ -88,25 +94,31 @@ class OrdersController < ApplicationController
|
||||||
redirect_to @order
|
redirect_to @order
|
||||||
else
|
else
|
||||||
logger.debug "[debug] order errors: #{@order.errors.messages}"
|
logger.debug "[debug] order errors: #{@order.errors.messages}"
|
||||||
render action: 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
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.
|
# Update an existing order.
|
||||||
def update
|
def update
|
||||||
@order = Order.find params[:id]
|
@order = Order.find params[:id]
|
||||||
if @order.update(params[:order].merge(updated_by: current_user))
|
if @order.update(params[:order].merge(updated_by: current_user))
|
||||||
flash[:notice] = I18n.t('orders.update.notice')
|
flash[:notice] = I18n.t('orders.update.notice')
|
||||||
redirect_to action: 'show', id: @order
|
redirect_to :action => 'show', :id => @order
|
||||||
else
|
else
|
||||||
render action: 'edit'
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delete an order.
|
# Delete an order.
|
||||||
def destroy
|
def destroy
|
||||||
Order.find(params[:id]).destroy
|
Order.find(params[:id]).destroy
|
||||||
redirect_to action: 'index'
|
redirect_to :action => 'index'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finish a current order.
|
# Finish a current order.
|
||||||
|
@ -114,8 +126,8 @@ class OrdersController < ApplicationController
|
||||||
order = Order.find(params[:id])
|
order = Order.find(params[:id])
|
||||||
order.finish!(@current_user)
|
order.finish!(@current_user)
|
||||||
redirect_to order, notice: I18n.t('orders.finish.notice')
|
redirect_to order, notice: I18n.t('orders.finish.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message)
|
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Send a order to the supplier.
|
# Send a order to the supplier.
|
||||||
|
@ -123,18 +135,20 @@ class OrdersController < ApplicationController
|
||||||
order = Order.find(params[:id])
|
order = Order.find(params[:id])
|
||||||
order.send_to_supplier!(@current_user)
|
order.send_to_supplier!(@current_user)
|
||||||
redirect_to order, notice: I18n.t('orders.send_to_supplier.notice')
|
redirect_to order, notice: I18n.t('orders.send_to_supplier.notice')
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message)
|
redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def receive
|
def receive
|
||||||
@order = Order.find(params[:id])
|
@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
|
Order.transaction do
|
||||||
s = update_order_amounts
|
s = update_order_amounts
|
||||||
@order.update_attribute(:state, 'received') if @order.state != 'received'
|
@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
|
end
|
||||||
NotifyReceivedOrderJob.perform_later(@order)
|
NotifyReceivedOrderJob.perform_later(@order)
|
||||||
if current_user.role_orders? || current_user.role_finance?
|
if current_user.role_orders? || current_user.role_finance?
|
||||||
|
@ -144,25 +158,23 @@ class OrdersController < ApplicationController
|
||||||
else
|
else
|
||||||
redirect_to receive_order_path(@order)
|
redirect_to receive_order_path(@order)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
@order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def receive_on_order_article_create # See publish/subscribe design pattern in /doc.
|
def receive_on_order_article_create # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def receive_on_order_article_update # See publish/subscribe design pattern in /doc.
|
def receive_on_order_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@order_article = OrderArticle.find(params[:order_article_id])
|
@order_article = OrderArticle.find(params[:order_article_id])
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def update_order_amounts
|
def update_order_amounts
|
||||||
return unless params[:order_articles]
|
return if not params[:order_articles]
|
||||||
|
|
||||||
# where to leave remainder during redistribution
|
# where to leave remainder during redistribution
|
||||||
rest_to = []
|
rest_to = []
|
||||||
|
@ -177,42 +189,35 @@ class OrdersController < ApplicationController
|
||||||
# "MySQL lock timeout exceeded" errors. It's ok to do
|
# "MySQL lock timeout exceeded" errors. It's ok to do
|
||||||
# this article-by-article anway.
|
# this article-by-article anway.
|
||||||
params[:order_articles].each do |oa_id, oa_params|
|
params[:order_articles].each do |oa_id, oa_params|
|
||||||
next if oa_params.blank?
|
unless oa_params.blank?
|
||||||
|
oa = OrderArticle.find(oa_id)
|
||||||
oa = OrderArticle.find(oa_id)
|
# update attributes; don't use update_attribute because it calls save
|
||||||
# update attributes; don't use update_attribute because it calls save
|
# which makes received_changed? not work anymore
|
||||||
# which makes received_changed? not work anymore
|
oa.attributes = oa_params
|
||||||
oa.attributes = oa_params
|
if oa.units_received_changed?
|
||||||
if oa.units_received_changed?
|
counts[0] += 1
|
||||||
counts[0] += 1
|
unless oa.units_received.blank?
|
||||||
if oa.units_received.present?
|
cunits[0] += oa.units_received * oa.article.unit_quantity
|
||||||
cunits[0] += oa.units_received * oa.article.unit_quantity
|
oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to
|
||||||
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 }
|
||||||
oacounts.each_with_index do |c, i|
|
|
||||||
cunits[i + 1] += c
|
|
||||||
counts[i + 1] += 1 if c > 0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
oa.save!
|
||||||
end
|
end
|
||||||
oa.save!
|
|
||||||
end
|
end
|
||||||
return nil if counts[0] == 0
|
return nil if counts[0] == 0
|
||||||
|
|
||||||
notice = []
|
notice = []
|
||||||
notice << I18n.t('orders.update_order_amounts.msg1', count: counts[0], units: cunits[0])
|
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]) if params[:rest_to_tolerance]
|
||||||
notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1],
|
|
||||||
units: cunits[1])
|
|
||||||
end
|
|
||||||
notice << I18n.t('orders.update_order_amounts.msg3', count: counts[2], units: cunits[2]) if params[:rest_to_stock]
|
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
|
if counts[3] > 0 || cunits[3] > 0
|
||||||
notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3],
|
notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3], units: cunits[3])
|
||||||
units: cunits[3])
|
|
||||||
end
|
end
|
||||||
notice.join(', ')
|
notice.join(', ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_empty_article
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,20 +12,16 @@ class SessionsController < ApplicationController
|
||||||
user = User.authenticate(params[:nick], params[:password])
|
user = User.authenticate(params[:nick], params[:password])
|
||||||
if user
|
if user
|
||||||
user.update_attribute(:last_login, Time.now)
|
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
|
else
|
||||||
flash.now.alert = I18n.t(FoodsoftConfig[:use_nick] ? 'sessions.login_invalid_nick' : 'sessions.login_invalid_email')
|
flash.now.alert = I18n.t(FoodsoftConfig[:use_nick] ? 'sessions.login_invalid_nick' : 'sessions.login_invalid_email')
|
||||||
render 'new'
|
render "new"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
logout
|
logout
|
||||||
if FoodsoftConfig[:logout_redirect_url].present?
|
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
||||||
redirect_to FoodsoftConfig[:logout_redirect_url]
|
|
||||||
else
|
|
||||||
redirect_to login_url, notice: I18n.t('sessions.logged_out')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# redirect to root, going to default foodcoop when none given
|
# redirect to root, going to default foodcoop when none given
|
||||||
|
|
|
@ -7,21 +7,21 @@ class StockTakingsController < ApplicationController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@stock_taking = StockTaking.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
|
end
|
||||||
|
|
||||||
def new_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
def new_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||||
stock_article = StockArticle.find(params[:stock_article_id])
|
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
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
create!(notice: I18n.t('stock_takings.create.notice'))
|
create!(:notice => I18n.t('stock_takings.create.notice'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
update!(notice: I18n.t('stock_takings.update.notice'))
|
update!(:notice => I18n.t('stock_takings.update.notice'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,18 +7,13 @@ class StockitController < ApplicationController
|
||||||
def index_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
def index_on_stock_article_create # See publish/subscribe design pattern in /doc.
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def index_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
def index_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
@stock_article = StockArticle.find(params[:id])
|
|
||||||
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# three possibilites to fill a new_stock_article form
|
# three possibilites to fill a new_stock_article form
|
||||||
|
@ -26,63 +21,68 @@ class StockitController < ApplicationController
|
||||||
def new
|
def new
|
||||||
@stock_article = StockArticle.new(params[:stock_article])
|
@stock_article = StockArticle.new(params[:stock_article])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
# (2) StockArticle as template
|
# (2) StockArticle as template
|
||||||
def copy
|
def copy
|
||||||
@stock_article = StockArticle.find(params[:stock_article_id]).dup
|
@stock_article = StockArticle.find(params[:stock_article_id]).dup
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
# (3) non-stock Article as template
|
# (3) non-stock Article as template
|
||||||
def derive
|
def derive
|
||||||
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
|
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
@stock_article = StockArticle.find(params[:id])
|
|
||||||
|
|
||||||
render layout: false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
|
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
|
||||||
@stock_article.save!
|
@stock_article.save!
|
||||||
render layout: false
|
render :layout => false
|
||||||
rescue ActiveRecord::RecordInvalid
|
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
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
@stock_article.update!(params[:stock_article])
|
@stock_article.update!(params[:stock_article])
|
||||||
render layout: false
|
render :layout => false
|
||||||
rescue ActiveRecord::RecordInvalid
|
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
|
end
|
||||||
|
|
||||||
def show_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
def show_on_stock_article_update # See publish/subscribe design pattern in /doc.
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
|
|
||||||
render layout: false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@stock_article = StockArticle.find(params[:id])
|
@stock_article = StockArticle.find(params[:id])
|
||||||
@stock_article.mark_as_deleted
|
@stock_article.mark_as_deleted
|
||||||
render layout: false
|
render :layout => false
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
render partial: 'destroy_fail', layout: false,
|
render :partial => "destroy_fail", :layout => false,
|
||||||
locals: { fail_msg: I18n.t('errors.general_msg', msg: e.message) }
|
:locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.message) }
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Fix this!!
|
# TODO: Fix this!!
|
||||||
def articles_search
|
def articles_search
|
||||||
@articles = Article.not_in_stock.limit(8).where('name LIKE ?', "%#{params[:term]}%")
|
@articles = Article.not_in_stock.limit(8).where('name LIKE ?', "%#{params[:term]}%")
|
||||||
render json: @articles.map(&:name)
|
render :json => @articles.map(&:name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ class StylesController < ApplicationController
|
||||||
def foodcoop
|
def foodcoop
|
||||||
css = FoodsoftConfig[:custom_css]
|
css = FoodsoftConfig[:custom_css]
|
||||||
if css.blank?
|
if css.blank?
|
||||||
render body: nil, content_type: 'text/css', status: :not_found
|
render body: nil, content_type: 'text/css', status: 404
|
||||||
else
|
else
|
||||||
expires_in 1.week, public: true if params[:md5].present?
|
expires_in 1.week, public: true if params[:md5].present?
|
||||||
render body: css, content_type: 'text/css'
|
render body: css, content_type: 'text/css'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class SuppliersController < ApplicationController
|
class SuppliersController < ApplicationController
|
||||||
before_action :authenticate_suppliers, except: %i[index list]
|
before_action :authenticate_suppliers, :except => [:index, :list]
|
||||||
helper :deliveries
|
helper :deliveries
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -24,10 +24,6 @@ class SuppliersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@supplier = Supplier.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@supplier = Supplier.new(supplier_params)
|
@supplier = Supplier.new(supplier_params)
|
||||||
@supplier.supplier_category ||= SupplierCategory.first
|
@supplier.supplier_category ||= SupplierCategory.first
|
||||||
|
@ -35,17 +31,21 @@ class SuppliersController < ApplicationController
|
||||||
flash[:notice] = I18n.t('suppliers.create.notice')
|
flash[:notice] = I18n.t('suppliers.create.notice')
|
||||||
redirect_to suppliers_path
|
redirect_to suppliers_path
|
||||||
else
|
else
|
||||||
render action: 'new'
|
render :action => 'new'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@supplier = Supplier.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@supplier = Supplier.find(params[:id])
|
@supplier = Supplier.find(params[:id])
|
||||||
if @supplier.update(supplier_params)
|
if @supplier.update(supplier_params)
|
||||||
flash[:notice] = I18n.t('suppliers.update.notice')
|
flash[:notice] = I18n.t('suppliers.update.notice')
|
||||||
redirect_to @supplier
|
redirect_to @supplier
|
||||||
else
|
else
|
||||||
render action: 'edit'
|
render :action => 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ class SuppliersController < ApplicationController
|
||||||
@supplier.mark_as_deleted
|
@supplier.mark_as_deleted
|
||||||
flash[:notice] = I18n.t('suppliers.destroy.notice')
|
flash[:notice] = I18n.t('suppliers.destroy.notice')
|
||||||
redirect_to suppliers_path
|
redirect_to suppliers_path
|
||||||
rescue StandardError => e
|
rescue => e
|
||||||
flash[:error] = I18n.t('errors.general_msg', msg: e.message)
|
flash[:error] = I18n.t('errors.general_msg', :msg => e.message)
|
||||||
redirect_to @supplier
|
redirect_to @supplier
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,33 +11,35 @@ class TasksController < ApplicationController
|
||||||
@accepted_tasks = Task.accepted_tasks_for(current_user)
|
@accepted_tasks = Task.accepted_tasks_for(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
|
||||||
@task = Task.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@task = Task.new(current_user_id: current_user.id)
|
@task = Task.new(current_user_id: current_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
@task = Task.find(params[:id])
|
|
||||||
@periodic = !!params[:periodic]
|
|
||||||
@task.current_user_id = current_user.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@task = Task.new(current_user_id: current_user.id)
|
@task = Task.new(current_user_id: current_user.id)
|
||||||
@task.created_by = current_user
|
@task.created_by = current_user
|
||||||
@task.attributes = (task_params)
|
@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
|
if @task.save
|
||||||
@task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic]
|
@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
|
else
|
||||||
render template: 'tasks/new'
|
render :template => "tasks/new"
|
||||||
end
|
end
|
||||||
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
|
def update
|
||||||
@task = Task.find(params[:id])
|
@task = Task.find(params[:id])
|
||||||
task_group = @task.periodic_task_group
|
task_group = @task.periodic_task_group
|
||||||
|
@ -48,14 +50,16 @@ class TasksController < ApplicationController
|
||||||
if @task.errors.empty? && @task.save
|
if @task.errors.empty? && @task.save
|
||||||
task_group.update_tasks_including(@task, prev_due_date) if params[:periodic]
|
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')
|
||||||
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
|
if @task.workgroup
|
||||||
redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id)
|
redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id)
|
||||||
else
|
else
|
||||||
redirect_to tasks_url
|
redirect_to tasks_url
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
render template: 'tasks/edit'
|
render :template => "tasks/edit"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ class TasksController < ApplicationController
|
||||||
end
|
end
|
||||||
task.update_ordergroup_stats(user_ids)
|
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
|
end
|
||||||
|
|
||||||
# assign current_user to the task and set the assignment to "accepted"
|
# 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)
|
if ass = task.is_assigned?(current_user)
|
||||||
ass.update_attribute(:accepted, true)
|
ass.update_attribute(:accepted, true)
|
||||||
else
|
else
|
||||||
task.assignments.create(user: current_user, accepted: true)
|
task.assignments.create(:user => current_user, :accepted => true)
|
||||||
end
|
end
|
||||||
redirect_to user_tasks_path, notice: I18n.t('tasks.accept.notice')
|
redirect_to user_tasks_path, :notice => I18n.t('tasks.accept.notice')
|
||||||
end
|
end
|
||||||
|
|
||||||
# deletes assignment between current_user and given taskcurrent_user_id: current_user.id
|
# deletes assignment between current_user and given taskcurrent_user_id: current_user.id
|
||||||
def reject
|
def reject
|
||||||
Task.find(params[:id]).users.delete(current_user)
|
Task.find(params[:id]).users.delete(current_user)
|
||||||
redirect_to action: 'index'
|
redirect_to :action => "index"
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_done
|
def set_done
|
||||||
Task.find(params[:id]).update_attribute :done, true
|
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
|
end
|
||||||
|
|
||||||
# Shows all tasks, which are already done
|
# Shows all tasks, which are already done
|
||||||
|
@ -105,9 +109,9 @@ class TasksController < ApplicationController
|
||||||
# shows workgroup (normal group) to edit weekly_tasks_template
|
# shows workgroup (normal group) to edit weekly_tasks_template
|
||||||
def workgroup
|
def workgroup
|
||||||
@group = Group.find(params[:workgroup_id])
|
@group = Group.find(params[:workgroup_id])
|
||||||
return unless @group.is_a? Ordergroup
|
if @group.is_a? Ordergroup
|
||||||
|
redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found')
|
||||||
redirect_to tasks_url, alert: I18n.t('tasks.error_not_found')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -3,7 +3,7 @@ class UsersController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@users = User.undeleted.natural_search(params[:q])
|
@users = User.undeleted.natural_search(params[:q])
|
||||||
respond_to do |format|
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
class OrderByArticles < OrderPdf
|
class OrderByArticles < OrderPdf
|
||||||
def filename
|
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
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
I18n.t('documents.order_by_articles.title', name: order.name,
|
I18n.t('documents.order_by_articles.title', :name => order.name,
|
||||||
date: order.ends.strftime(I18n.t('date.formats.default')))
|
:date => order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
end
|
end
|
||||||
|
|
||||||
def body
|
def body
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
class OrderByGroups < OrderPdf
|
class OrderByGroups < OrderPdf
|
||||||
def filename
|
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
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
I18n.t('documents.order_by_groups.title', name: order.name,
|
I18n.t('documents.order_by_groups.title', :name => order.name,
|
||||||
date: order.ends.strftime(I18n.t('date.formats.default')))
|
:date => order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
end
|
end
|
||||||
|
|
||||||
def body
|
def body
|
||||||
|
|
|
@ -2,7 +2,7 @@ class OrderFax < OrderPdf
|
||||||
BATCH_SIZE = 250
|
BATCH_SIZE = 250
|
||||||
|
|
||||||
def filename
|
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
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
|
@ -20,18 +20,16 @@ class OrderFax < OrderPdf
|
||||||
move_down 5
|
move_down 5
|
||||||
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :right
|
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :right
|
||||||
move_down 5
|
move_down 5
|
||||||
if order.supplier.try(:customer_number).present?
|
unless order.supplier.try(:customer_number).blank?
|
||||||
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}",
|
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", size: fontsize(9), align: :right
|
||||||
size: fontsize(9), align: :right
|
|
||||||
move_down 5
|
move_down 5
|
||||||
end
|
end
|
||||||
if contact[:phone].present?
|
unless contact[:phone].blank?
|
||||||
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :right
|
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :right
|
||||||
move_down 5
|
move_down 5
|
||||||
end
|
end
|
||||||
if contact[:email].present?
|
unless contact[:email].blank?
|
||||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9),
|
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :right
|
||||||
align: :right
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,7 +38,7 @@ class OrderFax < OrderPdf
|
||||||
text order.name
|
text order.name
|
||||||
move_down 5
|
move_down 5
|
||||||
text order.supplier.try(:address).to_s
|
text order.supplier.try(:address).to_s
|
||||||
if order.supplier.try(:fax).present?
|
unless order.supplier.try(:fax).blank?
|
||||||
move_down 5
|
move_down 5
|
||||||
text "#{Supplier.human_attribute_name :fax}: #{order.supplier[:fax]}"
|
text "#{Supplier.human_attribute_name :fax}: #{order.supplier[:fax]}"
|
||||||
end
|
end
|
||||||
|
@ -52,7 +50,7 @@ class OrderFax < OrderPdf
|
||||||
move_down 10
|
move_down 10
|
||||||
text "#{Delivery.human_attribute_name :date}:"
|
text "#{Delivery.human_attribute_name :date}:"
|
||||||
move_down 10
|
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]}"
|
text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
|
||||||
move_down 10
|
move_down 10
|
||||||
end
|
end
|
||||||
|
@ -80,8 +78,8 @@ class OrderFax < OrderPdf
|
||||||
table.row(0).border_bottom_width = 2
|
table.row(0).border_bottom_width = 2
|
||||||
table.columns(1).align = :right
|
table.columns(1).align = :right
|
||||||
table.columns(3..6).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..5).borders = [:top, :bottom]
|
||||||
table.row(data.length - 1).columns(0).borders = %i[top bottom left]
|
table.row(data.length - 1).columns(0).borders = [:top, :bottom, :left]
|
||||||
table.row(data.length - 1).border_top_width = 2
|
table.row(data.length - 1).border_top_width = 2
|
||||||
end
|
end
|
||||||
# font_size: fontsize(8),
|
# font_size: fontsize(8),
|
||||||
|
@ -100,7 +98,7 @@ class OrderFax < OrderPdf
|
||||||
.preload(:article, :article_price)
|
.preload(:article, :article_price)
|
||||||
end
|
end
|
||||||
|
|
||||||
def each_order_article(&block)
|
def each_order_article
|
||||||
order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block)
|
order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,12 @@ class OrderMatrix < OrderPdf
|
||||||
PLACEHOLDER_CHAR = 'X'
|
PLACEHOLDER_CHAR = 'X'
|
||||||
|
|
||||||
def filename
|
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
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
I18n.t('documents.order_matrix.title', name: @order.name,
|
I18n.t('documents.order_matrix.title', :name => @order.name,
|
||||||
date: @order.ends.strftime(I18n.t('date.formats.default')))
|
:date => @order.ends.strftime(I18n.t('date.formats.default')))
|
||||||
end
|
end
|
||||||
|
|
||||||
def body
|
def body
|
||||||
|
@ -87,7 +87,7 @@ class OrderMatrix < OrderPdf
|
||||||
table.cells.border_width = 0.5
|
table.cells.border_width = 0.5
|
||||||
table.cells.border_color = '666666'
|
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(0).padding = [2, 0, 2, 0]
|
||||||
table.row(1..-1).height = row_height_1
|
table.row(1..-1).height = row_height_1
|
||||||
table.column(0..1).borders = []
|
table.column(0..1).borders = []
|
||||||
|
@ -106,7 +106,7 @@ class OrderMatrix < OrderPdf
|
||||||
table.column(2 + idx).border_width = 2
|
table.column(2 + idx).border_width = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
table.row_colors = %w[dddddd ffffff]
|
table.row_colors = ['dddddd', 'ffffff']
|
||||||
end
|
end
|
||||||
|
|
||||||
first_page = false
|
first_page = false
|
||||||
|
|
|
@ -28,11 +28,7 @@ module Admin::ConfigsHelper
|
||||||
options[:default] = options[:input_html].delete(:value)
|
options[:default] = options[:input_html].delete(:value)
|
||||||
return form.input key, options, &block
|
return form.input key, options, &block
|
||||||
end
|
end
|
||||||
if options[:as] == :select_recurring
|
block ||= proc { config_input_field form, key, options.merge(options[:input_html]) } if options[:as] == :select_recurring
|
||||||
block ||= proc {
|
|
||||||
config_input_field form, key, options.merge(options[:input_html])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
form.input key, options, &block
|
form.input key, options, &block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -61,12 +57,11 @@ module Admin::ConfigsHelper
|
||||||
unchecked_value = options.delete(:unchecked_value) || 'false'
|
unchecked_value = options.delete(:unchecked_value) || 'false'
|
||||||
options[:checked] = 'checked' if v = options.delete(:value) && v != '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
|
# different key for hidden field so that allow clocking on label focuses the control
|
||||||
form.hidden_field(key, id: "#{key}_", value: unchecked_value,
|
form.hidden_field(key, id: "#{key}_", value: unchecked_value, as: :hidden) + form.check_box(key, options, checked_value, false)
|
||||||
as: :hidden) + form.check_box(key, options, checked_value, false)
|
|
||||||
elsif options[:as] == :select_recurring
|
elsif options[:as] == :select_recurring
|
||||||
options[:value] = FoodsoftDateUtil.rule_from(options[:value])
|
options[:value] = FoodsoftDateUtil.rule_from(options[:value])
|
||||||
options[:rules] ||= []
|
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
|
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
|
form.select_recurring key, options.delete(:rules).uniq, options
|
||||||
else
|
else
|
||||||
|
@ -78,7 +73,7 @@ module Admin::ConfigsHelper
|
||||||
# @param form [ActionView::Helpers::FormBuilder] Form object.
|
# @param form [ActionView::Helpers::FormBuilder] Form object.
|
||||||
# @param key [Symbol, String] Configuration key of a boolean (e.g. +use_messages+).
|
# @param key [Symbol, String] Configuration key of a boolean (e.g. +use_messages+).
|
||||||
# @option options [String] :label Label to show
|
# @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
|
head = content_tag :label do
|
||||||
lbl = options[:label] || config_input_label(form, key)
|
lbl = options[:label] || config_input_label(form, key)
|
||||||
field = config_input_field(form, key, as: :boolean, boolean_style: :inline,
|
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, {})
|
content_tag :span, (lbl + field).html_safe, config_input_tooltip_options(form, key, {})
|
||||||
end
|
end
|
||||||
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
|
head + fields
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -130,7 +127,7 @@ module Admin::ConfigsHelper
|
||||||
# tooltip with help info to the right
|
# tooltip with help info to the right
|
||||||
cfg_path = form.lookup_model_names[1..-1] + [key]
|
cfg_path = form.lookup_model_names[1..-1] + [key]
|
||||||
tooltip = I18n.t("config.hints.#{cfg_path.map(&:to_s).join('.')}", default: '')
|
tooltip = I18n.t("config.hints.#{cfg_path.map(&:to_s).join('.')}", default: '')
|
||||||
if tooltip.present?
|
unless tooltip.blank?
|
||||||
options[:data] ||= {}
|
options[:data] ||= {}
|
||||||
options[:data][:toggle] ||= 'tooltip'
|
options[:data][:toggle] ||= 'tooltip'
|
||||||
options[:data][:placement] ||= 'right'
|
options[:data][:placement] ||= 'right'
|
||||||
|
|
|
@ -2,7 +2,9 @@ module Admin::OrdergroupsHelper
|
||||||
def ordergroup_members_title(ordergroup)
|
def ordergroup_members_title(ordergroup)
|
||||||
s = ''
|
s = ''
|
||||||
s += ordergroup.users.map(&:name).join(', ') if ordergroup.users.any?
|
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
|
s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ module ApplicationHelper
|
||||||
include PathHelper
|
include PathHelper
|
||||||
|
|
||||||
def format_time(time = Time.now)
|
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
|
end
|
||||||
|
|
||||||
def format_date(time = Time.now)
|
def format_date(time = Time.now)
|
||||||
|
@ -16,7 +16,7 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_datetime_timespec(time, format)
|
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
|
end
|
||||||
|
|
||||||
def format_currency(amount)
|
def format_currency(amount)
|
||||||
|
@ -26,28 +26,28 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Splits an IBAN into groups of 4 digits displayed with margins in between
|
# Splits an IBAN into groups of 4 digits displayed with margins in between
|
||||||
def format_iban(iban)
|
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
|
end
|
||||||
|
|
||||||
# Creates ajax-controlled-links for pagination
|
# Creates ajax-controlled-links for pagination
|
||||||
def pagination_links_remote(collection, options = {})
|
def pagination_links_remote(collection, options = {})
|
||||||
per_page = options[:per_page] || @per_page
|
per_page = options[:per_page] || @per_page
|
||||||
params = options[:params] || {}
|
params = options[:params] || {}
|
||||||
params = params.merge({ per_page: per_page })
|
params = params.merge({ :per_page => per_page })
|
||||||
paginate collection, params: params, remote: true
|
paginate collection, :params => params, :remote => true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Link-collection for per_page-options when using the pagination-plugin
|
# Link-collection for per_page-options when using the pagination-plugin
|
||||||
def items_per_page(options = {})
|
def items_per_page(options = {})
|
||||||
per_page_options = options[:per_page_options] || [20, 50, 100, 500]
|
per_page_options = options[:per_page_options] || [20, 50, 100, 500]
|
||||||
current = options[:current] || @per_page
|
current = options[:current] || @per_page
|
||||||
params ||= {}
|
params = params || {}
|
||||||
|
|
||||||
links = per_page_options.map do |per_page|
|
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 = 'btn'
|
||||||
link_class << ' disabled' if per_page == current
|
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
|
end
|
||||||
|
|
||||||
if options[:wrap] == false
|
if options[:wrap] == false
|
||||||
|
@ -63,19 +63,21 @@ module ApplicationHelper
|
||||||
# Hmtl options
|
# Hmtl options
|
||||||
remote = options[:remote].nil? ? true : options[:remote]
|
remote = options[:remote].nil? ? true : options[:remote]
|
||||||
class_name = case params[:sort]
|
class_name = case params[:sort]
|
||||||
when key
|
when key then
|
||||||
'sortup'
|
'sortup'
|
||||||
when key + '_reverse'
|
when key + '_reverse' then
|
||||||
'sortdown'
|
'sortdown'
|
||||||
|
else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
html_options = {
|
html_options = {
|
||||||
title: I18n.t('helpers.application.sort_by', text: text),
|
:title => I18n.t('helpers.application.sort_by', text: text),
|
||||||
remote: remote,
|
:remote => remote,
|
||||||
class: class_name
|
:class => class_name
|
||||||
}
|
}
|
||||||
|
|
||||||
# Url options
|
# Url options
|
||||||
key += '_reverse' if params[:sort] == key
|
key += "_reverse" if params[:sort] == key
|
||||||
per_page = options[:per_page] || @per_page
|
per_page = options[:per_page] || @per_page
|
||||||
url_options = params.merge(per_page: per_page, sort: key)
|
url_options = params.merge(per_page: per_page, sort: key)
|
||||||
url_options.merge!({ page: params[:page] }) if params[:page]
|
url_options.merge!({ page: params[:page] }) if params[:page]
|
||||||
|
@ -93,16 +95,14 @@ module ApplicationHelper
|
||||||
# be overridden by the option 'desc'.
|
# be overridden by the option 'desc'.
|
||||||
# Other options are passed through to I18n.
|
# Other options are passed through to I18n.
|
||||||
def heading_helper(model, attribute, options = {})
|
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)
|
s = model.human_attribute_name(attribute, i18nopts)
|
||||||
if options[:short]
|
if options[:short]
|
||||||
desc = options[:desc]
|
desc = options[:desc]
|
||||||
desc ||= model.human_attribute_name("#{attribute}_desc".to_sym,
|
desc ||= model.human_attribute_name("#{attribute}_desc".to_sym, options.merge({ fallback: true, default: '', count: 2 }))
|
||||||
options.merge({ fallback: true, default: '', count: 2 }))
|
|
||||||
desc.blank? && desc = s
|
desc.blank? && desc = s
|
||||||
sshort = model.human_attribute_name("#{attribute}_short".to_sym,
|
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({ fallback: true, default: '', count: 2 }))
|
||||||
options.merge({ fallback: true, default: '', count: 2 }))
|
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" unless sshort.blank?
|
||||||
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" if sshort.present?
|
|
||||||
end
|
end
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
@ -117,7 +117,7 @@ module ApplicationHelper
|
||||||
# Returns the weekday. 0 is sunday, 1 is monday and so on
|
# Returns the weekday. 0 is sunday, 1 is monday and so on
|
||||||
def weekday(dayNumber)
|
def weekday(dayNumber)
|
||||||
weekdays = I18n.t('date.day_names')
|
weekdays = I18n.t('date.day_names')
|
||||||
weekdays[dayNumber]
|
return weekdays[dayNumber]
|
||||||
end
|
end
|
||||||
|
|
||||||
# to set a title for both the h1-tag and the title in the header
|
# 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 = {})
|
def icon(name, options = {})
|
||||||
icons = {
|
icons = {
|
||||||
delete: { file: 'b_drop.png', alt: I18n.t('ui.delete') },
|
:delete => { :file => 'b_drop.png', :alt => I18n.t('ui.delete') },
|
||||||
edit: { file: 'b_edit.png', alt: I18n.t('ui.edit') },
|
:edit => { :file => 'b_edit.png', :alt => I18n.t('ui.edit') },
|
||||||
members: { file: 'b_users.png', alt: I18n.t('helpers.application.edit_user') }
|
:members => { :file => 'b_users.png', :alt => I18n.t('helpers.application.edit_user') }
|
||||||
}
|
}
|
||||||
options[:alt] ||= icons[name][:alt]
|
options[:alt] ||= icons[name][:alt]
|
||||||
options[:title] ||= icons[name][:title]
|
options[:title] ||= icons[name][:title]
|
||||||
options.merge!({ size: '16x16', border: '0' })
|
options.merge!({ :size => '16x16', :border => "0" })
|
||||||
|
|
||||||
image_tag icons[name][:file], options
|
image_tag icons[name][:file], options
|
||||||
end
|
end
|
||||||
|
@ -150,29 +150,27 @@ module ApplicationHelper
|
||||||
# Remote links with default 'loader'.gif during request
|
# Remote links with default 'loader'.gif during request
|
||||||
def remote_link_to(text, options = {})
|
def remote_link_to(text, options = {})
|
||||||
remote_options = {
|
remote_options = {
|
||||||
before: "Element.show('loader')",
|
:before => "Element.show('loader')",
|
||||||
success: "Element.hide('loader')",
|
:success => "Element.hide('loader')",
|
||||||
method: :get
|
:method => :get
|
||||||
}
|
}
|
||||||
link_to(text, options[:url], remote_options.merge(options))
|
link_to(text, options[:url], remote_options.merge(options))
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_roles(record, icon = false)
|
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}?" }
|
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
|
if icon
|
||||||
roles.map do |r|
|
roles.map { |r| image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) }.join(' ').html_safe
|
||||||
image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r])
|
|
||||||
end.join(' ').html_safe
|
|
||||||
else
|
else
|
||||||
roles.map { |r| names[r] }.join(', ')
|
roles.map { |r| names[r] }.join(', ')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_to_gmaps(address)
|
def link_to_gmaps(address)
|
||||||
link_to h(address), "http://maps.google.com/?q=#{h(address)}", title: I18n.t('helpers.application.show_google_maps'),
|
link_to h(address), "http://maps.google.com/?q=#{h(address)}", :title => I18n.t('helpers.application.show_google_maps'),
|
||||||
target: '_blank', rel: 'noopener'
|
:target => "_blank"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns flash messages html.
|
# Returns flash messages html.
|
||||||
|
@ -188,8 +186,8 @@ module ApplicationHelper
|
||||||
type = :success if type == 'notice'
|
type = :success if type == 'notice'
|
||||||
type = :error if type == 'alert'
|
type = :error if type == 'alert'
|
||||||
text = content_tag(:div,
|
text = content_tag(:div,
|
||||||
content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => 'close', 'data-dismiss' => 'alert') +
|
content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => "close", "data-dismiss" => "alert") +
|
||||||
message, class: "alert fade in alert-#{type}")
|
message, :class => "alert fade in alert-#{type}")
|
||||||
flash_messages << text if message
|
flash_messages << text if message
|
||||||
end
|
end
|
||||||
flash_messages.join("\n").html_safe
|
flash_messages.join("\n").html_safe
|
||||||
|
@ -197,17 +195,17 @@ module ApplicationHelper
|
||||||
|
|
||||||
# render base errors in a form after failed validation
|
# render base errors in a form after failed validation
|
||||||
# http://railsapps.github.io/twitter-bootstrap-rails.html
|
# 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?
|
return '' if resource.errors.empty? || resource.errors[:base].empty?
|
||||||
|
|
||||||
messages = resource.errors[:base].map { |msg| content_tag(:li, msg) }.join
|
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
|
end
|
||||||
|
|
||||||
# show a user, depending on settings
|
# show a user, depending on settings
|
||||||
def show_user(user = @current_user, options = {})
|
def show_user(user = @current_user, options = {})
|
||||||
if user.nil?
|
if user.nil?
|
||||||
'?'
|
"?"
|
||||||
elsif FoodsoftConfig[:use_nick]
|
elsif FoodsoftConfig[:use_nick]
|
||||||
if options[:full] && options[:markup]
|
if options[:full] && options[:markup]
|
||||||
raw "<b>#{h user.nick}</b> (#{h user.first_name} #{h user.last_name})"
|
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
|
user.nick.nil? ? I18n.t('helpers.application.nick_fallback') : user.nick
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
"#{user.first_name} #{user.last_name}" + (options[:unique] ? " (##{user.id})" : '')
|
"#{user.first_name} #{user.last_name}" + (options[:unique] ? " (\##{user.id})" : '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -260,9 +258,9 @@ module ApplicationHelper
|
||||||
|
|
||||||
# @return [String] stylesheet tag for foodcoop CSS style (+custom_css+ foodcoop config)
|
# @return [String] stylesheet tag for foodcoop CSS style (+custom_css+ foodcoop config)
|
||||||
# @see #foodcoop_css_path
|
# @see #foodcoop_css_path
|
||||||
def foodcoop_css_tag(_options = {})
|
def foodcoop_css_tag(options = {})
|
||||||
return if FoodsoftConfig[:custom_css].blank?
|
unless FoodsoftConfig[:custom_css].blank?
|
||||||
|
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
||||||
stylesheet_link_tag foodcoop_css_path, media: 'all'
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,13 +3,13 @@ module ArticlesHelper
|
||||||
def highlight_new(unequal_attributes, attribute)
|
def highlight_new(unequal_attributes, attribute)
|
||||||
return unless unequal_attributes
|
return unless unequal_attributes
|
||||||
|
|
||||||
unequal_attributes.has_key?(attribute) ? 'background-color: yellow' : ''
|
unequal_attributes.has_key?(attribute) ? "background-color: yellow" : ""
|
||||||
end
|
end
|
||||||
|
|
||||||
def row_classes(article)
|
def row_classes(article)
|
||||||
classes = []
|
classes = []
|
||||||
classes << 'unavailable' unless article.availability
|
classes << "unavailable" if !article.availability
|
||||||
classes << 'just-updated' if article.recently_updated && article.availability
|
classes << "just-updated" if article.recently_updated && article.availability
|
||||||
classes.join(' ')
|
classes.join(" ")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,11 +11,11 @@ module DeliveriesHelper
|
||||||
|
|
||||||
def articles_for_select2(articles, except = [], &block)
|
def articles_for_select2(articles, except = [], &block)
|
||||||
articles = articles.reorder('articles.name ASC')
|
articles = articles.reorder('articles.name ASC')
|
||||||
articles = articles.reject { |a| !except.index(a.id).nil? } if except
|
articles = articles.reject { |a| not except.index(a.id).nil? } if except
|
||||||
block_given? or block = proc { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
|
block_given? or block = Proc.new { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
|
||||||
articles.map do |a|
|
articles.map do |a|
|
||||||
{ id: a.id, text: block.call(a) }
|
{ :id => a.id, :text => block.call(a) }
|
||||||
end.unshift({ id: '', text: '' })
|
end.unshift({ :id => '', :text => '' })
|
||||||
end
|
end
|
||||||
|
|
||||||
def articles_for_table(articles)
|
def articles_for_table(articles)
|
||||||
|
@ -23,14 +23,10 @@ module DeliveriesHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def stock_change_remove_link(stock_change_form)
|
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' if stock_change_form.object.new_record?
|
||||||
return link_to t('deliveries.stock_change_fields.remove_article'), '#',
|
|
||||||
class: 'remove_new_stock_change btn btn-small'
|
|
||||||
end
|
|
||||||
|
|
||||||
output = stock_change_form.hidden_field :_destroy
|
output = stock_change_form.hidden_field :_destroy
|
||||||
output += link_to t('deliveries.stock_change_fields.remove_article'), '#',
|
output += link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'destroy_stock_change btn btn-small'
|
||||||
class: 'destroy_stock_change btn btn-small'
|
return output.html_safe
|
||||||
output.html_safe
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@ module Finance::BalancingHelper
|
||||||
def balancing_view_partial
|
def balancing_view_partial
|
||||||
view = params[:view] || 'edit_results'
|
view = params[:view] || 'edit_results'
|
||||||
case view
|
case view
|
||||||
when 'edit_results'
|
when 'edit_results' then
|
||||||
'edit_results_by_articles'
|
'edit_results_by_articles'
|
||||||
when 'groups_overview'
|
when 'groups_overview' then
|
||||||
'shared/articles_by/groups'
|
'shared/articles_by/groups'
|
||||||
when 'articles_overview'
|
when 'articles_overview' then
|
||||||
'shared/articles_by/articles'
|
'shared/articles_by/articles'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
module Finance::InvoicesHelper
|
module Finance::InvoicesHelper
|
||||||
def format_delivery_item(delivery)
|
def format_delivery_item delivery
|
||||||
format_date(delivery.date)
|
format_date(delivery.date)
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_order_item(order)
|
def format_order_item order
|
||||||
"#{format_date(order.ends)} (#{number_to_currency(order.sum)})"
|
"#{format_date(order.ends)} (#{number_to_currency(order.sum)})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,12 +2,12 @@ module GroupOrderArticlesHelper
|
||||||
# return an edit field for a GroupOrderArticle result
|
# return an edit field for a GroupOrderArticle result
|
||||||
def group_order_article_edit_result(goa)
|
def group_order_article_edit_result(goa)
|
||||||
result = number_with_precision goa.result, strip_insignificant_zeros: true
|
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|
|
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
|
f.input_field :result, as: :delta, class: 'input-nano', data: { min: 0 }, id: "r_#{goa.id}", value: result
|
||||||
end
|
end
|
||||||
else
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
module GroupOrdersHelper
|
module GroupOrdersHelper
|
||||||
def data_to_js(ordering_data)
|
def data_to_js(ordering_data)
|
||||||
ordering_data[:order_articles].map do |id, data|
|
ordering_data[:order_articles].map { |id, data|
|
||||||
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance],
|
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], data[:used_quantity], data[:quantity_available]]
|
||||||
data[:used_quantity], data[:quantity_available]]
|
}.map { |row|
|
||||||
end.map do |row|
|
|
||||||
"addData(#{row.join(', ')});"
|
"addData(#{row.join(', ')});"
|
||||||
end.join("\n")
|
}.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a link to the page where a group_order can be edited.
|
# 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
|
path = if options[:show] && group_order
|
||||||
group_order_path(group_order)
|
group_order_path(group_order)
|
||||||
elsif 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
|
else
|
||||||
new_group_order_path(order_id: order.id)
|
new_group_order_path(:order_id => order.id)
|
||||||
end
|
end
|
||||||
options.delete(:show)
|
options.delete(:show)
|
||||||
name = block_given? ? capture(&block) : order.name
|
name = block_given? ? capture(&block) : order.name
|
||||||
|
@ -27,7 +26,7 @@ module GroupOrdersHelper
|
||||||
# Return css class names for order result table
|
# Return css class names for order result table
|
||||||
|
|
||||||
def order_article_class_name(quantity, tolerance, result)
|
def order_article_class_name(quantity, tolerance, result)
|
||||||
if quantity + tolerance > 0
|
if (quantity + tolerance > 0)
|
||||||
result > 0 ? 'success' : 'failed'
|
result > 0 ? 'success' : 'failed'
|
||||||
else
|
else
|
||||||
'ignored'
|
'ignored'
|
||||||
|
@ -46,12 +45,20 @@ module GroupOrdersHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_missing_units_css_class(quantity_missing)
|
def get_missing_units_css_class(quantity_missing)
|
||||||
if quantity_missing == 1
|
if (quantity_missing == 1)
|
||||||
'missing-few'
|
return 'missing-few';
|
||||||
elsif quantity_missing == 0
|
elsif (quantity_missing == 0)
|
||||||
''
|
return ''
|
||||||
else
|
else
|
||||||
'missing-many'
|
return 'missing-many'
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module OrderArticlesHelper
|
module OrderArticlesHelper
|
||||||
def article_label_with_unit(article)
|
def article_label_with_unit(article)
|
||||||
pkg_info = pkg_helper(article, plain: true)
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ module OrdersHelper
|
||||||
|
|
||||||
def options_for_suppliers_to_select
|
def options_for_suppliers_to_select
|
||||||
options = [[I18n.t('helpers.orders.option_choose')]]
|
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 += [[I18n.t('helpers.orders.option_stock'), url_for(action: 'new', supplier_id: nil)]]
|
||||||
options_for_select(options)
|
options_for_select(options)
|
||||||
end
|
end
|
||||||
|
@ -29,13 +29,13 @@ module OrdersHelper
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
units_info = []
|
units_info = []
|
||||||
%i[units_to_order units_billed units_received].map do |unit|
|
[:units_to_order, :units_billed, :units_received].map do |unit|
|
||||||
next unless n = order_article.send(unit)
|
if n = order_article.send(unit)
|
||||||
|
line = n.to_s + ' '
|
||||||
line = n.to_s + ' '
|
line += pkg_helper(order_article.price, options) + ' ' unless n == 0
|
||||||
line += pkg_helper(order_article.price, options) + ' ' unless n == 0
|
line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
|
||||||
line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
|
units_info << line
|
||||||
units_info << line
|
end
|
||||||
end
|
end
|
||||||
units_info.join(', ').html_safe
|
units_info.join(', ').html_safe
|
||||||
end
|
end
|
||||||
|
@ -67,8 +67,8 @@ module OrdersHelper
|
||||||
def pkg_helper_icon(c = nil, options = {})
|
def pkg_helper_icon(c = nil, options = {})
|
||||||
options = { tag: 'i', class: '' }.merge(options)
|
options = { tag: 'i', class: '' }.merge(options)
|
||||||
if c.nil?
|
if c.nil?
|
||||||
c = ' '.html_safe
|
c = " ".html_safe
|
||||||
options[:class] += ' icon-only'
|
options[:class] += " icon-only"
|
||||||
end
|
end
|
||||||
content_tag(options[:tag], c, class: "package #{options[:class]}").html_safe
|
content_tag(options[:tag], c, class: "package #{options[:class]}").html_safe
|
||||||
end
|
end
|
||||||
|
@ -94,12 +94,11 @@ module OrdersHelper
|
||||||
autocomplete: 'off'
|
autocomplete: 'off'
|
||||||
|
|
||||||
if order_article.result_manually_changed?
|
if order_article.result_manually_changed?
|
||||||
input_html = content_tag(:span, class: 'input-prepend intable',
|
input_html = content_tag(:span, class: 'input-prepend intable', title: t('orders.edit_amount.field_locked_title', default: '')) {
|
||||||
title: t('orders.edit_amount.field_locked_title', default: '')) do
|
|
||||||
button_tag(nil, type: :button, class: 'btn unlocker') {
|
button_tag(nil, type: :button, class: 'btn unlocker') {
|
||||||
content_tag(:i, nil, class: 'icon icon-unlock')
|
content_tag(:i, nil, class: 'icon icon-unlock')
|
||||||
} + input_html
|
} + input_html
|
||||||
end
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
input_html.html_safe
|
input_html.html_safe
|
||||||
|
@ -110,16 +109,18 @@ module OrdersHelper
|
||||||
def ordergroup_count(order)
|
def ordergroup_count(order)
|
||||||
group_orders = order.group_orders.includes(:ordergroup)
|
group_orders = order.group_orders.includes(:ordergroup)
|
||||||
txt = "#{group_orders.count} #{Ordergroup.model_name.human count: group_orders.count}"
|
txt = "#{group_orders.count} #{Ordergroup.model_name.human count: group_orders.count}"
|
||||||
return txt if group_orders.count == 0
|
if group_orders.count == 0
|
||||||
|
return txt
|
||||||
desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ')
|
else
|
||||||
content_tag(:abbr, txt, title: desc).html_safe
|
desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ')
|
||||||
|
content_tag(:abbr, txt, title: desc).html_safe
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param order_or_supplier [Order, Supplier] Order or supplier to link to
|
# @param order_or_supplier [Order, Supplier] Order or supplier to link to
|
||||||
# @return [String] Link to order or supplier, showing its name.
|
# @return [String] Link to order or supplier, showing its name.
|
||||||
def supplier_link(order_or_supplier)
|
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
|
link_to(order_or_supplier.name, stock_articles_path).html_safe
|
||||||
else
|
else
|
||||||
link_to(@order.supplier.name, supplier_path(@order.supplier)).html_safe
|
link_to(@order.supplier.name, supplier_path(@order.supplier)).html_safe
|
||||||
|
@ -151,8 +152,19 @@ module OrdersHelper
|
||||||
if order.stockit?
|
if order.stockit?
|
||||||
content_tag :div, t('orders.index.action_receive'), class: "btn disabled #{options[:class]}"
|
content_tag :div, t('orders.index.action_receive'), class: "btn disabled #{options[:class]}"
|
||||||
else
|
else
|
||||||
link_to t('orders.index.action_receive'), receive_order_path(order),
|
link_to t('orders.index.action_receive'), receive_order_path(order), class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
||||||
class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
|
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
module StockitHelper
|
module StockitHelper
|
||||||
def stock_article_classes(article)
|
def stock_article_classes(article)
|
||||||
class_names = []
|
class_names = []
|
||||||
class_names << 'unavailable' if article.quantity_available <= 0
|
class_names << "unavailable" if article.quantity_available <= 0
|
||||||
class_names.join(' ')
|
class_names.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_to_stock_change_reason(stock_change)
|
def link_to_stock_change_reason(stock_change)
|
||||||
|
@ -17,8 +17,8 @@ module StockitHelper
|
||||||
|
|
||||||
def stock_article_price_hint(stock_article)
|
def stock_article_price_hint(stock_article)
|
||||||
t('simple_form.hints.stock_article.edit_stock_article.price',
|
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_link => link_to(t('stockit.form.copy_stock_article'),
|
||||||
stock_article_copy_path(stock_article),
|
stock_article_copy_path(stock_article),
|
||||||
remote: true))
|
:remote => true))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
module TasksHelper
|
module TasksHelper
|
||||||
def task_assignments(task)
|
def task_assignments(task)
|
||||||
task.assignments.map do |ass|
|
task.assignments.map do |ass|
|
||||||
content_tag :span, show_user(ass.user), class: (ass.accepted? ? 'accepted' : 'unaccepted')
|
content_tag :span, show_user(ass.user), :class => (ass.accepted? ? 'accepted' : 'unaccepted')
|
||||||
end.join(', ').html_safe
|
end.join(", ").html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
# generate colored number of still required users
|
# generate colored number of still required users
|
||||||
def highlighted_required_users(task)
|
def highlighted_required_users(task)
|
||||||
return if task.enough_users_assigned?
|
unless task.enough_users_assigned?
|
||||||
|
content_tag :span, task.still_required_users, class: 'badge badge-important',
|
||||||
content_tag :span, task.still_required_users, class: 'badge badge-important',
|
title: I18n.t('helpers.tasks.required_users', :count => task.still_required_users)
|
||||||
title: I18n.t('helpers.tasks.required_users', count: task.still_required_users)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def task_title(task)
|
def task_title(task)
|
||||||
|
|
|
@ -6,7 +6,7 @@ class DeltaInput < SimpleForm::Inputs::StringInput
|
||||||
options[:data] ||= {}
|
options[:data] ||= {}
|
||||||
options[:data][:delta] ||= 1
|
options[:data][:delta] ||= 1
|
||||||
options[:autocomplete] ||= 'off'
|
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
|
template.content_tag :div, class: 'delta-input input-prepend input-append' do
|
||||||
delta_button(content_tag(:i, nil, class: 'icon icon-minus'), -1, options) +
|
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
|
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||||
import "trix"
|
import "trix"
|
||||||
import "@rails/actiontext"
|
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
|
def mean_order_amount_per_job
|
||||||
(1 / @global_avg).round
|
(1 / @global_avg).round
|
||||||
rescue StandardError
|
rescue
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ArticlesCsv < RenderCsv
|
||||||
def data
|
def data
|
||||||
@object.each do |o|
|
@object.each do |o|
|
||||||
yield [
|
yield [
|
||||||
o.availability ? I18n.t('simple_form.yes') : I18n.t('simple_form.no'),
|
'',
|
||||||
o.order_number,
|
o.order_number,
|
||||||
o.name,
|
o.name,
|
||||||
o.note,
|
o.note,
|
||||||
|
|
|
@ -41,14 +41,14 @@ class BankAccountConnector
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@registered_classes = Set.new
|
@@registered_classes = Set.new
|
||||||
|
|
||||||
def self.register(klass)
|
def self.register(klass)
|
||||||
@registered_classes.add klass
|
@@registered_classes.add klass
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find(iban)
|
def self.find(iban)
|
||||||
@registered_classes.each do |klass|
|
@@registered_classes.each do |klass|
|
||||||
return klass if klass.handles(iban)
|
return klass if klass.handles(iban)
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
|
|
|
@ -17,16 +17,16 @@ class BankAccountInformationImporter
|
||||||
ret = 0
|
ret = 0
|
||||||
booked.each do |t|
|
booked.each do |t|
|
||||||
amount = parse_account_information_amount t[:transactionAmount]
|
amount = parse_account_information_amount t[:transactionAmount]
|
||||||
entity_name = amount < 0 ? t[:creditorName] : t[:debtorName]
|
entityName = amount < 0 ? t[:creditorName] : t[:debtorName]
|
||||||
entity_account = amount < 0 ? t[:creditorAccount] : t[:debtorAccount]
|
entityAccount = amount < 0 ? t[:creditorAccount] : t[:debtorAccount]
|
||||||
reference = [t[:endToEndId], t[:remittanceInformationUnstructured]].join("\n").strip
|
reference = [t[:endToEndId], t[:remittanceInformationUnstructured]].join("\n").strip
|
||||||
|
|
||||||
@bank_account.bank_transactions.where(external_id: t[:transactionId]).first_or_create.update({
|
@bank_account.bank_transactions.where(external_id: t[:transactionId]).first_or_create.update({
|
||||||
date: t[:bookingDate],
|
date: t[:bookingDate],
|
||||||
amount: amount,
|
amount: amount,
|
||||||
iban: entity_account && entity_account[:iban],
|
iban: entityAccount && entityAccount[:iban],
|
||||||
reference: reference,
|
reference: reference,
|
||||||
text: entity_name,
|
text: entityName,
|
||||||
receipt: t[:additionalInformation]
|
receipt: t[:additionalInformation]
|
||||||
})
|
})
|
||||||
ret += 1
|
ret += 1
|
||||||
|
@ -34,7 +34,7 @@ class BankAccountInformationImporter
|
||||||
|
|
||||||
balances = (data[:balances] ? data[:balances].map { |b| [b[:balanceType], b[:balanceAmount]] } : []).to_h
|
balances = (data[:balances] ? data[:balances].map { |b| [b[:balanceType], b[:balanceAmount]] } : []).to_h
|
||||||
balance = balances.values.first
|
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]
|
value = balances[type]
|
||||||
if value
|
if value
|
||||||
balance = value
|
balance = value
|
||||||
|
|
|
@ -10,68 +10,66 @@ module DateTimeAttributeValidate
|
||||||
super
|
super
|
||||||
|
|
||||||
attributes.each do |attribute|
|
attributes.each do |attribute|
|
||||||
validate -> { send("#{attribute}_datetime_value_valid") }
|
validate -> { self.send("#{attribute}_datetime_value_valid") }
|
||||||
|
|
||||||
# allow resetting the field to nil
|
# allow resetting the field to nil
|
||||||
before_validation do
|
before_validation do
|
||||||
if instance_variable_get("@#{attribute}_is_set")
|
if self.instance_variable_get("@#{attribute}_is_set")
|
||||||
date = instance_variable_get("@#{attribute}_date_value")
|
date = self.instance_variable_get("@#{attribute}_date_value")
|
||||||
time = instance_variable_get("@#{attribute}_time_value")
|
time = self.instance_variable_get("@#{attribute}_time_value")
|
||||||
send("#{attribute}=", nil) if date.blank? && time.blank?
|
if date.blank? && time.blank?
|
||||||
|
self.send("#{attribute}=", nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# remember old date and time values
|
# remember old date and time values
|
||||||
define_method("#{attribute}_date_value=") do |val|
|
define_method("#{attribute}_date_value=") do |val|
|
||||||
instance_variable_set("@#{attribute}_is_set", true)
|
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||||
instance_variable_set("@#{attribute}_date_value", val)
|
self.instance_variable_set("@#{attribute}_date_value", val)
|
||||||
begin
|
begin
|
||||||
send("#{attribute}_date=", val)
|
self.send("#{attribute}_date=", val)
|
||||||
rescue StandardError
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
define_method("#{attribute}_time_value=") do |val|
|
define_method("#{attribute}_time_value=") do |val|
|
||||||
instance_variable_set("@#{attribute}_is_set", true)
|
self.instance_variable_set("@#{attribute}_is_set", true)
|
||||||
instance_variable_set("@#{attribute}_time_value", val)
|
self.instance_variable_set("@#{attribute}_time_value", val)
|
||||||
begin
|
begin
|
||||||
send("#{attribute}_time=", val)
|
self.send("#{attribute}_time=", val)
|
||||||
rescue StandardError
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# fallback to field when values are not set
|
# fallback to field when values are not set
|
||||||
define_method("#{attribute}_date_value") do
|
define_method("#{attribute}_date_value") do
|
||||||
instance_variable_get("@#{attribute}_date_value") || send("#{attribute}_date").try do |e|
|
self.instance_variable_get("@#{attribute}_date_value") || self.send("#{attribute}_date").try { |e| e.strftime('%Y-%m-%d') }
|
||||||
e.strftime('%Y-%m-%d')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
define_method("#{attribute}_time_value") do
|
define_method("#{attribute}_time_value") do
|
||||||
instance_variable_get("@#{attribute}_time_value") || send("#{attribute}_time").try do |e|
|
self.instance_variable_get("@#{attribute}_time_value") || self.send("#{attribute}_time").try { |e| e.strftime('%H:%M') }
|
||||||
e.strftime('%H:%M')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# validate date and time
|
# validate date and time
|
||||||
define_method("#{attribute}_datetime_value_valid") do
|
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
|
unless date.blank? || begin
|
||||||
Date.parse(date)
|
Date.parse(date)
|
||||||
rescue StandardError
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
errors.add(attribute, 'is not a valid date') # @todo I18n
|
errors.add(attribute, "is not a valid date") # @todo I18n
|
||||||
end
|
end
|
||||||
time = instance_variable_get("@#{attribute}_time_value")
|
time = self.instance_variable_get("@#{attribute}_time_value")
|
||||||
unless time.blank? || begin
|
unless time.blank? || begin
|
||||||
Time.parse(time)
|
Time.parse(time)
|
||||||
rescue StandardError
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
errors.add(attribute, 'is not a valid time') # @todo I18n
|
errors.add(attribute, "is not a valid time") # @todo I18n
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Foodsoft
|
||||||
cattr_accessor :variables
|
cattr_accessor :variables
|
||||||
|
|
||||||
# Hash of variables. Note that keys are Strings.
|
# Hash of variables. Note that keys are Strings.
|
||||||
@variables = {
|
@@variables = {
|
||||||
'scope' => -> { FoodsoftConfig.scope },
|
'scope' => -> { FoodsoftConfig.scope },
|
||||||
'name' => -> { FoodsoftConfig[:name] },
|
'name' => -> { FoodsoftConfig[:name] },
|
||||||
'contact.street' => -> { FoodsoftConfig[:contact][:street] },
|
'contact.street' => -> { FoodsoftConfig[:contact][:street] },
|
||||||
|
@ -39,13 +39,13 @@ module Foodsoft
|
||||||
'supplier_count' => -> { Supplier.undeleted.count },
|
'supplier_count' => -> { Supplier.undeleted.count },
|
||||||
'active_supplier_count' => -> { active_supplier_count },
|
'active_supplier_count' => -> { active_supplier_count },
|
||||||
'active_suppliers' => -> { active_suppliers },
|
'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 expanded variable
|
||||||
# @return [String] Expanded variable
|
# @return [String] Expanded variable
|
||||||
def self.get(var)
|
def self.get(var)
|
||||||
s = @variables[var.to_s]
|
s = @@variables[var.to_s]
|
||||||
s.respond_to?(:call) ? s.call : s.to_s
|
s.respond_to?(:call) ? s.call : s.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ module Foodsoft
|
||||||
# @return [String] Expanded string
|
# @return [String] Expanded string
|
||||||
def self.expand(str, options = {})
|
def self.expand(str, options = {})
|
||||||
str.gsub(/{{([._a-zA-Z0-9]+)}}/) do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ class FoodsoftConfig
|
||||||
# Load initial config from development or production
|
# Load initial config from development or production
|
||||||
set_config Rails.env
|
set_config Rails.env
|
||||||
# Overwrite scope to have a better namescope than 'production'
|
# 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 defaults for backward-compatibility
|
||||||
set_missing
|
set_missing
|
||||||
# Make sure relevant configuration is applied, also in single coops mode,
|
# Make sure relevant configuration is applied, also in single coops mode,
|
||||||
|
@ -79,7 +79,7 @@ class FoodsoftConfig
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_mailing
|
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]
|
ActionMailer::Base.default_url_options[k] = self[k] if self[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -117,7 +117,7 @@ class FoodsoftConfig
|
||||||
# @return [Object] Value of the key.
|
# @return [Object] Value of the key.
|
||||||
def [](key)
|
def [](key)
|
||||||
if RailsSettings::CachedSettings.table_exists? && allowed_key?(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 = config[key] if value.nil?
|
||||||
value
|
value
|
||||||
else
|
else
|
||||||
|
@ -139,20 +139,20 @@ class FoodsoftConfig
|
||||||
if config[key] == value || (config[key].nil? && value == false)
|
if config[key] == value || (config[key].nil? && value == false)
|
||||||
# delete (ok if it was already deleted)
|
# delete (ok if it was already deleted)
|
||||||
begin
|
begin
|
||||||
RailsSettings::CachedSettings.destroy "foodcoop.#{scope}.#{key}"
|
RailsSettings::CachedSettings.destroy "foodcoop.#{self.scope}.#{key}"
|
||||||
rescue RailsSettings::Settings::SettingNotFound
|
rescue RailsSettings::Settings::SettingNotFound
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# or store
|
# or store
|
||||||
RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"] = value
|
RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"] = value
|
||||||
end
|
end
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [Array<String>] Configuration keys that are set (either in +app_config.yml+ or database).
|
# @return [Array<String>] Configuration keys that are set (either in +app_config.yml+ or database).
|
||||||
def keys
|
def keys
|
||||||
keys = RailsSettings::CachedSettings.get_all("foodcoop.#{scope}.").try(:keys) || []
|
keys = RailsSettings::CachedSettings.get_all("foodcoop.#{self.scope}.").try(:keys) || []
|
||||||
keys.map! { |k| k.gsub(/^foodcoop\.#{scope}\./, '') }
|
keys.map! { |k| k.gsub(/^foodcoop\.#{self.scope}\./, '') }
|
||||||
keys += config.keys
|
keys += config.keys
|
||||||
keys.map(&:to_s).uniq
|
keys.map(&:to_s).uniq
|
||||||
end
|
end
|
||||||
|
@ -181,10 +181,10 @@ class FoodsoftConfig
|
||||||
# @return [Boolean] Whether this key may be set in the database
|
# @return [Boolean] Whether this key may be set in the database
|
||||||
def allowed_key?(key)
|
def allowed_key?(key)
|
||||||
# fast check for keys without nesting
|
# fast check for keys without nesting
|
||||||
if config[:protected].include? key
|
if self.config[:protected].include? key
|
||||||
!config[:protected][key]
|
!self.config[:protected][key]
|
||||||
else
|
else
|
||||||
!config[:protected][:all]
|
!self.config[:protected][:all]
|
||||||
end
|
end
|
||||||
# @todo allow to check nested keys as well
|
# @todo allow to check nested keys as well
|
||||||
end
|
end
|
||||||
|
@ -287,9 +287,7 @@ class FoodsoftConfig
|
||||||
def normalize_value(value)
|
def normalize_value(value)
|
||||||
value = value.map { |v| normalize_value(v) } if value.is_a? Array
|
value = value.map { |v| normalize_value(v) } if value.is_a? Array
|
||||||
if value.is_a? Hash
|
if value.is_a? Hash
|
||||||
value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map do |a|
|
value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map { |a| [a[0], normalize_value(a[1])] }]
|
||||||
[a[0], normalize_value(a[1])]
|
|
||||||
end]
|
|
||||||
end
|
end
|
||||||
case value
|
case value
|
||||||
when 'true' then true
|
when 'true' then true
|
||||||
|
|
|
@ -8,24 +8,26 @@ module FoodsoftDateUtil
|
||||||
# @todo handle ical parse errors
|
# @todo handle ical parse errors
|
||||||
occ = begin
|
occ = begin
|
||||||
schedule.next_occurrence(from).to_time
|
schedule.next_occurrence(from).to_time
|
||||||
rescue StandardError
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
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
|
occ
|
||||||
end
|
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
|
# @return [IceCube::Rule] Recurring rule
|
||||||
def self.rule_from(rule)
|
def self.rule_from(p)
|
||||||
case rule
|
case p
|
||||||
when String
|
when String
|
||||||
IceCube::Rule.from_ical(rule)
|
IceCube::Rule.from_ical(p)
|
||||||
when Hash
|
when Hash
|
||||||
IceCube::Rule.from_hash(rule)
|
IceCube::Rule.from_hash(p)
|
||||||
else
|
else
|
||||||
rule
|
p
|
||||||
end
|
end
|
||||||
end
|
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')
|
recipient = rcpt_to.gsub(/^\s*<\s*(.*)\s*>\s*$/, '\1')
|
||||||
@handlers << self.class.find_handler(recipient)
|
@handlers << self.class.find_handler(recipient)
|
||||||
rcpt_to
|
rcpt_to
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
logger.info("Can not accept mail for '#{rcpt_to}': #{e}")
|
logger.info("Can not accept mail for '#{rcpt_to}': #{error}")
|
||||||
raise MidiSmtpServer::Smtpd550Exception
|
raise MidiSmtpServer::Smtpd550Exception
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,16 +32,16 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
||||||
@handlers.each do |handler|
|
@handlers.each do |handler|
|
||||||
handler.call(ctx[:message][:data])
|
handler.call(ctx[:message][:data])
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue => error
|
||||||
ExceptionNotifier.notify_exception(e, data: ctx)
|
ExceptionNotifier.notify_exception(error, data: ctx)
|
||||||
raise e
|
raise error
|
||||||
ensure
|
ensure
|
||||||
@handlers.clear
|
@handlers.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_handler(recipient)
|
def self.find_handler(recipient)
|
||||||
m = /(?<foodcoop>[^@.]+)\.(?<address>[^@]+)(@(?<hostname>[^@]+))?/.match 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]
|
raise "Foodcoop '#{m[:foodcoop]}' could not be found" unless FoodsoftConfig.allowed_foodcoop? m[:foodcoop]
|
||||||
|
|
||||||
FoodsoftConfig.select_multifoodcoop m[:foodcoop]
|
FoodsoftConfig.select_multifoodcoop m[:foodcoop]
|
||||||
|
@ -53,6 +53,6 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
raise 'invalid format for recipient'
|
raise "invalid format for recipient"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,28 +2,60 @@ require 'csv'
|
||||||
|
|
||||||
class OrderCsv < RenderCsv
|
class OrderCsv < RenderCsv
|
||||||
def header
|
def header
|
||||||
[
|
params = @options[:custom_csv]
|
||||||
OrderArticle.human_attribute_name(:units_to_order),
|
arr = if params.nil?
|
||||||
Article.human_attribute_name(:order_number),
|
[
|
||||||
Article.human_attribute_name(:name),
|
OrderArticle.human_attribute_name(:units_to_order),
|
||||||
Article.human_attribute_name(:unit),
|
Article.human_attribute_name(:order_number),
|
||||||
Article.human_attribute_name(:unit_quantity_short),
|
Article.human_attribute_name(:name),
|
||||||
ArticlePrice.human_attribute_name(:price),
|
Article.human_attribute_name(:unit),
|
||||||
OrderArticle.human_attribute_name(:total_price)
|
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
|
end
|
||||||
|
|
||||||
def data
|
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 [
|
yield [
|
||||||
oa.units_to_order,
|
match_params(oa, header[0]),
|
||||||
oa.article.order_number,
|
match_params(oa, header[1]),
|
||||||
oa.article.name,
|
match_params(oa, header[2]),
|
||||||
oa.article.unit,
|
match_params(oa, header[3]),
|
||||||
oa.price.unit_quantity > 1 ? oa.price.unit_quantity : nil,
|
match_params(oa, header[4]),
|
||||||
number_to_currency(oa.price.price * oa.price.unit_quantity),
|
match_params(oa, header[5]),
|
||||||
number_to_currency(oa.total_price)
|
match_params(oa, header[6])
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -55,7 +55,7 @@ class OrderPdf < RenderPdf
|
||||||
end
|
end
|
||||||
|
|
||||||
def group_order_article_quantity_with_tolerance(goa)
|
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
|
end
|
||||||
|
|
||||||
def group_order_article_result(goa)
|
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)')
|
.pluck('groups.name', 'SUM(group_orders.price)', 'ordergroup_id', 'SUM(group_orders.transport)')
|
||||||
|
|
||||||
result.map do |item|
|
result.map do |item|
|
||||||
[item.first || stock_ordergroup_name] + item[1..]
|
[item.first || stock_ordergroup_name] + item[1..-1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ class OrderPdf < RenderPdf
|
||||||
def each_ordergroup_batch(batch_size)
|
def each_ordergroup_batch(batch_size)
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
loop do
|
while true
|
||||||
go_records = ordergroups(offset, batch_size)
|
go_records = ordergroups(offset, batch_size)
|
||||||
|
|
||||||
break unless go_records.any?
|
break unless go_records.any?
|
||||||
|
@ -113,7 +113,7 @@ class OrderPdf < RenderPdf
|
||||||
# get quantity for each article and ordergroup
|
# get quantity for each article and ordergroup
|
||||||
goa_records = group_order_articles(group_ids)
|
goa_records = group_order_articles(group_ids)
|
||||||
.group('group_order_articles.order_article_id, group_orders.ordergroup_id')
|
.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
|
# 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|
|
results = goa_records.group_by(&:first).transform_values do |value|
|
||||||
|
@ -136,7 +136,7 @@ class OrderPdf < RenderPdf
|
||||||
group_order_articles(ordergroup)
|
group_order_articles(ordergroup)
|
||||||
.includes(order_article: { article: [:supplier] })
|
.includes(order_article: { article: [:supplier] })
|
||||||
.order('suppliers.name, articles.name')
|
.order('suppliers.name, articles.name')
|
||||||
.preload(order_article: %i[article_price order])
|
.preload(order_article: [:article_price, :order])
|
||||||
.each(&block)
|
.each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,23 @@ class OrderTxt
|
||||||
def to_txt
|
def to_txt
|
||||||
supplier = @order.supplier
|
supplier = @order.supplier
|
||||||
contact = FoodsoftConfig[:contact].symbolize_keys
|
contact = FoodsoftConfig[:contact].symbolize_keys
|
||||||
text = I18n.t('orders.fax.heading', name: FoodsoftConfig[:name])
|
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
|
||||||
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" if supplier.customer_number.present?
|
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
|
||||||
text += "\n" + I18n.t('orders.fax.delivery_day')
|
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 += "\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 += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
|
||||||
text += '****** ' + I18n.t('orders.fax.articles') + "\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'),
|
text += format("%8s %8s %s\n", I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'), I18n.t('orders.fax.name'))
|
||||||
I18n.t('orders.fax.name'))
|
|
||||||
# now display all ordered articles
|
# 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)
|
text += format("%8s %8d %s\n", oa.article.order_number, oa.units_to_order.to_i, oa.article.name)
|
||||||
end
|
end
|
||||||
text
|
text
|
||||||
end
|
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
|
end
|
||||||
|
|
59
app/lib/quantity_unit.rb
Normal file
59
app/lib/quantity_unit.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
class QuantityUnit
|
||||||
|
def initialize(quantity, unit)
|
||||||
|
@quantity = quantity
|
||||||
|
@unit = unit
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse(number_with_unit)
|
||||||
|
# remove whitespace
|
||||||
|
number_with_unit = number_with_unit.gsub(/\s+/, '')
|
||||||
|
# to lowercase
|
||||||
|
number_with_unit = number_with_unit.downcase
|
||||||
|
# remove numerical part
|
||||||
|
number = number_with_unit.gsub(/[^0-9.,]/, '')
|
||||||
|
# remove unit part
|
||||||
|
unit = number_with_unit.gsub(/[^a-zA-Z]/, '')
|
||||||
|
# convert comma to dot
|
||||||
|
number = number.gsub(',', '.')
|
||||||
|
# convert to float
|
||||||
|
number = number.to_f
|
||||||
|
|
||||||
|
return nil unless unit.in?(%w[g kg l ml])
|
||||||
|
|
||||||
|
QuantityUnit.new(number, unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def scale_price_to_base_unit(price)
|
||||||
|
return nil unless price.is_a?(Numeric)
|
||||||
|
|
||||||
|
factor = if @unit == 'kg' || @unit == 'l'
|
||||||
|
1
|
||||||
|
elsif @unit == 'g' || @unit == 'ml'
|
||||||
|
1000
|
||||||
|
end
|
||||||
|
|
||||||
|
scaled_price = price / @quantity * factor
|
||||||
|
scaled_price.round(2)
|
||||||
|
|
||||||
|
base_unit = if @unit == 'kg' || @unit == 'g'
|
||||||
|
'kg'
|
||||||
|
elsif @unit == 'l' || @unit == 'ml'
|
||||||
|
'L'
|
||||||
|
end
|
||||||
|
|
||||||
|
[scaled_price, base_unit]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{@quantity} #{@unit}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def quantity
|
||||||
|
@quantity
|
||||||
|
end
|
||||||
|
|
||||||
|
def unit
|
||||||
|
@unit
|
||||||
|
end
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue