Compare commits

...

65 commits

Author SHA1 Message Date
Philipp Rothmann
92edac9f9f add invalid funds message on grouporder page 2023-10-05 22:44:38 +02:00
Philipp Rothmann
2ef17ceca4 adapt financial transaction type for online payment provider 2023-10-05 22:44:38 +02:00
Philipp Rothmann
c59eefd219 add mollie plugin 2023-10-05 22:44:38 +02:00
Philipp Rothmann
55234b4e27 continue development after release 2023-09-09 17:01:48 +02:00
Philipp Rothmann
e194c68397 chore: bump version to 4.8.0 2023-09-09 10:52:39 +02:00
Philipp Rothmann
e1b5824830 update changelog v4.8 2023-09-09 10:52:39 +02:00
Philipp Rothmann
91f27a0a48 chore: update chrowdin translations 2023-09-04 13:01:04 +02:00
Philipp Rothmann
caa32de30c fix: rubocop violation 2023-08-23 12:47:58 +02:00
Philipp Rothmann
1e63c59a8a fix: loading trix editor overwrite in production 2023-08-23 12:17:32 +02:00
Philipp Rothmann
a96f21134e feat(messages): attachment retention task 2023-08-04 12:48:15 +02:00
Philipp Rothmann
bcf47ec92b feat(messages): add max file size for attachments 2023-08-04 12:48:15 +02:00
Philipp Rothmann
ef6d6aa368 feat(messages): use trix editor in messages 2023-08-04 12:48:15 +02:00
Philipp Rothmann
c4a53caf52 feat: add actiontext and trix editor 2023-08-04 12:48:15 +02:00
Philipp Rothmann
9282590c06 fix: update setup-chromedriver github action 2023-08-04 12:35:58 +02:00
Philipp Rothmann
817e409a2b fix test 2023-07-14 10:27:20 +02:00
Philipp Rothmann
e80ec9c1ce change tests to use assert_select 2023-07-14 10:27:20 +02:00
Philipp Rothmann
7f23b4784c feat(finance): show sum of ordergroup balances 2023-07-14 10:27:20 +02:00
Harald Reingruber
b07653b34f Add explanation comment to .gitattributes 2023-07-03 16:12:12 +02:00
Harald Reingruber
c442327275 Fix line endings for Windows docker environment 2023-07-03 16:12:12 +02:00
Philipp Rothmann
33034e66b8 fix: add null checks for articles convert_units
Prevents division by zero exception because of a unit beeing 0.
A Unit becomes also zero e.g. when a comma symbol is used Unit.new("0,9kg") == 0

fixes #1014
2023-06-22 22:49:22 +02:00
kidhab
45e2668cea Update mail gem to .8.1 which fixes the permission error
Revert libv8 version
2023-06-17 14:03:58 +02:00
Philipp Rothmann
5f2130ca44 fix: rubocop todo EmptyExampleGroup wildcard 2023-06-17 13:44:21 +02:00
Philipp Rothmann
913136bb72 fix: invalid params request test
fixes #999
2023-06-17 13:31:43 +02:00
kidhab
4ac5bcae06 Update Ruby version and add info about dev packages 2023-06-17 10:33:15 +02:00
Philipp Rothmann
37b3b4523a fix: github action mysqladmin -> mariadb-admin ping 2023-06-16 13:33:21 +02:00
Philipp Rothmann
a1682932ac fix: price_markup with value nil gives exception
fixes #1011
2023-06-16 13:20:33 +02:00
Philipp Rothmann
026c3a6285
introduce importmaps (#983)
* introduce importmaps

This commit introduces importmaps. They allow to use modern javacript ESM within rails without webpack, yarn etc.
see https://github.com/rails/importmap-rails for more details.

Co-authored-by: Philipp Rothmann <philipprothmann@posteo.de>
Co-authored-by: FGU <fgu@pragma-shift.net>

* fix: rubocop violations

---------

Co-authored-by: FGU <fgu@pragma-shift.net>
2023-06-14 13:29:31 +02:00
dependabot[bot]
a8b2f387db
Bump doorkeeper from 5.6.2 to 5.6.6 (#1010)
Bumps [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) from 5.6.2 to 5.6.6.
- [Release notes](https://github.com/doorkeeper-gem/doorkeeper/releases)
- [Changelog](https://github.com/doorkeeper-gem/doorkeeper/blob/main/CHANGELOG.md)
- [Commits](https://github.com/doorkeeper-gem/doorkeeper/compare/v5.6.2...v5.6.6)

---
updated-dependencies:
- dependency-name: doorkeeper
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 08:18:33 +02:00
Philipp Rothmann
2151835afb fix: rubocop violation 2023-06-12 13:08:36 +02:00
kidhab
20dc8b8b82 Bump Ruby version to latest in 2.7 series 2023-06-10 10:54:03 +02:00
kidhab
e4f91ef67a
Fill availability column at article export
closes #884
2023-06-10 10:47:47 +02:00
kidhab
c50ba6eda5
feat: Disable member list via configuration (#990) 2023-06-10 10:32:16 +02:00
kidhab
075f3cfa1a
Make date configurable via locales (#997) 2023-06-10 10:31:22 +02:00
dependabot[bot]
64b99038e6
Bump nokogiri from 1.13.10 to 1.15.2 (#1005)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.10 to 1.15.2.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.10...v1.15.2)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-09 20:06:14 +02:00
dependabot[bot]
7fe5fb4592
Bump rack from 2.2.5 to 2.2.7 (#1004)
Bumps [rack](https://github.com/rack/rack) from 2.2.5 to 2.2.7.
- [Release notes](https://github.com/rack/rack/releases)
- [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rack/rack/compare/v2.2.5...v2.2.7)

---
updated-dependencies:
- dependency-name: rack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-09 19:39:32 +02:00
8b0e03ff60 downgrade haml to make deface work 2023-06-09 19:02:41 +02:00
Philipp Rothmann
4bfa87d258 move CORS setup to initializer 2023-06-09 17:35:05 +02:00
Philipp Rothmann
20a67becf5 fix: assets precompile by using terser 2023-06-09 17:35:05 +02:00
Philipp Rothmann
91e07ab660 fix external link allow_other_host 2023-06-09 17:35:05 +02:00
Philipp Rothmann
285441cb4b fix group order matrix pdf 2023-06-09 17:35:05 +02:00
Philipp Rothmann
fb2b4d8a8a chore: rubocop
chore: fix api test conventions

chore: rubocop -A spec/

chore: more rubocop -A

fix failing test

rubocop fixes

removes helper methods that are in my opinion dead code

more rubocop fixes

rubocop -a --auto-gen-config
2023-06-09 17:35:05 +02:00
Philipp Rothmann
f6fb804bbe chore: update Gemfile.lock 2023-06-09 17:35:05 +02:00
Philipp Rothmann
a7775f5a98 add setup_storage to stock_config 2023-06-09 17:35:05 +02:00
FGU
b06656ba80 fix docker-compose 2023-06-09 17:35:05 +02:00
Philipp Rothmann
6e721db654 upgrade dockerfile to rails7 2023-06-09 17:35:05 +02:00
45ae192891 move BigDecimal.new to BigDecimal() 2023-06-09 17:35:05 +02:00
808baa5a98 change .search to .ransack for updated ransack gem 2023-06-09 17:35:05 +02:00
Philipp Rothmann
5cbe8dd968 fix database_config 2023-06-09 17:35:05 +02:00
Philipp Rothmann
34e238466f fix mail file permission bug 2023-06-09 17:35:05 +02:00
5fb10ec686 make foodsoft run for dev on rails 7 and ruby 2.7 2023-06-09 17:35:05 +02:00
50bf879fbf resolve zeitwerk issues 2023-06-09 17:35:05 +02:00
ea248a5f28 removing concerns from autoload path 2023-06-09 17:35:05 +02:00
4ff44aed4c mv lib to app/lib due to upgrade 2023-06-09 17:35:05 +02:00
3d81dd6b57 rails up to 7.0and ruby to 2.7.2 2023-06-09 17:35:05 +02:00
Philipp Rothmann
c67e9b5be8
Replace apivore with rswag for api tests (#969)
* Replace apivore api tests with rswag
* move to OpenAPI Spec 3.0.1
* a swagger UI is now reachable at http://localhost:3000/api-docs/index.html
*  swagger file is generated by running  `RAILS_ENV=test rails rswag`
    and it was moved from /docs/swagger.v1.yml to /swagger/v1/swagger.yml

---------

Co-authored-by: viehlieb <pf@pragma-shift.net>
2023-05-12 11:11:48 +02:00
hamaryns
8604e27fe9 Spelfouten, maar ook verbeteringen in Nederlands (#954)
* Spelfouten, maar ook verbeteringen in Nederlands

Correct spelling errors and improvements of Dutch

* Update nl.yml

* Update nl.yml

some more Dutch improvements
2023-04-21 18:58:06 +02:00
nurp
f2d5936cf0
Turkish language support added (#995)
* Added Turkish translation with help of ChatGPT

* Changed 'article' and 'item' to 'ürün' and addedtranslations for messages plugin

* added translation for the rest of plugins

* merge conflicts

* fix tr.yml in messages plugin

* Corrected more translations

---------

Co-authored-by: Nurp <>
2023-04-12 21:42:03 +02:00
kidhab
c01c16ecdb
Specify an URL to redirect after logout via settings (#989) 2023-03-30 10:05:47 +02:00
dependabot[bot]
67d0492ac4
Bump rack from 2.2.4 to 2.2.6.4 (#986)
Bumps [rack](https://github.com/rack/rack) from 2.2.4 to 2.2.6.4.
- [Release notes](https://github.com/rack/rack/releases)
- [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rack/rack/compare/2.2.4...v2.2.6.4)

---
updated-dependencies:
- dependency-name: rack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-29 17:37:04 +02:00
dependabot[bot]
5f00a39841
Bump globalid from 1.0.0 to 1.0.1 (#978)
Bumps [globalid](https://github.com/rails/globalid) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/rails/globalid/releases)
- [Commits](https://github.com/rails/globalid/compare/v1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: globalid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-29 17:34:38 +02:00
kidhab
8420323c92
Show a foodcoop's name as subtitle at login screen (#957) 2023-03-29 16:01:00 +02:00
kidhab
e0f63eebdc
Open external websites in new browser window (#981)
Usually the Foodcoop's website and the help pages are external
resources. If they load in the same window one could forget to logout
from the Foodsoft.
2023-03-29 16:00:18 +02:00
kidhab
a7a0830d43
Show order note as tooltip (#965) 2023-03-29 15:15:59 +02:00
Philipp Rothmann
503ed6c379
Add home controller test (#972)
Co-authored-by: viehlieb <pf@pragma-shift.net>
Co-authored-by: Tobias Kneuker <tk@pragma-shift.net>
2023-03-25 18:20:13 +01:00
decentral1se
debce2a635
docs: roadmap & call (#984)
Co-authored-by: decentral1se <cellarspoon@riseup.net>
2023-03-05 14:07:49 +01:00
459 changed files with 11114 additions and 7168 deletions

5
.gitattributes vendored Normal file
View file

@ -0,0 +1,5 @@
# Fixes line endings for Windows (Docker) environment, which are by default converted to crlf
* text=auto
*.sh text eol=lf
proc-start text eol=lf
Rakefile text eol=lf

View file

@ -15,7 +15,7 @@ jobs:
MYSQL_DATABASE: test MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: password MYSQL_ROOT_PASSWORD: password
options: >- options: >-
--health-cmd "mysqladmin ping" --health-cmd "mariadb-admin ping"
--health-interval 10s --health-interval 10s
--health-timeout 5s --health-timeout 5s
--health-retries 5 --health-retries 5
@ -35,7 +35,9 @@ 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@v1.0.1 uses: nanasess/setup-chromedriver@v2
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:

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
2.6.9 2.7.8

View file

@ -1,3 +1,36 @@
# 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)

View file

@ -1,4 +1,4 @@
FROM ruby:2.6 FROM ruby:2.7
RUN supercronicUrl=https://github.com/aptible/supercronic/releases/download/v0.1.3/supercronic-linux-amd64 && \ RUN supercronicUrl=https://github.com/aptible/supercronic/releases/download/v0.1.3/supercronic-linux-amd64 && \
supercronicBin=/usr/local/bin/supercronic && \ supercronicBin=/usr/local/bin/supercronic && \
@ -22,6 +22,7 @@ RUN buildDeps='libmagic-dev' && \
apt-get update && \ apt-get update && \
apt-get install --no-install-recommends -y $buildDeps && \ apt-get install --no-install-recommends -y $buildDeps && \
echo 'gem: --no-document' >> ~/.gemrc && \ echo 'gem: --no-document' >> ~/.gemrc && \
gem install bundler && \
bundle config build.nokogiri "--use-system-libraries" && \ bundle config build.nokogiri "--use-system-libraries" && \
bundle install --deployment --without development test -j 4 && \ bundle install --deployment --without development test -j 4 && \
apt-get purge -y --auto-remove $buildDeps && \ apt-get purge -y --auto-remove $buildDeps && \
@ -48,9 +49,10 @@ RUN export DATABASE_URL=mysql2://localhost/temp?encoding=utf8 && \
rm -Rf /var/lib/apt/lists/* /var/cache/apt/* rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
# Make relevant dirs and files writable for app user # Make relevant dirs and files writable for app user
RUN mkdir -p tmp && \ RUN mkdir -p tmp storage && \
chown nobody config/app_config.yml && \ chown nobody config/app_config.yml && \
chown nobody tmp chown nobody tmp && \
chown nobody storage
# Run app as unprivileged user # Run app as unprivileged user
USER nobody USER nobody

View file

@ -1,4 +1,4 @@
FROM ruby:2.6 FROM ruby:2.7
# Install dependencies # Install dependencies
RUN deps='libmagic-dev chromium nodejs' && \ RUN deps='libmagic-dev chromium nodejs' && \
@ -19,6 +19,7 @@ ENV PORT=3000 \
WORKDIR /app WORKDIR /app
RUN gem install bundler
RUN bundle config build.nokogiri "--use-system-libraries" RUN bundle config build.nokogiri "--use-system-libraries"
EXPOSE 3000 EXPOSE 3000

105
Gemfile
View file

@ -1,87 +1,95 @@
# A sample Gemfile # A sample Gemfile
source "https://rubygems.org" source 'https://rubygems.org'
gem "rails", '~> 5.2' gem 'rails', '~> 7.0'
gem 'sass-rails'
gem 'less-rails' gem 'less-rails'
gem 'uglifier', '>= 1.0.3' gem 'sassc-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 'jquery-rails' gem 'bootsnap', require: false
gem 'select2-rails'
gem 'rails_tokeninput'
gem 'bootstrap-datepicker-rails' gem 'bootstrap-datepicker-rails'
gem 'date_time_attribute' gem 'date_time_attribute'
gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update
gem 'i18n-js', '~> 3.0.0.rc8' gem 'i18n-js', '~> 3.0.0.rc8'
gem 'jquery-rails'
gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update
gem 'rails-i18n' gem 'rails-i18n'
gem 'bootsnap', require: false gem 'rails_tokeninput'
gem 'select2-rails'
gem 'mysql2' gem 'active_model_serializers', '~> 0.10.0'
gem 'prawn' gem 'acts_as_tree'
gem 'prawn-table' gem 'attribute_normalizer'
gem 'haml'
gem 'haml-rails'
gem 'kaminari'
gem 'simple_form'
gem 'inherited_resources'
gem 'daemons' gem 'daemons'
gem 'doorkeeper' gem 'doorkeeper'
gem 'doorkeeper-i18n' gem 'doorkeeper-i18n'
gem 'haml', '~> 5.0'
gem 'haml-rails'
gem 'ice_cube'
gem 'inherited_resources'
gem 'kaminari'
gem 'mysql2'
gem 'prawn'
gem 'prawn-table'
gem 'puma'
gem 'rack-cors', require: 'rack/cors' gem 'rack-cors', require: 'rack/cors'
gem 'active_model_serializers', '~> 0.10.0' gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73
gem 'twitter-bootstrap-rails', '~> 2.2.8' gem 'ransack'
gem 'resque'
gem 'ruby-units'
gem 'sd_notify'
gem 'simple_form'
gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap
gem 'simple-navigation-bootstrap' gem 'simple-navigation-bootstrap'
gem 'sprockets', '< 4' gem 'sprockets', '< 4'
gem 'ransack' gem 'twitter-bootstrap-rails', '~> 2.2.8'
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' # 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 'attribute_normalizer'
gem 'ice_cube'
gem 'recurring_select'
gem 'roo'
gem 'roo-xls'
gem 'spreadsheet'
gem 'exception_notification' gem 'exception_notification'
gem 'gaffe' gem 'gaffe'
gem 'ruby-filemagic' gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
gem 'mime-types' gem "image_processing", "~> 1.12"
gem "importmap-rails", "~> 1.1"
gem 'midi-smtp-server' gem 'midi-smtp-server'
gem 'mime-types'
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
gem 'roo'
gem 'roo-xls'
gem 'rswag-api'
gem 'rswag-ui'
gem 'ruby-filemagic'
gem 'spreadsheet'
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_wiki', path: 'plugins/wiki'
gem 'foodsoft_messages', path: 'plugins/messages'
gem 'foodsoft_documents', path: 'plugins/documents'
gem 'foodsoft_discourse', path: 'plugins/discourse' gem 'foodsoft_discourse', path: 'plugins/discourse'
gem 'foodsoft_documents', path: 'plugins/documents'
gem 'foodsoft_links', path: 'plugins/links' gem 'foodsoft_links', path: 'plugins/links'
gem 'foodsoft_messages', path: 'plugins/messages'
gem 'foodsoft_polls', path: 'plugins/polls' gem 'foodsoft_polls', path: 'plugins/polls'
gem 'foodsoft_wiki', path: 'plugins/wiki'
# 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 'sqlite3', '~> 1.3.6'
gem 'mailcatcher'
gem 'web-console'
gem 'listen' gem 'listen'
gem 'mailcatcher'
gem 'sqlite3', '~> 1.3.6'
gem 'web-console'
# Better error output # Better error output
gem 'better_errors' gem 'better_errors'
gem 'binding_of_caller' gem 'binding_of_caller'
# gem "rails-i18n-debug" # gem "rails-i18n-debug"
# chrome debugging extension https://github.com/dejan/rails_panel # chrome debugging extension https://github.com/dejan/rails_panel
gem 'meta_request' # TODO: disabled due to https://github.com/rails/rails/issues/40781
# gem 'meta_request'
# Get infos when not using proper eager loading # Get infos when not using proper eager loading
gem 'bullet' gem 'bullet'
@ -101,21 +109,20 @@ group :development, :test do
end end
group :test do group :test do
gem 'rspec-rails' gem 'apparition' # Capybara javascript driver
gem 'capybara'
gem 'connection_pool'
gem 'database_cleaner'
gem 'factory_bot_rails' gem 'factory_bot_rails'
gem 'faker' gem 'faker'
gem 'capybara' gem 'rspec-rails'
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'
# 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 'apivore', require: false gem 'rswag-specs'
gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114
end end

View file

@ -1,3 +1,14 @@
GIT
remote: https://github.com/gregschmit/recurring_select
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
specs:
recurring_select (3.0.0)
coffee-rails (>= 3.1)
ice_cube (>= 0.11)
jquery-rails (>= 3.0)
rails (>= 5.2)
sass-rails (>= 4.0)
GIT GIT
remote: https://github.com/technoweenie/acts_as_versioned.git remote: https://github.com/technoweenie/acts_as_versioned.git
revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b
@ -37,6 +48,13 @@ 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:
@ -59,67 +77,83 @@ PATH
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.2.8.1) actioncable (7.0.4)
actionpack (= 5.2.8.1) actionpack (= 7.0.4)
activesupport (= 7.0.4)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailer (5.2.8.1) actionmailbox (7.0.4)
actionpack (= 5.2.8.1) actionpack (= 7.0.4)
actionview (= 5.2.8.1) activejob (= 7.0.4)
activejob (= 5.2.8.1) activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.4)
actionpack (= 7.0.4)
actionview (= 7.0.4)
activejob (= 7.0.4)
activesupport (= 7.0.4)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.2.8.1) actionpack (7.0.4)
actionview (= 5.2.8.1) actionview (= 7.0.4)
activesupport (= 5.2.8.1) activesupport (= 7.0.4)
rack (~> 2.0, >= 2.0.8) rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (5.2.8.1) actiontext (7.0.4)
activesupport (= 5.2.8.1) actionpack (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.4)
activesupport (= 7.0.4)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.10.13) active_model_serializers (0.10.13)
actionpack (>= 4.1, < 7.1) actionpack (>= 4.1, < 7.1)
activemodel (>= 4.1, < 7.1) activemodel (>= 4.1, < 7.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (5.2.8.1) activejob (7.0.4)
activesupport (= 5.2.8.1) activesupport (= 7.0.4)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.2.8.1) activemodel (7.0.4)
activesupport (= 5.2.8.1) activesupport (= 7.0.4)
activerecord (5.2.8.1) activerecord (7.0.4)
activemodel (= 5.2.8.1) activemodel (= 7.0.4)
activesupport (= 5.2.8.1) activesupport (= 7.0.4)
arel (>= 9.0) activestorage (7.0.4)
activestorage (5.2.8.1) actionpack (= 7.0.4)
actionpack (= 5.2.8.1) activejob (= 7.0.4)
activerecord (= 5.2.8.1) activerecord (= 7.0.4)
marcel (~> 1.0.0) activesupport (= 7.0.4)
activesupport (5.2.8.1) marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 1.6, < 2)
minitest (~> 5.1) minitest (>= 5.1)
tzinfo (~> 1.1) tzinfo (~> 2.0)
acts_as_tree (2.9.1) acts_as_tree (2.9.1)
activerecord (>= 3.0.0) activerecord (>= 3.0.0)
addressable (2.8.1) addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
apivore (1.6.2)
actionpack (>= 4, < 6)
hashie (~> 3.3)
json-schema (~> 2.5)
rspec (~> 3)
rspec-expectations (~> 3.1)
rspec-mocks (~> 3.1)
apparition (0.6.0) apparition (0.6.0)
capybara (~> 3.13, < 4) capybara (~> 3.13, < 4)
websocket-driver (>= 0.6.5) websocket-driver (>= 0.6.5)
arel (9.0.0)
ast (2.4.2) ast (2.4.2)
attribute_normalizer (1.2.0) attribute_normalizer (1.2.0)
base32 (0.3.4) base32 (0.3.4)
@ -130,15 +164,15 @@ GEM
bindex (0.8.1) bindex (0.8.1)
binding_of_caller (1.0.0) binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.13.0) bootsnap (1.15.0)
msgpack (~> 1.2) msgpack (~> 1.2)
bootstrap-datepicker-rails (1.9.0.1) bootstrap-datepicker-rails (1.9.0.1)
railties (>= 3.0) railties (>= 3.0)
builder (3.2.4) builder (3.2.4)
bullet (7.0.3) bullet (7.0.7)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.11) uniform_notifier (~> 1.11)
capybara (3.36.0) capybara (3.38.0)
addressable addressable
matrix matrix
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
@ -159,7 +193,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.1.10) concurrent-ruby (1.2.2)
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)
@ -170,6 +204,7 @@ GEM
activerecord (>= 5.a) activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0) database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1) database_cleaner-core (2.0.1)
date (3.3.3)
date_time_attribute (0.1.2) date_time_attribute (0.1.2)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
debug_inspector (1.1.0) debug_inspector (1.1.0)
@ -182,13 +217,13 @@ 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.0) doorkeeper (5.6.6)
railties (>= 5) railties (>= 5)
doorkeeper-i18n (5.2.5) doorkeeper-i18n (5.2.6)
doorkeeper (>= 5.2) doorkeeper (>= 5.2)
email_reply_trimmer (0.1.13) email_reply_trimmer (0.1.13)
erubi (1.11.0) erubi (1.12.0)
eventmachine (1.2.7) eventmachine (1.0.9.1)
exception_notification (4.5.0) exception_notification (4.5.0)
actionmailer (>= 5.2, < 8) actionmailer (>= 5.2, < 8)
activesupport (>= 5.2, < 8) activesupport (>= 5.2, < 8)
@ -199,16 +234,15 @@ GEM
factory_bot_rails (6.2.0) factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0) factory_bot (~> 6.2.0)
railties (>= 5.0.0) railties (>= 5.0.0)
faker (2.22.0) faker (3.1.0)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
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.0) globalid (1.0.1)
activesupport (>= 5.0) activesupport (>= 5.0)
haml (6.0.5) haml (5.2.2)
temple (>= 0.8.2) temple (>= 0.8.0)
thor
tilt tilt
haml-rails (2.1.0) haml-rails (2.1.0)
actionpack (>= 5.1) actionpack (>= 5.1)
@ -220,13 +254,19 @@ 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.12.0) i18n (1.14.1)
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)
i18n-spec (0.6.0) i18n-spec (0.6.0)
iso iso
ice_cube (0.16.4) ice_cube (0.16.4)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
importmap-rails (1.1.5)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
inherited_resources (1.13.1) inherited_resources (1.13.1)
actionpack (>= 5.2, < 7.1) actionpack (>= 5.2, < 7.1)
has_scope (~> 0.6) has_scope (~> 0.6)
@ -235,13 +275,13 @@ GEM
interception (0.5) interception (0.5)
iso (0.4.0) iso (0.4.0)
i18n i18n
jquery-rails (4.5.0) jquery-rails (4.5.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (2.6.2) json (2.6.3)
json-schema (2.8.1) json-schema (3.0.0)
addressable (>= 2.4) addressable (>= 2.8)
jsonapi-renderer (0.2.2) jsonapi-renderer (0.2.2)
kaminari (1.2.2) kaminari (1.2.2)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
@ -262,14 +302,18 @@ GEM
less (~> 2.6.0) less (~> 2.6.0)
sprockets (~> 3.0) sprockets (~> 3.0)
libv8 (3.16.14.19) libv8 (3.16.14.19)
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.19.1) loofah (2.21.3)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.12.0)
mail (2.7.1) mail (2.8.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
@ -282,29 +326,39 @@ GEM
thin thin
marcel (1.0.2) marcel (1.0.2)
matrix (0.4.2) matrix (0.4.2)
meta_request (0.7.3)
rack-contrib (>= 1.1, < 3)
railties (>= 3.0.0, < 7)
method_source (1.0.0) method_source (1.0.0)
midi-smtp-server (3.0.3) midi-smtp-server (3.0.3)
mime-types (3.4.1) mime-types (3.4.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2022.0105) mime-types-data (3.2022.0105)
mini_magick (4.12.0)
mini_mime (1.1.2) mini_mime (1.1.2)
mini_portile2 (2.8.0) mini_portile2 (2.8.2)
minitest (5.16.3) 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)
mustermann (3.0.0) mustermann (3.0.0)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
mysql2 (0.5.4) mysql2 (0.5.4)
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
timeout
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8) nio4r (2.5.8)
nokogiri (1.13.10) nokogiri (1.15.2)
mini_portile2 (~> 2.8.0) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
parallel (1.22.1) nokogiri (1.15.2-x86_64-linux)
parser (3.1.2.1) racc (~> 1.4)
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)
@ -322,75 +376,70 @@ GEM
pry-stack_explorer (0.6.1) pry-stack_explorer (0.6.1)
binding_of_caller (~> 1.0) binding_of_caller (~> 1.0)
pry (~> 0.13) pry (~> 0.13)
public_suffix (5.0.0) public_suffix (5.0.1)
puma (5.6.5) puma (6.0.2)
nio4r (~> 2.0) nio4r (~> 2.0)
racc (1.6.1) racc (1.7.0)
rack (2.2.4) rack (2.2.7)
rack-contrib (2.3.0)
rack (~> 2.0)
rack-cors (1.1.1) rack-cors (1.1.1)
rack (>= 2.0.0) rack (>= 2.0.0)
rack-protection (3.0.4) rack-protection (3.0.5)
rack rack
rack-test (2.0.2) rack-test (2.1.0)
rack (>= 1.3) rack (>= 1.3)
rails (5.2.8.1) rails (7.0.4)
actioncable (= 5.2.8.1) actioncable (= 7.0.4)
actionmailer (= 5.2.8.1) actionmailbox (= 7.0.4)
actionpack (= 5.2.8.1) actionmailer (= 7.0.4)
actionview (= 5.2.8.1) actionpack (= 7.0.4)
activejob (= 5.2.8.1) actiontext (= 7.0.4)
activemodel (= 5.2.8.1) actionview (= 7.0.4)
activerecord (= 5.2.8.1) activejob (= 7.0.4)
activestorage (= 5.2.8.1) activemodel (= 7.0.4)
activesupport (= 5.2.8.1) activerecord (= 7.0.4)
bundler (>= 1.3.0) activestorage (= 7.0.4)
railties (= 5.2.8.1) activesupport (= 7.0.4)
sprockets-rails (>= 2.0.0) bundler (>= 1.15.0)
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-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.4.4) rails-html-sanitizer (1.6.0)
loofah (~> 2.19, >= 2.19.1) loofah (~> 2.21)
rails-i18n (5.1.3) nokogiri (~> 1.14)
rails-i18n (7.0.6)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 5.0, < 6) railties (>= 6.0.0, < 8)
rails-settings-cached (0.4.3) rails-settings-cached (0.4.3)
rails (>= 4.2.0) rails (>= 4.2.0)
rails_tokeninput (1.7.0) rails_tokeninput (1.7.0)
railties (>= 3.1.0) railties (>= 3.1.0)
railties (5.2.8.1) railties (7.0.4)
actionpack (= 5.2.8.1) actionpack (= 7.0.4)
activesupport (= 5.2.8.1) activesupport (= 7.0.4)
method_source method_source
rake (>= 0.8.7) rake (>= 12.2)
thor (>= 0.19.0, < 2.0) thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.0.6)
ransack (2.5.0) ransack (3.2.1)
activerecord (>= 5.2.4) activerecord (>= 6.1.5)
activesupport (>= 5.2.4) activesupport (>= 6.1.5)
i18n i18n
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
recurring_select (3.0.0)
coffee-rails (>= 3.1)
ice_cube (>= 0.11)
jquery-rails (>= 3.0)
rails (>= 5.2)
sass-rails (>= 4.0)
redis (5.0.5) redis (5.0.5)
redis-client (>= 0.9.0) redis-client (>= 0.9.0)
redis-client (0.9.0) redis-client (0.11.2)
connection_pool connection_pool
redis-namespace (1.9.0) redis-namespace (1.10.0)
redis (>= 4) redis (>= 4)
ref (2.0.0) ref (2.0.0)
regexp_parser (2.6.0) regexp_parser (2.8.0)
responders (3.0.1) responders (3.0.1)
actionpack (>= 5.0) actionpack (>= 5.0)
railties (>= 5.0) railties (>= 5.0)
@ -400,59 +449,71 @@ GEM
redis-namespace (~> 1.6) redis-namespace (~> 1.6)
sinatra (>= 0.9.2) sinatra (>= 0.9.2)
rexml (3.2.5) rexml (3.2.5)
roo (2.8.3) roo (2.9.0)
nokogiri (~> 1) nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0) rubyzip (>= 1.3.0, < 3.0.0)
roo-xls (1.2.0) roo-xls (1.2.0)
nokogiri nokogiri
roo (>= 2.0.0, < 3) roo (>= 2.0.0, < 3)
spreadsheet (> 0.9.0) spreadsheet (> 0.9.0)
rspec (3.11.0) rspec (3.12.0)
rspec-core (~> 3.11.0) rspec-core (~> 3.12.0)
rspec-expectations (~> 3.11.0) rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.11.0) rspec-mocks (~> 3.12.0)
rspec-core (3.11.0) rspec-core (3.12.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.12.0)
rspec-expectations (3.11.1) rspec-expectations (3.12.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.12.0)
rspec-mocks (3.11.1) rspec-mocks (3.12.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.12.0)
rspec-rails (5.1.2) rspec-rails (6.0.1)
actionpack (>= 5.2) actionpack (>= 6.1)
activesupport (>= 5.2) activesupport (>= 6.1)
railties (>= 5.2) railties (>= 6.1)
rspec-core (~> 3.10) rspec-core (~> 3.11)
rspec-expectations (~> 3.10) rspec-expectations (~> 3.11)
rspec-mocks (~> 3.10) rspec-mocks (~> 3.11)
rspec-support (~> 3.10) rspec-support (~> 3.11)
rspec-rerun (1.1.0) rspec-rerun (1.1.0)
rspec (~> 3.0) rspec (~> 3.0)
rspec-support (3.11.1) rspec-support (3.12.0)
rubocop (1.36.0) rswag-api (2.7.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.9.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.7.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.50.2)
json (~> 2.3) json (~> 2.3)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.1.2.1) 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.20.1, < 2.0) rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.21.0) rubocop-ast (1.28.1)
parser (>= 3.1.1.0) parser (>= 3.2.1.0)
rubocop-rails (2.16.1) rubocop-rails (2.17.4)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.33.0, < 2.0)
rubocop-rspec (2.13.2) rubocop-rspec (2.16.0)
rubocop (~> 1.33) rubocop (~> 1.33)
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.3) ruby-prof (1.4.5)
ruby-progressbar (1.11.0) ruby-progressbar (1.13.0)
ruby-units (3.0.0) ruby-units (3.0.0)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
sass-rails (6.0.0) sass-rails (6.0.0)
@ -475,21 +536,21 @@ GEM
simple_form (5.1.0) simple_form (5.1.0)
actionpack (>= 5.2) actionpack (>= 5.2)
activemodel (>= 5.2) activemodel (>= 5.2)
simplecov (0.21.2) simplecov (0.22.0)
docile (~> 1.1) docile (~> 1.1)
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1) simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3) simplecov-html (0.12.3)
simplecov-lcov (0.8.0) simplecov-lcov (0.8.0)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
sinatra (3.0.4) sinatra (3.0.5)
mustermann (~> 3.0) mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4) rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.4) rack-protection (= 3.0.5)
tilt (~> 2.0) tilt (~> 2.0)
skinny (0.2.2) skinny (0.2.4)
eventmachine (~> 1.0) eventmachine (~> 1.0.0)
thin thin (>= 1.5, < 1.7)
spreadsheet (1.3.0) spreadsheet (1.3.0)
ruby-ole ruby-ole
sprockets (3.7.2) sprockets (3.7.2)
@ -503,17 +564,19 @@ GEM
sqlite3-ruby (1.3.3) sqlite3-ruby (1.3.3)
sqlite3 (>= 1.3.3) sqlite3 (>= 1.3.3)
table_print (1.5.7) table_print (1.5.7)
temple (0.8.2) temple (0.9.1)
terser (1.1.13)
execjs (>= 0.3.0, < 3)
therubyracer (0.12.3) therubyracer (0.12.3)
libv8 (~> 3.16.14.15) libv8 (~> 3.16.14.15)
ref ref
thin (1.8.1) thin (1.6.2)
daemons (~> 1.0, >= 1.0.9) daemons (>= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4) eventmachine (>= 1.0.0)
rack (>= 1, < 3) rack (>= 1.0.0)
thor (1.2.1) thor (1.2.2)
thread_safe (0.3.6)
tilt (2.0.11) tilt (2.0.11)
timeout (0.3.1)
ttfunk (1.7.0) ttfunk (1.7.0)
twitter-bootstrap-rails (2.2.8) twitter-bootstrap-rails (2.2.8)
actionpack (>= 3.1) actionpack (>= 3.1)
@ -522,20 +585,18 @@ 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 (1.2.10) tzinfo (2.0.6)
thread_safe (~> 0.1) concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8.2) unf_ext (0.0.8.2)
unicode-display_width (2.3.0) unicode-display_width (2.4.2)
uniform_notifier (1.16.0) uniform_notifier (1.16.0)
web-console (3.7.0) web-console (4.2.0)
actionview (>= 5.0) actionview (>= 6.0.0)
activemodel (>= 5.0) activemodel (>= 6.0.0)
bindex (>= 0.4.0) bindex (>= 0.4.0)
railties (>= 5.0) railties (>= 6.0.0)
websocket-driver (0.7.5) websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
@ -549,15 +610,16 @@ GEM
twitter-text twitter-text
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.8)
PLATFORMS PLATFORMS
ruby ruby
x86_64-linux
DEPENDENCIES DEPENDENCIES
active_model_serializers (~> 0.10.0) active_model_serializers (~> 0.10.0)
acts_as_tree acts_as_tree
acts_as_versioned! acts_as_versioned!
apivore
apparition apparition
attribute_normalizer attribute_normalizer
better_errors better_errors
@ -579,22 +641,24 @@ DEPENDENCIES
foodsoft_documents! foodsoft_documents!
foodsoft_links! foodsoft_links!
foodsoft_messages! foodsoft_messages!
foodsoft_mollie!
foodsoft_polls! foodsoft_polls!
foodsoft_wiki! foodsoft_wiki!
gaffe gaffe
haml haml (~> 5.0)
haml-rails haml-rails
hashie (~> 3.4.6) hashie (~> 3.4.6)
i18n-js (~> 3.0.0.rc8) i18n-js (~> 3.0.0.rc8)
i18n-spec i18n-spec
ice_cube ice_cube
image_processing (~> 1.12)
importmap-rails (~> 1.1)
inherited_resources inherited_resources
jquery-rails jquery-rails
kaminari kaminari
less-rails less-rails
listen listen
mailcatcher mailcatcher
meta_request
midi-smtp-server midi-smtp-server
mime-types mime-types
mysql2 mysql2
@ -604,26 +668,29 @@ DEPENDENCIES
pry-stack_explorer pry-stack_explorer
puma puma
rack-cors rack-cors
rails (~> 5.2) rails (~> 7.0)
rails-assets-listjs (= 0.2.0.beta.4) rails-assets-listjs (= 0.2.0.beta.4)
rails-i18n rails-i18n
rails-settings-cached (= 0.4.3) rails-settings-cached (= 0.4.3)
rails_tokeninput rails_tokeninput
ransack ransack
recurring_select recurring_select!
resque resque
roo roo
roo-xls roo-xls
rspec-core rspec-core
rspec-rails rspec-rails
rspec-rerun rspec-rerun
rswag-api
rswag-specs
rswag-ui
rubocop rubocop
rubocop-rails rubocop-rails
rubocop-rspec rubocop-rspec
ruby-filemagic ruby-filemagic
ruby-prof ruby-prof
ruby-units ruby-units
sass-rails sassc-rails
sd_notify sd_notify
select2-rails select2-rails
simple-navigation (~> 3.14.0) simple-navigation (~> 3.14.0)
@ -635,11 +702,11 @@ DEPENDENCIES
sprockets (< 4) sprockets (< 4)
sqlite3 (~> 1.3.6) sqlite3 (~> 1.3.6)
table_print table_print
terser (~> 1.1)
therubyracer therubyracer
twitter-bootstrap-rails (~> 2.2.8) twitter-bootstrap-rails (~> 2.2.8)
uglifier (>= 1.0.3)
web-console web-console
whenever whenever
BUNDLED WITH BUNDLED WITH
1.17.3 2.4.5

View file

@ -1,5 +1,6 @@
Foodsoft Foodsoft
========= =========
[![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions) [![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions)
[![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master) [![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master)
[![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft) [![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft)
@ -15,10 +16,16 @@ If you're a food coop considering to use foodsoft, please have a look at the [wi
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki). More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).
Roadmap
-------
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.
Developing Developing
---------- ----------
> 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.
Get foodsoft [running locally](doc/SETUP_DEVELOPMENT.md), Get foodsoft [running locally](doc/SETUP_DEVELOPMENT.md),
then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines) then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines)
page on the wiki. page on the wiki.
@ -35,7 +42,6 @@ Deploying
Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing
[hosting platform](https://foodcoops.net/foodsoft-hosting/). [hosting platform](https://foodcoops.net/foodsoft-hosting/).
License License
------- -------

View file

@ -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', __FILE__) require File.expand_path('config/application', __dir__)
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

View file

@ -1 +1 @@
4.7.99 4.8.99

View file

@ -8,6 +8,7 @@
//= 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

View file

@ -180,16 +180,12 @@ function updateBalance() {
$('#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 // determine bgcolor and submit button state according to balance
var bgcolor = '';
if (balance < minimumBalance) { if (balance < minimumBalance) {
bgcolor = '#FF0000';
$('#submit_button').attr('disabled', 'disabled') $('#submit_button').attr('disabled', 'disabled')
$('#available_funds_error').css('display', 'block')
} else { } else {
$('#submit_button').removeAttr('disabled') $('#submit_button').removeAttr('disabled')
} $('#available_funds_error').css('display', 'none')
// update bgcolor
for (i in itemTotal) {
$('#td_price_' + i).css('background-color', bgcolor);
} }
} }
@ -232,4 +228,6 @@ $(function() {
}); });
updateButtons($(document)); updateButtons($(document));
$(document).ready(updateBalance);
}); });

View file

@ -0,0 +1,31 @@
/*
* Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
* the trix-editor content (whether displayed or under editing). Feel free to incorporate this
* inclusion directly in any other asset bundle and remove this file.
*
*= require trix
*/
/*
* We need to override trix.csss image gallery styles to accommodate the
* <action-text-attachment> element we wrap around attachments. Otherwise,
* images in galleries will be squished by the max-width: 33%; rule.
*/
.trix-content .attachment-gallery > action-text-attachment,
.trix-content .attachment-gallery > .attachment {
flex: 1 0 33%;
padding: 0 0.5em;
max-width: 33%;
}
.trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
.trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
flex-basis: 50%;
max-width: 50%;
}
.trix-content action-text-attachment .attachment {
padding: 0 !important;
max-width: 100% !important;
}

View file

@ -7,4 +7,5 @@
*= require list.unlist *= require list.unlist
*= require list.missing *= require list.missing
*= require recurring_select *= require recurring_select
*= require actiontext
*/ */

View file

@ -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 => 303 redirect_to update_bank_accounts_admin_finances_url, status: :see_other
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 => 303 redirect_to update_bank_accounts_admin_finances_url, status: :see_other
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 => 303 redirect_to update_bank_accounts_admin_finances_url, status: :see_other
rescue => error rescue StandardError => e
flash.now[:alert] = error.message flash.now[:alert] = e.message
render template: 'shared/alert' render template: 'shared/alert'
end end
end end

View file

@ -6,6 +6,11 @@ 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
@ -15,11 +20,6 @@ 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])

View file

@ -1,5 +1,5 @@
class Admin::ConfigsController < Admin::BaseController class Admin::ConfigsController < Admin::BaseController
before_action :get_tabs, only: [:show, :list] before_action :get_tabs, only: %i[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,8 +38,9 @@ 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)
if config return unless 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] != '{}'
@ -51,7 +52,6 @@ class Admin::ConfigsController < Admin::BaseController
end end
end end
end end
end
def convert_config_value(value) def convert_config_value(value)
if value.is_a? ActionController::Parameters if value.is_a? ActionController::Parameters

View file

@ -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

View file

@ -6,25 +6,25 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController
render layout: false render layout: false
end end
def create
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
if @financial_transaction_class.save
redirect_to update_transaction_types_admin_finances_url, status: 303
else
render action: 'new', layout: false
end
end
def edit def edit
@financial_transaction_class = FinancialTransactionClass.find(params[:id]) @financial_transaction_class = FinancialTransactionClass.find(params[:id])
render action: 'new', layout: false render action: 'new', layout: false
end end
def create
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
if @financial_transaction_class.save
redirect_to update_transaction_types_admin_finances_url, status: :see_other
else
render action: 'new', layout: false
end
end
def update 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: 303 redirect_to update_transaction_types_admin_finances_url, status: :see_other
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: 303 redirect_to update_transaction_types_admin_finances_url, status: :see_other
rescue => error rescue StandardError => e
flash.now[:alert] = error.message flash.now[:alert] = e.message
render template: 'shared/alert' render template: 'shared/alert'
end end
end end

View file

@ -7,25 +7,25 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController
render layout: false render layout: false
end end
def create
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
if @financial_transaction_type.save
redirect_to update_transaction_types_admin_finances_url, status: 303
else
render action: 'new', layout: false
end
end
def edit def edit
@financial_transaction_type = FinancialTransactionType.find(params[:id]) @financial_transaction_type = FinancialTransactionType.find(params[:id])
render action: 'new', layout: false render action: 'new', layout: false
end end
def create
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
if @financial_transaction_type.save
redirect_to update_transaction_types_admin_finances_url, status: :see_other
else
render action: 'new', layout: false
end
end
def update 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: 303 redirect_to update_transaction_types_admin_finances_url, status: :see_other
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: 303 redirect_to update_transaction_types_admin_finances_url, status: :see_other
rescue => error rescue StandardError => e
flash.now[:alert] = error.message flash.now[:alert] = e.message
render template: 'shared/alert' render template: 'shared/alert'
end end
end end

View file

@ -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]) unless params[:email].blank? @maildeliverystatus = @maildeliverystatus.where(email: params[:email]) if params[:email].present?
@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 => error rescue StandardError => e
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message) redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.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 => error rescue StandardError => e
redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message) redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message)
end end
end end

View file

@ -2,16 +2,15 @@ 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', type: 'text/csv' send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv',
type: 'text/csv'
end end
# if somebody uses the search field: # if somebody uses the search field:
unless params[:query].blank? @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present?
@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
@ -19,8 +18,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('admin.ordergroups.destroy.notice') redirect_to admin_ordergroups_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error') redirect_to admin_ordergroups_url, alert: t('.error')
end end
end end

View file

@ -6,6 +6,11 @@ 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
@ -15,11 +20,6 @@ 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])

View file

@ -3,16 +3,14 @@ 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)
if request.format.csv? send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/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]) unless params[:user_name].blank? @users = @users.natural_search(params[:user_name]) if params[:user_name].present?
@users = @users.page(params[:page]).per(@per_page) @users = @users.page(params[:page]).per(@per_page)
end end
@ -20,17 +18,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('admin.users.destroy.notice') redirect_to admin_users_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to admin_users_url, alert: t('admin.users.destroy.error', error: error.message) redirect_to admin_users_url, alert: t('.error', error: e.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('admin.users.restore.notice') redirect_to admin_users_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to admin_users_url, alert: t('admin.users.restore.error', error: error.message) redirect_to admin_users_url, alert: t('.error', error: e.message)
end end
def sudo def sudo

View file

@ -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]}%") unless params[:query].blank? @workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present?
@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('admin.workgroups.destroy.notice') redirect_to admin_workgroups_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to admin_workgroups_url, alert: t('admin.workgroups.destroy.error', error: error.message) redirect_to admin_workgroups_url, alert: t('.error', error: e.message)
end end
end end

View file

@ -20,29 +20,30 @@ class Api::V1::BaseController < ApplicationController
def require_ordergroup def require_ordergroup
authenticate authenticate
unless current_ordergroup.present? return if current_ordergroup.present?
raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup')
end raise Api::Errors::PermissionRequired, 'Forbidden, must be in an ordergroup'
end end
def require_minimum_balance def require_minimum_balance
minimum_balance = FoodsoftConfig[:minimum_balance] or return minimum_balance = FoodsoftConfig[:minimum_balance] or return
if current_ordergroup.account_balance < minimum_balance return unless current_ordergroup.account_balance < minimum_balance
raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance))
end raise Api::Errors::PermissionRequired, t('application.controller.error_minimum_balance', min: minimum_balance)
end end
def require_enough_apples def require_enough_apples
if current_ordergroup.not_enough_apples? return unless current_ordergroup.not_enough_apples?
s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
raise Api::Errors::PermissionRequired.new(s) s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples,
end stop_ordering_under: FoodsoftConfig[:stop_ordering_under])
raise Api::Errors::PermissionRequired, s
end end
def require_config_enabled(config) def require_config_enabled(config)
unless FoodsoftConfig[config] return if FoodsoftConfig[config]
raise Api::Errors::PermissionRequired.new(t('application.controller.error_not_enabled', config: config))
end raise Api::Errors::PermissionRequired, t('application.controller.error_not_enabled', config: config)
end end
def skip_session def skip_session
@ -52,12 +53,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: 404, json: { error: 'not_found', error_description: msg } render status: :not_found, 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: 422, json: { error: 'not_acceptable', error_description: msg } render status: :unprocessable_entity, json: { error: 'not_acceptable', error_description: msg }
end end
def doorkeeper_unauthorized_render_options(error:) def doorkeeper_unauthorized_render_options(error:)
@ -70,11 +71,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: 403, json: { error: 'forbidden', error_description: msg } render status: :forbidden, 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

View file

@ -16,7 +16,8 @@ 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, transaction_type) ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user,
transaction_type)
render json: ft render json: ft
end end

View file

@ -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: [:create, :update] # destroy is ok before_action :require_minimum_balance, only: %i[create update] # destroy is ok
before_action :require_enough_apples, only: [:create, :update] # destroy is ok before_action :require_enough_apples, only: %i[create update] # destroy is ok
# @todo allow decreasing amounts when minimum balance isn't met # @todo allow decreasing amounts when minimum balance isn't met
def index def index
@ -35,7 +35,8 @@ 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, (update_params[:tolerance] || goa.tolerance).to_i) goa.update_quantities((update_params[:quantity] || goa.quantity).to_i,
(update_params[:tolerance] || goa.tolerance).to_i)
goa.order_article.update_results! goa.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)

View file

@ -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 { |c| financial_transaction_class_sums: FinancialTransactionClass.sorted.map do |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

View file

@ -19,11 +19,11 @@ class ApplicationController < ActionController::Base
private private
def set_user_last_activity def set_user_last_activity
if current_user && (session[:last_activity] == nil || session[:last_activity] < 1.minutes.ago) return unless current_user && (session[:last_activity].nil? || session[:last_activity] < 1.minute.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.
# To disable a controller in the plugin, you can use this as a `before_action`: # To disable a controller in the plugin, you can use this as a `before_action`:
@ -64,10 +64,10 @@ class ApplicationController < ActionController::Base
end end
def items_per_page def items_per_page
if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500 @per_page = if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
@per_page = params[:per_page].to_i params[:per_page].to_i
else else
@per_page = 20 20
end end
end end

View file

@ -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 => error rescue StandardError => e
redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message) redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: e.message)
end end
protected protected

View file

@ -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
if params['sort'] sort = if params['sort']
sort = case params['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
sort = "article_categories.name, articles.name" '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,42 +32,42 @@ 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
def edit
@article = Article.find(params[:id])
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
def edit
@article = Article.find(params[:id])
render :action => 'new', :layout => false
end
# Updates one Article and highlights the line if succeded # Updates one Article and highlights the line if succeded
def update def update
@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,21 +87,19 @@ class ArticlesController < ApplicationController
def update_all def update_all
invalid_articles = false invalid_articles = false
begin
Article.transaction do Article.transaction do
unless params[:articles].blank? if params[:articles].present?
# Update other article attributes... # Update other article attributes...
@articles = Article.find(params[:articles].keys) @articles = Article.find(params[:articles].keys)
@articles.each do |article| @articles.each do |article|
unless article.update(params[:articles][article.id.to_s]) unless article.update(params[:articles][article.id.to_s])
invalid_articles = true unless invalid_articles # Remember that there are validation errors invalid_articles ||= true # 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
if invalid_articles if invalid_articles
# An error has occurred, transaction has been rolled back. # An error has occurred, transaction has been rolled back.
@ -134,16 +132,15 @@ 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 => error rescue StandardError => e
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 => error) alert: I18n.t('errors.general_msg', msg: e)
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 def upload; end
end
# Update articles from a spreadsheet # Update articles from a spreadsheet
def parse_upload def parse_upload
@ -151,13 +148,15 @@ class ArticlesController < ApplicationController
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 @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile,
options
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice') redirect_to supplier_articles_path(@supplier),
notice: I18n.t('articles.controller.parse_upload.notice')
end end
@ignored_article_count = 0 @ignored_article_count = 0
rescue => error rescue StandardError => e
redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message) redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message)
end end
# sync all articles with the external database # sync all articles with the external database
@ -165,13 +164,14 @@ 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), :alert => I18n.t('articles.controller.sync.shared_alert', :supplier => @supplier.name) redirect_to supplier_articles_url(@supplier),
alert: I18n.t('articles.controller.sync.shared_alert', supplier: @supplier.name)
end 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
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? return unless @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
end redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.sync.notice')
end end
# Updates, deletes articles when upload or sync form is submitted # Updates, deletes articles when upload or sync form is submitted
@ -186,7 +186,7 @@ class ArticlesController < ApplicationController
# delete articles # delete articles
begin begin
@outlisted_articles.each(&:mark_as_deleted) @outlisted_articles.each(&:mark_as_deleted)
rescue rescue StandardError
# 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 +198,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 +218,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] unless params[:article_category_id].blank? @article.article_category_id = params[:article_category_id] if params[:article_category_id].present?
if params[:direct] && !params[:article_category_id].blank? && @article.valid? && @article.save if params[:direct] && params[:article_category_id].present? && @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

View file

@ -9,15 +9,19 @@ 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)
if session[:user_id] && params[:foodcoop] return unless 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, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path)) redirect_to root_url,
alert: I18n.t('application.controller.error_denied',
sign_in: ActionController::Base.helpers.link_to(
t('application.controller.error_denied_sign_in'), login_path
))
end end
private private
@ -47,12 +51,7 @@ 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
@ -73,6 +72,11 @@ 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
@ -116,13 +120,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)
unless @group.member?(@current_user) || @current_user.role_admin? return if @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 not params[:token].blank? if params[:token].present?
begin begin
TokenVerifier.new(prefix).verify(params[:token]) TokenVerifier.new(prefix).verify(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature rescue ActiveSupport::MessageVerifier::InvalidSignature

View file

@ -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)
unless scopes.any? { |scope| doorkeeper_scope_permitted?(scope) } return if scopes.any? { |scope| doorkeeper_scope_permitted?(scope) }
raise Api::Errors::PermissionRequired.new('Forbidden, no permission')
end raise Api::Errors::PermissionRequired, 'Forbidden, no permission'
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,9 +48,7 @@ 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
if scope_parts.last == 'user' return true 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
@ -64,8 +62,8 @@ module Concerns::AuthApi
end end
case scope case scope
when 'orders:read' then return true when 'orders:read' then true
when 'orders:write' then return current_user.role_orders? when 'orders:write' then current_user.role_orders?
end end
end end
end end

View file

@ -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.new 'Foodcoop Not Found' raise ActionController::RoutingError, '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

View file

@ -18,7 +18,7 @@ module Concerns::Locale
end end
def browser_language def browser_language
request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil request.env['HTTP_ACCEPT_LANGUAGE']&.scan(/^[a-z]{2}/)&.first
end 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 unless language.blank? language.presence&.to_sym if language.present?
end end
def available_locales def available_locales
@ -38,10 +38,10 @@ module Concerns::Locale
end end
def set_locale def set_locale
if available_locales.include?(select_language_according_to_priority) ::I18n.locale = if available_locales.include?(select_language_according_to_priority)
::I18n.locale = select_language_according_to_priority select_language_according_to_priority
else else
::I18n.locale = default_language default_language
end end
locale = session[:locale] = ::I18n.locale locale = session[:locale] = ::I18n.locale

View file

@ -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

View file

@ -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,6 +15,10 @@ 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])
@ -22,14 +26,10 @@ 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

View file

@ -1,13 +1,12 @@
class FeedbackController < ApplicationController class FeedbackController < ApplicationController
def new def new; end
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('feedback.create.notice') redirect_to root_url, notice: t('.notice')
else else
render :action => 'new' render action: 'new'
end end
end end
end end

View file

@ -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('finance.balancing.new.alert') if @order.closed? flash.now.alert = t('.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' then when 'name'
@articles.order('articles.name ASC') @articles.order('articles.name ASC')
when 'name_reverse' then when 'name_reverse'
@articles.order('articles.name DESC') @articles.order('articles.name DESC')
when 'order_number' then when 'order_number'
@articles.order('articles.order_number ASC') @articles.order('articles.order_number ASC')
when 'order_number_reverse' then when 'order_number_reverse'
@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 => error rescue StandardError => e
redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: error.message) redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: e.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('finance.balancing.close.notice') redirect_to finance_order_index_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message) redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.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('finance.balancing.close_direct.notice') redirect_to finance_order_index_url, notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to finance_order_index_url, alert: t('finance.balancing.close_direct.alert', message: error.message) redirect_to finance_order_index_url, alert: t('.alert', message: e.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('finance.balancing.close_all_direct_with_invoice.notice', count: count) redirect_to finance_order_index_url, notice: t('.notice', count: count)
rescue => error rescue StandardError => e
redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: error.message) redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: e.message)
end end
end end

View file

@ -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 => error rescue StandardError => e
redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: error.message) redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: e.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 => error rescue StandardError => e
flash.alert = t('errors.general_msg', msg: error.message) flash.alert = t('errors.general_msg', msg: e.message)
needs_redirect = true needs_redirect = true
ensure ensure
return unless needs_redirect return unless needs_redirect

View file

@ -3,26 +3,30 @@ class Finance::BankTransactionsController < ApplicationController
inherit_resources inherit_resources
def index def index
if params["sort"] sort = if params['sort']
sort = case params["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
sort = "date DESC" '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)
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%", "%#{params[:query]}%") unless params[:query].nil? unless params[:query].nil?
@bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%",
"%#{params[:query]}%")
end
@bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page) @bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page)
respond_to do |format| respond_to do |format|
format.js; format.html { render } format.js
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

View file

@ -1,5 +1,5 @@
class Finance::FinancialLinksController < Finance::BaseController class Finance::FinancialLinksController < Finance::BaseController
before_action :find_financial_link, except: [:create, :incomplete] before_action :find_financial_link, except: %i[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] then if params[:bank_transaction]
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,14 +72,16 @@ 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.note, current_user, financial_transaction.financial_transaction_type, @financial_link financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount,
financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link
redirect_to finance_link_url(@financial_link), notice: t('.notice') redirect_to finance_link_url(@financial_link), notice: t('.notice')
rescue => error rescue StandardError => e
redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: error) redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: e)
end end
def index_financial_transaction def index_financial_transaction
@financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type, :ordergroup) @financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type,
:ordergroup)
end end
def add_financial_transaction def add_financial_transaction
@ -123,7 +125,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).pluck(:ordergroup_id).first FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pick(:ordergroup_id)
JOIN bank_transactions a ON financial_transactions.financial_link_id = a.financial_link_id JOIN bank_transactions 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

View file

@ -1,24 +1,24 @@
class Finance::FinancialTransactionsController < ApplicationController class Finance::FinancialTransactionsController < ApplicationController
before_action :authenticate_finance before_action :authenticate_finance
before_action :find_ordergroup, :except => [:new_collection, :create_collection, :index_collection] before_action :find_ordergroup, except: %i[new_collection create_collection index_collection]
inherit_resources inherit_resources
# belongs_to :ordergroup # belongs_to :ordergroup
def index def index
if params['sort'] sort = if params['sort']
sort = case params['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
sort = "created_on DESC" 'created_on DESC'
end end
@q = FinancialTransaction.search(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)
@financial_transactions_all = @financial_transactions_all.visible unless params[:show_hidden] @financial_transactions_all = @financial_transactions_all.visible unless params[:show_hidden]
@financial_transactions_all = @financial_transactions_all.where(ordergroup_id: @ordergroup.id) if @ordergroup @financial_transactions_all = @financial_transactions_all.where(ordergroup_id: @ordergroup.id) if @ordergroup
@ -26,9 +26,11 @@ 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.html { render } format.js
format.html { render }
format.csv do format.csv do
send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv' send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv',
type: 'text/csv'
end end
end end
end end
@ -38,31 +40,29 @@ class Finance::FinancialTransactionsController < ApplicationController
end end
def new def new
if @ordergroup @financial_transaction = if @ordergroup
@financial_transaction = @ordergroup.financial_transactions.build @ordergroup.financial_transactions.build
else else
@financial_transaction = FinancialTransaction.new 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
if @financial_transaction.ordergroup
@financial_transaction.add_transaction!
else
@financial_transaction.save! @financial_transaction.save!
end redirect_to finance_group_transactions_path(@ordergroup),
redirect_to finance_group_transactions_path(@ordergroup), notice: I18n.t('finance.financial_transactions.controller.create.notice') notice: I18n.t('finance.financial_transactions.controller.create.notice')
rescue ActiveRecord::RecordInvalid => error rescue ActiveRecord::RecordInvalid => e
flash.now[:alert] = error.message flash.now[:alert] = e.message
render :action => :new 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), notice: t('finance.financial_transactions.controller.destroy.notice') redirect_to finance_group_transactions_path(transaction.ordergroup),
notice: t('finance.financial_transactions.controller.destroy.notice')
end end
def new_collection def new_collection
@ -88,7 +88,8 @@ class Finance::FinancialTransactionsController < ApplicationController
params[:financial_transactions].each do |trans| params[:financial_transactions].each do |trans|
# ignore empty amount fields ... # ignore empty amount fields ...
unless trans[:amount].blank? next if 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])
@ -99,7 +100,6 @@ class Finance::FinancialTransactionsController < ApplicationController
ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link) ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link)
foodcoop_amount -= amount foodcoop_amount -= amount
end end
end
if params[:create_foodcoop_transaction] if params[:create_foodcoop_transaction]
ft = FinancialTransaction.new({ ft = FinancialTransaction.new({
@ -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 => error rescue StandardError => e
flash.now[:alert] = error.message flash.now[:alert] = e.message
render action: :new_collection render action: :new_collection
end end

View file

@ -1,15 +1,16 @@
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: [:show, :edit, :update, :destroy] before_action :find_invoice, only: %i[show edit update destroy]
before_action :ensure_can_edit, only: [:edit, :update, :destroy] before_action :ensure_can_edit, only: %i[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.html { render } format.js
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
@ -20,11 +21,10 @@ 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 def show; end
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,12 +36,14 @@ 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, supplier_id).order(date: :desc).limit(25) @deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id,
@orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(ends: :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)
end end
def create def create
@ -58,7 +60,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
@ -81,7 +83,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
@ -92,8 +94,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
unless @invoice.user_can_edit?(current_user) return if @invoice.user_can_edit?(current_user)
deny_access deny_access
end end
end
end end

View file

@ -1,17 +1,20 @@
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)
@ordergroups = @ordergroups.include_transaction_class_sum @ordergroups = @ordergroups.include_transaction_class_sum
@ordergroups = @ordergroups.where('groups.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil? @ordergroups = @ordergroups.where('groups.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
@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|
tmp[c.id] = c.financial_transactions.reduce(0) { |sum, t| sum + (t.amount || 0) }
end
end end
end end

View file

@ -1,20 +1,16 @@
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'])
unless params[:name].blank? # Search by name @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%") if params[:name].present? # Search by name
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%")
end
if params[:only_active] # Select only active groups @ordergroups = @ordergroups.active 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

View file

@ -1,19 +1,22 @@
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]) unless params[:user_name].blank? @users = @users.natural_search(params[:user_name]) if params[:user_name].present?
if params[:ordergroup_name] if params[:ordergroup_name]
@users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", "%#{params[:ordergroup_name]}%") @users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?",
"%#{params[:ordergroup_name]}%")
end 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

View file

@ -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

View file

@ -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: [:new, :create] before_action :find_group_order_article, except: %i[new create]
layout false # We only use this controller to server js snippets, no need for layout rendering layout false # We only use this controller to server js snippets, no need for layout rendering

View file

@ -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 => [:new, :create, :edit, :update, :order, :stock_order, :saveOrder] before_action :ensure_open_order, only: %i[new create edit update order stock_order saveOrder]
before_action :ensure_my_group_order, only: [:show, :edit, :update] before_action :ensure_my_group_order, only: %i[show edit update]
before_action :enough_apples?, only: [:new, :create] before_action :enough_apples?, only: %i[new create]
# Index page. # Index page.
def index def index
@ -13,9 +13,17 @@ class GroupOrdersController < ApplicationController
@finished_not_closed_orders_including_group_order = Order.finished_not_closed.ordergroup_group_orders_map(@ordergroup) @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
@ -23,34 +31,26 @@ 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 => exception rescue StandardError => e
logger.error('Failed to update order: ' + exception.message) logger.error('Failed to update order: ' + e.message)
redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_general') redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_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 => exception rescue StandardError => e
logger.error('Failed to update order: ' + exception.message) logger.error('Failed to update order: ' + e.message)
redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_general') redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_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
if @ordergroup.nil? return unless @ordergroup.nil?
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
end redirect_to root_url, alert: I18n.t('group_orders.errors.no_member')
end end
def ensure_open_order def ensure_open_order
@order = Order.includes([:supplier, :order_articles]).find(order_id_param) @order = Order.includes(%i[supplier order_articles]).find(order_id_param)
unless @order.open? 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,18 +91,18 @@ 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])
if @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?) return unless @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?)
redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound') redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound')
end end
end
def enough_apples? def enough_apples?
if @ordergroup.not_enough_apples? return unless @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
params[:order_id] || (params[:group_order] && params[:group_order][:order_id]) params[:order_id] || (params[:group_order] && params[:group_order][:order_id])

View file

@ -9,8 +9,7 @@ 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 def profile; end
end
def reference_calculator def reference_calculator
if current_user.ordergroup if current_user.ordergroup
@ -36,40 +35,43 @@ class HomeController < ApplicationController
@user = @current_user @user = @current_user
@ordergroup = @user.ordergroup @ordergroup = @user.ordergroup
unless @ordergroup.nil? if @ordergroup.nil?
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
else
@ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id) @ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id)
if params['sort'] sort = if params['sort']
sort = case params['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
sort = "created_on DESC" 'created_on DESC'
end end
@financial_transactions = @ordergroup.financial_transactions.visible.page(params[:page]).per(@per_page).order(sort) @financial_transactions = @ordergroup.financial_transactions.visible.page(params[:page]).per(@per_page).order(sort)
@financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?', "%#{params[:query]}%") if params[:query].present? if params[:query].present?
@financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?',
"%#{params[:query]}%")
end
else
redirect_to root_path, alert: I18n.t('home.no_ordergroups')
end end
end end
# cancel personal memberships direct from the myProfile-page # cancel personal memberships direct from the myProfile-page
def cancel_membership def cancel_membership
if params[:membership_id] membership = if params[:membership_id]
membership = @current_user.memberships.find!(params[:membership_id]) @current_user.memberships.find(params[:membership_id])
else else
membership = @current_user.memberships.find_by_group_id!(params[:group_id]) @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
@ -82,8 +84,8 @@ class HomeController < ApplicationController
end end
def ordergroup_params def ordergroup_params
if params[:user][:ordergroup] return unless params[:user][:ordergroup]
params.require(:user).require(:ordergroup).permit(:contact_address) params.require(:user).require(:ordergroup).permit(:contact_address)
end end
end
end end

View file

@ -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,6 +27,10 @@ class InvitesController < ApplicationController
protected protected
def authenticate_membership_or_admin_for_invites def authenticate_membership_or_admin_for_invites
authenticate_membership_or_admin((params[:invite][:group_id] rescue params[:id])) authenticate_membership_or_admin(begin
params[:invite][:group_id]
rescue StandardError
params[:id]
end)
end end
end end

View file

@ -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 => [:new_password, :update_password] before_action :validate_token, only: %i[new_password update_password]
# Display the form to enter an email address requesting a token to set a new password. # Display the form to enter an email address requesting a token to set a new password.
def forgot_password def forgot_password
@ -9,20 +9,17 @@ class LoginController < ApplicationController
# Sends an email to a user with the token that allows setting a new password through action "password". # Sends an email to a user with the token that allows setting a new password through action "password".
def reset_password def reset_password
if request.get? || params[:user].nil? # Catch for get request and give better error message. redirect_to forgot_password_url, alert: I18n.t('errors.general_again') and return 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 def new_password; end
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.
@ -32,7 +29,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
@ -50,14 +47,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
@ -65,8 +62,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])
if (@user.nil? || @user.reset_password_expires < Time.now) return unless @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

View file

@ -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: [:new, :create] before_action :authenticate_finance_or_invoices, except: %i[new create]
before_action :authenticate_finance_orders_or_pickup, except: [:edit, :update, :destroy] before_action :authenticate_finance_orders_or_pickup, except: %i[edit update destroy]
layout false # We only use this controller to serve js snippets, no need for layout rendering layout false # We only use this controller to serve js snippets, no need for layout rendering
@ -9,28 +9,26 @@ class OrderArticlesController < ApplicationController
@order_article = @order.order_articles.build(params[:order_article]) @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
unless @order_article && @order_article.units_to_order == 0 @order_article = @order.order_articles.build(params[:order_article]) 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 rescue StandardError
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 rescue StandardError
render action: :edit render action: :edit
end end
end end

View file

@ -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

View file

@ -5,24 +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, except: [:receive, :receive_on_order_article_create, :receive_on_order_article_update, :show] before_action :authenticate_orders,
before_action :remove_empty_article, only: [:create, :update] except: %i[receive receive_on_order_article_create receive_on_order_article_update show]
before_action :remove_empty_article, only: %i[create update]
# List orders # 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
if params['sort'] sort = if params['sort']
sort = case params['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
sort = "ends DESC" '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)
@ -43,7 +44,7 @@ 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]
@ -66,8 +67,14 @@ 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 => error rescue StandardError => e
redirect_to orders_url, alert: t('errors.general_msg', msg: error.message) redirect_to orders_url, alert: t('errors.general_msg', msg: e.message)
end
# Page to edit an exsiting order.
# editing finished orders is done in FinanceController
def edit
@order = Order.includes(:articles).find(params[:id])
end end
# Save a new order. # Save a new order.
@ -81,31 +88,25 @@ 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.
@ -113,8 +114,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 => error rescue StandardError => e
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message) redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message)
end end
# Send a order to the supplier. # Send a order to the supplier.
@ -122,20 +123,18 @@ 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 => error rescue StandardError => e
redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.message) redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message)
end end
def receive def receive
@order = Order.find(params[:id]) @order = Order.find(params[:id])
unless request.post? if 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?
@ -145,23 +144,25 @@ 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 if not params[:order_articles] return unless params[:order_articles]
# where to leave remainder during redistribution # where to leave remainder during redistribution
rest_to = [] rest_to = []
@ -176,35 +177,42 @@ 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|
unless oa_params.blank? next if 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 end
oa.save! oa.save!
end 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])
notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1], units: cunits[1]) if params[:rest_to_tolerance] if params[:rest_to_tolerance]
notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1],
units: cunits[1])
end
notice << I18n.t('orders.update_order_amounts.msg3', count: counts[2], units: cunits[2]) if params[:rest_to_stock] 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], units: cunits[3]) notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3],
units: cunits[3])
end end
notice.join(', ') notice.join(', ')
end end
def remove_empty_article def remove_empty_article
params[:order][:article_ids].reject!(&:blank?) if params[:order] && params[:order][:article_ids] params[:order][:article_ids].compact_blank! if params[:order] && params[:order][:article_ids]
end end
end end

View file

@ -12,16 +12,20 @@ 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
redirect_to login_url, :notice => I18n.t('sessions.logged_out') if FoodsoftConfig[:logout_redirect_url].present?
redirect_to FoodsoftConfig[:logout_redirect_url]
else
redirect_to login_url, notice: I18n.t('sessions.logged_out')
end
end end
# redirect to root, going to default foodcoop when none given # redirect to root, going to default foodcoop when none given

View file

@ -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

View file

@ -7,57 +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
# three possibilites to fill a new_stock_article form
# (1) start from blank or use params
def new
@stock_article = StockArticle.new(params[:stock_article])
render :layout => false
end
# (2) StockArticle as template
def copy
@stock_article = StockArticle.find(params[:stock_article_id]).dup
render :layout => false
end
# (3) non-stock Article as template
def derive
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
render :layout => false
end
def create
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
@stock_article.save!
render :layout => false
rescue ActiveRecord::RecordInvalid
render :action => 'new', :layout => false
end
def edit
@stock_article = StockArticle.find(params[:id])
render :layout => false
end
def update
@stock_article = StockArticle.find(params[:id])
@stock_article.update!(params[:stock_article])
render :layout => false
rescue ActiveRecord::RecordInvalid
render :action => 'edit', :layout => false
end end
def show def show
@ -65,24 +21,68 @@ class StockitController < ApplicationController
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC') @stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
end end
# three possibilites to fill a new_stock_article form
# (1) start from blank or use params
def new
@stock_article = StockArticle.new(params[:stock_article])
render layout: false
end
# (2) StockArticle as template
def copy
@stock_article = StockArticle.find(params[:stock_article_id]).dup
render layout: false
end
# (3) non-stock Article as template
def derive
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
render layout: false
end
def edit
@stock_article = StockArticle.find(params[:id])
render layout: false
end
def create
@stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article]))
@stock_article.save!
render layout: false
rescue ActiveRecord::RecordInvalid
render action: 'new', layout: false
end
def update
@stock_article = StockArticle.find(params[:id])
@stock_article.update!(params[:stock_article])
render layout: false
rescue ActiveRecord::RecordInvalid
render action: 'edit', layout: false
end
def show_on_stock_article_update # See publish/subscribe design pattern in /doc. 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 => error rescue StandardError => e
render :partial => "destroy_fail", :layout => false, render partial: 'destroy_fail', layout: false,
:locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.message) } locals: { fail_msg: I18n.t('errors.general_msg', msg: e.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

View file

@ -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: 404 render body: nil, content_type: 'text/css', status: :not_found
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'

View file

@ -1,5 +1,5 @@
class SuppliersController < ApplicationController class SuppliersController < ApplicationController
before_action :authenticate_suppliers, :except => [:index, :list] before_action :authenticate_suppliers, except: %i[index list]
helper :deliveries helper :deliveries
def index def index
@ -24,6 +24,10 @@ 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
@ -31,21 +35,17 @@ 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 => e rescue StandardError => 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

View file

@ -11,35 +11,33 @@ class TasksController < ApplicationController
@accepted_tasks = Task.accepted_tasks_for(current_user) @accepted_tasks = Task.accepted_tasks_for(current_user)
end end
def new
@task = Task.new(current_user_id: current_user.id)
end
def create
@task = Task.new(current_user_id: current_user.id)
@task.created_by = current_user
@task.attributes = (task_params)
if params[:periodic]
@task.periodic_task_group = PeriodicTaskGroup.new
end
if @task.save
@task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic]
redirect_to tasks_url, :notice => I18n.t('tasks.create.notice')
else
render :template => "tasks/new"
end
end
def show def show
@task = Task.find(params[:id]) @task = Task.find(params[:id])
end end
def new
@task = Task.new(current_user_id: current_user.id)
end
def edit def edit
@task = Task.find(params[:id]) @task = Task.find(params[:id])
@periodic = !!params[:periodic] @periodic = !!params[:periodic]
@task.current_user_id = current_user.id @task.current_user_id = current_user.id
end end
def create
@task = Task.new(current_user_id: current_user.id)
@task.created_by = current_user
@task.attributes = (task_params)
@task.periodic_task_group = PeriodicTaskGroup.new if params[:periodic]
if @task.save
@task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic]
redirect_to tasks_url, notice: I18n.t('tasks.create.notice')
else
render template: 'tasks/new'
end
end
def update def update
@task = Task.find(params[:id]) @task = Task.find(params[:id])
task_group = @task.periodic_task_group task_group = @task.periodic_task_group
@ -50,16 +48,14 @@ 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')
if was_periodic && !@task.periodic? flash[:notice] = I18n.t('tasks.update.notice_converted') 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
@ -75,7 +71,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"
@ -85,20 +81,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
@ -109,9 +105,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])
if @group.is_a? Ordergroup return unless @group.is_a? Ordergroup
redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found')
end redirect_to tasks_url, alert: I18n.t('tasks.error_not_found')
end end
private private

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,16 +20,18 @@ 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
unless order.supplier.try(:customer_number).blank? if order.supplier.try(:customer_number).present?
text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", size: fontsize(9), align: :right text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}",
size: fontsize(9), align: :right
move_down 5 move_down 5
end end
unless contact[:phone].blank? if contact[:phone].present?
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
unless contact[:email].blank? if contact[:email].present?
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :right text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9),
align: :right
end end
end end
@ -38,7 +40,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
unless order.supplier.try(:fax).blank? if order.supplier.try(:fax).present?
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
@ -50,7 +52,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
unless order.supplier.try(:contact_person).blank? if order.supplier.try(:contact_person).present?
text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}" text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
move_down 10 move_down 10
end end
@ -78,8 +80,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 = [:top, :bottom] table.row(data.length - 1).columns(0..5).borders = %i[top bottom]
table.row(data.length - 1).columns(0).borders = [:top, :bottom, :left] table.row(data.length - 1).columns(0).borders = %i[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),
@ -98,7 +100,7 @@ class OrderFax < OrderPdf
.preload(:article, :article_price) .preload(:article, :article_price)
end end
def each_order_article def each_order_article(&block)
order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa } order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block)
end end
end end

View file

@ -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 = [:bottom, :left] table.row(0).borders = %i[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 = ['dddddd', 'ffffff'] table.row_colors = %w[dddddd ffffff]
end end
first_page = false first_page = false

View file

@ -28,7 +28,11 @@ 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
block ||= proc { config_input_field form, key, options.merge(options[:input_html]) } if options[:as] == :select_recurring if options[:as] == :select_recurring
block ||= proc {
config_input_field form, key, options.merge(options[:input_html])
}
end
form.input key, options, &block form.input key, options, &block
end end
@ -57,11 +61,12 @@ 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, as: :hidden) + form.check_box(key, options, checked_value, false) form.hidden_field(key, id: "#{key}_", value: unchecked_value,
as: :hidden) + form.check_box(key, options, checked_value, false)
elsif options[:as] == :select_recurring 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] unless options[:value].blank? options[:rules].unshift options[:value] if options[:value].present?
options[:rules].push [I18n.t('recurring_select.not_recurring'), '{}'] if options.delete(:allow_blank) # blank after current value 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
@ -73,7 +78,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 = {}) def config_use_heading(form, key, options = {}, &block)
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,
@ -83,9 +88,7 @@ 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]}") do fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}", &block)
yield
end
head + fields head + fields
end end
@ -127,7 +130,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: '')
unless tooltip.blank? if tooltip.present?
options[:data] ||= {} options[:data] ||= {}
options[:data][:toggle] ||= 'tooltip' options[:data][:toggle] ||= 'tooltip'
options[:data][:placement] ||= 'right' options[:data][:placement] ||= 'right'

View file

@ -2,9 +2,7 @@ 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?
if ordergroup.contact_person.present? s += "\n" + Ordergroup.human_attribute_name(:contact) + ': ' + ordergroup.contact_person if ordergroup.contact_person.present?
s += "\n" + Ordergroup.human_attribute_name(:contact) + ": " + ordergroup.contact_person
end
s s
end end
end end

View file

@ -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 => "%d.%m.%Y %H:%M") unless time.nil? I18n.l(time, format: :foodsoft_datetime) 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,21 +63,19 @@ 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 then when key
'sortup' 'sortup'
when key + '_reverse' then when key + '_reverse'
'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]
@ -95,14 +93,16 @@ 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| !['short', 'desc'].include?(a) }) i18nopts = { count: 2 }.merge(options.select { |a| !%w[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, options.merge({ fallback: true, default: '', count: 2 })) desc ||= model.human_attribute_name("#{attribute}_desc".to_sym,
options.merge({ fallback: true, default: '', count: 2 }))
desc.blank? && desc = s desc.blank? && desc = s
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({ fallback: true, default: '', count: 2 })) sshort = model.human_attribute_name("#{attribute}_short".to_sym,
s = raw "<abbr title='#{desc}'>#{sshort}</abbr>" unless sshort.blank? options.merge({ fallback: true, default: '', count: 2 }))
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')
return weekdays[dayNumber] 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,27 +150,29 @@ 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 = Hash[roles.map { |r| [r, I18n.t("helpers.application.role_#{r}")] }] names = roles.index_with { |r| I18n.t("helpers.application.role_#{r}") }
if icon if icon
roles.map { |r| image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) }.join('&nbsp;').html_safe roles.map do |r|
image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r])
end.join('&nbsp;').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" target: '_blank', rel: 'noopener'
end end
# Returns flash messages html. # Returns flash messages html.
@ -186,8 +188,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
@ -195,17 +197,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})"
@ -216,7 +218,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
@ -258,9 +260,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 = {})
unless FoodsoftConfig[:custom_css].blank? return if 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

View file

@ -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" if !article.availability classes << 'unavailable' unless 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

View file

@ -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| not except.index(a.id).nil? } if except articles = articles.reject { |a| !except.index(a.id).nil? } if except
block_given? or block = Proc.new { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" } block_given? or block = proc { |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,10 +23,14 @@ module DeliveriesHelper
end end
def stock_change_remove_link(stock_change_form) def stock_change_remove_link(stock_change_form)
return link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'remove_new_stock_change btn btn-small' if stock_change_form.object.new_record? if stock_change_form.object.new_record?
return link_to t('deliveries.stock_change_fields.remove_article'), '#',
class: 'remove_new_stock_change btn btn-small'
end
output = stock_change_form.hidden_field :_destroy output = stock_change_form.hidden_field :_destroy
output += link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'destroy_stock_change btn btn-small' output += link_to t('deliveries.stock_change_fields.remove_article'), '#',
return output.html_safe class: 'destroy_stock_change btn btn-small'
output.html_safe
end end
end end

View file

@ -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' then when 'edit_results'
'edit_results_by_articles' 'edit_results_by_articles'
when 'groups_overview' then when 'groups_overview'
'shared/articles_by/groups' 'shared/articles_by/groups'
when 'articles_overview' then when 'articles_overview'
'shared/articles_by/articles' 'shared/articles_by/articles'
end end
end end

View file

@ -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

View file

@ -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
unless goa.group_order.order.finished? && current_user.role_finance? if 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

View file

@ -1,10 +1,11 @@
module GroupOrdersHelper module GroupOrdersHelper
def data_to_js(ordering_data) def data_to_js(ordering_data)
ordering_data[:order_articles].map { |id, data| ordering_data[:order_articles].map do |id, data|
[id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], data[:used_quantity], data[:quantity_available]] [id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance],
}.map { |row| data[:used_quantity], data[:quantity_available]]
end.map do |row|
"addData(#{row.join(', ')});" "addData(#{row.join(', ')});"
}.join("\n") end.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.
@ -14,9 +15,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
@ -26,7 +27,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'
@ -45,12 +46,12 @@ 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
return 'missing-few'; 'missing-few'
elsif (quantity_missing == 0) elsif quantity_missing == 0
return '' ''
else else
return 'missing-many' 'missing-many'
end end
end end
end end

View file

@ -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].reject(&:blank?).join(' ')})" "#{article.name} (#{[article.unit, pkg_info].compact_blank.join(' ')})"
end end
end end

View file

@ -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,14 +29,14 @@ module OrdersHelper
nil nil
else else
units_info = [] units_info = []
[:units_to_order, :units_billed, :units_received].map do |unit| %i[units_to_order units_billed units_received].map do |unit|
if n = order_article.send(unit) next unless 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
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 = "&nbsp;".html_safe c = '&nbsp;'.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,11 +94,12 @@ 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', title: t('orders.edit_amount.field_locked_title', default: '')) { input_html = content_tag(:span, class: 'input-prepend intable',
title: t('orders.edit_amount.field_locked_title', default: '')) do
button_tag(nil, type: :button, class: 'btn unlocker') { 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
@ -109,18 +110,16 @@ 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}"
if group_orders.count == 0 return txt if group_orders.count == 0
return txt
else
desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ') desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ')
content_tag(:abbr, txt, title: desc).html_safe 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.kind_of?(Order) && order_or_supplier.stockit? if order_or_supplier.is_a?(Order) && order_or_supplier.stockit?
link_to(order_or_supplier.name, stock_articles_path).html_safe 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
@ -152,7 +151,8 @@ 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), class: "btn#{' btn-success' unless order.received?} #{options[:class]}" link_to t('orders.index.action_receive'), receive_order_path(order),
class: "btn#{' btn-success' unless order.received?} #{options[:class]}"
end end
end end
end end

View file

@ -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

View file

@ -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)
unless task.enough_users_assigned? return if 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)

View file

@ -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) +

View file

@ -0,0 +1,4 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "trix"
import "@rails/actiontext"
import "trix-editor-overrides"

View file

@ -0,0 +1,7 @@
// 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'))
}
})

View file

@ -14,23 +14,23 @@ class AppleBar
def group_bar_state def group_bar_state
if apples >= 100 if apples >= 100
'success' 'success'
else elsif FoodsoftConfig[:stop_ordering_under].present? &&
if FoodsoftConfig[:stop_ordering_under].present? and (apples >= FoodsoftConfig[:stop_ordering_under])
apples >= FoodsoftConfig[:stop_ordering_under]
'warning' 'warning'
else else
'danger' 'danger'
end end
end end
end
# Use apples as percentage, but show at least 10 percent # Use apples as percentage, but show at least 10 percent
def group_bar_width def group_bar_width
@ordergroup.apples < 2 ? 2 : @ordergroup.apples [@ordergroup.apples, 2].max
end end
def mean_order_amount_per_job def mean_order_amount_per_job
(1 / @global_avg).round rescue 0 (1 / @global_avg).round
rescue StandardError
0
end end
def apples def apples

View file

@ -1,4 +1,4 @@
class ArticlesCsv < RenderCSV class ArticlesCsv < RenderCsv
include ApplicationHelper include ApplicationHelper
def header def header
@ -16,14 +16,14 @@ class ArticlesCsv < RenderCSV
Article.human_attribute_name(:unit_quantity), Article.human_attribute_name(:unit_quantity),
'', '',
'', '',
Article.human_attribute_name(:article_category), Article.human_attribute_name(:article_category)
] ]
end end
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,
@ -36,7 +36,7 @@ class ArticlesCsv < RenderCSV
o.unit_quantity, o.unit_quantity,
'', '',
'', '',
o.article_category.try(:name), o.article_category.try(:name)
] ]
end end
end end

View file

@ -8,9 +8,7 @@ class BankAccountConnector
nil nil
end end
def text attr_reader :text
@text
end
end end
class TextField class TextField
@ -24,13 +22,7 @@ class BankAccountConnector
nil nil
end end
def name attr_reader :name, :value
@name
end
def value
@value
end
def label def label
@label || @name.to_s @label || @name.to_s
@ -49,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
@ -73,17 +65,7 @@ class BankAccountConnector
@bank_account.iban @bank_account.iban
end end
def auto_submit attr_reader :auto_submit, :controls, :count
@auto_submit
end
def controls
@controls
end
def count
@count
end
def text(data) def text(data)
@controls += [TextItem.new(data)] @controls += [TextItem.new(data)]
@ -142,11 +124,9 @@ class BankAccountConnector
@bank_account.save! @bank_account.save!
end end
def load(data) def load(data); end
end
def dump def dump; end
end
def t(key, args = {}) def t(key, args = {})
return t(".fields.#{key}") unless key.is_a? String return t(".fields.#{key}") unless key.is_a? String

View file

@ -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]
entityName = amount < 0 ? t[:creditorName] : t[:debtorName] entity_name = amount < 0 ? t[:creditorName] : t[:debtorName]
entityAccount = amount < 0 ? t[:creditorAccount] : t[:debtorAccount] entity_account = 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: entityAccount && entityAccount[:iban], iban: entity_account && entity_account[:iban],
reference: reference, reference: reference,
text: entityName, text: entity_name,
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

View file

@ -1,7 +1,7 @@
class BankTransactionReference class BankTransactionReference
# parses a string from a bank transaction field # parses a string from a bank transaction field
def self.parse(data) def self.parse(data)
m = /(^|[^\w\.])FS(?<group>\d+)(\.(?<user>\d+))?(?<parts>([A-Za-z]+\d+(\.\d+)?)+)([^\w\.]|$)/.match(data) m = /(^|[^\w.])FS(?<group>\d+)(\.(?<user>\d+))?(?<parts>([A-Za-z]+\d+(\.\d+)?)+)([^\w.]|$)/.match(data)
return unless m return unless m
parts = {} parts = {}
@ -13,7 +13,7 @@ class BankTransactionReference
ret = { group: m[:group].to_i, parts: parts } ret = { group: m[:group].to_i, parts: parts }
ret[:user] = m[:user].to_i if m[:user] ret[:user] = m[:user].to_i if m[:user]
return ret ret
end end
def self.js_code_for_user(user) def self.js_code_for_user(user)

View file

@ -1,6 +1,6 @@
require 'csv' require 'csv'
class BankTransactionsCsv < RenderCSV class BankTransactionsCsv < RenderCsv
include ApplicationHelper include ApplicationHelper
def header def header

View file

@ -0,0 +1,80 @@
# workaround for https://github.com/einzige/date_time_attribute/issues/14
require 'date_time_attribute'
module DateTimeAttributeValidate
extend ActiveSupport::Concern
include DateTimeAttribute
module ClassMethods
def date_time_attribute(*attributes)
super
attributes.each do |attribute|
validate -> { send("#{attribute}_datetime_value_valid") }
# allow resetting the field to nil
before_validation do
if instance_variable_get("@#{attribute}_is_set")
date = instance_variable_get("@#{attribute}_date_value")
time = instance_variable_get("@#{attribute}_time_value")
send("#{attribute}=", nil) if date.blank? && time.blank?
end
end
# remember old date and time values
define_method("#{attribute}_date_value=") do |val|
instance_variable_set("@#{attribute}_is_set", true)
instance_variable_set("@#{attribute}_date_value", val)
begin
send("#{attribute}_date=", val)
rescue StandardError
nil
end
end
define_method("#{attribute}_time_value=") do |val|
instance_variable_set("@#{attribute}_is_set", true)
instance_variable_set("@#{attribute}_time_value", val)
begin
send("#{attribute}_time=", val)
rescue StandardError
nil
end
end
# fallback to field when values are not set
define_method("#{attribute}_date_value") do
instance_variable_get("@#{attribute}_date_value") || send("#{attribute}_date").try do |e|
e.strftime('%Y-%m-%d')
end
end
define_method("#{attribute}_time_value") do
instance_variable_get("@#{attribute}_time_value") || send("#{attribute}_time").try do |e|
e.strftime('%H:%M')
end
end
private
# validate date and time
define_method("#{attribute}_datetime_value_valid") do
date = instance_variable_get("@#{attribute}_date_value")
unless date.blank? || begin
Date.parse(date)
rescue StandardError
nil
end
errors.add(attribute, 'is not a valid date') # @todo I18n
end
time = instance_variable_get("@#{attribute}_time_value")
unless time.blank? || begin
Time.parse(time)
rescue StandardError
nil
end
errors.add(attribute, 'is not a valid time') # @todo I18n
end
end
end
end
end
end

View file

@ -1,6 +1,6 @@
require 'csv' require 'csv'
class FinancialTransactionsCsv < RenderCSV class FinancialTransactionsCsv < RenderCsv
include ApplicationHelper include ApplicationHelper
def header def header

View file

@ -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
@ -54,8 +54,8 @@ module Foodsoft
# @param options [Hash<String, String>] Extra variables to expand # @param options [Hash<String, String>] Extra variables to expand
# @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[$1] || self.get($1) options[::Regexp.last_match(1)] || get(::Regexp.last_match(1))
end end
end end

View file

@ -44,6 +44,8 @@ class FoodsoftConfig
# @return [ActiveSupport::HashWithIndifferentAccess] Current configuration from configuration file. # @return [ActiveSupport::HashWithIndifferentAccess] Current configuration from configuration file.
mattr_accessor :config mattr_accessor :config
mattr_accessor :default_config
# Configuration file location. # Configuration file location.
# Taken from environment variable +FOODSOFT_APP_CONFIG+, # Taken from environment variable +FOODSOFT_APP_CONFIG+,
# or else +config/app_config.yml+. # or else +config/app_config.yml+.
@ -68,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,
@ -77,7 +79,7 @@ class FoodsoftConfig
end end
def init_mailing def init_mailing
[:protocol, :host, :port, :script_name].each do |k| %i[protocol host port script_name].each do |k|
ActionMailer::Base.default_url_options[k] = self[k] if self[k] ActionMailer::Base.default_url_options[k] = self[k] if self[k]
end end
end end
@ -115,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.#{self.scope}.#{key}"] value = RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"]
value = config[key] if value.nil? value = config[key] if value.nil?
value value
else else
@ -137,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.#{self.scope}.#{key}" RailsSettings::CachedSettings.destroy "foodcoop.#{scope}.#{key}"
rescue RailsSettings::Settings::SettingNotFound rescue RailsSettings::Settings::SettingNotFound
end end
else else
# or store # or store
RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"] = value RailsSettings::CachedSettings["foodcoop.#{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.#{self.scope}.").try(:keys) || [] keys = RailsSettings::CachedSettings.get_all("foodcoop.#{scope}.").try(:keys) || []
keys.map! { |k| k.gsub(/^foodcoop\.#{self.scope}\./, '') } keys.map! { |k| k.gsub(/^foodcoop\.#{scope}\./, '') }
keys += config.keys keys += config.keys
keys.map(&:to_s).uniq keys.map(&:to_s).uniq
end end
@ -179,17 +181,17 @@ 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 self.config[:protected].include? key if config[:protected].include? key
!self.config[:protected][key] !config[:protected][key]
else else
!self.config[:protected][:all] !config[:protected][:all]
end end
# @todo allow to check nested keys as well # @todo allow to check nested keys as well
end end
# @return [Hash] Full configuration. # @return [Hash] Full configuration.
def to_hash def to_hash
keys.to_h { |k| [k, self[k]] } keys.index_with { |k| self[k] }
end end
# for using active_model_serializer in the api/v1/configs controller # for using active_model_serializer in the api/v1/configs controller
@ -216,7 +218,6 @@ class FoodsoftConfig
# end # end
# #
# @return [Hash] Default configuration values # @return [Hash] Default configuration values
mattr_accessor :default_config
private private
@ -229,7 +230,7 @@ class FoodsoftConfig
end end
def setup_database def setup_database
database_config = ActiveRecord::Base.configurations[Rails.env] database_config = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash
database_config = database_config.merge(config[:database]) if config[:database].present? database_config = database_config.merge(config[:database]) if config[:database].present?
ActiveRecord::Base.establish_connection(database_config) ActiveRecord::Base.establish_connection(database_config)
end end
@ -286,7 +287,9 @@ 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 { |a| [a[0], normalize_value(a[1])] }] value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map do |a|
[a[0], normalize_value(a[1])]
end]
end end
case value case value
when 'true' then true when 'true' then true

View file

@ -0,0 +1,31 @@
module FoodsoftDateUtil
# find next occurence given a recurring ical string and time
def self.next_occurrence(start = Time.now, from = start, options = {})
occ = nil
if options && options[:recurr]
schedule = IceCube::Schedule.new(start)
schedule.add_recurrence_rule rule_from(options[:recurr])
# @todo handle ical parse errors
occ = begin
schedule.next_occurrence(from).to_time
rescue StandardError
nil
end
end
occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight) if options && options[:time] && occ
occ
end
# @param rule [String, Symbol, Hash, IceCube::Rule] What to return a rule from.
# @return [IceCube::Rule] Recurring rule
def self.rule_from(rule)
case rule
when String
IceCube::Rule.from_ical(rule)
when Hash
IceCube::Rule.from_hash(rule)
else
rule
end
end
end

25
app/lib/foodsoft_file.rb Normal file
View file

@ -0,0 +1,25 @@
# Foodsoft-file import
class FoodsoftFile
# parses a string from a foodsoft-file
# returns two arrays with articles and outlisted_articles
# the parsed article is a simple hash
def self.parse(file, options = {})
SpreadsheetFile.parse file, options do |row, row_index|
next if row[2].blank?
article = { order_number: row[1],
name: row[2],
note: row[3],
manufacturer: row[4],
origin: row[5],
unit: row[6],
price: row[7],
tax: row[8],
deposit: (row[9].nil? ? '0' : row[9]),
unit_quantity: row[10],
article_category: row[13] }
status = row[0] && row[0].strip.downcase == 'x' ? :outlisted : nil
yield status, article, row_index
end
end
end

Some files were not shown because too many files have changed in this diff Show more