diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f2a26b4c --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 34b5ce06..fbfe268f 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -15,7 +15,7 @@ jobs: MYSQL_DATABASE: test MYSQL_ROOT_PASSWORD: password options: >- - --health-cmd "mysqladmin ping" + --health-cmd "mariadb-admin ping" --health-interval 10s --health-timeout 5s --health-retries 5 @@ -35,7 +35,9 @@ jobs: - name: Checkout source code uses: actions/checkout@v2 - 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 uses: ruby/setup-ruby@v1 with: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b7e21eab..cbbec263 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,297 +1,310 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2022-10-08 12:00:00 UTC using RuboCop version 1.36.0. +# on 2023-05-26 14:15:44 UTC using RuboCop version 1.50.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 28 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. -# Include: **/*.gemfile, **/Gemfile, **/gems.rb -Bundler/OrderedGems: +# Offense count: 2 +# Configuration parameters: EnforcedStyle, AllowedGems, Include. +# SupportedStyles: Gemfile, gems.rb, gemspec +# Include: **/*.gemspec, **/Gemfile, **/gems.rb +Gemspec/DevelopmentDependencies: Exclude: - - "Gemfile" + - 'plugins/messages/foodsoft_messages.gemspec' + - 'plugins/wiki/foodsoft_wiki.gemspec' # Offense count: 9 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. -# Include: **/*.gemspec -Gemspec/RequireMFA: - Exclude: - - "plugins/current_orders/foodsoft_current_orders.gemspec" - - "plugins/discourse/foodsoft_discourse.gemspec" - - "plugins/documents/foodsoft_documents.gemspec" - - "plugins/links/foodsoft_links.gemspec" - - "plugins/messages/foodsoft_messages.gemspec" - - "plugins/polls/foodsoft_polls.gemspec" - - "plugins/printer/foodsoft_printer.gemspec" - - "plugins/uservoice/foodsoft_uservoice.gemspec" - - "plugins/wiki/foodsoft_wiki.gemspec" - -# Offense count: 9 -# Configuration parameters: Include. +# Configuration parameters: Severity, Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: Exclude: - - "plugins/current_orders/foodsoft_current_orders.gemspec" - - "plugins/discourse/foodsoft_discourse.gemspec" - - "plugins/documents/foodsoft_documents.gemspec" - - "plugins/links/foodsoft_links.gemspec" - - "plugins/messages/foodsoft_messages.gemspec" - - "plugins/polls/foodsoft_polls.gemspec" - - "plugins/printer/foodsoft_printer.gemspec" - - "plugins/uservoice/foodsoft_uservoice.gemspec" - - "plugins/wiki/foodsoft_wiki.gemspec" + - 'plugins/current_orders/foodsoft_current_orders.gemspec' + - 'plugins/discourse/foodsoft_discourse.gemspec' + - 'plugins/documents/foodsoft_documents.gemspec' + - 'plugins/links/foodsoft_links.gemspec' + - 'plugins/messages/foodsoft_messages.gemspec' + - 'plugins/polls/foodsoft_polls.gemspec' + - 'plugins/printer/foodsoft_printer.gemspec' + - 'plugins/uservoice/foodsoft_uservoice.gemspec' + - 'plugins/wiki/foodsoft_wiki.gemspec' -# Offense count: 8 -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods. -Lint/AmbiguousBlockAssociation: - Exclude: - - "lib/foodsoft/expansion_variables.rb" - - "spec/api/v1/user/financial_transactions_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/models/article_spec.rb" - -# Offense count: 4 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). -Lint/AmbiguousOperatorPrecedence: +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/ArgumentAlignment: Exclude: - - "app/models/concerns/price_calculation.rb" - - "db/seeds/seed_helper.rb" - - "plugins/messages/app/mail_receivers/messages_mail_receiver.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" + - 'app/controllers/articles_controller.rb' + - 'app/models/ordergroup.rb' + - 'config/initializers/currency_display.rb' + - 'db/migrate/001_create_users.rb' + - 'db/migrate/002_create_groups.rb' + - 'db/migrate/008_create_orders.rb' + - 'plugins/current_orders/app/helpers/current_orders_helper.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith. +# SupportedStylesAlignWith: either, start_of_block, start_of_line +Layout/BlockAlignment: + Exclude: + - 'app/lib/foodsoft_config.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/BlockEndNewline: + Exclude: + - 'app/lib/foodsoft_config.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/ClosingParenthesisIndentation: + Exclude: + - 'app/controllers/concerns/auth.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). -Lint/AmbiguousRegexpLiteral: +Layout/EmptyLineAfterGuardClause: Exclude: - - "app/models/article_category.rb" - - "lib/foodsoft/expansion_variables.rb" + - 'db/migrate/20130622095040_move_weekly_tasks.rb' + - 'db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb' -# Offense count: 40 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/EmptyLinesAroundMethodBody: + Exclude: + - 'db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Exclude: + - 'db/migrate/021_remove_table_article_prices.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/FirstArgumentIndentation: + Exclude: + - 'app/controllers/concerns/auth.rb' + +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/HashAlignment: + Exclude: + - 'app/controllers/admin/ordergroups_controller.rb' + - 'app/controllers/orders_controller.rb' + - 'app/documents/order_fax.rb' + - 'db/migrate/001_create_users.rb' + - 'db/migrate/002_create_groups.rb' + - 'db/migrate/008_create_orders.rb' + - 'db/migrate/20190101000000_create_active_storage_tables.active_storage.rb' + - 'spec/lib/bank_transaction_reference_spec.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: normal, indented_internal_methods +Layout/IndentationConsistency: + Exclude: + - 'db/migrate/20090120184410_road_to_version_three.rb' + - 'db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, AllowedPatterns. +Layout/IndentationWidth: + Exclude: + - 'app/lib/foodsoft_config.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Exclude: + - 'app/controllers/concerns/auth.rb' + +# Offense count: 15 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Exclude: + - 'app/controllers/admin/ordergroups_controller.rb' + - 'app/controllers/articles_controller.rb' + - 'app/controllers/orders_controller.rb' + - 'app/documents/order_fax.rb' + - 'app/models/ordergroup.rb' + - 'config/initializers/currency_display.rb' + - 'db/migrate/001_create_users.rb' + - 'db/migrate/002_create_groups.rb' + - 'db/migrate/008_create_orders.rb' + - 'db/migrate/20190101000000_create_active_storage_tables.active_storage.rb' + - 'db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb' + - 'plugins/current_orders/app/helpers/current_orders_helper.rb' + - 'spec/lib/bank_transaction_reference_spec.rb' + +# Offense count: 41 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Enabled: false -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -Lint/BigDecimalNew: - Exclude: - - "app/models/group_order.rb" - - "config/initializers/extensions.rb" - # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/BooleanSymbol: Exclude: - - "app/models/delivery.rb" + - 'app/models/delivery.rb' -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Lint/DeprecatedClassMethods: - Exclude: - - "config/initializers/secret_token.rb" - - "lib/tasks/foodsoft_setup.rake" - -# Offense count: 3 +# Offense count: 4 # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches. Lint/DuplicateBranch: Exclude: - - "app/controllers/concerns/auth_api.rb" - - "app/controllers/orders_controller.rb" + - 'app/controllers/concerns/auth_api.rb' + - 'app/controllers/orders_controller.rb' + - 'plugins/wiki/app/controllers/pages_controller.rb' # Offense count: 3 Lint/DuplicateMethods: Exclude: - - "app/models/invoice.rb" - - "plugins/messages/app/models/message.rb" + - 'app/models/invoice.rb' + - 'plugins/messages/app/models/message.rb' # Offense count: 2 # Configuration parameters: AllowComments, AllowEmptyLambdas. Lint/EmptyBlock: Exclude: - - "spec/factories/group_order_article.rb" - - "spec/factories/group_order_article_quantity.rb" + - 'spec/factories/group_order_article.rb' + - 'spec/factories/group_order_article_quantity.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/EnsureReturn: Exclude: - - "app/controllers/finance/bank_accounts_controller.rb" + - 'app/controllers/finance/bank_accounts_controller.rb' -# Offense count: 2 +# Offense count: 1 Lint/IneffectiveAccessModifier: Exclude: - - "lib/foodsoft_mail_receiver.rb" - - "lib/token_verifier.rb" + - 'app/lib/foodsoft_mail_receiver.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/Loop: Exclude: - - "app/models/concerns/mark_as_deleted_with_name.rb" + - 'app/models/concerns/mark_as_deleted_with_name.rb' # Offense count: 2 Lint/MixedRegexpCaptureTypes: Exclude: - - "lib/bank_transaction_reference.rb" - - "lib/foodsoft_mail_receiver.rb" + - 'app/lib/bank_transaction_reference.rb' + - 'app/lib/foodsoft_mail_receiver.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/NonDeterministicRequireOrder: Exclude: - - "spec/spec_helper.rb" + - 'spec/spec_helper.rb' # Offense count: 1 Lint/ReturnInVoidContext: Exclude: - - "lib/foodsoft_config.rb" - -# Offense count: 4 -# This cop supports safe autocorrection (--autocorrect). -Lint/SendWithMixinArgument: - Exclude: - - "plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb" - - "plugins/messages/lib/foodsoft_messages/user_link.rb" - - "plugins/uservoice/lib/foodsoft_uservoice.rb" - - "plugins/wiki/lib/foodsoft_wiki/mailer.rb" + - 'app/lib/foodsoft_config.rb' # Offense count: 1 # Configuration parameters: IgnoreImplicitReferences. Lint/ShadowedArgument: Exclude: - - "app/helpers/deliveries_helper.rb" + - 'app/helpers/deliveries_helper.rb' -# Offense count: 8 +# Offense count: 6 Lint/ShadowingOuterLocalVariable: Exclude: - - "app/documents/order_matrix.rb" - - "app/helpers/group_orders_helper.rb" - - "app/models/group_order.rb" - - "app/models/group_order_article.rb" - - "plugins/discourse/app/controllers/discourse_login_controller.rb" - - "plugins/polls/app/controllers/polls_controller.rb" - - "spec/integration/config_spec.rb" + - 'app/documents/order_matrix.rb' + - 'app/helpers/group_orders_helper.rb' + - 'app/models/group_order.rb' + - 'app/models/group_order_article.rb' + - 'plugins/discourse/app/controllers/discourse_login_controller.rb' + - 'plugins/polls/app/controllers/polls_controller.rb' -# Offense count: 3 +# Offense count: 2 # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Exclude: - - "config/initializers/rails6_backports.rb" - - "lib/foodsoft_config.rb" - - "lib/tasks/rspec.rake" + - 'app/lib/foodsoft_config.rb' + - 'lib/tasks/rspec.rake' # Offense count: 1 # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: Exclude: - - "app/models/order_article.rb" - -# Offense count: 16 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. -Lint/UnusedBlockArgument: - Exclude: - - "app/models/article.rb" - - "app/models/group_order.rb" - - "config/initializers/exception_notification.rb" - - "plugins/printer/lib/foodsoft_printer/engine.rb" - - "plugins/uservoice/lib/foodsoft_uservoice.rb" - - "plugins/wiki/lib/foodsoft_wiki/wiki_parser.rb" - - "spec/factories/supplier.rb" - - "spec/factories/user.rb" - - "spec/integration/config_spec.rb" - - "spec/models/article_spec.rb" - -# Offense count: 23 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. -Lint/UnusedMethodArgument: - Exclude: - - "app/controllers/api/v1/base_controller.rb" - - "app/controllers/concerns/foodcoop_scope.rb" - - "app/helpers/application_helper.rb" - - "app/models/article.rb" - - "app/models/article_category.rb" - - "app/models/financial_transaction.rb" - - "app/models/group_order.rb" - - "app/models/group_order_article.rb" - - "app/models/order.rb" - - "app/models/order_article.rb" - - "app/models/supplier.rb" - - "lib/foodsoft_mail_receiver.rb" - - "lib/order_txt.rb" - - "lib/render_pdf.rb" - - "plugins/wiki/lib/foodsoft_wiki/mailer.rb" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. -Lint/UselessAccessModifier: - Exclude: - - "lib/token_verifier.rb" - - "plugins/messages/app/models/messagegroup.rb" + - 'app/models/order_article.rb' # Offense count: 14 Lint/UselessAssignment: Exclude: - - "app/controllers/admin/ordergroups_controller.rb" - - "app/helpers/admin/configs_helper.rb" - - "app/inputs/date_picker_time_input.rb" - - "app/models/order_article.rb" - - "db/migrate/003_create_suppliers.rb" - - "db/migrate/004_create_article_meta.rb" - - "db/migrate/005_create_financial_transactions.rb" - - "db/migrate/008_create_orders.rb" - - "db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_articles.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_groups.rb" - - "spec/lib/foodsoft_config_spec.rb" + - 'app/controllers/admin/ordergroups_controller.rb' + - 'app/helpers/admin/configs_helper.rb' + - 'app/inputs/date_picker_time_input.rb' + - 'app/models/order_article.rb' + - 'db/migrate/003_create_suppliers.rb' + - 'db/migrate/004_create_article_meta.rb' + - 'db/migrate/005_create_financial_transactions.rb' + - 'db/migrate/008_create_orders.rb' + - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_articles.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_groups.rb' + - 'spec/lib/foodsoft_config_spec.rb' # Offense count: 1 # Configuration parameters: CheckForMethodsWithNoSideEffects. Lint/Void: Exclude: - - "lib/foodsoft_config.rb" + - 'app/lib/foodsoft_config.rb' -# Offense count: 160 -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes. +# Offense count: 161 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: Max: 143 -# Offense count: 17 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods, inherit_mode. +# Offense count: 13 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: - Max: 210 + Max: 66 -# Offense count: 6 +# Offense count: 2 # Configuration parameters: CountBlocks. Metrics/BlockNesting: - Max: 5 + Max: 4 -# Offense count: 18 +# Offense count: 19 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 288 + Max: 294 -# Offense count: 52 -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods. +# Offense count: 51 +# Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: - Max: 22 + Max: 20 -# Offense count: 163 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods. +# Offense count: 164 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Max: 112 # Offense count: 4 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: - Max: 190 + Max: 191 # Offense count: 1 # Configuration parameters: CountKeywordArgs, MaxOptionalParameters. @@ -299,47 +312,45 @@ Metrics/ParameterLists: Max: 6 # Offense count: 36 -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods. +# Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: - Max: 23 + Max: 21 # Offense count: 6 Naming/AccessorMethodName: Exclude: - - "app/controllers/admin/configs_controller.rb" - - "lib/bank_account_connector.rb" - - "lib/foodsoft_config.rb" - - "spec/integration/config_spec.rb" + - 'app/controllers/admin/configs_controller.rb' + - 'app/lib/bank_account_connector.rb' + - 'app/lib/foodsoft_config.rb' + - 'spec/integration/config_spec.rb' # Offense count: 1 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. Naming/BlockParameterName: Exclude: - - "db/migrate/008_create_orders.rb" + - 'db/migrate/008_create_orders.rb' # Offense count: 1 # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Exclude: - - "plugins/messages/app/models/message.rb" + - 'plugins/messages/app/models/message.rb' -# Offense count: 19 +# Offense count: 16 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. -# AllowedNames: as, at, by, db, id, in, io, ip, of, on, os, pp, to +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - - "app/controllers/api/v1/base_controller.rb" - - "app/controllers/api/v1/user/group_order_articles_controller.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/orders_helper.rb" - - "app/models/user.rb" - - "lib/foodsoft_date_util.rb" - - "lib/render_pdf.rb" - - "spec/integration/config_spec.rb" - - "spec/integration/receive_spec.rb" - - "spec/models/order_article_spec.rb" - - "spec/support/shared_database.rb" + - 'app/controllers/api/v1/base_controller.rb' + - 'app/controllers/api/v1/user/group_order_articles_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/orders_helper.rb' + - 'app/models/user.rb' + - 'spec/integration/config_spec.rb' + - 'spec/integration/receive_spec.rb' + - 'spec/models/order_article_spec.rb' + - 'spec/support/shared_database.rb' # Offense count: 11 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros. @@ -349,208 +360,156 @@ Naming/MethodParameterName: # MethodDefinitionMacros: define_method, define_singleton_method Naming/PredicateName: Exclude: - - "app/models/financial_transaction_class.rb" - - "app/models/financial_transaction_type.rb" - - "app/models/order.rb" - - "app/models/periodic_task_group.rb" - - "app/models/supplier.rb" - - "app/models/task.rb" - - "app/models/user.rb" - - "app/serializers/order_serializer.rb" - - "plugins/messages/app/models/message.rb" + - 'app/models/financial_transaction_class.rb' + - 'app/models/financial_transaction_type.rb' + - 'app/models/order.rb' + - 'app/models/periodic_task_group.rb' + - 'app/models/supplier.rb' + - 'app/models/task.rb' + - 'app/models/user.rb' + - 'app/serializers/order_serializer.rb' + - 'plugins/messages/app/models/message.rb' -# Offense count: 45 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: PreferredName. -Naming/RescuedExceptionsVariableName: - Enabled: false - -# Offense count: 22 +# Offense count: 17 # Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, camelCase Naming/VariableName: Exclude: - - "app/controllers/concerns/auth.rb" - - "app/helpers/application_helper.rb" - - "db/migrate/008_create_orders.rb" - - "lib/bank_account_information_importer.rb" - -# Offense count: 23 -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. -# SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 -Naming/VariableNumber: - Exclude: - - "app/documents/order_matrix.rb" - - "spec/api/v1/swagger_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/api/v1/user/ordergroup_spec.rb" + - 'app/controllers/concerns/auth.rb' + - 'app/helpers/application_helper.rb' + - 'db/migrate/008_create_orders.rb' # Offense count: 4 -RSpec/AnyInstance: +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. +# SupportedStyles: snake_case, normalcase, non_integer +# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 +Naming/VariableNumber: Exclude: - - "spec/api/v1/swagger_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" + - 'app/documents/order_matrix.rb' # Offense count: 2 RSpec/BeforeAfterAll: Exclude: - - "spec/lib/foodsoft_mail_receiver_spec.rb" + - 'spec/lib/foodsoft_mail_receiver_spec.rb' -# Offense count: 9 +# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnabledMethods. RSpec/Capybara/FeatureMethods: Exclude: - - "spec/integration/articles_spec.rb" - - "spec/integration/balancing_spec.rb" - - "spec/integration/config_spec.rb" - - "spec/integration/home_spec.rb" - - "spec/integration/login_spec.rb" - - "spec/integration/order_spec.rb" - - "spec/integration/product_distribution_example_spec.rb" - - "spec/integration/receive_spec.rb" - - "spec/integration/session_spec.rb" - - "spec/integration/supplier_spec.rb" + - 'spec/integration/articles_spec.rb' + - 'spec/integration/balancing_spec.rb' + - 'spec/integration/config_spec.rb' + - 'spec/integration/home_spec.rb' + - 'spec/integration/login_spec.rb' + - 'spec/integration/order_spec.rb' + - 'spec/integration/product_distribution_example_spec.rb' + - 'spec/integration/receive_spec.rb' + - 'spec/integration/session_spec.rb' + - 'spec/integration/supplier_spec.rb' # Offense count: 4 RSpec/Capybara/SpecificMatcher: Exclude: - - "spec/integration/login_spec.rb" - - "spec/integration/session_spec.rb" + - 'spec/integration/login_spec.rb' + - 'spec/integration/session_spec.rb' -# Offense count: 27 +# Offense count: 12 # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without RSpec/ContextWording: Exclude: - - "spec/api/v1/swagger_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/models/order_article_spec.rb" - - "spec/models/supplier_spec.rb" + - 'spec/models/order_article_spec.rb' + - 'spec/models/supplier_spec.rb' -# Offense count: 1 +# Offense count: 7 # Configuration parameters: IgnoredMetadata. RSpec/DescribeClass: Exclude: - - "spec/api/v1/swagger_spec.rb" + - 'spec/integration/balancing_spec.rb' + - 'spec/integration/config_spec.rb' + - 'spec/integration/home_spec.rb' + - 'spec/integration/product_distribution_example_spec.rb' + - 'spec/integration/receive_spec.rb' + - 'spec/integration/session_spec.rb' + - 'spec/integration/supplier_spec.rb' -# Offense count: 126 +# Offense count: 128 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle. # SupportedStyles: described_class, explicit RSpec/DescribedClass: Exclude: - - "spec/lib/bank_transaction_reference_spec.rb" - - "spec/lib/foodsoft_config_spec.rb" - - "spec/lib/foodsoft_mail_receiver_spec.rb" - - "spec/lib/token_verifier_spec.rb" - - "spec/models/group_order_article_spec.rb" - - "spec/models/order_article_spec.rb" - - "spec/models/order_spec.rb" - - "spec/models/ordergroup_spec.rb" - - "spec/models/user_spec.rb" + - 'spec/integration/order_spec.rb' + - 'spec/lib/bank_transaction_reference_spec.rb' + - 'spec/lib/foodsoft_config_spec.rb' + - 'spec/lib/foodsoft_mail_receiver_spec.rb' + - 'spec/lib/token_verifier_spec.rb' + - 'spec/models/group_order_article_spec.rb' + - 'spec/models/order_article_spec.rb' + - 'spec/models/order_spec.rb' + - 'spec/models/ordergroup_spec.rb' + - 'spec/models/user_spec.rb' -# Offense count: 65 +# Offense count: 14 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/EmptyExampleGroup: + Exclude: + - 'spec/requests/api/**/*_spec.rb' + +# Offense count: 69 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 81 -# Offense count: 7 +# Offense count: 3 # Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly. # Include: **/*_spec*rb*, **/spec/**/* RSpec/FilePath: Exclude: - - "spec/api/v1/order_articles_spec.rb" - - "spec/api/v1/user/financial_transactions_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/api/v1/user/ordergroup_spec.rb" - - "spec/integration/articles_spec.rb" - - "spec/integration/login_spec.rb" - - "spec/lib/bank_account_information_importer_spec.rb" - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: implicit, each, example -RSpec/HookArgument: - Exclude: - - "spec/spec_helper.rb" - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -RSpec/HooksBeforeExamples: - Exclude: - - "spec/integration/balancing_spec.rb" - - "spec/lib/foodsoft_mail_receiver_spec.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: single_line_only, single_statement_only, disallow -RSpec/ImplicitSubject: - Exclude: - - "spec/api/v1/swagger_spec.rb" + - 'spec/integration/articles_spec.rb' + - 'spec/integration/login_spec.rb' + - 'spec/lib/bank_account_information_importer_spec.rb' # Offense count: 6 # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Exclude: - - "spec/lib/foodsoft_mail_receiver_spec.rb" + - 'spec/lib/foodsoft_mail_receiver_spec.rb' -# Offense count: 2 +# Offense count: 3 RSpec/IteratedExpectation: Exclude: - - "spec/models/order_spec.rb" - - "spec/models/supplier_spec.rb" + - 'spec/models/order_spec.rb' + - 'spec/models/supplier_spec.rb' -# Offense count: 13 +# Offense count: 3 RSpec/LetSetup: Exclude: - - "spec/api/v1/swagger_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/api/v1/user/ordergroup_spec.rb" - - "spec/models/bank_transaction_spec.rb" - - "spec/models/group_order_article_spec.rb" - - "spec/models/supplier_spec.rb" + - 'spec/models/bank_transaction_spec.rb' + - 'spec/models/group_order_article_spec.rb' + - 'spec/models/supplier_spec.rb' # Offense count: 3 RSpec/MissingExampleGroupArgument: Exclude: - - "spec/models/group_order_article_spec.rb" - - "spec/models/group_order_spec.rb" - - "spec/models/user_spec.rb" + - 'spec/models/group_order_article_spec.rb' + - 'spec/models/group_order_spec.rb' + - 'spec/models/user_spec.rb' -# Offense count: 88 +# Offense count: 103 RSpec/MultipleExpectations: Max: 22 -# Offense count: 83 +# Offense count: 36 # Configuration parameters: AllowSubject. RSpec/MultipleMemoizedHelpers: - Max: 17 + Max: 15 -# Offense count: 29 +# Offense count: 8 # Configuration parameters: AllowedGroups. RSpec/NestedGroups: - Max: 6 - -# Offense count: 31 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: not_to, to_not -RSpec/NotToNot: - Exclude: - - "spec/api/v1/user/financial_transactions_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/integration/balancing_spec.rb" - - "spec/integration/login_spec.rb" - - "spec/integration/receive_spec.rb" - - "spec/integration/session_spec.rb" - - "spec/lib/token_verifier_spec.rb" - - "spec/models/article_spec.rb" - - "spec/models/order_spec.rb" - - "spec/models/supplier_spec.rb" + Max: 4 # Offense count: 8 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -558,84 +517,61 @@ RSpec/NotToNot: # SupportedStyles: inflected, explicit RSpec/PredicateMatcher: Exclude: - - "spec/models/article_spec.rb" - - "spec/models/user_spec.rb" + - 'spec/models/article_spec.rb' + - 'spec/models/user_spec.rb' # Offense count: 6 RSpec/RepeatedDescription: Exclude: - - "spec/lib/bank_account_information_importer_spec.rb" - - "spec/lib/bank_transaction_reference_spec.rb" - - "spec/lib/foodsoft_mail_receiver_spec.rb" + - 'spec/lib/bank_account_information_importer_spec.rb' + - 'spec/lib/bank_transaction_reference_spec.rb' + - 'spec/lib/foodsoft_mail_receiver_spec.rb' # Offense count: 4 RSpec/RepeatedExample: Exclude: - - "spec/lib/bank_transaction_reference_spec.rb" - - "spec/lib/foodsoft_mail_receiver_spec.rb" + - 'spec/lib/bank_transaction_reference_spec.rb' + - 'spec/lib/foodsoft_mail_receiver_spec.rb' -# Offense count: 7 +# Offense count: 5 RSpec/ScatteredSetup: Exclude: - - "spec/api/v1/user/ordergroup_spec.rb" - - "spec/integration/balancing_spec.rb" - - "spec/integration/login_spec.rb" + - 'spec/integration/balancing_spec.rb' + - 'spec/integration/login_spec.rb' # Offense count: 1 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: - - "spec/support/api_oauth.rb" - -# Offense count: 45 -# This cop supports unsafe autocorrection (--autocorrect-all). -Rails/ActiveRecordAliases: - Enabled: false - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/ActiveRecordCallbacksOrder: - Exclude: - - "app/models/financial_transaction_type.rb" - - "app/models/order.rb" - - "app/models/stock_change.rb" + - 'spec/support/api_oauth.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationMailer: Exclude: - - "app/mailers/mailer.rb" + - 'app/mailers/mailer.rb' # Offense count: 20 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationRecord: Exclude: - - "app/models/supplier_category.rb" - - "db/migrate/20130718183101_migrate_user_settings.rb" - - "db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb" - - "db/migrate/20181201000301_change_ordergroup_default_in_financial_transaction.rb" - - "db/migrate/20181201000302_change_stock_supplier_to_null_in_order.rb" - - "db/migrate/20181201000305_ensure_article_for_article_price.rb" - - "db/migrate/20181201000400_create_supplier_categories.rb" - - "db/migrate/20181204000000_clear_invalid_invoices_from_orders.rb" - - "db/migrate/20181204070000_create_stock_events.rb" - - "plugins/messages/app/models/message_recipient.rb" - - "plugins/polls/app/models/poll.rb" - - "plugins/polls/app/models/poll_choice.rb" - - "plugins/polls/app/models/poll_vote.rb" - - "plugins/printer/app/models/printer_job.rb" - - "plugins/printer/app/models/printer_job_update.rb" + - 'app/models/supplier_category.rb' + - 'db/migrate/20130718183101_migrate_user_settings.rb' + - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb' + - 'db/migrate/20181201000301_change_ordergroup_default_in_financial_transaction.rb' + - 'db/migrate/20181201000302_change_stock_supplier_to_null_in_order.rb' + - 'db/migrate/20181201000305_ensure_article_for_article_price.rb' + - 'db/migrate/20181201000400_create_supplier_categories.rb' + - 'db/migrate/20181204000000_clear_invalid_invoices_from_orders.rb' + - 'db/migrate/20181204070000_create_stock_events.rb' + - 'plugins/messages/app/models/message_recipient.rb' + - 'plugins/polls/app/models/poll.rb' + - 'plugins/polls/app/models/poll_choice.rb' + - 'plugins/polls/app/models/poll_vote.rb' + - 'plugins/printer/app/models/printer_job.rb' + - 'plugins/printer/app/models/printer_job_update.rb' -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. -Rails/Blank: - Exclude: - - "app/controllers/api/v1/base_controller.rb" - -# Offense count: 34 +# Offense count: 35 # Configuration parameters: Include. # Include: db/migrate/*.rb Rails/CreateTableWithTimestamps: @@ -646,146 +582,94 @@ Rails/CreateTableWithTimestamps: # SupportedStyles: strict, flexible Rails/Date: Exclude: - - "app/controllers/deliveries_controller.rb" - - "app/documents/order_fax.rb" - - "app/models/periodic_task_group.rb" - - "spec/integration/order_spec.rb" - - "spec/models/order_spec.rb" + - 'app/controllers/deliveries_controller.rb' + - 'app/documents/order_fax.rb' + - 'app/models/periodic_task_group.rb' + - 'spec/integration/order_spec.rb' + - 'spec/models/order_spec.rb' -# Offense count: 67 +# Offense count: 68 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Whitelist, AllowedMethods, AllowedReceivers. -# Whitelist: find_by_sql -# AllowedMethods: find_by_sql -# AllowedReceivers: Gem::Specification +# Whitelist: find_by_sql, find_by_token_for +# AllowedMethods: find_by_sql, find_by_token_for +# AllowedReceivers: Gem::Specification, page Rails/DynamicFindBy: Enabled: false -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/EnumHash: - Exclude: - - "app/models/order.rb" - -# Offense count: 8 +# Offense count: 4 # Configuration parameters: EnforcedStyle. # SupportedStyles: slashes, arguments Rails/FilePath: Exclude: - - "config/application.rb" - - "config/initializers/secret_token.rb" - - "lib/order_txt.rb" - - "lib/render_csv.rb" - - "lib/render_pdf.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_groups.rb" - - "spec/api/v1/swagger_spec.rb" - -# Offense count: 7 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include, AllowedMethods, AllowedPatterns, IgnoredMethods. -# Include: app/models/**/*.rb -# AllowedMethods: order, limit, select, lock -# IgnoredMethods: order, limit, select, lock -Rails/FindEach: - Exclude: - - "app/models/bank_account.rb" - - "app/models/order.rb" - - "app/models/ordergroup.rb" - - "app/models/periodic_task_group.rb" - - "app/models/task.rb" + - 'config/application.rb' + - 'config/initializers/secret_token.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_groups.rb' # Offense count: 26 # Configuration parameters: Include. # Include: app/models/**/*.rb Rails/HasManyOrHasOneDependent: Exclude: - - "app/models/article.rb" - - "app/models/article_category.rb" - - "app/models/article_price.rb" - - "app/models/financial_link.rb" - - "app/models/financial_transaction.rb" - - "app/models/group_order.rb" - - "app/models/order.rb" - - "app/models/ordergroup.rb" - - "app/models/shared_supplier.rb" - - "app/models/stock_article.rb" - - "app/models/supplier.rb" - - "app/models/supplier_category.rb" - - "app/models/user.rb" - - "app/models/workgroup.rb" + - 'app/models/article.rb' + - 'app/models/article_category.rb' + - 'app/models/article_price.rb' + - 'app/models/financial_link.rb' + - 'app/models/financial_transaction.rb' + - 'app/models/group_order.rb' + - 'app/models/order.rb' + - 'app/models/ordergroup.rb' + - 'app/models/shared_supplier.rb' + - 'app/models/stock_article.rb' + - 'app/models/supplier.rb' + - 'app/models/supplier_category.rb' + - 'app/models/user.rb' + - 'app/models/workgroup.rb' # Offense count: 14 # Configuration parameters: Include. # Include: app/helpers/**/*.rb Rails/HelperInstanceVariable: Exclude: - - "app/helpers/admin/configs_helper.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/orders_helper.rb" + - 'app/helpers/admin/configs_helper.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/orders_helper.rb' -# Offense count: 14 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: numeric, symbolic -Rails/HttpStatus: - Exclude: - - "app/controllers/admin/bank_accounts_controller.rb" - - "app/controllers/admin/financial_transaction_classes_controller.rb" - - "app/controllers/admin/financial_transaction_types_controller.rb" - - "app/controllers/api/v1/base_controller.rb" - - "app/controllers/styles_controller.rb" - - "plugins/links/app/controllers/links_controller.rb" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Rails/IndexBy: - Exclude: - - "app/models/order.rb" - - "spec/api/v1/user/ordergroup_spec.rb" - -# Offense count: 23 +# Offense count: 22 # Configuration parameters: IgnoreScopes, Include. # Include: app/models/**/*.rb Rails/InverseOf: Exclude: - - "app/models/article.rb" - - "app/models/bank_transaction.rb" - - "app/models/financial_transaction.rb" - - "app/models/group_order.rb" - - "app/models/invoice.rb" - - "app/models/mail_delivery_status.rb" - - "app/models/order.rb" - - "app/models/shared_article.rb" - - "app/models/shared_supplier.rb" - - "app/models/stock_change.rb" - - "app/models/supplier.rb" - - "app/models/task.rb" - - "app/models/user.rb" - - "app/models/workgroup.rb" + - 'app/models/article.rb' + - 'app/models/bank_transaction.rb' + - 'app/models/financial_transaction.rb' + - 'app/models/group_order.rb' + - 'app/models/invoice.rb' + - 'app/models/mail_delivery_status.rb' + - 'app/models/order.rb' + - 'app/models/shared_article.rb' + - 'app/models/shared_supplier.rb' + - 'app/models/stock_change.rb' + - 'app/models/supplier.rb' + - 'app/models/task.rb' + - 'app/models/user.rb' + - 'app/models/workgroup.rb' # Offense count: 2 # Configuration parameters: Include. # Include: app/controllers/**/*.rb, app/mailers/**/*.rb Rails/LexicallyScopedActionFilter: Exclude: - - "app/controllers/group_orders_controller.rb" - - "app/controllers/suppliers_controller.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Rails/LinkToBlank: - Exclude: - - "app/helpers/application_helper.rb" + - 'app/controllers/group_orders_controller.rb' + - 'app/controllers/suppliers_controller.rb' # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/NegateInclude: Exclude: - - "app/helpers/application_helper.rb" - - "app/models/supplier.rb" - - "lib/tasks/foodsoft_setup.rake" + - 'app/helpers/application_helper.rb' + - 'app/models/supplier.rb' + - 'lib/tasks/foodsoft_setup.rake' # Offense count: 34 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -793,55 +677,36 @@ Rails/NegateInclude: # Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb Rails/Output: Exclude: - - "config/initializers/resque.rb" - - "config/initializers/secret_token.rb" - - "db/migrate/001_create_users.rb" - - "db/migrate/002_create_groups.rb" - - "db/migrate/003_create_suppliers.rb" - - "db/migrate/004_create_article_meta.rb" - - "db/migrate/005_create_financial_transactions.rb" - - "db/migrate/006_create_articles.rb" - - "db/migrate/007_create_article_prices.rb" - - "db/migrate/008_create_orders.rb" - - "db/migrate/021_remove_table_article_prices.rb" - - "db/migrate/20090120184410_road_to_version_three.rb" - - "db/migrate/20130622095040_move_weekly_tasks.rb" + - 'config/initializers/resque.rb' + - 'config/initializers/secret_token.rb' + - 'db/migrate/001_create_users.rb' + - 'db/migrate/002_create_groups.rb' + - 'db/migrate/003_create_suppliers.rb' + - 'db/migrate/004_create_article_meta.rb' + - 'db/migrate/005_create_financial_transactions.rb' + - 'db/migrate/006_create_articles.rb' + - 'db/migrate/007_create_article_prices.rb' + - 'db/migrate/008_create_orders.rb' + - 'db/migrate/021_remove_table_article_prices.rb' + - 'db/migrate/20090120184410_road_to_version_three.rb' + - 'db/migrate/20130622095040_move_weekly_tasks.rb' # Offense count: 28 Rails/OutputSafety: Exclude: - - "app/helpers/admin/configs_helper.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/deliveries_helper.rb" - - "app/helpers/orders_helper.rb" - - "app/helpers/tasks_helper.rb" - - "plugins/messages/app/helpers/messages_helper.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Rails/Pluck: - Exclude: - - "lib/ordergroups_csv.rb" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Rails/PluralizationGrammar: - Exclude: - - "app/controllers/application_controller.rb" - - "lib/tasks/foodsoft.rake" + - 'app/helpers/admin/configs_helper.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/deliveries_helper.rb' + - 'app/helpers/orders_helper.rb' + - 'app/helpers/tasks_helper.rb' + - 'plugins/messages/app/helpers/messages_helper.rb' + - 'plugins/wiki/app/helpers/pages_helper.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Rails/Presence: Exclude: - - "db/migrate/021_remove_table_article_prices.rb" - -# Offense count: 36 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank. -Rails/Present: - Enabled: false + - 'db/migrate/021_remove_table_article_prices.rb' # Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -849,30 +714,34 @@ Rails/Present: # Include: **/Rakefile, **/*.rake Rails/RakeEnvironment: Exclude: - - "lib/tasks/foodsoft_setup.rake" - - "lib/tasks/resque.rake" + - 'lib/tasks/foodsoft_setup.rake' + - 'lib/tasks/resque.rake' -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -Rails/RedundantForeignKey: - Exclude: - - "app/models/financial_transaction.rb" - - "plugins/messages/app/models/message.rb" - -# Offense count: 1 +# Offense count: 14 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/RedundantPresenceValidationOnBelongsTo: Exclude: - - "app/models/financial_transaction_type.rb" + - 'app/models/article.rb' + - 'app/models/bank_transaction.rb' + - 'app/models/delivery.rb' + - 'app/models/financial_transaction.rb' + - 'app/models/financial_transaction_type.rb' + - 'app/models/group_order.rb' + - 'app/models/group_order_article.rb' + - 'app/models/group_order_article_quantity.rb' + - 'app/models/invite.rb' + - 'app/models/invoice.rb' + - 'app/models/order_article.rb' + - 'app/models/order_comment.rb' + - 'app/models/stock_change.rb' # Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: ConvertTry. -Rails/SafeNavigation: +# This cop supports unsafe autocorrection (--autocorrect-all). +Rails/RootPathnameMethods: Exclude: - - "app/models/group_order_article.rb" + - 'lib/tasks/foodsoft_setup.rake' -# Offense count: 63 +# Offense count: 64 # Configuration parameters: ForbiddenMethods, AllowedMethods. # ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all Rails/SkipsModelValidations: @@ -882,11 +751,11 @@ Rails/SkipsModelValidations: # This cop supports unsafe autocorrection (--autocorrect-all). Rails/SquishedSQLHeredocs: Exclude: - - "app/controllers/finance/financial_links_controller.rb" - - "app/models/financial_link.rb" - - "db/migrate/20181201000305_ensure_article_for_article_price.rb" + - 'app/controllers/finance/financial_links_controller.rb' + - 'app/models/financial_link.rb' + - 'db/migrate/20181201000305_ensure_article_for_article_price.rb' -# Offense count: 41 +# Offense count: 42 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: strict, flexible @@ -896,37 +765,34 @@ Rails/TimeZone: # Offense count: 1 Rails/TransactionExitStatement: Exclude: - - "app/models/bank_transaction.rb" + - 'app/models/bank_transaction.rb' -# Offense count: 3 +# Offense count: 8 # Configuration parameters: Include. # Include: app/models/**/*.rb Rails/UniqueValidationWithoutIndex: Exclude: - - "app/models/bank_account.rb" - - "app/models/supplier_category.rb" + - 'app/models/bank_account.rb' + - 'app/models/financial_transaction_class.rb' + - 'app/models/financial_transaction_type.rb' + - 'app/models/supplier.rb' + - 'app/models/supplier_category.rb' + - 'app/models/user.rb' # Offense count: 2 # Configuration parameters: Environments. # Environments: development, test, production Rails/UnknownEnv: Exclude: - - "config/initializers/gaffe.rb" - - "config/initializers/secret_token.rb" - -# Offense count: 69 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/Validation: - Enabled: false + - 'config/initializers/gaffe.rb' + - 'config/initializers/secret_token.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/WhereEquals: Exclude: - - "app/controllers/finance/invoices_controller.rb" - - "app/models/financial_transaction.rb" + - 'app/controllers/finance/invoices_controller.rb' + - 'app/models/financial_transaction.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -934,84 +800,54 @@ Rails/WhereEquals: # SupportedStyles: exists, where Rails/WhereExists: Exclude: - - "app/models/concerns/mark_as_deleted_with_name.rb" + - 'app/models/concerns/mark_as_deleted_with_name.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Rails/WhereNot: Exclude: - - "db/migrate/20140921104907_remove_stale_memberships.rb" - - "db/migrate/20210205090257_introduce_received_state_in_orders.rb" + - 'db/migrate/20140921104907_remove_stale_memberships.rb' + - 'db/migrate/20210205090257_introduce_received_state_in_orders.rb' -# Offense count: 5 +# Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). Security/YAMLLoad: Exclude: - - "app/controllers/finance/bank_accounts_controller.rb" - - "db/migrate/20130718183101_migrate_user_settings.rb" - - "db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb" - - "lib/foodsoft_config.rb" - - "spec/api/v1/swagger_spec.rb" + - 'app/controllers/finance/bank_accounts_controller.rb' + - 'app/lib/foodsoft_config.rb' + - 'db/migrate/20130718183101_migrate_user_settings.rb' + - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb' # Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: prefer_alias, prefer_alias_method -Style/Alias: - Exclude: - - "config/initializers/session_store.rb" - - "plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb" - - "plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb" - -# Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals Style/AndOr: Exclude: - - "config/initializers/extensions.rb" - - "lib/apple_bar.rb" - - "plugins/documents/app/controllers/documents_controller.rb" - - "spec/support/coverage.rb" + - 'config/initializers/extensions.rb' + - 'plugins/documents/app/controllers/documents_controller.rb' + - 'spec/support/coverage.rb' -# Offense count: 19 +# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch # AllowedMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - - "app/controllers/api/v1/user/ordergroup_controller.rb" - - "app/helpers/group_orders_helper.rb" - - "app/helpers/orders_helper.rb" - - "app/models/order.rb" - - "db/migrate/008_create_orders.rb" - - "lib/tasks/resque.rake" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/factories/user.rb" - - "spec/lib/foodsoft_mail_receiver_spec.rb" - - "spec/support/coverage.rb" + - 'app/lib/foodsoft_config.rb' + - 'db/migrate/008_create_orders.rb' + - 'spec/factories/user.rb' + - 'spec/support/coverage.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowOnConstant, AllowOnSelfClass. Style/CaseEquality: Exclude: - - "lib/tasks/foodsoft_setup.rake" - -# Offense count: 7 -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/CaseLikeIf: - Exclude: - - "app/helpers/admin/configs_helper.rb" - - "app/helpers/group_orders_helper.rb" - - "app/models/order.rb" - - "lib/foodsoft_date_util.rb" - - "lib/render_pdf.rb" - - "lib/tasks/foodsoft_setup.rake" - - "plugins/uservoice/lib/foodsoft_uservoice.rb" + - 'lib/tasks/foodsoft_setup.rake' # Offense count: 55 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1021,95 +857,23 @@ Style/ClassAndModuleChildren: Enabled: false # Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: is_a?, kind_of? -Style/ClassCheck: - Exclude: - - "app/helpers/orders_helper.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods. -# AllowedMethods: ==, equal?, eql? -Style/ClassEqualityComparison: - Exclude: - - "spec/factories/supplier.rb" - -# Offense count: 3 Style/ClassVars: Exclude: - - "lib/bank_account_connector.rb" - - "lib/foodsoft/expansion_variables.rb" - - "lib/foodsoft_mail_receiver.rb" - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -Style/ColonMethodCall: - Exclude: - - "app/models/supplier.rb" - - "plugins/discourse/app/controllers/discourse_controller.rb" - - "plugins/messages/app/mail_receivers/messages_mail_receiver.rb" - -# Offense count: 7 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowInnerBackticks. -# SupportedStyles: backticks, percent_x, mixed -Style/CommandLiteral: - Exclude: - - "lib/tasks/foodsoft_setup.rake" - -# Offense count: 10 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Keywords, RequireColon. -# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE -Style/CommentAnnotation: - Exclude: - - "app/controllers/admin/configs_controller.rb" - - "app/inputs/delta_input.rb" - - "app/models/order_article.rb" - - "app/models/shared_supplier.rb" - - "config/application.rb" - - "spec/models/article_spec.rb" - - "spec/models/order_spec.rb" - - "spec/support/shared_database.rb" + - 'app/lib/foodsoft_mail_receiver.rb' # Offense count: 12 # This cop supports unsafe autocorrection (--autocorrect-all). Style/CommentedKeyword: Exclude: - - "app/controllers/deliveries_controller.rb" - - "app/controllers/finance/balancing_controller.rb" - - "app/controllers/orders_controller.rb" - - "app/controllers/stock_takings_controller.rb" - - "app/controllers/stockit_controller.rb" - - "config/routes.rb" - - "db/migrate/20090120184410_road_to_version_three.rb" + - 'app/controllers/deliveries_controller.rb' + - 'app/controllers/finance/balancing_controller.rb' + - 'app/controllers/orders_controller.rb' + - 'app/controllers/stock_takings_controller.rb' + - 'app/controllers/stockit_controller.rb' + - 'config/routes.rb' + - 'db/migrate/20090120184410_road_to_version_three.rb' -# Offense count: 13 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. -# SupportedStyles: assign_to_condition, assign_inside_condition -Style/ConditionalAssignment: - Exclude: - - "app/controllers/application_controller.rb" - - "app/controllers/articles_controller.rb" - - "app/controllers/concerns/locale.rb" - - "app/controllers/finance/bank_transactions_controller.rb" - - "app/controllers/finance/financial_transactions_controller.rb" - - "app/controllers/home_controller.rb" - - "app/controllers/orders_controller.rb" - - "plugins/documents/app/controllers/documents_controller.rb" - - "plugins/messages/app/mail_receivers/messages_mail_receiver.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/DefWithParentheses: - Exclude: - - "app/models/user.rb" - -# Offense count: 322 +# Offense count: 337 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false @@ -1120,60 +884,20 @@ Style/Documentation: # SupportedStyles: allowed_in_returns, forbidden Style/DoubleNegation: Exclude: - - "app/controllers/tasks_controller.rb" + - 'app/controllers/tasks_controller.rb' -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowComments. -# SupportedStyles: empty, nil, both -Style/EmptyElse: - Exclude: - - "app/helpers/application_helper.rb" - - "app/models/article.rb" - - "app/models/order_article.rb" - - "app/models/user.rb" - - "lib/token_verifier.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/EmptyLiteral: - Exclude: - - "plugins/wiki/app/helpers/pages_helper.rb" - -# Offense count: 14 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - - "app/controllers/articles_controller.rb" - - "app/controllers/feedback_controller.rb" - - "app/controllers/finance/invoices_controller.rb" - - "app/controllers/home_controller.rb" - - "app/controllers/login_controller.rb" - - "app/mailers/mailer.rb" - - "db/migrate/024_add_deposit_defaults.rb" - - "db/migrate/20090120184410_road_to_version_three.rb" - - "db/migrate/20090907120012_add_missing_indexes.rb" - - "db/migrate/20130702113610_update_group_order_totals.rb" - - "db/migrate/20130718183101_migrate_user_settings.rb" - - "db/migrate/20140318173000_delete_empty_group_order_articles.rb" - - "lib/bank_account_connector.rb" - -# Offense count: 21 -# This cop supports safe autocorrection (--autocorrect). -Style/ExpandPathArguments: - Enabled: false - -# Offense count: 7 -# This cop supports safe autocorrection (--autocorrect). -Style/ExplicitBlockArgument: - Exclude: - - "app/documents/order_fax.rb" - - "app/helpers/admin/configs_helper.rb" - - "app/models/concerns/find_each_with_order.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_articles.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_groups.rb" + - 'db/migrate/024_add_deposit_defaults.rb' + - 'db/migrate/20090120184410_road_to_version_three.rb' + - 'db/migrate/20090907120012_add_missing_indexes.rb' + - 'db/migrate/20130702113610_update_group_order_totals.rb' + - 'db/migrate/20130718183101_migrate_user_settings.rb' + - 'db/migrate/20140318173000_delete_empty_group_order_articles.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1181,7 +905,7 @@ Style/ExplicitBlockArgument: # SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv Style/FloatDivision: Exclude: - - "app/models/ordergroup.rb" + - 'app/models/ordergroup.rb' # Offense count: 18 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1189,33 +913,25 @@ Style/FloatDivision: # SupportedStyles: each, for Style/For: Exclude: - - "app/controllers/admin/configs_controller.rb" - - "app/models/delivery.rb" - - "app/models/group_order.rb" - - "app/models/order.rb" - - "app/models/stock_taking.rb" - - "app/models/supplier.rb" - - "db/migrate/005_create_financial_transactions.rb" - - "lib/tasks/foodsoft.rake" - - "plugins/messages/app/mail_receivers/messages_mail_receiver.rb" - - "plugins/wiki/app/views/pages/all.rss.builder" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: format, sprintf, percent -Style/FormatString: - Exclude: - - "lib/order_txt.rb" + - 'app/controllers/admin/configs_controller.rb' + - 'app/models/delivery.rb' + - 'app/models/group_order.rb' + - 'app/models/order.rb' + - 'app/models/stock_taking.rb' + - 'app/models/supplier.rb' + - 'db/migrate/005_create_financial_transactions.rb' + - 'lib/tasks/foodsoft.rake' + - 'plugins/messages/app/mail_receivers/messages_mail_receiver.rb' + - 'plugins/wiki/app/views/pages/all.rss.builder' # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns, IgnoredMethods. +# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated -# Offense count: 498 +# Offense count: 511 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -1226,48 +942,38 @@ Style/FrozenStringLiteralComment: # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: - - "config/environments/production.rb" - - "lib/tasks/foodsoft.rake" - - "lib/tasks/foodsoft_setup.rake" + - 'config/environments/production.rb' + - 'lib/tasks/foodsoft.rake' + - 'lib/tasks/foodsoft_setup.rake' -# Offense count: 61 +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: - Enabled: false - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: braces, no_braces -Style/HashAsLastArrayItem: Exclude: - - "app/models/order.rb" + - 'db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb' + - 'plugins/wiki/app/controllers/pages_controller.rb' -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowSplatArgument. -Style/HashConversion: +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/HashExcept: Exclude: - - "app/helpers/application_helper.rb" - - "app/models/article.rb" - - "app/models/order.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" - - "spec/api/v1/user/ordergroup_spec.rb" + - 'spec/models/article_spec.rb' # Offense count: 8 # Configuration parameters: MinBranchesCount. Style/HashLikeCase: Exclude: - - "app/controllers/articles_controller.rb" - - "app/controllers/finance/bank_transactions_controller.rb" - - "app/controllers/finance/financial_transactions_controller.rb" - - "app/controllers/home_controller.rb" - - "app/controllers/orders_controller.rb" - - "app/helpers/finance/balancing_helper.rb" - - "plugins/documents/app/controllers/documents_controller.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" + - 'app/controllers/articles_controller.rb' + - 'app/controllers/finance/bank_transactions_controller.rb' + - 'app/controllers/finance/financial_transactions_controller.rb' + - 'app/controllers/home_controller.rb' + - 'app/controllers/orders_controller.rb' + - 'app/helpers/finance/balancing_helper.rb' + - 'plugins/documents/app/controllers/documents_controller.rb' + - 'plugins/wiki/app/controllers/pages_controller.rb' -# Offense count: 3904 +# Offense count: 375 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys @@ -1275,122 +981,64 @@ Style/HashLikeCase: Style/HashSyntax: Enabled: false -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowIfModifier. -Style/IfInsideElse: - Exclude: - - "app/models/article.rb" - - "app/models/task.rb" - - "lib/apple_bar.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" - -# Offense count: 61 +# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: - Enabled: false + Exclude: + - 'db/migrate/20090120184410_road_to_version_three.rb' -# Offense count: 2 +# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods. # AllowedMethods: nonzero? Style/IfWithBooleanLiteralBranches: Exclude: - - "app/models/order_article.rb" - - "app/models/task.rb" - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/InfiniteLoop: - Exclude: - - "lib/order_pdf.rb" + - 'app/models/order_article.rb' # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: InverseMethods, InverseBlocks. Style/InverseMethods: Exclude: - - "app/helpers/application_helper.rb" - - "app/helpers/deliveries_helper.rb" - - "spec/support/coverage.rb" - -# Offense count: 4 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: line_count_dependent, lambda, literal -Style/Lambda: - Exclude: - - "app/models/financial_link.rb" - - "lib/foodsoft_mail_receiver.rb" - - "plugins/messages/app/models/message.rb" + - 'app/helpers/application_helper.rb' + - 'app/helpers/deliveries_helper.rb' + - 'spec/support/coverage.rb' # Offense count: 5 # This cop supports unsafe autocorrection (--autocorrect-all). Style/LineEndConcatenation: Exclude: - - "db/migrate/20130702113610_update_group_order_totals.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_articles.rb" + - 'db/migrate/20130702113610_update_group_order_totals.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_articles.rb' # Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods. -Style/MethodCallWithoutArgsParentheses: +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/MapToHash: Exclude: - - "plugins/discourse/app/controllers/discourse_login_controller.rb" - -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline -Style/MethodDefParentheses: - Exclude: - - "app/controllers/concerns/send_order_pdf.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/finance/invoices_helper.rb" - - "plugins/discourse/app/controllers/discourse_controller.rb" + - 'app/models/article.rb' # Offense count: 1 Style/MixinUsage: Exclude: - - "lib/tasks/foodsoft_setup.rake" - -# Offense count: 3 -Style/MultilineBlockChain: - Exclude: - - "app/helpers/group_orders_helper.rb" - - "app/models/order.rb" - - "config/initializers/rails6_backports.rb" + - 'lib/tasks/foodsoft_setup.rake' # Offense count: 2 +Style/MultilineBlockChain: + Exclude: + - 'app/helpers/group_orders_helper.rb' + - 'app/models/order.rb' + +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). Style/MultilineIfModifier: Exclude: - - "app/models/user.rb" - - "plugins/current_orders/app/controllers/current_orders/ordergroups_controller.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/MultilineIfThen: - Exclude: - - "app/controllers/finance/financial_links_controller.rb" - -# Offense count: 12 -# This cop supports safe autocorrection (--autocorrect). -Style/MultilineWhenThen: - Exclude: - - "app/controllers/finance/balancing_controller.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/finance/balancing_helper.rb" - - "app/models/order.rb" - -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMethodComparison. -Style/MultipleComparison: - Exclude: - - "app/models/order.rb" - - "app/models/order_article.rb" - - "spec/models/article_spec.rb" + - 'app/controllers/admin/ordergroups_controller.rb' + - 'app/controllers/articles_controller.rb' + - 'app/controllers/orders_controller.rb' + - 'app/documents/order_fax.rb' + - 'app/lib/foodsoft_config.rb' + - 'app/models/ordergroup.rb' + - 'config/initializers/currency_display.rb' # Offense count: 24 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1399,75 +1047,23 @@ Style/MultipleComparison: Style/MutableConstant: Enabled: false -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: both, prefix, postfix -Style/NegatedIf: - Exclude: - - "app/controllers/orders_controller.rb" - - "app/helpers/articles_helper.rb" - -# Offense count: 4 -# This cop supports safe autocorrection (--autocorrect). -Style/NegatedIfElseCondition: - Exclude: - - "app/controllers/articles_controller.rb" - - "app/controllers/concerns/auth.rb" - - "app/models/article.rb" - -# Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods. -# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with -Style/NestedParenthesizedCalls: - Exclude: - - "app/models/user.rb" - - "spec/models/order_article_spec.rb" - -# Offense count: 7 +# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinBodyLength. # SupportedStyles: skip_modifier_ifs, always Style/Next: Exclude: - - "app/controllers/finance/financial_transactions_controller.rb" - - "app/controllers/orders_controller.rb" - - "app/helpers/orders_helper.rb" - - "db/migrate/20130622095040_move_weekly_tasks.rb" - - "lib/tasks/foodsoft.rake" + - 'db/migrate/20130622095040_move_weekly_tasks.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: predicate, comparison -Style/NilComparison: - Exclude: - - "app/controllers/application_controller.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" - -# Offense count: 10 -# This cop supports safe autocorrection (--autocorrect). -Style/Not: - Exclude: - - "app/controllers/concerns/auth.rb" - - "app/controllers/orders_controller.rb" - - "app/helpers/deliveries_helper.rb" - - "app/models/group_order_article.rb" - - "app/models/order_article.rb" - - "app/models/supplier.rb" - - "app/models/task.rb" - - "spec/support/coverage.rb" - -# Offense count: 6 -# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: - MinDigits: 7 + MinDigits: 6 -# Offense count: 61 +# Offense count: 60 # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns, IgnoredMethods. +# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false @@ -1477,51 +1073,10 @@ Style/NumericPredicate: # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - "app/helpers/application_helper.rb" - - "app/helpers/orders_helper.rb" - - "app/models/order_article.rb" - - "lib/tasks/foodsoft_setup.rake" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/OrAssignment: - Exclude: - - "app/controllers/articles_controller.rb" - -# Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). -Style/ParallelAssignment: - Exclude: - - "app/models/article.rb" - - "app/models/group_order_article.rb" - - "app/models/supplier.rb" - - "app/models/user.rb" - - "spec/models/group_order_article_spec.rb" - - "spec/support/session_helper.rb" - -# Offense count: 12 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. -Style/ParenthesesAroundCondition: - Exclude: - - "app/controllers/login_controller.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/group_orders_helper.rb" - - "app/models/group_order_article.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" - -# Offense count: 41 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: PreferredDelimiters. -Style/PercentLiteralDelimiters: - Enabled: false - -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -Style/PerlBackrefs: - Exclude: - - "lib/foodsoft/expansion_variables.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" + - 'app/helpers/application_helper.rb' + - 'app/helpers/orders_helper.rb' + - 'app/models/order_article.rb' + - 'lib/tasks/foodsoft_setup.rake' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1529,207 +1084,77 @@ Style/PerlBackrefs: # SupportedStyles: short, verbose Style/PreferredHashMethods: Exclude: - - "app/helpers/admin/configs_helper.rb" - - "app/helpers/articles_helper.rb" + - 'app/helpers/admin/configs_helper.rb' + - 'app/helpers/articles_helper.rb' -# Offense count: 14 -# This cop supports safe autocorrection (--autocorrect). -Style/Proc: - Exclude: - - "app/helpers/deliveries_helper.rb" - - "app/models/user.rb" - - "config/navigation.rb" - - "plugins/current_orders/lib/foodsoft_current_orders/engine.rb" - - "plugins/links/lib/foodsoft_links/engine.rb" - -# Offense count: 6 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowedCompactTypes. -# SupportedStyles: compact, exploded -Style/RaiseArgs: - Exclude: - - "app/controllers/api/v1/base_controller.rb" - - "app/controllers/concerns/auth_api.rb" - - "app/controllers/concerns/foodcoop_scope.rb" - -# Offense count: 5 +# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Style/RandomWithOffset: Exclude: - - "db/migrate/007_create_article_prices.rb" - - "db/migrate/008_create_orders.rb" - - "db/seeds/seed_helper.rb" + - 'db/migrate/007_create_article_prices.rb' + - 'db/migrate/008_create_orders.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Methods. Style/RedundantArgument: Exclude: - - "app/controllers/articles_controller.rb" - -# Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantBegin: - Exclude: - - "app/controllers/articles_controller.rb" - - "app/models/order.rb" - - "lib/foodsoft_mail_receiver.rb" - - "lib/tasks/multicoops.rake" - - "spec/lib/foodsoft_mail_receiver_spec.rb" - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantConditional: - Exclude: - - "app/models/task.rb" + - 'app/controllers/articles_controller.rb' # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SafeForConstants. Style/RedundantFetchBlock: Exclude: - - "config/puma.rb" + - 'config/puma.rb' -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantFileExtensionInRequire: - Exclude: - - "db/seeds/small.en.seeds.rb" - - "db/seeds/small.nl.seeds.rb" - -# Offense count: 5 +# Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: - - "db/migrate/20130718183101_migrate_user_settings.rb" - - "lib/order_pdf.rb" - - "spec/i18n_spec.rb" - - "spec/models/user_spec.rb" + - 'db/migrate/20130718183101_migrate_user_settings.rb' + - 'spec/i18n_spec.rb' + - 'spec/models/user_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -Style/RedundantRegexpCharacterClass: +Style/RedundantParentheses: Exclude: - - "plugins/wiki/app/helpers/pages_helper.rb" - -# Offense count: 7 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantRegexpEscape: - Exclude: - - "lib/bank_transaction_reference.rb" - - "lib/foodsoft_mail_receiver.rb" - - "plugins/documents/app/controllers/documents_controller.rb" - - "plugins/wiki/app/models/page.rb" - -# Offense count: 15 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMultipleReturnValues. -Style/RedundantReturn: - Exclude: - - "app/controllers/concerns/auth_api.rb" - - "app/helpers/application_helper.rb" - - "app/helpers/deliveries_helper.rb" - - "app/helpers/group_orders_helper.rb" - - "app/helpers/orders_helper.rb" - - "app/models/article.rb" - - "app/models/bank_transaction.rb" - - "app/models/periodic_task_group.rb" - - "app/models/supplier.rb" - - "lib/bank_transaction_reference.rb" - -# Offense count: 83 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantSelf: - Enabled: false + - 'db/migrate/021_remove_table_article_prices.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantSort: Exclude: - - "app/models/article_category.rb" + - 'app/models/article_category.rb' -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowInnerSlashes. -# SupportedStyles: slashes, percent_r, mixed -Style/RegexpLiteral: - Exclude: - - "plugins/wiki/app/models/page.rb" - - "spec/support/coverage.rb" - -# Offense count: 16 -# This cop supports safe autocorrection (--autocorrect). -Style/RescueModifier: - Exclude: - - "app/controllers/invites_controller.rb" - - "app/models/article.rb" - - "app/models/order.rb" - - "app/models/ordergroup.rb" - - "config/application.rb" - - "lib/apple_bar.rb" - - "lib/date_time_attribute_validate.rb" - - "lib/foodsoft_date_util.rb" - - "plugins/messages/app/models/message.rb" - -# Offense count: 51 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: implicit, explicit -Style/RescueStandardError: - Enabled: false - -# Offense count: 9 +# Offense count: 8 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - - "app/controllers/concerns/auth_api.rb" - - "app/controllers/group_order_articles_controller.rb" - - "app/models/article_category.rb" - - "app/models/financial_transaction.rb" - - "app/models/ordergroup.rb" - - "app/models/user.rb" - - "plugins/printer/app/controllers/printer_controller.rb" - - "spec/factories/order.rb" + - 'app/controllers/concerns/auth_api.rb' + - 'app/controllers/group_order_articles_controller.rb' + - 'app/models/article_category.rb' + - 'app/models/financial_transaction.rb' + - 'app/models/user.rb' + - 'plugins/printer/app/controllers/printer_controller.rb' + - 'spec/factories/order.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/SelfAssignment: - Exclude: - - "app/helpers/application_helper.rb" - -# Offense count: 16 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - - "app/controllers/finance/bank_transactions_controller.rb" - - "app/controllers/finance/financial_transactions_controller.rb" - - "app/controllers/finance/invoices_controller.rb" - - "app/controllers/orders_controller.rb" - - "app/helpers/group_orders_helper.rb" - - "db/migrate/20090120184410_road_to_version_three.rb" - - "spec/api/v1/swagger_spec.rb" - - "spec/api/v1/user/group_order_articles_spec.rb" - - "spec/api/v1/user/ordergroup_spec.rb" - - "spec/models/order_article_spec.rb" + - 'db/migrate/20090120184410_road_to_version_three.rb' -# Offense count: 5 +# Offense count: 4 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: - - "app/helpers/admin/configs_helper.rb" - - "config/initializers/session_store.rb" - - "lib/order_pdf.rb" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowModifier. -Style/SoleNestedConditional: - Exclude: - - "app/controllers/articles_controller.rb" - - "app/controllers/concerns/auth.rb" + - 'app/helpers/admin/configs_helper.rb' + - 'config/initializers/session_store.rb' # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1738,125 +1163,65 @@ Style/SoleNestedConditional: Style/SpecialGlobalVars: EnforcedStyle: use_perl_names -# Offense count: 33 +# Offense count: 34 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Enabled: false -# Offense count: 1855 +# Offense count: 140 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Enabled: false -# Offense count: 80 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MinSize. +# Configuration parameters: . # SupportedStyles: percent, brackets Style/SymbolArray: - EnforcedStyle: brackets + EnforcedStyle: percent + MinSize: 5 -# Offense count: 19 +# Offense count: 20 # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, IgnoredMethods, AllowComments. -# AllowedMethods: respond_to, define_method +# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. +# AllowedMethods: define_method, mail, respond_to Style/SymbolProc: Exclude: - - "app/controllers/pickups_controller.rb" - - "app/helpers/orders_helper.rb" - - "app/models/delivery.rb" - - "app/models/financial_transaction_class.rb" - - "app/models/financial_transaction_type.rb" - - "app/models/group_order_article.rb" - - "app/models/order.rb" - - "app/models/order_article.rb" - - "app/models/stock_article.rb" - - "app/models/user.rb" - - "db/migrate/20090731132547_add_stats_to_groups.rb" - - "spec/factories/order.rb" - -# Offense count: 4 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowSafeAssignment. -# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex -Style/TernaryParentheses: - Exclude: - - "app/models/order_article.rb" - -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma -Style/TrailingCommaInArrayLiteral: - Exclude: - - "lib/articles_csv.rb" - - "lib/invoices_csv.rb" - - "lib/ordergroups_csv.rb" - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma -Style/TrailingCommaInHashLiteral: - Exclude: - - "app/controllers/finance/financial_transactions_controller.rb" - - "config/initializers/exception_notification.rb" - -# Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. -# AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym -Style/TrivialAccessors: - Exclude: - - "app/models/order.rb" - - "lib/bank_account_connector.rb" - - "plugins/messages/app/models/message.rb" - -# Offense count: 5 -# This cop supports safe autocorrection (--autocorrect). -Style/UnlessElse: - Exclude: - - "app/controllers/home_controller.rb" - - "app/controllers/orders_controller.rb" - - "app/helpers/group_order_articles_helper.rb" - - "plugins/current_orders/app/controllers/current_orders/articles_controller.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" + - 'app/controllers/pickups_controller.rb' + - 'app/helpers/orders_helper.rb' + - 'app/models/delivery.rb' + - 'app/models/financial_transaction_class.rb' + - 'app/models/financial_transaction_type.rb' + - 'app/models/group_order_article.rb' + - 'app/models/order.rb' + - 'app/models/order_article.rb' + - 'app/models/stock_article.rb' + - 'app/models/user.rb' + - 'db/migrate/20090731132547_add_stats_to_groups.rb' + - 'spec/factories/order.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -Style/WhileUntilModifier: - Exclude: - - "app/models/periodic_task_group.rb" - -# Offense count: 11 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, MinSize, WordRegex. +# Configuration parameters: WordRegex. # SupportedStyles: percent, brackets Style/WordArray: - Exclude: - - "app/documents/order_matrix.rb" - - "app/helpers/application_helper.rb" - - "app/models/supplier.rb" - - "db/migrate/006_create_articles.rb" - - "lib/tasks/foodsoft_setup.rake" - - "plugins/current_orders/app/controllers/current_orders/group_orders_controller.rb" - - "plugins/wiki/app/controllers/pages_controller.rb" - - "plugins/wiki/app/helpers/pages_helper.rb" - - "spec/support/faker.rb" + EnforcedStyle: percent + MinSize: 4 # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: - - "app/models/group_order_article.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_articles.rb" - - "plugins/current_orders/app/documents/multiple_orders_by_groups.rb" + - 'app/models/group_order_article.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_articles.rb' + - 'plugins/current_orders/app/documents/multiple_orders_by_groups.rb' -# Offense count: 446 +# Offense count: 282 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns. +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. # URISchemes: http, https Layout/LineLength: - Max: 420 + Max: 320 diff --git a/.ruby-version b/.ruby-version index d48d3702..6a81b4c8 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.9 +2.7.8 diff --git a/Dockerfile b/Dockerfile index c999b3d4..e8f6a4c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 && \ supercronicBin=/usr/local/bin/supercronic && \ @@ -22,6 +22,7 @@ RUN buildDeps='libmagic-dev' && \ apt-get update && \ apt-get install --no-install-recommends -y $buildDeps && \ echo 'gem: --no-document' >> ~/.gemrc && \ + gem install bundler && \ bundle config build.nokogiri "--use-system-libraries" && \ bundle install --deployment --without development test -j 4 && \ 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/* # 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 tmp + chown nobody tmp && \ + chown nobody storage # Run app as unprivileged user USER nobody diff --git a/Dockerfile-dev b/Dockerfile-dev index ca7865a5..37dce5f6 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -1,4 +1,4 @@ -FROM ruby:2.6 +FROM ruby:2.7 # Install dependencies RUN deps='libmagic-dev chromium nodejs' && \ @@ -19,6 +19,7 @@ ENV PORT=3000 \ WORKDIR /app +RUN gem install bundler RUN bundle config build.nokogiri "--use-system-libraries" EXPOSE 3000 diff --git a/Gemfile b/Gemfile index a6e27fae..f724d5bf 100644 --- a/Gemfile +++ b/Gemfile @@ -1,69 +1,77 @@ # 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 'uglifier', '>= 1.0.3' +gem 'sassc-rails' # See https://github.com/sstephenson/execjs#readme for more supported runtimes gem 'therubyracer', platforms: :ruby -gem 'jquery-rails' -gem 'select2-rails' -gem 'rails_tokeninput' +gem 'bootsnap', require: false gem 'bootstrap-datepicker-rails' gem 'date_time_attribute' -gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update gem 'i18n-js', '~> 3.0.0.rc8' +gem 'jquery-rails' +gem 'rails-assets-listjs', '0.2.0.beta.4' # remember to maintain list.*.js plugins and template engines on update gem 'rails-i18n' -gem 'bootsnap', require: false +gem 'rails_tokeninput' +gem 'select2-rails' -gem 'mysql2' -gem 'prawn' -gem 'prawn-table' -gem 'haml' -gem 'haml-rails' -gem 'kaminari' -gem 'simple_form' -gem 'inherited_resources' +gem 'active_model_serializers', '~> 0.10.0' +gem 'acts_as_tree' +gem 'attribute_normalizer' gem 'daemons' gem 'doorkeeper' gem 'doorkeeper-i18n' +gem 'haml', '~> 5.0' +gem 'haml-rails' +gem 'ice_cube' +gem 'inherited_resources' +gem 'kaminari' +gem 'mysql2' +gem 'prawn' +gem 'prawn-table' +gem 'puma' gem 'rack-cors', require: 'rack/cors' -gem 'active_model_serializers', '~> 0.10.0' -gem 'twitter-bootstrap-rails', '~> 2.2.8' +gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73 +gem 'ransack' +gem 'resque' +gem 'ruby-units' +gem 'sd_notify' +gem 'simple_form' gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap gem 'simple-navigation-bootstrap' gem 'sprockets', '< 4' -gem 'ransack' -gem 'acts_as_tree' -gem 'rails-settings-cached', '= 0.4.3' # caching breaks tests until Rails 5 https://github.com/huacnlee/rails-settings-cached/issues/73 -gem 'resque' -gem 'puma' -gem 'sd_notify' +gem 'twitter-bootstrap-rails', '~> 2.2.8' gem 'whenever', require: false # For defining cronjobs, see config/schedule.rb -gem 'ruby-units' -gem 'attribute_normalizer' -gem 'ice_cube' -gem 'recurring_select' -gem 'roo' -gem 'roo-xls' -gem 'spreadsheet' +# At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152 gem 'exception_notification' gem 'gaffe' -gem 'ruby-filemagic' -gem 'mime-types' +gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114 +gem "image_processing", "~> 1.12" +gem "importmap-rails", "~> 1.1" gem 'midi-smtp-server' +gem 'mime-types' +gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select' +gem '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 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_article_import', path: 'plugins/article_import' gem 'foodsoft_discourse', path: 'plugins/discourse' +gem 'foodsoft_documents', path: 'plugins/documents' gem 'foodsoft_links', path: 'plugins/links' +gem 'foodsoft_messages', path: 'plugins/messages' gem 'foodsoft_polls', path: 'plugins/polls' +gem 'foodsoft_wiki', path: 'plugins/wiki' # plugins not enabled by default # gem 'foodsoft_current_orders', path: 'plugins/current_orders' @@ -71,17 +79,18 @@ gem 'foodsoft_polls', path: 'plugins/polls' # gem 'foodsoft_uservoice', path: 'plugins/uservoice' group :development do - gem 'sqlite3', '~> 1.3.6' - gem 'mailcatcher' - gem 'web-console' gem 'listen' + gem 'mailcatcher' + gem 'sqlite3', '~> 1.3.6' + gem 'web-console' # Better error output gem 'better_errors' gem 'binding_of_caller' # gem "rails-i18n-debug" # 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 gem 'bullet' @@ -101,21 +110,20 @@ group :development, :test do end group :test do - gem 'rspec-rails' + gem 'apparition' # Capybara javascript driver + gem 'capybara' + gem 'connection_pool' + gem 'database_cleaner' gem 'factory_bot_rails' gem 'faker' - gem 'capybara' - gem 'apparition' # Capybara javascript driver - gem 'database_cleaner' - gem 'connection_pool' + gem 'rspec-rails' # need to include rspec components before i18n-spec or rake fails in test environment + gem 'i18n-spec' gem 'rspec-core' gem 'rspec-rerun' - gem 'i18n-spec' # code coverage gem 'simplecov', require: false gem 'simplecov-lcov', require: false # api - gem 'apivore', require: false - gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114 + gem 'rswag-specs' end diff --git a/Gemfile.lock b/Gemfile.lock index c53687fb..23b0f672 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 remote: https://github.com/technoweenie/acts_as_versioned.git revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b @@ -5,6 +16,14 @@ GIT acts_as_versioned (0.6.0) activerecord (>= 3.0.9) +PATH + remote: plugins/article_import + specs: + foodsoft_article_import (0.0.1) + deface (~> 1.0) + rails + roo (~> 2.9.0) + PATH remote: plugins/discourse specs: @@ -59,67 +78,83 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (5.2.8.1) - actionpack (= 5.2.8.1) + actioncable (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) + actionmailbox (7.0.4) + actionpack (= 7.0.4) + activejob (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) + 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) + net-imap + net-pop + net-smtp rails-dom-testing (~> 2.0) - actionpack (5.2.8.1) - actionview (= 5.2.8.1) - activesupport (= 5.2.8.1) - rack (~> 2.0, >= 2.0.8) + actionpack (7.0.4) + actionview (= 7.0.4) + activesupport (= 7.0.4) + rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.8.1) - activesupport (= 5.2.8.1) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (7.0.4) + actionpack (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.0.4) + activesupport (= 7.0.4) builder (~> 3.1) erubi (~> 1.4) 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) actionpack (>= 4.1, < 7.1) activemodel (>= 4.1, < 7.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (5.2.8.1) - activesupport (= 5.2.8.1) + activejob (7.0.4) + activesupport (= 7.0.4) globalid (>= 0.3.6) - activemodel (5.2.8.1) - activesupport (= 5.2.8.1) - activerecord (5.2.8.1) - activemodel (= 5.2.8.1) - activesupport (= 5.2.8.1) - arel (>= 9.0) - activestorage (5.2.8.1) - actionpack (= 5.2.8.1) - activerecord (= 5.2.8.1) - marcel (~> 1.0.0) - activesupport (5.2.8.1) + activemodel (7.0.4) + activesupport (= 7.0.4) + activerecord (7.0.4) + activemodel (= 7.0.4) + activesupport (= 7.0.4) + activestorage (7.0.4) + actionpack (= 7.0.4) + activejob (= 7.0.4) + activerecord (= 7.0.4) + activesupport (= 7.0.4) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (7.0.4) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) acts_as_tree (2.9.1) activerecord (>= 3.0.0) addressable (2.8.1) 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) capybara (~> 3.13, < 4) websocket-driver (>= 0.6.5) - arel (9.0.0) ast (2.4.2) attribute_normalizer (1.2.0) base32 (0.3.4) @@ -130,15 +165,15 @@ GEM bindex (0.8.1) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - bootsnap (1.13.0) + bootsnap (1.15.0) msgpack (~> 1.2) bootstrap-datepicker-rails (1.9.0.1) railties (>= 3.0) builder (3.2.4) - bullet (7.0.3) + bullet (7.0.7) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - capybara (3.36.0) + capybara (3.38.0) addressable matrix mini_mime (>= 0.1.3) @@ -159,7 +194,7 @@ GEM execjs coffee-script-source (1.12.2) commonjs (0.2.7) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.2) connection_pool (2.3.0) content_for_in_controllers (0.0.2) crass (1.0.6) @@ -170,6 +205,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) + date (3.3.3) date_time_attribute (0.1.2) activesupport (>= 3.0.0) debug_inspector (1.1.0) @@ -182,13 +218,13 @@ GEM diff-lcs (1.5.0) diffy (3.4.2) docile (1.4.0) - doorkeeper (5.6.0) + doorkeeper (5.6.6) railties (>= 5) - doorkeeper-i18n (5.2.5) + doorkeeper-i18n (5.2.6) doorkeeper (>= 5.2) email_reply_trimmer (0.1.13) - erubi (1.11.0) - eventmachine (1.2.7) + erubi (1.12.0) + eventmachine (1.0.9.1) exception_notification (4.5.0) actionmailer (>= 5.2, < 8) activesupport (>= 5.2, < 8) @@ -199,16 +235,15 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faker (2.22.0) + faker (3.1.0) i18n (>= 1.8.11, < 2) ffi (1.15.5) gaffe (1.2.0) rails (>= 4.0.0) - globalid (1.0.0) + globalid (1.0.1) activesupport (>= 5.0) - haml (6.0.5) - temple (>= 0.8.2) - thor + haml (5.2.2) + temple (>= 0.8.0) tilt haml-rails (2.1.0) actionpack (>= 5.1) @@ -220,13 +255,19 @@ GEM activesupport (>= 5.2) hashie (3.4.6) htmlentities (4.3.4) - i18n (1.12.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) i18n-js (3.0.11) i18n (>= 0.6.6, < 2) i18n-spec (0.6.0) iso ice_cube (0.16.4) + image_processing (1.12.2) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) + importmap-rails (1.1.5) + actionpack (>= 6.0.0) + railties (>= 6.0.0) inherited_resources (1.13.1) actionpack (>= 5.2, < 7.1) has_scope (~> 0.6) @@ -235,13 +276,13 @@ GEM interception (0.5) iso (0.4.0) i18n - jquery-rails (4.5.0) + jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.6.2) - json-schema (2.8.1) - addressable (>= 2.4) + json (2.6.3) + json-schema (3.0.0) + addressable (>= 2.8) jsonapi-renderer (0.2.2) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -261,15 +302,18 @@ GEM actionpack (>= 5.0) less (~> 2.6.0) sprockets (~> 3.0) - libv8 (3.16.14.19) + libv8 (3.16.14.19-x86_64-linux) listen (3.7.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.19.1) + loofah (2.21.3) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp mailcatcher (0.2.4) eventmachine haml @@ -282,29 +326,34 @@ GEM thin marcel (1.0.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) midi-smtp-server (3.0.3) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) + mini_magick (4.12.0) mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitest (5.16.3) + minitest (5.18.0) mono_logger (1.1.1) msgpack (1.6.0) multi_json (1.15.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) mysql2 (0.5.4) + net-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) - nokogiri (1.13.10) - mini_portile2 (~> 2.8.0) + nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) - parallel (1.22.1) - parser (3.1.2.1) + parallel (1.23.0) + parser (3.2.2.1) ast (~> 2.4.1) pdf-core (0.9.0) polyglot (0.3.5) @@ -322,75 +371,70 @@ GEM pry-stack_explorer (0.6.1) binding_of_caller (~> 1.0) pry (~> 0.13) - public_suffix (5.0.0) - puma (5.6.5) + public_suffix (5.0.1) + puma (6.0.2) nio4r (~> 2.0) - racc (1.6.1) - rack (2.2.4) - rack-contrib (2.3.0) - rack (~> 2.0) + racc (1.7.0) + rack (2.2.7) rack-cors (1.1.1) rack (>= 2.0.0) - rack-protection (3.0.4) + rack-protection (3.0.5) rack - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) - rails (5.2.8.1) - actioncable (= 5.2.8.1) - actionmailer (= 5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) - activemodel (= 5.2.8.1) - activerecord (= 5.2.8.1) - activestorage (= 5.2.8.1) - activesupport (= 5.2.8.1) - bundler (>= 1.3.0) - railties (= 5.2.8.1) - sprockets-rails (>= 2.0.0) + rails (7.0.4) + actioncable (= 7.0.4) + actionmailbox (= 7.0.4) + actionmailer (= 7.0.4) + actionpack (= 7.0.4) + actiontext (= 7.0.4) + actionview (= 7.0.4) + activejob (= 7.0.4) + activemodel (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) + bundler (>= 1.15.0) + railties (= 7.0.4) rails-assets-listjs (0.2.0.beta.4) railties (>= 3.1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.4) - loofah (~> 2.19, >= 2.19.1) - rails-i18n (5.1.3) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + rails-i18n (7.0.6) i18n (>= 0.7, < 2) - railties (>= 5.0, < 6) + railties (>= 6.0.0, < 8) rails-settings-cached (0.4.3) rails (>= 4.2.0) rails_tokeninput (1.7.0) railties (>= 3.1.0) - railties (5.2.8.1) - actionpack (= 5.2.8.1) - activesupport (= 5.2.8.1) + railties (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) method_source - rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) - ransack (2.5.0) - activerecord (>= 5.2.4) - activesupport (>= 5.2.4) + ransack (3.2.1) + activerecord (>= 6.1.5) + activesupport (>= 6.1.5) i18n rb-fsevent (0.11.2) rb-inotify (0.10.1) 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-client (>= 0.9.0) - redis-client (0.9.0) + redis-client (0.11.2) connection_pool - redis-namespace (1.9.0) + redis-namespace (1.10.0) redis (>= 4) ref (2.0.0) - regexp_parser (2.6.0) + regexp_parser (2.8.0) responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) @@ -400,59 +444,71 @@ GEM redis-namespace (~> 1.6) sinatra (>= 0.9.2) rexml (3.2.5) - roo (2.8.3) + roo (2.9.0) nokogiri (~> 1) rubyzip (>= 1.3.0, < 3.0.0) roo-xls (1.2.0) nokogiri roo (>= 2.0.0, < 3) spreadsheet (> 0.9.0) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.1) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.0) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-rails (5.1.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - railties (>= 5.2) - rspec-core (~> 3.10) - rspec-expectations (~> 3.10) - rspec-mocks (~> 3.10) - rspec-support (~> 3.10) + rspec-support (~> 3.12.0) + rspec-rails (6.0.1) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.11) + rspec-expectations (~> 3.11) + rspec-mocks (~> 3.11) + rspec-support (~> 3.11) rspec-rerun (1.1.0) rspec (~> 3.0) - rspec-support (3.11.1) - rubocop (1.36.0) + rspec-support (3.12.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) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.20.1, < 2.0) + rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.21.0) - parser (>= 3.1.1.0) - rubocop-rails (2.16.1) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.28.1) + parser (>= 3.2.1.0) + rubocop-rails (2.17.4) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - rubocop-rspec (2.13.2) + rubocop-rspec (2.16.0) rubocop (~> 1.33) ruby-filemagic (0.7.3) ruby-ole (1.2.12.2) - ruby-prof (1.4.3) - ruby-progressbar (1.11.0) + ruby-prof (1.4.5) + ruby-progressbar (1.13.0) ruby-units (3.0.0) + ruby-vips (2.1.4) + ffi (~> 1.12) ruby2_keywords (0.0.5) rubyzip (2.3.2) sass-rails (6.0.0) @@ -475,21 +531,21 @@ GEM simple_form (5.1.0) actionpack (>= 5.2) activemodel (>= 5.2) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) - sinatra (3.0.4) + sinatra (3.0.5) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.0.4) + rack-protection (= 3.0.5) tilt (~> 2.0) - skinny (0.2.2) - eventmachine (~> 1.0) - thin + skinny (0.2.4) + eventmachine (~> 1.0.0) + thin (>= 1.5, < 1.7) spreadsheet (1.3.0) ruby-ole sprockets (3.7.2) @@ -503,17 +559,19 @@ GEM sqlite3-ruby (1.3.3) sqlite3 (>= 1.3.3) 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) libv8 (~> 3.16.14.15) ref - thin (1.8.1) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) - thor (1.2.1) - thread_safe (0.3.6) + thin (1.6.2) + daemons (>= 1.0.9) + eventmachine (>= 1.0.0) + rack (>= 1.0.0) + thor (1.2.2) tilt (2.0.11) + timeout (0.3.1) ttfunk (1.7.0) twitter-bootstrap-rails (2.2.8) actionpack (>= 3.1) @@ -522,20 +580,18 @@ GEM railties (>= 3.1) twitter-text (1.14.7) unf (~> 0.1.0) - tzinfo (1.2.10) - thread_safe (~> 0.1) - uglifier (4.2.0) - execjs (>= 0.3.0, < 3) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (2.3.0) + unicode-display_width (2.4.2) uniform_notifier (1.16.0) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) + web-console (4.2.0) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) bindex (>= 0.4.0) - railties (>= 5.0) + railties (>= 6.0.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -549,15 +605,16 @@ GEM twitter-text xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.6.8) PLATFORMS ruby + x86_64-linux DEPENDENCIES active_model_serializers (~> 0.10.0) acts_as_tree acts_as_versioned! - apivore apparition attribute_normalizer better_errors @@ -575,6 +632,7 @@ DEPENDENCIES exception_notification factory_bot_rails faker + foodsoft_article_import! foodsoft_discourse! foodsoft_documents! foodsoft_links! @@ -582,19 +640,20 @@ DEPENDENCIES foodsoft_polls! foodsoft_wiki! gaffe - haml + haml (~> 5.0) haml-rails hashie (~> 3.4.6) i18n-js (~> 3.0.0.rc8) i18n-spec ice_cube + image_processing (~> 1.12) + importmap-rails (~> 1.1) inherited_resources jquery-rails kaminari less-rails listen mailcatcher - meta_request midi-smtp-server mime-types mysql2 @@ -604,26 +663,29 @@ DEPENDENCIES pry-stack_explorer puma rack-cors - rails (~> 5.2) + rails (~> 7.0) rails-assets-listjs (= 0.2.0.beta.4) rails-i18n rails-settings-cached (= 0.4.3) rails_tokeninput ransack - recurring_select + recurring_select! resque roo roo-xls rspec-core rspec-rails rspec-rerun + rswag-api + rswag-specs + rswag-ui rubocop rubocop-rails rubocop-rspec ruby-filemagic ruby-prof ruby-units - sass-rails + sassc-rails sd_notify select2-rails simple-navigation (~> 3.14.0) @@ -635,11 +697,11 @@ DEPENDENCIES sprockets (< 4) sqlite3 (~> 1.3.6) table_print + terser (~> 1.1) therubyracer twitter-bootstrap-rails (~> 2.2.8) - uglifier (>= 1.0.3) web-console whenever BUNDLED WITH - 1.17.3 + 2.4.5 diff --git a/README.md b/README.md index a1a9de24..3f253f86 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Foodsoft ========= + [![Build Status](https://github.com/foodcoops/foodsoft/workflows/Ruby/badge.svg)](https://github.com/foodcoops/foodsoft/actions) [![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.svg?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master) [![Docs Status](https://inch-ci.org/github/foodcoops/foodsoft.svg?branch=master)](http://inch-ci.org/github/foodcoops/foodsoft) @@ -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). +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 ---------- +> 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), then visit our [Developing Guidelines](https://github.com/foodcoops/foodsoft/wiki/Developing-Guidelines) page on the wiki. @@ -35,7 +42,6 @@ Deploying Setup foodsoft to [run in production](doc/SETUP_PRODUCTION.md), or join an existing [hosting platform](https://foodcoops.net/foodsoft-hosting/). - License ------- diff --git a/Rakefile b/Rakefile index 835180b2..e59b186f 100755 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ #!/usr/bin/env rake # Add your own tasks in files placed in lib/tasks ending in .rake, -require File.expand_path('../config/application', __FILE__) +require File.expand_path('config/application', __dir__) require 'rake' require 'rspec-rerun/tasks' if defined?(RSpec) # http://stackoverflow.com/a/16853615/2866660 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application_legacy.js similarity index 99% rename from app/assets/javascripts/application.js rename to app/assets/javascripts/application_legacy.js index ebe63685..cd4a7273 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application_legacy.js @@ -8,6 +8,7 @@ //= require bootstrap-datepicker/locales/bootstrap-datepicker.es //= require bootstrap-datepicker/locales/bootstrap-datepicker.nl //= require bootstrap-datepicker/locales/bootstrap-datepicker.fr +//= require bootstrap-datepicker/locales/bootstrap-datepicker.tr //= require list //= require list.unlist //= require list.delay diff --git a/app/assets/stylesheets/actiontext.css b/app/assets/stylesheets/actiontext.css new file mode 100644 index 00000000..3cfcb2b7 --- /dev/null +++ b/app/assets/stylesheets/actiontext.css @@ -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.css’s image gallery styles to accommodate the + * 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; +} diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 6bdfecd2..01dba421 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -7,4 +7,5 @@ *= require list.unlist *= require list.missing *= require recurring_select +*= require actiontext */ diff --git a/app/controllers/admin/bank_accounts_controller.rb b/app/controllers/admin/bank_accounts_controller.rb index e23b03b2..d37f57e8 100644 --- a/app/controllers/admin/bank_accounts_controller.rb +++ b/app/controllers/admin/bank_accounts_controller.rb @@ -3,39 +3,39 @@ class Admin::BankAccountsController < Admin::BaseController def new @bank_account = BankAccount.new(params[:bank_account]) - render :layout => false + render layout: false + end + + def edit + @bank_account = BankAccount.find(params[:id]) + render action: 'new', layout: false end def create @bank_account = BankAccount.new(params[:bank_account]) if @bank_account.valid? && @bank_account.save - redirect_to update_bank_accounts_admin_finances_url, :status => 303 + redirect_to update_bank_accounts_admin_finances_url, status: :see_other else - render :action => 'new', :layout => false + render action: 'new', layout: false end end - def edit - @bank_account = BankAccount.find(params[:id]) - render :action => 'new', :layout => false - end - def update @bank_account = BankAccount.find(params[:id]) if @bank_account.update(params[:bank_account]) - redirect_to update_bank_accounts_admin_finances_url, :status => 303 + redirect_to update_bank_accounts_admin_finances_url, status: :see_other else - render :action => 'new', :layout => false + render action: 'new', layout: false end end def destroy @bank_account = BankAccount.find(params[:id]) @bank_account.destroy - redirect_to update_bank_accounts_admin_finances_url, :status => 303 - rescue => error - flash.now[:alert] = error.message + redirect_to update_bank_accounts_admin_finances_url, status: :see_other + rescue StandardError => e + flash.now[:alert] = e.message render template: 'shared/alert' end end diff --git a/app/controllers/admin/bank_gateways_controller.rb b/app/controllers/admin/bank_gateways_controller.rb index 3965c91b..c7ca5516 100644 --- a/app/controllers/admin/bank_gateways_controller.rb +++ b/app/controllers/admin/bank_gateways_controller.rb @@ -6,6 +6,11 @@ class Admin::BankGatewaysController < Admin::BaseController render layout: false end + def edit + @bank_gateway = BankGateway.find(params[:id]) + render action: 'new', layout: false + end + def create @bank_gateway = BankGateway.new(params[:bank_gateway]) if @bank_gateway.valid? && @bank_gateway.save @@ -15,11 +20,6 @@ class Admin::BankGatewaysController < Admin::BaseController end end - def edit - @bank_gateway = BankGateway.find(params[:id]) - render action: 'new', layout: false - end - def update @bank_gateway = BankGateway.find(params[:id]) diff --git a/app/controllers/admin/configs_controller.rb b/app/controllers/admin/configs_controller.rb index 516113af..500c1b87 100644 --- a/app/controllers/admin/configs_controller.rb +++ b/app/controllers/admin/configs_controller.rb @@ -1,5 +1,5 @@ class Admin::ConfigsController < Admin::BaseController - before_action :get_tabs, only: [:show, :list] + before_action :get_tabs, only: %i[show list] def show @current_tab = @tabs.include?(params[:tab]) ? params[:tab] : @tabs.first @@ -16,7 +16,7 @@ class Admin::ConfigsController < Admin::BaseController def update parse_recurring_selects! params[:config][:order_schedule] ActiveRecord::Base.transaction do - # TODO support nested configuration keys + # TODO: support nested configuration keys params[:config].each do |key, val| FoodsoftConfig[key] = convert_config_value val end @@ -29,7 +29,7 @@ class Admin::ConfigsController < Admin::BaseController # Set configuration tab names as `@tabs` def get_tabs - @tabs = %w(foodcoop payment tasks messages layout language security others) + @tabs = %w[foodcoop payment tasks messages layout language security others] # allow engines to modify this list engines = Rails::Engine.subclasses.map(&:instance).select { |e| e.respond_to?(:configuration) } engines.each { |e| e.configuration(@tabs, self) } @@ -38,16 +38,16 @@ class Admin::ConfigsController < Admin::BaseController # turn recurring rules into something palatable def parse_recurring_selects!(config) - if config - for k in [:pickup, :boxfill, :ends] do - if config[k] - # allow clearing it using dummy value '{}' ('' would break recurring_select) - if config[k][:recurr].present? && config[k][:recurr] != '{}' - config[k][:recurr] = ActiveSupport::JSON.decode(config[k][:recurr]) - config[k][:recurr] = FoodsoftDateUtil.rule_from(config[k][:recurr]).to_ical if config[k][:recurr] - else - config[k] = nil - end + return unless config + + for k in %i[pickup boxfill ends] do + if config[k] + # allow clearing it using dummy value '{}' ('' would break recurring_select) + if config[k][:recurr].present? && config[k][:recurr] != '{}' + config[k][:recurr] = ActiveSupport::JSON.decode(config[k][:recurr]) + config[k][:recurr] = FoodsoftDateUtil.rule_from(config[k][:recurr]).to_ical if config[k][:recurr] + else + config[k] = nil end end end diff --git a/app/controllers/admin/finances_controller.rb b/app/controllers/admin/finances_controller.rb index 5aae587b..75bb7456 100644 --- a/app/controllers/admin/finances_controller.rb +++ b/app/controllers/admin/finances_controller.rb @@ -10,21 +10,21 @@ class Admin::FinancesController < Admin::BaseController def update_bank_accounts @bank_accounts = BankAccount.order('name') - render :layout => false + render layout: false end def update_bank_gateways @bank_gateways = BankGateway.order('name') - render :layout => false + render layout: false end def update_transaction_types @financial_transaction_classes = FinancialTransactionClass.includes(:financial_transaction_types).order('name ASC') - render :layout => false + render layout: false end def update_supplier_categories @supplier_categories = SupplierCategory.order('name') - render :layout => false + render layout: false end end diff --git a/app/controllers/admin/financial_transaction_classes_controller.rb b/app/controllers/admin/financial_transaction_classes_controller.rb index e5d27efd..132e9038 100644 --- a/app/controllers/admin/financial_transaction_classes_controller.rb +++ b/app/controllers/admin/financial_transaction_classes_controller.rb @@ -6,25 +6,25 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController render layout: false end - def create - @financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class]) - if @financial_transaction_class.save - redirect_to update_transaction_types_admin_finances_url, status: 303 - else - render action: 'new', layout: false - end - end - def edit @financial_transaction_class = FinancialTransactionClass.find(params[:id]) render action: 'new', layout: false end + def create + @financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class]) + if @financial_transaction_class.save + redirect_to update_transaction_types_admin_finances_url, status: :see_other + else + render action: 'new', layout: false + end + end + def update @financial_transaction_class = FinancialTransactionClass.find(params[:id]) if @financial_transaction_class.update(params[:financial_transaction_class]) - redirect_to update_transaction_types_admin_finances_url, status: 303 + redirect_to update_transaction_types_admin_finances_url, status: :see_other else render action: 'new', layout: false end @@ -33,9 +33,9 @@ class Admin::FinancialTransactionClassesController < Admin::BaseController def destroy @financial_transaction_class = FinancialTransactionClass.find(params[:id]) @financial_transaction_class.destroy! - redirect_to update_transaction_types_admin_finances_url, status: 303 - rescue => error - flash.now[:alert] = error.message + redirect_to update_transaction_types_admin_finances_url, status: :see_other + rescue StandardError => e + flash.now[:alert] = e.message render template: 'shared/alert' end end diff --git a/app/controllers/admin/financial_transaction_types_controller.rb b/app/controllers/admin/financial_transaction_types_controller.rb index 2710bd6e..322451e4 100644 --- a/app/controllers/admin/financial_transaction_types_controller.rb +++ b/app/controllers/admin/financial_transaction_types_controller.rb @@ -7,25 +7,25 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController render layout: false end - def create - @financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type]) - if @financial_transaction_type.save - redirect_to update_transaction_types_admin_finances_url, status: 303 - else - render action: 'new', layout: false - end - end - def edit @financial_transaction_type = FinancialTransactionType.find(params[:id]) render action: 'new', layout: false end + def create + @financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type]) + if @financial_transaction_type.save + redirect_to update_transaction_types_admin_finances_url, status: :see_other + else + render action: 'new', layout: false + end + end + def update @financial_transaction_type = FinancialTransactionType.find(params[:id]) if @financial_transaction_type.update(params[:financial_transaction_type]) - redirect_to update_transaction_types_admin_finances_url, status: 303 + redirect_to update_transaction_types_admin_finances_url, status: :see_other else render action: 'new', layout: false end @@ -34,9 +34,9 @@ class Admin::FinancialTransactionTypesController < Admin::BaseController def destroy @financial_transaction_type = FinancialTransactionType.find(params[:id]) @financial_transaction_type.destroy! - redirect_to update_transaction_types_admin_finances_url, status: 303 - rescue => error - flash.now[:alert] = error.message + redirect_to update_transaction_types_admin_finances_url, status: :see_other + rescue StandardError => e + flash.now[:alert] = e.message render template: 'shared/alert' end end diff --git a/app/controllers/admin/mail_delivery_status_controller.rb b/app/controllers/admin/mail_delivery_status_controller.rb index 52a4db92..c0086044 100644 --- a/app/controllers/admin/mail_delivery_status_controller.rb +++ b/app/controllers/admin/mail_delivery_status_controller.rb @@ -3,28 +3,28 @@ class Admin::MailDeliveryStatusController < Admin::BaseController def index @maildeliverystatus = MailDeliveryStatus.order(created_at: :desc) - @maildeliverystatus = @maildeliverystatus.where(email: params[:email]) unless params[:email].blank? + @maildeliverystatus = @maildeliverystatus.where(email: params[:email]) if params[:email].present? @maildeliverystatus = @maildeliverystatus.page(params[:page]).per(@per_page) end def show @maildeliverystatus = MailDeliveryStatus.find(params[:id]) filename = "maildeliverystatus_#{params[:id]}.#{MIME::Types[@maildeliverystatus.attachment_mime].first.preferred_extension}" - send_data(@maildeliverystatus.attachment_data, :filename => filename, :type => @maildeliverystatus.attachment_mime) + send_data(@maildeliverystatus.attachment_data, filename: filename, type: @maildeliverystatus.attachment_mime) end def destroy_all @maildeliverystatus = MailDeliveryStatus.delete_all redirect_to admin_mail_delivery_status_index_path, notice: t('.notice') - rescue => error - redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message) end def destroy @maildeliverystatus = MailDeliveryStatus.find(params[:id]) @maildeliverystatus.destroy redirect_to admin_mail_delivery_status_index_path, notice: t('.notice') - rescue => error - redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to admin_mail_delivery_status_index_path, alert: I18n.t('errors.general_msg', msg: e.message) end end diff --git a/app/controllers/admin/ordergroups_controller.rb b/app/controllers/admin/ordergroups_controller.rb index d9dabe1e..213f3a0d 100644 --- a/app/controllers/admin/ordergroups_controller.rb +++ b/app/controllers/admin/ordergroups_controller.rb @@ -2,16 +2,15 @@ class Admin::OrdergroupsController < Admin::BaseController inherit_resources def index - @ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"]) + @ordergroups = Ordergroup.undeleted.sort_by_param(params['sort']) if request.format.csv? - send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv', type: 'text/csv' + send_data OrdergroupsCsv.new(@ordergroups).to_csv, filename: 'ordergroups.csv', + type: 'text/csv' end # if somebody uses the search field: - unless params[:query].blank? - @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") - end + @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present? @ordergroups = @ordergroups.page(params[:page]).per(@per_page) end @@ -19,8 +18,8 @@ class Admin::OrdergroupsController < Admin::BaseController def destroy @ordergroup = Ordergroup.find(params[:id]) @ordergroup.mark_as_deleted - redirect_to admin_ordergroups_url, notice: t('admin.ordergroups.destroy.notice') - rescue => error - redirect_to admin_ordergroups_url, alert: t('admin.ordergroups.destroy.error') + redirect_to admin_ordergroups_url, notice: t('.notice') + rescue StandardError => e + redirect_to admin_ordergroups_url, alert: t('.error') end end diff --git a/app/controllers/admin/supplier_categories_controller.rb b/app/controllers/admin/supplier_categories_controller.rb index f5768a21..f119dfb6 100644 --- a/app/controllers/admin/supplier_categories_controller.rb +++ b/app/controllers/admin/supplier_categories_controller.rb @@ -6,6 +6,11 @@ class Admin::SupplierCategoriesController < Admin::BaseController render layout: false end + def edit + @supplier_category = SupplierCategory.find(params[:id]) + render action: 'new', layout: false + end + def create @supplier_category = SupplierCategory.new(params[:supplier_category]) if @supplier_category.valid? && @supplier_category.save @@ -15,11 +20,6 @@ class Admin::SupplierCategoriesController < Admin::BaseController end end - def edit - @supplier_category = SupplierCategory.find(params[:id]) - render action: 'new', layout: false - end - def update @supplier_category = SupplierCategory.find(params[:id]) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 18bbbc1d..7d7e9295 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -3,16 +3,14 @@ class Admin::UsersController < Admin::BaseController def index @users = params[:show_deleted] ? User.deleted : User.undeleted - @users = @users.sort_by_param(params["sort"]) + @users = @users.sort_by_param(params['sort']) @users = @users.includes(:mail_delivery_status) - if request.format.csv? - send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv' - end + send_data UsersCsv.new(@users).to_csv, filename: 'users.csv', type: 'text/csv' if request.format.csv? # if somebody uses the search field: - @users = @users.natural_search(params[:user_name]) unless params[:user_name].blank? + @users = @users.natural_search(params[:user_name]) if params[:user_name].present? @users = @users.page(params[:page]).per(@per_page) end @@ -20,17 +18,17 @@ class Admin::UsersController < Admin::BaseController def destroy @user = User.find(params[:id]) @user.mark_as_deleted - redirect_to admin_users_url, notice: t('admin.users.destroy.notice') - rescue => error - redirect_to admin_users_url, alert: t('admin.users.destroy.error', error: error.message) + redirect_to admin_users_url, notice: t('.notice') + rescue StandardError => e + redirect_to admin_users_url, alert: t('.error', error: e.message) end def restore @user = User.find(params[:id]) @user.restore - redirect_to admin_users_url, notice: t('admin.users.restore.notice') - rescue => error - redirect_to admin_users_url, alert: t('admin.users.restore.error', error: error.message) + redirect_to admin_users_url, notice: t('.notice') + rescue StandardError => e + redirect_to admin_users_url, alert: t('.error', error: e.message) end def sudo diff --git a/app/controllers/admin/workgroups_controller.rb b/app/controllers/admin/workgroups_controller.rb index 184000bd..f5a9c2a3 100644 --- a/app/controllers/admin/workgroups_controller.rb +++ b/app/controllers/admin/workgroups_controller.rb @@ -4,7 +4,7 @@ class Admin::WorkgroupsController < Admin::BaseController def index @workgroups = Workgroup.order('name ASC') # if somebody uses the search field: - @workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].blank? + @workgroups = @workgroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present? @workgroups = @workgroups.page(params[:page]).per(@per_page) end @@ -12,8 +12,8 @@ class Admin::WorkgroupsController < Admin::BaseController def destroy @workgroup = Workgroup.find(params[:id]) @workgroup.destroy - redirect_to admin_workgroups_url, notice: t('admin.workgroups.destroy.notice') - rescue => error - redirect_to admin_workgroups_url, alert: t('admin.workgroups.destroy.error', error: error.message) + redirect_to admin_workgroups_url, notice: t('.notice') + rescue StandardError => e + redirect_to admin_workgroups_url, alert: t('.error', error: e.message) end end diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 13e903f1..8bed20ec 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -20,29 +20,30 @@ class Api::V1::BaseController < ApplicationController def require_ordergroup authenticate - unless current_ordergroup.present? - raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup') - end + return if current_ordergroup.present? + + raise Api::Errors::PermissionRequired, 'Forbidden, must be in an ordergroup' end def require_minimum_balance minimum_balance = FoodsoftConfig[:minimum_balance] or return - if current_ordergroup.account_balance < minimum_balance - raise Api::Errors::PermissionRequired.new(t('application.controller.error_minimum_balance', min: minimum_balance)) - end + return unless current_ordergroup.account_balance < minimum_balance + + raise Api::Errors::PermissionRequired, t('application.controller.error_minimum_balance', min: minimum_balance) end def require_enough_apples - if current_ordergroup.not_enough_apples? - s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, stop_ordering_under: FoodsoftConfig[:stop_ordering_under]) - raise Api::Errors::PermissionRequired.new(s) - end + return unless current_ordergroup.not_enough_apples? + + s = t('group_orders.messages.not_enough_apples', apples: current_ordergroup.apples, + stop_ordering_under: FoodsoftConfig[:stop_ordering_under]) + raise Api::Errors::PermissionRequired, s end def require_config_enabled(config) - unless FoodsoftConfig[config] - raise Api::Errors::PermissionRequired.new(t('application.controller.error_not_enabled', config: config)) - end + return if FoodsoftConfig[config] + + raise Api::Errors::PermissionRequired, t('application.controller.error_not_enabled', config: config) end def skip_session @@ -52,12 +53,12 @@ class Api::V1::BaseController < ApplicationController def not_found_handler(e) # remove where-clauses from error message (not suitable for end-users) msg = e.message.try { |m| m.sub(/\s*\[.*?\]\s*$/, '') } || 'Not found' - render status: 404, json: { error: 'not_found', error_description: msg } + render status: :not_found, json: { error: 'not_found', error_description: msg } end def not_acceptable_handler(e) msg = e.message || 'Data not acceptable' - render status: 422, json: { error: 'not_acceptable', error_description: msg } + render status: :unprocessable_entity, json: { error: 'not_acceptable', error_description: msg } end def doorkeeper_unauthorized_render_options(error:) @@ -70,11 +71,11 @@ class Api::V1::BaseController < ApplicationController def permission_required_handler(e) msg = e.message || 'Forbidden, user has no access' - render status: 403, json: { error: 'forbidden', error_description: msg } + render status: :forbidden, json: { error: 'forbidden', error_description: msg } end # @todo something with ApplicationHelper#show_user - def show_user(user = current_user, **options) + def show_user(user = current_user, **_options) user.display end end diff --git a/app/controllers/api/v1/user/financial_transactions_controller.rb b/app/controllers/api/v1/user/financial_transactions_controller.rb index 96b32e28..3de38de9 100644 --- a/app/controllers/api/v1/user/financial_transactions_controller.rb +++ b/app/controllers/api/v1/user/financial_transactions_controller.rb @@ -16,7 +16,8 @@ class Api::V1::User::FinancialTransactionsController < Api::V1::BaseController def create transaction_type = FinancialTransactionType.find(create_params[:financial_transaction_type_id]) - ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user, transaction_type) + ft = current_ordergroup.add_financial_transaction!(create_params[:amount], create_params[:note], current_user, + transaction_type) render json: ft end diff --git a/app/controllers/api/v1/user/group_order_articles_controller.rb b/app/controllers/api/v1/user/group_order_articles_controller.rb index ce258898..4b65a61d 100644 --- a/app/controllers/api/v1/user/group_order_articles_controller.rb +++ b/app/controllers/api/v1/user/group_order_articles_controller.rb @@ -4,8 +4,8 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController before_action -> { doorkeeper_authorize! 'group_orders:user' } before_action :require_ordergroup - before_action :require_minimum_balance, only: [:create, :update] # destroy is ok - before_action :require_enough_apples, only: [:create, :update] # destroy is ok + before_action :require_minimum_balance, only: %i[create update] # destroy is ok + before_action :require_enough_apples, only: %i[create update] # destroy is ok # @todo allow decreasing amounts when minimum balance isn't met def index @@ -35,7 +35,8 @@ class Api::V1::User::GroupOrderArticlesController < Api::V1::BaseController goa = nil GroupOrderArticle.transaction do goa = scope_for_update.includes(:group_order_article_quantities).find(params.require(:id)) - goa.update_quantities((update_params[:quantity] || goa.quantity).to_i, (update_params[:tolerance] || goa.tolerance).to_i) + goa.update_quantities((update_params[:quantity] || goa.quantity).to_i, + (update_params[:tolerance] || goa.tolerance).to_i) goa.order_article.update_results! goa.group_order.update_price! goa.group_order.update!(updated_by: current_user) diff --git a/app/controllers/api/v1/user/ordergroup_controller.rb b/app/controllers/api/v1/user/ordergroup_controller.rb index 08c12b4c..23889fe8 100644 --- a/app/controllers/api/v1/user/ordergroup_controller.rb +++ b/app/controllers/api/v1/user/ordergroup_controller.rb @@ -8,13 +8,13 @@ class Api::V1::User::OrdergroupController < Api::V1::BaseController financial_overview: { account_balance: ordergroup.account_balance.to_f, available_funds: ordergroup.get_available_funds.to_f, - financial_transaction_class_sums: FinancialTransactionClass.sorted.map { |c| + financial_transaction_class_sums: FinancialTransactionClass.sorted.map do |c| { id: c.id, name: c.display, amount: ordergroup["sum_of_class_#{c.id}"].to_f } - } + end } } end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eb90f9b4..3537f8c4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -19,10 +19,10 @@ class ApplicationController < ActionController::Base private def set_user_last_activity - if current_user && (session[:last_activity] == nil || session[:last_activity] < 1.minutes.ago) - current_user.update_attribute(:last_activity, Time.now) - session[:last_activity] = Time.now - end + return unless current_user && (session[:last_activity].nil? || session[:last_activity] < 1.minute.ago) + + current_user.update_attribute(:last_activity, Time.now) + session[:last_activity] = Time.now end # Many plugins can be turned on and off on the fly with a `use_` configuration option. @@ -64,11 +64,11 @@ class ApplicationController < ActionController::Base end def items_per_page - if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500 - @per_page = params[:per_page].to_i - else - @per_page = 20 - end + @per_page = if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500 + params[:per_page].to_i + else + 20 + end end # Set timezone according to foodcoop preference. diff --git a/app/controllers/article_categories_controller.rb b/app/controllers/article_categories_controller.rb index bfa601d3..810bb3ce 100644 --- a/app/controllers/article_categories_controller.rb +++ b/app/controllers/article_categories_controller.rb @@ -4,17 +4,17 @@ class ArticleCategoriesController < ApplicationController before_action :authenticate_article_meta def create - create!(:notice => I18n.t('article_categories.create.notice')) { article_categories_path } + create!(notice: I18n.t('article_categories.create.notice')) { article_categories_path } end def update - update!(:notice => I18n.t('article_categories.update.notice')) { article_categories_path } + update!(notice: I18n.t('article_categories.update.notice')) { article_categories_path } end def destroy destroy! - rescue => error - redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: error.message) + rescue StandardError => e + redirect_to article_categories_path, alert: I18n.t('article_categories.destroy.error', message: e.message) end protected diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 4161e66a..232391cf 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -2,24 +2,24 @@ class ArticlesController < ApplicationController before_action :authenticate_article_meta, :find_supplier def index - if params['sort'] - sort = case params['sort'] - when "name" then "articles.name" - when "unit" then "articles.unit" - when "article_category" then "article_categories.name" - when "note" then "articles.note" - when "availability" then "articles.availability" - when "name_reverse" then "articles.name DESC" - when "unit_reverse" then "articles.unit DESC" - when "article_category_reverse" then "article_categories.name DESC" - when "note_reverse" then "articles.note DESC" - when "availability_reverse" then "articles.availability DESC" + sort = if params['sort'] + case params['sort'] + when 'name' then 'articles.name' + when 'unit' then 'articles.unit' + when 'article_category' then 'article_categories.name' + when 'note' then 'articles.note' + when 'availability' then 'articles.availability' + when 'name_reverse' then 'articles.name DESC' + when 'unit_reverse' then 'articles.unit DESC' + when 'article_category_reverse' then 'article_categories.name DESC' + when 'note_reverse' then 'articles.note DESC' + when 'availability_reverse' then 'articles.availability DESC' end - else - sort = "article_categories.name, articles.name" - end + else + 'article_categories.name, articles.name' + end - @articles = Article.undeleted.where(supplier_id: @supplier, :type => nil).includes(:article_category).order(sort) + @articles = Article.undeleted.where(supplier_id: @supplier, type: nil).includes(:article_category).order(sort) if request.format.csv? send_data ArticlesCsv.new(@articles, encoding: 'utf-8').to_csv, filename: 'articles.csv', type: 'text/csv' @@ -32,42 +32,42 @@ class ArticlesController < ApplicationController respond_to do |format| format.html - format.js { render :layout => false } + format.js { render layout: false } end end def new - @article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default]) - render :layout => false + @article = @supplier.articles.build(tax: FoodsoftConfig[:tax_default]) + render layout: false end def copy @article = @supplier.articles.find(params[:article_id]).dup - render :layout => false + render layout: false + end + + def edit + @article = Article.find(params[:id]) + render action: 'new', layout: false end def create @article = Article.new(params[:article]) if @article.valid? && @article.save - render :layout => false + render layout: false else - render :action => 'new', :layout => false + render action: 'new', layout: false end end - def edit - @article = Article.find(params[:id]) - render :action => 'new', :layout => false - end - # Updates one Article and highlights the line if succeded def update @article = Article.find(params[:id]) if @article.update(params[:article]) - render :layout => false + render layout: false else - render :action => 'new', :layout => false + render action: 'new', layout: false end end @@ -75,7 +75,7 @@ class ArticlesController < ApplicationController def destroy @article = Article.find(params[:id]) @article.mark_as_deleted unless @order = @article.in_open_order # If article is in an active Order, the Order will be returned - render :layout => false + render layout: false end # Renders a form for editing all articles from a supplier @@ -87,19 +87,17 @@ class ArticlesController < ApplicationController def update_all invalid_articles = false - begin - Article.transaction do - unless params[:articles].blank? - # Update other article attributes... - @articles = Article.find(params[:articles].keys) - @articles.each do |article| - unless article.update(params[:articles][article.id.to_s]) - invalid_articles = true unless invalid_articles # Remember that there are validation errors - end + Article.transaction do + if params[:articles].present? + # Update other article attributes... + @articles = Article.find(params[:articles].keys) + @articles.each do |article| + unless article.update(params[:articles][article.id.to_s]) + invalid_articles ||= true # Remember that there are validation errors end - - raise ActiveRecord::Rollback if invalid_articles # Rollback all changes end + + raise ActiveRecord::Rollback if invalid_articles # Rollback all changes end end @@ -134,16 +132,15 @@ class ArticlesController < ApplicationController end end # action succeded - redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]) - rescue => error - redirect_to supplier_articles_url(@supplier, :per_page => params[:per_page]), - :alert => I18n.t('errors.general_msg', :msg => error) + redirect_to supplier_articles_url(@supplier, per_page: params[:per_page]) + rescue StandardError => e + redirect_to supplier_articles_url(@supplier, per_page: params[:per_page]), + alert: I18n.t('errors.general_msg', msg: e) end # lets start with parsing articles from uploaded file, yeah # Renders the upload form - def upload - end + def upload; end # Update articles from a spreadsheet def parse_upload @@ -151,13 +148,15 @@ class ArticlesController < ApplicationController options = { filename: uploaded_file.original_filename } options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1') options[:convert_units] = (params[:articles]['convert_units'] == '1') - @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, options + @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, + options if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? - redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice') + redirect_to supplier_articles_path(@supplier), + notice: I18n.t('articles.controller.parse_upload.notice') end @ignored_article_count = 0 - rescue => error - redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message) + rescue StandardError => e + redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message) end # sync all articles with the external database @@ -165,13 +164,14 @@ class ArticlesController < ApplicationController def sync # check if there is an shared_supplier unless @supplier.shared_supplier - redirect_to supplier_articles_url(@supplier), :alert => I18n.t('articles.controller.sync.shared_alert', :supplier => @supplier.name) + redirect_to supplier_articles_url(@supplier), + alert: I18n.t('articles.controller.sync.shared_alert', supplier: @supplier.name) end # sync articles against external database @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_all - if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? - redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice') - end + return unless @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? + + redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.sync.notice') end # Updates, deletes articles when upload or sync form is submitted @@ -186,7 +186,7 @@ class ArticlesController < ApplicationController # delete articles begin @outlisted_articles.each(&:mark_as_deleted) - rescue + rescue StandardError # raises an exception when used in current order has_error = true end @@ -198,15 +198,15 @@ class ArticlesController < ApplicationController raise ActiveRecord::Rollback if has_error end - if !has_error - redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice') - else + if has_error @updated_article_pairs = @updated_articles.map do |article| orig_article = Article.find(article.id) [article, orig_article.unequal_attributes(article)] end flash.now.alert = I18n.t('articles.controller.error_invalid') render params[:from_action] == 'sync' ? :sync : :parse_upload + else + redirect_to supplier_articles_path(@supplier), notice: I18n.t('articles.controller.update_sync.notice') end end @@ -218,18 +218,18 @@ class ArticlesController < ApplicationController q[:name_cont_all] = params.fetch(:name_cont_all_joined, '').split(' ') search = @supplier.shared_supplier.shared_articles.ransack(q) @articles = search.result.page(params[:page]).per(10) - render :layout => false + render layout: false end # fills a form whith values of the selected shared_article # when the direct parameter is set and the article is valid, it is imported directly def import @article = SharedArticle.find(params[:shared_article_id]).build_new_article(@supplier) - @article.article_category_id = params[:article_category_id] unless params[:article_category_id].blank? - if params[:direct] && !params[:article_category_id].blank? && @article.valid? && @article.save - render :action => 'create', :layout => false + @article.article_category_id = params[:article_category_id] if params[:article_category_id].present? + if params[:direct] && params[:article_category_id].present? && @article.valid? && @article.save + render action: 'create', layout: false else - render :action => 'new', :layout => false + render action: 'new', layout: false end end diff --git a/app/controllers/concerns/auth.rb b/app/controllers/concerns/auth.rb index 277acd69..edf6ec6f 100644 --- a/app/controllers/concerns/auth.rb +++ b/app/controllers/concerns/auth.rb @@ -9,15 +9,19 @@ module Concerns::Auth def current_user # check if there is a valid session and return the logged-in user (its object) - if session[:user_id] && params[:foodcoop] - # for shared-host installations. check if the cookie-subdomain fits to request. - @current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope - end + return unless session[:user_id] && params[:foodcoop] + + # for shared-host installations. check if the cookie-subdomain fits to request. + @current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope end def deny_access session[:return_to] = request.original_url - redirect_to root_url, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path)) + redirect_to root_url, + alert: I18n.t('application.controller.error_denied', + sign_in: ActionController::Base.helpers.link_to( + t('application.controller.error_denied_sign_in'), login_path + )) end private @@ -47,12 +51,7 @@ module Concerns::Auth def authenticate(role = 'any') # Attempt to retrieve authenticated user from controller instance or session... - if !current_user - # No user at all: redirect to login page. - logout - session[:return_to] = request.original_url - redirect_to_login :alert => I18n.t('application.controller.error_authn') - else + if current_user # We have an authenticated user, now check role... # Roles gets the user through his memberships. hasRole = case role @@ -73,6 +72,11 @@ module Concerns::Auth else deny_access end + else + # No user at all: redirect to login page. + logout + session[:return_to] = request.original_url + redirect_to_login alert: I18n.t('application.controller.error_authn') end end @@ -116,13 +120,13 @@ module Concerns::Auth # if fails the user will redirected to startpage def authenticate_membership_or_admin(group_id = params[:id]) @group = Group.find(group_id) - unless @group.member?(@current_user) || @current_user.role_admin? - redirect_to root_path, alert: I18n.t('application.controller.error_members_only') - end + return if @group.member?(@current_user) || @current_user.role_admin? + + redirect_to root_path, alert: I18n.t('application.controller.error_members_only') end def authenticate_or_token(prefix, role = 'any') - if not params[:token].blank? + if params[:token].present? begin TokenVerifier.new(prefix).verify(params[:token]) rescue ActiveSupport::MessageVerifier::InvalidSignature diff --git a/app/controllers/concerns/auth_api.rb b/app/controllers/concerns/auth_api.rb index 2c80dddf..fc16a2c2 100644 --- a/app/controllers/concerns/auth_api.rb +++ b/app/controllers/concerns/auth_api.rb @@ -36,9 +36,9 @@ module Concerns::AuthApi # Make sure that at least one the given OAuth scopes is valid for the current user's permissions. # @raise Api::Errors::PermissionsRequired def doorkeeper_authorize_roles!(*scopes) - unless scopes.any? { |scope| doorkeeper_scope_permitted?(scope) } - raise Api::Errors::PermissionRequired.new('Forbidden, no permission') - end + return if scopes.any? { |scope| doorkeeper_scope_permitted?(scope) } + + raise Api::Errors::PermissionRequired, 'Forbidden, no permission' end # Check whether a given OAuth scope is permitted for the current user. @@ -48,9 +48,7 @@ module Concerns::AuthApi def doorkeeper_scope_permitted?(scope) scope_parts = scope.split(':') # user sub-scopes like +config:user+ are always permitted - if scope_parts.last == 'user' - return true - end + return true if scope_parts.last == 'user' case scope_parts.first when 'user' then return true # access to the current user's own profile @@ -64,8 +62,8 @@ module Concerns::AuthApi end case scope - when 'orders:read' then return true - when 'orders:write' then return current_user.role_orders? + when 'orders:read' then true + when 'orders:write' then current_user.role_orders? end end end diff --git a/app/controllers/concerns/foodcoop_scope.rb b/app/controllers/concerns/foodcoop_scope.rb index 0a8e382e..7a99adf9 100644 --- a/app/controllers/concerns/foodcoop_scope.rb +++ b/app/controllers/concerns/foodcoop_scope.rb @@ -24,12 +24,12 @@ module Concerns::FoodcoopScope elsif FoodsoftConfig.allowed_foodcoop? foodcoop FoodsoftConfig.select_foodcoop foodcoop else - raise ActionController::RoutingError.new 'Foodcoop Not Found' + raise ActionController::RoutingError, 'Foodcoop Not Found' end end # Always stay in foodcoop url scope - def default_url_options(options = {}) + def default_url_options(_options = {}) super().merge({ foodcoop: FoodsoftConfig.scope }) end end diff --git a/app/controllers/concerns/locale.rb b/app/controllers/concerns/locale.rb index 22686c15..6a9736fb 100644 --- a/app/controllers/concerns/locale.rb +++ b/app/controllers/concerns/locale.rb @@ -18,7 +18,7 @@ module Concerns::Locale end def browser_language - request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil + request.env['HTTP_ACCEPT_LANGUAGE']&.scan(/^[a-z]{2}/)&.first end def default_language @@ -30,7 +30,7 @@ module Concerns::Locale def select_language_according_to_priority language = explicitly_requested_language || session_language || user_settings_language language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale] - language.presence&.to_sym unless language.blank? + language.presence&.to_sym if language.present? end def available_locales @@ -38,11 +38,11 @@ module Concerns::Locale end def set_locale - if available_locales.include?(select_language_according_to_priority) - ::I18n.locale = select_language_according_to_priority - else - ::I18n.locale = default_language - end + ::I18n.locale = if available_locales.include?(select_language_according_to_priority) + select_language_according_to_priority + else + default_language + end locale = session[:locale] = ::I18n.locale logger.info("Set locale to #{locale}") diff --git a/app/controllers/concerns/send_order_pdf.rb b/app/controllers/concerns/send_order_pdf.rb index 09225b7c..283512da 100644 --- a/app/controllers/concerns/send_order_pdf.rb +++ b/app/controllers/concerns/send_order_pdf.rb @@ -3,7 +3,7 @@ module Concerns::SendOrderPdf protected - def send_order_pdf order, document + def send_order_pdf(order, document) klass = case document when 'groups' then OrderByGroups when 'articles' then OrderByArticles diff --git a/app/controllers/deliveries_controller.rb b/app/controllers/deliveries_controller.rb index 0ecacc9c..15900022 100644 --- a/app/controllers/deliveries_controller.rb +++ b/app/controllers/deliveries_controller.rb @@ -1,5 +1,5 @@ class DeliveriesController < ApplicationController - before_action :find_supplier, :exclude => :fill_new_stock_article_form + before_action :find_supplier, exclude: :fill_new_stock_article_form def index @deliveries = @supplier.deliveries.order('date DESC') @@ -15,6 +15,10 @@ class DeliveriesController < ApplicationController @delivery.date = Date.today # TODO: move to model/database end + def edit + @delivery = Delivery.find(params[:id]) + end + def create @delivery = Delivery.new(params[:delivery]) @@ -22,14 +26,10 @@ class DeliveriesController < ApplicationController flash[:notice] = I18n.t('deliveries.create.notice') redirect_to [@supplier, @delivery] else - render :action => "new" + render action: 'new' end end - def edit - @delivery = Delivery.find(params[:id]) - end - def update @delivery = Delivery.find(params[:id]) @@ -37,7 +37,7 @@ class DeliveriesController < ApplicationController flash[:notice] = I18n.t('deliveries.update.notice') redirect_to [@supplier, @delivery] else - render :action => "edit" + render action: 'edit' end end @@ -52,18 +52,18 @@ class DeliveriesController < ApplicationController def add_stock_change @stock_change = StockChange.new @stock_change.stock_article = StockArticle.find(params[:stock_article_id]) - render :layout => false + render layout: false end def form_on_stock_article_create # See publish/subscribe design pattern in /doc. @stock_article = StockArticle.find(params[:id]) - render :layout => false + render layout: false end def form_on_stock_article_update # See publish/subscribe design pattern in /doc. @stock_article = StockArticle.find(params[:id]) - render :layout => false + render layout: false end end diff --git a/app/controllers/feedback_controller.rb b/app/controllers/feedback_controller.rb index ada72859..b4e5ea7e 100644 --- a/app/controllers/feedback_controller.rb +++ b/app/controllers/feedback_controller.rb @@ -1,13 +1,12 @@ class FeedbackController < ApplicationController - def new - end + def new; end def create if params[:message].present? Mailer.feedback(current_user, params[:message]).deliver_now - redirect_to root_url, notice: t('feedback.create.notice') + redirect_to root_url, notice: t('.notice') else - render :action => 'new' + render action: 'new' end end end diff --git a/app/controllers/finance/balancing_controller.rb b/app/controllers/finance/balancing_controller.rb index 4f23ac4f..e1a2dafb 100644 --- a/app/controllers/finance/balancing_controller.rb +++ b/app/controllers/finance/balancing_controller.rb @@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController def new @order = Order.find(params[:order_id]) - flash.now.alert = t('finance.balancing.new.alert') if @order.closed? + flash.now.alert = t('.alert') if @order.closed? @comments = @order.comments @articles = @order.order_articles.ordered_or_member.includes(:article, :article_price, @@ -13,13 +13,13 @@ class Finance::BalancingController < Finance::BaseController sort_param = params['sort'] || 'name' @articles = case sort_param - when 'name' then + when 'name' @articles.order('articles.name ASC') - when 'name_reverse' then + when 'name_reverse' @articles.order('articles.name DESC') - when 'order_number' then + when 'order_number' @articles.order('articles.order_number ASC') - when 'order_number_reverse' then + when 'order_number_reverse' @articles.order('articles.order_number DESC') else @articles @@ -31,13 +31,13 @@ class Finance::BalancingController < Finance::BaseController def new_on_order_article_create # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) - render :layout => false + render layout: false end def new_on_order_article_update # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) - render :layout => false + render layout: false end def update_summary @@ -46,29 +46,29 @@ class Finance::BalancingController < Finance::BaseController def edit_note @order = Order.find(params[:id]) - render :layout => false + render layout: false end def update_note @order = Order.find(params[:id]) if @order.update(params[:order]) - render :layout => false + render layout: false else - render :action => :edit_note, :layout => false + render action: :edit_note, layout: false end end def edit_transport @order = Order.find(params[:id]) - render :layout => false + render layout: false end def update_transport @order = Order.find(params[:id]) @order.update!(params[:order]) redirect_to new_finance_order_path(order_id: @order.id) - rescue => error - redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to new_finance_order_path(order_id: @order.id), alert: t('errors.general_msg', msg: e.message) end # before the order will booked, a view lists all Ordergroups and its order_prices @@ -81,18 +81,18 @@ class Finance::BalancingController < Finance::BaseController @order = Order.find(params[:id]) @type = FinancialTransactionType.find_by_id(params.permit(:type)[:type]) @order.close!(@current_user, @type) - redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice') - rescue => error - redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message) + redirect_to finance_order_index_url, notice: t('.notice') + rescue StandardError => e + redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.message) end # Close the order directly, without automaticly updating ordergroups account balances def close_direct @order = Order.find(params[:id]) @order.close_direct!(@current_user) - redirect_to finance_order_index_url, notice: t('finance.balancing.close_direct.notice') - rescue => error - redirect_to finance_order_index_url, alert: t('finance.balancing.close_direct.alert', message: error.message) + redirect_to finance_order_index_url, notice: t('.notice') + rescue StandardError => e + redirect_to finance_order_index_url, alert: t('.alert', message: e.message) end def close_all_direct_with_invoice @@ -103,8 +103,8 @@ class Finance::BalancingController < Finance::BaseController count += 1 end end - redirect_to finance_order_index_url, notice: t('finance.balancing.close_all_direct_with_invoice.notice', count: count) - rescue => error - redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: error.message) + redirect_to finance_order_index_url, notice: t('.notice', count: count) + rescue StandardError => e + redirect_to finance_order_index_url, alert: t('errors.general_msg', msg: e.message) end end diff --git a/app/controllers/finance/bank_accounts_controller.rb b/app/controllers/finance/bank_accounts_controller.rb index 66d9fddd..81403f6a 100644 --- a/app/controllers/finance/bank_accounts_controller.rb +++ b/app/controllers/finance/bank_accounts_controller.rb @@ -8,8 +8,8 @@ class Finance::BankAccountsController < Finance::BaseController @bank_account = BankAccount.find(params[:id]) count = @bank_account.assign_unlinked_transactions redirect_to finance_bank_account_transactions_url(@bank_account), notice: t('.notice', count: count) - rescue => error - redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to finance_bank_account_transactions_url(@bank_account), alert: t('errors.general_msg', msg: e.message) end def import @@ -33,8 +33,8 @@ class Finance::BankAccountsController < Finance::BaseController end needs_redirect = ok - rescue => error - flash.alert = t('errors.general_msg', msg: error.message) + rescue StandardError => e + flash.alert = t('errors.general_msg', msg: e.message) needs_redirect = true ensure return unless needs_redirect diff --git a/app/controllers/finance/bank_transactions_controller.rb b/app/controllers/finance/bank_transactions_controller.rb index 53c35168..22b38d06 100644 --- a/app/controllers/finance/bank_transactions_controller.rb +++ b/app/controllers/finance/bank_transactions_controller.rb @@ -3,26 +3,30 @@ class Finance::BankTransactionsController < ApplicationController inherit_resources def index - if params["sort"] - sort = case params["sort"] - when "date" then "date" - when "amount" then "amount" - when "financial_link" then "financial_link_id" - when "date_reverse" then "date DESC" - when "amount_reverse" then "amount DESC" - when "financial_link_reverse" then "financial_link_id DESC" + sort = if params['sort'] + case params['sort'] + when 'date' then 'date' + when 'amount' then 'amount' + when 'financial_link' then 'financial_link_id' + when 'date_reverse' then 'date DESC' + when 'amount_reverse' then 'amount DESC' + when 'financial_link_reverse' then 'financial_link_id DESC' end - else - sort = "date DESC" - end + else + 'date DESC' + end @bank_account = BankAccount.find(params[:bank_account_id]) @bank_transactions_all = @bank_account.bank_transactions.order(sort).includes(:financial_link) - @bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%", "%#{params[:query]}%") unless params[:query].nil? + unless params[:query].nil? + @bank_transactions_all = @bank_transactions_all.where('reference LIKE ? OR text LIKE ?', "%#{params[:query]}%", + "%#{params[:query]}%") + end @bank_transactions = @bank_transactions_all.page(params[:page]).per(@per_page) respond_to do |format| - format.js; format.html { render } + format.js + format.html { render } format.csv do send_data BankTransactionsCsv.new(@bank_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv' end diff --git a/app/controllers/finance/financial_links_controller.rb b/app/controllers/finance/financial_links_controller.rb index 17d8399a..c78a79b3 100644 --- a/app/controllers/finance/financial_links_controller.rb +++ b/app/controllers/finance/financial_links_controller.rb @@ -1,5 +1,5 @@ class Finance::FinancialLinksController < Finance::BaseController - before_action :find_financial_link, except: [:create, :incomplete] + before_action :find_financial_link, except: %i[create incomplete] def show @items = @financial_link.bank_transactions.map do |bt| @@ -37,7 +37,7 @@ class Finance::FinancialLinksController < Finance::BaseController def create @financial_link = FinancialLink.first_unused_or_create - if params[:bank_transaction] then + if params[:bank_transaction] bank_transaction = BankTransaction.find(params[:bank_transaction]) bank_transaction.update_attribute :financial_link, @financial_link end @@ -72,14 +72,16 @@ class Finance::FinancialLinksController < Finance::BaseController def create_financial_transaction financial_transaction = FinancialTransaction.new(financial_transaction_params) - financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount, financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link + financial_transaction.ordergroup.add_financial_transaction! financial_transaction.amount, + financial_transaction.note, current_user, financial_transaction.financial_transaction_type, @financial_link redirect_to finance_link_url(@financial_link), notice: t('.notice') - rescue => error - redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: error) + rescue StandardError => e + redirect_to finance_link_url(@financial_link), alert: t('errors.general_msg', msg: e) end def index_financial_transaction - @financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type, :ordergroup) + @financial_transactions = FinancialTransaction.without_financial_link.includes(:financial_transaction_type, + :ordergroup) end def add_financial_transaction @@ -123,7 +125,7 @@ class Finance::FinancialLinksController < Finance::BaseController end def find_best_fitting_ordergroup_id_for_financial_link(financial_link_id) - FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pluck(:ordergroup_id).first + FinancialTransaction.joins(<<-SQL).order(created_on: :desc).pick(:ordergroup_id) JOIN bank_transactions a ON financial_transactions.financial_link_id = a.financial_link_id JOIN bank_transactions b ON a.iban = b.iban AND b.financial_link_id = #{financial_link_id.to_i} SQL diff --git a/app/controllers/finance/financial_transactions_controller.rb b/app/controllers/finance/financial_transactions_controller.rb index 930acebe..6b06cbee 100644 --- a/app/controllers/finance/financial_transactions_controller.rb +++ b/app/controllers/finance/financial_transactions_controller.rb @@ -1,24 +1,24 @@ class Finance::FinancialTransactionsController < ApplicationController before_action :authenticate_finance - before_action :find_ordergroup, :except => [:new_collection, :create_collection, :index_collection] + before_action :find_ordergroup, except: %i[new_collection create_collection index_collection] inherit_resources # belongs_to :ordergroup def index - if params['sort'] - sort = case params['sort'] - when "date" then "created_on" - when "note" then "note" - when "amount" then "amount" - when "date_reverse" then "created_on DESC" - when "note_reverse" then "note DESC" - when "amount_reverse" then "amount DESC" + sort = if params['sort'] + case params['sort'] + when 'date' then 'created_on' + when 'note' then 'note' + when 'amount' then 'amount' + when 'date_reverse' then 'created_on DESC' + when 'note_reverse' then 'note DESC' + when 'amount_reverse' then 'amount DESC' end - else - sort = "created_on DESC" - end + else + 'created_on DESC' + end - @q = FinancialTransaction.search(params[:q]) + @q = FinancialTransaction.ransack(params[:q]) @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.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) respond_to do |format| - format.js; format.html { render } + format.js + format.html { render } format.csv do - send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv', type: 'text/csv' + send_data FinancialTransactionsCsv.new(@financial_transactions_all).to_csv, filename: 'transactions.csv', + type: 'text/csv' end end end @@ -38,11 +40,11 @@ class Finance::FinancialTransactionsController < ApplicationController end def new - if @ordergroup - @financial_transaction = @ordergroup.financial_transactions.build - else - @financial_transaction = FinancialTransaction.new - end + @financial_transaction = if @ordergroup + @ordergroup.financial_transactions.build + else + FinancialTransaction.new + end end def create @@ -53,16 +55,18 @@ class Finance::FinancialTransactionsController < ApplicationController else @financial_transaction.save! end - redirect_to finance_group_transactions_path(@ordergroup), notice: I18n.t('finance.financial_transactions.controller.create.notice') - rescue ActiveRecord::RecordInvalid => error - flash.now[:alert] = error.message - render :action => :new + redirect_to finance_group_transactions_path(@ordergroup), + notice: I18n.t('finance.financial_transactions.controller.create.notice') + rescue ActiveRecord::RecordInvalid => e + flash.now[:alert] = e.message + render action: :new end def destroy transaction = FinancialTransaction.find(params[:id]) transaction.revert!(current_user) - redirect_to finance_group_transactions_path(transaction.ordergroup), notice: t('finance.financial_transactions.controller.destroy.notice') + redirect_to finance_group_transactions_path(transaction.ordergroup), + notice: t('finance.financial_transactions.controller.destroy.notice') end def new_collection @@ -88,17 +92,17 @@ class Finance::FinancialTransactionsController < ApplicationController params[:financial_transactions].each do |trans| # ignore empty amount fields ... - unless trans[:amount].blank? - amount = LocalizeInput.parse(trans[:amount]).to_f - note = params[:note] - ordergroup = Ordergroup.find(trans[:ordergroup_id]) - if params[:set_balance] - note += " (#{amount})" - amount -= ordergroup.financial_transaction_class_balance(type.financial_transaction_class) - end - ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link) - foodcoop_amount -= amount + next if trans[:amount].blank? + + amount = LocalizeInput.parse(trans[:amount]).to_f + note = params[:note] + ordergroup = Ordergroup.find(trans[:ordergroup_id]) + if params[:set_balance] + note += " (#{amount})" + amount -= ordergroup.financial_transaction_class_balance(type.financial_transaction_class) end + ordergroup.add_financial_transaction!(amount, note, @current_user, type, financial_link) + foodcoop_amount -= amount end if params[:create_foodcoop_transaction] @@ -107,7 +111,7 @@ class Finance::FinancialTransactionsController < ApplicationController user: @current_user, amount: foodcoop_amount, note: params[:note], - financial_link: financial_link, + financial_link: financial_link }) ft.save! end @@ -117,8 +121,8 @@ class Finance::FinancialTransactionsController < ApplicationController url = financial_link ? finance_link_url(financial_link.id) : finance_ordergroups_url redirect_to url, notice: I18n.t('finance.financial_transactions.controller.create_collection.notice') - rescue => error - flash.now[:alert] = error.message + rescue StandardError => e + flash.now[:alert] = e.message render action: :new_collection end diff --git a/app/controllers/finance/invoices_controller.rb b/app/controllers/finance/invoices_controller.rb index d981277b..d70b92ec 100644 --- a/app/controllers/finance/invoices_controller.rb +++ b/app/controllers/finance/invoices_controller.rb @@ -1,15 +1,16 @@ class Finance::InvoicesController < ApplicationController before_action :authenticate_finance_or_invoices - before_action :find_invoice, only: [:show, :edit, :update, :destroy] - before_action :ensure_can_edit, only: [:edit, :update, :destroy] + before_action :find_invoice, only: %i[show edit update destroy] + before_action :ensure_can_edit, only: %i[edit update destroy] def index @invoices_all = Invoice.includes(:supplier, :deliveries, :orders).order('date DESC') @invoices = @invoices_all.page(params[:page]).per(@per_page) respond_to do |format| - format.js; format.html { render } + format.js + format.html { render } format.csv do send_data InvoicesCsv.new(@invoices_all).to_csv, filename: 'invoices.csv', type: 'text/csv' end @@ -20,11 +21,10 @@ class Finance::InvoicesController < ApplicationController @suppliers = Supplier.includes(:invoices).where('invoices.paid_on IS NULL').references(:invoices) end - def show - end + def show; end def new - @invoice = Invoice.new :supplier_id => params[:supplier_id] + @invoice = Invoice.new supplier_id: params[:supplier_id] @invoice.deliveries << Delivery.find_by_id(params[:delivery_id]) if params[:delivery_id] @invoice.orders << Order.find_by_id(params[:order_id]) if params[:order_id] fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id @@ -36,12 +36,14 @@ class Finance::InvoicesController < ApplicationController def form_on_supplier_id_change fill_deliveries_and_orders_collection params[:invoice_id], params[:supplier_id] - render :layout => false + render layout: false end def fill_deliveries_and_orders_collection(invoice_id, supplier_id) - @deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(date: :desc).limit(25) - @orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, supplier_id).order(ends: :desc).limit(25) + @deliveries_collection = Delivery.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, + supplier_id).order(date: :desc).limit(25) + @orders_collection = Order.where('invoice_id = ? OR (invoice_id IS NULL AND supplier_id = ?)', invoice_id, + supplier_id).order(ends: :desc).limit(25) end def create @@ -58,7 +60,7 @@ class Finance::InvoicesController < ApplicationController end else fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id - render :action => "new" + render action: 'new' end end @@ -81,7 +83,7 @@ class Finance::InvoicesController < ApplicationController @invoice = Invoice.find(params[:invoice_id]) type = MIME::Types[@invoice.attachment_mime].first filename = "invoice_#{@invoice.id}_attachment.#{type.preferred_extension}" - send_data(@invoice.attachment_data, :filename => filename, :type => type) + send_data(@invoice.attachment_data, filename: filename, type: type) end private @@ -92,8 +94,8 @@ class Finance::InvoicesController < ApplicationController # Returns true if @current_user can edit the invoice.. def ensure_can_edit - unless @invoice.user_can_edit?(current_user) - deny_access - end + return if @invoice.user_can_edit?(current_user) + + deny_access end end diff --git a/app/controllers/finance/ordergroups_controller.rb b/app/controllers/finance/ordergroups_controller.rb index cb661571..58ba0c36 100644 --- a/app/controllers/finance/ordergroups_controller.rb +++ b/app/controllers/finance/ordergroups_controller.rb @@ -1,17 +1,20 @@ class Finance::OrdergroupsController < Finance::BaseController def index - m = /^(?name|sum_of_class_\d+)(?_reverse)?$/.match params["sort"] + m = /^(?name|sum_of_class_\d+)(?_reverse)?$/.match params['sort'] if m sort = m[:col] sort += ' DESC' if m[:reverse] else - sort = "name" + sort = 'name' end @ordergroups = Ordergroup.undeleted.order(sort) @ordergroups = @ordergroups.include_transaction_class_sum @ordergroups = @ordergroups.where('groups.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil? - @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 } + end end end diff --git a/app/controllers/foodcoop/ordergroups_controller.rb b/app/controllers/foodcoop/ordergroups_controller.rb index 6940a376..05dfe9cb 100644 --- a/app/controllers/foodcoop/ordergroups_controller.rb +++ b/app/controllers/foodcoop/ordergroups_controller.rb @@ -1,20 +1,16 @@ class Foodcoop::OrdergroupsController < ApplicationController def index - @ordergroups = Ordergroup.undeleted.sort_by_param(params["sort"]) + @ordergroups = Ordergroup.undeleted.sort_by_param(params['sort']) - unless params[:name].blank? # Search by name - @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%") - end + @ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:name]}%") if params[:name].present? # Search by name - if params[:only_active] # Select only active groups - @ordergroups = @ordergroups.active - end + @ordergroups = @ordergroups.active if params[:only_active] # Select only active groups @ordergroups = @ordergroups.page(params[:page]).per(@per_page) respond_to do |format| format.html # index.html.erb - format.js { render :layout => false } + format.js { render layout: false } end end end diff --git a/app/controllers/foodcoop/users_controller.rb b/app/controllers/foodcoop/users_controller.rb index 196f1be8..5dfe0c6f 100644 --- a/app/controllers/foodcoop/users_controller.rb +++ b/app/controllers/foodcoop/users_controller.rb @@ -1,19 +1,22 @@ class Foodcoop::UsersController < ApplicationController + before_action -> { require_config_disabled :disable_members_overview } + def index - @users = User.undeleted.sort_by_param(params["sort"]) + @users = User.undeleted.sort_by_param(params['sort']) # if somebody uses the search field: - @users = @users.natural_search(params[:user_name]) unless params[:user_name].blank? + @users = @users.natural_search(params[:user_name]) if params[:user_name].present? if params[:ordergroup_name] - @users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", "%#{params[:ordergroup_name]}%") + @users = @users.joins(:groups).where("groups.type = 'Ordergroup' AND groups.name LIKE ?", + "%#{params[:ordergroup_name]}%") end @users = @users.page(params[:page]).per(@per_page) respond_to do |format| format.html # index.html.haml - format.js { render :layout => false } # index.js.erb + format.js { render layout: false } # index.js.erb end end end diff --git a/app/controllers/foodcoop/workgroups_controller.rb b/app/controllers/foodcoop/workgroups_controller.rb index e0f571be..8fd5f423 100644 --- a/app/controllers/foodcoop/workgroups_controller.rb +++ b/app/controllers/foodcoop/workgroups_controller.rb @@ -1,9 +1,9 @@ class Foodcoop::WorkgroupsController < ApplicationController before_action :authenticate_membership_or_admin, - :except => [:index] + except: [:index] def index - @workgroups = Workgroup.order("name") + @workgroups = Workgroup.order('name') end def edit @@ -13,9 +13,9 @@ class Foodcoop::WorkgroupsController < ApplicationController def update @workgroup = Workgroup.find(params[:id]) if @workgroup.update(params[:workgroup]) - redirect_to foodcoop_workgroups_url, :notice => I18n.t('workgroups.update.notice') + redirect_to foodcoop_workgroups_url, notice: I18n.t('workgroups.update.notice') else - render :action => 'edit' + render action: 'edit' end end end diff --git a/app/controllers/group_order_articles_controller.rb b/app/controllers/group_order_articles_controller.rb index 5aa50a87..5f58c48a 100644 --- a/app/controllers/group_order_articles_controller.rb +++ b/app/controllers/group_order_articles_controller.rb @@ -1,6 +1,6 @@ class GroupOrderArticlesController < ApplicationController before_action :authenticate_finance - before_action :find_group_order_article, except: [:new, :create] + before_action :find_group_order_article, except: %i[new create] layout false # We only use this controller to server js snippets, no need for layout rendering diff --git a/app/controllers/group_orders_controller.rb b/app/controllers/group_orders_controller.rb index 686f0617..e5a442aa 100644 --- a/app/controllers/group_orders_controller.rb +++ b/app/controllers/group_orders_controller.rb @@ -3,9 +3,9 @@ class GroupOrdersController < ApplicationController # Security before_action :ensure_ordergroup_member - before_action :ensure_open_order, :only => [:new, :create, :edit, :update, :order, :stock_order, :saveOrder] - before_action :ensure_my_group_order, only: [:show, :edit, :update] - before_action :enough_apples?, only: [:new, :create] + before_action :ensure_open_order, only: %i[new create edit update order stock_order saveOrder] + before_action :ensure_my_group_order, only: %i[show edit update] + before_action :enough_apples?, only: %i[new create] # Index page. def index @@ -13,9 +13,17 @@ class GroupOrdersController < ApplicationController @finished_not_closed_orders_including_group_order = Order.finished_not_closed.ordergroup_group_orders_map(@ordergroup) end + def show + @order = @group_order.order + end + def new ordergroup = params[:stock_order] ? nil : @ordergroup - @group_order = @order.group_orders.build(:ordergroup => ordergroup, :updated_by => current_user) + @group_order = @order.group_orders.build(ordergroup: ordergroup, updated_by: current_user) + @ordering_data = @group_order.load_data + end + + def edit @ordering_data = @group_order.load_data end @@ -23,34 +31,26 @@ class GroupOrdersController < ApplicationController @group_order = GroupOrder.new(params[:group_order]) begin @group_order.save_ordering! - redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.create.notice') + redirect_to group_order_url(@group_order), notice: I18n.t('group_orders.create.notice') rescue ActiveRecord::StaleObjectError - redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_stale') - rescue => exception - logger.error('Failed to update order: ' + exception.message) - redirect_to group_orders_url, :alert => I18n.t('group_orders.create.error_general') + redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_stale') + rescue StandardError => e + logger.error('Failed to update order: ' + e.message) + redirect_to group_orders_url, alert: I18n.t('group_orders.create.error_general') end end - def show - @order = @group_order.order - end - - def edit - @ordering_data = @group_order.load_data - end - def update @group_order.attributes = params[:group_order] @group_order.updated_by = current_user begin @group_order.save_ordering! - redirect_to group_order_url(@group_order), :notice => I18n.t('group_orders.update.notice') + redirect_to group_order_url(@group_order), notice: I18n.t('group_orders.update.notice') rescue ActiveRecord::StaleObjectError - redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_stale') - rescue => exception - logger.error('Failed to update order: ' + exception.message) - redirect_to group_orders_url, :alert => I18n.t('group_orders.update.error_general') + redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_stale') + rescue StandardError => e + logger.error('Failed to update order: ' + e.message) + redirect_to group_orders_url, alert: I18n.t('group_orders.update.error_general') end end @@ -74,16 +74,16 @@ class GroupOrdersController < ApplicationController # Used as a :before_action by OrdersController. def ensure_ordergroup_member @ordergroup = @current_user.ordergroup - if @ordergroup.nil? - redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member') - end + return unless @ordergroup.nil? + + redirect_to root_url, alert: I18n.t('group_orders.errors.no_member') end def ensure_open_order - @order = Order.includes([:supplier, :order_articles]).find(order_id_param) + @order = Order.includes(%i[supplier order_articles]).find(order_id_param) unless @order.open? flash[:notice] = I18n.t('group_orders.errors.closed') - redirect_to :action => 'index' + redirect_to action: 'index' end rescue ActiveRecord::RecordNotFound redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound') @@ -91,17 +91,17 @@ class GroupOrdersController < ApplicationController def ensure_my_group_order @group_order = GroupOrder.find(params[:id]) - if @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?) - redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound') - end + return unless @group_order.ordergroup != @ordergroup && (@group_order.ordergroup || !current_user.role_orders?) + + redirect_to group_orders_url, alert: I18n.t('group_orders.errors.notfound') end def enough_apples? - if @ordergroup.not_enough_apples? - redirect_to group_orders_url, - alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples, - stop_ordering_under: FoodsoftConfig[:stop_ordering_under]) - end + return unless @ordergroup.not_enough_apples? + + redirect_to group_orders_url, + alert: t('not_enough_apples', scope: 'group_orders.messages', apples: @ordergroup.apples, + stop_ordering_under: FoodsoftConfig[:stop_ordering_under]) end def order_id_param diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 86f9e2eb..f40fb6fb 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -9,8 +9,7 @@ class HomeController < ApplicationController @unassigned_tasks = Task.order(:due_date).next_unassigned_tasks_for(current_user) end - def profile - end + def profile; end def reference_calculator if current_user.ordergroup @@ -36,40 +35,43 @@ class HomeController < ApplicationController @user = @current_user @ordergroup = @user.ordergroup - unless @ordergroup.nil? + if @ordergroup.nil? + redirect_to root_path, alert: I18n.t('home.no_ordergroups') + else @ordergroup = Ordergroup.include_transaction_class_sum.find(@ordergroup.id) - if params['sort'] - sort = case params['sort'] - when "date" then "created_on" - when "note" then "note" - when "amount" then "amount" - when "date_reverse" then "created_on DESC" - when "note_reverse" then "note DESC" - when "amount_reverse" then "amount DESC" + sort = if params['sort'] + case params['sort'] + when 'date' then 'created_on' + when 'note' then 'note' + when 'amount' then 'amount' + when 'date_reverse' then 'created_on DESC' + when 'note_reverse' then 'note DESC' + when 'amount_reverse' then 'amount DESC' end - else - sort = "created_on DESC" - end + else + 'created_on DESC' + end @financial_transactions = @ordergroup.financial_transactions.visible.page(params[:page]).per(@per_page).order(sort) - @financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?', "%#{params[:query]}%") if params[:query].present? + if params[:query].present? + @financial_transactions = @financial_transactions.where('financial_transactions.note LIKE ?', + "%#{params[:query]}%") + end - else - redirect_to root_path, alert: I18n.t('home.no_ordergroups') end end # cancel personal memberships direct from the myProfile-page def cancel_membership - if params[:membership_id] - membership = @current_user.memberships.find!(params[:membership_id]) - else - membership = @current_user.memberships.find_by_group_id!(params[:group_id]) - end + membership = if params[:membership_id] + @current_user.memberships.find(params[:membership_id]) + else + @current_user.memberships.find_by_group_id!(params[:group_id]) + end membership.destroy - redirect_to my_profile_path, notice: I18n.t('home.ordergroup_cancelled', :group => membership.group.name) + redirect_to my_profile_path, notice: I18n.t('home.ordergroup_cancelled', group: membership.group.name) end protected @@ -82,8 +84,8 @@ class HomeController < ApplicationController end def ordergroup_params - if params[:user][:ordergroup] - params.require(:user).require(:ordergroup).permit(:contact_address) - end + return unless params[:user][:ordergroup] + + params.require(:user).require(:ordergroup).permit(:contact_address) end end diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 37fc757b..266a1de5 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -3,7 +3,7 @@ class InvitesController < ApplicationController before_action -> { require_config_disabled :disable_invite } def new - @invite = Invite.new(:user => @current_user, :group => @group) + @invite = Invite.new(user: @current_user, group: @group) end def create @@ -27,6 +27,10 @@ class InvitesController < ApplicationController protected def authenticate_membership_or_admin_for_invites - authenticate_membership_or_admin((params[:invite][:group_id] rescue params[:id])) + authenticate_membership_or_admin(begin + params[:invite][:group_id] + rescue StandardError + params[:id] + end) end end diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index 052231c5..4c2fd95b 100644 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -1,6 +1,6 @@ class LoginController < ApplicationController skip_before_action :authenticate # no authentication since this is the login page - before_action :validate_token, :only => [:new_password, :update_password] + before_action :validate_token, only: %i[new_password update_password] # Display the form to enter an email address requesting a token to set a new password. def forgot_password @@ -9,20 +9,17 @@ class LoginController < ApplicationController # Sends an email to a user with the token that allows setting a new password through action "password". def reset_password - if request.get? || params[:user].nil? # Catch for get request and give better error message. - redirect_to forgot_password_url, alert: I18n.t('errors.general_again') and return - end + redirect_to forgot_password_url, alert: I18n.t('errors.general_again') and return if request.get? || params[:user].nil? # Catch for get request and give better error message. if (user = User.undeleted.find_by_email(params[:user][:email])) user.request_password_reset! end - redirect_to login_url, :notice => I18n.t('login.controller.reset_password.notice') + redirect_to login_url, notice: I18n.t('login.controller.reset_password.notice') end # Set a new password with a token from the password reminder email. # Called with params :id => User.id and :token => User.reset_password_token to specify a new password. - def new_password - end + def new_password; end # Sets a new password. # Called with params :id => User.id and :token => User.reset_password_token to specify a new password. @@ -32,7 +29,7 @@ class LoginController < ApplicationController @user.reset_password_token = nil @user.reset_password_expires = nil @user.save - redirect_to login_url, :notice => I18n.t('login.controller.update_password.notice') + redirect_to login_url, notice: I18n.t('login.controller.update_password.notice') else render :new_password end @@ -50,14 +47,14 @@ class LoginController < ApplicationController @user = User.new(params[:user]) @user.email = @invite.email if @user.save - Membership.new(:user => @user, :group => @invite.group).save! + Membership.new(user: @user, group: @invite.group).save! @invite.destroy session[:locale] = @user.locale redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice') end end else - @user = User.new(:email => @invite.email) + @user = User.new(email: @invite.email) end end @@ -65,8 +62,8 @@ class LoginController < ApplicationController def validate_token @user = User.find_by_id_and_reset_password_token(params[:id], params[:token]) - if (@user.nil? || @user.reset_password_expires < Time.now) - redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid') - end + return unless @user.nil? || @user.reset_password_expires < Time.now + + redirect_to forgot_password_url, alert: I18n.t('login.controller.error_token_invalid') end end diff --git a/app/controllers/order_articles_controller.rb b/app/controllers/order_articles_controller.rb index 0552269d..43a0ea14 100644 --- a/app/controllers/order_articles_controller.rb +++ b/app/controllers/order_articles_controller.rb @@ -1,7 +1,7 @@ class OrderArticlesController < ApplicationController before_action :fetch_order, except: :destroy - before_action :authenticate_finance_or_invoices, except: [:new, :create] - before_action :authenticate_finance_orders_or_pickup, except: [:edit, :update, :destroy] + before_action :authenticate_finance_or_invoices, except: %i[new create] + before_action :authenticate_finance_orders_or_pickup, except: %i[edit update destroy] layout false # We only use this controller to serve js snippets, no need for layout rendering @@ -9,28 +9,26 @@ class OrderArticlesController < ApplicationController @order_article = @order.order_articles.build(params[:order_article]) end + def edit + @order_article = OrderArticle.find(params[:id]) + end + def create # The article may be ordered with zero units - in that case do not complain. # If order_article is ordered and a new order_article is created, an error message will be # given mentioning that the article already exists, which is desired. - @order_article = @order.order_articles.where(:article_id => params[:order_article][:article_id]).first - unless @order_article && @order_article.units_to_order == 0 - @order_article = @order.order_articles.build(params[:order_article]) - end + @order_article = @order.order_articles.where(article_id: params[:order_article][:article_id]).first + @order_article = @order.order_articles.build(params[:order_article]) unless @order_article && @order_article.units_to_order == 0 @order_article.save! - rescue + rescue StandardError render action: :new end - def edit - @order_article = OrderArticle.find(params[:id]) - end - def update @order_article = OrderArticle.find(params[:id]) begin @order_article.update_article_and_price!(params[:order_article], params[:article], params[:article_price]) - rescue + rescue StandardError render action: :edit end end diff --git a/app/controllers/order_comments_controller.rb b/app/controllers/order_comments_controller.rb index 39067577..3583bb0e 100644 --- a/app/controllers/order_comments_controller.rb +++ b/app/controllers/order_comments_controller.rb @@ -1,15 +1,15 @@ class OrderCommentsController < ApplicationController def new @order = Order.find(params[:order_id]) - @order_comment = @order.comments.build(:user => current_user) + @order_comment = @order.comments.build(user: current_user) end def create @order_comment = OrderComment.new(params[:order_comment]) if @order_comment.save - render :layout => false + render layout: false else - render :action => :new, :layout => false + render action: :new, layout: false end end end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index cfa7cef6..bc2d9195 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -5,25 +5,26 @@ class OrdersController < ApplicationController include Concerns::SendOrderPdf before_action :authenticate_pickups_or_orders - before_action :authenticate_orders, except: [:receive, :receive_on_order_article_create, :receive_on_order_article_update, :show] - before_action :remove_empty_article, only: [:create, :update] + before_action :authenticate_orders, + except: %i[receive receive_on_order_article_create receive_on_order_article_update show] + before_action :remove_empty_article, only: %i[create update] # List orders def index @open_orders = Order.open.includes(:supplier) @finished_orders = Order.finished_not_closed.includes(:supplier) @per_page = 15 - if params['sort'] - sort = case params['sort'] - when "supplier" then "suppliers.name, ends DESC" - when "pickup" then "pickup DESC" - when "ends" then "ends DESC" - when "supplier_reverse" then "suppliers.name DESC" - when "ends_reverse" then "ends" + sort = if params['sort'] + case params['sort'] + when 'supplier' then 'suppliers.name, ends DESC' + when 'pickup' then 'pickup DESC' + when 'ends' then 'ends DESC' + when 'supplier_reverse' then 'suppliers.name DESC' + when 'ends_reverse' then 'ends' end - else - sort = "ends DESC" - end + else + 'ends DESC' + end @suppliers = Supplier.having_articles.order('suppliers.name') @orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page) end @@ -43,7 +44,7 @@ class OrdersController < ApplicationController respond_to do |format| format.html format.js do - render :layout => false + render layout: false end format.pdf do send_order_pdf @order, params[:document] @@ -66,8 +67,14 @@ class OrdersController < ApplicationController else @order = Order.new(supplier_id: params[:supplier_id]).init_dates end - rescue => error - redirect_to orders_url, alert: t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to orders_url, alert: t('errors.general_msg', msg: e.message) + end + + # Page to edit an exsiting order. + # editing finished orders is done in FinanceController + def edit + @order = Order.includes(:articles).find(params[:id]) end # Save a new order. @@ -81,31 +88,25 @@ class OrdersController < ApplicationController redirect_to @order else logger.debug "[debug] order errors: #{@order.errors.messages}" - render :action => 'new' + render action: 'new' end end - # Page to edit an exsiting order. - # editing finished orders is done in FinanceController - def edit - @order = Order.includes(:articles).find(params[:id]) - end - # Update an existing order. def update @order = Order.find params[:id] if @order.update(params[:order].merge(updated_by: current_user)) flash[:notice] = I18n.t('orders.update.notice') - redirect_to :action => 'show', :id => @order + redirect_to action: 'show', id: @order else - render :action => 'edit' + render action: 'edit' end end # Delete an order. def destroy Order.find(params[:id]).destroy - redirect_to :action => 'index' + redirect_to action: 'index' end # Finish a current order. @@ -113,8 +114,8 @@ class OrdersController < ApplicationController order = Order.find(params[:id]) order.finish!(@current_user) redirect_to order, notice: I18n.t('orders.finish.notice') - rescue => error - redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message) + rescue StandardError => e + redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message) end # Send a order to the supplier. @@ -122,20 +123,18 @@ class OrdersController < ApplicationController order = Order.find(params[:id]) order.send_to_supplier!(@current_user) redirect_to order, notice: I18n.t('orders.send_to_supplier.notice') - rescue => error - redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.message) + rescue StandardError => e + redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message) end def receive @order = Order.find(params[:id]) - unless request.post? - @order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name') - else + if request.post? Order.transaction do s = update_order_amounts @order.update_attribute(:state, 'received') if @order.state != 'received' - flash[:notice] = (s ? I18n.t('orders.receive.notice', :msg => s) : I18n.t('orders.receive.notice_none')) + flash[:notice] = (s ? I18n.t('orders.receive.notice', msg: s) : I18n.t('orders.receive.notice_none')) end NotifyReceivedOrderJob.perform_later(@order) if current_user.role_orders? || current_user.role_finance? @@ -145,23 +144,25 @@ class OrdersController < ApplicationController else redirect_to receive_order_path(@order) end + else + @order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name') end end def receive_on_order_article_create # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) - render :layout => false + render layout: false end def receive_on_order_article_update # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) - render :layout => false + render layout: false end protected def update_order_amounts - return if not params[:order_articles] + return unless params[:order_articles] # where to leave remainder during redistribution rest_to = [] @@ -176,35 +177,42 @@ class OrdersController < ApplicationController # "MySQL lock timeout exceeded" errors. It's ok to do # this article-by-article anway. params[:order_articles].each do |oa_id, oa_params| - unless oa_params.blank? - oa = OrderArticle.find(oa_id) - # update attributes; don't use update_attribute because it calls save - # which makes received_changed? not work anymore - oa.attributes = oa_params - if oa.units_received_changed? - counts[0] += 1 - unless oa.units_received.blank? - cunits[0] += oa.units_received * oa.article.unit_quantity - oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to - oacounts.each_with_index { |c, i| cunits[i + 1] += c; counts[i + 1] += 1 if c > 0 } + next if oa_params.blank? + + oa = OrderArticle.find(oa_id) + # update attributes; don't use update_attribute because it calls save + # which makes received_changed? not work anymore + oa.attributes = oa_params + if oa.units_received_changed? + counts[0] += 1 + if oa.units_received.present? + cunits[0] += oa.units_received * oa.article.unit_quantity + oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to + oacounts.each_with_index do |c, i| + cunits[i + 1] += c + counts[i + 1] += 1 if c > 0 end end - oa.save! end + oa.save! end return nil if counts[0] == 0 notice = [] notice << I18n.t('orders.update_order_amounts.msg1', count: counts[0], units: cunits[0]) - notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1], units: cunits[1]) if params[:rest_to_tolerance] + if params[:rest_to_tolerance] + notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1], + units: cunits[1]) + end notice << I18n.t('orders.update_order_amounts.msg3', count: counts[2], units: cunits[2]) if params[:rest_to_stock] if counts[3] > 0 || cunits[3] > 0 - notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3], units: cunits[3]) + notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3], + units: cunits[3]) end notice.join(', ') end def remove_empty_article - params[:order][:article_ids].reject!(&:blank?) if params[:order] && params[:order][:article_ids] + params[:order][:article_ids].compact_blank! if params[:order] && params[:order][:article_ids] end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5b3d0780..22750360 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -12,16 +12,20 @@ class SessionsController < ApplicationController user = User.authenticate(params[:nick], params[:password]) if user user.update_attribute(:last_login, Time.now) - login_and_redirect_to_return_to user, :notice => I18n.t('sessions.logged_in') + login_and_redirect_to_return_to user, notice: I18n.t('sessions.logged_in') else flash.now.alert = I18n.t(FoodsoftConfig[:use_nick] ? 'sessions.login_invalid_nick' : 'sessions.login_invalid_email') - render "new" + render 'new' end end def destroy logout - redirect_to login_url, :notice => I18n.t('sessions.logged_out') + if FoodsoftConfig[:logout_redirect_url].present? + redirect_to FoodsoftConfig[:logout_redirect_url] + else + redirect_to login_url, notice: I18n.t('sessions.logged_out') + end end # redirect to root, going to default foodcoop when none given diff --git a/app/controllers/stock_takings_controller.rb b/app/controllers/stock_takings_controller.rb index bdf1dc77..e12af6f9 100644 --- a/app/controllers/stock_takings_controller.rb +++ b/app/controllers/stock_takings_controller.rb @@ -7,21 +7,21 @@ class StockTakingsController < ApplicationController def new @stock_taking = StockTaking.new - StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(:stock_article => a) } + StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(stock_article: a) } end def new_on_stock_article_create # See publish/subscribe design pattern in /doc. stock_article = StockArticle.find(params[:stock_article_id]) - @stock_change = StockChange.new(:stock_article => stock_article) + @stock_change = StockChange.new(stock_article: stock_article) - render :layout => false + render layout: false end def create - create!(:notice => I18n.t('stock_takings.create.notice')) + create!(notice: I18n.t('stock_takings.create.notice')) end def update - update!(:notice => I18n.t('stock_takings.update.notice')) + update!(notice: I18n.t('stock_takings.update.notice')) end end diff --git a/app/controllers/stockit_controller.rb b/app/controllers/stockit_controller.rb index 6dd1511e..8f9b3b3d 100644 --- a/app/controllers/stockit_controller.rb +++ b/app/controllers/stockit_controller.rb @@ -7,57 +7,13 @@ class StockitController < ApplicationController def index_on_stock_article_create # See publish/subscribe design pattern in /doc. @stock_article = StockArticle.find(params[:id]) - render :layout => false + render layout: false end def index_on_stock_article_update # See publish/subscribe design pattern in /doc. @stock_article = StockArticle.find(params[:id]) - render :layout => false - end - - # three possibilites to fill a new_stock_article form - # (1) start from blank or use params - def new - @stock_article = StockArticle.new(params[:stock_article]) - - render :layout => false - end - - # (2) StockArticle as template - def copy - @stock_article = StockArticle.find(params[:stock_article_id]).dup - - render :layout => false - end - - # (3) non-stock Article as template - def derive - @stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup - - render :layout => false - end - - def create - @stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article])) - @stock_article.save! - render :layout => false - rescue ActiveRecord::RecordInvalid - render :action => 'new', :layout => false - end - - def edit - @stock_article = StockArticle.find(params[:id]) - - render :layout => false - end - - def update - @stock_article = StockArticle.find(params[:id]) - @stock_article.update!(params[:stock_article]) - render :layout => false - rescue ActiveRecord::RecordInvalid - render :action => 'edit', :layout => false + render layout: false end def show @@ -65,24 +21,68 @@ class StockitController < ApplicationController @stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC') end + # three possibilites to fill a new_stock_article form + # (1) start from blank or use params + def new + @stock_article = StockArticle.new(params[:stock_article]) + + render layout: false + end + + # (2) StockArticle as template + def copy + @stock_article = StockArticle.find(params[:stock_article_id]).dup + + render layout: false + end + + # (3) non-stock Article as template + def derive + @stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup + + render layout: false + end + + def edit + @stock_article = StockArticle.find(params[:id]) + + render layout: false + end + + def create + @stock_article = StockArticle.new({ quantity: 0 }.merge(params[:stock_article])) + @stock_article.save! + render layout: false + rescue ActiveRecord::RecordInvalid + render action: 'new', layout: false + end + + def update + @stock_article = StockArticle.find(params[:id]) + @stock_article.update!(params[:stock_article]) + render layout: false + rescue ActiveRecord::RecordInvalid + render action: 'edit', layout: false + end + def show_on_stock_article_update # See publish/subscribe design pattern in /doc. @stock_article = StockArticle.find(params[:id]) - render :layout => false + render layout: false end def destroy @stock_article = StockArticle.find(params[:id]) @stock_article.mark_as_deleted - render :layout => false - rescue => error - render :partial => "destroy_fail", :layout => false, - :locals => { :fail_msg => I18n.t('errors.general_msg', :msg => error.message) } + render layout: false + rescue StandardError => e + render partial: 'destroy_fail', layout: false, + locals: { fail_msg: I18n.t('errors.general_msg', msg: e.message) } end # TODO: Fix this!! def articles_search @articles = Article.not_in_stock.limit(8).where('name LIKE ?', "%#{params[:term]}%") - render :json => @articles.map(&:name) + render json: @articles.map(&:name) end end diff --git a/app/controllers/styles_controller.rb b/app/controllers/styles_controller.rb index 5636ec03..6d3a9fd1 100644 --- a/app/controllers/styles_controller.rb +++ b/app/controllers/styles_controller.rb @@ -9,7 +9,7 @@ class StylesController < ApplicationController def foodcoop css = FoodsoftConfig[:custom_css] if css.blank? - render body: nil, content_type: 'text/css', status: 404 + render body: nil, content_type: 'text/css', status: :not_found else expires_in 1.week, public: true if params[:md5].present? render body: css, content_type: 'text/css' diff --git a/app/controllers/suppliers_controller.rb b/app/controllers/suppliers_controller.rb index e5188f8b..1f1e055d 100644 --- a/app/controllers/suppliers_controller.rb +++ b/app/controllers/suppliers_controller.rb @@ -1,5 +1,5 @@ class SuppliersController < ApplicationController - before_action :authenticate_suppliers, :except => [:index, :list] + before_action :authenticate_suppliers, except: %i[index list] helper :deliveries def index @@ -24,6 +24,10 @@ class SuppliersController < ApplicationController end end + def edit + @supplier = Supplier.find(params[:id]) + end + def create @supplier = Supplier.new(supplier_params) @supplier.supplier_category ||= SupplierCategory.first @@ -31,21 +35,17 @@ class SuppliersController < ApplicationController flash[:notice] = I18n.t('suppliers.create.notice') redirect_to suppliers_path else - render :action => 'new' + render action: 'new' end end - def edit - @supplier = Supplier.find(params[:id]) - end - def update @supplier = Supplier.find(params[:id]) if @supplier.update(supplier_params) flash[:notice] = I18n.t('suppliers.update.notice') redirect_to @supplier else - render :action => 'edit' + render action: 'edit' end end @@ -54,8 +54,8 @@ class SuppliersController < ApplicationController @supplier.mark_as_deleted flash[:notice] = I18n.t('suppliers.destroy.notice') redirect_to suppliers_path - rescue => e - flash[:error] = I18n.t('errors.general_msg', :msg => e.message) + rescue StandardError => e + flash[:error] = I18n.t('errors.general_msg', msg: e.message) redirect_to @supplier end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index db4ca1ab..352c71ae 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -11,35 +11,33 @@ class TasksController < ApplicationController @accepted_tasks = Task.accepted_tasks_for(current_user) end - def new - @task = Task.new(current_user_id: current_user.id) - end - - def create - @task = Task.new(current_user_id: current_user.id) - @task.created_by = current_user - @task.attributes = (task_params) - if params[:periodic] - @task.periodic_task_group = PeriodicTaskGroup.new - end - if @task.save - @task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic] - redirect_to tasks_url, :notice => I18n.t('tasks.create.notice') - else - render :template => "tasks/new" - end - end - def show @task = Task.find(params[:id]) end + def new + @task = Task.new(current_user_id: current_user.id) + end + def edit @task = Task.find(params[:id]) @periodic = !!params[:periodic] @task.current_user_id = current_user.id end + def create + @task = Task.new(current_user_id: current_user.id) + @task.created_by = current_user + @task.attributes = (task_params) + @task.periodic_task_group = PeriodicTaskGroup.new if params[:periodic] + if @task.save + @task.periodic_task_group.create_tasks_for_upfront_days if params[:periodic] + redirect_to tasks_url, notice: I18n.t('tasks.create.notice') + else + render template: 'tasks/new' + end + end + def update @task = Task.find(params[:id]) task_group = @task.periodic_task_group @@ -50,16 +48,14 @@ class TasksController < ApplicationController if @task.errors.empty? && @task.save task_group.update_tasks_including(@task, prev_due_date) if params[:periodic] flash[:notice] = I18n.t('tasks.update.notice') - if was_periodic && !@task.periodic? - flash[:notice] = I18n.t('tasks.update.notice_converted') - end + flash[:notice] = I18n.t('tasks.update.notice_converted') if was_periodic && !@task.periodic? if @task.workgroup redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id) else redirect_to tasks_url end else - render :template => "tasks/edit" + render template: 'tasks/edit' end end @@ -75,7 +71,7 @@ class TasksController < ApplicationController end task.update_ordergroup_stats(user_ids) - redirect_to tasks_url, :notice => I18n.t('tasks.destroy.notice') + redirect_to tasks_url, notice: I18n.t('tasks.destroy.notice') end # assign current_user to the task and set the assignment to "accepted" @@ -85,20 +81,20 @@ class TasksController < ApplicationController if ass = task.is_assigned?(current_user) ass.update_attribute(:accepted, true) else - task.assignments.create(:user => current_user, :accepted => true) + task.assignments.create(user: current_user, accepted: true) end - redirect_to user_tasks_path, :notice => I18n.t('tasks.accept.notice') + redirect_to user_tasks_path, notice: I18n.t('tasks.accept.notice') end # deletes assignment between current_user and given taskcurrent_user_id: current_user.id def reject Task.find(params[:id]).users.delete(current_user) - redirect_to :action => "index" + redirect_to action: 'index' end def set_done Task.find(params[:id]).update_attribute :done, true - redirect_to tasks_url, :notice => I18n.t('tasks.set_done.notice') + redirect_to tasks_url, notice: I18n.t('tasks.set_done.notice') end # Shows all tasks, which are already done @@ -109,9 +105,9 @@ class TasksController < ApplicationController # shows workgroup (normal group) to edit weekly_tasks_template def workgroup @group = Group.find(params[:workgroup_id]) - if @group.is_a? Ordergroup - redirect_to tasks_url, :alert => I18n.t('tasks.error_not_found') - end + return unless @group.is_a? Ordergroup + + redirect_to tasks_url, alert: I18n.t('tasks.error_not_found') end private diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 503bc79b..df56ade0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,7 +3,7 @@ class UsersController < ApplicationController def index @users = User.undeleted.natural_search(params[:q]) respond_to do |format| - format.json { render :json => @users.map(&:token_attributes).to_json } + format.json { render json: @users.map(&:token_attributes).to_json } end end end diff --git a/app/documents/order_by_articles.rb b/app/documents/order_by_articles.rb index 84fb5c00..b1a68a11 100644 --- a/app/documents/order_by_articles.rb +++ b/app/documents/order_by_articles.rb @@ -1,11 +1,11 @@ class OrderByArticles < OrderPdf def filename - I18n.t('documents.order_by_articles.filename', :name => order.name, :date => order.ends.to_date) + '.pdf' + I18n.t('documents.order_by_articles.filename', name: order.name, date: order.ends.to_date) + '.pdf' end def title - I18n.t('documents.order_by_articles.title', :name => order.name, - :date => order.ends.strftime(I18n.t('date.formats.default'))) + I18n.t('documents.order_by_articles.title', name: order.name, + date: order.ends.strftime(I18n.t('date.formats.default'))) end def body diff --git a/app/documents/order_by_groups.rb b/app/documents/order_by_groups.rb index d6711731..e5a72c35 100644 --- a/app/documents/order_by_groups.rb +++ b/app/documents/order_by_groups.rb @@ -1,11 +1,11 @@ class OrderByGroups < OrderPdf def filename - I18n.t('documents.order_by_groups.filename', :name => order.name, :date => order.ends.to_date) + '.pdf' + I18n.t('documents.order_by_groups.filename', name: order.name, date: order.ends.to_date) + '.pdf' end def title - I18n.t('documents.order_by_groups.title', :name => order.name, - :date => order.ends.strftime(I18n.t('date.formats.default'))) + I18n.t('documents.order_by_groups.title', name: order.name, + date: order.ends.strftime(I18n.t('date.formats.default'))) end def body diff --git a/app/documents/order_fax.rb b/app/documents/order_fax.rb index b4b50577..e881c93f 100644 --- a/app/documents/order_fax.rb +++ b/app/documents/order_fax.rb @@ -2,7 +2,7 @@ class OrderFax < OrderPdf BATCH_SIZE = 250 def filename - I18n.t('documents.order_fax.filename', :name => order.name, :date => order.ends.to_date) + '.pdf' + I18n.t('documents.order_fax.filename', name: order.name, date: order.ends.to_date) + '.pdf' end def title @@ -20,16 +20,18 @@ class OrderFax < OrderPdf move_down 5 text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :right move_down 5 - unless order.supplier.try(:customer_number).blank? - text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", size: fontsize(9), align: :right + if order.supplier.try(:customer_number).present? + text "#{Supplier.human_attribute_name :customer_number}: #{order.supplier[:customer_number]}", + size: fontsize(9), align: :right move_down 5 end - unless contact[:phone].blank? + if contact[:phone].present? text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :right move_down 5 end - unless contact[:email].blank? - text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :right + if contact[:email].present? + text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), + align: :right end end @@ -38,7 +40,7 @@ class OrderFax < OrderPdf text order.name move_down 5 text order.supplier.try(:address).to_s - unless order.supplier.try(:fax).blank? + if order.supplier.try(:fax).present? move_down 5 text "#{Supplier.human_attribute_name :fax}: #{order.supplier[:fax]}" end @@ -50,7 +52,7 @@ class OrderFax < OrderPdf move_down 10 text "#{Delivery.human_attribute_name :date}:" move_down 10 - unless order.supplier.try(:contact_person).blank? + if order.supplier.try(:contact_person).present? text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}" move_down 10 end @@ -78,8 +80,8 @@ class OrderFax < OrderPdf table.row(0).border_bottom_width = 2 table.columns(1).align = :right table.columns(3..6).align = :right - table.row(data.length - 1).columns(0..5).borders = [:top, :bottom] - table.row(data.length - 1).columns(0).borders = [:top, :bottom, :left] + table.row(data.length - 1).columns(0..5).borders = %i[top bottom] + table.row(data.length - 1).columns(0).borders = %i[top bottom left] table.row(data.length - 1).border_top_width = 2 end # font_size: fontsize(8), @@ -98,7 +100,7 @@ class OrderFax < OrderPdf .preload(:article, :article_price) end - def each_order_article - order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa } + def each_order_article(&block) + order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block) end end diff --git a/app/documents/order_matrix.rb b/app/documents/order_matrix.rb index 7269feaf..c45ca5fd 100644 --- a/app/documents/order_matrix.rb +++ b/app/documents/order_matrix.rb @@ -3,12 +3,12 @@ class OrderMatrix < OrderPdf PLACEHOLDER_CHAR = 'X' def filename - I18n.t('documents.order_matrix.filename', :name => @order.name, :date => @order.ends.to_date) + '.pdf' + I18n.t('documents.order_matrix.filename', name: @order.name, date: @order.ends.to_date) + '.pdf' end def title - I18n.t('documents.order_matrix.title', :name => @order.name, - :date => @order.ends.strftime(I18n.t('date.formats.default'))) + I18n.t('documents.order_matrix.title', name: @order.name, + date: @order.ends.strftime(I18n.t('date.formats.default'))) end def body @@ -87,7 +87,7 @@ class OrderMatrix < OrderPdf table.cells.border_width = 0.5 table.cells.border_color = '666666' - table.row(0).borders = [:bottom, :left] + table.row(0).borders = %i[bottom left] table.row(0).padding = [2, 0, 2, 0] table.row(1..-1).height = row_height_1 table.column(0..1).borders = [] @@ -106,7 +106,7 @@ class OrderMatrix < OrderPdf table.column(2 + idx).border_width = 2 end - table.row_colors = ['dddddd', 'ffffff'] + table.row_colors = %w[dddddd ffffff] end first_page = false diff --git a/app/helpers/admin/configs_helper.rb b/app/helpers/admin/configs_helper.rb index 0185a0df..3c1da9f0 100644 --- a/app/helpers/admin/configs_helper.rb +++ b/app/helpers/admin/configs_helper.rb @@ -28,7 +28,11 @@ module Admin::ConfigsHelper options[:default] = options[:input_html].delete(:value) return form.input key, options, &block end - block ||= proc { config_input_field form, key, options.merge(options[:input_html]) } if options[:as] == :select_recurring + if options[:as] == :select_recurring + block ||= proc { + config_input_field form, key, options.merge(options[:input_html]) + } + end form.input key, options, &block end @@ -57,11 +61,12 @@ module Admin::ConfigsHelper unchecked_value = options.delete(:unchecked_value) || 'false' options[:checked] = 'checked' if v = options.delete(:value) && v != 'false' # different key for hidden field so that allow clocking on label focuses the control - form.hidden_field(key, id: "#{key}_", value: unchecked_value, as: :hidden) + form.check_box(key, options, checked_value, false) + form.hidden_field(key, id: "#{key}_", value: unchecked_value, + as: :hidden) + form.check_box(key, options, checked_value, false) elsif options[:as] == :select_recurring options[:value] = FoodsoftDateUtil.rule_from(options[:value]) options[:rules] ||= [] - options[:rules].unshift options[:value] unless options[:value].blank? + options[:rules].unshift options[:value] if options[:value].present? options[:rules].push [I18n.t('recurring_select.not_recurring'), '{}'] if options.delete(:allow_blank) # blank after current value form.select_recurring key, options.delete(:rules).uniq, options else @@ -73,7 +78,7 @@ module Admin::ConfigsHelper # @param form [ActionView::Helpers::FormBuilder] Form object. # @param key [Symbol, String] Configuration key of a boolean (e.g. +use_messages+). # @option options [String] :label Label to show - def config_use_heading(form, key, options = {}) + def config_use_heading(form, key, options = {}, &block) head = content_tag :label do lbl = options[:label] || config_input_label(form, key) field = config_input_field(form, key, as: :boolean, boolean_style: :inline, @@ -83,9 +88,7 @@ module Admin::ConfigsHelper content_tag :span, (lbl + field).html_safe, config_input_tooltip_options(form, key, {}) end end - fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}") do - yield - end + fields = content_tag(:fieldset, id: "#{key}-fields", class: "collapse#{' in' if @cfg[key]}", &block) head + fields end @@ -127,7 +130,7 @@ module Admin::ConfigsHelper # tooltip with help info to the right cfg_path = form.lookup_model_names[1..-1] + [key] tooltip = I18n.t("config.hints.#{cfg_path.map(&:to_s).join('.')}", default: '') - unless tooltip.blank? + if tooltip.present? options[:data] ||= {} options[:data][:toggle] ||= 'tooltip' options[:data][:placement] ||= 'right' diff --git a/app/helpers/admin/ordergroups_helper.rb b/app/helpers/admin/ordergroups_helper.rb index e74fdde5..ecb4bd39 100644 --- a/app/helpers/admin/ordergroups_helper.rb +++ b/app/helpers/admin/ordergroups_helper.rb @@ -2,9 +2,7 @@ module Admin::OrdergroupsHelper def ordergroup_members_title(ordergroup) s = '' s += ordergroup.users.map(&:name).join(', ') if ordergroup.users.any? - if ordergroup.contact_person.present? - s += "\n" + Ordergroup.human_attribute_name(:contact) + ": " + ordergroup.contact_person - end + s += "\n" + Ordergroup.human_attribute_name(:contact) + ': ' + ordergroup.contact_person if ordergroup.contact_person.present? s end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de207901..8b8a5f95 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -4,7 +4,7 @@ module ApplicationHelper include PathHelper def format_time(time = Time.now) - I18n.l(time, :format => "%d.%m.%Y %H:%M") unless time.nil? + I18n.l(time, format: :foodsoft_datetime) unless time.nil? end def format_date(time = Time.now) @@ -16,7 +16,7 @@ module ApplicationHelper end def format_datetime_timespec(time, format) - I18n.l(time, :format => format) unless (time.nil? || format.nil?) + I18n.l(time, format: format) unless time.nil? || format.nil? end def format_currency(amount) @@ -26,28 +26,28 @@ module ApplicationHelper # Splits an IBAN into groups of 4 digits displayed with margins in between def format_iban(iban) - iban && iban.scan(/..?.?.?/).map { |item| content_tag(:span, item, style: "margin-right: 0.5em;") }.join.html_safe + iban && iban.scan(/..?.?.?/).map { |item| content_tag(:span, item, style: 'margin-right: 0.5em;') }.join.html_safe end # Creates ajax-controlled-links for pagination def pagination_links_remote(collection, options = {}) per_page = options[:per_page] || @per_page params = options[:params] || {} - params = params.merge({ :per_page => per_page }) - paginate collection, :params => params, :remote => true + params = params.merge({ per_page: per_page }) + paginate collection, params: params, remote: true end # Link-collection for per_page-options when using the pagination-plugin def items_per_page(options = {}) per_page_options = options[:per_page_options] || [20, 50, 100, 500] current = options[:current] || @per_page - params = params || {} + params ||= {} links = per_page_options.map do |per_page| - params.merge!({ :per_page => per_page }) + params.merge!({ per_page: per_page }) link_class = 'btn' link_class << ' disabled' if per_page == current - link_to(per_page, params, :remote => true, class: link_class) + link_to(per_page, params, remote: true, class: link_class) end if options[:wrap] == false @@ -63,21 +63,19 @@ module ApplicationHelper # Hmtl options remote = options[:remote].nil? ? true : options[:remote] class_name = case params[:sort] - when key then + when key 'sortup' - when key + '_reverse' then + when key + '_reverse' 'sortdown' - else - nil end html_options = { - :title => I18n.t('helpers.application.sort_by', text: text), - :remote => remote, - :class => class_name + title: I18n.t('helpers.application.sort_by', text: text), + remote: remote, + class: class_name } # Url options - key += "_reverse" if params[:sort] == key + key += '_reverse' if params[:sort] == key per_page = options[:per_page] || @per_page url_options = params.merge(per_page: per_page, sort: key) url_options.merge!({ page: params[:page] }) if params[:page] @@ -95,14 +93,16 @@ module ApplicationHelper # be overridden by the option 'desc'. # Other options are passed through to I18n. def heading_helper(model, attribute, options = {}) - i18nopts = { count: 2 }.merge(options.select { |a| !['short', 'desc'].include?(a) }) + i18nopts = { count: 2 }.merge(options.select { |a| !%w[short desc].include?(a) }) s = model.human_attribute_name(attribute, i18nopts) if options[:short] desc = options[:desc] - desc ||= model.human_attribute_name("#{attribute}_desc".to_sym, options.merge({ fallback: true, default: '', count: 2 })) + desc ||= model.human_attribute_name("#{attribute}_desc".to_sym, + options.merge({ fallback: true, default: '', count: 2 })) desc.blank? && desc = s - sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({ fallback: true, default: '', count: 2 })) - s = raw "#{sshort}" unless sshort.blank? + sshort = model.human_attribute_name("#{attribute}_short".to_sym, + options.merge({ fallback: true, default: '', count: 2 })) + s = raw "#{sshort}" if sshort.present? end s end @@ -117,7 +117,7 @@ module ApplicationHelper # Returns the weekday. 0 is sunday, 1 is monday and so on def weekday(dayNumber) weekdays = I18n.t('date.day_names') - return weekdays[dayNumber] + weekdays[dayNumber] end # to set a title for both the h1-tag and the title in the header @@ -136,13 +136,13 @@ module ApplicationHelper def icon(name, options = {}) icons = { - :delete => { :file => 'b_drop.png', :alt => I18n.t('ui.delete') }, - :edit => { :file => 'b_edit.png', :alt => I18n.t('ui.edit') }, - :members => { :file => 'b_users.png', :alt => I18n.t('helpers.application.edit_user') } + delete: { file: 'b_drop.png', alt: I18n.t('ui.delete') }, + edit: { file: 'b_edit.png', alt: I18n.t('ui.edit') }, + members: { file: 'b_users.png', alt: I18n.t('helpers.application.edit_user') } } options[:alt] ||= icons[name][:alt] options[:title] ||= icons[name][:title] - options.merge!({ :size => '16x16', :border => "0" }) + options.merge!({ size: '16x16', border: '0' }) image_tag icons[name][:file], options end @@ -150,27 +150,29 @@ module ApplicationHelper # Remote links with default 'loader'.gif during request def remote_link_to(text, options = {}) remote_options = { - :before => "Element.show('loader')", - :success => "Element.hide('loader')", - :method => :get + before: "Element.show('loader')", + success: "Element.hide('loader')", + method: :get } link_to(text, options[:url], remote_options.merge(options)) end def format_roles(record, icon = false) - roles = %w(suppliers article_meta orders pickups finance invoices admin) + roles = %w[suppliers article_meta orders pickups finance invoices admin] roles.select! { |role| record.send "role_#{role}?" } - names = Hash[roles.map { |r| [r, I18n.t("helpers.application.role_#{r}")] }] + names = roles.index_with { |r| I18n.t("helpers.application.role_#{r}") } if icon - roles.map { |r| image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) }.join(' ').html_safe + roles.map do |r| + image_tag("role-#{r}.png", size: '22x22', border: 0, alt: names[r], title: names[r]) + end.join(' ').html_safe else roles.map { |r| names[r] }.join(', ') end end def link_to_gmaps(address) - link_to h(address), "http://maps.google.com/?q=#{h(address)}", :title => I18n.t('helpers.application.show_google_maps'), - :target => "_blank" + link_to h(address), "http://maps.google.com/?q=#{h(address)}", title: I18n.t('helpers.application.show_google_maps'), + target: '_blank', rel: 'noopener' end # Returns flash messages html. @@ -186,8 +188,8 @@ module ApplicationHelper type = :success if type == 'notice' type = :error if type == 'alert' text = content_tag(:div, - content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => "close", "data-dismiss" => "alert") + - message, :class => "alert fade in alert-#{type}") + content_tag(:button, I18n.t('ui.marks.close').html_safe, :class => 'close', 'data-dismiss' => 'alert') + + message, class: "alert fade in alert-#{type}") flash_messages << text if message end flash_messages.join("\n").html_safe @@ -195,17 +197,17 @@ module ApplicationHelper # render base errors in a form after failed validation # http://railsapps.github.io/twitter-bootstrap-rails.html - def base_errors resource + def base_errors(resource) return '' if resource.errors.empty? || resource.errors[:base].empty? messages = resource.errors[:base].map { |msg| content_tag(:li, msg) }.join - render :partial => 'shared/base_errors', :locals => { :error_messages => messages } + render partial: 'shared/base_errors', locals: { error_messages: messages } end # show a user, depending on settings def show_user(user = @current_user, options = {}) if user.nil? - "?" + '?' elsif FoodsoftConfig[:use_nick] if options[:full] && options[:markup] raw "#{h user.nick} (#{h user.first_name} #{h user.last_name})" @@ -216,7 +218,7 @@ module ApplicationHelper user.nick.nil? ? I18n.t('helpers.application.nick_fallback') : user.nick end else - "#{user.first_name} #{user.last_name}" + (options[:unique] ? " (\##{user.id})" : '') + "#{user.first_name} #{user.last_name}" + (options[:unique] ? " (##{user.id})" : '') end end @@ -258,9 +260,9 @@ module ApplicationHelper # @return [String] stylesheet tag for foodcoop CSS style (+custom_css+ foodcoop config) # @see #foodcoop_css_path - def foodcoop_css_tag(options = {}) - unless FoodsoftConfig[:custom_css].blank? - stylesheet_link_tag foodcoop_css_path, media: 'all' - end + def foodcoop_css_tag(_options = {}) + return if FoodsoftConfig[:custom_css].blank? + + stylesheet_link_tag foodcoop_css_path, media: 'all' end end diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb index add1c6ba..ebad29a4 100644 --- a/app/helpers/articles_helper.rb +++ b/app/helpers/articles_helper.rb @@ -3,13 +3,13 @@ module ArticlesHelper def highlight_new(unequal_attributes, attribute) return unless unequal_attributes - unequal_attributes.has_key?(attribute) ? "background-color: yellow" : "" + unequal_attributes.has_key?(attribute) ? 'background-color: yellow' : '' end def row_classes(article) classes = [] - classes << "unavailable" if !article.availability - classes << "just-updated" if article.recently_updated && article.availability - classes.join(" ") + classes << 'unavailable' unless article.availability + classes << 'just-updated' if article.recently_updated && article.availability + classes.join(' ') end end diff --git a/app/helpers/deliveries_helper.rb b/app/helpers/deliveries_helper.rb index a97a7df7..ac6e4b35 100644 --- a/app/helpers/deliveries_helper.rb +++ b/app/helpers/deliveries_helper.rb @@ -11,11 +11,11 @@ module DeliveriesHelper def articles_for_select2(articles, except = [], &block) articles = articles.reorder('articles.name ASC') - articles = articles.reject { |a| not except.index(a.id).nil? } if except - block_given? or block = Proc.new { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" } + articles = articles.reject { |a| !except.index(a.id).nil? } if except + block_given? or block = proc { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" } articles.map do |a| - { :id => a.id, :text => block.call(a) } - end.unshift({ :id => '', :text => '' }) + { id: a.id, text: block.call(a) } + end.unshift({ id: '', text: '' }) end def articles_for_table(articles) @@ -23,10 +23,14 @@ module DeliveriesHelper end def stock_change_remove_link(stock_change_form) - return link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'remove_new_stock_change btn btn-small' if stock_change_form.object.new_record? + if stock_change_form.object.new_record? + return link_to t('deliveries.stock_change_fields.remove_article'), '#', + class: 'remove_new_stock_change btn btn-small' + end output = stock_change_form.hidden_field :_destroy - output += link_to t('deliveries.stock_change_fields.remove_article'), "#", :class => 'destroy_stock_change btn btn-small' - return output.html_safe + output += link_to t('deliveries.stock_change_fields.remove_article'), '#', + class: 'destroy_stock_change btn btn-small' + output.html_safe end end diff --git a/app/helpers/finance/balancing_helper.rb b/app/helpers/finance/balancing_helper.rb index bc528f04..a123b161 100644 --- a/app/helpers/finance/balancing_helper.rb +++ b/app/helpers/finance/balancing_helper.rb @@ -2,11 +2,11 @@ module Finance::BalancingHelper def balancing_view_partial view = params[:view] || 'edit_results' case view - when 'edit_results' then + when 'edit_results' 'edit_results_by_articles' - when 'groups_overview' then + when 'groups_overview' 'shared/articles_by/groups' - when 'articles_overview' then + when 'articles_overview' 'shared/articles_by/articles' end end diff --git a/app/helpers/finance/invoices_helper.rb b/app/helpers/finance/invoices_helper.rb index ef01a275..0644b501 100644 --- a/app/helpers/finance/invoices_helper.rb +++ b/app/helpers/finance/invoices_helper.rb @@ -1,9 +1,9 @@ module Finance::InvoicesHelper - def format_delivery_item delivery + def format_delivery_item(delivery) format_date(delivery.date) end - def format_order_item order + def format_order_item(order) "#{format_date(order.ends)} (#{number_to_currency(order.sum)})" end end diff --git a/app/helpers/group_order_articles_helper.rb b/app/helpers/group_order_articles_helper.rb index ff003731..3a7efc33 100644 --- a/app/helpers/group_order_articles_helper.rb +++ b/app/helpers/group_order_articles_helper.rb @@ -2,12 +2,12 @@ module GroupOrderArticlesHelper # return an edit field for a GroupOrderArticle result def group_order_article_edit_result(goa) result = number_with_precision goa.result, strip_insignificant_zeros: true - unless goa.group_order.order.finished? && current_user.role_finance? - result - else + if goa.group_order.order.finished? && current_user.role_finance? simple_form_for goa, remote: true, html: { 'data-submit-onchange' => 'changed', class: 'delta-input' } do |f| f.input_field :result, as: :delta, class: 'input-nano', data: { min: 0 }, id: "r_#{goa.id}", value: result end + else + result end end end diff --git a/app/helpers/group_orders_helper.rb b/app/helpers/group_orders_helper.rb index c5e27c66..a09a066c 100644 --- a/app/helpers/group_orders_helper.rb +++ b/app/helpers/group_orders_helper.rb @@ -1,10 +1,11 @@ module GroupOrdersHelper def data_to_js(ordering_data) - ordering_data[:order_articles].map { |id, data| - [id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], data[:used_quantity], data[:quantity_available]] - }.map { |row| + ordering_data[:order_articles].map do |id, data| + [id, data[:price], data[:unit], data[:total_price], data[:others_quantity], data[:others_tolerance], + data[:used_quantity], data[:quantity_available]] + end.map do |row| "addData(#{row.join(', ')});" - }.join("\n") + end.join("\n") end # Returns a link to the page where a group_order can be edited. @@ -14,9 +15,9 @@ module GroupOrdersHelper path = if options[:show] && group_order group_order_path(group_order) elsif group_order - edit_group_order_path(group_order, :order_id => order.id) + edit_group_order_path(group_order, order_id: order.id) else - new_group_order_path(:order_id => order.id) + new_group_order_path(order_id: order.id) end options.delete(:show) name = block_given? ? capture(&block) : order.name @@ -26,7 +27,7 @@ module GroupOrdersHelper # Return css class names for order result table def order_article_class_name(quantity, tolerance, result) - if (quantity + tolerance > 0) + if quantity + tolerance > 0 result > 0 ? 'success' : 'failed' else 'ignored' @@ -45,12 +46,12 @@ module GroupOrdersHelper end def get_missing_units_css_class(quantity_missing) - if (quantity_missing == 1) - return 'missing-few'; - elsif (quantity_missing == 0) - return '' + if quantity_missing == 1 + 'missing-few' + elsif quantity_missing == 0 + '' else - return 'missing-many' + 'missing-many' end end end diff --git a/app/helpers/order_articles_helper.rb b/app/helpers/order_articles_helper.rb index b4290e84..7af4b409 100644 --- a/app/helpers/order_articles_helper.rb +++ b/app/helpers/order_articles_helper.rb @@ -1,6 +1,6 @@ module OrderArticlesHelper def article_label_with_unit(article) pkg_info = pkg_helper(article, plain: true) - "#{article.name} (#{[article.unit, pkg_info].reject(&:blank?).join(' ')})" + "#{article.name} (#{[article.unit, pkg_info].compact_blank.join(' ')})" end end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb index ff238730..d629ccb1 100644 --- a/app/helpers/orders_helper.rb +++ b/app/helpers/orders_helper.rb @@ -18,7 +18,7 @@ module OrdersHelper def options_for_suppliers_to_select options = [[I18n.t('helpers.orders.option_choose')]] - options += Supplier.map { |s| [s.name, url_for(action: "new", supplier_id: s.id)] } + options += Supplier.map { |s| [s.name, url_for(action: 'new', supplier_id: s.id)] } options += [[I18n.t('helpers.orders.option_stock'), url_for(action: 'new', supplier_id: nil)]] options_for_select(options) end @@ -29,13 +29,13 @@ module OrdersHelper nil else units_info = [] - [:units_to_order, :units_billed, :units_received].map do |unit| - if n = order_article.send(unit) - line = n.to_s + ' ' - line += pkg_helper(order_article.price, options) + ' ' unless n == 0 - line += OrderArticle.human_attribute_name("#{unit}_short", count: n) - units_info << line - end + %i[units_to_order units_billed units_received].map do |unit| + next unless n = order_article.send(unit) + + line = n.to_s + ' ' + line += pkg_helper(order_article.price, options) + ' ' unless n == 0 + line += OrderArticle.human_attribute_name("#{unit}_short", count: n) + units_info << line end units_info.join(', ').html_safe end @@ -67,8 +67,8 @@ module OrdersHelper def pkg_helper_icon(c = nil, options = {}) options = { tag: 'i', class: '' }.merge(options) if c.nil? - c = " ".html_safe - options[:class] += " icon-only" + c = ' '.html_safe + options[:class] += ' icon-only' end content_tag(options[:tag], c, class: "package #{options[:class]}").html_safe end @@ -94,11 +94,12 @@ module OrdersHelper autocomplete: 'off' if order_article.result_manually_changed? - input_html = content_tag(:span, class: 'input-prepend intable', title: t('orders.edit_amount.field_locked_title', default: '')) { + input_html = content_tag(:span, class: 'input-prepend intable', + title: t('orders.edit_amount.field_locked_title', default: '')) do button_tag(nil, type: :button, class: 'btn unlocker') { content_tag(:i, nil, class: 'icon icon-unlock') } + input_html - } + end end input_html.html_safe @@ -109,18 +110,16 @@ module OrdersHelper def ordergroup_count(order) group_orders = order.group_orders.includes(:ordergroup) txt = "#{group_orders.count} #{Ordergroup.model_name.human count: group_orders.count}" - if group_orders.count == 0 - return txt - else - desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ') - content_tag(:abbr, txt, title: desc).html_safe - end + return txt if group_orders.count == 0 + + desc = group_orders.includes(:ordergroup).map { |g| g.ordergroup_name }.join(', ') + content_tag(:abbr, txt, title: desc).html_safe end # @param order_or_supplier [Order, Supplier] Order or supplier to link to # @return [String] Link to order or supplier, showing its name. def supplier_link(order_or_supplier) - if order_or_supplier.kind_of?(Order) && order_or_supplier.stockit? + if order_or_supplier.is_a?(Order) && order_or_supplier.stockit? link_to(order_or_supplier.name, stock_articles_path).html_safe else link_to(@order.supplier.name, supplier_path(@order.supplier)).html_safe @@ -152,7 +151,8 @@ module OrdersHelper if order.stockit? content_tag :div, t('orders.index.action_receive'), class: "btn disabled #{options[:class]}" else - link_to t('orders.index.action_receive'), receive_order_path(order), class: "btn#{' btn-success' unless order.received?} #{options[:class]}" + link_to t('orders.index.action_receive'), receive_order_path(order), + class: "btn#{' btn-success' unless order.received?} #{options[:class]}" end end end diff --git a/app/helpers/stockit_helper.rb b/app/helpers/stockit_helper.rb index a08e8335..9848198d 100644 --- a/app/helpers/stockit_helper.rb +++ b/app/helpers/stockit_helper.rb @@ -1,8 +1,8 @@ module StockitHelper def stock_article_classes(article) class_names = [] - class_names << "unavailable" if article.quantity_available <= 0 - class_names.join(" ") + class_names << 'unavailable' if article.quantity_available <= 0 + class_names.join(' ') end def link_to_stock_change_reason(stock_change) @@ -17,8 +17,8 @@ module StockitHelper def stock_article_price_hint(stock_article) t('simple_form.hints.stock_article.edit_stock_article.price', - :stock_article_copy_link => link_to(t('stockit.form.copy_stock_article'), - stock_article_copy_path(stock_article), - :remote => true)) + stock_article_copy_link: link_to(t('stockit.form.copy_stock_article'), + stock_article_copy_path(stock_article), + remote: true)) end end diff --git a/app/helpers/tasks_helper.rb b/app/helpers/tasks_helper.rb index f6f1fa14..4b12f7a8 100644 --- a/app/helpers/tasks_helper.rb +++ b/app/helpers/tasks_helper.rb @@ -1,16 +1,16 @@ module TasksHelper def task_assignments(task) task.assignments.map do |ass| - content_tag :span, show_user(ass.user), :class => (ass.accepted? ? 'accepted' : 'unaccepted') - end.join(", ").html_safe + content_tag :span, show_user(ass.user), class: (ass.accepted? ? 'accepted' : 'unaccepted') + end.join(', ').html_safe end # generate colored number of still required users def highlighted_required_users(task) - unless task.enough_users_assigned? - content_tag :span, task.still_required_users, class: 'badge badge-important', - title: I18n.t('helpers.tasks.required_users', :count => task.still_required_users) - end + return if task.enough_users_assigned? + + content_tag :span, task.still_required_users, class: 'badge badge-important', + title: I18n.t('helpers.tasks.required_users', count: task.still_required_users) end def task_title(task) diff --git a/app/inputs/delta_input.rb b/app/inputs/delta_input.rb index adc08960..f4ce1a2b 100644 --- a/app/inputs/delta_input.rb +++ b/app/inputs/delta_input.rb @@ -6,7 +6,7 @@ class DeltaInput < SimpleForm::Inputs::StringInput options[:data] ||= {} options[:data][:delta] ||= 1 options[:autocomplete] ||= 'off' - # TODO get generated id, don't know how yet - `add_default_name_and_id_for_value` might be an option + # TODO: get generated id, don't know how yet - `add_default_name_and_id_for_value` might be an option template.content_tag :div, class: 'delta-input input-prepend input-append' do delta_button(content_tag(:i, nil, class: 'icon icon-minus'), -1, options) + diff --git a/app/javascript/application.js b/app/javascript/application.js new file mode 100644 index 00000000..141d3cae --- /dev/null +++ b/app/javascript/application.js @@ -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" diff --git a/app/javascript/trix-editor-overrides.js b/app/javascript/trix-editor-overrides.js new file mode 100644 index 00000000..64cecbef --- /dev/null +++ b/app/javascript/trix-editor-overrides.js @@ -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')) + } +}) \ No newline at end of file diff --git a/lib/api/errors.rb b/app/lib/api/errors.rb similarity index 100% rename from lib/api/errors.rb rename to app/lib/api/errors.rb diff --git a/lib/apple_bar.rb b/app/lib/apple_bar.rb similarity index 75% rename from lib/apple_bar.rb rename to app/lib/apple_bar.rb index a2176ea3..fb6fcef7 100644 --- a/lib/apple_bar.rb +++ b/app/lib/apple_bar.rb @@ -14,23 +14,23 @@ class AppleBar def group_bar_state if apples >= 100 'success' + elsif FoodsoftConfig[:stop_ordering_under].present? && + (apples >= FoodsoftConfig[:stop_ordering_under]) + 'warning' else - if FoodsoftConfig[:stop_ordering_under].present? and - apples >= FoodsoftConfig[:stop_ordering_under] - 'warning' - else - 'danger' - end + 'danger' end end # Use apples as percentage, but show at least 10 percent def group_bar_width - @ordergroup.apples < 2 ? 2 : @ordergroup.apples + [@ordergroup.apples, 2].max end def mean_order_amount_per_job - (1 / @global_avg).round rescue 0 + (1 / @global_avg).round + rescue StandardError + 0 end def apples diff --git a/lib/articles_csv.rb b/app/lib/articles_csv.rb similarity index 81% rename from lib/articles_csv.rb rename to app/lib/articles_csv.rb index 910de9be..61f5743f 100644 --- a/lib/articles_csv.rb +++ b/app/lib/articles_csv.rb @@ -1,4 +1,4 @@ -class ArticlesCsv < RenderCSV +class ArticlesCsv < RenderCsv include ApplicationHelper def header @@ -16,14 +16,14 @@ class ArticlesCsv < RenderCSV Article.human_attribute_name(:unit_quantity), '', '', - Article.human_attribute_name(:article_category), + Article.human_attribute_name(:article_category) ] end def data @object.each do |o| yield [ - '', + o.availability ? I18n.t('simple_form.yes') : I18n.t('simple_form.no'), o.order_number, o.name, o.note, @@ -36,7 +36,7 @@ class ArticlesCsv < RenderCSV o.unit_quantity, '', '', - o.article_category.try(:name), + o.article_category.try(:name) ] end end diff --git a/lib/bank_account_connector.rb b/app/lib/bank_account_connector.rb similarity index 85% rename from lib/bank_account_connector.rb rename to app/lib/bank_account_connector.rb index 93e7cc7c..5e18a816 100644 --- a/lib/bank_account_connector.rb +++ b/app/lib/bank_account_connector.rb @@ -8,9 +8,7 @@ class BankAccountConnector nil end - def text - @text - end + attr_reader :text end class TextField @@ -24,13 +22,7 @@ class BankAccountConnector nil end - def name - @name - end - - def value - @value - end + attr_reader :name, :value def label @label || @name.to_s @@ -49,14 +41,14 @@ class BankAccountConnector end end - @@registered_classes = Set.new + @registered_classes = Set.new def self.register(klass) - @@registered_classes.add klass + @registered_classes.add klass end def self.find(iban) - @@registered_classes.each do |klass| + @registered_classes.each do |klass| return klass if klass.handles(iban) end nil @@ -73,17 +65,7 @@ class BankAccountConnector @bank_account.iban end - def auto_submit - @auto_submit - end - - def controls - @controls - end - - def count - @count - end + attr_reader :auto_submit, :controls, :count def text(data) @controls += [TextItem.new(data)] @@ -142,11 +124,9 @@ class BankAccountConnector @bank_account.save! end - def load(data) - end + def load(data); end - def dump - end + def dump; end def t(key, args = {}) return t(".fields.#{key}") unless key.is_a? String diff --git a/lib/bank_account_connector_external.rb b/app/lib/bank_account_connector_external.rb similarity index 100% rename from lib/bank_account_connector_external.rb rename to app/lib/bank_account_connector_external.rb diff --git a/lib/bank_account_information_importer.rb b/app/lib/bank_account_information_importer.rb similarity index 83% rename from lib/bank_account_information_importer.rb rename to app/lib/bank_account_information_importer.rb index bebc1ff4..a83c53f7 100644 --- a/lib/bank_account_information_importer.rb +++ b/app/lib/bank_account_information_importer.rb @@ -17,16 +17,16 @@ class BankAccountInformationImporter ret = 0 booked.each do |t| amount = parse_account_information_amount t[:transactionAmount] - entityName = amount < 0 ? t[:creditorName] : t[:debtorName] - entityAccount = amount < 0 ? t[:creditorAccount] : t[:debtorAccount] + entity_name = amount < 0 ? t[:creditorName] : t[:debtorName] + entity_account = amount < 0 ? t[:creditorAccount] : t[:debtorAccount] reference = [t[:endToEndId], t[:remittanceInformationUnstructured]].join("\n").strip @bank_account.bank_transactions.where(external_id: t[:transactionId]).first_or_create.update({ date: t[:bookingDate], amount: amount, - iban: entityAccount && entityAccount[:iban], + iban: entity_account && entity_account[:iban], reference: reference, - text: entityName, + text: entity_name, receipt: t[:additionalInformation] }) ret += 1 @@ -34,7 +34,7 @@ class BankAccountInformationImporter balances = (data[:balances] ? data[:balances].map { |b| [b[:balanceType], b[:balanceAmount]] } : []).to_h balance = balances.values.first - %w(closingBooked expected authorised openingBooked interimAvailable forwardAvailable nonInvoiced).each do |type| + %w[closingBooked expected authorised openingBooked interimAvailable forwardAvailable nonInvoiced].each do |type| value = balances[type] if value balance = value diff --git a/lib/bank_transaction_reference.rb b/app/lib/bank_transaction_reference.rb similarity index 85% rename from lib/bank_transaction_reference.rb rename to app/lib/bank_transaction_reference.rb index d033c544..22b9f181 100644 --- a/lib/bank_transaction_reference.rb +++ b/app/lib/bank_transaction_reference.rb @@ -1,7 +1,7 @@ class BankTransactionReference # parses a string from a bank transaction field def self.parse(data) - m = /(^|[^\w\.])FS(?\d+)(\.(?\d+))?(?([A-Za-z]+\d+(\.\d+)?)+)([^\w\.]|$)/.match(data) + m = /(^|[^\w.])FS(?\d+)(\.(?\d+))?(?([A-Za-z]+\d+(\.\d+)?)+)([^\w.]|$)/.match(data) return unless m parts = {} @@ -13,7 +13,7 @@ class BankTransactionReference ret = { group: m[:group].to_i, parts: parts } ret[:user] = m[:user].to_i if m[:user] - return ret + ret end def self.js_code_for_user(user) diff --git a/lib/bank_transactions_csv.rb b/app/lib/bank_transactions_csv.rb similarity index 93% rename from lib/bank_transactions_csv.rb rename to app/lib/bank_transactions_csv.rb index 34c39403..4adbc192 100644 --- a/lib/bank_transactions_csv.rb +++ b/app/lib/bank_transactions_csv.rb @@ -1,6 +1,6 @@ require 'csv' -class BankTransactionsCsv < RenderCSV +class BankTransactionsCsv < RenderCsv include ApplicationHelper def header diff --git a/app/lib/date_time_attribute_validate.rb b/app/lib/date_time_attribute_validate.rb new file mode 100644 index 00000000..fa53c361 --- /dev/null +++ b/app/lib/date_time_attribute_validate.rb @@ -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 diff --git a/lib/financial_transactions_csv.rb b/app/lib/financial_transactions_csv.rb similarity index 95% rename from lib/financial_transactions_csv.rb rename to app/lib/financial_transactions_csv.rb index dc21d892..fc12d000 100644 --- a/lib/financial_transactions_csv.rb +++ b/app/lib/financial_transactions_csv.rb @@ -1,6 +1,6 @@ require 'csv' -class FinancialTransactionsCsv < RenderCSV +class FinancialTransactionsCsv < RenderCsv include ApplicationHelper def header diff --git a/lib/foodsoft/expansion_variables.rb b/app/lib/foodsoft/expansion_variables.rb similarity index 92% rename from lib/foodsoft/expansion_variables.rb rename to app/lib/foodsoft/expansion_variables.rb index bcf67e7a..a4a8153e 100644 --- a/lib/foodsoft/expansion_variables.rb +++ b/app/lib/foodsoft/expansion_variables.rb @@ -14,7 +14,7 @@ module Foodsoft cattr_accessor :variables # Hash of variables. Note that keys are Strings. - @@variables = { + @variables = { 'scope' => -> { FoodsoftConfig.scope }, 'name' => -> { FoodsoftConfig[:name] }, 'contact.street' => -> { FoodsoftConfig[:contact][:street] }, @@ -39,13 +39,13 @@ module Foodsoft 'supplier_count' => -> { Supplier.undeleted.count }, 'active_supplier_count' => -> { active_supplier_count }, 'active_suppliers' => -> { active_suppliers }, - 'first_order_date' => -> { I18n.l Order.first.try { |o| o.starts.to_date } } + 'first_order_date' => -> { I18n.l(Order.first.try { |o| o.starts.to_date }) } } # Return expanded variable # @return [String] Expanded variable def self.get(var) - s = @@variables[var.to_s] + s = @variables[var.to_s] s.respond_to?(:call) ? s.call : s.to_s end @@ -54,8 +54,8 @@ module Foodsoft # @param options [Hash] Extra variables to expand # @return [String] Expanded string def self.expand(str, options = {}) - str.gsub /{{([._a-zA-Z0-9]+)}}/ do - options[$1] || self.get($1) + str.gsub(/{{([._a-zA-Z0-9]+)}}/) do + options[::Regexp.last_match(1)] || get(::Regexp.last_match(1)) end end diff --git a/lib/foodsoft_config.rb b/app/lib/foodsoft_config.rb similarity index 92% rename from lib/foodsoft_config.rb rename to app/lib/foodsoft_config.rb index 5a370459..c7dda590 100644 --- a/lib/foodsoft_config.rb +++ b/app/lib/foodsoft_config.rb @@ -44,6 +44,8 @@ class FoodsoftConfig # @return [ActiveSupport::HashWithIndifferentAccess] Current configuration from configuration file. mattr_accessor :config + mattr_accessor :default_config + # Configuration file location. # Taken from environment variable +FOODSOFT_APP_CONFIG+, # or else +config/app_config.yml+. @@ -68,7 +70,7 @@ class FoodsoftConfig # Load initial config from development or production set_config Rails.env # Overwrite scope to have a better namescope than 'production' - self.scope = config[:default_scope] or raise "No default_scope is set" + self.scope = config[:default_scope] or raise 'No default_scope is set' # Set defaults for backward-compatibility set_missing # Make sure relevant configuration is applied, also in single coops mode, @@ -77,7 +79,7 @@ class FoodsoftConfig end def init_mailing - [:protocol, :host, :port, :script_name].each do |k| + %i[protocol host port script_name].each do |k| ActionMailer::Base.default_url_options[k] = self[k] if self[k] end end @@ -115,7 +117,7 @@ class FoodsoftConfig # @return [Object] Value of the key. def [](key) if RailsSettings::CachedSettings.table_exists? && allowed_key?(key) - value = RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"] + value = RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"] value = config[key] if value.nil? value else @@ -137,20 +139,20 @@ class FoodsoftConfig if config[key] == value || (config[key].nil? && value == false) # delete (ok if it was already deleted) begin - RailsSettings::CachedSettings.destroy "foodcoop.#{self.scope}.#{key}" + RailsSettings::CachedSettings.destroy "foodcoop.#{scope}.#{key}" rescue RailsSettings::Settings::SettingNotFound end else # or store - RailsSettings::CachedSettings["foodcoop.#{self.scope}.#{key}"] = value + RailsSettings::CachedSettings["foodcoop.#{scope}.#{key}"] = value end true end # @return [Array] Configuration keys that are set (either in +app_config.yml+ or database). def keys - keys = RailsSettings::CachedSettings.get_all("foodcoop.#{self.scope}.").try(:keys) || [] - keys.map! { |k| k.gsub(/^foodcoop\.#{self.scope}\./, '') } + keys = RailsSettings::CachedSettings.get_all("foodcoop.#{scope}.").try(:keys) || [] + keys.map! { |k| k.gsub(/^foodcoop\.#{scope}\./, '') } keys += config.keys keys.map(&:to_s).uniq end @@ -179,17 +181,17 @@ class FoodsoftConfig # @return [Boolean] Whether this key may be set in the database def allowed_key?(key) # fast check for keys without nesting - if self.config[:protected].include? key - !self.config[:protected][key] + if config[:protected].include? key + !config[:protected][key] else - !self.config[:protected][:all] + !config[:protected][:all] end # @todo allow to check nested keys as well end # @return [Hash] Full configuration. def to_hash - keys.to_h { |k| [k, self[k]] } + keys.index_with { |k| self[k] } end # for using active_model_serializer in the api/v1/configs controller @@ -216,7 +218,6 @@ class FoodsoftConfig # end # # @return [Hash] Default configuration values - mattr_accessor :default_config private @@ -229,7 +230,7 @@ class FoodsoftConfig end 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? ActiveRecord::Base.establish_connection(database_config) end @@ -286,7 +287,9 @@ class FoodsoftConfig def normalize_value(value) value = value.map { |v| normalize_value(v) } if value.is_a? Array if value.is_a? Hash - value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map { |a| [a[0], normalize_value(a[1])] }] + value = ActiveSupport::HashWithIndifferentAccess[value.to_a.map do |a| + [a[0], normalize_value(a[1])] + end] end case value when 'true' then true diff --git a/app/lib/foodsoft_date_util.rb b/app/lib/foodsoft_date_util.rb new file mode 100644 index 00000000..38dbc6be --- /dev/null +++ b/app/lib/foodsoft_date_util.rb @@ -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 diff --git a/app/lib/foodsoft_file.rb b/app/lib/foodsoft_file.rb new file mode 100644 index 00000000..0a7128ed --- /dev/null +++ b/app/lib/foodsoft_file.rb @@ -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 diff --git a/lib/foodsoft_mail_receiver.rb b/app/lib/foodsoft_mail_receiver.rb similarity index 60% rename from lib/foodsoft_mail_receiver.rb rename to app/lib/foodsoft_mail_receiver.rb index 560e7edd..c5ec2edb 100644 --- a/lib/foodsoft_mail_receiver.rb +++ b/app/lib/foodsoft_mail_receiver.rb @@ -19,31 +19,29 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd private - def on_rcpt_to_event(ctx, rcpt_to) + def on_rcpt_to_event(_ctx, rcpt_to) recipient = rcpt_to.gsub(/^\s*<\s*(.*)\s*>\s*$/, '\1') @handlers << self.class.find_handler(recipient) rcpt_to - rescue => error - logger.info("Can not accept mail for '#{rcpt_to}': #{error}") + rescue StandardError => e + logger.info("Can not accept mail for '#{rcpt_to}': #{e}") raise MidiSmtpServer::Smtpd550Exception end def on_message_data_event(ctx) - begin - @handlers.each do |handler| - handler.call(ctx[:message][:data]) - end - rescue => error - ExceptionNotifier.notify_exception(error, data: ctx) - raise error - ensure - @handlers.clear + @handlers.each do |handler| + handler.call(ctx[:message][:data]) end + rescue StandardError => e + ExceptionNotifier.notify_exception(e, data: ctx) + raise e + ensure + @handlers.clear end def self.find_handler(recipient) - m = /(?[^@\.]+)\.(?
[^@]+)(@(?[^@]+))?/.match recipient - raise "recipient is missing or has an invalid format" if m.nil? + m = /(?[^@.]+)\.(?
[^@]+)(@(?[^@]+))?/.match recipient + raise 'recipient is missing or has an invalid format' if m.nil? raise "Foodcoop '#{m[:foodcoop]}' could not be found" unless FoodsoftConfig.allowed_foodcoop? m[:foodcoop] FoodsoftConfig.select_multifoodcoop m[:foodcoop] @@ -51,10 +49,10 @@ class FoodsoftMailReceiver < MidiSmtpServer::Smtpd @@registered_classes.each do |klass| if match = klass.regexp.match(m[:address]) handler = klass.new match - return lambda { |data| handler.received(data) } + return ->(data) { handler.received(data) } end end - raise "invalid format for recipient" + raise 'invalid format for recipient' end end diff --git a/lib/invoices_csv.rb b/app/lib/invoices_csv.rb similarity index 95% rename from lib/invoices_csv.rb rename to app/lib/invoices_csv.rb index aa20cd08..eecad298 100644 --- a/lib/invoices_csv.rb +++ b/app/lib/invoices_csv.rb @@ -1,6 +1,6 @@ require 'csv' -class InvoicesCsv < RenderCSV +class InvoicesCsv < RenderCsv include ApplicationHelper def header @@ -32,7 +32,7 @@ class InvoicesCsv < RenderCSV t.deposit, t.deposit_credit, t.paid_on, - t.note, + t.note ] end end diff --git a/lib/order_csv.rb b/app/lib/order_csv.rb similarity index 86% rename from lib/order_csv.rb rename to app/lib/order_csv.rb index 6ec96581..e2449596 100644 --- a/lib/order_csv.rb +++ b/app/lib/order_csv.rb @@ -1,6 +1,6 @@ require 'csv' -class OrderCsv < RenderCSV +class OrderCsv < RenderCsv def header [ OrderArticle.human_attribute_name(:units_to_order), @@ -14,7 +14,7 @@ class OrderCsv < RenderCSV end def data - @object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa| + @object.order_articles.ordered.includes(%i[article article_price]).all.map do |oa| yield [ oa.units_to_order, oa.article.order_number, diff --git a/lib/order_pdf.rb b/app/lib/order_pdf.rb similarity index 92% rename from lib/order_pdf.rb rename to app/lib/order_pdf.rb index 034ca51f..869cb0e8 100644 --- a/lib/order_pdf.rb +++ b/app/lib/order_pdf.rb @@ -1,4 +1,4 @@ -class OrderPdf < RenderPDF +class OrderPdf < RenderPdf attr_reader :order def initialize(order, options = {}) @@ -55,7 +55,7 @@ class OrderPdf < RenderPDF end def group_order_article_quantity_with_tolerance(goa) - goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : "#{goa.quantity}" + goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : goa.quantity.to_s end def group_order_article_result(goa) @@ -88,7 +88,7 @@ class OrderPdf < RenderPDF .pluck('groups.name', 'SUM(group_orders.price)', 'ordergroup_id', 'SUM(group_orders.transport)') result.map do |item| - [item.first || stock_ordergroup_name] + item[1..-1] + [item.first || stock_ordergroup_name] + item[1..] end end @@ -103,7 +103,7 @@ class OrderPdf < RenderPDF def each_ordergroup_batch(batch_size) offset = 0 - while true + loop do go_records = ordergroups(offset, batch_size) break unless go_records.any? @@ -113,7 +113,7 @@ class OrderPdf < RenderPDF # get quantity for each article and ordergroup goa_records = group_order_articles(group_ids) .group('group_order_articles.order_article_id, group_orders.ordergroup_id') - .pluck('group_order_articles.order_article_id', 'group_orders.ordergroup_id', 'SUM(COALESCE(group_order_articles.result, group_order_articles.quantity))') + .pluck('group_order_articles.order_article_id', 'group_orders.ordergroup_id', Arel.sql('SUM(COALESCE(group_order_articles.result, group_order_articles.quantity))')) # transform the flat list of results in a hash (with the article as key), which contains an array for all ordergroups results = goa_records.group_by(&:first).transform_values do |value| @@ -136,7 +136,7 @@ class OrderPdf < RenderPDF group_order_articles(ordergroup) .includes(order_article: { article: [:supplier] }) .order('suppliers.name, articles.name') - .preload(order_article: [:article_price, :order]) + .preload(order_article: %i[article_price order]) .each(&block) end diff --git a/app/lib/order_txt.rb b/app/lib/order_txt.rb new file mode 100644 index 00000000..320e429f --- /dev/null +++ b/app/lib/order_txt.rb @@ -0,0 +1,26 @@ +class OrderTxt + def initialize(order, _options = {}) + @order = order + end + + # Renders the fax-text-file + # e.g. for easier use with online-fax-software, which don't accept pdf-files + def to_txt + supplier = @order.supplier + contact = FoodsoftConfig[:contact].symbolize_keys + text = I18n.t('orders.fax.heading', name: FoodsoftConfig[:name]) + text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" if supplier.customer_number.present? + text += "\n" + I18n.t('orders.fax.delivery_day') + text += "\n\n#{supplier.name}\n#{supplier.address}\n#{Supplier.human_attribute_name(:fax)}: #{supplier.fax}\n\n" + text += '****** ' + I18n.t('orders.fax.to_address') + "\n\n" + text += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n" + text += '****** ' + I18n.t('orders.fax.articles') + "\n\n" + text += format("%8s %8s %s\n", I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'), + I18n.t('orders.fax.name')) + # now display all ordered articles + @order.order_articles.ordered.includes(%i[article article_price]).each do |oa| + text += format("%8s %8d %s\n", oa.article.order_number, oa.units_to_order.to_i, oa.article.name) + end + text + end +end diff --git a/lib/ordergroups_csv.rb b/app/lib/ordergroups_csv.rb similarity index 85% rename from lib/ordergroups_csv.rb rename to app/lib/ordergroups_csv.rb index c41d2e83..f6fba00f 100644 --- a/lib/ordergroups_csv.rb +++ b/app/lib/ordergroups_csv.rb @@ -1,4 +1,4 @@ -class OrdergroupsCsv < RenderCSV +class OrdergroupsCsv < RenderCsv include ApplicationHelper def header @@ -14,9 +14,9 @@ class OrdergroupsCsv < RenderCSV Ordergroup.human_attribute_name(:break_start), Ordergroup.human_attribute_name(:break_end), Ordergroup.human_attribute_name(:last_user_activity), - Ordergroup.human_attribute_name(:last_order), + Ordergroup.human_attribute_name(:last_order) ] - row + Ordergroup.custom_fields.map { |f| f[:label] } + row + Ordergroup.custom_fields.pluck(:label) end def data @@ -33,7 +33,7 @@ class OrdergroupsCsv < RenderCSV o.break_start, o.break_end, o.last_user_activity, - o.last_order.try(:starts), + o.last_order.try(:starts) ] yield row + Ordergroup.custom_fields.map { |f| o.settings.custom_fields[f[:name]] } end diff --git a/lib/render_csv.rb b/app/lib/render_csv.rb similarity index 73% rename from lib/render_csv.rb rename to app/lib/render_csv.rb index b900f1f7..76d77f11 100644 --- a/lib/render_csv.rb +++ b/app/lib/render_csv.rb @@ -1,6 +1,6 @@ require 'csv' -class RenderCSV +class RenderCsv include ActionView::Helpers::NumberHelper def initialize(object, options = {}) @@ -13,7 +13,7 @@ class RenderCSV end def to_csv - options = @options.select { |k| %w(col_sep row_sep).include? k.to_s } + options = @options.select { |k| %w[col_sep row_sep].include? k.to_s } ret = CSV.generate options do |csv| if h = header csv << h @@ -31,12 +31,6 @@ class RenderCSV yield [] end - # Helper method to test pdf via rails console: OrderCsv.new(order).save_tmp - def save_tmp - encoding = @options[:encoding] || 'UTF-8' - File.write("#{Rails.root}/tmp/#{self.class.to_s.underscore}.csv", to_csv.force_encoding(encoding)) - end - # XXX disable unit to avoid encoding problems, both in unit and whitespace. Also allows computations in spreadsheet. def number_to_currency(number, options = {}) super(number, options.merge({ unit: '' })) diff --git a/lib/render_pdf.rb b/app/lib/render_pdf.rb similarity index 92% rename from lib/render_pdf.rb rename to app/lib/render_pdf.rb index a5cde2b6..2311e646 100644 --- a/lib/render_pdf.rb +++ b/app/lib/render_pdf.rb @@ -18,7 +18,7 @@ class RotatedCell < Prawn::Table::Cell::Text (height + (border_top_width / 2.0) + (border_bottom_width / 2.0)) / tan_rotation end - def styled_width_of(text) + def styled_width_of(_text) options = @text_options.reject { |k| k == :style } with_font { (@pdf.height_of(@content, options) + padding_top + padding_bottom) / tan_rotation } end @@ -28,9 +28,9 @@ class RotatedCell < Prawn::Table::Cell::Text with_font { (@pdf.width_of(@content, options) + padding_top + padding_bottom) * tan_rotation } end - def draw_borders(pt) + def draw_borders(point) @pdf.mask(:line_width, :stroke_color) do - x, y = pt + x, y = point from = [[x - skew, y + (border_top_width / 2.0)], to = [x, y - height - (border_bottom_width / 2.0)]] @@ -52,7 +52,7 @@ class RotatedCell < Prawn::Table::Cell::Text end end -class RenderPDF < Prawn::Document +class RenderPdf < Prawn::Document include ActionView::Helpers::NumberHelper include ApplicationHelper @@ -118,11 +118,6 @@ class RenderPDF < Prawn::Document render # Render pdf end - # Helper method to test pdf via rails console: OrderByGroups.new(order).save_tmp - def save_tmp - File.write("#{Rails.root}/tmp/#{self.class.to_s.underscore}.pdf", to_pdf.force_encoding("UTF-8")) - end - # @todo avoid underscore instead of unicode whitespace in pdf :/ def number_to_currency(number, options = {}) super(number, options).gsub("\u202f", ' ') if number @@ -148,17 +143,18 @@ class RenderPDF < Prawn::Document protected - def fontsize(n) - n + def fontsize(size) + size end # return whether pagebreak or vertical whitespace is used for breaks def pdf_add_page_breaks?(docid = nil) docid ||= self.class.name.underscore cfg = FoodsoftConfig[:pdf_add_page_breaks] - if cfg.is_a? Array + case cfg + when Array cfg.index(docid.to_s).any? - elsif cfg.is_a? Hash + when Hash cfg[docid.to_s] else cfg diff --git a/lib/spreadsheet_file.rb b/app/lib/spreadsheet_file.rb similarity index 100% rename from lib/spreadsheet_file.rb rename to app/lib/spreadsheet_file.rb diff --git a/lib/templates/haml/scaffold/_form.html.haml b/app/lib/templates/haml/scaffold/_form.html.haml similarity index 100% rename from lib/templates/haml/scaffold/_form.html.haml rename to app/lib/templates/haml/scaffold/_form.html.haml diff --git a/lib/token_verifier.rb b/app/lib/token_verifier.rb similarity index 94% rename from lib/token_verifier.rb rename to app/lib/token_verifier.rb index a8a0f1eb..5f389943 100644 --- a/lib/token_verifier.rb +++ b/app/lib/token_verifier.rb @@ -19,11 +19,9 @@ class TokenVerifier < ActiveSupport::MessageVerifier raise InvalidPrefix unless r[1] == @_prefix # return original message - if r.length > 2 - r[2] - else - nil - end + return unless r.length > 2 + + r[2] end class InvalidMessage < ActiveSupport::MessageVerifier::InvalidSignature; end @@ -32,8 +30,6 @@ class TokenVerifier < ActiveSupport::MessageVerifier class InvalidPrefix < ActiveSupport::MessageVerifier::InvalidSignature; end - protected - def self.secret # secret_key_base for Rails 4, but Rails 3 initializer may still be used Foodsoft::Application.config.secret_key_base || Foodsoft::Application.config.secret_token diff --git a/lib/users_csv.rb b/app/lib/users_csv.rb similarity index 97% rename from lib/users_csv.rb rename to app/lib/users_csv.rb index 56ec3a23..a7d54698 100644 --- a/lib/users_csv.rb +++ b/app/lib/users_csv.rb @@ -1,4 +1,4 @@ -class UsersCsv < RenderCSV +class UsersCsv < RenderCsv include ApplicationHelper def header diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index 52e1354f..90c8a062 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -81,7 +81,7 @@ class Mailer < ActionMailer::Base add_order_result_attachments order, options - subject = I18n.t('mailer.order_result_supplier.subject', :name => order.supplier.name) + subject = I18n.t('mailer.order_result_supplier.subject', name: order.supplier.name) subject += " (#{I18n.t('activerecord.attributes.order.pickup')}: #{format_date(order.pickup)})" if order.pickup mail to: order.supplier.email, @@ -122,10 +122,11 @@ class Mailer < ActionMailer::Base if args[:from].is_a? User args[:reply_to] ||= args[:from] - args[:from] = format_address(FoodsoftConfig[:email_sender], I18n.t('mailer.from_via_foodsoft', name: show_user(args[:from]))) + args[:from] = + format_address(FoodsoftConfig[:email_sender], I18n.t('mailer.from_via_foodsoft', name: show_user(args[:from]))) end - [:bcc, :cc, :reply_to, :sender, :to].each do |k| + %i[bcc cc reply_to sender to].each do |k| user = args[k] args[k] = format_address(user.email, show_user(user)) if user.is_a? User end @@ -145,21 +146,21 @@ class Mailer < ActionMailer::Base def self.deliver_now_with_user_locale(user, &block) I18n.with_locale(user.settings['profile']['language']) do - self.deliver_now(&block) + deliver_now(&block) end end def self.deliver_now_with_default_locale(&block) I18n.with_locale(FoodsoftConfig[:default_locale]) do - self.deliver_now(&block) + deliver_now(&block) end end def self.deliver_now message = yield message.deliver_now - rescue => error - MailDeliveryStatus.create email: message.to[0], message: error.message + rescue StandardError => e + MailDeliveryStatus.create email: message.to[0], message: e.message end # separate method to allow plugins to mess with the attachments @@ -169,8 +170,7 @@ class Mailer < ActionMailer::Base end # separate method to allow plugins to mess with the text - def additonal_welcome_text(user) - end + def additonal_welcome_text(user); end private diff --git a/app/models/article.rb b/app/models/article.rb index 76a68605..561deaf8 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -42,7 +42,7 @@ class Article < ApplicationRecord belongs_to :supplier # @!attribute article_prices # @return [Array] Price history (current price first). - has_many :article_prices, -> { order("created_at DESC") } + has_many :article_prices, -> { order('created_at DESC') } # @!attribute order_articles # @return [Array] Order articles for this article. has_many :order_articles @@ -60,16 +60,16 @@ class Article < ApplicationRecord scope :not_in_stock, -> { where(type: nil) } # Validations - validates_presence_of :name, :unit, :price, :tax, :deposit, :unit_quantity, :supplier_id, :article_category - validates_length_of :name, :in => 4..60 - validates_length_of :unit, :in => 1..15 - validates_length_of :note, :maximum => 255 - validates_length_of :origin, :maximum => 255 - validates_length_of :manufacturer, :maximum => 255 - validates_length_of :order_number, :maximum => 255 - validates_numericality_of :price, :greater_than_or_equal_to => 0 - validates_numericality_of :unit_quantity, :greater_than => 0 - validates_numericality_of :deposit, :tax + validates :name, :unit, :price, :tax, :deposit, :unit_quantity, :supplier_id, :article_category, presence: true + validates :name, length: { in: 4..60 } + validates :unit, length: { in: 1..15 } + validates :note, length: { maximum: 255 } + validates :origin, length: { maximum: 255 } + validates :manufacturer, length: { maximum: 255 } + validates :order_number, length: { maximum: 255 } + validates :price, numericality: { greater_than_or_equal_to: 0 } + validates :unit_quantity, numericality: { greater_than: 0 } + validates :deposit, :tax, numericality: true # validates_uniqueness_of :name, :scope => [:supplier_id, :deleted_at, :type], if: Proc.new {|a| a.supplier.shared_sync_method.blank? or a.supplier.shared_sync_method == 'import' } # validates_uniqueness_of :name, :scope => [:supplier_id, :deleted_at, :type, :unit, :unit_quantity] validate :uniqueness_of_name @@ -78,12 +78,12 @@ class Article < ApplicationRecord before_save :update_price_history before_destroy :check_article_in_use - def self.ransackable_attributes(auth_object = nil) - %w(id name supplier_id article_category_id unit note manufacturer origin unit_quantity order_number) + def self.ransackable_attributes(_auth_object = nil) + %w[id name supplier_id article_category_id unit note manufacturer origin unit_quantity order_number] end - def self.ransackable_associations(auth_object = nil) - %w(article_category supplier order_articles orders) + def self.ransackable_associations(_auth_object = nil) + %w[article_category supplier order_articles orders] end # Returns true if article has been updated at least 2 days ago @@ -96,7 +96,7 @@ class Article < ApplicationRecord @in_open_order ||= begin order_articles = OrderArticle.where(order_id: Order.open.collect(&:id)) order_article = order_articles.detect { |oa| oa.article_id == id } - order_article ? order_article.order : nil + order_article&.order end end @@ -112,15 +112,15 @@ class Article < ApplicationRecord def shared_article_changed?(supplier = self.supplier) # skip early if the timestamp hasn't changed shared_article = self.shared_article(supplier) - unless shared_article.nil? || self.shared_updated_on == shared_article.updated_on - attrs = unequal_attributes(shared_article) - if attrs.empty? - # when attributes not changed, update timestamp of article - self.update_attribute(:shared_updated_on, shared_article.updated_on) - false - else - attrs - end + return if shared_article.nil? || shared_updated_on == shared_article.updated_on + + attrs = unequal_attributes(shared_article) + if attrs.empty? + # when attributes not changed, update timestamp of article + update_attribute(:shared_updated_on, shared_article.updated_on) + false + else + attrs end end @@ -131,30 +131,31 @@ class Article < ApplicationRecord def unequal_attributes(new_article, options = {}) # try to convert different units when desired if options[:convert_units] == false - new_price, new_unit_quantity = nil, nil + new_price = nil + new_unit_quantity = nil else new_price, new_unit_quantity = convert_units(new_article) end if new_price && new_unit_quantity - new_unit = self.unit + new_unit = unit else new_price = new_article.price new_unit_quantity = new_article.unit_quantity new_unit = new_article.unit end - return Article.compare_attributes( + Article.compare_attributes( { - :name => [self.name, new_article.name], - :manufacturer => [self.manufacturer, new_article.manufacturer.to_s], - :origin => [self.origin, new_article.origin], - :unit => [self.unit, new_unit], - :price => [self.price.to_f.round(2), new_price.to_f.round(2)], - :tax => [self.tax, new_article.tax], - :deposit => [self.deposit.to_f.round(2), new_article.deposit.to_f.round(2)], + name: [name, new_article.name], + manufacturer: [manufacturer, new_article.manufacturer.to_s], + origin: [origin, new_article.origin], + unit: [unit, new_unit], + price: [price.to_f.round(2), new_price.to_f.round(2)], + tax: [tax, new_article.tax], + deposit: [deposit.to_f.round(2), new_article.deposit.to_f.round(2)], # take care of different num-objects. - :unit_quantity => [self.unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f], - :note => [self.note.to_s, new_article.note.to_s] + unit_quantity: [unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f], + note: [note.to_s, new_article.note.to_s] } ) end @@ -165,14 +166,20 @@ class Article < ApplicationRecord # @param attributes [Hash] Attributes with old and new values # @return [Hash] Changed attributes with new values def self.compare_attributes(attributes) - unequal_attributes = attributes.select { |name, values| values[0] != values[1] && !(values[0].blank? && values[1].blank?) } - Hash[unequal_attributes.to_a.map { |a| [a[0], a[1].last] }] + unequal_attributes = attributes.select do |_name, values| + values[0] != values[1] && !(values[0].blank? && values[1].blank?) + end + unequal_attributes.to_a.map { |a| [a[0], a[1].last] }.to_h end # to get the correspondent shared article def shared_article(supplier = self.supplier) - self.order_number.blank? and return nil - @shared_article ||= supplier.shared_supplier.find_article_by_number(self.order_number) rescue nil + order_number.blank? and return nil + @shared_article ||= begin + supplier.shared_supplier.find_article_by_number(order_number) + rescue StandardError + nil + end end # convert units in foodcoop-size @@ -181,31 +188,37 @@ class Article < ApplicationRecord # returns false if units aren't foodsoft-compatible # returns nil if units are eqal def convert_units(new_article = shared_article) - if unit != new_article.unit - # legacy, used by foodcoops in Germany - if new_article.unit == "KI" && unit == "ST" # 'KI' means a box, with a different amount of items in it - # try to match the size out of its name, e.g. "banana 10-12 St" => 10 - new_unit_quantity = /[0-9\-\s]+(St)/.match(new_article.name).to_s.to_i - if new_unit_quantity && new_unit_quantity > 0 - new_price = (new_article.price / new_unit_quantity.to_f).round(2) - [new_price, new_unit_quantity] - else - false - end - else # use ruby-units to convert - fc_unit = (::Unit.new(unit) rescue nil) - supplier_unit = (::Unit.new(new_article.unit) rescue nil) - if fc_unit && supplier_unit && fc_unit =~ supplier_unit - conversion_factor = (supplier_unit / fc_unit).to_base.to_r - new_price = new_article.price / conversion_factor - new_unit_quantity = new_article.unit_quantity * conversion_factor - [new_price, new_unit_quantity] - else - false - end + return unless unit != new_article.unit + + # legacy, used by foodcoops in Germany + if new_article.unit == 'KI' && unit == 'ST' # 'KI' means a box, with a different amount of items in it + # try to match the size out of its name, e.g. "banana 10-12 St" => 10 + new_unit_quantity = /[0-9\-\s]+(St)/.match(new_article.name).to_s.to_i + if new_unit_quantity && new_unit_quantity > 0 + new_price = (new_article.price / new_unit_quantity.to_f).round(2) + [new_price, new_unit_quantity] + else + false + end + else # use ruby-units to convert + fc_unit = begin + ::Unit.new(unit) + rescue StandardError + nil + end + supplier_unit = begin + ::Unit.new(new_article.unit) + rescue StandardError + nil + end + if fc_unit != 0 && supplier_unit != 0 && fc_unit && supplier_unit && fc_unit =~ supplier_unit + conversion_factor = (supplier_unit / fc_unit).to_base.to_r + new_price = new_article.price / conversion_factor + new_unit_quantity = new_article.unit_quantity * conversion_factor + [new_price, new_unit_quantity] + else + false end - else - nil end end @@ -222,19 +235,19 @@ class Article < ApplicationRecord # Checks if the article is in use before it will deleted def check_article_in_use - raise I18n.t('articles.model.error_in_use', :article => self.name.to_s) if self.in_open_order + raise I18n.t('articles.model.error_in_use', article: name.to_s) if in_open_order end # Create an ArticlePrice, when the price-attr are changed. def update_price_history - if price_changed? - article_prices.build( - :price => price, - :tax => tax, - :deposit => deposit, - :unit_quantity => unit_quantity - ) - end + return unless price_changed? + + article_prices.build( + price: price, + tax: tax, + deposit: deposit, + unit_quantity: unit_quantity + ) end def price_changed? @@ -250,8 +263,8 @@ class Article < ApplicationRecord # supplier should always be there - except, perhaps, on initialization (on seeding) if supplier && (supplier.shared_sync_method.blank? || supplier.shared_sync_method == 'import') errors.add :name, :taken if matches.any? - else - errors.add :name, :taken_with_unit if matches.where(unit: unit, unit_quantity: unit_quantity).any? + elsif matches.where(unit: unit, unit_quantity: unit_quantity).any? + errors.add :name, :taken_with_unit end end end diff --git a/app/models/article_category.rb b/app/models/article_category.rb index 28597a59..1574b5d5 100644 --- a/app/models/article_category.rb +++ b/app/models/article_category.rb @@ -17,16 +17,16 @@ class ArticleCategory < ApplicationRecord normalize_attributes :name, :description - validates :name, :presence => true, :uniqueness => true, :length => { :minimum => 2 } + validates :name, presence: true, uniqueness: true, length: { minimum: 2 } before_destroy :check_for_associated_articles - def self.ransackable_attributes(auth_object = nil) - %w(id name) + def self.ransackable_attributes(_auth_object = nil) + %w[id name] end - def self.ransackable_associations(auth_object = nil) - %w(articles order_articles orders) + def self.ransackable_associations(_auth_object = nil) + %w[articles order_articles orders] end # Find a category that matches a category name; may return nil. @@ -40,7 +40,11 @@ class ArticleCategory < ApplicationRecord # case-insensitive substring match (take the closest match = shortest) c = ArticleCategory.where('name LIKE ?', "%#{category}%") unless c && c.any? # case-insensitive phrase present in category description - c = ArticleCategory.where('description LIKE ?', "%#{category}%").select { |s| s.description.match /(^|,)\s*#{category}\s*(,|$)/i } unless c && c.any? + unless c && c.any? + c = ArticleCategory.where('description LIKE ?', "%#{category}%").select do |s| + s.description.match(/(^|,)\s*#{category}\s*(,|$)/i) + end + end # return closest match if there are multiple c = c.sort_by { |s| s.name.length }.first if c.respond_to? :sort_by c @@ -50,6 +54,9 @@ class ArticleCategory < ApplicationRecord # Deny deleting the category when there are associated articles. def check_for_associated_articles - raise I18n.t('activerecord.errors.has_many_left', collection: Article.model_name.human) if articles.undeleted.exists? + return unless articles.undeleted.exists? + + raise I18n.t('activerecord.errors.has_many_left', + collection: Article.model_name.human) end end diff --git a/app/models/article_price.rb b/app/models/article_price.rb index f6879eac..ac3b2b4c 100644 --- a/app/models/article_price.rb +++ b/app/models/article_price.rb @@ -24,8 +24,8 @@ class ArticlePrice < ApplicationRecord localize_input_of :price, :tax, :deposit - validates_presence_of :price, :tax, :deposit, :unit_quantity - validates_numericality_of :price, :greater_than_or_equal_to => 0 - validates_numericality_of :unit_quantity, :greater_than => 0 - validates_numericality_of :deposit, :tax + validates :price, :tax, :deposit, :unit_quantity, presence: true + validates :price, numericality: { greater_than_or_equal_to: 0 } + validates :unit_quantity, numericality: { greater_than: 0 } + validates :deposit, :tax, numericality: true end diff --git a/app/models/bank_account.rb b/app/models/bank_account.rb index de15ee4b..f433b48a 100644 --- a/app/models/bank_account.rb +++ b/app/models/bank_account.rb @@ -5,10 +5,10 @@ class BankAccount < ApplicationRecord normalize_attributes :name, :iban, :description - validates :name, :presence => true, :uniqueness => true, :length => { :minimum => 2 } - validates :iban, :presence => true, :uniqueness => true - validates_format_of :iban, :with => /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/ - validates_numericality_of :balance, :message => I18n.t('bank_account.model.invalid_balance') + validates :name, presence: true, uniqueness: true, length: { minimum: 2 } + validates :iban, presence: true, uniqueness: true + validates :iban, format: { with: /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/ } + validates :balance, numericality: { message: I18n.t('bank_account.model.invalid_balance') } # @return [Function] Method wich can be called to import transaction from a bank or nil if unsupported def find_connector @@ -18,10 +18,8 @@ class BankAccount < ApplicationRecord def assign_unlinked_transactions count = 0 - bank_transactions.without_financial_link.includes(:supplier, :user).each do |t| - if t.assign_to_ordergroup || t.assign_to_invoice - count += 1 - end + bank_transactions.without_financial_link.includes(:supplier, :user).find_each do |t| + count += 1 if t.assign_to_ordergroup || t.assign_to_invoice end count end diff --git a/app/models/bank_gateway.rb b/app/models/bank_gateway.rb index 3811f128..f8043755 100644 --- a/app/models/bank_gateway.rb +++ b/app/models/bank_gateway.rb @@ -4,5 +4,5 @@ class BankGateway < ApplicationRecord scope :with_unattended_support, -> { where.not(unattended_user: nil) } - validates_presence_of :name, :url + validates :name, :url, presence: true end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 5d9d6c04..0f74d1e0 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -22,8 +22,8 @@ class BankTransaction < ApplicationRecord belongs_to :supplier, optional: true, foreign_key: 'iban', primary_key: 'iban' belongs_to :user, optional: true, foreign_key: 'iban', primary_key: 'iban' - validates_presence_of :date, :amount, :bank_account_id - validates_numericality_of :amount + validates :date, :amount, :bank_account_id, presence: true + validates :amount, numericality: true scope :without_financial_link, -> { where(financial_link: nil) } @@ -31,13 +31,13 @@ class BankTransaction < ApplicationRecord localize_input_of :amount def image_url - 'data:image/png;base64,' + Base64.encode64(self.image) + 'data:image/png;base64,' + Base64.encode64(image) end def assign_to_invoice return false unless supplier - content = text || "" + content = text || '' content += "\n" + reference if reference.present? invoices = supplier.invoices.unpaid.select { |i| content.include? i.number } invoices_sum = invoices.map(&:amount).sum @@ -49,7 +49,7 @@ class BankTransaction < ApplicationRecord update_attribute :financial_link, link end - return true + true end def assign_to_ordergroup @@ -78,6 +78,6 @@ class BankTransaction < ApplicationRecord update_attribute :financial_link, link end - return true + true end end diff --git a/app/models/concerns/custom_fields.rb b/app/models/concerns/custom_fields.rb index d54cebe5..aafec389 100644 --- a/app/models/concerns/custom_fields.rb +++ b/app/models/concerns/custom_fields.rb @@ -10,7 +10,7 @@ module CustomFields end after_save do - self.settings.custom_fields = custom_fields if custom_fields + settings.custom_fields = custom_fields if custom_fields end end end diff --git a/app/models/concerns/find_each_with_order.rb b/app/models/concerns/find_each_with_order.rb index 0e7cd5cd..faf545b2 100644 --- a/app/models/concerns/find_each_with_order.rb +++ b/app/models/concerns/find_each_with_order.rb @@ -3,9 +3,9 @@ module FindEachWithOrder extend ActiveSupport::Concern class_methods do - def find_each_with_order(options = {}) + def find_each_with_order(options = {}, &block) find_in_batches_with_order(options) do |records| - records.each { |record| yield record } + records.each(&block) end end diff --git a/app/models/concerns/localize_input.rb b/app/models/concerns/localize_input.rb index cfb44a44..7cd26acd 100644 --- a/app/models/concerns/localize_input.rb +++ b/app/models/concerns/localize_input.rb @@ -8,9 +8,9 @@ module LocalizeInput separator = I18n.t("separator", scope: "number.format") delimiter = I18n.t("delimiter", scope: "number.format") input.gsub!(delimiter, "") if input.match(/\d+#{Regexp.escape(delimiter)}+\d+#{Regexp.escape(separator)}+\d+/) # Remove delimiter - input.gsub!(separator, ".") # Replace separator with db compatible character + input.gsub!(separator, ".") or input.gsub!(",", ".") # Replace separator with db compatible character input - rescue + rescue StandardError Rails.logger.warn "Can't localize input: #{input}" input end diff --git a/app/models/concerns/mark_as_deleted_with_name.rb b/app/models/concerns/mark_as_deleted_with_name.rb index 4b888438..fb0aa590 100644 --- a/app/models/concerns/mark_as_deleted_with_name.rb +++ b/app/models/concerns/mark_as_deleted_with_name.rb @@ -3,7 +3,7 @@ module MarkAsDeletedWithName def mark_as_deleted # get maximum length of name - max_length = 100000 + max_length = 100_000 if lenval = self.class.validators_on(:name).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) } max_length = lenval.options[:maximum] end diff --git a/app/models/concerns/price_calculation.rb b/app/models/concerns/price_calculation.rb index 03b9a7ad..8d56d671 100644 --- a/app/models/concerns/price_calculation.rb +++ b/app/models/concerns/price_calculation.rb @@ -9,12 +9,12 @@ module PriceCalculation # @return [Number] Price for the foodcoop-member. def fc_price - add_percent(gross_price, FoodsoftConfig[:price_markup]) + add_percent(gross_price, FoodsoftConfig[:price_markup].to_i) end private def add_percent(value, percent) - (value * (percent * 0.01 + 1)).round(2) + (value * ((percent * 0.01) + 1)).round(2) end end diff --git a/app/models/delivery.rb b/app/models/delivery.rb index ab5ca5ec..bb2aed45 100644 --- a/app/models/delivery.rb +++ b/app/models/delivery.rb @@ -4,10 +4,10 @@ class Delivery < StockEvent scope :recent, -> { order('created_at DESC').limit(10) } - validates_presence_of :supplier_id + validates :supplier_id, presence: true validate :stock_articles_must_be_unique - accepts_nested_attributes_for :stock_changes, :allow_destroy => :true + accepts_nested_attributes_for :stock_changes, allow_destroy: :true def new_stock_changes=(stock_change_attributes) for attributes in stock_change_attributes @@ -16,7 +16,7 @@ class Delivery < StockEvent end def includes_article?(article) - self.stock_changes.map { |stock_change| stock_change.stock_article.id }.include? article.id + stock_changes.map { |stock_change| stock_change.stock_article.id }.include? article.id end def sum(type = :gross) @@ -39,8 +39,8 @@ class Delivery < StockEvent protected def stock_articles_must_be_unique - unless stock_changes.reject { |sc| sc.marked_for_destruction? }.map { |sc| sc.stock_article.id }.uniq!.nil? - errors.add(:base, I18n.t('model.delivery.each_stock_article_must_be_unique')) - end + return if stock_changes.reject { |sc| sc.marked_for_destruction? }.map { |sc| sc.stock_article.id }.uniq!.nil? + + errors.add(:base, I18n.t('model.delivery.each_stock_article_must_be_unique')) end end diff --git a/app/models/financial_link.rb b/app/models/financial_link.rb index 30a1955c..51108cd2 100644 --- a/app/models/financial_link.rb +++ b/app/models/financial_link.rb @@ -4,13 +4,13 @@ class FinancialLink < ApplicationRecord has_many :invoices scope :incomplete, -> { with_full_sum.where.not('full_sums.full_sum' => 0) } - scope :unused, -> { + scope :unused, lambda { includes(:bank_transactions, :financial_transactions, :invoices) .where(bank_transactions: { financial_link_id: nil }) .where(financial_transactions: { financial_link_id: nil }) .where(invoices: { financial_link_id: nil }) } - scope :with_full_sum, -> { + scope :with_full_sum, lambda { select(:id, :note, :full_sum).joins(<<-SQL) LEFT JOIN ( SELECT id, COALESCE(bt_sum, 0) - COALESCE(ft_sum, 0) + COALESCE(i_sum, 0) AS full_sum diff --git a/app/models/financial_transaction.rb b/app/models/financial_transaction.rb index bd2c4e58..1556ecbe 100644 --- a/app/models/financial_transaction.rb +++ b/app/models/financial_transaction.rb @@ -8,14 +8,16 @@ class FinancialTransaction < ApplicationRecord belongs_to :financial_link, optional: true belongs_to :financial_transaction_type belongs_to :group_order, optional: true - belongs_to :reverts, optional: true, class_name: 'FinancialTransaction', foreign_key: 'reverts_id' + belongs_to :reverts, optional: true, class_name: 'FinancialTransaction' has_one :reverted_by, class_name: 'FinancialTransaction', foreign_key: 'reverts_id' - validates_presence_of :amount, :note, :user_id - validates_numericality_of :amount, greater_then: -100_000, - less_than: 100_000 + validates :amount, :note, :user_id, presence: true + validates :amount, numericality: { greater_then: -100_000, + less_than: 100_000 } - scope :visible, -> { joins('LEFT JOIN financial_transactions r ON financial_transactions.id = r.reverts_id').where('r.id IS NULL').where(reverts: nil) } + scope :visible, lambda { + joins('LEFT JOIN financial_transactions r ON financial_transactions.id = r.reverts_id').where('r.id IS NULL').where(reverts: nil) + } scope :without_financial_link, -> { where(financial_link: nil) } scope :with_ordergroup, -> { where.not(ordergroup: nil) } @@ -28,12 +30,12 @@ class FinancialTransaction < ApplicationRecord # @todo remove alias (and rename created_on to created_at below) after #575 ransack_alias :created_at, :created_on - def self.ransackable_attributes(auth_object = nil) - %w(id amount note created_on user_id) + def self.ransackable_attributes(_auth_object = nil) + %w[id amount note created_on user_id] end - def self.ransackable_associations(auth_object = nil) - %w() # none, and certainly not user until we've secured that more + def self.ransackable_associations(_auth_object = nil) + %w[] # none, and certainly not user until we've secured that more end # Use this save method instead of simple save and after callback diff --git a/app/models/financial_transaction_class.rb b/app/models/financial_transaction_class.rb index 43ded5fd..0c924993 100644 --- a/app/models/financial_transaction_class.rb +++ b/app/models/financial_transaction_class.rb @@ -5,7 +5,7 @@ class FinancialTransactionClass < ApplicationRecord has_many :ordergroups, -> { distinct }, through: :financial_transactions validates :name, presence: true - validates_uniqueness_of :name + validates :name, uniqueness: true after_save :update_balance_of_ordergroups diff --git a/app/models/financial_transaction_type.rb b/app/models/financial_transaction_type.rb index 392a1a95..97ed7979 100644 --- a/app/models/financial_transaction_type.rb +++ b/app/models/financial_transaction_type.rb @@ -5,13 +5,13 @@ class FinancialTransactionType < ApplicationRecord has_many :ordergroups, -> { distinct }, through: :financial_transactions validates :name, presence: true - validates_uniqueness_of :name - validates_uniqueness_of :name_short, allow_blank: true, allow_nil: true - validates_format_of :name_short, :with => /\A[A-Za-z]*\z/ + validates :name, uniqueness: true + validates :name_short, uniqueness: { allow_blank: true } + validates :name_short, format: { with: /\A[A-Za-z]*\z/ } validates :financial_transaction_class, presence: true - after_save :update_balance_of_ordergroups before_destroy :restrict_deleting_last_financial_transaction_type + after_save :update_balance_of_ordergroups scope :with_name_short, -> { where.not(name_short: [nil, '']) } @@ -20,7 +20,7 @@ class FinancialTransactionType < ApplicationRecord end def self.has_multiple_types - self.count > 1 + count > 1 end protected diff --git a/app/models/group.rb b/app/models/group.rb index a667ea5a..a4a770eb 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -7,8 +7,8 @@ class Group < ApplicationRecord has_many :memberships, dependent: :destroy has_many :users, -> { where(deleted_at: nil) }, through: :memberships - validates :name, :presence => true, :length => { :in => 1..25 } - validates_uniqueness_of :name + validates :name, presence: true, length: { in: 1..25 } + validates :name, uniqueness: true attr_reader :user_tokens @@ -25,7 +25,7 @@ class Group < ApplicationRecord end def user_tokens=(ids) - self.user_ids = ids.split(",") + self.user_ids = ids.split(',') end def deleted? diff --git a/app/models/group_order.rb b/app/models/group_order.rb index c789ef4e..183b663a 100644 --- a/app/models/group_order.rb +++ b/app/models/group_order.rb @@ -6,14 +6,14 @@ class GroupOrder < ApplicationRecord belongs_to :order belongs_to :ordergroup, optional: true - has_many :group_order_articles, :dependent => :destroy - has_many :order_articles, :through => :group_order_articles + has_many :group_order_articles, dependent: :destroy + has_many :order_articles, through: :group_order_articles has_one :financial_transaction belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id' - validates_presence_of :order_id - validates_numericality_of :price - validates_uniqueness_of :ordergroup_id, :scope => :order_id # order groups can only order once per order + validates :order_id, presence: true + validates :price, numericality: true + validates :ordergroup_id, uniqueness: { scope: :order_id } # order groups can only order once per order scope :in_open_orders, -> { joins(:order).merge(Order.open) } scope :in_finished_orders, -> { joins(:order).merge(Order.finished_not_closed) } @@ -21,40 +21,40 @@ class GroupOrder < ApplicationRecord scope :ordered, -> { includes(:ordergroup).order('groups.name') } - def self.ransackable_attributes(auth_object = nil) - %w(id price) + def self.ransackable_attributes(_auth_object = nil) + %w[id price] end - def self.ransackable_associations(auth_object = nil) - %w(order group_order_articles) + def self.ransackable_associations(_auth_object = nil) + %w[order group_order_articles] end # Generate some data for the javascript methods in ordering view def load_data data = {} - data[:account_balance] = ordergroup.nil? ? BigDecimal.new('+Infinity') : ordergroup.account_balance - data[:available_funds] = ordergroup.nil? ? BigDecimal.new('+Infinity') : ordergroup.get_available_funds(self) + data[:account_balance] = ordergroup.nil? ? BigDecimal('+Infinity') : ordergroup.account_balance + data[:available_funds] = ordergroup.nil? ? BigDecimal('+Infinity') : ordergroup.get_available_funds(self) # load prices and other stuff.... data[:order_articles] = {} - order.articles_grouped_by_category.each do |article_category, order_articles| + order.articles_grouped_by_category.each do |_article_category, order_articles| order_articles.each do |order_article| # Get the result of last time ordering, if possible goa = group_order_articles.detect { |goa| goa.order_article_id == order_article.id } # Build hash with relevant data data[:order_articles][order_article.id] = { - :price => order_article.article.fc_price, - :unit => order_article.article.unit_quantity, - :quantity => (goa ? goa.quantity : 0), - :others_quantity => order_article.quantity - (goa ? goa.quantity : 0), - :used_quantity => (goa ? goa.result(:quantity) : 0), - :tolerance => (goa ? goa.tolerance : 0), - :others_tolerance => order_article.tolerance - (goa ? goa.tolerance : 0), - :used_tolerance => (goa ? goa.result(:tolerance) : 0), - :total_price => (goa ? goa.total_price : 0), - :missing_units => order_article.missing_units, - :quantity_available => (order.stockit? ? order_article.article.quantity_available : 0) + price: order_article.article.fc_price, + unit: order_article.article.unit_quantity, + quantity: (goa ? goa.quantity : 0), + others_quantity: order_article.quantity - (goa ? goa.quantity : 0), + used_quantity: (goa ? goa.result(:quantity) : 0), + tolerance: (goa ? goa.tolerance : 0), + others_tolerance: order_article.tolerance - (goa ? goa.tolerance : 0), + used_tolerance: (goa ? goa.result(:tolerance) : 0), + total_price: (goa ? goa.total_price : 0), + missing_units: order_article.missing_units, + quantity_available: (order.stockit? ? order_article.article.quantity_available : 0) } end end @@ -69,12 +69,12 @@ class GroupOrder < ApplicationRecord # Get ordered quantities and update group_order_articles/_quantities... if group_order_articles_attributes - quantities = group_order_articles_attributes.fetch(order_article.id.to_s, { :quantity => 0, :tolerance => 0 }) + quantities = group_order_articles_attributes.fetch(order_article.id.to_s, { quantity: 0, tolerance: 0 }) group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i) end # Also update results for the order_article - logger.debug "[save_group_order_articles] update order_article.results!" + logger.debug '[save_group_order_articles] update order_article.results!' order_article.update_results! end @@ -83,7 +83,7 @@ class GroupOrder < ApplicationRecord # Updates the "price" attribute. def update_price! - total = group_order_articles.includes(:order_article => [:article, :article_price]).to_a.sum(&:total_price) + total = group_order_articles.includes(order_article: %i[article article_price]).to_a.sum(&:total_price) update_attribute(:price, total) end @@ -97,7 +97,12 @@ class GroupOrder < ApplicationRecord end def ordergroup_name - ordergroup ? ordergroup.name : I18n.t('model.group_order.stock_ordergroup_name', :user => updated_by.try(:name) || '?') + if ordergroup + ordergroup.name + else + I18n.t('model.group_order.stock_ordergroup_name', + user: updated_by.try(:name) || '?') + end end def total diff --git a/app/models/group_order_article.rb b/app/models/group_order_article.rb index 5a02734d..7b95d462 100644 --- a/app/models/group_order_article.rb +++ b/app/models/group_order_article.rb @@ -8,21 +8,21 @@ class GroupOrderArticle < ApplicationRecord belongs_to :order_article has_many :group_order_article_quantities, dependent: :destroy - validates_presence_of :group_order, :order_article - validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order + validates :group_order, :order_article, presence: true + validates :order_article_id, uniqueness: { scope: :group_order_id } # just once an article per group order validate :check_order_not_closed # don't allow changes to closed (aka settled) orders validates :quantity, :tolerance, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - scope :ordered, -> { includes(:group_order => :ordergroup).order('groups.name') } + scope :ordered, -> { includes(group_order: :ordergroup).order('groups.name') } localize_input_of :result - def self.ransackable_attributes(auth_object = nil) - %w(id quantity tolerance result) + def self.ransackable_attributes(_auth_object = nil) + %w[id quantity tolerance result] end - def self.ransackable_associations(auth_object = nil) - %w(order_article group_order) + def self.ransackable_associations(_auth_object = nil) + %w[order_article group_order] end # Setter used in group_order_article#new @@ -32,7 +32,7 @@ class GroupOrderArticle < ApplicationRecord end def ordergroup_id - group_order.try!(:ordergroup_id) + group_order&.ordergroup_id end # Updates the quantity/tolerance for this GroupOrderArticle by updating both GroupOrderArticle properties @@ -45,7 +45,7 @@ class GroupOrderArticle < ApplicationRecord # When quantity and tolerance are zero, we don't serve any purpose if quantity == 0 && tolerance == 0 - logger.debug("Self-destructing since requested quantity and tolerance are zero") + logger.debug('Self-destructing since requested quantity and tolerance are zero') destroy! return end @@ -54,26 +54,28 @@ class GroupOrderArticle < ApplicationRecord quantities = group_order_article_quantities.order('created_on DESC').to_a logger.debug("GroupOrderArticleQuantity items found: #{quantities.size}") - if (quantities.size == 0) + if quantities.size == 0 # There is no GroupOrderArticleQuantity item yet, just insert with desired quantities... - logger.debug("No quantities entry at all, inserting a new one with the desired quantities") - quantities.push(GroupOrderArticleQuantity.new(:group_order_article => self, :quantity => quantity, :tolerance => tolerance)) - self.quantity, self.tolerance = quantity, tolerance + logger.debug('No quantities entry at all, inserting a new one with the desired quantities') + quantities.push(GroupOrderArticleQuantity.new(group_order_article: self, quantity: quantity, + tolerance: tolerance)) + self.quantity = quantity + self.tolerance = tolerance else # Decrease quantity/tolerance if necessary by going through the existing items and decreasing their values... i = 0 - while (i < quantities.size && (quantity < self.quantity || tolerance < self.tolerance)) + while i < quantities.size && (quantity < self.quantity || tolerance < self.tolerance) logger.debug("Need to decrease quantities for GroupOrderArticleQuantity[#{quantities[i].id}]") - if (quantity < self.quantity && quantities[i].quantity > 0) + if quantity < self.quantity && quantities[i].quantity > 0 delta = self.quantity - quantity - delta = (delta > quantities[i].quantity ? quantities[i].quantity : delta) + delta = [delta, quantities[i].quantity].min logger.debug("Decreasing quantity by #{delta}") quantities[i].quantity -= delta self.quantity -= delta end - if (tolerance < self.tolerance && quantities[i].tolerance > 0) + if tolerance < self.tolerance && quantities[i].tolerance > 0 delta = self.tolerance - tolerance - delta = (delta > quantities[i].tolerance ? quantities[i].tolerance : delta) + delta = [delta, quantities[i].tolerance].min logger.debug("Decreasing tolerance by #{delta}") quantities[i].tolerance -= delta self.tolerance -= delta @@ -81,12 +83,12 @@ class GroupOrderArticle < ApplicationRecord i += 1 end # If there is at least one increased value: insert a new GroupOrderArticleQuantity object - if (quantity > self.quantity || tolerance > self.tolerance) - logger.debug("Inserting a new GroupOrderArticleQuantity") + if quantity > self.quantity || tolerance > self.tolerance + logger.debug('Inserting a new GroupOrderArticleQuantity') quantities.insert(0, GroupOrderArticleQuantity.new( - :group_order_article => self, - :quantity => (quantity > self.quantity ? quantity - self.quantity : 0), - :tolerance => (tolerance > self.tolerance ? tolerance - self.tolerance : 0) + group_order_article: self, + quantity: (quantity > self.quantity ? quantity - self.quantity : 0), + tolerance: (tolerance > self.tolerance ? tolerance - self.tolerance : 0) )) # Recalc totals: self.quantity += quantities[0].quantity @@ -95,8 +97,9 @@ class GroupOrderArticle < ApplicationRecord end # Check if something went terribly wrong and quantites have not been adjusted as desired. - if (self.quantity != quantity || self.tolerance != tolerance) - raise ActiveRecord::RecordNotSaved.new('Unable to update GroupOrderArticle/-Quantities to desired quantities!', self) + if self.quantity != quantity || self.tolerance != tolerance + raise ActiveRecord::RecordNotSaved.new('Unable to update GroupOrderArticle/-Quantities to desired quantities!', + self) end # Remove zero-only items. @@ -121,7 +124,7 @@ class GroupOrderArticle < ApplicationRecord quantity = tolerance = total_quantity = 0 # Get total - if not total.nil? + if !total.nil? logger.debug "<#{order_article.article.name}> => #{total} (given)" elsif order_article.article.is_a?(StockArticle) total = order_article.article.quantity @@ -145,7 +148,7 @@ class GroupOrderArticle < ApplicationRecord q = goaq.quantity q = [q, total - total_quantity].min if first_order_first_serve total_quantity += q - if goaq.group_order_article_id == self.id + if goaq.group_order_article_id == id logger.debug "increasing quantity by #{q}" quantity += q end @@ -154,11 +157,11 @@ class GroupOrderArticle < ApplicationRecord # Determine tolerance to be ordered... if total_quantity < total - logger.debug "determining additional items to be ordered from tolerance" + logger.debug 'determining additional items to be ordered from tolerance' order_quantities.each do |goaq| q = [goaq.tolerance, total - total_quantity].min total_quantity += q - if goaq.group_order_article_id == self.id + if goaq.group_order_article_id == id logger.debug "increasing tolerance by #{q}" tolerance += q end @@ -170,7 +173,7 @@ class GroupOrderArticle < ApplicationRecord end # memoize result unless a total is given - r = { :quantity => quantity, :tolerance => tolerance, :total => quantity + tolerance } + r = { quantity: quantity, tolerance: tolerance, total: quantity + tolerance } @calculate_result = r if total.nil? r end @@ -185,8 +188,8 @@ class GroupOrderArticle < ApplicationRecord # This is used for automatic distribution, e.g., in order.finish! or when receiving orders def save_results!(article_total = nil) new_result = calculate_result(article_total)[:total] - self.update_attribute(:result_computed, new_result) - self.update_attribute(:result, new_result) + update_attribute(:result_computed, new_result) + update_attribute(:result, new_result) end # Returns total price for this individual article @@ -213,8 +216,8 @@ class GroupOrderArticle < ApplicationRecord private def check_order_not_closed - if order_article.order.closed? - errors.add(:order_article, I18n.t('model.group_order_article.order_closed')) - end + return unless order_article.order.closed? + + errors.add(:order_article, I18n.t('model.group_order_article.order_closed')) end end diff --git a/app/models/group_order_article_quantity.rb b/app/models/group_order_article_quantity.rb index 1e29985f..12832b2c 100644 --- a/app/models/group_order_article_quantity.rb +++ b/app/models/group_order_article_quantity.rb @@ -4,5 +4,5 @@ class GroupOrderArticleQuantity < ApplicationRecord belongs_to :group_order_article - validates_presence_of :group_order_article_id + validates :group_order_article_id, presence: true end diff --git a/app/models/invite.rb b/app/models/invite.rb index e37a8a18..d471aa50 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -5,12 +5,12 @@ class Invite < ApplicationRecord belongs_to :user belongs_to :group - validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i - validates_presence_of :user - validates_presence_of :group - validates_presence_of :token - validates_presence_of :expires_at - validate :email_not_already_registered, :on => :create + validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } + validates :user, presence: true + validates :group, presence: true + validates :token, presence: true + validates :expires_at, presence: true + validate :email_not_already_registered, on: :create before_validation :set_token_and_expires_at @@ -19,15 +19,15 @@ class Invite < ApplicationRecord # Before validation, set token and expires_at. def set_token_and_expires_at self.token = Digest::SHA1.hexdigest(Time.now.to_s + rand(100).to_s) - self.expires_at = Time.now.advance(:days => 7) + self.expires_at = Time.now.advance(days: 7) end private # Custom validation: check that email does not already belong to a registered user. def email_not_already_registered - unless User.find_by_email(self.email).nil? - errors.add(:email, I18n.t('invites.errors.already_member')) - end + return if User.find_by_email(email).nil? + + errors.add(:email, I18n.t('invites.errors.already_member')) end end diff --git a/app/models/invoice.rb b/app/models/invoice.rb index f2a8866f..2bf3aaee 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -3,13 +3,13 @@ class Invoice < ApplicationRecord include LocalizeInput belongs_to :supplier - belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by_user_id' + belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id' belongs_to :financial_link, optional: true has_many :deliveries, dependent: :nullify has_many :orders, dependent: :nullify - validates_presence_of :supplier_id - validates_numericality_of :amount, :deposit, :deposit_credit + validates :supplier_id, presence: true + validates :amount, :deposit, :deposit_credit, numericality: true validate :valid_attachment scope :unpaid, -> { where(paid_on: nil) } @@ -23,18 +23,18 @@ class Invoice < ApplicationRecord def attachment=(incoming_file) self.attachment_data = incoming_file.read # allow to soft-fail when FileMagic isn't present and removed from Gemfile (e.g. Heroku) - self.attachment_mime = defined?(FileMagic) ? FileMagic.new(FileMagic::MAGIC_MIME).buffer(self.attachment_data) : 'application/octet-stream' + self.attachment_mime = defined?(FileMagic) ? FileMagic.new(FileMagic::MAGIC_MIME).buffer(attachment_data) : 'application/octet-stream' end def delete_attachment=(value) - if value == '1' - self.attachment_data = nil - self.attachment_mime = nil - end + return unless value == '1' + + self.attachment_data = nil + self.attachment_mime = nil end def user_can_edit?(user) - user.role_finance? || (user.role_invoices? && !self.paid_on && self.created_by.try(:id) == user.id) + user.role_finance? || (user.role_invoices? && !paid_on && created_by.try(:id) == user.id) end # Amount without deposit @@ -45,9 +45,9 @@ class Invoice < ApplicationRecord def orders_sum orders .joins(order_articles: [:article_price]) - .sum("COALESCE(order_articles.units_received, order_articles.units_billed, order_articles.units_to_order)" \ - + "* article_prices.unit_quantity" \ - + "* ROUND((article_prices.price + article_prices.deposit) * (100 + article_prices.tax) / 100, 2)") + .sum('COALESCE(order_articles.units_received, order_articles.units_billed, order_articles.units_to_order)' \ + + '* article_prices.unit_quantity' \ + + '* ROUND((article_prices.price + article_prices.deposit) * (100 + article_prices.tax) / 100, 2)') end def orders_transport_sum @@ -63,11 +63,11 @@ class Invoice < ApplicationRecord protected def valid_attachment - if attachment_data - mime = MIME::Type.simplified(attachment_mime) - unless ['application/pdf', 'image/jpeg'].include? mime - errors.add :attachment, I18n.t('model.invoice.invalid_mime', :mime => mime) - end - end + return unless attachment_data + + mime = MIME::Type.simplified(attachment_mime) + return if ['application/pdf', 'image/jpeg'].include? mime + + errors.add :attachment, I18n.t('model.invoice.invalid_mime', mime: mime) end end diff --git a/app/models/membership.rb b/app/models/membership.rb index bebf00e2..4ebc061c 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -8,6 +8,6 @@ class Membership < ApplicationRecord # check if this is the last admin-membership and deny def check_last_admin - raise I18n.t('model.membership.no_admin_delete') if self.group.role_admin? && self.group.memberships.size == 1 && Group.where(role_admin: true).count == 1 + raise I18n.t('model.membership.no_admin_delete') if group.role_admin? && group.memberships.size == 1 && Group.where(role_admin: true).count == 1 end end diff --git a/app/models/order.rb b/app/models/order.rb index e83307f3..ada62e59 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -2,29 +2,29 @@ class Order < ApplicationRecord attr_accessor :ignore_warnings, :transport_distribution # Associations - has_many :order_articles, :dependent => :destroy - has_many :articles, :through => :order_articles - has_many :group_orders, :dependent => :destroy - has_many :ordergroups, :through => :group_orders - has_many :users_ordered, :through => :ordergroups, :source => :users - has_many :comments, -> { order('created_at') }, :class_name => "OrderComment" + has_many :order_articles, dependent: :destroy + has_many :articles, through: :order_articles + has_many :group_orders, dependent: :destroy + has_many :ordergroups, through: :group_orders + has_many :users_ordered, through: :ordergroups, source: :users + has_many :comments, -> { order('created_at') }, class_name: 'OrderComment' has_many :stock_changes belongs_to :invoice, optional: true belongs_to :supplier, optional: true - belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by_user_id' - belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by_user_id' + belongs_to :updated_by, class_name: 'User', foreign_key: 'updated_by_user_id' + belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id' enum end_action: { no_end_action: 0, auto_close: 1, auto_close_and_send: 2, auto_close_and_send_min_quantity: 3 } - enum transport_distribution: [:skip, :ordergroup, :price, :articles] + enum transport_distribution: { skip: 0, ordergroup: 1, price: 2, articles: 3 } # Validations - validates_presence_of :starts + validates :starts, presence: true validate :starts_before_ends, :include_articles validate :keep_ordered_articles + before_validation :distribute_transport # Callbacks after_save :save_order_articles, :update_price_of_group_orders! - before_validation :distribute_transport # Finders scope :started, -> { where('starts <= ?', Time.now) } @@ -49,12 +49,12 @@ class Order < ApplicationRecord include DateTimeAttributeValidate date_time_attribute :starts, :boxfill, :ends - def self.ransackable_attributes(auth_object = nil) - %w(id state supplier_id starts boxfill ends pickup) + def self.ransackable_attributes(_auth_object = nil) + %w[id state supplier_id starts boxfill ends pickup] end - def self.ransackable_associations(auth_object = nil) - %w(supplier articles order_articles) + def self.ransackable_associations(_auth_object = nil) + %w[supplier articles order_articles] end def stockit? @@ -70,9 +70,9 @@ class Order < ApplicationRecord # make sure to include those articles which are no longer available # but which have already been ordered in this stock order StockArticle.available.includes(:article_category) - .order('article_categories.name', 'articles.name').reject { |a| + .order('article_categories.name', 'articles.name').reject do |a| a.quantity_available <= 0 && !a.ordered_in_order?(self) - }.group_by { |a| a.article_category.name } + end.group_by { |a| a.article_category.name } else supplier.articles.available.group_by { |a| a.article_category.name } end @@ -87,9 +87,7 @@ class Order < ApplicationRecord end # Save ids, and create/delete order_articles after successfully saved the order - def article_ids=(ids) - @article_ids = ids - end + attr_writer :article_ids def article_ids @article_ids ||= order_articles.map { |a| a.article_id.to_s } @@ -101,19 +99,19 @@ class Order < ApplicationRecord end def open? - state == "open" + state == 'open' end def finished? - state == "finished" || state == "received" + state == 'finished' || state == 'received' end def received? - state == "received" + state == 'received' end def closed? - state == "closed" + state == 'closed' end def boxfill? @@ -134,11 +132,18 @@ class Order < ApplicationRecord self.starts ||= Time.now if FoodsoftConfig[:order_schedule] # try to be smart when picking a reference day - last = (DateTime.parse(FoodsoftConfig[:order_schedule][:initial]) rescue nil) + last = begin + DateTime.parse(FoodsoftConfig[:order_schedule][:initial]) + rescue StandardError + nil + end last ||= Order.finished.reorder(:starts).first.try(:starts) last ||= self.starts # adjust boxfill and end date - self.boxfill ||= FoodsoftDateUtil.next_occurrence last, self.starts, FoodsoftConfig[:order_schedule][:boxfill] if is_boxfill_useful? + if is_boxfill_useful? + self.boxfill ||= FoodsoftDateUtil.next_occurrence last, self.starts, + FoodsoftConfig[:order_schedule][:boxfill] + end self.ends ||= FoodsoftDateUtil.next_occurrence last, self.starts, FoodsoftConfig[:order_schedule][:ends] end self @@ -149,7 +154,7 @@ class Order < ApplicationRecord def self.ordergroup_group_orders_map(ordergroup) orders = includes(:supplier) group_orders = GroupOrder.where(ordergroup_id: ordergroup.id, order_id: orders.map(&:id)) - group_orders_hash = Hash[group_orders.collect { |go| [go.order_id, go] }] + group_orders_hash = group_orders.index_by { |go| go.order_id } orders.map do |order| { order: order, @@ -160,11 +165,11 @@ class Order < ApplicationRecord # search GroupOrder of given Ordergroup def group_order(ordergroup) - group_orders.where(:ordergroup_id => ordergroup.id).first + group_orders.where(ordergroup_id: ordergroup.id).first end def stock_group_order - group_orders.where(:ordergroup_id => nil).first + group_orders.where(ordergroup_id: nil).first end # Returns OrderArticles in a nested Array, grouped by category and ordered by article name. @@ -172,7 +177,7 @@ class Order < ApplicationRecord # e.g: [["drugs",[teethpaste, toiletpaper]], ["fruits" => [apple, banana, lemon]]] def articles_grouped_by_category @articles_grouped_by_category ||= order_articles - .includes([:article_price, :group_order_articles, :article => :article_category]) + .includes([:article_price, :group_order_articles, { article: :article_category }]) .order('articles.name') .group_by { |a| a.article.article_category.name } .sort { |a, b| a[0] <=> b[0] } @@ -189,10 +194,10 @@ class Order < ApplicationRecord # FIXME: Consider order.foodcoop_result def profit(options = {}) markup = options[:without_markup] || false - if invoice - groups_sum = markup ? sum(:groups_without_markup) : sum(:groups) - groups_sum - invoice.net_amount - end + return unless invoice + + groups_sum = markup ? sum(:groups_without_markup) : sum(:groups) + groups_sum - invoice.net_amount end # Returns the all round price of a finished order @@ -202,7 +207,7 @@ class Order < ApplicationRecord # :fc, guess what... def sum(type = :gross) total = 0 - if type == :net || type == :gross || type == :fc + if %i[net gross fc].include?(type) for oa in order_articles.ordered.includes(:article, :article_price) quantity = oa.units * oa.price.unit_quantity case type @@ -214,8 +219,8 @@ class Order < ApplicationRecord total += quantity * oa.price.fc_price end end - elsif type == :groups || type == :groups_without_markup - for go in group_orders.includes(group_order_articles: { order_article: [:article, :article_price] }) + elsif %i[groups groups_without_markup].include?(type) + for go in group_orders.includes(group_order_articles: { order_article: %i[article article_price] }) for goa in go.group_order_articles case type when :groups @@ -232,36 +237,36 @@ class Order < ApplicationRecord # Finishes this order. This will set the order state to "finish" and the end property to the current time. # Ignored if the order is already finished. def finish!(user) - unless finished? - Order.transaction do - # set new order state (needed by notify_order_finished) - update!(state: 'finished', ends: Time.now, updated_by: user) + return if finished? - # Update order_articles. Save the current article_price to keep price consistency - # Also save results for each group_order_result - # Clean up - order_articles.includes(:article).each do |oa| - oa.update_attribute(:article_price, oa.article.article_prices.first) - oa.group_order_articles.each do |goa| - goa.save_results! - # Delete no longer required order-history (group_order_article_quantities) and - # TODO: Do we need articles, which aren't ordered? (units_to_order == 0 ?) - # A: Yes, we do - for redistributing articles when the number of articles - # delivered changes, and for statistics on popular articles. Records - # with both tolerance and quantity zero can be deleted. - # goa.group_order_article_quantities.clear - end + Order.transaction do + # set new order state (needed by notify_order_finished) + update!(state: 'finished', ends: Time.now, updated_by: user) + + # Update order_articles. Save the current article_price to keep price consistency + # Also save results for each group_order_result + # Clean up + order_articles.includes(:article).find_each do |oa| + oa.update_attribute(:article_price, oa.article.article_prices.first) + oa.group_order_articles.each do |goa| + goa.save_results! + # Delete no longer required order-history (group_order_article_quantities) and + # TODO: Do we need articles, which aren't ordered? (units_to_order == 0 ?) + # A: Yes, we do - for redistributing articles when the number of articles + # delivered changes, and for statistics on popular articles. Records + # with both tolerance and quantity zero can be deleted. + # goa.group_order_article_quantities.clear end - - # Update GroupOrder prices - group_orders.each(&:update_price!) - - # Stats - ordergroups.each(&:update_stats!) - - # Notifications - NotifyFinishedOrderJob.perform_later(self) end + + # Update GroupOrder prices + group_orders.each(&:update_price!) + + # Stats + ordergroups.each(&:update_stats!) + + # Notifications + NotifyFinishedOrderJob.perform_later(self) end end @@ -277,11 +282,11 @@ class Order < ApplicationRecord if stockit? # Decreases the quantity of stock_articles for oa in order_articles.includes(:article) oa.update_results! # Update units_to_order of order_article - stock_changes.create! :stock_article => oa.article, :quantity => oa.units_to_order * -1 + stock_changes.create! stock_article: oa.article, quantity: oa.units_to_order * -1 end end - self.update!(state: 'closed', updated_by: user, foodcoop_result: profit) + update!(state: 'closed', updated_by: user, foodcoop_result: profit) end end @@ -289,7 +294,10 @@ class Order < ApplicationRecord def close_direct!(user) raise I18n.t('orders.model.error_closed') if closed? - comments.create(user: user, text: I18n.t('orders.model.close_direct_message')) unless FoodsoftConfig[:charge_members_manually] + unless FoodsoftConfig[:charge_members_manually] + comments.create(user: user, + text: I18n.t('orders.model.close_direct_message')) + end update!(state: 'closed', updated_by: user) end @@ -313,13 +321,12 @@ class Order < ApplicationRecord end def self.finish_ended! - orders = Order.where.not(end_action: Order.end_actions[:no_end_action]).where(state: 'open').where('ends <= ?', DateTime.now) + orders = Order.where.not(end_action: Order.end_actions[:no_end_action]).where(state: 'open').where('ends <= ?', + DateTime.now) orders.each do |order| - begin - order.do_end_action! - rescue => error - ExceptionNotifier.notify_exception(error, data: { foodcoop: FoodsoftConfig.scope, order_id: order.id }) - end + order.do_end_action! + rescue StandardError => e + ExceptionNotifier.notify_exception(e, data: { foodcoop: FoodsoftConfig.scope, order_id: order.id }) end end @@ -329,7 +336,10 @@ class Order < ApplicationRecord delta = Rails.env.test? ? 1 : 0 # since Rails 4.2 tests appear to have time differences, with this validation failing errors.add(:ends, I18n.t('orders.model.error_starts_before_ends')) if ends && starts && ends <= (starts - delta) errors.add(:ends, I18n.t('orders.model.error_boxfill_before_ends')) if ends && boxfill && ends <= (boxfill - delta) - errors.add(:boxfill, I18n.t('orders.model.error_starts_before_boxfill')) if boxfill && starts && boxfill <= (starts - delta) + return unless boxfill && starts && boxfill <= (starts - delta) + + errors.add(:boxfill, + I18n.t('orders.model.error_starts_before_boxfill')) end def include_articles @@ -340,17 +350,17 @@ class Order < ApplicationRecord chosen_order_articles = order_articles.where(article_id: article_ids) to_be_removed = order_articles - chosen_order_articles to_be_removed_but_ordered = to_be_removed.select { |a| a.quantity > 0 || a.tolerance > 0 } - unless to_be_removed_but_ordered.empty? || ignore_warnings - errors.add(:articles, I18n.t(stockit? ? 'orders.model.warning_ordered_stock' : 'orders.model.warning_ordered')) - @erroneous_article_ids = to_be_removed_but_ordered.map { |a| a.article_id } - end + return if to_be_removed_but_ordered.empty? || ignore_warnings + + errors.add(:articles, I18n.t(stockit? ? 'orders.model.warning_ordered_stock' : 'orders.model.warning_ordered')) + @erroneous_article_ids = to_be_removed_but_ordered.map { |a| a.article_id } end def save_order_articles # fetch selected articles articles_list = Article.find(article_ids) # create new order_articles - (articles_list - articles).each { |article| order_articles.create(:article => article) } + (articles_list - articles).each { |article| order_articles.create(article: article) } # delete old order_articles articles.reject { |article| articles_list.include?(article) }.each do |article| order_articles.detect { |order_article| order_article.article_id == article.id }.destroy @@ -363,17 +373,17 @@ class Order < ApplicationRecord return unless group_orders.any? case transport_distribution.try(&:to_i) - when Order.transport_distributions[:ordergroup] then + when Order.transport_distributions[:ordergroup] amount = transport / group_orders.size group_orders.each do |go| go.transport = amount.ceil(2) end - when Order.transport_distributions[:price] then + when Order.transport_distributions[:price] amount = transport / group_orders.sum(:price) group_orders.each do |go| go.transport = (amount * go.price).ceil(2) end - when Order.transport_distributions[:articles] then + when Order.transport_distributions[:articles] amount = transport / group_orders.includes(:group_order_articles).sum(:result) group_orders.each do |go| go.transport = (amount * go.group_order_articles.sum(:result)).ceil(2) @@ -389,7 +399,7 @@ class Order < ApplicationRecord def charge_group_orders!(user, transaction_type = nil) note = transaction_note - group_orders.includes(:ordergroup).each do |group_order| + group_orders.includes(:ordergroup).find_each do |group_order| if group_order.ordergroup price = group_order.total * -1 # decrease! account balance group_order.ordergroup.add_financial_transaction!(price, note, user, transaction_type, nil, group_order) diff --git a/app/models/order_article.rb b/app/models/order_article.rb index cda24ae2..14193d15 100644 --- a/app/models/order_article.rb +++ b/app/models/order_article.rb @@ -7,25 +7,27 @@ class OrderArticle < ApplicationRecord belongs_to :order belongs_to :article belongs_to :article_price, optional: true - has_many :group_order_articles, :dependent => :destroy + has_many :group_order_articles, dependent: :destroy - validates_presence_of :order_id, :article_id + validates :order_id, :article_id, presence: true validate :article_and_price_exist - validates_uniqueness_of :article_id, scope: :order_id + validates :article_id, uniqueness: { scope: :order_id } - _ordered_sql = "order_articles.units_to_order > 0 OR order_articles.units_billed > 0 OR order_articles.units_received > 0" + _ordered_sql = 'order_articles.units_to_order > 0 OR order_articles.units_billed > 0 OR order_articles.units_received > 0' scope :ordered, -> { where(_ordered_sql) } - scope :ordered_or_member, -> { includes(:group_order_articles).where("#{_ordered_sql} OR order_articles.quantity > 0 OR group_order_articles.result > 0") } + scope :ordered_or_member, lambda { + includes(:group_order_articles).where("#{_ordered_sql} OR order_articles.quantity > 0 OR group_order_articles.result > 0") + } before_create :init_from_balancing after_destroy :update_ordergroup_prices - def self.ransackable_attributes(auth_object = nil) - %w(id order_id article_id quantity tolerance units_to_order) + def self.ransackable_attributes(_auth_object = nil) + %w[id order_id article_id quantity tolerance units_to_order] end - def self.ransackable_associations(auth_object = nil) - %w(order article) + def self.ransackable_associations(_auth_object = nil) + %w[order article] end # This method returns either the ArticlePrice or the Article @@ -46,7 +48,7 @@ class OrderArticle < ApplicationRecord # In balancing this can differ from ordered (by supplier) quantity for this article. def group_orders_sum quantity = group_order_articles.collect(&:result).sum - { :quantity => quantity, :price => quantity * price.fc_price } + { quantity: quantity, price: quantity * price.fc_price } end # Update quantity/tolerance/units_to_order from group_order_articles @@ -97,15 +99,13 @@ class OrderArticle < ApplicationRecord units * price.unit_quantity * price.gross_price end - def ordered_quantities_different_from_group_orders?(ordered_mark = "!", billed_mark = "?", received_mark = "?") - if not units_received.nil? - ((units_received * price.unit_quantity) == group_orders_sum[:quantity]) ? false : received_mark - elsif not units_billed.nil? - ((units_billed * price.unit_quantity) == group_orders_sum[:quantity]) ? false : billed_mark - elsif not units_to_order.nil? - ((units_to_order * price.unit_quantity) == group_orders_sum[:quantity]) ? false : ordered_mark - else - nil # can happen in integration tests + def ordered_quantities_different_from_group_orders?(ordered_mark = '!', billed_mark = '?', received_mark = '?') + if !units_received.nil? + (units_received * price.unit_quantity) == group_orders_sum[:quantity] ? false : received_mark + elsif !units_billed.nil? + (units_billed * price.unit_quantity) == group_orders_sum[:quantity] ? false : billed_mark + elsif !units_to_order.nil? + (units_to_order * price.unit_quantity) == group_orders_sum[:quantity] ? false : ordered_mark end end @@ -124,7 +124,7 @@ class OrderArticle < ApplicationRecord if surplus.index(:tolerance).nil? qty_for_members = [qty_left, self.quantity].min else - qty_for_members = [qty_left, self.quantity + self.tolerance].min + qty_for_members = [qty_left, self.quantity + tolerance].min counts[surplus.index(:tolerance)] = [0, qty_for_members - self.quantity].max end @@ -139,9 +139,7 @@ class OrderArticle < ApplicationRecord # 2) if not found, create new stock article # avoiding duplicate stock article names end - if qty_left > 0 && surplus.index(nil) - counts[surplus.index(nil)] = qty_left - end + counts[surplus.index(nil)] = qty_left if qty_left > 0 && surplus.index(nil) # Update GroupOrder prices & Ordergroup stats # TODO only affected group_orders, and once after redistributing all articles @@ -150,7 +148,7 @@ class OrderArticle < ApplicationRecord order.ordergroups.each(&:update_stats!) end - # TODO notifications + # TODO: notifications counts end @@ -159,7 +157,7 @@ class OrderArticle < ApplicationRecord def update_article_and_price!(order_article_attributes, article_attributes, price_attributes = nil) OrderArticle.transaction do # Updates self - self.update!(order_article_attributes) + update!(order_article_attributes) # Updates article article.update!(article_attributes) @@ -186,7 +184,7 @@ class OrderArticle < ApplicationRecord end def update_global_price=(value) - @update_global_price = (value == true || value == '1') ? true : false + @update_global_price = [true, '1'].include?(value) ? true : false end # @return [Number] Units missing for the last +unit_quantity+ of the article. @@ -210,16 +208,19 @@ class OrderArticle < ApplicationRecord private def article_and_price_exist - errors.add(:article, I18n.t('model.order_article.error_price')) if !(article = Article.find(article_id)) || article.fc_price.nil? - rescue + if !(article = Article.find(article_id)) || article.fc_price.nil? + errors.add(:article, + I18n.t('model.order_article.error_price')) + end + rescue StandardError errors.add(:article, I18n.t('model.order_article.error_price')) end # Associate with current article price if created in a finished order def init_from_balancing - if order.present? && order.finished? - self.article_price = article.article_prices.first - end + return unless order.present? && order.finished? + + self.article_price = article.article_prices.first end def update_ordergroup_prices @@ -241,7 +242,8 @@ class OrderArticle < ApplicationRecord unless (delta_q == 0 && delta_t >= 0) || (delta_mis < 0 && delta_box >= 0 && delta_t >= 0) || (delta_q > 0 && delta_q == -delta_t) - raise ActiveRecord::RecordNotSaved.new("Change not acceptable in boxfill phase for '#{article.name}', sorry.", self) + raise ActiveRecord::RecordNotSaved.new("Change not acceptable in boxfill phase for '#{article.name}', sorry.", + self) end end diff --git a/app/models/order_comment.rb b/app/models/order_comment.rb index 5f35d98c..b11388b0 100644 --- a/app/models/order_comment.rb +++ b/app/models/order_comment.rb @@ -2,6 +2,6 @@ class OrderComment < ApplicationRecord belongs_to :order belongs_to :user - validates_presence_of :order_id, :user_id, :text - validates_length_of :text, :minimum => 3 + validates :order_id, :user_id, :text, presence: true + validates :text, length: { minimum: 3 } end diff --git a/app/models/ordergroup.rb b/app/models/ordergroup.rb index c29ec762..6770fc55 100644 --- a/app/models/ordergroup.rb +++ b/app/models/ordergroup.rb @@ -15,7 +15,7 @@ class Ordergroup < Group has_many :orders, through: :group_orders has_many :group_order_articles, through: :group_orders - validates_numericality_of :account_balance, :message => I18n.t('ordergroups.model.invalid_balance') + validates :account_balance, numericality: { message: I18n.t('ordergroups.model.invalid_balance') } validate :uniqueness_of_name, :uniqueness_of_members after_create :update_stats! @@ -32,7 +32,7 @@ class Ordergroup < Group def self.include_transaction_class_sum columns = ['groups.*'] - FinancialTransactionClass.all.each do |c| + FinancialTransactionClass.all.find_each do |c| columns << "sum(CASE financial_transaction_types.financial_transaction_class_id WHEN #{c.id} THEN financial_transactions.amount ELSE 0 END) AS sum_of_class_#{c.id}" end @@ -51,9 +51,9 @@ class Ordergroup < Group def last_user_activity last_active_user = users.order('users.last_activity DESC').first - if last_active_user - last_active_user.last_activity - end + return unless last_active_user + + last_active_user.last_activity end # the most recent order this ordergroup was participating in @@ -86,12 +86,14 @@ class Ordergroup < Group # Throws an exception if it fails. def add_financial_transaction!(amount, note, user, transaction_type, link = nil, group_order = nil) transaction do - t = FinancialTransaction.new(ordergroup: self, amount: amount, note: note, user: user, financial_transaction_type: transaction_type, financial_link: link, group_order: group_order) + t = FinancialTransaction.new(ordergroup: self, amount: amount, note: note, user: user, + financial_transaction_type: transaction_type, financial_link: link, group_order: group_order) t.save! update_balance! # Notify only when order group had a positive balance before the last transaction: - if t.amount < 0 && self.account_balance < 0 && self.account_balance - t.amount >= 0 - NotifyNegativeBalanceJob.perform_later(self, t) + if t.amount < 0 && account_balance < 0 && account_balance - t.amount >= 0 + NotifyNegativeBalanceJob.perform_later(self, + t) end t end @@ -101,10 +103,11 @@ class Ordergroup < Group # Get hours for every job of each user in period jobs = users.to_a.sum { |u| u.tasks.done.where('updated_on > ?', APPLE_MONTH_AGO.month.ago).sum(:duration) } # Get group_order.price for every finished order in this period - orders_sum = group_orders.includes(:order).merge(Order.finished).where('orders.ends >= ?', APPLE_MONTH_AGO.month.ago).references(:orders).sum(:price) + orders_sum = group_orders.includes(:order).merge(Order.finished).where('orders.ends >= ?', + APPLE_MONTH_AGO.month.ago).references(:orders).sum(:price) @readonly = false # Dirty hack, avoid getting RecordReadOnly exception when called in task after_save callback. A rails bug? - update_attribute(:stats, { :jobs_size => jobs, :orders_sum => orders_sum }) + update_attribute(:stats, { jobs_size: jobs, orders_sum: orders_sum }) end def update_balance! @@ -116,13 +119,17 @@ class Ordergroup < Group end def avg_jobs_per_euro - stats[:jobs_size].to_f / stats[:orders_sum].to_f rescue 0 + stats[:jobs_size].to_f / stats[:orders_sum].to_f + rescue StandardError + 0 end # This is the ordergroup job per euro performance # in comparison to the hole foodcoop average def apples - ((avg_jobs_per_euro / Ordergroup.avg_jobs_per_euro) * 100).to_i rescue 0 + ((avg_jobs_per_euro / Ordergroup.avg_jobs_per_euro) * 100).to_i + rescue StandardError + 0 end # If the the option stop_ordering_under is set, the ordergroup is only allowed to participate in an order, @@ -141,7 +148,11 @@ class Ordergroup < Group # Global average def self.avg_jobs_per_euro stats = Ordergroup.pluck(:stats) - stats.sum { |s| s[:jobs_size].to_f } / stats.sum { |s| s[:orders_sum].to_f } rescue 0 + begin + stats.sum { |s| s[:jobs_size].to_f } / stats.sum { |s| s[:orders_sum].to_f } + rescue StandardError + 0 + end end def account_updated @@ -149,22 +160,22 @@ class Ordergroup < Group end def self.sort_by_param(param) - param ||= "name" + param ||= 'name' sort_param_map = { - "name" => "name", - "name_reverse" => "name DESC", - "members_count" => "count(users.id)", - "members_count_reverse" => "count(users.id) DESC", - "last_user_activity" => "max(users.last_activity)", - "last_user_activity_reverse" => "max(users.last_activity) DESC", - "last_order" => "max(orders.starts)", - "last_order_reverse" => "max(orders.starts) DESC" + 'name' => 'name', + 'name_reverse' => 'name DESC', + 'members_count' => 'count(users.id)', + 'members_count_reverse' => 'count(users.id) DESC', + 'last_user_activity' => 'max(users.last_activity)', + 'last_user_activity_reverse' => 'max(users.last_activity) DESC', + 'last_order' => 'max(orders.starts)', + 'last_order_reverse' => 'max(orders.starts) DESC' } result = self - result = result.left_joins(:users).group("groups.id") if param.starts_with?("members_count", "last_user_activity") - result = result.left_joins(:orders).group("groups.id") if param.starts_with?("last_order") + result = result.left_joins(:users).group('groups.id') if param.starts_with?('members_count', 'last_user_activity') + result = result.left_joins(:orders).group('groups.id') if param.starts_with?('last_order') # Never pass user input data to Arel.sql() because of SQL Injection vulnerabilities. # This case here is okay, as param is mapped to the actual order string. @@ -176,17 +187,21 @@ class Ordergroup < Group # Make sure, that a user can only be in one ordergroup def uniqueness_of_members users.each do |user| - errors.add :user_tokens, I18n.t('ordergroups.model.error_single_group', :user => user.display) if user.groups.where(:type => 'Ordergroup').size > 1 + next unless user.groups.where(type: 'Ordergroup').size > 1 + + errors.add :user_tokens, + I18n.t('ordergroups.model.error_single_group', + user: user.display) end end # Make sure, the name is uniq, add usefull message if uniq group is already deleted def uniqueness_of_name group = Ordergroup.where(name: name) - group = group.where.not(id: self.id) unless new_record? - if group.exists? - message = group.first.deleted? ? :taken_with_deleted : :taken - errors.add :name, message - end + group = group.where.not(id: id) unless new_record? + return unless group.exists? + + message = group.first.deleted? ? :taken_with_deleted : :taken + errors.add :name, message end end diff --git a/app/models/periodic_task_group.rb b/app/models/periodic_task_group.rb index c0a2b10f..f9e9f249 100644 --- a/app/models/periodic_task_group.rb +++ b/app/models/periodic_task_group.rb @@ -5,7 +5,7 @@ class PeriodicTaskGroup < ApplicationRecord return false if tasks.empty? return false if tasks.first.due_date.nil? - return true + true end def create_next_task @@ -18,15 +18,13 @@ class PeriodicTaskGroup < ApplicationRecord next_task.save self.next_task_date += period_days - self.save + save end def create_tasks_until(create_until) - if has_next_task? - while next_task_date.nil? || next_task_date < create_until - create_next_task - end - end + return unless has_next_task? + + create_next_task while next_task_date.nil? || next_task_date < create_until end def create_tasks_for_upfront_days @@ -36,7 +34,7 @@ class PeriodicTaskGroup < ApplicationRecord end def exclude_tasks_before(task) - tasks.where("due_date < '#{task.due_date}'").each do |t| + tasks.where("due_date < '#{task.due_date}'").find_each do |t| t.update_attribute(:periodic_task_group, nil) end end @@ -53,7 +51,7 @@ class PeriodicTaskGroup < ApplicationRecord due_date: task.due_date + due_date_delta) end group_tasks.each do |task| - task.update_columns(periodic_task_group_id: self.id) + task.update_columns(periodic_task_group_id: id) end end diff --git a/app/models/shared_article.rb b/app/models/shared_article.rb index 238b48f0..c390a021 100644 --- a/app/models/shared_article.rb +++ b/app/models/shared_article.rb @@ -4,23 +4,23 @@ class SharedArticle < ApplicationRecord # set correct table_name in external DB self.table_name = 'articles' - belongs_to :shared_supplier, :foreign_key => :supplier_id + belongs_to :shared_supplier, foreign_key: :supplier_id def build_new_article(supplier) supplier.articles.build( - :name => name, - :unit => unit, - :note => note, - :manufacturer => manufacturer, - :origin => origin, - :price => price, - :tax => tax, - :deposit => deposit, - :unit_quantity => unit_quantity, - :order_number => number, - :article_category => ArticleCategory.find_match(category), + name: name, + unit: unit, + note: note, + manufacturer: manufacturer, + origin: origin, + price: price, + tax: tax, + deposit: deposit, + unit_quantity: unit_quantity, + order_number: number, + article_category: ArticleCategory.find_match(category), # convert to db-compatible-string - :shared_updated_on => updated_on.to_formatted_s(:db) + shared_updated_on: updated_on.to_fs(:db) ) end end diff --git a/app/models/shared_supplier.rb b/app/models/shared_supplier.rb index 29c9c1ab..e2b23805 100644 --- a/app/models/shared_supplier.rb +++ b/app/models/shared_supplier.rb @@ -5,10 +5,10 @@ class SharedSupplier < ApplicationRecord self.table_name = 'suppliers' has_many :suppliers, -> { undeleted } - has_many :shared_articles, :foreign_key => :supplier_id + has_many :shared_articles, foreign_key: :supplier_id def find_article_by_number(order_number) - # note that `shared_articles` uses number instead order_number + # NOTE: that `shared_articles` uses number instead order_number cached_articles.detect { |a| a.number == order_number } end @@ -19,15 +19,18 @@ class SharedSupplier < ApplicationRecord # These set of attributes are used to autofill attributes of new supplier, # when created by import from shared supplier feature. def autofill_attributes - whitelist = %w(name address phone fax email url delivery_days note) + whitelist = %w[name address phone fax email url delivery_days note] attributes.select { |k, _v| whitelist.include?(k) } end # return list of synchronisation methods available for this supplier def shared_sync_methods methods = [] - methods += %w(all_available all_unavailable) if shared_articles.count < FoodsoftConfig[:shared_supplier_article_sync_limit] - methods += %w(import) + if shared_articles.count < FoodsoftConfig[:shared_supplier_article_sync_limit] + methods += %w[all_available + all_unavailable] + end + methods += %w[import] methods end end diff --git a/app/models/stock_article.rb b/app/models/stock_article.rb index 42a06d49..14b8d5ef 100644 --- a/app/models/stock_article.rb +++ b/app/models/stock_article.rb @@ -10,11 +10,11 @@ class StockArticle < Article ransack_alias :quantity_available, :quantity # in-line with {StockArticleSerializer} def self.ransackable_attributes(auth_object = nil) - super(auth_object) - %w(supplier_id) + %w(quantity) + super(auth_object) - %w[supplier_id] + %w[quantity] end def self.ransackable_associations(auth_object = nil) - super(auth_object) - %w(supplier) + super(auth_object) - %w[supplier] end # Update the quantity of items in stock @@ -48,7 +48,7 @@ class StockArticle < Article protected def check_quantity - raise I18n.t('stockit.check.not_empty', :name => name) unless quantity == 0 + raise I18n.t('stockit.check.not_empty', name: name) unless quantity == 0 end # Overwrite Price history of Article. For StockArticles isn't it necessary. diff --git a/app/models/stock_change.rb b/app/models/stock_change.rb index 4cbd8939..03d92c74 100644 --- a/app/models/stock_change.rb +++ b/app/models/stock_change.rb @@ -4,11 +4,11 @@ class StockChange < ApplicationRecord belongs_to :stock_taking, optional: true, foreign_key: 'stock_event_id' belongs_to :stock_article - validates_presence_of :stock_article_id, :quantity - validates_numericality_of :quantity + validates :stock_article_id, :quantity, presence: true + validates :quantity, numericality: true - after_save :update_article_quantity after_destroy :update_article_quantity + after_save :update_article_quantity protected diff --git a/app/models/stock_event.rb b/app/models/stock_event.rb index 4fd82864..7134f7b0 100644 --- a/app/models/stock_event.rb +++ b/app/models/stock_event.rb @@ -2,5 +2,5 @@ class StockEvent < ApplicationRecord has_many :stock_changes, dependent: :destroy has_many :stock_articles, through: :stock_changes - validates_presence_of :date + validates :date, presence: true end diff --git a/app/models/supplier.rb b/app/models/supplier.rb index 862f5c24..56999be1 100644 --- a/app/models/supplier.rb +++ b/app/models/supplier.rb @@ -2,7 +2,9 @@ class Supplier < ApplicationRecord include MarkAsDeletedWithName include CustomFields - has_many :articles, -> { where(:type => nil).includes(:article_category).order('article_categories.name', 'articles.name') } + has_many :articles, lambda { + where(type: nil).includes(:article_category).order('article_categories.name', 'articles.name') + } has_many :stock_articles, -> { includes(:article_category).order('article_categories.name', 'articles.name') } has_many :orders has_many :deliveries @@ -10,24 +12,24 @@ class Supplier < ApplicationRecord belongs_to :supplier_category belongs_to :shared_supplier, optional: true # for the sharedLists-App - validates :name, :presence => true, :length => { :in => 4..30 } - validates :phone, :presence => true, :length => { :in => 8..25 } - validates :address, :presence => true, :length => { :in => 8..50 } - validates_format_of :iban, :with => /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/, :allow_blank => true - validates_uniqueness_of :iban, :case_sensitive => false, :allow_blank => true - validates_length_of :order_howto, :note, maximum: 250 + validates :name, presence: true, length: { in: 4..30 } + validates :phone, presence: true, length: { in: 8..25 } + validates :address, presence: true, length: { in: 8..50 } + validates :iban, format: { with: /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/, allow_blank: true } + validates :iban, uniqueness: { case_sensitive: false, allow_blank: true } + validates :order_howto, :note, length: { maximum: 250 } validate :valid_shared_sync_method validate :uniqueness_of_name scope :undeleted, -> { where(deleted_at: nil) } scope :having_articles, -> { where(id: Article.undeleted.select(:supplier_id).distinct) } - def self.ransackable_attributes(auth_object = nil) - %w(id name) + def self.ransackable_attributes(_auth_object = nil) + %w[id name] end - def self.ransackable_associations(auth_object = nil) - %w(articles stock_articles orders) + def self.ransackable_associations(_auth_object = nil) + %w[articles stock_articles orders] end # sync all articles with the external database @@ -35,7 +37,9 @@ class Supplier < ApplicationRecord # also returns an array with outlisted_articles, which should be deleted # also returns an array with new articles, which should be added (depending on shared_sync_method) def sync_all - updated_article_pairs, outlisted_articles, new_articles = [], [], [] + updated_article_pairs = [] + outlisted_articles = [] + new_articles = [] existing_articles = Set.new for article in articles.undeleted # try to find the associated shared_article @@ -44,30 +48,28 @@ class Supplier < ApplicationRecord if shared_article # article will be updated existing_articles.add(shared_article.id) unequal_attributes = article.shared_article_changed?(self) - unless unequal_attributes.blank? # skip if shared_article has not been changed + if unequal_attributes.present? # skip if shared_article has not been changed article.attributes = unequal_attributes updated_article_pairs << [article, unequal_attributes] end # Articles with no order number can be used to put non-shared articles # in a shared supplier, with sync keeping them. - elsif not article.order_number.blank? + elsif article.order_number.present? # article isn't in external database anymore outlisted_articles << article end end # Find any new articles, unless the import is manual - if ['all_available', 'all_unavailable'].include?(shared_sync_method) + if %w[all_available all_unavailable].include?(shared_sync_method) # build new articles shared_supplier .shared_articles .where.not(id: existing_articles.to_a) .find_each { |new_shared_article| new_articles << new_shared_article.build_new_article(self) } # make them unavailable when desired - if shared_sync_method == 'all_unavailable' - new_articles.each { |new_article| new_article.availability = false } - end + new_articles.each { |new_article| new_article.availability = false } if shared_sync_method == 'all_unavailable' end - return [updated_article_pairs, outlisted_articles, new_articles] + [updated_article_pairs, outlisted_articles, new_articles] end # Synchronise articles with spreadsheet. @@ -78,8 +80,10 @@ class Supplier < ApplicationRecord # @option options [Boolean] :convert_units Omit or set to +true+ to keep current units, recomputing unit quantity and price. def sync_from_file(file, options = {}) all_order_numbers = [] - updated_article_pairs, outlisted_articles, new_articles = [], [], [] - FoodsoftFile::parse file, options do |status, new_attrs, line| + updated_article_pairs = [] + outlisted_articles = [] + new_articles = [] + FoodsoftFile.parse file, options do |status, new_attrs, line| article = articles.undeleted.where(order_number: new_attrs[:order_number]).first new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category]) new_attrs[:tax] ||= FoodsoftConfig[:tax_default] @@ -101,15 +105,13 @@ class Supplier < ApplicationRecord # stop when there is a parsing error elsif status.is_a? String # @todo move I18n key to model - raise I18n.t('articles.model.error_parse', :msg => status, :line => line.to_s) + raise I18n.t('articles.model.error_parse', msg: status, line: line.to_s) end all_order_numbers << article.order_number if article end - if options[:outlist_absent] - outlisted_articles += articles.undeleted.where.not(order_number: all_order_numbers + [nil]) - end - return [updated_article_pairs, outlisted_articles, new_articles] + outlisted_articles += articles.undeleted.where.not(order_number: all_order_numbers + [nil]) if options[:outlist_absent] + [updated_article_pairs, outlisted_articles, new_articles] end # default value @@ -140,18 +142,18 @@ class Supplier < ApplicationRecord # make sure the shared_sync_method is allowed for the shared supplier def valid_shared_sync_method - if shared_supplier && !shared_supplier.shared_sync_methods.include?(shared_sync_method) - errors.add :shared_sync_method, :included - end + return unless shared_supplier && !shared_supplier.shared_sync_methods.include?(shared_sync_method) + + errors.add :shared_sync_method, :included end # Make sure, the name is uniq, add usefull message if uniq group is already deleted def uniqueness_of_name supplier = Supplier.where(name: name) - supplier = supplier.where.not(id: self.id) unless new_record? - if supplier.exists? - message = supplier.first.deleted? ? :taken_with_deleted : :taken - errors.add :name, message - end + supplier = supplier.where.not(id: id) unless new_record? + return unless supplier.exists? + + message = supplier.first.deleted? ? :taken_with_deleted : :taken + errors.add :name, message end end diff --git a/app/models/task.rb b/app/models/task.rb index cd748eb3..1343b8f4 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,9 +1,9 @@ class Task < ApplicationRecord - has_many :assignments, :dependent => :destroy - has_many :users, :through => :assignments + has_many :assignments, dependent: :destroy + has_many :users, through: :assignments belongs_to :workgroup, optional: true belongs_to :periodic_task_group, optional: true - belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by_user_id', optional: true + belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id', optional: true scope :non_group, -> { where(workgroup_id: nil, done: false) } scope :done, -> { where(done: true) } @@ -11,12 +11,12 @@ class Task < ApplicationRecord attr_accessor :current_user_id - validates :name, :presence => true, :length => { :minimum => 3 } - validates :required_users, :presence => true - validates_numericality_of :duration, :required_users, :only_integer => true, :greater_than => 0 - validates_length_of :description, maximum: 250 + validates :name, presence: true, length: { minimum: 3 } + validates :required_users, presence: true + validates :duration, :required_users, numericality: { only_integer: true, greater_than: 0 } + validates :description, length: { maximum: 250 } validates :done, exclusion: { in: [true] }, if: :periodic?, on: :create - validates_presence_of :due_date, if: :periodic? + validates :due_date, presence: { if: :periodic? } before_save :exclude_from_periodic_task_group, if: :changed?, unless: :new_record? after_save :update_ordergroup_stats @@ -35,7 +35,7 @@ class Task < ApplicationRecord # find all tasks in the period (or another number of days) def self.next_assigned_tasks_for(user, number = FoodsoftConfig[:tasks_period_days].to_i) user.tasks.undone.where(assignments: { accepted: true }) - .where(["tasks.due_date >= ? AND tasks.due_date <= ?", Time.now, number.days.from_now]) + .where(['tasks.due_date >= ? AND tasks.due_date <= ?', Time.now, number.days.from_now]) end # count tasks with not enough responsible people @@ -49,7 +49,7 @@ class Task < ApplicationRecord def self.next_unassigned_tasks_for(user, max = 2) periodic_task_group_count = {} - self.unassigned_tasks_for(user).reject do |item| + unassigned_tasks_for(user).reject do |item| next false unless item.periodic_task_group count = periodic_task_group_count[item.periodic_task_group] || 0 @@ -59,19 +59,19 @@ class Task < ApplicationRecord end def periodic? - not periodic_task_group.nil? + !periodic_task_group.nil? end def is_assigned?(user) - self.assignments.detect { |ass| ass.user_id == user.id } + assignments.detect { |ass| ass.user_id == user.id } end def is_accepted?(user) - self.assignments.detect { |ass| ass.user_id == user.id && ass.accepted } + assignments.detect { |ass| ass.user_id == user.id && ass.accepted } end def enough_users_assigned? - assignments.to_a.count(&:accepted) >= required_users ? true : false + assignments.to_a.count(&:accepted) >= required_users end def still_required_users @@ -82,39 +82,35 @@ class Task < ApplicationRecord # and makes the users responsible for the task # TODO: check for maximal number of users def user_list=(ids) - list = ids.split(",").map(&:to_i) + list = ids.split(',').map(&:to_i) new_users = (list - users.collect(&:id)).uniq old_users = users.reject { |user| list.include?(user.id) } self.class.transaction do # delete old assignments - if old_users.any? - assignments.where(user_id: old_users.map(&:id)).each(&:destroy) - end + assignments.where(user_id: old_users.map(&:id)).find_each(&:destroy) if old_users.any? # create new assignments new_users.each do |id| user = User.find(id) if user.blank? errors.add(:user_list) + elsif id == current_user_id.to_i + assignments.build user: user, accepted: true + # current_user will accept, when he puts himself to the list of users else - if id == current_user_id.to_i - # current_user will accept, when he puts himself to the list of users - self.assignments.build :user => user, :accepted => true - else - # normal assignement - self.assignments.build :user => user - end + # normal assignement + assignments.build user: user end end end end def user_list - @user_list ||= users.collect(&:id).join(", ") + @user_list ||= users.collect(&:id).join(', ') end def update_ordergroup_stats(user_ids = self.user_ids) - Ordergroup.joins(:users).where(users: { id: user_ids }).each(&:update_stats!) + Ordergroup.joins(:users).where(users: { id: user_ids }).find_each(&:update_stats!) end def exclude_from_periodic_task_group diff --git a/app/models/user.rb b/app/models/user.rb index 05a67547..12d457b0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,19 +4,19 @@ class User < ApplicationRecord include CustomFields # TODO: acts_as_paraniod ?? - has_many :memberships, :dependent => :destroy - has_many :groups, :through => :memberships + has_many :memberships, dependent: :destroy + has_many :groups, through: :memberships # has_one :ordergroup, :through => :memberships, :source => :group, :class_name => "Ordergroup" def ordergroup - Ordergroup.joins(:memberships).where(memberships: { user_id: self.id }).first + Ordergroup.joins(:memberships).where(memberships: { user_id: id }).first end - has_many :workgroups, :through => :memberships, :source => :group, :class_name => "Workgroup" - has_many :assignments, :dependent => :destroy - has_many :tasks, :through => :assignments - has_many :send_messages, :class_name => "Message", :foreign_key => "sender_id" - has_many :created_orders, :class_name => 'Order', :foreign_key => 'created_by_user_id', :dependent => :nullify - has_many :mail_delivery_status, :class_name => 'MailDeliveryStatus', :foreign_key => 'email', :primary_key => 'email' + has_many :workgroups, through: :memberships, source: :group, class_name: 'Workgroup' + has_many :assignments, dependent: :destroy + has_many :tasks, through: :assignments + has_many :send_messages, class_name: 'Message', foreign_key: 'sender_id' + has_many :created_orders, class_name: 'Order', foreign_key: 'created_by_user_id', dependent: :nullify + has_many :mail_delivery_status, class_name: 'MailDeliveryStatus', foreign_key: 'email', primary_key: 'email' attr_accessor :create_ordergroup, :password, :send_welcome_mail, :settings_attributes @@ -26,22 +26,22 @@ class User < ApplicationRecord # makes the current_user (logged-in-user) available in models cattr_accessor :current_user - validates_presence_of :email - validates_presence_of :password, :on => :create - validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i - validates_uniqueness_of :email, :case_sensitive => false - validates_presence_of :first_name # for simple_form validations - validates_length_of :first_name, :in => 2..50 - validates_confirmation_of :password - validates_length_of :password, :in => 5..50, :allow_blank => true + validates :email, presence: true + validates :password, presence: { on: :create } + validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } + validates :email, uniqueness: { case_sensitive: false } + validates :first_name, presence: true # for simple_form validations + validates :first_name, length: { in: 2..50 } + validates :password, confirmation: true + validates :password, length: { in: 5..50, allow_blank: true } # allow nick to be nil depending on foodcoop config # TODO Rails 4 may have a more beautiful way # http://stackoverflow.com/questions/19845910/conditional-allow-nil-part-of-validation - validates_length_of :nick, :in => 2..25, :allow_nil => true, :unless => Proc.new { FoodsoftConfig[:use_nick] } - validates_length_of :nick, :in => 2..25, :allow_nil => false, :if => Proc.new { FoodsoftConfig[:use_nick] } - validates_uniqueness_of :nick, :case_sensitive => false, :allow_nil => true # allow_nil in length validation - validates_format_of :iban, :with => /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/, :allow_blank => true - validates_uniqueness_of :iban, :case_sensitive => false, :allow_blank => true + validates :nick, length: { in: 2..25, allow_nil: true, unless: proc { FoodsoftConfig[:use_nick] } } + validates :nick, length: { in: 2..25, allow_nil: false, if: proc { FoodsoftConfig[:use_nick] } } + validates :nick, uniqueness: { case_sensitive: false, allow_nil: true } # allow_nil in length validation + validates :iban, format: { with: /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/, allow_blank: true } + validates :iban, uniqueness: { case_sensitive: false, allow_blank: true } before_validation :set_password after_initialize do @@ -58,17 +58,19 @@ class User < ApplicationRecord end after_save do - settings_attributes.each do |key, value| - value.each do |k, v| - case v - when '1' - value[k] = true - when '0' - value[k] = false + if settings_attributes + settings_attributes.each do |key, value| + value.each do |k, v| + case v + when '1' + value[k] = true + when '0' + value[k] = false + end end + settings.merge!(key, value) end - self.settings.merge!(key, value) - end if settings_attributes + end if ActiveModel::Type::Boolean.new.cast(create_ordergroup) og = Ordergroup.new({ name: name }) @@ -103,7 +105,7 @@ class User < ApplicationRecord match_name = q.split.map do |a| users[:first_name].matches("%#{a}%").or users[:last_name].matches("%#{a}%") end.reduce(:and) - User.where(match_nick.or match_name) + User.where(match_nick.or(match_name)) end def locale @@ -111,7 +113,7 @@ class User < ApplicationRecord end def name - [first_name, last_name].join(" ") + [first_name, last_name].join(' ') end def receive_email? @@ -120,22 +122,24 @@ class User < ApplicationRecord # Sets the user's password. It will be stored encrypted along with a random salt. def set_password - unless password.blank? - salt = [Array.new(6) { rand(256).chr }.join].pack("m").chomp - self.password_hash, self.password_salt = Digest::SHA1.hexdigest(password + salt), salt - end + return if password.blank? + + salt = [Array.new(6) { rand(256).chr }.join].pack('m').chomp + self.password_hash = Digest::SHA1.hexdigest(password + salt) + self.password_salt = salt end # Returns true if the password argument matches the user's password. def has_password(password) - Digest::SHA1.hexdigest(password + self.password_salt) == self.password_hash + Digest::SHA1.hexdigest(password + password_salt) == password_hash end # Returns a random password. def new_random_password(size = 6) - c = %w(b c d f g h j k l m n p qu r s t v w x z ch cr fr nd ng nk nt ph pr rd sh sl sp st th tr) - v = %w(a e i o u y) - f, r = true, '' + c = %w[b c d f g h j k l m n p qu r s t v w x z ch cr fr nd ng nk nt ph pr rd sh sl sp st th tr] + v = %w[a e i o u y] + f = true + r = '' (size * 2).times do r << (f ? c[rand * c.size] : v[rand * v.size]) f = !f @@ -198,12 +202,12 @@ class User < ApplicationRecord # returns true if user is a member of a given group def member_of?(group) - group.users.exists?(self.id) + group.users.exists?(id) end # Returns an array with the users groups (but without the Ordergroups -> because tpye=>"") - def member_of_groups() - self.groups.where(type: '') + def member_of_groups + groups.where(type: '') end def deleted? @@ -220,11 +224,9 @@ class User < ApplicationRecord def self.authenticate(login, password) user = find_by_nick(login) || find_by_email(login) - if user && password && user.has_password(password) - user - else - nil - end + return unless user && password && user.has_password(password) + + user end def self.custom_fields @@ -248,29 +250,29 @@ class User < ApplicationRecord def token_attributes # would be sensible to match ApplicationController#show_user # this should not be part of the model anyway - { :id => id, :name => "#{display} (#{ordergroup.try(:name)})" } + { id: id, name: "#{display} (#{ordergroup.try(:name)})" } end def self.sort_by_param(param) - param ||= "name" + param ||= 'name' sort_param_map = { - "nick" => "nick", - "nick_reverse" => "nick DESC", - "name" => "first_name, last_name", - "name_reverse" => "first_name DESC, last_name DESC", - "email" => "users.email", - "email_reverse" => "users.email DESC", - "phone" => "phone", - "phone_reverse" => "phone DESC", - "last_activity" => "last_activity", - "last_activity_reverse" => "last_activity DESC", - "ordergroup" => "IFNULL(groups.type, '') <> 'Ordergroup', groups.name", - "ordergroup_reverse" => "IFNULL(groups.type, '') <> 'Ordergroup', groups.name DESC" + 'nick' => 'nick', + 'nick_reverse' => 'nick DESC', + 'name' => 'first_name, last_name', + 'name_reverse' => 'first_name DESC, last_name DESC', + 'email' => 'users.email', + 'email_reverse' => 'users.email DESC', + 'phone' => 'phone', + 'phone_reverse' => 'phone DESC', + 'last_activity' => 'last_activity', + 'last_activity_reverse' => 'last_activity DESC', + 'ordergroup' => "IFNULL(groups.type, '') <> 'Ordergroup', groups.name", + 'ordergroup_reverse' => "IFNULL(groups.type, '') <> 'Ordergroup', groups.name DESC" } # Never pass user input data to Arel.sql() because of SQL Injection vulnerabilities. # This case here is okay, as param is mapped to the actual order string. - self.eager_load(:groups).order(Arel.sql(sort_param_map[param])) # eager_load is like left_join but without duplicates + eager_load(:groups).order(Arel.sql(sort_param_map[param])) # eager_load is like left_join but without duplicates end end diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb index bf50c27b..271dec8d 100644 --- a/app/models/workgroup.rb +++ b/app/models/workgroup.rb @@ -3,26 +3,26 @@ class Workgroup < Group has_many :tasks # returns all non-finished tasks - has_many :open_tasks, -> { where(:done => false).order('due_date', 'name') }, :class_name => 'Task' + has_many :open_tasks, -> { where(done: false).order('due_date', 'name') }, class_name: 'Task' - validates_uniqueness_of :name - validate :last_admin_on_earth, :on => :update + validates :name, uniqueness: true + validate :last_admin_on_earth, on: :update before_destroy :check_last_admin_group protected # Check before destroy a group, if this is the last group with admin role def check_last_admin_group - if role_admin && Workgroup.where(role_admin: true).size == 1 - raise I18n.t('workgroups.error_last_admin_group') - end + return unless role_admin && Workgroup.where(role_admin: true).size == 1 + + raise I18n.t('workgroups.error_last_admin_group') end # add validation check on update # Return an error if this is the last group with admin role and role_admin should set to false def last_admin_on_earth - if !role_admin && !Workgroup.where(role_admin: true).where.not(id: id).exists? - errors.add(:role_admin, I18n.t('workgroups.error_last_admin_role')) - end + return unless !role_admin && !Workgroup.where(role_admin: true).where.not(id: id).exists? + + errors.add(:role_admin, I18n.t('workgroups.error_last_admin_role')) end end diff --git a/app/views/active_storage/blobs/_blob.html.haml b/app/views/active_storage/blobs/_blob.html.haml new file mode 100644 index 00000000..6ddb2e08 --- /dev/null +++ b/app/views/active_storage/blobs/_blob.html.haml @@ -0,0 +1,9 @@ +%figure{class: "attachment attachment--#{blob.representable? ? "preview" : "file"} attachment--#{blob.filename.extension}"} + - if blob.representable? + = image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) + %figcaption.attachment__caption + - if caption = blob.try(:caption) + = caption + - else + %span.attachment__name= link_to blob.filename, blob + %span.attachment__size= number_to_human_size blob.byte_size diff --git a/app/views/admin/configs/_tab_others.html.haml b/app/views/admin/configs/_tab_others.html.haml index 907cf840..93e1be2d 100644 --- a/app/views/admin/configs/_tab_others.html.haml +++ b/app/views/admin/configs/_tab_others.html.haml @@ -4,5 +4,6 @@ = config_input form, :distribution_strategy, as: :select, collection: distribution_strategy_options, include_blank: false, input_html: {class: 'input-xxlarge'}, label_method: ->(s){ t("config.keys.distribution_strategy_options.#{s}") } = config_input form, :disable_invite, as: :boolean += config_input form, :disable_members_overview, as: :boolean = config_input form, :help_url, as: :url, input_html: {class: 'input-xlarge'} = config_input form, :webstats_tracking_code, as: :text, input_html: {class: 'input-xxlarge', rows: 3} diff --git a/app/views/articles/_sync_table.html.haml b/app/views/articles/_sync_table.html.haml index ac17adfa..68db9477 100644 --- a/app/views/articles/_sync_table.html.haml +++ b/app/views/articles/_sync_table.html.haml @@ -32,8 +32,8 @@ = form.text_field 'name', :size => 0 - hidden_fields.each do |field| = form.hidden_field field - %td{:style => highlight_new(attrs, :note)}= form.text_field 'note', class: 'input-small' - %td{:style => highlight_new(attrs, :manufacturer)}= form.text_field 'manufacturer', class: 'input-small' + %td{:style => highlight_new(attrs, :note)}= form.text_field 'note', class: 'input-medium' + %td{:style => highlight_new(attrs, :manufacturer)}= form.text_field 'manufacturer', class: 'input-medium' %td{:style => highlight_new(attrs, :origin)}= form.text_field 'origin', class: 'input-mini' %td{:style => highlight_new(attrs, :unit)}= form.text_field 'unit', class: 'input-mini' %td{:style => highlight_new(attrs, :unit_quantity)}= form.text_field 'unit_quantity', class: 'input-mini' @@ -49,8 +49,8 @@ .input-prepend %span.add-on= t 'number.currency.format.unit' = form.text_field 'deposit', class: 'input-mini', style: 'width: 45px' - %td= form.select :article_category_id, ArticleCategory.all.map {|a| [ a.name, a.id ] }, - {include_blank: true}, class: 'input-small' + %td{:style => highlight_new(attrs, :article_category)} + = form.select :article_category_id, ArticleCategory.all.map {|a| [ a.name, a.id ] }, {include_blank: true} - unless changed_article.errors.empty? %tr.alert %td(colspan=11)= changed_article.errors.full_messages.join(', ') diff --git a/app/views/finance/ordergroups/_ordergroups.html.haml b/app/views/finance/ordergroups/_ordergroups.html.haml index 83a05ed2..6cf12c16 100644 --- a/app/views/finance/ordergroups/_ordergroups.html.haml +++ b/app/views/finance/ordergroups/_ordergroups.html.haml @@ -22,3 +22,12 @@ %td = link_to t('.new_transaction'), new_finance_ordergroup_transaction_path(ordergroup), class: 'btn btn-mini' = link_to t('.account_statement'), finance_ordergroup_transactions_path(ordergroup), class: 'btn btn-mini' + %thead + %tr + %th= t 'Total' + %th + - FinancialTransactionClass.sorted.each do |c| + - name = FinancialTransactionClass.has_multiple_classes ? c.display : heading_helper(Ordergroup, :account_balance) + %th.numeric{:id => "total_balance#{c.id}"}= format_currency @total_balances[c.id] + %th.numeric#total_balance_sum + = format_currency @total_balances.values.reduce(:+) \ No newline at end of file diff --git a/app/views/home/_start_nav.haml b/app/views/home/_start_nav.haml index 708e4c85..96313861 100644 --- a/app/views/home/_start_nav.haml +++ b/app/views/home/_start_nav.haml @@ -2,7 +2,8 @@ %h3= t '.title' %ul.nav.nav-list %li.nav-header= t '.foodcoop' - %li= link_to t('.members'), foodcoop_users_path + - unless FoodsoftConfig[:disable_members_overview] + %li= link_to t('.members'), foodcoop_users_path %li= link_to t('.tasks'), user_tasks_path - has_ordergroup = !@current_user.ordergroup.nil? diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 974ce8f2..66e14355 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -8,10 +8,10 @@ = csrf_meta_tags = stylesheet_link_tag "application", :media => "all" //%link(href="images/favicon.ico" rel="shortcut icon") - = yield(:head) = foodcoop_css_tag + %body = yield @@ -19,7 +19,9 @@ Javascripts \================================================== / Placed at the end of the document so the pages load faster - = javascript_include_tag "application" + = javascript_importmap_tags + = javascript_include_tag "application_legacy" + :javascript I18n.defaultLocale = "#{I18n.default_locale}"; I18n.locale = "#{I18n.locale}"; diff --git a/app/views/layouts/action_text/contents/_content.html.erb b/app/views/layouts/action_text/contents/_content.html.erb new file mode 100644 index 00000000..9e3c0d0d --- /dev/null +++ b/app/views/layouts/action_text/contents/_content.html.erb @@ -0,0 +1,3 @@ +
+ <%= yield -%> +
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7781096d..c1b1cf00 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -13,9 +13,9 @@ %li= link_to t('.reference_calculator'), home_reference_calculator_path %li= link_to t('.logout'), logout_path %li{class: ('disabled' if FoodsoftConfig[:homepage].blank?)} - = link_to FoodsoftConfig[:name], FoodsoftConfig[:homepage] + = link_to FoodsoftConfig[:name], FoodsoftConfig[:homepage], target: '_blank' - if FoodsoftConfig[:help_url] - %li= link_to t('.help'), FoodsoftConfig[:help_url] + %li= link_to t('.help'), FoodsoftConfig[:help_url], target: '_blank' %li= link_to t('.feedback.title'), new_feedback_path, title: t('.feedback.desc') .clearfix diff --git a/app/views/layouts/email.html.haml b/app/views/layouts/email.html.haml new file mode 100644 index 00000000..6bcf3b4a --- /dev/null +++ b/app/views/layouts/email.html.haml @@ -0,0 +1,12 @@ += yield +\ +%hr +%ul + %li + %a{href: root_url} Foodsoft + - if FoodsoftConfig[:homepage] + %li + %a{href: FoodsoftConfig[:homepage]} Foodcoop + - if FoodsoftConfig[:help_url] + %li + %a{href: FoodsoftConfig[:help_url]}= t '.help' \ No newline at end of file diff --git a/app/views/orders/index.html.haml b/app/views/orders/index.html.haml index 1bd870ad..51b426bc 100644 --- a/app/views/orders/index.html.haml +++ b/app/views/orders/index.html.haml @@ -33,7 +33,7 @@ %td= order.name %td= format_date(order.pickup) unless order.pickup.nil? %td= format_time(order.ends) unless order.ends.nil? - %td= truncate(order.note) + %td= truncate(order.note, length: 25, tooltip: true) %td= link_to t('.action_end'), finish_order_path(order), data: {confirm: t('.confirm_end', order: order.name)}, method: :post, class: 'btn btn-small btn-success' diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml index 76760654..5f147baf 100644 --- a/app/views/sessions/new.html.haml +++ b/app/views/sessions/new.html.haml @@ -6,6 +6,8 @@ - title t('.title') +.lead= FoodsoftConfig[:name] + %noscript .alert.alert-error != t '.nojs', link: link_to(t('.noscript'), "http://noscript.net/") diff --git a/bin/importmap b/bin/importmap new file mode 100755 index 00000000..36502ab1 --- /dev/null +++ b/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/bin/setup b/bin/setup index 94fd4d79..ec47b79b 100755 --- a/bin/setup +++ b/bin/setup @@ -1,36 +1,33 @@ #!/usr/bin/env ruby -require 'fileutils' -include FileUtils +require "fileutils" # path to your application root. -APP_ROOT = File.expand_path('..', __dir__) +APP_ROOT = File.expand_path("..", __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do - # This script is a starting point to setup your application. +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end puts "\n== Preparing database ==" - system! 'bin/rails db:setup' + system! "bin/rails db:prepare" puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' + system! "bin/rails log:clear tmp:clear" puts "\n== Restarting application server ==" - system! 'bin/rails restart' + system! "bin/rails restart" end diff --git a/config.ru b/config.ru index f986eadb..eeab736b 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,6 @@ # This file is used by Rack-based servers to start the application. -require ::File.expand_path('../config/environment', __FILE__) +require File.expand_path('config/environment', __dir__) # https://gist.github.com/ebeigarts/5450422 map ENV.fetch('RAILS_RELATIVE_URL_ROOT', '/') do diff --git a/config/app_config.yml.SAMPLE b/config/app_config.yml.SAMPLE index e43705b6..b43b7935 100644 --- a/config/app_config.yml.SAMPLE +++ b/config/app_config.yml.SAMPLE @@ -32,6 +32,9 @@ default: &defaults # custom foodsoft software URL (used in footer) #foodsoft_url: https://github.com/foodcoops/foodsoft + # URL to redirect to after logging out + # logout_redirect_url: https://foodcoop.test + # Default language #default_locale: en # By default, foodsoft takes the language from the webbrowser/operating system. @@ -90,6 +93,9 @@ default: &defaults #use_wiki: true #use_messages: true + # When enabled only administrators can access the member list. + #disable_members_overview: true + # Base font size for generated PDF documents #pdf_font_size: 12 # Page size for generated PDF documents @@ -167,6 +173,9 @@ default: &defaults # default to allow automatically adding new articles on sync only when less than 200 articles in total #shared_supplier_article_sync_limit: 200 + # number of days after which attachment files get deleted + #attachment_retention_days: 365 + development: <<: *defaults diff --git a/config/application.rb b/config/application.rb index 544e534c..696d6647 100644 --- a/config/application.rb +++ b/config/application.rb @@ -9,7 +9,7 @@ Bundler.require(*Rails.groups) module Foodsoft class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 5.0 + config.load_defaults 7.0 # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers @@ -29,15 +29,14 @@ module Foodsoft # Internationalization. config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '*.yml')] - config.i18n.available_locales = Pathname.glob(Rails.root.join('config', 'locales', '{??,???}{-*,}.yml')).map { |p| p.basename('.yml').to_s } + config.i18n.available_locales = Pathname.glob(Rails.root.join('config', 'locales', '{??,???}{-*,}.yml')).map do |p| + p.basename('.yml').to_s + end config.i18n.default_locale = :en config.i18n.fallbacks = [:en] # Configure the default encoding used in templates for Ruby 1.9. - config.encoding = "utf-8" - - # TODO: Remove this. See CVE-2022-32224 for details. - config.active_record.yaml_column_permitted_classes = [BigDecimal, Date, Symbol, Time] + config.encoding = 'utf-8' # Enable escaping HTML in JSON. config.active_support.escape_html_entities_in_json = true @@ -47,7 +46,7 @@ module Foodsoft # like if you have constraints or database-specific column types # config.active_record.schema_format = :sql - # TODO Disable this. Uncommenting this line will currently cause rspec to fail. + # TODO: Disable this. Uncommenting this line will currently cause rspec to fail. config.action_controller.permit_all_parameters = true config.active_job.queue_adapter = :resque @@ -66,18 +65,19 @@ module Foodsoft # Load legacy scripts from vendor config.assets.precompile += ['vendor/assets/javascripts/*.js'] - # CORS for API - config.middleware.insert_before 0, Rack::Cors do - allow do - origins '*' - # this restricts Foodsoft scopes to certain characters - let's discuss it when it becomes an actual problem - resource %r{\A/[-a-zA-Z0-9_]+/api/v1/}, headers: :any, methods: :any - end - end + config.active_record.yaml_column_permitted_classes = [Symbol, BigDecimal] + + config.autoloader = :zeitwerk + + config.active_storage.variant_processor = :mini_magick end # Foodsoft version VERSION = Rails.root.join('VERSION').read.strip # Current revision, or +nil+ - REVISION = (Rails.root.join('REVISION').read.strip rescue nil) + REVISION = begin + Rails.root.join('REVISION').read.strip + rescue StandardError + nil + end end diff --git a/config/environments/production.rb b/config/environments/production.rb index 0560b38d..bb846bd7 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/integer/time' + # Foodsoft production configuration. # # This file is in the public domain. @@ -27,30 +29,32 @@ Rails.application.configure do config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + config.assets.js_compressor = :terser config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = "http://assets.example.com" # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - # Store uploaded files on the local file system (see config/storage.yml for options) + # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local - # Mount Action Cable outside main process or domain + # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil # config.action_cable.url = 'wss://example.com/cable' # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - config.force_ssl = ENV["RAILS_FORCE_SSL"] != "false" + config.force_ssl = ENV['RAILS_FORCE_SSL'] != 'false' + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). # Set to :debug to see everything in the log. config.log_level = :info @@ -63,6 +67,10 @@ Rails.application.configure do # Use a different cache store in production. # config.cache_store = :mem_cache_store + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "foodsoft_production" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. @@ -89,22 +97,31 @@ Rails.application.configure do config.action_mailer.smtp_settings[:domain] = ENV['SMTP_DOMAIN'] if ENV['SMTP_DOMAIN'].present? config.action_mailer.smtp_settings[:user_name] = ENV['SMTP_USER_NAME'] if ENV['SMTP_USER_NAME'].present? config.action_mailer.smtp_settings[:password] = ENV['SMTP_PASSWORD'] if ENV['SMTP_PASSWORD'].present? - config.action_mailer.smtp_settings[:authentication] = ENV['SMTP_AUTHENTICATION'] if ENV['SMTP_AUTHENTICATION'].present? - config.action_mailer.smtp_settings[:enable_starttls_auto] = ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true' if ENV['SMTP_ENABLE_STARTTLS_AUTO'].present? - config.action_mailer.smtp_settings[:openssl_verify_mode] = ENV['SMTP_OPENSSL_VERIFY_MODE'] if ENV['SMTP_OPENSSL_VERIFY_MODE'].present? + if ENV['SMTP_AUTHENTICATION'].present? + config.action_mailer.smtp_settings[:authentication] = + ENV['SMTP_AUTHENTICATION'] + end + if ENV['SMTP_ENABLE_STARTTLS_AUTO'].present? + config.action_mailer.smtp_settings[:enable_starttls_auto] = + ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true' + end + if ENV['SMTP_OPENSSL_VERIFY_MODE'].present? + config.action_mailer.smtp_settings[:openssl_verify_mode] = + ENV['SMTP_OPENSSL_VERIFY_MODE'] + end else # Use sendmail as default to avoid ssl cert problems config.action_mailer.delivery_method = :sendmail end # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new + config.log_formatter = Logger::Formatter.new # Use a different logger for distributed setups. # require 'syslog/logger' # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - if ENV["RAILS_LOG_TO_STDOUT"].present? + if ENV['RAILS_LOG_TO_STDOUT'].present? logger = ActiveSupport::Logger.new(STDOUT) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) diff --git a/config/environments/test.rb b/config/environments/test.rb index ccf3767f..5f6cef4d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,20 +1,20 @@ -# Foodsoft test configuration. -# -# This file is in the public domain. +require 'active_support/core_ext/integer/time' + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! + # Turn false under Spring and add config.action_view.cache_template_loading = true. config.cache_classes = true - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV['CI'].present? # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true @@ -25,6 +25,7 @@ Rails.application.configure do # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false + config.cache_store = :null_store # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false @@ -32,7 +33,7 @@ Rails.application.configure do # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false - # Store uploaded files on the local file system in a temporary directory + # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test config.action_mailer.perform_caching = false @@ -45,6 +46,15 @@ Rails.application.configure do # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true end diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 00000000..3ba2318b --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,5 @@ +# Pin npm packages by running ./bin/importmap +pin "application", preload: true +pin "trix" +pin "@rails/actiontext", to: "actiontext.js" +pin "trix-editor-overrides" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e80..d52cecaa 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,10 +5,8 @@ Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path -# Add Yarn node_modules folder to the asset load path. -Rails.application.config.assets.paths << Rails.root.join('node_modules') # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w[application_legacy.js jquery.min.js trix-editor-overrides.js] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index d3bcaa5e..54f47cf1 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,25 +1,25 @@ # Be sure to restart your server when you modify this file. -# Define an application-wide content security policy -# For further information see the following documentation -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header -# Rails.application.config.content_security_policy do |policy| -# policy.default_src :self, :https -# policy.font_src :self, :https, :data -# policy.img_src :self, :https, :data -# policy.object_src :none -# policy.script_src :self, :https -# policy.style_src :self, :https - -# # Specify URI for violation reports -# # policy.report_uri "/csp-violation-report-endpoint" +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap and inline scripts +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true # end - -# If you are using UJS then enable automatic nonce generation -# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } - -# Report CSP violations to a specified URI -# For further information see the following documentation: -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only -# Rails.application.config.content_security_policy_report_only = true diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 00000000..24ec0662 --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + # this restricts Foodsoft scopes to certain characters - let's discuss it when it becomes an actual problem + resource %r{\A/[-a-zA-Z0-9_]+/api/v1/}, headers: :any, methods: :any + end +end diff --git a/config/initializers/currency_display.rb b/config/initializers/currency_display.rb index 7caa6a64..24ceeb8b 100644 --- a/config/initializers/currency_display.rb +++ b/config/initializers/currency_display.rb @@ -1,7 +1,8 @@ # remove all currency translations, so that we can set the default language and # have it shown in all other languages too -::I18n.available_locales.each do |locale| - unless locale == ::I18n.default_locale - ::I18n.backend.store_translations(locale, number: { currency: { format: { unit: nil } } }) +I18n.available_locales.each do |locale| + unless locale == I18n.default_locale + I18n.backend.store_translations(locale, + number: { currency: { format: { unit: nil } } }) end end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 83293820..d01c9a5f 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -100,7 +100,7 @@ Doorkeeper.configure do # http://tools.ietf.org/html/rfc6819#section-4.4.2 # http://tools.ietf.org/html/rfc6819#section-4.4.3 # - grant_flows %w(authorization_code implicit password) + grant_flows %w[authorization_code implicit password] # Under some circumstances you might want to have applications auto-approved, # so that the user skips the authorization step. diff --git a/config/initializers/exception_notification.rb b/config/initializers/exception_notification.rb index 10107865..3d342465 100644 --- a/config/initializers/exception_notification.rb +++ b/config/initializers/exception_notification.rb @@ -14,7 +14,7 @@ ExceptionNotification.configure do |config| # Adds a condition to decide when an exception must be ignored or not. # The ignore_if method can be invoked multiple times to add extra conditions. - config.ignore_if do |exception, options| + config.ignore_if do |_exception, _options| Rails.env.development? || Rails.env.test? end @@ -23,9 +23,9 @@ ExceptionNotification.configure do |config| # Email notifier sends notifications by email. if notification = FoodsoftConfig[:notification] config.add_notifier :email, { - :email_prefix => notification[:email_prefix], - :sender_address => notification[:sender_address], - :exception_recipients => notification[:error_recipients], + email_prefix: notification[:email_prefix], + sender_address: notification[:sender_address], + exception_recipients: notification[:error_recipients] } end diff --git a/config/initializers/extensions.rb b/config/initializers/extensions.rb index 799f52e6..d276aecb 100644 --- a/config/initializers/extensions.rb +++ b/config/initializers/extensions.rb @@ -2,8 +2,8 @@ class String # remove comma from decimal inputs def self.delocalized_decimal(string) - if !string.blank? and string.is_a?(String) - BigDecimal.new(string.sub(',', '.')) + if string.present? and string.is_a?(String) + BigDecimal(string.sub(',', '.')) else string end @@ -13,6 +13,6 @@ end class Array def cumulative_sum csum = 0 - self.map { |val| csum += val } + map { |val| csum += val } end end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1e..166997c5 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,4 +1,8 @@ # Be sure to restart your server when you modify this file. -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +# Configure parameters to be filtered from the log file. Use this to limit dissemination of +# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported +# notations and behaviors. +Rails.application.config.filter_parameters += %i[ + passw secret token _key crypt salt certificate otp ssn +] diff --git a/config/initializers/mail_receiver.rb b/config/initializers/mail_receiver.rb index 67288cc1..088d7c93 100644 --- a/config/initializers/mail_receiver.rb +++ b/config/initializers/mail_receiver.rb @@ -1 +1,3 @@ -FoodsoftMailReceiver.register BounceMailReceiver +Rails.application.config.to_prepare do + FoodsoftMailReceiver.register BounceMailReceiver +end diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb deleted file mode 100644 index fac64e0a..00000000 --- a/config/initializers/new_framework_defaults.rb +++ /dev/null @@ -1,17 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.0 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Enable per-form CSRF tokens. Previous versions had false. -Rails.application.config.action_controller.per_form_csrf_tokens = false - -# Enable origin-checking CSRF mitigation. Previous versions had false. -Rails.application.config.action_controller.forgery_protection_origin_check = false - -# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. -# Previous versions had false. -ActiveSupport.to_time_preserves_timezone = false diff --git a/config/initializers/new_framework_defaults_5_1.rb b/config/initializers/new_framework_defaults_5_1.rb deleted file mode 100644 index 9010abd5..00000000 --- a/config/initializers/new_framework_defaults_5_1.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.1 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make `form_with` generate non-remote forms. -Rails.application.config.action_view.form_with_generates_remote_forms = false - -# Unknown asset fallback will return the path passed in when the given -# asset is not present in the asset pipeline. -# Rails.application.config.assets.unknown_asset_fallback = false diff --git a/config/initializers/new_framework_defaults_5_2.rb b/config/initializers/new_framework_defaults_5_2.rb deleted file mode 100644 index 5132a0b1..00000000 --- a/config/initializers/new_framework_defaults_5_2.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.2 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make Active Record use stable #cache_key alongside new #cache_version method. -# This is needed for recyclable cache keys. -# Rails.application.config.active_record.cache_versioning = true - -# Use AES-256-GCM authenticated encryption for encrypted cookies. -# Also, embed cookie expiry in signed or encrypted cookies for increased security. -# -# This option is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 5.2. -# -# Existing cookies will be converted on read then written with the new scheme. -# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true - -# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages -# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. -# Rails.application.config.active_support.use_authenticated_message_encryption = true - -# Add default protection from forgery to ActionController::Base instead of in -# ApplicationController. -# Rails.application.config.action_controller.default_protect_from_forgery = true - -# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and -# 'f' after migrating old data. -Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - -# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. -# Rails.application.config.active_support.use_sha1_digests = true - -# Make `form_with` generate id attributes for any generated HTML tags. -# Rails.application.config.action_view.form_with_generates_ids = true diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb new file mode 100644 index 00000000..00f64d71 --- /dev/null +++ b/config/initializers/permissions_policy.rb @@ -0,0 +1,11 @@ +# Define an application-wide HTTP permissions policy. For further +# information see https://developers.google.com/web/updates/2018/06/feature-policy +# +# Rails.application.config.permissions_policy do |f| +# f.camera :none +# f.gyroscope :none +# f.microphone :none +# f.usb :none +# f.fullscreen :self +# f.payment :self, "https://secure.example.com" +# end diff --git a/config/initializers/rack.rb b/config/initializers/rack.rb index 30970ec9..aa462561 100644 --- a/config/initializers/rack.rb +++ b/config/initializers/rack.rb @@ -1,3 +1,3 @@ # Increase key space for post request. # Warning, this is dangerous. See http://stackoverflow.com/questions/12243694/getting-error-exceeded-available-parameter-key-space -Rack::Utils.key_space_limit = 262144 +Rack::Utils.key_space_limit = 262_144 diff --git a/config/initializers/rails6_backports.rb b/config/initializers/rails6_backports.rb deleted file mode 100644 index b72f4220..00000000 --- a/config/initializers/rails6_backports.rb +++ /dev/null @@ -1,98 +0,0 @@ -raise "Remove no-longer-needed #{__FILE__}!" if Rails::VERSION::MAJOR >= 6 - -require "weakref" - -module ActiveRecord - # Backport https://github.com/rails/rails/pull/36998 and https://github.com/rails/rails/pull/36999 - # to avoid `ThreadError: can't create Thread: Resource temporarily unavailable` issues - module ConnectionAdapters - class ConnectionPool - class Reaper - @mutex = Mutex.new - @pools = {} - @threads = {} - - class << self - def register_pool(pool, frequency) # :nodoc: - @mutex.synchronize do - unless @threads[frequency]&.alive? - @threads[frequency] = spawn_thread(frequency) - end - @pools[frequency] ||= [] - @pools[frequency] << WeakRef.new(pool) - end - end - - private - - def spawn_thread(frequency) - Thread.new(frequency) do |t| - running = true - while running - sleep t - @mutex.synchronize do - @pools[frequency].select!(&:weakref_alive?) - @pools[frequency].each do |p| - p.reap - p.flush - rescue WeakRef::RefError - end - - if @pools[frequency].empty? - @pools.delete(frequency) - @threads.delete(frequency) - running = false - end - end - end - end - end - end - - def run - return unless frequency && frequency > 0 - - self.class.register_pool(pool, frequency) - end - end - - def reap - stale_connections = synchronize do - return unless @connections - - @connections.select do |conn| - conn.in_use? && !conn.owner.alive? - end.each(&:steal!) - end - - stale_connections.each do |conn| - if conn.active? - conn.reset! - checkin conn - else - remove conn - end - end - end - - def flush(minimum_idle = @idle_timeout) - return if minimum_idle.nil? - - idle_connections = synchronize do - return unless @connections - - @connections.select do |conn| - !conn.in_use? && conn.seconds_idle >= minimum_idle - end.each do |conn| - conn.lease - - @available.delete conn - @connections.delete conn - end - end - - idle_connections.each(&:disconnect!) - end - end - end -end diff --git a/config/initializers/rswag_api.rb b/config/initializers/rswag_api.rb new file mode 100644 index 00000000..e4b798f6 --- /dev/null +++ b/config/initializers/rswag_api.rb @@ -0,0 +1,13 @@ +Rswag::Api.configure do |c| + # Specify a root folder where Swagger JSON files are located + # This is used by the Swagger middleware to serve requests for API descriptions + # NOTE: If you're using rswag-specs to generate Swagger, you'll need to ensure + # that it's configured to generate files in the same folder + c.swagger_root = Rails.root.to_s + '/swagger' + + # Inject a lambda function to alter the returned Swagger prior to serialization + # The function will have access to the rack env for the current request + # For example, you could leverage this to dynamically assign the "host" property + # + # c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } +end diff --git a/config/initializers/rswag_ui.rb b/config/initializers/rswag_ui.rb new file mode 100644 index 00000000..cc9f2ef8 --- /dev/null +++ b/config/initializers/rswag_ui.rb @@ -0,0 +1,15 @@ +Rswag::Ui.configure do |c| + # List the Swagger endpoints that you want to be documented through the + # swagger-ui. The first parameter is the path (absolute or relative to the UI + # host) to the corresponding endpoint and the second is a title that will be + # displayed in the document selector. + # NOTE: If you're using rspec-api to expose Swagger files + # (under swagger_root) as JSON or YAML endpoints, then the list below should + # correspond to the relative paths for those endpoints. + + c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs' + + # Add Basic Auth in case your API is private + # c.basic_auth_enabled = true + # c.basic_auth_credentials 'username', 'password' +end diff --git a/config/initializers/ruby_units.rb b/config/initializers/ruby_units.rb index b8b56cca..af422fcb 100644 --- a/config/initializers/ruby_units.rb +++ b/config/initializers/ruby_units.rb @@ -2,28 +2,28 @@ if defined? RubyUnits RubyUnits::Unit.redefine!('liter') do |unit| - unit.aliases += %w{ltr} + unit.aliases += %w[ltr] end RubyUnits::Unit.redefine!('kilogram') do |unit| - unit.aliases += %w{KG} + unit.aliases += %w[KG] end RubyUnits::Unit.redefine!('gram') do |unit| - unit.aliases += %w{gr} + unit.aliases += %w[gr] end RubyUnits::Unit.define('piece') do |unit| unit.definition = RubyUnits::Unit.new('1 each') - unit.aliases = %w{pc pcs piece pieces} # locale: en - unit.aliases += %w{st stuk stuks} # locale: nl + unit.aliases = %w[pc pcs piece pieces] # locale: en + unit.aliases += %w[st stuk stuks] # locale: nl unit.kind = :counting end RubyUnits::Unit.define('bag') do |unit| unit.definition = RubyUnits::Unit.new('1 each') - unit.aliases = %w{bag bags blt sachet sachets} # locale: en - unit.aliases += %w{zak zakken zakje zakjes} # locale: nl + unit.aliases = %w[bag bags blt sachet sachets] # locale: en + unit.aliases += %w[zak zakken zakje zakjes] # locale: nl unit.kind = :counting end diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 4f01f173..fe62df4a 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -8,12 +8,12 @@ Foodsoft::Application.config.secret_key_base = begin if (token = ENV.fetch('SECRET_KEY_BASE', nil)).present? token elsif Rails.env.production? || Rails.env.staging? - raise "You must set SECRET_KEY_BASE" + raise 'You must set SECRET_KEY_BASE' elsif Rails.env.test? SecureRandom.hex(30) # doesn't really matter else sf = Rails.root.join('tmp', 'secret_key_base') - if File.exists?(sf) + if File.exist?(sf) File.read(sf) else puts "=> Generating initial SECRET_KEY_BASE in #{sf}" diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index d7841180..370a202e 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -3,7 +3,7 @@ module ActionDispatch module Session class SlugCookieStore < CookieStore - alias_method :orig_set_cookie, :set_cookie + alias orig_set_cookie set_cookie def set_cookie(request, session_id, cookie) if script_name = FoodsoftConfig[:script_name] diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb index a95278d0..1292ac83 100644 --- a/config/initializers/simple_form_bootstrap.rb +++ b/config/initializers/simple_form_bootstrap.rb @@ -11,7 +11,7 @@ SimpleForm.setup do |config| end end - config.wrappers :prepend, tag: 'div', class: "control-group", error_class: 'error' do |b| + config.wrappers :prepend, tag: 'div', class: 'control-group', error_class: 'error' do |b| b.use :html5 b.use :placeholder b.use :label @@ -24,7 +24,7 @@ SimpleForm.setup do |config| end end - config.wrappers :append, tag: 'div', class: "control-group", error_class: 'error' do |b| + config.wrappers :append, tag: 'div', class: 'control-group', error_class: 'error' do |b| b.use :html5 b.use :placeholder b.use :label diff --git a/config/initializers/time_formats.rb b/config/initializers/time_formats.rb new file mode 100644 index 00000000..b0447b7e --- /dev/null +++ b/config/initializers/time_formats.rb @@ -0,0 +1 @@ +Time::DATE_FORMATS[:foodsoft_datetime] = "%d.%m.%Y %H:%M" diff --git a/config/initializers/zeitwerk.rb b/config/initializers/zeitwerk.rb new file mode 100644 index 00000000..ede05b16 --- /dev/null +++ b/config/initializers/zeitwerk.rb @@ -0,0 +1,4 @@ +# config/initializers/zeitwerk.rb +ActiveSupport::Dependencies + .autoload_paths + .delete(Rails.root.join('app/controllers/concerns').to_s) diff --git a/config/locales/de.yml b/config/locales/de.yml index 5a1a5b35..b7f77c5d 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -655,6 +655,7 @@ de: default_role_pickups: Abholtage default_role_suppliers: Lieferanten disable_invite: Einladungen deaktivieren + disable_members_overview: Mitgliederliste deaktivieren email_from: Absenderadresse email_replyto: Antwortadresse email_sender: Senderadresse @@ -1215,12 +1216,15 @@ de: js: ordering: confirm_change: Änderungen an dieser Bestellung gehen verloren, wenn zu einer anderen Bestellung gewechselt wird. Möchtest Du trotzdem wechseln? + trix_editor: + file_size_alert: Der Dateianhang ist zu groß! Die maximale Größe beträgt 512Mb layouts: email: footer_1_separator: "--" footer_2_foodsoft: 'Foodsoft: %{url}' footer_3_homepage: 'Foodcoop: %{url}' footer_4_help: 'Hilfe: %{url}' + help: 'Hilfe' foodsoft: Foodsoft footer: revision: Revision %{revision} @@ -1672,6 +1676,7 @@ de: language: de: Deutsch es: Spanisch + tr: Türkisch fr: Französisch nl: Niederländisch required: @@ -1875,3 +1880,6 @@ de: title: Arbeitsgruppen update: notice: Arbeitsgruppe wurde aktualisiert + time: + formats: + foodsoft_datetime: "%d.%m.%Y %H:%M" diff --git a/config/locales/en.yml b/config/locales/en.yml index 59e94385..b4f41c5c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -656,6 +656,7 @@ en: default_role_pickups: Pickup days default_role_suppliers: Suppliers disable_invite: Disable invites + disable_members_overview: Disable members list email_from: From address email_replyto: Reply-to address email_sender: Sender address @@ -1218,12 +1219,15 @@ en: js: ordering: confirm_change: Modifications to this order will be lost when you change the order. Do you want to lose the changes you made and continue? + trix_editor: + file_size_alert: The file is to large! The supported file size is 512Mb! layouts: email: footer_1_separator: "--" footer_2_foodsoft: 'Foodsoft: %{url}' footer_3_homepage: 'Foodcoop: %{url}' footer_4_help: 'Help: %{url}' + help: 'Help' foodsoft: Foodsoft footer: revision: revision %{revision} @@ -1693,6 +1697,7 @@ en: es: Spanish fr: French nl: Dutch + tr: Turkish required: mark: "*" text: required @@ -1902,3 +1907,6 @@ en: title: Workgroups update: notice: Workgroup was updated + time: + formats: + foodsoft_datetime: "%Y-%m-%d %H:%M" diff --git a/config/locales/es.yml b/config/locales/es.yml index 620ec3bb..6cacb564 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -580,6 +580,7 @@ es: custom_css: CSS adicional default_locale: Idioma por defecto disable_invite: Desactivar invitaciones + disable_members_overview: Desactivar la lista de miembros email_from: Dirección de email de origen email_replyto: Dirección reply-to email_sender: Dirección del remitente @@ -1079,9 +1080,12 @@ es: js: ordering: confirm_change: Las modificaciones sobre este pedido se perderán cuando cambies el pedido. ¿Quieres perder los cambios que has hecho y continuar? + trix_editor: + file_size_alert: ¡El archivo adjunto es demasiado grande! El tamaño máximo es de 512Mb layouts: email: footer_4_help: 'Ayuda: %{url}' + help: 'Ayuda' footer: revision: revisión %{revision} header: @@ -1440,6 +1444,7 @@ es: es: Español fr: Francés nl: Neerlandés + tr: Turco required: text: requerido 'yes': 'Sí' @@ -1617,3 +1622,6 @@ es: title: Grupos de trabajo update: notice: El grupo de trabajo se actualizó. + time: + formats: + foodsoft_datetime: "%d/%b/%Y %H:%M" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4dbdb864..cd0971da 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -418,6 +418,7 @@ fr: zip_code: Code postal currency_unit: Monnaie name: Nom + disable_members_overview: Désactiver la liste des membres distribution_strategy: Stratégie de distribution distribution_strategy_options: first_order_first_serve: Distribuez d'abord à ceux qui ont commandé en premier @@ -830,10 +831,13 @@ fr: js: ordering: confirm_change: Les changements apportés à cette commande vont être perdus. Est-ce que tu veux vraiment continuer? + trix_editor: + file_size_alert: Le fichier joint est trop volumineux ! La taille maximale est de 512Mb layouts: email: footer_3_homepage: 'Boufcoop: %{url}' footer_4_help: 'Aide: %{url}' + help: 'Aide' footer: revision: révision %{revision} header: @@ -1196,6 +1200,7 @@ fr: es: Espagnol fr: Français nl: Néerlandais + tr: Turc required: text: requis 'yes': 'Oui' @@ -1374,3 +1379,6 @@ fr: title: Équipes update: notice: L'équipe a été mise à jour + time: + formats: + foodsoft_datetime: "%d/%m/%Y %H:%M" diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 4c97dda4..d972c088 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -6,19 +6,19 @@ nl: availability: Artikel leverbaar? availability_short: leverb. deposit: Statiegeld - fc_price: Foodcoop prijs - fc_price_desc: Prijs inclusief belasting, statiegeld en foodcoop marge. - fc_price_short: FC prijs - fc_share: Foodcoop marge - fc_share_short: FC marge - gross_price: Bruto prijs + fc_price: Foodcoop-prijs + fc_price_desc: Prijs inclusief belasting, statiegeld en foodcoop-marge. + fc_price_short: FC-prijs + fc_share: Foodcoop-marge + fc_share_short: FC-marge + gross_price: Brutoprijs manufacturer: Producent name: Naam note: Notitie order_number: Artikelnummer order_number_short: Nr. origin: Herkomst - price: Netto prijs + price: Nettoprijs supplier: Leverancier tax: BTW unit: Eenheid @@ -26,11 +26,11 @@ nl: unit_quantity_short: Colli units: Eenheden article_category: - description: Import namen + description: Namen importeren name: Naam article_price: deposit: Statiegeld - price: Netto prijs + price: Nettoprijs tax: BTW unit_quantity: Colligrootte bank_account: @@ -69,7 +69,7 @@ nl: financial_transaction_type: bank_account: Bankrekening name: Naam - financial_transaction_class: Financiële transactie klasse + financial_transaction_class: Klasse financiële transactie name_short: Verkorte naam group_order: ordergroup: Huishouden @@ -89,7 +89,7 @@ nl: created_at: Gemaakt op created_by: Gemaakt door date: Factuurdatum - delete_attachment: Verwijder bijlage + delete_attachment: Bijlage verwijderen deliveries: Voorraad levering deposit: Statiegeld in rekening gebracht deposit_credit: Statiegeld teruggekregen @@ -102,7 +102,7 @@ nl: supplier: Leverancier mail_delivery_status: created_at: Tijd - email: Email + email: E-mail message: Bericht order: boxfill: Dozen vullen na @@ -111,8 +111,8 @@ nl: end_action: Sluitingsactie end_actions: auto_close: Bestelling sluiten - auto_close_and_send: Sluit de bestelling en verzend het naar de leverancier - auto_close_and_send_min_quantity: Sluit de bestelling en verzend het naar de leverancier als de minimum bestelhoeveelheid gehaald is + auto_close_and_send: De bestelling sluiten en naar de leverancier verzenden + auto_close_and_send_min_quantity: De bestelling sluiten en naar de leverancier verzenden als de minimale bestelhoeveelheid gehaald is no_end_action: Geen automatische actie ends: Sluit op name: Leverancier @@ -124,10 +124,10 @@ nl: transport: Vervoerskosten transport_distribution: Verdeling van vervoerskosten transport_distributions: - articles: Verdeel de kosten over het aantal ontvangen artikelen - ordergroup: Ieder huishouden betaald hetzelfde bedrag - price: Verdeel de kosten over het orderbedrag - skip: Verdeel de kosten niet + articles: De kosten over het aantal ontvangen artikelen verdelen + ordergroup: Ieder huishouden betaalt hetzelfde bedrag + price: De kosten over het orderbedrag verdelen + skip: De kosten niet verdelen updated_by: Laatst aangepast door order_article: article: Artikel @@ -141,7 +141,7 @@ nl: units_to_order_short: Besteld update_global_price: Huidige prijs overal bijwerken order_comment: - text: Commentaar voor deze bestelling toevoegen ... + text: Commentaar voor deze bestelling toevoegen… ordergroup: account_balance: Tegoed available_funds: Beschikbaar tegoed @@ -173,7 +173,7 @@ nl: customer_number: Klantnummer customer_number_short: Klantnr. delivery_days: Bezorgdagen - email: Email + email: E-mail fax: Fax iban: IBAN is_subscribed: geabonneerd? @@ -198,11 +198,11 @@ nl: user_list: Verantwoordelijken workgroup: Werkgroep user: - email: Email + email: E-mail first_name: Voornaam iban: IBAN last_activity: Laatst actief - last_login: Laatste login + last_login: Laatste aanmelding last_name: Achternaam name: Naam nick: Gebruikersnaam @@ -246,11 +246,11 @@ nl: bank_transaction: Banktransactie delivery: Levering financial_transaction: Financiële transactie - financial_transaction_class: Financiële transactie klasse - financial_transaction_type: Financiële transactie type + financial_transaction_class: Klasse financiële transactie + financial_transaction_type: Type financiële transactie invoice: Factuur order: Bestelling - order_article: Bestellingsartikel + order_article: Bestelartikel order_comment: Commentaar ordergroup: one: Huishouden @@ -269,8 +269,8 @@ nl: all_users: Alle gebruikers all_workgroups: Alle werkgroepen created_at: gemaakt op - first_paragraph: Hier kun je de groepen en gebruiker van Foodsoft beheren. - groupname: Groepnaam + first_paragraph: Hier kun je de groepen en gebruikers van Foodsoft beheren. + groupname: Groepsnaam members: leden name: naam new_ordergroup: Nieuw huishouden @@ -290,14 +290,14 @@ nl: submit: Opslaan title: Configuratie tab_layout: - pdf_title: PDF documenten + pdf_title: PDF-documenten tab_messages: - emails_title: Emailinstellingen + emails_title: E-mailinstellingen tab_payment: schedule_title: Bestelrooster tab_security: default_roles_title: Toegang tot - default_roles_paragraph: Ieder lid van de foodcoop heeft standaard toegang tot de volgende onderdelen. + default_roles_paragraph: "Ieder lid van de foodcoop heeft standaard toegang tot de volgende onderdelen:" tab_tasks: periodic_title: Periodieke taken tabs: @@ -308,29 +308,29 @@ nl: finances: index: bank_accounts: Bankrekeningen - first_paragraph: Hier kunt u de financiële transactieklassen en de bijbehorende financiële transactietypes beheren. Elke financiële transactie heeft een type, die je bij elke transactie moet selecteren, als je meer dan één type hebt gemaakt. De financiële transactieklassen kunnen worden gebruikt om de financiële transactietypes te groeperen en zullen worden weergegeven als extra kolommen in het rekeningoverzicht, als er meer dan één is gecreëerd. + first_paragraph: Hier kunt u de klassen van financiële transacties en de bijbehorende typen financiële transacties beheren. Elke financiële transactie heeft een type, die je bij elke transactie moet selecteren, als je meer dan één type hebt gemaakt. De klassen financiële transacties kunnen worden gebruikt om de types financiële transacties te groeperen en zullen worden weergegeven als extra kolommen in het rekeningoverzicht, als er meer dan één is gecreëerd. new_bank_account: Nieuwe bankrekening toevoegen - new_financial_transaction_class: Nieuwe financiële transactie klasse toevoegen + new_financial_transaction_class: Nieuwe klasse voor financiële transacties toevoegen title: Financiën - transaction_types: Financiële transactie typen + transaction_types: Typen financiële transacties transaction_types: name: Naam - new_financial_transaction_type: Nieuw financiëel transactie type toevoegen + new_financial_transaction_type: Nieuw type financiële transactie toevoegen financial_transaction_classes: form: - title_edit: Financiële transactie klasse bewerken - title_new: Nieuw financiëel transactie type toevoegen + title_edit: Klasse voor financiële transactie bewerken + title_new: Nieuw type financiële transactie toevoegen financial_transaction_types: form: - name_short_desc: De korte naam is verplicht voor financiële transactietypes die automatisch moeten kunnen worden toegewezen bij banktransacties. Als er meerdere bankrekeningen zijn, kan de voorkeursrekening voor bankoverschrijvingen worden geselecteerd. - title_edit: Financiëel transactie type bewerken - title_new: Nieuw financiëel transactie type toevoegen + name_short_desc: De korte naam is verplicht voor typen financiële transacties die automatisch moeten kunnen worden toegewezen bij banktransacties. Als er meerdere bankrekeningen zijn, kan de voorkeursrekening voor bankoverschrijvingen worden geselecteerd. + title_edit: Type financiële transactie bewerken + title_new: Nieuw type financiëel transactie toevoegen mail_delivery_status: destroy_all: - notice: Alle emailproblemen zijn verwijderd + notice: Alle e-mailproblemen zijn verwijderd index: - destroy_all: Alle emailproblemen verwijderen - title: Emailproblemen + destroy_all: Alle e-mailproblemen verwijderen + title: E-mailproblemen ordergroups: destroy: error: 'Huishouden kon niet als verwijderd gemarkeerd worden: %{error}' @@ -342,9 +342,9 @@ nl: here: hier index: first_paragraph: Hier kun je %{url} toevoegen, bewerken en verwijderen. - new_ordergroup: Nieuw huishouden + new_ordergroup: een nieuw huishouden new_ordergroups: nieuwe huishoudens - second_paragraph: 'Bedenk het onderscheid tussen werkgroep en huishouden: een huishouden heeft een rekening en kan bestellen. in een %{url} (bijv. sorteergroep) werken leden samen om taken te vervullen. Leden kunnen slechts lid zijn van éen huishouden, maar van meerdere werkgroepen.' + second_paragraph: 'Let op het onderscheid tussen werkgroep en huishouden: een huishouden heeft een rekening en kan bestellen; in een %{url} (bijv. sorteergroep) werken leden samen om taken te vervullen. Leden kunnen slechts lid zijn van één huishouden, maar van meerdere werkgroepen.' title: Huishoudens workgroup: werkgroep new: @@ -353,39 +353,39 @@ nl: confirm: Weet je het zeker? edit: Groep/leden bewerken title: Huishouden %{name} - search_placeholder: naam ... + search_placeholder: naam… users: controller: - sudo_done: Je bent nu ingelogd als %{user}. Wees voorzichtig, en vergeet niet uit te loggen als je klaar bent! + sudo_done: Je bent nu aangemeld als %{user}. Wees voorzichtig, en vergeet niet af te melden als je klaar bent! destroy: error: 'Gebruiker kon niet verwijderd worden: %{error}' notice: Gebruiker is verwijderd edit: title: Lid bewerken form: - create_ordergroup: Maak een huishouden met dezelfde naam en voeg een gebruiker toe. - send_welcome_mail: Verstuur een welkomstmail naar de gebruiker. + create_ordergroup: Een huishouden met dezelfde naam aanmaken en een gebruiker toevoegen. + send_welcome_mail: Een welkomstmail naar de gebruiker versturen. index: first_paragraph: Hier kun je gebruikers %{url}, bewerken en wissen. new_user: Nieuwe gebruiker new_users: toevoegen show_deleted: Verwijderde gebruikers tonen - title: Gebruikers admin + title: Gebruikersbeheer new: title: Nieuwe gebruiker toevoegen restore: error: 'Gebruiker kon niet opnieuw actief gemaakt worden: %{error}' notice: Gebruiker is opnieuw actief show: - confirm_sudo: Als je doorgaat, neem je de identiteit aan van gebruiker %{user}. Vergeet hierna niet uit te loggen! + confirm_sudo: Als je doorgaat, neem je de identiteit aan van gebruiker %{user}. Vergeet hierna niet af te melden! groupabos: Groepslidmaatschappen member_since: Lid sinds %{time} person: Persoon preference: Voorkeuren - show_email_problems: Bekijk emailproblemen - sudo: Inloggen als + show_email_problems: E-mailproblemen bekijken + sudo: Aanmelden als users: - show_email_problems: Bekijk emailproblemen + show_email_problems: E-mailproblemen bekijken workgroups: destroy: error: 'Werkgroep kon niet verwijderd worden: %{error}' @@ -400,7 +400,7 @@ nl: new_workgroup: Nieuwe werkgroep new_workgroups: nieuwe werkgroepen ordergroup: huishouden - second_paragraph: 'Let op het verschil tussen een groep en een huishouden: een %{url} heeft een tegoed en kan bestellen. In een werkgroep (bijv. ''sorteergroep'') organizeren zich de leden met behulp van taken en berichten. Gebruikers kunnen slechts lid zijn van één huishouden, maar van meerdere werkgroepen.' + second_paragraph: 'Let op het verschil tussen een groep en een huishouden: een %{url} heeft een tegoed en kan bestellen. In een werkgroep (bijv. ''sorteergroep'') organiseren zich de leden met behulp van taken en berichten. Gebruikers kunnen slechts lid zijn van één huishouden, maar van meerdere werkgroepen.' title: Werkgroepen new: title: Werkgroep toevoegen @@ -413,9 +413,9 @@ nl: name: naam application: controller: - error_authn: Inloggen vereist! - error_denied: Je hebt geen toegang tot de gevraagde pagina. Als je denkt dat je dat wel zou moeten hebben, vraag dan een beheerder je die rechten te geven. Als je meerdere accounts hebt, wil je mogelijk %{sign_in}. - error_denied_sign_in: inloggen als een andere gebruiker + error_authn: Aanmelden vereist! + error_denied: Je hebt geen toegang tot de gevraagde pagina. Als je denkt dat je dat wel zou moeten hebben, vraag dan een beheerder je die rechten te geven. Als je meerdere accounts hebt, wil je je mogelijk %{sign_in}. + error_denied_sign_in: aanmelden als een andere gebruiker error_feature_disabled: Deze optie is momenteel niet actief. error_members_only: Deze actie is alleen beschikbaar voor leden van de groep! error_minimum_balance: Sorry, je tegoed is lager dan het minimum van %{min}. @@ -429,7 +429,7 @@ nl: title: Categorie bewerken index: new: Nieuwe categorie - title: Categoriën + title: Categorieën new: title: Nieuwe categorie maken update: @@ -440,9 +440,9 @@ nl: articles: confirm_delete: Weet je zeker dat je alle artikelen wilt verwijderen? option_available: Artikelen beschikbaar maken - option_delete: Verwijder artikel + option_delete: Artikel verwijderen option_not_available: Artikelen onbeschikbaar maken - option_select: Kies actie ... + option_select: Actie kiezen… price_netto: Prijs unit_quantity_desc: Aantal eenheden per doos (colli) unit_quantity_short: Colli @@ -451,10 +451,10 @@ nl: notice: "Er zijn %{count} nieuwe artikelen opgeslagen." error_invalid: Er zijn artikelen die een fout hebben error_nosel: Geen artikelen geselecteerd - error_parse: "%{msg} ... in regel %{line}" + error_parse: "%{msg} … in regel %{line}" error_update: 'Er trad een fout op bij het bijwerken van artikel ''%{article}'': %{msg}' parse_upload: - no_file: Kies een bestand om te uploaden. + no_file: Een bestand om te uploaden kiezen. notice: "%{count} artikelen zijn geanalyseerd" sync: notice: Catalogus is bijgewerkt @@ -472,7 +472,7 @@ nl: drop: verwijderen note: "%{article} is deel van een lopende bestelling en kan niet verwijderd worden. Het artikel graag eerst uit de bestelling(en) %{drop_link}." edit_all: - note: 'Verplichte velden zijn: Naam, eenheid, (netto) prijs en bestellingsnummer.' + note: 'Verplichte velden zijn: Naam, eenheid, (netto) prijs en bestelnummer.' submit: Alle artikelen bijwerken title: Alle artikelen van %{supplier} bewerken warning: 'Let op, alle artikelen worden bijgewerkt!' @@ -484,7 +484,7 @@ nl: already_imported: geïmporteerd not_found: Geen artikelen gevonden index: - change_supplier: Leverancier wisselen ... + change_supplier: Leverancier wisselen… download: Artikelen downloaden edit_all: Alles bewerken ext_db: @@ -497,14 +497,14 @@ nl: title: Artikel importeren new: Nieuw artikel new_order: Nieuwe bestelling - search_placeholder: Naam ... + search_placeholder: Naam… title: Artikelen van %{supplier} (%{count}) upload: Artikelen uploaden model: error_in_use: "%{article} kan niet gewist worden, want deze is deel van een lopende bestelling!" error_nosel: Je hebt geen artikelen geselecteerd parse_upload: - body: "

Ingelezen artikelen graag controleren.

Let op, momenteel vind er geen controle op dubbele artikelen plaats.

" + body: "

Ingelezen artikelen graag controleren.

Let op, momenteel vindt er geen controle op dubbele artikelen plaats.

" submit: Upload verwerken title: Artikelen uploaden sync: @@ -512,32 +512,32 @@ nl: alert_used: Opgelet, %{article} wordt gebruikt in een lopende bestelling. Haal het eerst uit de bestelling. body: 'De volgende artikelen zijn uit de lijst gehaald en worden verwijderd:' body_ignored: - one: Er is één artikel zonder artikelnummer overslagen. + one: Er is één artikel zonder artikelnummer overgeslagen. other: "%{count} artikelen zonder artikelnummer zijn overgeslagen." body_skip: Er zijn geen artikelen om te verwijderen. - title: Uit de lijst halen ... + title: Uit de lijst halen… price_short: prijs submit: Alles synchroniseren title: Artikelen met externe database synchroniseren unit_quantity_short: Colli update: body: 'Ieder artikel wordt tweemaal getoond: oude waarden zijn grijs, en de tekstvelden bevatten de nieuwe waarden. Verschillen met de oude artikelen zijn geel gemarkeerd.' - title: Bijwerken ... + title: Bijwerken… update_msg: - one: Er moet éen artikel bijgewerkt worden. + one: Er moet één artikel bijgewerkt worden. other: "Er moeten %{count} artikelen bijgewerkt worden." upnew: body_count: - one: Er is éen nieuw artikel. + one: Er is één nieuw artikel. other: Er zijn %{count} nieuwe artikelen. - title: Toevoegen ... + title: Toevoegen… upload: fields: reserved: "(Leeg)" status: Status (x=overslaan) - file_label: Graag een compatibel bestand uitkiezen + file_label: Kies een compatibel bestand uit options: - convert_units: Bestaande eenheden behouden, herbereken groothandelseenheid en prijs (net als synchronizeren). + convert_units: Bestaande eenheden behouden, groothandelseenheid en prijs herberekenen (net als synchroniseren). outlist_absent: Artikelen die niet in het bestand voorkomen, verwijderen. sample: juices: Sappen @@ -549,21 +549,21 @@ nl: tomato_juice: Tomatensap walnuts: Walnoten submit: Bestand uploaden - text_1: 'Hier kun je een spreadsheet uploaden om de artikelen van %{supplier} bij te werken. Zowel Excel (xls, xlsx) als OpenOffice (ods) spreadsheets worden gelezen, evenals csv-bestanden (kolommen geschieden door ";", utf-8 encoding). Alleen de eerste sheet wordt geïmporteerd, en kolommen worden verwacht in deze volgorde:' - text_2: De rijen hier getoond zijn voorbeelden. Een "x" in de eerste kolom geeft aan dat het artikel niet meer beschikbaar is en zal worden verwijderd. Hiermee kun je snel meerdere artikelen tegelijk verwijderen. De categorie wordt gematched met de Foodsoft categorielijst (zowel met de categorienaam als de bijbehorende importnamen). + text_1: 'Hier kun je een spreadsheet uploaden om de artikelen van %{supplier} bij te werken. Spreadsheets van zowel Excel (xls, xlsx) als OpenOffice (ods) worden gelezen, evenals csv-bestanden (kolommen gescheiden door ";", utf-8 encoding). Alleen de eerste sheet wordt geïmporteerd, en kolommen worden verwacht in deze volgorde:' + text_2: De rijen hier getoond zijn voorbeelden. Een “x†in de eerste kolom geeft aan dat het artikel niet meer beschikbaar is en zal worden verwijderd. Hiermee kun je snel meerdere artikelen tegelijk verwijderen. De categorie wordt vergeleken met de categorielijst van Foodsoft (zowel met de categorienaam als de bijbehorende importnamen). title: Artikelen uploaden voor %{supplier} bank_account_connector: - confirm: Bevestig de code %{code}. + confirm: Code %{code} bevestigen. fields: - email: E-Mail + email: E-mail pin: PIN password: Wachtwoord tan: TAN username: Gebruikersnaam config: hints: - applepear_url: Website waar het appelpunten systeem wordt uitgelegd. - charge_members_manually: Kies deze optie als je elders bijhoudt wie welke producten heeft gekregen (bijvoorbeeld op papier), en dat ook niet naderhand in Foodsoft invoert. Na het afrekenen van bestellingen moet je dan iedere keer bij leden handmatig het in rekening te brengen bedrag afschrijven (gebruik "Nieuwe transacties toevoegen"). Het blijft wel nodig bestellingen af te rekenen, maar dat brengt dan niets in rekening bij leden. + applepear_url: Website waar het appelpuntensysteem wordt uitgelegd. + charge_members_manually: Kies deze optie als je elders bijhoudt wie welke producten heeft gekregen (bijvoorbeeld op papier), en dat ook niet naderhand in Foodsoft invoert. Na het afrekenen van bestellingen moet je dan iedere keer bij leden handmatig het in rekening te brengen bedrag afschrijven (gebruik “Nieuwe transacties toevoegenâ€). Het blijft wel nodig bestellingen af te rekenen, maar dat brengt dan niets in rekening bij leden. contact: email: Algemeen contactadres, zowel voor op de website als in formulieren. street: Adres, meestal is dit het aflever- en ophaaladres. @@ -626,6 +626,7 @@ nl: default_role_pickups: Ophaaldagen default_role_suppliers: Leveranciers disable_invite: Uitnodigingen deactiveren + disable_members_overview: Ledenlijst deactiveren email_from: From adres email_replyto: Reply-to adres email_sender: Sender adres @@ -1188,12 +1189,15 @@ nl: js: ordering: confirm_change: Als je naar een andere bestelling gaat, gaan je aanpassingen in deze bestelling verloren. Wijzigingen vergeten en naar de andere bestelling gaan? + trix_editor: + file_size_alert: De bestandsbijlage is te groot! De maximale grootte is 512Mb! layouts: email: footer_1_separator: "--" footer_2_foodsoft: 'Foodsoft: %{url}' footer_3_homepage: 'Foodcoop: %{url}' footer_4_help: 'Help: %{url}' + help: 'Help' foodsoft: Foodsoft footer: revision: revisie %{revision} @@ -1658,6 +1662,7 @@ nl: es: Spaans fr: Frans nl: Nederlands + tr: Turks required: mark: "*" text: verplicht @@ -1867,3 +1872,6 @@ nl: title: Werkgroepen update: notice: Werkgroep is bijgewerkt + time: + formats: + foodsoft_datetime: "%d-%m-%Y %H:%M" diff --git a/config/locales/tr.yml b/config/locales/tr.yml new file mode 100644 index 00000000..76408463 --- /dev/null +++ b/config/locales/tr.yml @@ -0,0 +1,1910 @@ +tr: + activerecord: + attributes: + article: + article_category: Kategori + availability: Ãœrün mevcut mu? + availability_short: mevcut. + deposit: Depozito + fc_price: FoodCoop fiyatı + fc_price_desc: Vergiler, depozito ve Foodcoop ücreti dahil fiyat. + fc_price_short: FC fiyatı + fc_share: FoodCoop marjı + fc_share_short: FC marjı + gross_price: Brüt fiyat + manufacturer: Ãœretici + name: Adı + note: Not + order_number: SipariÅŸ numarası + order_number_short: Nr. + origin: MenÅŸei + price: Fiyat (net) + supplier: Tedarikçi + tax: KDV + unit: Birim + unit_quantity: Birim miktarı + unit_quantity_short: B.M. + units: Birimler + article_category: + description: Ä°thalat isimleri + name: Adı + article_price: + deposit: Depozito + price: Fiyat (net) + tax: KDV + unit_quantity: Birim miktarı + bank_account: + balance: Bakiye + bank_gateway: Banka geçidi + description: Açıklama + iban: IBAN + name: Adı + bank_gateway: + authorization: Yetkilendirme baÅŸlığı + name: Adı + unattended_user: Devre dışı bırakılmış kullanıcı + url: URL + bank_transaction: + amount: Tutar + date: Tarih + external_id: Harici ID + financial_link: Finansal baÄŸlantı + iban: IBAN + reference: Referans + text: Açıklama + delivery: + date: Teslim tarihi + note: Not + supplier: Tedarikçi + document: + created_at: OluÅŸturulma tarihi + created_by: OluÅŸturan + data: Veri + mime: MIME tipi + name: Adı + financial_transaction: + amount: Tutar + created_on: Tarih + financial_transaction_class: Finansal iÅŸlem sınıfı + financial_transaction_type: Finansal iÅŸlem türü + note: Not + ordergroup: SipariÅŸ grubu + user: Giren kullanıcı + financial_transaction_class: + ignore_for_account_balance: Hesap bakiyesi için yoksay + name: Adı + financial_transaction_type: + bank_account: Banka Hesabı + name: Adı + financial_transaction_class: Finansal iÅŸlem sınıfı + name_short: Kısa Adı + group_order: + ordergroup: SipariÅŸ grubu + price: SipariÅŸ tutarı + updated_by: Son sipariÅŸi veren + group_order_article: + ordered: SipariÅŸ edildi + quantity: Miktar + received: Alındı + result: Sonuç + tolerance: Tolerans + total_price: Toplam + unit_price: Birim fiyatı + invoice: + amount: Tutar + attachment: Ek + created_at: OluÅŸturulma tarihi + created_by: OluÅŸturan + date: Fatura tarihi + delete_attachment: Eki sil + deliveries: Stok teslimatı + deposit: Tahsil edilen depozito + deposit_credit: Ä°ade edilen depozito + financial_link: Finansal baÄŸlantı + net_amount: Ä°ade için düzeltilmiÅŸ tutar + note: Not + number: Numara + orders: SipariÅŸ + paid_on: ÖdendiÄŸi tarih + supplier: Tedarikçi + mail_delivery_status: + created_at: Tarih + email: E-posta + message: Mesaj + order: + boxfill: Kutuları doldurma tarihi + closed_by: Kapatıldı + created_by: OluÅŸturan + end_action: Otomatik kapanma eylemi + end_actions: + auto_close: SipariÅŸi kapat + auto_close_and_send: SipariÅŸi kapat ve tedarikçiye gönder + auto_close_and_send_min_quantity: Minimum miktarı karşılandığında sipariÅŸi kapat ve tedarikçiye gönder + no_end_action: Otomatik eylem yok + ends: BitiÅŸ tarihi + name: Tedarikçi + note: Not + pickup: Teslim alma + starts: BaÅŸlangıç tarihi + status: Durum + supplier: Tedarikçi + transport: Taşıma maliyeti + transport_distribution: Taşıma maliyeti dağıtımı + transport_distributions: + articles: Alınan ürün sayısına göre maliyeti dağıt + ordergroup: Her sipariÅŸ grubu aynı tutarı öder + price: SipariÅŸ tutarına göre maliyeti dağıt + skip: Maliyeti dağıtma + updated_by: Son düzenleyen + order_article: + article: Ãœrün + missing_units: Eksik birimler + missing_units_short: Eksik + quantity: Talep edilen miktar + quantity_short: Talep + units_received: Alınan birimler + units_received_short: Alınan + units_to_order: SipariÅŸ edilen birimler + units_to_order_short: SipariÅŸ edildi + update_global_price: Mevcut fiyatı tüm sipariÅŸlerde güncelle + order_comment: + text: Bu sipariÅŸe yorum ekle ... + ordergroup: + account_balance: Hesap bakiyesi + available_funds: Kredi limiti + break: "(Son) mola" + break_until: kadar + contact: Ä°letiÅŸim + contact_address: Adres + contact_person: Ä°lgili kiÅŸi + contact_phone: Telefon + description: Açıklama + ignore_apple_restriction: Elma puan sınırlamasını yok say + last_order: Son sipariÅŸ + last_user_activity: Son etkinlik + name: Adı + user_tokens: Ãœyeler + stock_article: + available: Mevcut + price: Fiyat + quantity: Stoktaki miktar + quantity_available: Mevcut miktar + quantity_available_short: Mevcut + quantity_ordered: SipariÅŸ edilen miktar + stock_taking: + date: Tarih + note: Not + supplier: + address: Adres + contact_person: Ä°lgili kiÅŸi + customer_number: Müşteri numarası + customer_number_short: Müşteri nr. + delivery_days: Teslimat günleri + email: E-posta + fax: Faks + iban: IBAN + is_subscribed: abone mi? + min_order_quantity: Minimum sipariÅŸ miktarı + min_order_quantity_short: Min. miktar + name: Adı + note: Not + order_howto: Nasıl sipariÅŸ verilir + phone: Telefon + phone2: Telefon 2 + shared_sync_method: Nasıl senkronize edilir + url: Ana sayfa + supplier_category: + name: Adı + description: Açıklama + financial_transaction_class: Finansal iÅŸlem sınıfı + bank_account: Banka hesabı + task: + created_by: OluÅŸturan + created_on: OluÅŸturma tarihi + description: Açıklama + done: Yapıldı mı? + due_date: BitiÅŸ tarihi + duration: Süre + name: Aktivite + required_users: Gereken kiÅŸi sayısı + user_list: Sorumlu kullanıcılar + workgroup: Çalışma grubu + user: + email: E-posta + first_name: Ä°sim + iban: IBAN + last_activity: Son etkinlik + last_login: Son giriÅŸ + last_name: Soyadı + name: Adı + nick: Kullanıcı adı + ordergroup: SipariÅŸ grubu + password: Åžifre + password_confirmation: Åžifreyi tekrarla + phone: Telefon + workgroup: + one: Çalışma grubu + other: Çalışma grupları + workgroup: + description: Açıklama + name: Adı + role_admin: Yönetim + role_article_meta: Ãœrün veritabanı + role_finance: Finans + role_invoices: Faturalar + role_orders: SipariÅŸ yönetimi + role_pickups: Teslim günleri + role_suppliers: Tedarikçiler + user_tokens: Ãœyeler + errors: + has_many_left: Bu %{collection} ile hala iliÅŸkili! + models: + article: + attributes: + name: + taken: isim zaten alınmış + taken_with_unit: isim ve birim zaten alınmış + supplier: + attributes: + shared_sync_method: + included: bu tedarikçi için geçerli bir seçenek deÄŸil + task: + attributes: + done: + exclusion: tamamlanmış görevler tekrarlanamaz + models: + article: Ãœrün + article_category: Kategori + bank_account: Banka hesabı + bank_gateway: Banka geçidi + bank_transaction: Banka iÅŸlemi + delivery: Teslimat + financial_transaction: Finansal iÅŸlem + financial_transaction_class: Finansal iÅŸlem sınıfı + financial_transaction_type: Finansal iÅŸlem türü + invoice: Fatura + order: SipariÅŸ + order_article: SipariÅŸ ürünü + order_comment: SipariÅŸ yorumu + ordergroup: + one: SipariÅŸ grubu + other: SipariÅŸ grupları + stock_article: Stok ürünü + stock_taking: Stok sayımı + supplier: Tedarikçi + supplier_category: Tedarikçi kategorisi + task: Görev + user: Kullanıcı + workgroup: Çalışma grubu + admin: + access_to: EriÅŸim saÄŸla + base: + index: + all_ordergroups: Tüm sipariÅŸ grupları + all_users: Tüm kullanıcılar + all_workgroups: Tüm çalışma grupları + created_at: oluÅŸturma tarihi + first_paragraph: Burada Foodsoft gruplarını ve kullanıcılarını yönetebilirsiniz. + groupname: grup adı + members: üyeler + name: adı + new_ordergroup: Yeni sipariÅŸ grubu + new_user: Yeni kullanıcı + new_workgroup: Yeni çalışma grubu + newest_groups: en yeni gruplar + newest_users: en yeni kullanıcılar + title: Yönetim + type: tür + username: kullanıcı adı + bank_accounts: + form: + title_edit: Banka hesabını düzenle + title_new: Yeni banka hesabı ekle + bank_gateways: + form: + title_edit: Banka geçidini düzenle + title_new: Yeni banka geçidi ekle + configs: + list: + key: Anahtar + title: Konfigürasyon listesi + value: DeÄŸer + show: + submit: Kaydet + title: Konfigürasyon + tab_layout: + pdf_title: PDF belgeleri + tab_messages: + emails_title: E-posta gönderimi + tab_payment: + schedule_title: SipariÅŸ takvimi + tab_security: + default_roles_title: EriÅŸim saÄŸlanacak alanlar + default_roles_paragraph: Foodcoop üyesi herkes varsayılan olarak aÅŸağıdaki alanlara eriÅŸime sahiptir. + tab_tasks: + periodic_title: Düzenli görevler + tabs: + title: Yapılandırma + update: + notice: Yapılandırma kaydedildi. + confirm: Emin misiniz? + finances: + index: + bank_accounts: Banka hesapları + first_paragraph: Burada finansal iÅŸlem sınıflarını ve ilgili finansal iÅŸlem türlerini yönetebilirsiniz. Her finansal iÅŸlemin bir türü vardır ve birden fazla tür oluÅŸturduysanız, her iÅŸlemde seçmeniz gerekmektedir. Finansal iÅŸlem sınıfları, finansal iÅŸlem türlerini gruplamak için kullanılabilir ve birden fazla oluÅŸturulmuÅŸsa hesap özeti sayfasında ek sütunlar olarak gösterilir. + new_bank_account: Yeni banka hesabı ekle + new_financial_transaction_class: Yeni finansal iÅŸlem sınıfı ekle + new_bank_gateway: Yeni banka aÄŸ geçidi ekle + title: Finanslar + transaction_types: Finansal iÅŸlem türleri + supplier_categories: Tedarikçi kategorileri + new_supplier_category: Yeni tedarikçi kategorisi + transaction_types: + name: Ä°sim + new_financial_transaction_type: Yeni finansal iÅŸlem türü ekle + financial_transaction_classes: + form: + title_edit: Finansal iÅŸlem sınıfını düzenle + title_new: Yeni finansal iÅŸlem sınıfı ekle + financial_transaction_types: + form: + name_short_desc: Kısa isim, banka iÅŸlemlerinde otomatik olarak atanacak finansal iÅŸlem türleri için zorunludur. Birden fazla banka hesabı varsa, banka transferleri için tercih edilen hedef hesap seçilebilir. + title_edit: Finansal iÅŸlem türünü düzenle + title_new: Yeni finansal iÅŸlem türü ekle + mail_delivery_status: + destroy_all: + notice: Tüm e-posta problemleri silindi + index: + destroy_all: Tüm e-posta problemlerini sil + title: E-posta problemleri + ordergroups: + destroy: + error: 'SipariÅŸ grubu silindi olarak iÅŸaretlenemedi: %{error}' + notice: SipariÅŸ grubu silindi olarak iÅŸaretlendi + edit: + title: SipariÅŸ grubunu düzenle + form: + first_paragraph: Yeni üyeleri %{url} davet edebilirsiniz. + here: buradan + index: + first_paragraph: Burada %{url} grupları ekleyebilir, düzenleyebilir veya silebilirsiniz. + new_ordergroup: Yeni sipariÅŸ grubu ekle + new_ordergroups: yeni sipariÅŸ grupları + second_paragraph: 'Grup ve sipariÅŸ grubu arasındaki farkı şöyle düşünün: Bir sipariÅŸ grubu bir hesaba sahiptir ve yiyecek sipariÅŸi verebilir. Bir %{url} (örneÄŸin ''sınıflandırma grubu'') içindeki üyeler, görevler ve mesajlar yoluyla birbirleri arasında koordinasyon saÄŸlar. Kullanıcılar sadece bir sipariÅŸ grubunda olabilir, ancak birden fazla çalışma grubunda olabilirler.' + title: SipariÅŸ grupları + workgroup: çalışma grubu + new: + title: Yeni sipariÅŸ grubu oluÅŸtur + show: + confirm: Emin misiniz? + edit: Grup/Ãœyeleri Düzenle + title: SipariÅŸ grubu %{name} + search_placeholder: isim ... + users: + controller: + sudo_done: Åžimdi %{user} olarak giriÅŸ yaptınız. Dikkatli olun ve iÅŸiniz bittiÄŸinde çıkış yapmayı unutmayın! + destroy: + error: 'Kullanıcı silinemedi: %{error}' + notice: Kullanıcı silindi + edit: + title: Kullanıcıyı Düzenle + form: + create_ordergroup: Aynı adı taşıyan bir sipariÅŸ grubu oluÅŸturun ve kullanıcıyı ekleyin. + send_welcome_mail: Kullanıcıya hoÅŸ geldiniz e-postası gönderin. + index: + first_paragraph: Burada %{url}, düzenleyebilir ve silebilirsiniz. + new_user: Yeni kullanıcı oluÅŸturabilir + new_users: yeni oluÅŸtur + show_deleted: SilinmiÅŸ kullanıcıları göster + title: Kullanıcı Yönetimi + new: + title: Yeni kullanıcı oluÅŸtur + restore: + error: 'Kullanıcı geri yüklenemedi: %{error}' + notice: Kullanıcı geri yüklendi + show: + confirm_sudo: Devam ederseniz, %{user} kimliÄŸine bürüneceksiniz. Ä°ÅŸiniz bittiÄŸinde çıkış yapmayı unutmayın! + groupabos: Grup abonelikleri + member_since: Ãœye %{time} tarihinden bu yana + person: KiÅŸi + preference: Tercihler + show_email_problems: E-posta sorunlarını göster + sudo: KimliÄŸi kullanarak giriÅŸ yap + users: + show_email_problems: E-posta sorunlarını göster + workgroups: + destroy: + error: 'Çalışma grubu silinemedi: %{error}' + notice: Çalışma grubu silindi + edit: + title: Çalışma grubunu düzenle + form: + first_paragraph: Yeni üyeleri %{url} davet edebilirsiniz. + here: buradan + index: + first_paragraph: Burada %{url} oluÅŸturabilir, düzenleyebilir ve silebilirsiniz. + new_workgroup: Yeni çalışma grubu oluÅŸtur + new_workgroups: yeni çalışma grupları + ordergroup: sipariÅŸ grubu + second_paragraph: 'Çalışma grubu ve sipariÅŸ grubu arasındaki farkı dikkate alın: bir %{url} hesabı vardır ve yemek sipariÅŸi verebilir. Bir çalışma grubunda (örneÄŸin ''sınıflandırma grubu''), üyeler görevler ve mesajlar aracılığıyla birbirleri arasında koordinasyon saÄŸlarlar. Kullanıcılar yalnızca bir sipariÅŸ grubunda olabilir, ancak birden fazla çalışma grubunda olabilirler.' + title: Çalışma Grupları + new: + title: Yeni çalışma grubu oluÅŸtur + show: + confirm: Emin misiniz? + edit: Grubu/kullanıcıları düzenle + title: Çalışma Grubu %{name} + workgroups: + members: üyeler + name: isim + supplier_categories: + form: + title_new: Tedarikçi kategorisi ekle + title_edit: Tedarikçi kategorisi düzenle + application: + controller: + error_authn: Kimlik doÄŸrulama gerekiyor! + error_denied: Ä°stenen sayfayı görüntülemeye yetkiniz yok. Ä°lgili izinlere sahip olmanız gerektiÄŸini düşünüyorsanız bir yöneticiye baÅŸvurun. Birden fazla kullanıcı hesabına eriÅŸiminiz varsa, %{sign_in} deneyin. + error_denied_sign_in: baÅŸka bir kullanıcı olarak oturum açmayı + error_feature_disabled: Bu özellik ÅŸu anda devre dışı bırakılmış durumda. + error_members_only: Bu eylem, yalnızca grubun üyeleri tarafından kullanılabilir! + error_minimum_balance: Maalesef hesap bakiyeniz %{min} minimumunun altında. + error_token: EriÅŸim reddedildi (geçersiz belirteç)! + article_categories: + create: + notice: Kategori kaydedildi + destroy: + error: 'Kategori silinemiyor: %{message}' + edit: + title: Kategori düzenleme + index: + new: Yeni kategori ekle + title: Ãœrün kategorileri + new: + title: Yeni kategori ekleme + update: + notice: Kategori güncellendi + articles: + article: + last_update: 'son güncelleme: %{last_update} | brüt: %{gross_price}' + articles: + confirm_delete: Seçilen ürünleri gerçekten silmek istiyor musunuz? + option_available: Ãœrünleri uygun hale getir + option_delete: Ãœrünü sil + option_not_available: Ãœrünleri uygun deÄŸil yap + option_select: Bir eylem seçin ... + price_netto: Fiyat + unit_quantity_desc: Birim miktarı + unit_quantity_short: B.M. + controller: + create_from_upload: + notice: "%{count} yeni ürün kaydedildi." + error_invalid: Ãœrünlerde hatalar var + error_nosel: SeçilmiÅŸ bir ürün yok + error_parse: "%{msg} ... satır %{line}'da" + error_update: '%{article} ürünleri güncellerken bir hata oluÅŸtu: %{msg}' + parse_upload: + no_file: Lütfen yüklemek için bir dosya seçin. + notice: "%{count} ürün baÅŸarıyla analiz edildi." + sync: + notice: Katalog güncel + shared_alert: "%{supplier} harici bir veritabanına baÄŸlı deÄŸil" + update_all: + notice: Tüm ürünler ve fiyatlar güncellendi. + update_sel: + notice_avail: Tüm seçili ürünler uygun olarak ayarlandı. + notice_destroy: Tüm seçili ürünler silindi. + notice_noaction: Hiçbir iÅŸlem belirtilmedi! + notice_unavail: Tüm seçili ürünler uygun deÄŸil olarak ayarlandı. + update_sync: + notice: Tüm ürünler ve fiyatlar güncellendi. + destroy_active_article: + drop: sil + note: "%{article} mevcut sipariÅŸlerde kullanılıyor ve silinemez. Lütfen önce ... ürünleri sipariÅŸlerden %{drop_link} kaldırın." + edit_all: + note: 'Zorunlu alanlar: ad, birim, (net) fiyat ve sipariÅŸ numarası.' + submit: Tüm ürünleri güncelle + title: "%{supplier} tedarikçisinin tüm ürünleri düzenle" + warning: 'Uyarı: tüm ürünler güncellenecek!' + form: + title_edit: Ãœrün düzenle + title_new: Yeni ürün ekle + import_search_results: + action_import: İçe aktar + already_imported: içe aktarıldı + not_found: Ãœrün bulunamadı + index: + change_supplier: Tedarikçi deÄŸiÅŸtir ... + download: Ãœrünleri indir + edit_all: Tümünü düzenle + ext_db: + import: Ãœrünü içe aktar + sync: Senkronize et + import: + category: DoÄŸrudan kategoriye aktar + placeholder: Ad ile arama yapın ... + restrict_region: Sadece bölgeye özgü hale getir + title: Ãœrünü içe aktar + new: Yeni ürün + new_order: Yeni sipariÅŸ oluÅŸtur + search_placeholder: Ad ... + title: "%{supplier} ürünleri (%{count})" + upload: Ãœrünleri yükle + model: + error_in_use: "%{article}, bir mevcut sipariÅŸin parçası olduÄŸu için silinemiyor!" + error_nosel: Hiçbir ürün seçmediniz + parse_upload: + body: "

Lütfen ürünleri doğrulayın.

Dikkat, tekrar eden ürünler için şu anda herhangi bir kontrol yapılmıyor.

" + submit: Yüklemeyi işle + title: Ürünleri yükle + sync: + outlist: + alert_used: Uyarı, %{article} açık bir siparişte kullanılıyor. Lütfen önce siparişten kaldırın. + body: 'Aşağıdaki ürünler listeden çıkarıldı ve silinecek:' + body_ignored: + one: Sipariş numarası olmayan bir ürün atlandı. + other: "Sipariş numarası olmayan %{count} ürün atlandı." + body_skip: Silinecek ürün yok. + title: Listeden çıkar ... + price_short: Fiyat + submit: Tümünü senkronize et + title: Harici veritabanıyla ürünleri senkronize et + unit_quantity_short: Birim miktarı + update: + body: 'Her ürün iki kez gösterilir: eski değerler gri, metin alanları ise güncellenmiş değerler içerir. Eski ürünlerle farklılıklar sarı renkle işaretlenmiştir.' + title: Güncelle ... + update_msg: + one: Bir ürün güncellenmesi gerekiyor. + other: "%{count} ürün güncellenmesi gerekiyor." + upnew: + body_count: + one: Bir yeni ürün eklemek için. + other: Eklemek için %{count} ürün var. + title: Yeni ekle ... + upload: + fields: + reserved: "(Ayrılmış)" + status: Durum (x=atla) + file_label: Lütfen uyumlu bir dosya seçin + options: + convert_units: Mevcut birimleri koruyun, birim miktarını ve fiyatı yeniden hesaplayın (senkronize gibi). + outlist_absent: Yüklenen dosyada olmayan ürünleri sil. + sample: + juices: Meyve suları + nuts: Kuruyemişler + organic: Organik + supplier_1: Kuruyemişçi + supplier_2: Kahverengi tarladan + supplier_3: Yeşil tarladan + tomato_juice: Domates suyu + walnuts: Cevizler + submit: Dosyayı yükle + text_1: '%{supplier} ürünlerini güncellemek için bir elektronik tablo yükleyebilirsiniz. Excel (xls, xlsx) ve OpenOffice (ods) tabloları kabul edilir, virgülle ayrılmış dosyalar da kabul edilir (csv, utf-8 kodlamalı "; " ile ayrılmış sütunlar). Sadece ilk sayfa içe aktarılacak ve sütunlar aşağıdaki sıraya göre olmalıdır:' + text_2: Burada gösterilen satırlar örneklerdir. İlk sütunda "x" olduğunda, ürün listeden çıkarılır ve silinir. Bu, örneğin tedarikçiyle ürünler müsait olmadığında birçok ürünü hızlı bir şekilde kaldırmak için elektronik tabloyu düzenlemenize ve çıkarmanıza izin verir. Kategori, Foodsoft kategori listenize eşleştirilecek (hem kategori adı hem de içe aktarma adlarıyla). + title: "%{supplier} ürünlerini yükle" + bank_account_connector: + confirm: Lütfen %{code} kodunu onaylayın. + fields: + email: E-Posta + pin: PIN + password: Şifre + tan: TAN + username: Kullanıcı adı + config: + hints: + applepear_url: Görevler için kullanılan elma ve armut sisteminin açıklandığı web sitesi. + charge_members_manually: Üyelerin ne aldığını başka bir yerde takip ettiğinizde (örneğin kağıt üzerinde), bunu Foodsoft'a girmek istemezseniz, bunu seçin. Üye hesaplarını manuel olarak ücretlendirmeniz gerekecektir ("Yeni işlem ekle" kullanarak). Denge ekranında hala siparişleri uzlaştırmanız gerekecek, ancak üye hesaplarına ücret yansıtılmayacak. + contact: + email: Genel iletişim e-posta adresi, web sitesinde ve bazı formlarda gösterilir. + street: Adres, genellikle teslimat ve toplama noktanız olacaktır. + currency_space: Para birimi simgesinin ardından boşluk ekleyip eklemediğinizi belirtir. + currency_unit: Fiyatları görüntülemek için para birimi simgesi. + custom_css: Bu site'nin düzenini değiştirmek için, kaskatı stili (CSS) dilini kullanarak stil değişiklikleri yapabilirsiniz. Varsayılan stili kullanmak için boş bırakın. + email_from: E-postalar bu e-posta adresinden gönderilecek. Foodcoop'un iletişim adresini kullanmak için boş bırakın. + email_replyto: Foodsoft tarafından gönderilen e-postalardan farklı bir adresten yanıt almak istediğinizde bunu ayarlayın. + email_sender: E-postalar bu e-posta adresinden gönderilir. Gönderen e-posta adresinin etki alanının SPF kaydına web sunucusunun kaydedilmesi gerekebilir, böylece gönderilen e-postalar spam olarak sınıflandırılmaz. + help_url: Belgelendirme web sitesi. + homepage: Yiyecek kooperatifinizin web sitesi. + ignore_browser_locale: Kullanıcının henüz bir dil seçmediği zaman kullanıcının bilgisayarının dilini yoksayın. + minimum_balance: Üyeler, hesap bakiyelerinin bu miktarın üzerinde veya eşit olduğu durumlarda sadece sipariş verebilirler. + name: Yiyecek kooperatifinizin adı. + order_schedule: + boxfill: + recurr: Kutuları-doldurma aşamasının varsayılan başlama zamanı. + time: Siparişin başlangıç saati. + ends: + recurr: Varsayılan sipariş kapanma tarihi için plan. + time: Siparişlerin kapatılacağı varsayılan saat. + initial: Program bu tarihte başlar. + page_footer: Her sayfanın altında gösterilir. Tamamen devre dışı bırakmak için "boş" girin. + pdf_add_page_breaks: + order_by_articles: Her ürünü ayrı bir sayfada göster. + order_by_groups: Her sipariş grubunu ayrı bir sayfada göster. + pdf_font_size: PDF belgeleri için temel yazı tipi boyutu (standart 12'dir). + pdf_page_size: PDF belgeleri için sayfa boyutu, genellikle "A4" veya "zarf". + price_markup: Üyelerin toplam fiyatına eklenen yüzde. + stop_ordering_under: Üyeler sadece bu kadar elma puanı olduğunda sipariş verebilirler. + tasks_period_days: İki periyodik görev arasındaki gün sayısı (varsayılan olarak 7, yani bir hafta). + tasks_upfront_days: Periyodik görevleri kaç gün önceden planlamak istediğinize bağlı olarak değişir. + tax_default: Yeni ürünler için varsayılan KDV yüzdesi. + tolerance_is_costly: Üye toleransını maksimum dolduracak kadar sipariş edin (sadece son kutuyu dolduracak kadar ürün eklemek yerine). Açık siparişin toplam tutarına uygulanan tolerans da buna dahildir. + distribution_strategy: Bir sipariş alındıktan sonra ürünlerin nasıl dağıtılacağı. + use_apple_points: Elma puanı sistemi etkinleştirildiğinde, üyeler sipariş vermeye devam edebilmek için bazı görevleri yapmak zorundadırlar. + use_boxfill: Etkinleştirildiğinde, siparişin sonuna doğru, üyeler yalnızca toplam sipariş tutarını artırdığında siparişlerini değiştirebilirler. Bu, kalan kutuları doldurmaya yardımcı olur. Siparişler için bir kutuları-doldurma tarihi belirlemelisiniz. + use_iban: Etkinleştirildiğinde, tedarikçi ve kullanıcı uluslararası banka hesap numaralarını saklayabileceği ek bir alan sunar. + use_nick: Gerçek adlar yerine takma adları göster ve kullan. Bu seçeneği etkinleştirdiğinizde, her kullanıcının bir takma adı olup olmadığını kontrol edin. + use_self_service: Seçili dengeleme (balancing) işlevlerini üyeler kendileri kullanabilirler. + webstats_tracking_code: Web analitiği için takip kodu (Piwik veya Google analytics gibi). Takip etmek istemiyorsanız boş bırakın. + keys: + applepear_url: Elma sistemi yardım URL'si + charge_members_manually: Üyeleri manuel olarak şarj et + contact: + city: Şehir + country: Ülke + email: E-posta + phone: Telefon + street: Cadde/Sokak + zip_code: Posta kodu + currency_space: Boşluk ekle + currency_unit: Para birimi + custom_css: Özel CSS + default_locale: Varsayılan dil + default_role_article_meta: Ürünler + default_role_finance: Finans + default_role_invoices: Faturalar + default_role_orders: Siparişler + default_role_pickups: Alım günleri + default_role_suppliers: Tedarikçiler + disable_invite: Davetiyeleri devre dışı bırak + email_from: Adresinden + email_replyto: Yanıtlanacak adres + email_sender: Gönderen adresi + help_url: Belgeleme URL'si + homepage: Ana sayfa + ignore_browser_locale: Tarayıcı dilini yoksay + minimum_balance: Minimum bakiye + name: İsim + order_schedule: + boxfill: + recurr: Kutu doldurma sonrası + time: zaman + ends: + recurr: Sipariş sonu + time: zaman + initial: Program başlangıcı + page_footer: Sayfa altbilgisi + pdf_add_page_breaks: Sayfa atlamaları + pdf_font_size: Yazı tipi boyutu + pdf_page_size: Sayfa boyutu + price_markup: Foodcoop marjı + stop_ordering_under: Minimum elma puanı + tasks_period_days: Dönem + tasks_upfront_days: Önceden oluştur + tax_default: Varsayılan KDV + time_zone: Zaman dilimi + tolerance_is_costly: Tolerans maliyetlidir + distribution_strategy: Dağıtım stratejisi + distribution_strategy_options: + first_order_first_serve: İlk sipariş edenlere öncelik verin + no_automatic_distribution: Otomatik dağıtım yok + use_apple_points: Elma puanları kullan + use_boxfill: Kutuları-doldurma aşamasını kullan + use_iban: IBAN kullan + use_nick: Takma ad kullan + use_self_service: Kendi kendine (self service) hizmet kullan + webstats_tracking_code: Takip kodu + tabs: + applications: Uygulamalar + foodcoop: Foodcoop + language: Dil + layout: Düzen + list: Liste + messages: Mesajlar + others: Diğerleri + payment: Finans + security: Güvenlik + tasks: Görevler + deliveries: + add_stock_change: + how_many_units: 'Kaç birim (%{unit}) teslim edilecek? Stoğun adı: %{name}.' + create: + notice: Teslimat oluşturuldu. Lütfen fatura eklemeyi unutmayın! + destroy: + notice: Teslimat silindi. + edit: + title: Teslimatı düzenle + form: + confirm_foreign_supplier_reedit: Stok ürünü %{name} başarıyla kaydedildi. Ancak, bu teslimatın tedarikçisinden farklı bir tedarikçiye ait. Stok ürününü tekrar düzenlemek ister misiniz? + create_from_blank: Yeni ürün oluştur + create_stock_article: Stok ürünü oluştur + title_fill_quantities: 2. Teslimat miktarlarını belirle + title_finish_delivery: 3. Teslimatı tamamla + title_select_stock_articles: 1. Stok ürünlerini seç + index: + confirm_delete: Silmek istediğinizden emin misiniz? + new_delivery: '%{supplier} için yeni teslimat oluştur ' + title: "%{supplier}/teslimatlar" + invoice_amount: Fatura tutarı + invoice_net_amount: Fatura net tutarı + new: + title: "%{supplier} için yeni teslimat" + show: + sum: Toplam + sum_diff: Brüt - fatura tutarı + sum_gross: Brüt toplam + sum_net: Net toplam + title: Teslimatı göster + title_articles: Ürünler + stock_article_for_adding: + action_add_to_delivery: Teslimata ekle + action_edit: Düzenle + action_other_price: Kopyala + stock_change_fields: + remove_article: Teslimattan çıkar + suppliers_overview: Tedarikçi genel bakış + update: + notice: Teslimat güncellendi. + documents: + order_by_articles: + filename: Sipariş %{name}-%{date} - ürünlere göre + title: '%{name} için ürünlere göre sıralanmış sipariş, %{date} tarihinde kapanmıştır' + order_by_groups: + filename: Sipariş %{name}-%{date} - gruba göre + sum: Toplam + title: '%{name} için gruba göre sıralanmış sipariş, %{date} tarihinde kapanmıştır' + order_fax: + filename: Sipariş %{name}-%{date} - Faks + rows: + - Sipariş Numarası + - Miktar + - Ad + - Birim miktarı + - Birim + - Birim fiyatı + - Ara toplam + total: Toplam + order_matrix: + filename: Sipariş %{name}-%{date} - sıralama matrisi + heading: Ürün genel bakışı (%{count}) + title: '%{date} tarihinde kapatılan %{name} sipariş sıralama matrisi' + errors: + general: Bir problem oluştu. + general_again: Bir problem oluştu. Lütfen tekrar deneyin. + general_msg: 'Bir problem oluştu: %{msg}' + internal_server_error: + text1: Beklenmeyen bir hata oluştu. Özür dileriz! + text2: Bildirildi. Eğer sorun devam ederse, bize bildirin lütfen. + title: Dahili sunucu hatası + not_found: + text: Bu sayfa mevcut değil, üzgünüz! + title: Sayfa bulunamadı + feedback: + create: + notice: Geri bildiriminiz başarıyla gönderildi. Teşekkür ederiz! + new: + first_paragraph: Bir hata buldunuz mu? Önerileriniz mi var? Fikirleriniz mi? Geri bildirimlerinizi duymaktan mutluluk duyarız. + second_paragraph: Lütfen unutmayın, Foodsoft ekibi yalnızca yazılımın bakımından sorumludur. Foodcoop'unuzun organizasyonuyla ilgili sorularınız için uygun kişiye başvurmanız gerekmektedir. + send: Gönder + title: Geri Bildirim Ver + finance: + balancing: + close: + alert: 'Muhasebe yapılırken bir hata oluştu: %{message}' + notice: Sipariş başarıyla tamamlandı, hesap bakiyesi güncellendi. + close_all_direct_with_invoice: + notice: '%{count} sipariş tamamlandı.' + close_direct: + alert: 'Sipariş tamamlanamadı: %{message}' + notice: Sipariş tamamlandı. + confirm: + clear: Hesapla + first_paragraph: 'Sipariş tamamlandığında, tüm grup hesapları güncellenecektir.
Hesaplar şu şekilde tahsil edilecektir:' + or_cancel: ya da muhasebeye geri dön + title: Siparişi tamamla + edit_note: + title: Sipariş notunu düzenle + edit_results_by_articles: + add_article: Ürün ekle + amount: Miktar + edit_transport: Taşıma masraflarını düzenle + gross: Brüt + net: Net + edit_transport: + title: Taşıma maliyetlerini dağıt + group_order_articles: + add_group: Grup ekle + total: Toplam maliyet + total_fc: Toplam (FC fiyat) + units: Birimler + index: + close_all_direct_with_invoice: Hepsini faturayla kapat + title: Kapatılan siparişler + invoice: + edit: Faturayı düzenle + invoice_amount: 'Fatura tutarı:' + invoice_date: 'Fatura tarihi:' + invoice_number: 'Fatura numarası:' + minus_refund_calculated: "- Tahsil edilen depozito:" + new: Yeni fatura oluştur + new_body: 'Bu sipariş için bir fatura oluştur:' + plus_refund_credited: "+ İade edilen depozito:" + refund_adjusted_amount: 'iade için düzeltilen tutar:' + new: + alert: Dikkat, sipariş zaten hesaba katılmış + articles_overview: Ürünlere genel bakışı + close_direct: Ödemeyi atla + close_direct_confirm: Üye hesaplarını ücretlendirmeden siparişi tamamla. Üye hesaplarını zaten manuel olarak borçlandırdıysanız veya gerçekten ne yaptığınızı biliyorsanız bunu yapın. + comment_on_transaction: Muhasebenize bir yorum ekleyebilirsiniz. + comments: Yorumlar + confirm_order: Siparişi tamamla + create_invoice: Fatura ekle + edit_note: Notu düzenle + edit_order: Siparişi düzenle + groups_overview: Gruplara genel bakış + invoice: Fatura + notes_and_journal: Sipariş Notları + summary: Özet + title: Hesap defteri %{name} + view_options: Görüntüleme seçenekleri + order_article: + confirm: Emin misiniz? + orders: + clear: Hesapla + cleared: Hesaplandı (%{amount}) + end: Son + ended: Kapatıldı + name: Tedarikçi + no_closed_orders: Şu anda kapatılmış bir sipariş yok. + state: Durum + summary: + changed: Veri değiştirildi! + duration: "%{starts} ile %{ends} arası" + fc_amount: 'Satış değeri:' + fc_profit: Foodcoop'tan artan (surplus) + gross_amount: 'Brüt değer:' + groups_amount: 'Sipariş grupları toplamı:' + net_amount: 'Net değer:' + reload: Özeti yeniden yükle + with_extra_charge: 'ek ücretle birlikte:' + without_extra_charge: 'ek ücretsiz:' + bank_accounts: + assign_unlinked_transactions: + notice: '%{count} işlem atanmıştır' + import: + notice: '%{count} yeni işlem içe aktarıldı' + no_import_method: Bu banka hesabı için içe aktarma yöntemi yapılandırılmamıştır. + submit: İçe aktar + title: '%{name} için banka işlemlerini içe aktar' + index: + title: Banka Hesapları + bank_transactions: + index: + assign_unlinked_transactions: İşlemleri Ata + import_transactions: İçe Aktar + title: '%{name} için Banka İşlemleri (%{balance})' + show: + add_financial_link: Finansal bağlantı ekle + belongs_to_supplier: tedarikçiye ait + belongs_to_user: kullanıcıya ait + in_ordergroup: sipariş grubunda + transactions: + add_financial_link: Bağlantı ekle + create: + notice: Fatura oluşturuldu. + financial_links: + add_bank_transaction: + notice: Bağlantı banka işlemine eklendi. + add_financial_transaction: + notice: Bağlantı finansal işleme eklendi. + add_invoice: + notice: Bağlantı faturaya eklendi. + create: + notice: Yeni finansal bağlantı oluşturuldu. + create_financial_transaction: + notice: Finansal işlem eklendi. + index_bank_transaction: + title: Banka işlemi ekle + index_financial_transaction: + title: Finansal işlem ekle + index_invoice: + title: Fatura ekle + new_financial_transaction: + title: Finansal işlem ekle + remove_bank_transaction: + notice: Bağlantı banka işleminden kaldırıldı. + remove_financial_transaction: + notice: Bağlantı finansal işlemden kaldırıldı. + remove_invoice: + notice: Bağlantı faturadan kaldırıldı. + show: + add_bank_transaction: Banka işlemi ekle + add_financial_transaction: Finansal işlem ekle + add_invoice: Fatura ekle + amount: Miktar + date: Tarih + description: Açıklama + new_financial_transaction: Yeni finansal işlem + title: Finansal bağlantı %{number} + type: Tip + financial_transactions: + controller: + create: + notice: İşlem kaydedildi. + create_collection: + alert: 'Bir hata oluştu: %{error}' + error_note_required: Not doldurulması gereklidir! + notice: Tüm işlemler kaydedildi. + destroy: + notice: İşlem kaldırıldı. + index: + balance: '%{balance} hesap bakiyesi' + last_updated_at: "(son güncelleme %{when} tarihinden önce)" + new_transaction: Yeni işlem oluştur + title: '%{name} için hesap özeti' + index_collection: + show_groups: Hesapları yönet + title: Finansal işlemler + new: + paragraph: Burada, %{name} için para yatırabilir veya çekebilirsiniz. + paragraph_foodcoop: Burada, gıda kooperatifi için para yatırabilir veya çekebilirsiniz. + title: Yeni işlem + new_collection: + add_all_ordergroups: Tüm sipariş gruplarını ekle + add_all_ordergroups_custom_field: '%{label} etiketi ile tüm sipariş gruplarını ekle' + create_financial_link: Yeni işlemler için ortak finansal bağlantı oluşturun. + create_foodcoop_transaction: Gıda kooperatifi için ters toplam tutarlı bir işlem oluşturun ("çift taraflı muhasebe" durumunda) + new_ordergroup: Yeni sipariş grubu ekle + save: İşlemi kaydet + set_balance: Sipariş grubunun bakiyesini girilen tutara ayarlayın. + sidebar: Burada aynı anda birden fazla hesabı güncelleyebilirsiniz. Örneğin, bir hesap özetinden tüm sipariş grubu transferlerini. + title: Birden fazla hesap güncelleme + ordergroup: + remove: Kaldır + remove_group: Grubu kaldır + transactions: + confirm_revert: '%{name} işlemi geri almak istediğinizden emin misiniz? Bu durumda, tersine çevrilen bir miktarla yeni bir işlem oluşturulacak ve orijinal işlemle birleştirilecektir. Bu gizli işlemler, "Gizli işlemleri göster" seçeneği aracılığıyla sadece görüntülenebilir ve normal kullanıcılara hiç görünmez.' + revert_title: Normal kullanıcılardan gizleyecek şekilde işlemi geri alın. + transactions_search: + show_hidden: Gizli işlemleri göster + index: + amount_fc: Tutar(FC) + end: Son + everything_cleared: Harika, her şey hesaplandı... + last_transactions: Son işlemler + open_transactions: Tamamlanmamış siparişler + show_all: tümünü göster + title: Finanslar + unpaid_invoices: Ödenmemiş faturalar + invoices: + edit: + title: Faturayı düzenle + form: + attachment_hint: Sadece JPEG ve PDF dosyaları kabul edilir. + index: + action_new: Yeni fatura oluştur + show_unpaid: Ödenmemiş faturaları göster + title: Faturalar + new: + title: Yeni fatura oluştur + show: + title: Fatura %{number} + unpaid: + invoices_sum: Toplam tutar + invoices_text: Referans + title: Ödenmemiş faturalar + ordergroups: + index: + new_financial_link: Yeni finansal bağlantı ekle + new_transaction: Yeni işlem ekle + show_all: Tüm işlemler + show_foodcoop: Gıda kooperatifi işlemleri + title: Hesapları yönet + ordergroups: + account_statement: Hesap özeti + new_transaction: Yeni işlem + update: + notice: Fatura güncellendi + foodcoop: + ordergroups: + index: + name: İsim ... + only_active: Sadece aktif gruplar + only_active_desc: "(son 3 ayda en az bir kez sipariş vermiş olanlar)" + title: Sipariş Grupları + ordergroups: + break: "%{start} - %{end}" + users: + index: + body: "

Burada Gıda Kooperatifinizin üyelerine bir mesaj yazabilirsiniz. Diğer üyelerin sizinle iletişim kurmasını isterseniz, bunu %{profile_link} bölümünden etkinleştirin.

" + ph_name: İsim ... + ph_ordergroup: Sipariş grubu ... + profile_link: seçenekler + title: Kullanıcılar + workgroups: + edit: + invite_link: burada + invite_new: Yeni üyeleri %{invite_link} davet edebilirsiniz. + title: Grubu Düzenle + index: + body: "

Bir grubu düzenlemek yalnızca grubun üyeleri tarafından yapılabilir.
Bir gruba katılmak istiyorsanız, lütfen üyelere bir mesaj gönderin.

" + title: Çalışma Grupları + workgroup: + edit: Grubu Düzenle + show_tasks: Tüm Görevleri Göster + group_order_articles: + form: + amount_change_for: '%{article} için miktarı değiştirin' + result_hint: 'Birim: %{unit}' + group_orders: + archive: + desc: Tüm %{link} burada görüntüleyebilirsiniz. + open_orders: Mevcut siparişler + title: '%{group} Siparişleri' + title_closed: Hesaplandı + title_open: Hesaplanmadı/Kapatılmadı + create: + error_general: Sipariş hatası nedeniyle güncellenemedi. + error_stale: Başkası sipariş vermiş olabilir, sipariş güncellenemedi. + notice: Sipariş kaydedildi. + errors: + closed: Bu sipariş zaten kapatıldı. + no_member: Bir sipariş grubunun üyesi değilsiniz. + notfound: Yanlış URL, bu sizin siparişiniz değil. + form: + action_save: Siparişi kaydet + new_funds: Yeni hesap bakiyesi + price: Fiyat + reset_article_search: Arama sıfırla + search_article: Ürün ara... + sum_amount: Mevcut miktar + title: Siparişler + total_sum_amount: Toplam miktar + total_tolerance: Toplam tolerans + units: Birimler + units_full: Dolu birimler + units_total: Toplam birimler + index: + closed_orders: + more: daha fazla... + title: Kapanmış siparişler + finished_orders: + title: Unsettled orders + total_sum: Total sum + funds: + finished_orders: Tamamlanmamış siparişler + open_orders: Mevcut siparişler + title: Kredi + title: Siparişler genel bakışı + messages: + not_enough_apples: Sipariş vermek için en az %{stop_ordering_under} elma puanınız olmalıdır. Şu anda sipariş grubunuzda sadece %{apples} elma puanı var. + order: + title: Ürünler + show: + articles: + edit_order: Siparişi Düzenle + not_ordered_msg: Henüz sipariş vermediniz. + order_closed_msg: Maalesef, bu sipariş kapandı. + order_nopen_title: Tüm grupların güncel siparişleri göz önüne alındığında + order_not_open: Alındı + order_now: İşte şansın! + order_open: Mevcut + ordered: Sipariş edildi + ordered_title: Tutar + tolerans + show_hide: Sipariş edilmemiş ürünleri göster/gizle + show_note: Notu göster + title: Ürün genel bakışı + unit_price: Birim fiyatı + comment: Yorum + comments: + title: Yorumlar + not_ordered: Sipariş vermediniz. + sum: Toplam + title: '%{order} için sipariş sonucunuz' + switch_order: + remaining: "%{remaining} kaldı" + title: Mevcut siparişler + update: + error_general: Sipariş bir hatadan dolayı güncellenemedi. + error_stale: Bu sırada başka birisi sipariş vermiş, sipariş güncellenemedi. + notice: Sipariş kaydedildi. + helpers: + application: + edit_user: Kullanıcıyı düzenle + nick_fallback: "(kullanıcı adı yok)" + role_admin: Yönetici + role_article_meta: Ürünler + role_finance: Finans + role_invoices: Faturalar + role_orders: Siparişler + role_pickups: Teslimat günleri + role_suppliers: Tedarikçiler + show_google_maps: Google Haritalarda göster + sort_by: 'Şuna göre sırala: %{text}' + deliveries: + new_invoice: Yeni fatura + show_invoice: Faturayı göster + orders: + old_price: Eski fiyat + option_choose: Tedarikçi/Depo seçin + option_stock: Depo + order_pdf: PDF oluştur + submit: + invite: + create: davetiye gönder + tasks: + required_users: "%{count} üye daha gerekiyor!" + task_title: "%{name} (%{duration} saat)" + home: + apple_bar: + desc: 'Bu, sipariş grubunuzdaki tamamlanan görevlerin sipariş hacmi ile Foodcoop ortalaması arasındaki oranını gösterir. Uygulamada: Her %{amount} toplam sipariş için bir görev yapmalısınız!' + more_info: Daha fazla bilgi + points: 'Mevcut elma puanınız: %{points}' + warning: Uyarı, elma puanınız %{threshold} değerinden azsa, sipariş vermenize izin verilmez! + changes_saved: Değişiklikler kaydedildi. + index: + due_date_format: "%A %d %B" + my_ordergroup: + last_update: Son güncelleme %{when} tarihinden önce yapıldı + title: Benim sipariş grubum + transactions: + title: Son işlemler + view: Hesap özetini göster + ordergroup: + title: Sipariş grubunun katılımı + tasks_move: + action: Görevleri üstlen / görevleri reddet + desc: Bu görevlerden siz sorumlusunuz. + title: Görevleri üstlen + tasks_open: + title: Açık görevler + view_all: Tüm görevleri göster + title: Ana Sayfa + your_tasks: Görevleriniz + no_ordergroups: Maalesef bir sipariş grubu üyesi değilsiniz. + ordergroup: + account_summary: Hesap özeti + invite: Yeni kişi davet et + search: Ara ... + title: Benim sipariş grubum + ordergroup_cancelled: '%{group} grubundaki üyeliğinizi iptal ettiniz.' + profile: + groups: + cancel: Grubu terk et + cancel_confirm: Bu grubu terk etmek istediğinizden emin misiniz? + invite: Yeni üye davet et + title: Grup üyeliğiniz + title: Profilim + user: + since: "(%{when} üyesi)" + title: "%{user}" + reference_calculator: + transaction_types_headline: Amaç + placeholder: Bu işlem için kullanmanız gereken referansı görmek için önce lütfen her alan için aktarmak istediğiniz miktarları girin. + text0: Lütfen şu miktarı transfer edin + text1: referans numarası ile birlikte + text2: şu banka hesabına + title: Referans Hesaplayıcı + start_nav: + admin: Yönetim + finances: + accounts: Hesapları güncelle + settle: Hesap siparişleri + title: Finanslar + foodcoop: Gıda kooperatifi + members: Üyeler + new_ordergroup: Yeni sipariş grubu + new_user: Yeni üye + orders: + end: Siparişleri kapat + overview: Sipariş özeti + title: Siparişler + products: + edit: Ürünleri güncelle + edit_stock: Stokları güncelle + edit_suppliers: Tedarikçileri güncelle + title: Ürünler + tasks: Görevlerim + title: Direkt olarak... + invites: + errors: + already_member: kullanımda. Kişi zaten bu Foodcoop'un üyesi. + modal_form: + body: "

Burada, Foodcoop'un üyesi olmayan bir kişiyi <%{group}> gruplarına davet edebilirsiniz. Davet kabul edildikten sonra, kişi siparişinize ürün ekleyebilecek (ve kaldırabilecek).

Bu, birini foodcoop'a tanıtmak veya aynı evde birden fazla kişiyle sipariş vermeye yardımcı olmak için harika bir yoldur.

" + title: Kişi davet et + new: + action: Davet gönder + body: "

Burada, henüz Foodcoop üyesi olmayan bir kişiyi <%{group}> grubuna ekleyebilirsiniz.

" + success: Kullanıcı başarıyla davet edildi. + js: + ordering: + confirm_change: Bu siparişe yapılan değişiklikler kaybolacak. Değişikliklerinizi kaybetmek ve devam etmek istiyor musunuz? + trix_editor: + file_size_alert: Dosya eki çok büyük! Maksimum boyut 512Mb + layouts: + email: + footer_1_separator: "--" + footer_2_foodsoft: 'Foodsoft: %{url}' + footer_3_homepage: 'Foodcoop: %{url}' + footer_4_help: 'Yardım: %{url}' + foodsoft: Foodsoft + footer: + revision: revizyon %{revision} + header: + feedback: + desc: Bir hata mı buldunuz? Öneriler? Fikirler? İnceleme? + title: Geri bildirim + help: Yardım + logout: Çıkış yap + ordergroup: Benim sipariş grubum + profile: Profili düzenle + reference_calculator: Referans Hesaplayıcı + logo: "foodsoft" + lib: + render_pdf: + page: "%{count} sayfasının %{number}. sayfası" + login: + accept_invitation: + body: "

%{foodcoop} gıda kooperatifinin %{group} grubunun bir üyesi olarak davet edildiniz.

Katılmak isterseniz, lütfen bu formu doldurun.

Doğal olarak, kişisel bilgileriniz herhangi bir nedenle üçüncü taraflarla paylaşılmayacaktır. Tüm'ü, tüm Gıda Kooperatifleri üyeleri için görünür olacak şekilde kişisel bilgilerinizin ne kadarının görünür olacağını siz belirleyebilirsiniz. Lütfen not edin ki, yöneticiler bilgilerinize erişebilirler.

" + submit: Bir Foodsoft hesabı oluşturun + title: "%{name} için davet" + controller: + accept_invitation: + notice: Tebrikler, hesabınız başarıyla oluşturuldu. Şimdi giriş yapabilirsiniz. + error_group_invalid: Davet edildiğiniz grup artık mevcut değil. + error_invite_invalid: Davetiniz geçersiz (artık geçerli değil). + error_token_invalid: Geçersiz veya süresi dolmuş belirteç (token). Lütfen tekrar deneyin. + reset_password: + notice: Eğer e-postanız kayıtlıysa, şifrenizi sıfırlamak için bir bağlantı içeren bir mesaj alacaksınız. Spam klasörünüzü kontrol etmeniz gerekebilir. + update_password: + notice: Şifreniz güncellendi. Artık giriş yapabilirsiniz. + forgot_password: + body: "

Sorun değil, yeni bir şifre seçebilirsiniz.

Lütfen burada kayıtlı olan e-posta adresinizi girin. Daha fazla talimat için bir e-posta alacaksınız.

" + submit: Yeni ÅŸifre iste + title: Åžifremi unuttum? + new_password: + body: "

%{user} için yeni şifreyi girin.

" + submit: Yeni şifreyi kaydet + title: Yeni şifre + mailer: + dateformat: "%d %b" + feedback: + header: "%{user} tarafından %{date} tarihinde yazıldı:" + subject: Foodsoft için geri bildirim + from_via_foodsoft: "%{name} Foodsoft aracılığıyla" + invite: + subject: Foodcoop Davetiyesi + text: | + Merhaba! + + %{user} <%{mail}> seni "%{group}" grubuna katılmaya davet etti. + Davetiye kabul etmek ve foodcoop'a katılmak için lütfen bu bağlantıyı takip et: %{link} + Bu bağlantı sadece bir kez kullanılabilir ve %{expires} tarihinde süresi dolacaktır. + + + Sevgiler, Foodsoft Ekibi! + negative_balance: + subject: Negatif hesap bakiyesi + text: | + Sayın %{group}, + + Hesap bakiyeniz %{when} tarihinde yapılan %{amount} TL'lik işlem nedeniyle sıfırın altına düştü: "%{balance}" + + "%{user}" tarafından "%{note}" için %{amount} ücret alındı. + + Lütfen mümkün olan en kısa sürede hesabınıza para yatırınız. + + + + %{foodcoop} adına saygılar. + not_enough_users_assigned: + subject: '"%{task}" için hala kişilere ihtiyaç var!' + text: | + Sevgili %{user}, + + Çalışma grubunun '%{task}' görevi %{when} tarihinde tamamlanacak + ve daha fazla katılımcıya ihtiyaç duyuyor! + + Eğer henüz bu göreve atanmadıysanız, şimdi fırsatınız var: + + %{workgroup_tasks_url} + + Görevleriniz: %{user_tasks_url} + order_result: + subject: '%{name} siparişi kapatıldı' + text0: | + Sevgili %{ordergroup}, + + "%{order}" siparişi %{user} tarafından %{when} tarihinde kapatıldı. + text1: | + Tahmini olarak %{pickup} tarihinde teslim edilebilir. + text2: | + Sipariş grubunuz için aşağıdaki ürünler sipariş edildi: + text3: |- + o Toplam tutar: %{sum} + + Siparişi çevrimiçi olarak görüntüleyebilirsiniz: %{order_url} + + + %{foodcoop} adına sevgiler. + order_received: + subject: '%{name} için sipariş teslimi kaydedildi' + text0: | + Sevgili %{ordergroup}, + + "%{order}" için sipariş teslimi kaydedilmiştir. + abundant_articles: Fazla alındı + scarce_articles: Az alındı + article_details: | + o %{name}: + -- Sipariş edilen: %{ordered} x %{unit} + -- Alınan: %{received} x %{unit} + order_result_supplier: + subject: '%{name} için yeni sipariş' + text: | + Merhaba! + + %{foodcoop} Foodcoop'u sipariş vermek istiyor. + + Lütfen ekli PDF ve hesap tablosunu inceleyiniz. + + Saygılarımızla, + %{user} + %{foodcoop} + reset_password: + subject: '%{username} için yeni şifre' + text: | + Merhaba %{user}, + + Yeni bir şifre istediniz (veya başka birisi istedi). + Yeni bir şifre belirlemek için bu linke tıklayın: %{link} + Bu link sadece bir kez kullanılabilir ve %{expires} tarihinde geçersiz olacaktır. + Eğer şifrenizi değiştirmek istemiyorsanız, bu mesajı görmezden gelebilirsiniz. Şifreniz henüz değiştirilmedi. + + + Saygılarımızla, Foodsoft Ekibi! + upcoming_tasks: + nextweek: 'Gelecek hafta için görevler:' + subject: Görevler teslim edilmeli! + text0: | + Sayın %{user}, + + %{task} görevi size atanmıştır. Bu görev yarın (%{when}) teslim edilmelidir! + text1: | + Görevlerim: %{user_tasks_url} + + + %{foodcoop} adına saygılarımızla. + welcome: + subject: "%{foodcoop} Hoş Geldiniz" + text0: | + Sayın %{user}, + + %{foodcoop} için yeni bir Foodsoft hesabı oluşturuldu. + text1: | + Yeni bir şifre belirlemek için lütfen şu bağlantıyı takip edin: %{link} + Bu bağlantı sadece bir kez kullanılabilir ve %{expires} tarihinde geçerliliğini yitirir. + Her zaman "Şifrenizi mi unuttunuz?" seçeneğini kullanarak yeni bir bağlantı alabilirsiniz. + + + %{foodcoop} adına saygılarımızla. + messages_mailer: + foodsoft_message: + footer: | + Yanıtla: %{reply_url} + Mesajı çevrimiçi görüntüle: %{msg_url} + Mesaj seçenekleri: %{profile_url} + footer_group: | + Gruba gönderildi: %{group} + model: + delivery: + each_stock_article_must_be_unique: Her stok ürünü bir kez listelenmeli. + financial_transaction: + foodcoop_name: Gıda kooperatifi + financial_transaction_type: + no_delete_last: En az bir finansal işlem türü bulunmalıdır. + group_order: + stock_ordergroup_name: Stok (%{user}) + invoice: + invalid_mime: geçersiz bir MIME türüne sahip (%{mime}) + membership: + no_admin_delete: Son kalan yönetici olduğunuz için üyelikten çıkılamaz. + order_article: + error_price: belirtilmeli ve geçerli bir fiyata sahip olmalıdır + user: + no_ordergroup: sıfır sipariş grubu + group_order_article: + order_closed: Sipariş kapatıldı ve değiştirilemez. + navigation: + admin: + config: Konfigürasyon + finance: Finans + home: Genel Bakış + mail_delivery_status: E-posta sorunları + ordergroups: Sipariş Grupları + title: Yönetim + users: Kullanıcılar + workgroups: Çalışma Grupları + articles: + categories: Kategoriler + stock: Stok + suppliers: Tedarikçiler/ürünler + title: Ürünler + dashboard: Kontrol Paneli + finances: + accounts: Hesapları Yönet + balancing: Hesap siparişleri + bank_accounts: Banka Hesapları + home: Genel Bakış + invoices: Faturalar + title: Finanslar + foodcoop: Gıda Kooperatifi + members: Üyeler + ordergroups: Sipariş Grupları + orders: + archive: Benim Siparişlerim + manage: Siparişleri Yönet + ordering: Sipariş Ver! + pickups: Teslim Günleri + title: Siparişler + tasks: Görevler + workgroups: Çalışma Grupları + number: + percentage: + format: + strip_insignificant_zeros: true + order_articles: + edit: + stock_alert: Stok ürünlerinin fiyatı değiştirilemez! + title: Ürünü güncelle + new: + title: Teslim edilen ürünü siparişe ekle + ordergroups: + edit: + title: Sipariş gruplarını düzenle + index: + title: Sipariş grupları + model: + error_single_group: "%{user}, başka bir sipariş grubunun üyesidir" + invalid_balance: geçerli bir sayı değil + orders: + articles: + article_count: 'Sipariş edilen ürünler:' + prices: Net/brüt fiyatı + prices_sum: 'Toplam (net/brüt fiyat):' + units_full: Tam birimler + units_ordered: Sipariş edilen birimler + create: + notice: Sipariş oluşturuldu. + edit: + title: 'Siparişi düzenle: %{name}' + edit_amount: + field_locked_title: Bu ürünün sipariş grupları arasındaki dağılımı manuel olarak değiştirildi. Bu alan, bu değişiklikleri korumak için kilitlidir. Yeni bir dağılım yapmak ve bu değişiklikleri üzerine yazmak için kilidi açın ve miktarı değiştirin. + field_unlocked_title: Bu ürünün sipariş grupları arasındaki dağılımı manuel olarak değiştirildi. Miktarı değiştirirken, bu manuel değişiklikler üzerine yazılacaktır. + edit_amounts: + no_articles_available: Eklenecek ürün yok. + set_all_to_zero: Tümünü sıfıra ayarla + fax: + amount: Miktar + articles: Ürünler + delivery_day: Teslim günü + heading: "%{name} için sipariş" + name: İsim + number: Numara + to_address: Gönderim adresi + finish: + notice: Sipariş kapatıldı. + form: + ignore_warnings: Uyarıları yok say + prices: Fiyatlar (net/FC) + select_all: Hepsini seç + stockit: Stokta + title: Ürün + index: + action_end: Kapat + action_receive: Teslim al + confirm_delete: Siparişi gerçekten silmek istiyor musunuz? + confirm_end: Siparişi gerçekten kapatmak istiyor musunuz %{order}? Geri dönüş yok. + new_order: Yeni sipariş oluştur + no_open_or_finished_orders: Şu anda açık veya kapalı sipariş yok. + orders_finished: Kapatıldı + orders_open: Açık + orders_settled: Düzenlendi + title: Siparişleri yönet + model: + close_direct_message: Üye hesaplarına ücret yansıtılmadan sipariş kapatıldı. + error_boxfill_before_ends: Kutu doldurma tarihi son tarihten önce olmalıdır (veya boş bırakılmalıdır). + error_closed: Sipariş zaten kapatılmış + error_nosel: En az bir ürün seçilmelidir. Ya da belki siparişi silmek istiyor olabilirsiniz? + error_starts_before_boxfill: Başlangıç tarihi kutu doldurma tarihinden sonra olmalıdır (veya boş bırakılmalıdır). + error_starts_before_ends: Başlangıç tarihi bitiş tarihinden sonra olmalıdır (veya boş bırakılmalıdır). + notice_close: '%{name} siparişi, %{ends} kadar.' + stock: Stok + warning_ordered: 'Uyarı: Kırmızı olarak işaretlenen ürünler bu açık siparişte zaten sipariş edildi. Burada işaretini kaldırırsanız, tüm bu ürünlerin mevcut siparişleri silinecektir. Devam etmek için aşağıdaki onaylayın.' + warning_ordered_stock: 'Uyarı: Kırmızı olarak işaretlenen ürünler bu açık stok siparişinde zaten sipariş edildi/satın alındı. Burada seçimleri kaldırırsanız, tüm bu ürünlerin mevcut siparişleri/satı nalımları silinecek ve bunlar hesaba katılmayacaktır. Devam etmek için aşağıda onay verin.' + new: + title: Yeni sipariş oluştur + receive: + add_article: Ürün ekle + consider_member_tolerance: toleransı dikkate al + notice: '%{msg} siparişi alındı.' + notice_none: Teslim alınacak yeni bir ürün yok + paragraph: Sipariş edilen ve alınan miktar aynıysa, ilgili alanlar boş bırakılabilir. Yine de tüm alanların girilmesi iyi olur, çünkü bu, tüm ürünlerin kontrol edildiğinin anlaşılmasını sağlar. + rest_to_stock: stokta kalanlar + submit: Siparişi Al + surplus_options: 'Dağıtım Seçenekleri:' + title: "%{order} Siparişini Al" + send_to_supplier: + notice: Sipariş tedarikçiye gönderildi. + show: + action_end: Kapat! + amounts: 'Net/Brüt toplam:' + articles: Ürün özeti + articles_ordered: 'Sipariş edilen üründür:' + comments: + title: Yorumlar + comments_link: Yorumlar + confirm_delete: Siparişi gerçekten silmek istiyor musunuz? + confirm_end: |- + Siparişi gerçekten kapatmak istiyor musunuz %{order}? + Geri dönüşü yok. + confirm_send_to_supplier: Sipariş %{when} tarihinde zaten tedarikçiye gönderildi. Yeniden göndermek istiyor musunuz? + create_invoice: Fatura Ekle + description1_order: "%{who} tarafından açılan %{supplier} siparişi, %{state}," + description1_period: + pickup: alınabileceği tarih %{pickup} + starts: '%{starts} tarihinden itibaren açık' + starts_ends: '%{starts} tarihinden %{ends} tarihine kadar açık' + description2: "%{ordergroups} %{article_count} adet ürün sipariş verdi, toplam değeri %{net_sum} / %{gross_sum} (net / brüt)." + group_orders: 'Grup siparişleri:' + search_placeholder: + articles: Ürün ara... + default: Arama yap... + groups: Sipariş grupları ara... + search_reset: Aramayı sıfırla + send_to_supplier: Tedarikçiye gönder + show_invoice: Faturayı göster + sort_article: Ürüne göre sırala + sort_group: Gruba göre sırala + stock_order: Stok Siparişi + title: '%{name} Siparişi' + warn_not_closed: Uyarı, sipariş henüz kapatılmadı. + state: + closed: kapatıldı + finished: tamamlandı + open: açık + received: alındı + update: + notice: Sipariş güncellendi. + update_order_amounts: + msg1: "%{count} adet (%{units} birim) güncellendi" + msg2: "%{count} (%{units}) tolerans kullanılarak güncellendi" + msg4: "%{count} (%{units}) fazla kaldı" + pickups: + document: + empty_selection: En az bir sipariş seçmelisiniz. + filename: "%{date} Teslimatı" + invalid_document: Geçersiz belge türü + title: "%{date} Teslimatı" + index: + article_pdf: Ürün PDF'i + group_pdf: Grup PDF'i + matrix_pdf: Matris PDF'i + title: Teslimat günleri + sessions: + logged_in: Giriş yapıldı! + logged_out: Çıkış yapıldı! + login_invalid_email: Geçersiz e-posta adresi veya şifre + login_invalid_nick: Geçersiz kullanıcı adı veya şifre + new: + forgot_password: Şifremi unuttum? + login: Giriş Yap + nojs: Dikkat, çerezlerin ve javascript'in etkinleştirilmesi gerekiyor! Lütfen %{link} kapatın. + noscript: NoScript + title: Foodsoft Girişi + shared: + articles: + ordered: Sipariş edilen + ordered_desc: Üye tarafından sipariş edilen ürün sayısı (miktar + tolerans) + received: Alınan + received_desc: Üye tarafından alınan ürün sayısı + articles_by: + price: Toplam fiyat + price_sum: Toplam + group: + access: Erişim + activated: aktifleştirildi + apple_limit: Elma puanı sipariş sınırı + break: "%{start} - %{end} arası" + deactivated: devre dışı bırakıldı + group_form_fields: + search: Ara ... + search_user: Kullanıcı ara + user_not_found: Kullanıcı bulunamadı + open_orders: + no_open_orders: Şu anda açık sipariş yok + not_enough_apples: Dikkat! Sipariş grubunuzun yeterli miktarda elma puanı bulunmamaktadır. + title: Mevcut siparişler + total_sum: Toplam tutar + who_ordered: Kim sipariş verdi? + order_download_button: + article_pdf: Ürün PDF'i + download_file: Dosya indir + fax_csv: Faks CSV'si + fax_pdf: Faks PDF'i + fax_txt: Faks metni + group_pdf: Grup PDF'i + matrix_pdf: Matris PDF'i + title: İndir + task_list: + accept_task: Görevi kabul et + done: Tamamlandı + done_q: Tamamlandı mı? + mark_done: Görevi tamamlandı olarak işaretle + reject_task: Görevi reddet + who: Kim yapıyor? + who_hint: "(Ne kadarı hala gerekiyor?)" + user_form_fields: + contact_address_hint: Sipariş grubunuzun adresi. Burayı güncellerseniz, diğer üyeler de güncellenir. + messagegroups: Mesaj gruplarına katılın veya ayrılın + workgroup_members: + title: Grup üyelikleri + simple_form: + error_notification: + default_message: Hatalar bulundu. Lütfen formu kontrol edin. + hints: + article: + unit: Örn. KG veya 1L veya 500g + article_category: + description: İçe aktarım/senkronizasyonda tanınan kategori adlarının virgülle ayrılmış listesi + order_article: + units_to_order: Teslim edilen toplam birim miktarını değiştirirseniz, ayrı ayrı grup miktarlarını değiştirmek için ürün adına tıklamanız gerekir. Bunlar otomatik olarak yeniden hesaplanmayacağından, sipariş grupları teslim edilmemiş ürünler için borçlu kalabilirler! + update_global_price: Ayrıca gelecekteki siparişlerin fiyatını da güncelleyin + stock_article: + copy: + name: Lütfen değiştirin + edit_stock_article: + price: "
  • Fiyat deÄŸiÅŸiklikleri yasaktır.
  • GerektiÄŸinde, %{stock_article_copy_link}.
" + supplier: + min_order_quantity: Sipariş vermek için gerekli minimum miktar sipariş sırasında gösterilir ve sipariş vermenizi teşvik etmelidir. + task: + duration: Görev ne kadar sürede tamamlanacak, 1-3 saat + required_users: Toplam kaç kullanıcıya ihtiyaç var? + tax: Yüzde olarak, standart 7,0'dır. + labels: + settings: + notify: + negative_balance: Sipariş grubumun negatif bakiyesi olduğunda beni bilgilendir. + order_finished: Siparişim tamamlandığında sipariş sonucum hakkında beni bilgilendir. + order_received: Teslimat detayları hakkında bilgilendirildiğimden emin ol. + upcoming_tasks: Yaklaşan görevler hakkında hatırlatmada bulun. + profile: + email_is_public: E-postam diğer üyeler tarafından görülebilir. + language: Dil + name_is_public: Adım diğer üyeler tarafından görülebilir. + phone_is_public: Telefon numaram diğer üyeler tarafından görülebilir. + settings_group: + messages: Mesajlar + privacy: Gizlilik + 'hayir': 'Hayir' + options: + settings: + profile: + language: + de: Almanca + en: İngilizce + es: İspanyolca + fr: Fransızca + nl: Hollandaca + tr: Türkçe + required: + mark: "*" + text: zorunlu + 'yes': 'Evet' + stock_takings: + create: + notice: Envanter başarıyla oluşturuldu. + edit: + title: Envanteri düzenle + index: + new_inventory: Yeni envanter oluştur + title: Envanter genel bakışı + new: + amount: Miktar + create: oluştur + stock_articles: Stok ürünleri + temp_inventory: geçici envanter + text_deviations: "%{inv_link} için tüm fazla sapmaları doldurunuz. Azaltma için negatif bir sayı kullanın." + text_need_articles: "Burada kullanmadan önce, şuradan yeni bir stok ürün oluşturmanız gerekiyor: %{create_link}" + title: Yeni envanter oluştur + show: + amount: Miktar + article: Ürün + confirm_delete: Envateri silmek istediğinize emin misiniz? + date: Tarih + note: Not + overview: Envanter genel bakışı + supplier: Tedarikçi + title: Envanteri göster + unit: Birim + stock_takings: + confirm_delete: Silmek istediğinize emin misiniz? + date: Tarih + note: Not + update: + notice: Envanter güncellendi. + stockit: + check: + not_empty: "%{name} silinemedi, envanter sıfır değil." + copy: + title: Stok ürünü kopyala + create: + notice: Yeni stok ürünü "%{name}" oluşturuldu. + derive: + title: Şablon kullanarak stok ürünü ekle + destroy: + notice: "%{name} ürünü silindi." + edit: + title: Stok ürünlerini düzenle + form: + copy_stock_article: stok ürünü kopyala + price_hint: Karmaşayı önlemek için, mevcut stok ürünlerinin fiyatlarını şimdilik düzenlemek mümkün değildir. + index: + confirm_delete: Silmek istediğinizden emin misiniz? + new_delivery: Yeni teslimat ... + new_stock_article: Yeni stok ürünü ekle + new_stock_taking: Envanter ekle + order_online: Stok siparişini çevrimiçi olarak ver + show_stock_takings: Envanter genel bakışı + stock_count: 'Ürün sayısı:' + stock_worth: 'Geçerli stok değeri:' + title: Stok (%{article_count}) + toggle_unavailable: Kullanılamayan ürünleri göster/gizle + view_options: Görünüm seçenekleri + new: + search_text: 'Tüm kataloglarda ürün arayın:' + title: Yeni stok ürünü ekle + show: + change_quantity: Değiştir + datetime: Zaman + new_quantity: Yeni miktar + reason: Sebep + stock_changes: Stok miktarı değişiklikleri + update: + notice: Stok ürünü %{name} kaydedildi. + suppliers: + create: + notice: Tedarikçi oluşturuldu + destroy: + notice: Tedarikçi silindi + edit: + title: Tedarikçi düzenle + index: + action_import: Dış veritabanından tedarikçi içe aktar + action_new: Yeni tedarikçi oluştur + articles: ürünler (%{count}) + confirm_del: "%{name} tedarikçisini gerçekten silmek istiyor musunuz?" + deliveries: teslimatlar (%{count}) + stock: stokta (%{count}) + title: Tedarikçiler + new: + title: Yeni tedarikçi + shared_supplier_methods: + all_available: Tüm ürünler (yeni mevcut) + all_unavailable: Tüm ürünler (yeni mevcut değil) + import: İçe aktarılacak ürünleri seçin + shared_supplier_note: Tedarikçi harici veritabanına bağlıdır. + shared_suppliers: + body: "

Harici veritabanındaki tedarikçiler burada görüntülenir.

Dış tedarikçileri abone olarak içe aktarabilirsiniz (aşağıya bakın).

Yeni bir tedarikçi oluşturulacak ve harici veritabanına bağlanacaktır.

" + subscribe: Abone ol + subscribe_again: Tekrar abone ol + supplier: Tedarikçi + title: Harici listeler + show: + last_deliveries: Son teslimatlar + last_orders: Son sipariÅŸler + new_delivery: Yeni teslimat + show_deliveries: Tümünü göster + update: + notice: Tedarikçi güncellendi + tasks: + accept: + notice: Görevi kabul ettiniz + archive: + title: Görev arÅŸivi + create: + notice: Görev oluÅŸturuldu + destroy: + notice: Görev silindi + edit: + submit_periodic: Tekrarlayan görevi kaydet + title: Görevi düzenle + title_periodic: Tekrarlayan görevi düzenle + warning_periodic: "Uyarı: Bu görev, tekrar eden görevler grubunun bir parçasıdır. KaydedildiÄŸinde gruptan çıkarılacak ve bir normal göreve dönüştürülecektir." + error_not_found: Hiçbir çalışma grubu bulunamadı + form: + search: + hint: Kullanıcı ara + noresult: Kullanıcı bulunamadı + placeholder: Ara ... + submit: + periodic: Tekrarlayan görevi kaydet + index: + show_group_tasks: Grup görevlerini göster + title: Görevler + title_non_group: Tüm görevler + nav: + all_tasks: Tüm görevler + archive: Tamamlanmış görevler (arÅŸiv) + group_tasks: Grup görevleri + my_tasks: Görevlerim + new_task: Yeni görev oluÅŸtur + pages: Sayfalar + new: + submit_periodic: Yinelenen görev oluÅŸtur + title: Yeni görev oluÅŸtur + repeated: Görev yineleniyor + set_done: + notice: Görev durumu güncellendi + show: + accept_task: Görevi kabul et + confirm_delete_group: Bu ve sonraki tüm görevleri gerçekten silmek istiyor musunuz? + confirm_delete_single: Görevi silmek istediÄŸinizden emin misiniz? + confirm_delete_single_from_group: Bu görevi (iliÅŸkili yinelenen görevleri tutarak) silmek istediÄŸinizden emin misiniz? + delete_group: Görevi ve takip edenleri sil + edit_group: Tekrarlayanı düzenle + hours: "%{count}s" + mark_done: Görevi tamamlandı olarak iÅŸaretle + reject_task: Görevi reddet + title: Görevi göster + update: + notice: Görev güncellendi. + notice_converted: Görev güncellendi ve tekrarlanmayan bir göreve dönüştürüldü. + user: + more: Yapacak hiçbir ÅŸey yok mu? %{tasks_link} kesinlikle görevler var. + tasks_link: Burada + title: Görevlerim + title_accepted: Kabul edilen görevler + title_open: Açık görevler + workgroup: + title: '%{workgroup} için görevler' + title_all: Tüm grup görevleri + ui: + actions: Ä°ÅŸlemler + back: Geri + cancel: Ä°ptal + close: Kapat + confirm_delete: "%{name} silmek istediÄŸinizden emin misiniz?" + confirm_restore: "%{name} geri yüklemek istediÄŸinizden emin misiniz?" + copy: Kopyala + delete: Sil + download: Ä°ndir + edit: Düzenle + marks: + close: "×" + success: + move: Taşı + or_cancel: ya da iptal + please_wait: Lütfen bekleyin... + restore: Geri yükle + save: Kaydet + search_placeholder: Ara... + show: Göster + views: + pagination: + first: "«" + last: "»" + next: "›" + previous: "‹" + truncate: "..." + workgroups: + edit: + title: Çalışma grubunu düzenle + error_last_admin_group: Yönetici hakları olan son grup silinemez + error_last_admin_role: Yönetici hakları olan son gruptan yönetici rolü geri alınamaz + index: + title: Çalışma Grupları + update: + notice: Çalışma grubu güncellendi. + time: + formats: + foodsoft_datetime: "%d.%b.%Y %H:%M" diff --git a/config/navigation.rb b/config/navigation.rb index c8b7d088..f483989f 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -12,7 +12,7 @@ SimpleNavigation::Configuration.run do |navigation| primary.item :dashboard_nav_item, I18n.t('navigation.dashboard'), root_path(anchor: '') primary.item :foodcoop, I18n.t('navigation.foodcoop'), '#' do |subnav| - subnav.item :members, I18n.t('navigation.members'), foodcoop_users_path + subnav.item :members, I18n.t('navigation.members'), foodcoop_users_path, unless: proc { FoodsoftConfig[:disable_members_overview] } subnav.item :workgroups, I18n.t('navigation.workgroups'), foodcoop_workgroups_path subnav.item :ordergroups, I18n.t('navigation.ordergroups'), foodcoop_ordergroups_path subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path @@ -21,31 +21,44 @@ SimpleNavigation::Configuration.run do |navigation| primary.item :orders, I18n.t('navigation.orders.title'), '#' do |subnav| subnav.item :ordering, I18n.t('navigation.orders.ordering'), group_orders_path subnav.item :ordering_archive, I18n.t('navigation.orders.archive'), archive_group_orders_path - subnav.item :orders, I18n.t('navigation.orders.manage'), orders_path, if: Proc.new { current_user.role_orders? } - subnav.item :pickups, I18n.t('navigation.orders.pickups'), pickups_path, if: Proc.new { current_user.role_pickups? } + subnav.item :orders, I18n.t('navigation.orders.manage'), orders_path, if: proc { current_user.role_orders? } + subnav.item :pickups, I18n.t('navigation.orders.pickups'), pickups_path, if: proc { + current_user.role_pickups? + } end primary.item :articles, I18n.t('navigation.articles.title'), '#', - if: Proc.new { current_user.role_article_meta? or current_user.role_suppliers? } do |subnav| + if: proc { current_user.role_article_meta? or current_user.role_suppliers? } do |subnav| subnav.item :suppliers, I18n.t('navigation.articles.suppliers'), suppliers_path subnav.item :stockit, I18n.t('navigation.articles.stock'), stock_articles_path subnav.item :categories, I18n.t('navigation.articles.categories'), article_categories_path end - primary.item :finance, I18n.t('navigation.finances.title'), '#', if: Proc.new { current_user.role_finance? || current_user.role_invoices? } do |subnav| - subnav.item :finance_home, I18n.t('navigation.finances.home'), finance_root_path, if: Proc.new { current_user.role_finance? } - subnav.item :finance_home, I18n.t('navigation.finances.bank_accounts'), finance_bank_accounts_path, if: Proc.new { current_user.role_finance? } - subnav.item :accounts, I18n.t('navigation.finances.accounts'), finance_ordergroups_path, if: Proc.new { current_user.role_finance? } - subnav.item :balancing, I18n.t('navigation.finances.balancing'), finance_order_index_path, if: Proc.new { current_user.role_finance? } + primary.item :finance, I18n.t('navigation.finances.title'), '#', if: proc { + current_user.role_finance? || current_user.role_invoices? + } do |subnav| + subnav.item :finance_home, I18n.t('navigation.finances.home'), finance_root_path, if: proc { + current_user.role_finance? + } + subnav.item :finance_home, I18n.t('navigation.finances.bank_accounts'), finance_bank_accounts_path, if: proc { + current_user.role_finance? + } + subnav.item :accounts, I18n.t('navigation.finances.accounts'), finance_ordergroups_path, if: proc { + current_user.role_finance? + } + subnav.item :balancing, I18n.t('navigation.finances.balancing'), finance_order_index_path, if: proc { + current_user.role_finance? + } subnav.item :invoices, I18n.t('navigation.finances.invoices'), finance_invoices_path end - primary.item :admin, I18n.t('navigation.admin.title'), '#', if: Proc.new { current_user.role_admin? } do |subnav| + primary.item :admin, I18n.t('navigation.admin.title'), '#', if: proc { current_user.role_admin? } do |subnav| subnav.item :admin_home, I18n.t('navigation.admin.home'), admin_root_path subnav.item :users, I18n.t('navigation.admin.users'), admin_users_path subnav.item :ordergroups, I18n.t('navigation.admin.ordergroups'), admin_ordergroups_path subnav.item :workgroups, I18n.t('navigation.admin.workgroups'), admin_workgroups_path - subnav.item :mail_delivery_status, I18n.t('navigation.admin.mail_delivery_status'), admin_mail_delivery_status_index_path + subnav.item :mail_delivery_status, I18n.t('navigation.admin.mail_delivery_status'), + admin_mail_delivery_status_index_path subnav.item :finances, I18n.t('navigation.admin.finance'), admin_finances_path subnav.item :config, I18n.t('navigation.admin.config'), admin_config_path end diff --git a/config/puma.rb b/config/puma.rb index 4f4fd1cf..609df0ad 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -4,19 +4,19 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 1 } +threads_count = ENV.fetch('RAILS_MAX_THREADS') { 1 } threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch('PORT') { 3000 } # Bind automatically to all systemd activated sockets bind_to_activated_sockets # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch('RAILS_ENV') { 'development' } # Specifies the number of `workers` to boot in clustered mode. # Workers are forked webserver processes. If using threads and workers together @@ -24,7 +24,7 @@ environment ENV.fetch("RAILS_ENV") { "development" } # Workers do not work on JRuby or Windows (both of which do not support # processes). # -workers ENV.fetch("WEB_CONCURRENCY") { 8 } +workers ENV.fetch('WEB_CONCURRENCY') { 8 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code diff --git a/config/routes.rb b/config/routes.rb index 5b27eba4..8fea34b0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,9 +1,12 @@ +# rubocop:disable Metrics/BlockLength Rails.application.routes.draw do - get "order_comments/new" + mount Rswag::Ui::Engine => '/api-docs' + mount Rswag::Api::Engine => '/api-docs' + get 'order_comments/new' - get "comments/new" + get 'comments/new' - get "sessions/new" + get 'sessions/new' root to: 'sessions#redirect_to_foodcoop', as: nil @@ -21,8 +24,8 @@ Rails.application.routes.draw do post '/login/reset_password' => 'login#reset_password', as: :reset_password get '/login/new_password' => 'login#new_password', as: :new_password patch '/login/update_password' => 'login#update_password', as: :update_password - match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation, via: [:get, :post] - resources :sessions, only: [:new, :create, :destroy] + match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation, via: %i[get post] + resources :sessions, only: %i[new create destroy] get '/foodcoop.css' => 'styles#foodcoop', as: 'foodcoop_css' @@ -62,11 +65,11 @@ Rails.application.routes.draw do resources :group_order_articles - resources :order_comments, only: [:new, :create] + resources :order_comments, only: %i[new create] ############ Foodcoop orga - resources :invites, only: [:new, :create] + resources :invites, only: %i[new create] resources :tasks do collection do @@ -88,7 +91,7 @@ Rails.application.routes.draw do resources :ordergroups, only: [:index] - resources :workgroups, only: [:index, :edit, :update] + resources :workgroups, only: %i[index edit update] end ########### Article management @@ -172,7 +175,7 @@ Rails.application.routes.draw do get :unpaid, on: :collection end - resources :links, controller: 'financial_links', only: [:create, :show] do + resources :links, controller: 'financial_links', only: %i[create show] do collection do get :incomplete end @@ -182,8 +185,10 @@ Rails.application.routes.draw do delete 'bank_transactions/:bank_transaction', action: 'remove_bank_transaction', as: 'remove_bank_transaction' get :index_financial_transaction - put 'financial_transactions/:financial_transaction', action: 'add_financial_transaction', as: 'add_financial_transaction' - delete 'financial_transactions/:financial_transaction', action: 'remove_financial_transaction', as: 'remove_financial_transaction' + put 'financial_transactions/:financial_transaction', action: 'add_financial_transaction', + as: 'add_financial_transaction' + delete 'financial_transactions/:financial_transaction', action: 'remove_financial_transaction', + as: 'remove_financial_transaction' get :index_invoice put 'invoices/:invoice', action: 'add_invoice', as: 'add_invoice' @@ -197,12 +202,14 @@ Rails.application.routes.draw do resources :ordergroups, only: [:index] do resources :financial_transactions, as: :transactions end - resources :financial_transactions, as: :foodcoop_financial_transactions, path: 'foodcoop/financial_transactions', only: [:index, :new, :create] + resources :financial_transactions, as: :foodcoop_financial_transactions, path: 'foodcoop/financial_transactions', + only: %i[index new create] get :transactions, controller: :financial_transactions, action: :index_collection delete 'transactions/:id', controller: :financial_transactions, action: :destroy, as: :transaction get 'transactions/new_collection' => 'financial_transactions#new_collection', as: 'new_transaction_collection' - post 'transactions/create_collection' => 'financial_transactions#create_collection', as: 'create_transaction_collection' + post 'transactions/create_collection' => 'financial_transactions#create_collection', + as: 'create_transaction_collection' resources :bank_accounts, only: [:index] do member do @@ -214,7 +221,7 @@ Rails.application.routes.draw do resources :bank_transactions, as: :transactions end - resources :bank_transactions, only: [:index, :show] + resources :bank_transactions, only: %i[index show] end ########### Administration @@ -248,11 +255,11 @@ Rails.application.routes.draw do get :memberships, on: :member end - resources :mail_delivery_status, only: [:index, :show, :destroy] do + resources :mail_delivery_status, only: %i[index show destroy] do delete :index, on: :collection, action: :destroy_all end - resource :config, only: [:show, :update] do + resource :config, only: %i[show update] do get :list end end @@ -267,26 +274,27 @@ Rails.application.routes.draw do namespace :user do root to: 'users#show' get :financial_overview, to: 'ordergroup#financial_overview' - resources :financial_transactions, only: [:index, :show, :create] + resources :financial_transactions, only: %i[index show create] resources :group_order_articles end - resources :financial_transaction_classes, only: [:index, :show] - resources :financial_transaction_types, only: [:index, :show] - resources :financial_transactions, only: [:index, :show] - resources :orders, only: [:index, :show] - resources :order_articles, only: [:index, :show] + resources :financial_transaction_classes, only: %i[index show] + resources :financial_transaction_types, only: %i[index show] + resources :financial_transactions, only: %i[index show] + resources :orders, only: %i[index show] + resources :order_articles, only: %i[index show] resources :group_order_articles - resources :article_categories, only: [:index, :show] + resources :article_categories, only: %i[index show] end end ############## Feedback - resource :feedback, only: [:new, :create], controller: 'feedback' + resource :feedback, only: %i[new create], controller: 'feedback' ############## The rest resources :users, only: [:index] end # End of /:foodcoop scope end +# rubocop:enable Metrics/BlockLength diff --git a/config/schedule.rb b/config/schedule.rb index f22c1348..6d424324 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -2,22 +2,23 @@ # Learn more: http://github.com/javan/whenever # Upcoming tasks notifier -every :day, :at => '7:20 am' do - rake "multicoops:run TASK=foodsoft:notify_upcoming_tasks" - rake "multicoops:run TASK=foodsoft:notify_users_of_weekly_task" +every :day, at: '7:20 am' do + rake 'multicoops:run TASK=foodsoft:notify_upcoming_tasks' + rake 'multicoops:run TASK=foodsoft:notify_users_of_weekly_task' end # Import and assign bank transactions -every :weekday, :at => %w(5:56am 6:04pm) do - rake "multicoops:run TASK=foodsoft:import_and_assign_bank_transactions" +every :weekday, at: %w[5:56am 6:04pm] do + rake 'multicoops:run TASK=foodsoft:import_and_assign_bank_transactions' end -# Weekly taks -every :sunday, :at => '7:14 am' do - rake "multicoops:run TASK=foodsoft:create_upcoming_periodic_tasks" +# Weekly tasks +every :sunday, at: '7:14 am' do + rake 'multicoops:run TASK=foodsoft:create_upcoming_periodic_tasks' + rake 'multicoops:run TASKS=foodsoft:prune_old_attachments' end # Finish ended orders every 1.minute do - rake "multicoops:run TASK=foodsoft:finish_ended_orders" + rake 'multicoops:run TASK=foodsoft:finish_ended_orders' end diff --git a/config/spring.rb b/config/spring.rb index c9119b40..9fa7863f 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,6 +1,6 @@ -%w( +%w[ .ruby-version .rbenv-vars tmp/restart.txt tmp/caching-dev.txt -).each { |path| Spring.watch(path) } +].each { |path| Spring.watch(path) } diff --git a/db/migrate/001_create_users.rb b/db/migrate/001_create_users.rb index ab4a560a..e530bdbf 100644 --- a/db/migrate/001_create_users.rb +++ b/db/migrate/001_create_users.rb @@ -4,30 +4,31 @@ class CreateUsers < ActiveRecord::Migration[4.2] def self.up create_table :users do |t| - t.column :nick, :string, :null => false - t.column :password_hash, :string, :null => false - t.column :password_salt, :string, :null => false - t.column :first_name, :string, :null => false - t.column :last_name, :string, :null => false - t.column :email, :string, :null => false + t.column :nick, :string, null: false + t.column :password_hash, :string, null: false + t.column :password_salt, :string, null: false + t.column :first_name, :string, null: false + t.column :last_name, :string, null: false + t.column :email, :string, null: false t.column :phone, :string t.column :address, :string - t.column :created_on, :timestamp, :null => false + t.column :created_on, :timestamp, null: false end - add_index(:users, :nick, :unique => true) - add_index(:users, :email, :unique => true) + add_index(:users, :nick, unique: true) + add_index(:users, :email, unique: true) # Create the default admin user... puts "Creating user #{USER_ADMIN} with password 'secret'..." - user = User.new(:nick => USER_ADMIN, :first_name => "Anton", :last_name => "Administrator", :email => "admin@foo.test") - user.password = "secret" - raise "Failed!" unless user.save && User.find_by_nick(USER_ADMIN).has_password("secret") + user = User.new(nick: USER_ADMIN, first_name: 'Anton', last_name: 'Administrator', + email: 'admin@foo.test') + user.password = 'secret' + raise 'Failed!' unless user.save && User.find_by_nick(USER_ADMIN).has_password('secret') # Create a normal user... puts "Creating user #{USER_TEST} with password 'foobar'..." - user = User.new(:nick => USER_TEST, :first_name => "Tim", :last_name => "Tester", :email => "test@foo.test") - user.password = "foobar" - raise "Failed!" unless user.save && User.find_by_nick(USER_TEST).has_password("foobar") + user = User.new(nick: USER_TEST, first_name: 'Tim', last_name: 'Tester', email: 'test@foo.test') + user.password = 'foobar' + raise 'Failed!' unless user.save && User.find_by_nick(USER_TEST).has_password('foobar') end def self.down diff --git a/db/migrate/002_create_groups.rb b/db/migrate/002_create_groups.rb index bb7427b9..31104a1e 100644 --- a/db/migrate/002_create_groups.rb +++ b/db/migrate/002_create_groups.rb @@ -4,47 +4,48 @@ class CreateGroups < ActiveRecord::Migration[4.2] def self.up create_table :groups do |t| - t.column :type, :string, :null => false # inheritance, types: Group, OrderGroup - t.column :name, :string, :null => false + t.column :type, :string, null: false # inheritance, types: Group, OrderGroup + t.column :name, :string, null: false t.column :description, :string t.column :actual_size, :integer # OrderGroup column - t.column :account_balance, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 # OrderGroup column + t.column :account_balance, :decimal, precision: 8, scale: 2, null: false, default: 0 # OrderGroup column t.column :account_updated, :timestamp # OrderGroup column - t.column :created_on, :timestamp, :null => false - t.column :role_admin, :boolean, :default => false, :null => false + t.column :created_on, :timestamp, null: false + t.column :role_admin, :boolean, default: false, null: false end - add_index(:groups, :name, :unique => true) + add_index(:groups, :name, unique: true) create_table :memberships do |t| - t.column :group_id, :integer, :null => false - t.column :user_id, :integer, :null => false + t.column :group_id, :integer, null: false + t.column :user_id, :integer, null: false end - add_index(:memberships, [:user_id, :group_id], :unique => true) + add_index(:memberships, %i[user_id group_id], unique: true) # Create the default "Administrators" group... puts "Creating group #{GROUP_ADMIN}..." - Group.create(:name => GROUP_ADMIN, :description => "System administrators.", :role_admin => true) + Group.create(name: GROUP_ADMIN, description: 'System administrators.', role_admin: true) raise 'Failed!' unless administrators = Group.find_by_name(GROUP_ADMIN) # Create a sample order group... puts "Creating order group #{GROUP_ORDER}..." - ordergroup = OrderGroup.create!(:name => GROUP_ORDER, :description => "A sample order group created by the migration.", :actual_size => 1, :account_updated => Time.now) - raise "Wrong type created!" unless ordergroup.is_a?(OrderGroup) + ordergroup = OrderGroup.create!(name: GROUP_ORDER, + description: 'A sample order group created by the migration.', actual_size: 1, account_updated: Time.now) + raise 'Wrong type created!' unless ordergroup.is_a?(OrderGroup) # Get the admin user and join the admin group... raise "User #{CreateUsers::USER_ADMIN} not found, cannot join group '#{administrators.name}'!" unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) puts "Joining #{CreateUsers::USER_ADMIN} user to new '#{administrators.name}' group as a group admin..." - membership = Membership.create(:group => administrators, :user => admin) - raise "Failed!" unless admin.memberships.first == membership + membership = Membership.create(group: administrators, user: admin) + raise 'Failed!' unless admin.memberships.first == membership raise "User #{CreateUsers::USER_ADMIN} has no admin_roles" unless admin.role_admin? # Get the test user and join the order group... raise "User #{CreateUsers::USER_TEST} not found, cannot join group '#{ordergroup.name}'!" unless test = User.find_by_nick(CreateUsers::USER_TEST) puts "Joining #{CreateUsers::USER_TEST} user to new '#{ordergroup.name}' group as a group admin..." - membership = Membership.create(:group => ordergroup, :user => test) - raise "Failed!" unless test.memberships.first == membership + membership = Membership.create(group: ordergroup, user: test) + raise 'Failed!' unless test.memberships.first == membership end def self.down diff --git a/db/migrate/003_create_suppliers.rb b/db/migrate/003_create_suppliers.rb index 2b38c9c1..72e148b8 100644 --- a/db/migrate/003_create_suppliers.rb +++ b/db/migrate/003_create_suppliers.rb @@ -2,17 +2,17 @@ class CreateSuppliers < ActiveRecord::Migration[4.2] SUPPLIER_SAMPLE = 'Sample Supplier' def self.up - add_column :groups, :role_suppliers, :boolean, :default => false, :null => false + add_column :groups, :role_suppliers, :boolean, default: false, null: false Group.reset_column_information puts "Give #{CreateGroups::GROUP_ADMIN} the role supplier .." - raise "Failed" unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_suppliers, true) - raise "Cannot find admin user!" unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) - raise "Failed to enable role_suppliers with admin user!" unless admin.role_suppliers? + raise 'Failed' unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_suppliers, true) + raise 'Cannot find admin user!' unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) + raise 'Failed to enable role_suppliers with admin user!' unless admin.role_suppliers? create_table :suppliers do |t| - t.column :name, :string, :null => false - t.column :address, :string, :null => false - t.column :phone, :string, :null => false + t.column :name, :string, null: false + t.column :address, :string, null: false + t.column :phone, :string, null: false t.column :phone2, :string t.column :fax, :string t.column :email, :string @@ -23,12 +23,12 @@ class CreateSuppliers < ActiveRecord::Migration[4.2] t.column :order_howto, :string t.column :note, :string end - add_index(:suppliers, :name, :unique => true) + add_index(:suppliers, :name, unique: true) # Create sample supplier... puts "Creating sample supplier '#{SUPPLIER_SAMPLE}'..." - Supplier.create(:name => SUPPLIER_SAMPLE, :address => "Organic City", :phone => "0123-555555") - raise "Failed!" unless supplier = Supplier.find_by_name(SUPPLIER_SAMPLE) + Supplier.create(name: SUPPLIER_SAMPLE, address: 'Organic City', phone: '0123-555555') + raise 'Failed!' unless supplier = Supplier.find_by_name(SUPPLIER_SAMPLE) end def self.down diff --git a/db/migrate/004_create_article_meta.rb b/db/migrate/004_create_article_meta.rb index eb81f550..36c22f65 100644 --- a/db/migrate/004_create_article_meta.rb +++ b/db/migrate/004_create_article_meta.rb @@ -5,24 +5,24 @@ class CreateArticleMeta < ActiveRecord::Migration[4.2] def self.up # Add user roles... - add_column :groups, :role_article_meta, :boolean, :default => false, :null => false + add_column :groups, :role_article_meta, :boolean, default: false, null: false Group.reset_column_information puts "Give #{CreateGroups::GROUP_ADMIN} the role article_meta .." - raise "Failed" unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_article_meta, true) + raise 'Failed' unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_article_meta, true) raise 'Cannot find admin user!' unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) raise 'Failed to enable role_article_meta with admin user!' unless admin.role_article_meta? # ArticleCategories create_table :article_categories do |t| - t.column :name, :string, :null => false + t.column :name, :string, null: false t.column :description, :string end - add_index(:article_categories, :name, :unique => true) + add_index(:article_categories, :name, unique: true) # Create sample category... puts "Creating sample article category '#{CATEGORY_SAMPLE}'..." - ArticleCategory.create(:name => CATEGORY_SAMPLE, :description => "This is just a sample article category.") - raise "Failed!" unless category = ArticleCategory.find_by_name(CATEGORY_SAMPLE) + ArticleCategory.create(name: CATEGORY_SAMPLE, description: 'This is just a sample article category.') + raise 'Failed!' unless category = ArticleCategory.find_by_name(CATEGORY_SAMPLE) end def self.down diff --git a/db/migrate/005_create_financial_transactions.rb b/db/migrate/005_create_financial_transactions.rb index 0b1cef89..7ca0e83c 100644 --- a/db/migrate/005_create_financial_transactions.rb +++ b/db/migrate/005_create_financial_transactions.rb @@ -2,19 +2,19 @@ class CreateFinancialTransactions < ActiveRecord::Migration[4.2] def self.up # Create Financial Transactions create_table :financial_transactions do |t| - t.column :order_group_id, :integer, :null => false - t.column :amount, :decimal, :precision => 8, :scale => 2, :null => false - t.column :note, :text, :null => false - t.column :user_id, :integer, :null => false - t.column :created_on, :datetime, :null => false + t.column :order_group_id, :integer, null: false + t.column :amount, :decimal, precision: 8, scale: 2, null: false + t.column :note, :text, null: false + t.column :user_id, :integer, null: false + t.column :created_on, :datetime, null: false end # add column for the finance role puts 'add column in "groups" for the finance role' - add_column :groups, :role_finance, :boolean, :default => false, :null => false + add_column :groups, :role_finance, :boolean, default: false, null: false Group.reset_column_information puts "Give #{CreateGroups::GROUP_ADMIN} the role finance .." - raise "Failed" unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_finance, true) + raise 'Failed' unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_finance, true) raise 'Cannot find admin user!' unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) raise 'Failed to enable role_finance with admin user!' unless admin.role_finance? @@ -27,8 +27,8 @@ class CreateFinancialTransactions < ActiveRecord::Migration[4.2] ordergroup.addFinancialTransaction(i, "Sample Transaction Nr. #{i}", admin) balance += i end - raise "Failed!" unless financial_transaction = FinancialTransaction.find_by_note('Sample Transaction Nr. 1') - raise "Failed to update account_balance!" unless OrderGroup.find(ordergroup.id).account_balance == balance + raise 'Failed!' unless financial_transaction = FinancialTransaction.find_by_note('Sample Transaction Nr. 1') + raise 'Failed to update account_balance!' unless OrderGroup.find(ordergroup.id).account_balance == balance end def self.down diff --git a/db/migrate/006_create_articles.rb b/db/migrate/006_create_articles.rb index 9a43c8dc..fd11e659 100644 --- a/db/migrate/006_create_articles.rb +++ b/db/migrate/006_create_articles.rb @@ -1,17 +1,17 @@ class CreateArticles < ActiveRecord::Migration[4.2] - SAMPLE_ARTICLE_NAMES = ['banana', 'kiwi', 'strawberry'] + SAMPLE_ARTICLE_NAMES = %w[banana kiwi strawberry] def self.up create_table :articles do |t| - t.column :name, :string, :null => false - t.column :supplier_id, :integer, :null => false - t.column :article_category_id, :integer, :null => false - t.column :unit, :string, :null => false + t.column :name, :string, null: false + t.column :supplier_id, :integer, null: false + t.column :article_category_id, :integer, null: false + t.column :unit, :string, null: false t.column :note, :string - t.column :availability, :boolean, :default => true, :null => false + t.column :availability, :boolean, default: true, null: false t.column :current_price_id, :integer end - add_index(:articles, :name, :unique => true) + add_index(:articles, :name, unique: true) # Create 30 sample articles... puts "Create 3 articles of the supplier '#{CreateSuppliers::SUPPLIER_SAMPLE}'..." @@ -20,14 +20,14 @@ class CreateArticles < ActiveRecord::Migration[4.2] SAMPLE_ARTICLE_NAMES.each do |a| puts 'Create Article ' + a - Article.create(:name => a, - :supplier => supplier, - :article_category => category, - :unit => '500g', - :note => 'delicious', - :availability => true) + Article.create(name: a, + supplier: supplier, + article_category: category, + unit: '500g', + note: 'delicious', + availability: true) end - raise "Failed!" unless Article.find(:all).length == SAMPLE_ARTICLE_NAMES.length + raise 'Failed!' unless Article.find(:all).length == SAMPLE_ARTICLE_NAMES.length end def self.down diff --git a/db/migrate/007_create_article_prices.rb b/db/migrate/007_create_article_prices.rb index ed5b0793..56167a1a 100644 --- a/db/migrate/007_create_article_prices.rb +++ b/db/migrate/007_create_article_prices.rb @@ -1,13 +1,13 @@ class CreateArticlePrices < ActiveRecord::Migration[4.2] def self.up create_table :article_prices do |t| - t.column :article_id, :int, :null => false - t.column :clear_price, :decimal, :precision => 8, :scale => 2, :null => false - t.column :gross_price, :decimal, :precision => 8, :scale => 2, :null => false # gross price, incl. vat, refund and price markup - t.column :tax, :float, :null => false, :default => 0 - t.column :refund, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 + t.column :article_id, :int, null: false + t.column :clear_price, :decimal, precision: 8, scale: 2, null: false + t.column :gross_price, :decimal, precision: 8, scale: 2, null: false # gross price, incl. vat, refund and price markup + t.column :tax, :float, null: false, default: 0 + t.column :refund, :decimal, precision: 8, scale: 2, null: false, default: 0 t.column :updated_on, :datetime - t.column :unit_quantity, :int, :default => 1, :null => false + t.column :unit_quantity, :int, default: 1, null: false t.column :order_number, :string end add_index(:article_prices, :article_id) @@ -18,11 +18,11 @@ class CreateArticlePrices < ActiveRecord::Migration[4.2] puts 'Create Price for article ' + a raise "article #{a} not found!" unless article = Article.find_by_name(a) - new_price = ArticlePrice.new(:clear_price => rand(4) + 1, - :tax => 7.0, - :refund => 0, - :unit_quantity => rand(10) + 1, - :order_number => rand(9999)) + new_price = ArticlePrice.new(clear_price: rand(1..4), + tax: 7.0, + refund: 0, + unit_quantity: rand(1..10), + order_number: rand(9999)) article.add_price(new_price) raise 'Failed!' unless ArticlePrice.find_by_article_id(article.id) end diff --git a/db/migrate/008_create_orders.rb b/db/migrate/008_create_orders.rb index 6eb8c921..4e138899 100644 --- a/db/migrate/008_create_orders.rb +++ b/db/migrate/008_create_orders.rb @@ -4,28 +4,28 @@ class CreateOrders < ActiveRecord::Migration[4.2] def self.up # Order role - add_column :groups, :role_orders, :boolean, :default => false, :null => false + add_column :groups, :role_orders, :boolean, default: false, null: false Group.reset_column_information puts "Give #{CreateGroups::GROUP_ADMIN} the role finance .." - raise "Failed" unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_orders, true) + raise 'Failed' unless Group.find_by_name(CreateGroups::GROUP_ADMIN).update_attribute(:role_orders, true) raise 'Cannot find admin user!' unless admin = User.find_by_nick(CreateUsers::USER_ADMIN) raise 'Failed to enable role_orders with admin user!' unless admin.role_orders? # Create the default "Order" group... puts 'Creating group "Orders"...' - Group.create(:name => GROUP_ORDER, :description => "working group for managing orders", :role_orders => true) - raise "Failed!" unless Group.find_by_name(GROUP_ORDER) + Group.create(name: GROUP_ORDER, description: 'working group for managing orders', role_orders: true) + raise 'Failed!' unless Group.find_by_name(GROUP_ORDER) # Order create_table :orders do |t| - t.column :name, :string, :null => false - t.column :supplier_id, :integer, :null => false - t.column :starts, :datetime, :null => false + t.column :name, :string, null: false + t.column :supplier_id, :integer, null: false + t.column :starts, :datetime, null: false t.column :ends, :datetime t.column :note, :string - t.column :finished, :boolean, :default => false, :null => false - t.column :booked, :boolean, :null => false, :default => false - t.column :lock_version, :integer, :null => false, :default => 0 + t.column :finished, :boolean, default: false, null: false + t.column :booked, :boolean, null: false, default: false + t.column :lock_version, :integer, null: false, default: 0 t.column :updated_by_user_id, :integer end add_index(:orders, :starts) @@ -35,74 +35,77 @@ class CreateOrders < ActiveRecord::Migration[4.2] puts "Creating order '#{ORDER_TEST}'..." raise "Supplier '#{CreateSuppliers::SUPPLIER_SAMPLE}' not found!" unless supplier = Supplier.find_by_name(CreateSuppliers::SUPPLIER_SAMPLE) - Order.create(:name => ORDER_TEST, :supplier => supplier, :starts => Time.now) + Order.create(name: ORDER_TEST, supplier: supplier, starts: Time.now) raise 'Creating test order failed!' unless order = Order.find_by_name(ORDER_TEST) # OrderArticle create_table :order_articles do |t| - t.column :order_id, :integer, :null => false - t.column :article_id, :integer, :null => false - t.column :quantity, :integer, :null => false, :default => 0 - t.column :tolerance, :integer, :null => false, :default => 0 - t.column :units_to_order, :integer, :null => false, :default => 0 - t.column :lock_version, :integer, :null => false, :default => 0 + t.column :order_id, :integer, null: false + t.column :article_id, :integer, null: false + t.column :quantity, :integer, null: false, default: 0 + t.column :tolerance, :integer, null: false, default: 0 + t.column :units_to_order, :integer, null: false, default: 0 + t.column :lock_version, :integer, null: false, default: 0 end - add_index(:order_articles, [:order_id, :article_id], :unique => true) + add_index(:order_articles, %i[order_id article_id], unique: true) puts 'Adding articles to the order...' - CreateArticles::SAMPLE_ARTICLE_NAMES.each { |a| + CreateArticles::SAMPLE_ARTICLE_NAMES.each do |a| puts "Article #{a}..." raise 'Article not found!' unless article = Article.find_by_name(a) raise 'No price found for article!' unless price = article.current_price - OrderArticle.create(:order => order, :article => article) + OrderArticle.create(order: order, article: article) raise 'Creating OrderArticle failed!' unless OrderArticle.find_by_order_id_and_article_id(order.id, article.id) - } + end raise 'Creating OrderArticles failed!' unless order.articles.size == CreateArticles::SAMPLE_ARTICLE_NAMES.length # GroupOrder create_table :group_orders do |t| - t.column :order_group_id, :integer, :null => false - t.column :order_id, :integer, :null => false - t.column :price, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 - t.column :lock_version, :integer, :null => false, :default => 0 - t.column :updated_on, :timestamp, :null => false - t.column :updated_by_user_id, :integer, :null => false + t.column :order_group_id, :integer, null: false + t.column :order_id, :integer, null: false + t.column :price, :decimal, precision: 8, scale: 2, null: false, default: 0 + t.column :lock_version, :integer, null: false, default: 0 + t.column :updated_on, :timestamp, null: false + t.column :updated_by_user_id, :integer, null: false end - add_index(:group_orders, [:order_group_id, :order_id], :unique => true) + add_index(:group_orders, %i[order_group_id order_id], unique: true) puts 'Adding group order...' raise "Cannot find user #{CreateUsers::USER_TEST}" unless user = User.find_by_nick(CreateUsers::USER_TEST) raise "Cannot find OrderGroup '#{CreateGroups::GROUP_ORDER}'!" unless orderGroup = OrderGroup.find_by_name(CreateGroups::GROUP_ORDER) - GroupOrder.create(:order_group => orderGroup, :order => order, :price => 0, :updated_by => user) - raise 'Retrieving group order failed!' unless groupOrder = orderGroup.group_orders.find(:first, :conditions => "order_id = #{order.id}") + GroupOrder.create(order_group: orderGroup, order: order, price: 0, updated_by: user) + raise 'Retrieving group order failed!' unless groupOrder = orderGroup.group_orders.find(:first, + conditions: "order_id = #{order.id}") # GroupOrderArticles create_table :group_order_articles do |t| - t.column :group_order_id, :integer, :null => false - t.column :order_article_id, :integer, :null => false - t.column :quantity, :integer, :null => false - t.column :tolerance, :integer, :null => false - t.column :updated_on, :timestamp, :null => false + t.column :group_order_id, :integer, null: false + t.column :order_article_id, :integer, null: false + t.column :quantity, :integer, null: false + t.column :tolerance, :integer, null: false + t.column :updated_on, :timestamp, null: false end - add_index(:group_order_articles, [:group_order_id, :order_article_id], :unique => true, :name => "goa_index") + add_index(:group_order_articles, %i[group_order_id order_article_id], unique: true, name: 'goa_index') # GroupOrderArticleQuantity create_table :group_order_article_quantities do |t| - t.column :group_order_article_id, :int, :null => false - t.column :quantity, :int, :default => 0 - t.column :tolerance, :int, :default => 0 - t.column :created_on, :timestamp, :null => false + t.column :group_order_article_id, :int, null: false + t.column :quantity, :int, default: 0 + t.column :tolerance, :int, default: 0 + t.column :created_on, :timestamp, null: false end puts 'Adding articles to group order...' - order.order_articles.each { |orderArticle| + order.order_articles.each do |orderArticle| puts "Article #{orderArticle.article.name}..." - GroupOrderArticle.create(:group_order => groupOrder, :order_article => orderArticle, :quantity => 0, :tolerance => 0) - raise 'Failed to create order!' unless article = GroupOrderArticle.find(:first, :conditions => "group_order_id = #{groupOrder.id} AND order_article_id = #{orderArticle.id}") + GroupOrderArticle.create(group_order: groupOrder, order_article: orderArticle, quantity: 0, + tolerance: 0) + raise 'Failed to create order!' unless article = GroupOrderArticle.find(:first, + conditions: "group_order_id = #{groupOrder.id} AND order_article_id = #{orderArticle.id}") - article.updateQuantities(rand(6) + 1, rand(4) + 1) - } + article.updateQuantities(rand(1..6), rand(1..4)) + end raise 'Failed to create orders!' unless groupOrder.order_articles.size == order.order_articles.size groupOrder.updatePrice diff --git a/db/migrate/009_create_order_results.rb b/db/migrate/009_create_order_results.rb index 20b75193..6b1cc65a 100644 --- a/db/migrate/009_create_order_results.rb +++ b/db/migrate/009_create_order_results.rb @@ -1,32 +1,32 @@ class CreateOrderResults < ActiveRecord::Migration[4.2] def self.up create_table :group_order_results do |t| - t.column :order_id, :int, :null => false - t.column :group_name, :string, :null => false - t.column :price, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 + t.column :order_id, :int, null: false + t.column :group_name, :string, null: false + t.column :price, :decimal, precision: 8, scale: 2, null: false, default: 0 end - add_index(:group_order_results, [:group_name, :order_id], :unique => true) + add_index(:group_order_results, %i[group_name order_id], unique: true) create_table :order_article_results do |t| - t.column :order_id, :int, :null => false - t.column :name, :string, :null => false - t.column :unit, :string, :null => false + t.column :order_id, :int, null: false + t.column :name, :string, null: false + t.column :unit, :string, null: false t.column :note, :string - t.column :clear_price, :decimal, :precision => 8, :scale => 2, :null => false - t.column :gross_price, :decimal, :precision => 8, :scale => 2, :null => false - t.column :tax, :float, :null => false, :default => 0 - t.column :refund, :decimal, :precision => 8, :scale => 2 - t.column :fc_markup, :float, :null => false + t.column :clear_price, :decimal, precision: 8, scale: 2, null: false + t.column :gross_price, :decimal, precision: 8, scale: 2, null: false + t.column :tax, :float, null: false, default: 0 + t.column :refund, :decimal, precision: 8, scale: 2 + t.column :fc_markup, :float, null: false t.column :order_number, :string - t.column :unit_quantity, :int, :null => false - t.column :units_to_order, :int, :null => false + t.column :unit_quantity, :int, null: false + t.column :units_to_order, :int, null: false end add_index(:order_article_results, :order_id) create_table :group_order_article_results do |t| - t.column :order_article_result_id, :int, :null => false - t.column :group_order_result_id, :int, :null => false - t.column :quantity, :int, :null => false + t.column :order_article_result_id, :int, null: false + t.column :group_order_result_id, :int, null: false + t.column :quantity, :int, null: false t.column :tolerance, :int end add_index(:group_order_article_results, :order_article_result_id) diff --git a/db/migrate/011_create_comments.rb b/db/migrate/011_create_comments.rb index 28fc0428..55148d08 100644 --- a/db/migrate/011_create_comments.rb +++ b/db/migrate/011_create_comments.rb @@ -1,16 +1,16 @@ class CreateComments < ActiveRecord::Migration[4.2] def self.up - create_table :comments, :force => true do |t| - t.column :title, :string, :limit => 50, :default => "" - t.column :comment, :string, :default => "" - t.column :created_at, :datetime, :null => false - t.column :commentable_id, :integer, :default => 0, :null => false - t.column :commentable_type, :string, :limit => 15, - :default => "", :null => false - t.column :user_id, :integer, :default => 0, :null => false + create_table :comments, force: true do |t| + t.column :title, :string, limit: 50, default: '' + t.column :comment, :string, default: '' + t.column :created_at, :datetime, null: false + t.column :commentable_id, :integer, default: 0, null: false + t.column :commentable_type, :string, limit: 15, + default: '', null: false + t.column :user_id, :integer, default: 0, null: false end - add_index :comments, ["user_id"], :name => "fk_comments_user" + add_index :comments, ['user_id'], name: 'fk_comments_user' end def self.down diff --git a/db/migrate/012_create_order_clearing.rb b/db/migrate/012_create_order_clearing.rb index 1d3133fd..9ddb4ad3 100644 --- a/db/migrate/012_create_order_clearing.rb +++ b/db/migrate/012_create_order_clearing.rb @@ -1,8 +1,8 @@ class CreateOrderClearing < ActiveRecord::Migration[4.2] def self.up - add_column :orders, :invoice_amount, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 - add_column :orders, :refund, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 - add_column :orders, :refund_credit, :decimal, :precision => 8, :scale => 2, :null => false, :default => 0 + add_column :orders, :invoice_amount, :decimal, precision: 8, scale: 2, null: false, default: 0 + add_column :orders, :refund, :decimal, precision: 8, scale: 2, null: false, default: 0 + add_column :orders, :refund_credit, :decimal, precision: 8, scale: 2, null: false, default: 0 add_column :orders, :invoice_number, :string add_column :orders, :invoice_date, :string end diff --git a/db/migrate/013_add_messaging.rb b/db/migrate/013_add_messaging.rb index 84ba8d6f..7f01b2dd 100644 --- a/db/migrate/013_add_messaging.rb +++ b/db/migrate/013_add_messaging.rb @@ -3,13 +3,13 @@ class AddMessaging < ActiveRecord::Migration[4.2] # Table that holds the messages: create_table :messages do |t| t.column :sender_id, :integer - t.column :recipient_id, :integer, :null => false - t.column :recipients, :string, :null => false - t.column :subject, :string, :null => false - t.column :body, :text, :null => false - t.column :read, :boolean, :null => false, :default => false - t.column :email_state, :integer, :null => false - t.column :created_on, :timestamp, :null => false + t.column :recipient_id, :integer, null: false + t.column :recipients, :string, null: false + t.column :subject, :string, null: false + t.column :body, :text, null: false + t.column :read, :boolean, null: false, default: false + t.column :email_state, :integer, null: false + t.column :created_on, :timestamp, null: false end add_index(:messages, :sender_id) add_index(:messages, :recipient_id) diff --git a/db/migrate/014_create_tasks.rb b/db/migrate/014_create_tasks.rb index db878546..8872523e 100644 --- a/db/migrate/014_create_tasks.rb +++ b/db/migrate/014_create_tasks.rb @@ -1,26 +1,26 @@ class CreateTasks < ActiveRecord::Migration[4.2] def self.up create_table :tasks do |t| - t.column :name, :string, :null => false + t.column :name, :string, null: false t.column :description, :string t.column :due_date, :date - t.column :done, :boolean, :default => false + t.column :done, :boolean, default: false t.column :group_id, :integer - t.column :assigned, :boolean, :default => false - t.column :created_on, :datetime, :null => false - t.column :updated_on, :datetime, :null => false + t.column :assigned, :boolean, default: false + t.column :created_on, :datetime, null: false + t.column :updated_on, :datetime, null: false end add_index :tasks, :name add_index :tasks, :due_date create_table :assignments do |t| - t.column :user_id, :integer, :null => false - t.column :task_id, :integer, :null => false - t.column :accepted, :boolean, :default => false + t.column :user_id, :integer, null: false + t.column :task_id, :integer, null: false + t.column :accepted, :boolean, default: false end - add_index :assignments, [:user_id, :task_id], :unique => true + add_index :assignments, %i[user_id task_id], unique: true - add_column :groups, :weekly_task, :boolean, :default => false # if group has an job for every week + add_column :groups, :weekly_task, :boolean, default: false # if group has an job for every week add_column :groups, :weekday, :integer # e.g. 1 means monday, 2 = tuesday an so on add_column :groups, :task_name, :string # the name of the weekly task add_column :groups, :task_description, :string diff --git a/db/migrate/015_change_result_quantities.rb b/db/migrate/015_change_result_quantities.rb index 23731334..56020eab 100644 --- a/db/migrate/015_change_result_quantities.rb +++ b/db/migrate/015_change_result_quantities.rb @@ -1,11 +1,11 @@ class ChangeResultQuantities < ActiveRecord::Migration[4.2] def self.up - change_column :group_order_article_results, :quantity, :decimal, :precision => 6, :scale => 3 - change_column :order_article_results, :units_to_order, :decimal, :precision => 6, :scale => 3, :null => false + change_column :group_order_article_results, :quantity, :decimal, precision: 6, scale: 3 + change_column :order_article_results, :units_to_order, :decimal, precision: 6, scale: 3, null: false end def self.down - change_column :group_order_article_results, :quantity, :integer, :null => false - change_column :order_article_results, :units_to_order, :integer, :default => 0, :null => false + change_column :group_order_article_results, :quantity, :integer, null: false + change_column :order_article_results, :units_to_order, :integer, default: 0, null: false end end diff --git a/db/migrate/018_create_invites.rb b/db/migrate/018_create_invites.rb index cc8a1ebc..49c3edf9 100644 --- a/db/migrate/018_create_invites.rb +++ b/db/migrate/018_create_invites.rb @@ -1,11 +1,11 @@ class CreateInvites < ActiveRecord::Migration[4.2] def self.up create_table :invites do |t| - t.column :token, :string, :null => false - t.column :expires_at, :timestamp, :null => false - t.column :group_id, :integer, :null => false - t.column :user_id, :integer, :null => false - t.column :email, :string, :null => false + t.column :token, :string, null: false + t.column :expires_at, :timestamp, null: false + t.column :group_id, :integer, null: false + t.column :user_id, :integer, null: false + t.column :email, :string, null: false end add_index :invites, :token end diff --git a/db/migrate/019_remove_uniqueness_of_article_name.rb b/db/migrate/019_remove_uniqueness_of_article_name.rb index 7504a66a..50170cb8 100644 --- a/db/migrate/019_remove_uniqueness_of_article_name.rb +++ b/db/migrate/019_remove_uniqueness_of_article_name.rb @@ -1,11 +1,11 @@ class RemoveUniquenessOfArticleName < ActiveRecord::Migration[4.2] def self.up remove_index :articles, :name - add_index :articles, [:name, :supplier_id] + add_index :articles, %i[name supplier_id] end def self.down - remove_index :articles, [:name, :supplier_id] - add_index :articles, :name, :unique => true + remove_index :articles, %i[name supplier_id] + add_index :articles, :name, unique: true end end diff --git a/db/migrate/021_remove_table_article_prices.rb b/db/migrate/021_remove_table_article_prices.rb index 7f172065..9b586fca 100644 --- a/db/migrate/021_remove_table_article_prices.rb +++ b/db/migrate/021_remove_table_article_prices.rb @@ -1,19 +1,19 @@ class RemoveTableArticlePrices < ActiveRecord::Migration[4.2] def self.up - puts "create columns in articles ..." - add_column "articles", "clear_price", :decimal, :precision => 8, :scale => 2, :default => 0.0, :null => false - add_column "articles", "gross_price", :decimal, :precision => 8, :scale => 2, :default => 0.0, :null => false - add_column "articles", "tax", :float - add_column "articles", "refund", :decimal, :precision => 8, :scale => 2, :default => 0.0, :null => false - add_column "articles", "unit_quantity", :integer, :default => 1, :null => false - add_column "articles", "order_number", :string - add_column "articles", "created_at", :datetime - add_column "articles", "updated_at", :datetime + puts 'create columns in articles ...' + add_column 'articles', 'clear_price', :decimal, precision: 8, scale: 2, default: 0.0, null: false + add_column 'articles', 'gross_price', :decimal, precision: 8, scale: 2, default: 0.0, null: false + add_column 'articles', 'tax', :float + add_column 'articles', 'refund', :decimal, precision: 8, scale: 2, default: 0.0, null: false + add_column 'articles', 'unit_quantity', :integer, default: 1, null: false + add_column 'articles', 'order_number', :string + add_column 'articles', 'created_at', :datetime + add_column 'articles', 'updated_at', :datetime # stop auto-updating the timestamps to make the data-copy safe! Article.record_timestamps = false - puts "now copy values of article_prices into new articles-columns..." + puts 'now copy values of article_prices into new articles-columns...' Article.find(:all).each do |article| price = article.current_price article.update!(clear_price: price.clear_price, @@ -26,46 +26,46 @@ class RemoveTableArticlePrices < ActiveRecord::Migration[4.2] created_at: price.updated_on) end - puts "delete article_prices, current_price attribute" + puts 'delete article_prices, current_price attribute' drop_table :article_prices remove_column :articles, :current_price_id end def self.down add_column :articles, :current_price_id, :integer - create_table "article_prices", :force => true do |t| - t.integer "article_id", :default => 0, :null => false - t.decimal "clear_price", :precision => 8, :scale => 2, :default => 0.0, :null => false - t.decimal "gross_price", :precision => 8, :scale => 2, :default => 0.0, :null => false - t.float "tax", :default => 0.0, :null => false - t.decimal "refund", :precision => 8, :scale => 2, :default => 0.0, :null => false - t.datetime "updated_on" - t.integer "unit_quantity", :default => 1, :null => false - t.string "order_number" + create_table 'article_prices', force: true do |t| + t.integer 'article_id', default: 0, null: false + t.decimal 'clear_price', precision: 8, scale: 2, default: 0.0, null: false + t.decimal 'gross_price', precision: 8, scale: 2, default: 0.0, null: false + t.float 'tax', default: 0.0, null: false + t.decimal 'refund', precision: 8, scale: 2, default: 0.0, null: false + t.datetime 'updated_on' + t.integer 'unit_quantity', default: 1, null: false + t.string 'order_number' end # copy data from article now into old ArticlePrice-object Article.find(:all).each do |article| - price = ArticlePrice.create(:clear_price => article.clear_price, - :gross_price => article.gross_price, - :tax => article.tax, - :refund => article.refund, - :unit_quantity => article.unit_quantity, - :order_number => article.order_number.blank? ? nil : article.order_number, - :updated_on => article.updated_at) + price = ArticlePrice.create(clear_price: article.clear_price, + gross_price: article.gross_price, + tax: article.tax, + refund: article.refund, + unit_quantity: article.unit_quantity, + order_number: article.order_number.presence, + updated_on: article.updated_at) article.update_attribute(:current_price, price) price.update_attribute(:article, article) end # remove new columns - remove_column "articles", "clear_price" - remove_column "articles", "gross_price" - remove_column "articles", "tax" - remove_column "articles", "refund" - remove_column "articles", "unit_quantity" - remove_column "articles", "order_number" - remove_column "articles", "created_at" - remove_column "articles", "updated_at" + remove_column 'articles', 'clear_price' + remove_column 'articles', 'gross_price' + remove_column 'articles', 'tax' + remove_column 'articles', 'refund' + remove_column 'articles', 'unit_quantity' + remove_column 'articles', 'order_number' + remove_column 'articles', 'created_at' + remove_column 'articles', 'updated_at' end end diff --git a/db/migrate/022_add_required_user_for_task.rb b/db/migrate/022_add_required_user_for_task.rb index 9e8d9621..105e1593 100644 --- a/db/migrate/022_add_required_user_for_task.rb +++ b/db/migrate/022_add_required_user_for_task.rb @@ -1,7 +1,7 @@ class AddRequiredUserForTask < ActiveRecord::Migration[4.2] def self.up - add_column :tasks, :required_users, :integer, :default => 1 - add_column :groups, :task_required_users, :integer, :default => 1 + add_column :tasks, :required_users, :integer, default: 1 + add_column :groups, :task_required_users, :integer, default: 1 # add default values to every task and group Task.find(:all).each { |task| task.update_attribute :required_users, 1 } Group.workgroups.each { |group| group.update_attribute :task_required_users, 1 } diff --git a/db/migrate/024_add_deposit_defaults.rb b/db/migrate/024_add_deposit_defaults.rb index ed9da063..68afdf5a 100644 --- a/db/migrate/024_add_deposit_defaults.rb +++ b/db/migrate/024_add_deposit_defaults.rb @@ -7,6 +7,5 @@ class AddDepositDefaults < ActiveRecord::Migration[4.2] change_column_default :orders, :deposit_credit, 0.0 end - def self.down - end + def self.down; end end diff --git a/db/migrate/025_extend_comments.rb b/db/migrate/025_extend_comments.rb index 662b92fd..3b1b1da2 100644 --- a/db/migrate/025_extend_comments.rb +++ b/db/migrate/025_extend_comments.rb @@ -1,9 +1,9 @@ class ExtendComments < ActiveRecord::Migration[4.2] def self.up - change_column :comments, :comment, :text, :default => "" + change_column :comments, :comment, :text, default: '' end def self.down - change_column :comments, :comment, :string, :default => "" + change_column :comments, :comment, :string, default: '' end end diff --git a/db/migrate/20090120184410_road_to_version_three.rb b/db/migrate/20090120184410_road_to_version_three.rb index 4bacff24..d271aee3 100644 --- a/db/migrate/20090120184410_road_to_version_three.rb +++ b/db/migrate/20090120184410_road_to_version_three.rb @@ -10,10 +10,10 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] create_table :messages do |t| t.references :sender t.text :recipients_ids - t.string :subject, :null => false + t.string :subject, null: false t.text :body - t.integer :email_state, :default => 0, :null => false - t.boolean :private, :default => false + t.integer :email_state, default: 0, null: false + t.boolean :private, default: false t.datetime :created_at end @@ -23,9 +23,9 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] add_column :groups, :deleted_at, :datetime # == Workgroups - puts "Migrate all groups to workgroups.." - Group.find(:all, :conditions => { :type => "" }).each do |workgroup| - workgroup.update_attribute(:type, "Workgroup") + puts 'Migrate all groups to workgroups..' + Group.find(:all, conditions: { type: '' }).each do |workgroup| + workgroup.update_attribute(:type, 'Workgroup') end # == Ordergroups @@ -34,11 +34,11 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] rename_column :financial_transactions, :order_group_id, :ordergroup_id rename_column :group_orders, :order_group_id, :ordergroup_id rename_column :tasks, :group_id, :workgroup_id - remove_index :group_orders, :name => "index_group_orders_on_order_group_id_and_order_id" - add_index :group_orders, [:ordergroup_id, :order_id], :unique => true + remove_index :group_orders, name: 'index_group_orders_on_order_group_id_and_order_id' + add_index :group_orders, %i[ordergroup_id order_id], unique: true - Group.find(:all, :conditions => { :type => "OrderGroup" }).each do |ordergroup| - ordergroup.update_attribute(:type, "Ordergroup") + Group.find(:all, conditions: { type: 'OrderGroup' }).each do |ordergroup| + ordergroup.update_attribute(:type, 'Ordergroup') end # move contact-infos from users to ordergroups add_column :groups, :contact_person, :string @@ -46,9 +46,7 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] add_column :groups, :contact_address, :string Ordergroup.all.each do |ordergroup| contact = ordergroup.users.first - if contact - ordergroup.update(contact_person: contact.name, contact_phone: contact.phone, contact_address: contact.address) - end + ordergroup.update(contact_person: contact.name, contact_phone: contact.phone, contact_address: contact.address) if contact end remove_column :users, :address @@ -57,15 +55,18 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] drop_table :group_order_results drop_table :order_article_results drop_table :group_order_article_results - GroupOrder.delete_all; OrderArticle.delete_all; GroupOrderArticle.delete_all; GroupOrderArticleQuantity.delete_all + GroupOrder.delete_all + OrderArticle.delete_all + GroupOrderArticle.delete_all + GroupOrderArticleQuantity.delete_all create_table :orders do |t| t.references :supplier t.text :note t.datetime :starts t.datetime :ends - t.string :state, :default => "open" # Statemachine ... open -> finished -> closed - t.integer :lock_version, :default => 0, :null => false + t.string :state, default: 'open' # Statemachine ... open -> finished -> closed + t.integer :lock_version, default: 0, null: false t.integer :updated_by_user_id end @@ -78,9 +79,9 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] t.date :date t.date :paid_on t.text :note - t.decimal :amount, :null => false, :precision => 8, :scale => 2, :default => 0.0 - t.decimal :deposit, :precision => 8, :scale => 2, :default => 0.0, :null => false - t.decimal :deposit_credit, :precision => 8, :scale => 2, :default => 0.0, :null => false + t.decimal :amount, null: false, precision: 8, scale: 2, default: 0.0 + t.decimal :deposit, precision: 8, scale: 2, default: 0.0, null: false + t.decimal :deposit_credit, precision: 8, scale: 2, default: 0.0, null: false t.timestamps end @@ -107,41 +108,41 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] # == ArticlePrice create_table :article_prices do |t| t.references :article - t.decimal :price, :precision => 8, :scale => 2, :default => 0.0, :null => false - t.decimal :tax, :precision => 8, :scale => 2, :default => 0.0, :null => false - t.decimal :deposit, :precision => 8, :scale => 2, :default => 0.0, :null => false + t.decimal :price, precision: 8, scale: 2, default: 0.0, null: false + t.decimal :tax, precision: 8, scale: 2, default: 0.0, null: false + t.decimal :deposit, precision: 8, scale: 2, default: 0.0, null: false t.integer :unit_quantity t.datetime :created_at end # Create price history for every Article Article.all.each do |a| - a.article_prices.create :price => a.price, :tax => a.tax, - :deposit => a.deposit, :unit_quantity => a.unit_quantity + a.article_prices.create price: a.price, tax: a.tax, + deposit: a.deposit, unit_quantity: a.unit_quantity end # Every Article has now a Category. Fix it if neccessary. - Article.all(:conditions => { :article_category_id => nil }).each do |article| + Article.all(conditions: { article_category_id: nil }).each do |article| article.update_attribute(:article_category, ArticleCategory.first) end # order-articles add_column :order_articles, :article_price_id, :integer # == GroupOrder - change_column :group_orders, :updated_by_user_id, :integer, :default => nil, :null => true + change_column :group_orders, :updated_by_user_id, :integer, default: nil, null: true # == GroupOrderArticle # The total order result in ordergroup is now saved! - add_column :group_order_articles, :result, :integer, :default => nil + add_column :group_order_articles, :result, :integer, default: nil # == StockArticle add_column :articles, :type, :string - add_column :articles, :quantity, :integer, :default => 0 + add_column :articles, :quantity, :integer, default: 0 # == StockChanges create_table :stock_changes do |t| t.references :delivery t.references :order t.references :stock_article - t.integer :quantity, :default => 0 + t.integer :quantity, default: 0 t.datetime :created_at end @@ -158,6 +159,5 @@ class RoadToVersionThree < ActiveRecord::Migration[4.2] User.all.each { |u| u.settings['notify.upcoming_tasks'] = 1 } end - def self.down - end + def self.down; end end diff --git a/db/migrate/20090317175355_add_profit_to_orders.rb b/db/migrate/20090317175355_add_profit_to_orders.rb index 59f79609..78013f7d 100644 --- a/db/migrate/20090317175355_add_profit_to_orders.rb +++ b/db/migrate/20090317175355_add_profit_to_orders.rb @@ -1,6 +1,6 @@ class AddProfitToOrders < ActiveRecord::Migration[4.2] def self.up - add_column :orders, :foodcoop_result, :decimal, :precision => 8, :scale => 2 + add_column :orders, :foodcoop_result, :decimal, precision: 8, scale: 2 Order.closed.each do |order| order.update_attribute(:foodcoop_result, order.profit) diff --git a/db/migrate/20090325175756_create_pages.foodsoft_wiki_engine.rb b/db/migrate/20090325175756_create_pages.foodsoft_wiki_engine.rb index c5692f35..d2221013 100644 --- a/db/migrate/20090325175756_create_pages.foodsoft_wiki_engine.rb +++ b/db/migrate/20090325175756_create_pages.foodsoft_wiki_engine.rb @@ -5,7 +5,7 @@ class CreatePages < ActiveRecord::Migration[4.2] t.string :title t.text :body t.string :permalink - t.integer :lock_version, :default => 0 + t.integer :lock_version, default: 0 t.integer :updated_by t.integer :redirect t.integer :parent_id diff --git a/db/migrate/20090405131156_modify_group_order_article_result.rb b/db/migrate/20090405131156_modify_group_order_article_result.rb index 44e0dea8..b979422e 100644 --- a/db/migrate/20090405131156_modify_group_order_article_result.rb +++ b/db/migrate/20090405131156_modify_group_order_article_result.rb @@ -1,6 +1,6 @@ class ModifyGroupOrderArticleResult < ActiveRecord::Migration[4.2] def self.up - change_column :group_order_articles, :result, :decimal, :precision => 8, :scale => 3 + change_column :group_order_articles, :result, :decimal, precision: 8, scale: 3 end def self.down diff --git a/db/migrate/20090907120012_add_missing_indexes.rb b/db/migrate/20090907120012_add_missing_indexes.rb index 189b0417..93ea8771 100644 --- a/db/migrate/20090907120012_add_missing_indexes.rb +++ b/db/migrate/20090907120012_add_missing_indexes.rb @@ -1,35 +1,34 @@ class AddMissingIndexes < ActiveRecord::Migration[4.2] def self.up - add_index "article_prices", ["article_id"] + add_index 'article_prices', ['article_id'] - add_index "articles", ["supplier_id"] - add_index "articles", ["article_category_id"] - add_index "articles", ["type"] + add_index 'articles', ['supplier_id'] + add_index 'articles', ['article_category_id'] + add_index 'articles', ['type'] - add_index "deliveries", ["supplier_id"] + add_index 'deliveries', ['supplier_id'] - add_index "financial_transactions", ["ordergroup_id"] + add_index 'financial_transactions', ['ordergroup_id'] - add_index "group_order_article_quantities", ["group_order_article_id"] - add_index "group_orders", ["order_id"] - add_index "group_orders", ["ordergroup_id"] + add_index 'group_order_article_quantities', ['group_order_article_id'] + add_index 'group_orders', ['order_id'] + add_index 'group_orders', ['ordergroup_id'] - add_index "invoices", ["supplier_id"] - add_index "invoices", ["delivery_id"] + add_index 'invoices', ['supplier_id'] + add_index 'invoices', ['delivery_id'] - add_index "order_articles", ["order_id"] + add_index 'order_articles', ['order_id'] - add_index "order_comments", ["order_id"] + add_index 'order_comments', ['order_id'] - add_index "orders", ["state"] + add_index 'orders', ['state'] - add_index "stock_changes", ["delivery_id"] - add_index "stock_changes", ["stock_article_id"] - add_index "stock_changes", ["stock_taking_id"] + add_index 'stock_changes', ['delivery_id'] + add_index 'stock_changes', ['stock_article_id'] + add_index 'stock_changes', ['stock_taking_id'] - add_index "tasks", ["workgroup_id"] + add_index 'tasks', ['workgroup_id'] end - def self.down - end + def self.down; end end diff --git a/db/migrate/20110507184920_add_duration_to_tasks.rb b/db/migrate/20110507184920_add_duration_to_tasks.rb index 33a11494..86347508 100644 --- a/db/migrate/20110507184920_add_duration_to_tasks.rb +++ b/db/migrate/20110507184920_add_duration_to_tasks.rb @@ -1,6 +1,6 @@ class AddDurationToTasks < ActiveRecord::Migration[4.2] def self.up - add_column :tasks, :duration, :integer, :default => 1 + add_column :tasks, :duration, :integer, default: 1 end def self.down diff --git a/db/migrate/20110507192928_add_task_duration_to_workgroups.rb b/db/migrate/20110507192928_add_task_duration_to_workgroups.rb index c5b4844b..fd703d17 100644 --- a/db/migrate/20110507192928_add_task_duration_to_workgroups.rb +++ b/db/migrate/20110507192928_add_task_duration_to_workgroups.rb @@ -1,6 +1,6 @@ class AddTaskDurationToWorkgroups < ActiveRecord::Migration[4.2] def self.up - add_column :groups, :task_duration, :integer, :default => 1 + add_column :groups, :task_duration, :integer, default: 1 end def self.down diff --git a/db/migrate/20120622094337_add_next_weekly_tasks_number_to_workgroups.rb b/db/migrate/20120622094337_add_next_weekly_tasks_number_to_workgroups.rb index b8ac8c81..eeca92b3 100644 --- a/db/migrate/20120622094337_add_next_weekly_tasks_number_to_workgroups.rb +++ b/db/migrate/20120622094337_add_next_weekly_tasks_number_to_workgroups.rb @@ -1,6 +1,6 @@ class AddNextWeeklyTasksNumberToWorkgroups < ActiveRecord::Migration[4.2] def self.up - add_column :groups, :next_weekly_tasks_number, :integer, :default => 8 + add_column :groups, :next_weekly_tasks_number, :integer, default: 8 end def self.down diff --git a/db/migrate/20130622095040_move_weekly_tasks.rb b/db/migrate/20130622095040_move_weekly_tasks.rb index 3865a498..d57f3108 100644 --- a/db/migrate/20130622095040_move_weekly_tasks.rb +++ b/db/migrate/20130622095040_move_weekly_tasks.rb @@ -15,21 +15,21 @@ class MoveWeeklyTasks < ActiveRecord::Migration[4.2] def down PeriodicTaskGroup.all.each do |task_group| - unless task_group.tasks.empty? - task = task_group.tasks.first - workgroup = task.workgroup - puts "Writing task data of group #{task_group.id} to workgroup #{workgroup.name}" - workgroup_attributes = { - weekly_task: true, - weekday: task.due_date.days_to_week_start(:sunday), - task_name: task.name, - task_description: task.description, - task_required_users: task.required_users, - task_duration: task.duration - } - workgroup.update(workgroup_attributes) - task_group.tasks.update_all weekly: true - end + next if task_group.tasks.empty? + + task = task_group.tasks.first + workgroup = task.workgroup + puts "Writing task data of group #{task_group.id} to workgroup #{workgroup.name}" + workgroup_attributes = { + weekly_task: true, + weekday: task.due_date.days_to_week_start(:sunday), + task_name: task.name, + task_description: task.description, + task_required_users: task.required_users, + task_duration: task.duration + } + workgroup.update(workgroup_attributes) + task_group.tasks.update_all weekly: true end end diff --git a/db/migrate/20130702113610_update_group_order_totals.rb b/db/migrate/20130702113610_update_group_order_totals.rb index da57126a..52edbad4 100644 --- a/db/migrate/20130702113610_update_group_order_totals.rb +++ b/db/migrate/20130702113610_update_group_order_totals.rb @@ -1,18 +1,17 @@ class UpdateGroupOrderTotals < ActiveRecord::Migration[4.2] def self.up say "If you have ever modified an order after it was settled, the group_order's " + - "price may be calculated incorrectly. This can take a lot of time on a " + - "large database." + 'price may be calculated incorrectly. This can take a lot of time on a ' + + 'large database.' - say "If you do want to update the ordergroup totals, open the rails console " + - "(by running `rails c`), and enter:" + say 'If you do want to update the ordergroup totals, open the rails console ' + + '(by running `rails c`), and enter:' - say "GroupOrder.all.each { |go| go.order.closed? and go.update_price! }", subitem: true + say 'GroupOrder.all.each { |go| go.order.closed? and go.update_price! }', subitem: true - say "You may want to check first that no undesired accounting issues are introduced. " + - "It may be wise to discuss this with those responsible for the ordering finances." + say 'You may want to check first that no undesired accounting issues are introduced. ' + + 'It may be wise to discuss this with those responsible for the ordering finances.' end - def self.down - end + def self.down; end end diff --git a/db/migrate/20130718183100_create_settings.rb b/db/migrate/20130718183100_create_settings.rb index 9928a9b3..90639d71 100644 --- a/db/migrate/20130718183100_create_settings.rb +++ b/db/migrate/20130718183100_create_settings.rb @@ -8,7 +8,7 @@ class CreateSettings < ActiveRecord::Migration[4.2] t.timestamps end - add_index :settings, [:thing_type, :thing_id, :var], unique: true + add_index :settings, %i[thing_type thing_id var], unique: true end def self.down diff --git a/db/migrate/20130718183101_migrate_user_settings.rb b/db/migrate/20130718183101_migrate_user_settings.rb index 04cfaeb8..2d0e3c56 100644 --- a/db/migrate/20130718183101_migrate_user_settings.rb +++ b/db/migrate/20130718183101_migrate_user_settings.rb @@ -46,8 +46,7 @@ class MigrateUserSettings < ActiveRecord::Migration[4.2] drop_table :configurable_settings end - def down - end + def down; end end # this is the base class of all configurable settings diff --git a/db/migrate/20130920201529_allow_missing_nick.rb b/db/migrate/20130920201529_allow_missing_nick.rb index ed818860..fcf1d8c8 100644 --- a/db/migrate/20130920201529_allow_missing_nick.rb +++ b/db/migrate/20130920201529_allow_missing_nick.rb @@ -1,9 +1,9 @@ class AllowMissingNick < ActiveRecord::Migration[4.2] def self.up - change_column :users, :nick, :string, :default => nil, :null => true + change_column :users, :nick, :string, default: nil, null: true end def self.down - change_column :users, :nick, :string, :default => "", :null => false + change_column :users, :nick, :string, default: '', null: false end end diff --git a/db/migrate/20140102170431_add_result_computed_to_group_order_articles.rb b/db/migrate/20140102170431_add_result_computed_to_group_order_articles.rb index dd9fc407..0bb885d9 100644 --- a/db/migrate/20140102170431_add_result_computed_to_group_order_articles.rb +++ b/db/migrate/20140102170431_add_result_computed_to_group_order_articles.rb @@ -1,6 +1,6 @@ class AddResultComputedToGroupOrderArticles < ActiveRecord::Migration[4.2] def change add_column :group_order_articles, :result_computed, - :decimal, :precision => 8, :scale => 3 + :decimal, precision: 8, scale: 3 end end diff --git a/db/migrate/20140318173000_delete_empty_group_order_articles.rb b/db/migrate/20140318173000_delete_empty_group_order_articles.rb index 1e053c3c..c5b396ed 100644 --- a/db/migrate/20140318173000_delete_empty_group_order_articles.rb +++ b/db/migrate/20140318173000_delete_empty_group_order_articles.rb @@ -4,6 +4,5 @@ class DeleteEmptyGroupOrderArticles < ActiveRecord::Migration[4.2] GroupOrderArticle.where(quantity: 0, tolerance: 0, result: [0, nil], result_computed: [0, nil]).delete_all end - def down - end + def down; end end diff --git a/db/migrate/20140921104907_remove_stale_memberships.rb b/db/migrate/20140921104907_remove_stale_memberships.rb index de5b719b..26b6c834 100644 --- a/db/migrate/20140921104907_remove_stale_memberships.rb +++ b/db/migrate/20140921104907_remove_stale_memberships.rb @@ -1,5 +1,5 @@ class RemoveStaleMemberships < ActiveRecord::Migration[4.2] def up - Membership.where("group_id NOT IN (?)", Group.ids).delete_all + Membership.where.not(group_id: Group.ids).delete_all end end diff --git a/db/migrate/20160217194036_add_role_invoices_to_group.rb b/db/migrate/20160217194036_add_role_invoices_to_group.rb index 6946fe05..5a86f425 100644 --- a/db/migrate/20160217194036_add_role_invoices_to_group.rb +++ b/db/migrate/20160217194036_add_role_invoices_to_group.rb @@ -1,5 +1,5 @@ class AddRoleInvoicesToGroup < ActiveRecord::Migration[4.2] def change - add_column :groups, :role_invoices, :boolean, :default => false, :null => false + add_column :groups, :role_invoices, :boolean, default: false, null: false end end diff --git a/db/migrate/20160218151041_add_attachment_to_invoice.rb b/db/migrate/20160218151041_add_attachment_to_invoice.rb index 58bac66d..1767905c 100644 --- a/db/migrate/20160218151041_add_attachment_to_invoice.rb +++ b/db/migrate/20160218151041_add_attachment_to_invoice.rb @@ -1,6 +1,6 @@ class AddAttachmentToInvoice < ActiveRecord::Migration[4.2] def change add_column :invoices, :attachment_mime, :string - add_column :invoices, :attachment_data, :binary, :limit => 8.megabyte + add_column :invoices, :attachment_data, :binary, limit: 8.megabyte end end diff --git a/db/migrate/20160219123220_create_financial_transaction_class_and_types.rb b/db/migrate/20160219123220_create_financial_transaction_class_and_types.rb index 5fcf318b..3c05035d 100644 --- a/db/migrate/20160219123220_create_financial_transaction_class_and_types.rb +++ b/db/migrate/20160219123220_create_financial_transaction_class_and_types.rb @@ -1,12 +1,12 @@ class CreateFinancialTransactionClassAndTypes < ActiveRecord::Migration[4.2] def change create_table :financial_transaction_classes do |t| - t.string :name, :null => false + t.string :name, null: false end create_table :financial_transaction_types do |t| - t.string :name, :null => false - t.references :financial_transaction_class, :null => false + t.string :name, null: false + t.references :financial_transaction_class, null: false end change_table :financial_transactions do |t| @@ -17,7 +17,7 @@ class CreateFinancialTransactionClassAndTypes < ActiveRecord::Migration[4.2] dir.up do execute "INSERT INTO financial_transaction_classes (id, name) VALUES (1, 'Standard')" execute "INSERT INTO financial_transaction_types (id, name, financial_transaction_class_id) VALUES (1, 'Foodsoft', 1)" - execute "UPDATE financial_transactions SET financial_transaction_type_id = 1" + execute 'UPDATE financial_transactions SET financial_transaction_type_id = 1' end end diff --git a/db/migrate/20160224201529_allow_stock_group_order.rb b/db/migrate/20160224201529_allow_stock_group_order.rb index a77879e3..6c9197f0 100644 --- a/db/migrate/20160224201529_allow_stock_group_order.rb +++ b/db/migrate/20160224201529_allow_stock_group_order.rb @@ -1,9 +1,9 @@ class AllowStockGroupOrder < ActiveRecord::Migration[4.2] def self.up - change_column :group_orders, :ordergroup_id, :integer, :default => nil, :null => true + change_column :group_orders, :ordergroup_id, :integer, default: nil, null: true end def self.down - change_column :group_orders, :ordergroup_id, :integer, :default => 0, :null => false + change_column :group_orders, :ordergroup_id, :integer, default: 0, null: false end end diff --git a/db/migrate/20160226000000_add_email_to_message.foodsoft_messages_engine.rb b/db/migrate/20160226000000_add_email_to_message.foodsoft_messages_engine.rb index 95b35273..ceeafa15 100644 --- a/db/migrate/20160226000000_add_email_to_message.foodsoft_messages_engine.rb +++ b/db/migrate/20160226000000_add_email_to_message.foodsoft_messages_engine.rb @@ -2,6 +2,6 @@ class AddEmailToMessage < ActiveRecord::Migration[4.2] def change add_column :messages, :salt, :string - add_column :messages, :received_email, :binary, :limit => 1.megabyte + add_column :messages, :received_email, :binary, limit: 1.megabyte end end diff --git a/db/migrate/20170801000000_create_mail_delivery_status.rb b/db/migrate/20170801000000_create_mail_delivery_status.rb index 2fd40674..69fa1b75 100644 --- a/db/migrate/20170801000000_create_mail_delivery_status.rb +++ b/db/migrate/20170801000000_create_mail_delivery_status.rb @@ -2,8 +2,8 @@ class CreateMailDeliveryStatus < ActiveRecord::Migration[4.2] def change create_table :mail_delivery_status do |t| t.datetime :created_at - t.string :email, :null => false - t.string :message, :null => false + t.string :email, null: false + t.string :message, null: false t.string :attachment_mime t.binary :attachment_data, limit: 16.megabyte diff --git a/db/migrate/20181110000000_create_polls.foodsoft_polls_engine.rb b/db/migrate/20181110000000_create_polls.foodsoft_polls_engine.rb index 990e75f0..120b7eef 100644 --- a/db/migrate/20181110000000_create_polls.foodsoft_polls_engine.rb +++ b/db/migrate/20181110000000_create_polls.foodsoft_polls_engine.rb @@ -24,14 +24,14 @@ class CreatePolls < ActiveRecord::Migration[4.2] t.references :ordergroup t.text :note t.timestamps - t.index [:poll_id, :user_id, :ordergroup_id], unique: true + t.index %i[poll_id user_id ordergroup_id], unique: true end create_table :poll_choices do |t| t.references :poll_vote, null: false t.integer :choice, null: false t.integer :value, null: false - t.index [:poll_vote_id, :choice], unique: true + t.index %i[poll_vote_id choice], unique: true end end end diff --git a/db/migrate/20181120000000_increase_choices_size.foodsoft_polls_engine.rb b/db/migrate/20181120000000_increase_choices_size.foodsoft_polls_engine.rb index d809e3ea..621863dd 100644 --- a/db/migrate/20181120000000_increase_choices_size.foodsoft_polls_engine.rb +++ b/db/migrate/20181120000000_increase_choices_size.foodsoft_polls_engine.rb @@ -1,5 +1,5 @@ class IncreaseChoicesSize < ActiveRecord::Migration[4.2] def up - change_column :polls, :choices, :text, limit: 65535 + change_column :polls, :choices, :text, limit: 65_535 end end diff --git a/db/migrate/20181201000000_create_printer_jobs.foodsoft_printer_engine.rb b/db/migrate/20181201000000_create_printer_jobs.foodsoft_printer_engine.rb index ee7665e4..36d175c5 100644 --- a/db/migrate/20181201000000_create_printer_jobs.foodsoft_printer_engine.rb +++ b/db/migrate/20181201000000_create_printer_jobs.foodsoft_printer_engine.rb @@ -15,6 +15,6 @@ class CreatePrinterJobs < ActiveRecord::Migration[4.2] t.text :message end - add_index :printer_job_updates, [:printer_job_id, :created_at] + add_index :printer_job_updates, %i[printer_job_id created_at] end end diff --git a/db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb b/db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb index e931f748..b1d0d51c 100644 --- a/db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb +++ b/db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb @@ -14,7 +14,7 @@ class CreateMessageRecipients < ActiveRecord::Migration[4.2] t.datetime :read_at end - add_index :message_recipients, [:user_id, :read_at] + add_index :message_recipients, %i[user_id read_at] Message.all.each do |m| recipients = YAML.load(m.recipients_ids).map do |r| diff --git a/db/migrate/20190101000000_create_active_storage_tables.active_storage.rb b/db/migrate/20190101000000_create_active_storage_tables.active_storage.rb index 3739c2e8..df49a7b0 100644 --- a/db/migrate/20190101000000_create_active_storage_tables.active_storage.rb +++ b/db/migrate/20190101000000_create_active_storage_tables.active_storage.rb @@ -20,7 +20,8 @@ class CreateActiveStorageTables < ActiveRecord::Migration[4.2][5.2] t.datetime :created_at, null: false - t.index [:record_type, :record_id, :name, :blob_id], name: "index_active_storage_attachments_uniqueness", unique: true + t.index %i[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', + unique: true t.foreign_key :active_storage_blobs, column: :blob_id end end diff --git a/db/migrate/20210205090257_introduce_received_state_in_orders.rb b/db/migrate/20210205090257_introduce_received_state_in_orders.rb index ffeff588..ca7ce999 100644 --- a/db/migrate/20210205090257_introduce_received_state_in_orders.rb +++ b/db/migrate/20210205090257_introduce_received_state_in_orders.rb @@ -1,7 +1,7 @@ class IntroduceReceivedStateInOrders < ActiveRecord::Migration[5.2] def up Order.where(state: 'finished').each do |order| - order.update_attribute(:state, 'received') if order.order_articles.where('units_received IS NOT NULL').any? + order.update_attribute(:state, 'received') if order.order_articles.where.not(units_received: nil).any? end end diff --git a/db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb b/db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb new file mode 100644 index 00000000..379003ed --- /dev/null +++ b/db/migrate/20230106144438_add_service_name_to_active_storage_blobs.active_storage.rb @@ -0,0 +1,22 @@ +# This migration comes from active_storage (originally 20190112182829) +class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0] + def up + return unless table_exists?(:active_storage_blobs) + + return if column_exists?(:active_storage_blobs, :service_name) + + add_column :active_storage_blobs, :service_name, :string + + if configured_service = ActiveStorage::Blob.service.name + ActiveStorage::Blob.unscoped.update_all(service_name: configured_service) + end + + change_column :active_storage_blobs, :service_name, :string, null: false + end + + def down + return unless table_exists?(:active_storage_blobs) + + remove_column :active_storage_blobs, :service_name + end +end diff --git a/db/migrate/20230106144439_create_active_storage_variant_records.active_storage.rb b/db/migrate/20230106144439_create_active_storage_variant_records.active_storage.rb new file mode 100644 index 00000000..58cc779a --- /dev/null +++ b/db/migrate/20230106144439_create_active_storage_variant_records.active_storage.rb @@ -0,0 +1,28 @@ +# This migration comes from active_storage (originally 20191206030411) +class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + # Use Active Record's configured type for primary key + create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t| + t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type + t.string :variation_digest, null: false + + t.index %i[blob_id variation_digest], name: 'index_active_storage_variant_records_uniqueness', unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end + + private + + def primary_key_type + config = Rails.configuration.generators + config.options[config.orm][:primary_key_type] || :primary_key + end + + def blobs_primary_key_type + pkey_name = connection.primary_key(:active_storage_blobs) + pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name } + pkey_column.bigint? ? :bigint : pkey_column.type + end +end diff --git a/db/migrate/20230106144440_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb b/db/migrate/20230106144440_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb new file mode 100644 index 00000000..93c8b85a --- /dev/null +++ b/db/migrate/20230106144440_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb @@ -0,0 +1,8 @@ +# This migration comes from active_storage (originally 20211119233751) +class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + change_column_null(:active_storage_blobs, :checksum, true) + end +end diff --git a/db/migrate/20230209105256_create_action_text_tables.action_text.rb b/db/migrate/20230209105256_create_action_text_tables.action_text.rb new file mode 100644 index 00000000..e9c30fac --- /dev/null +++ b/db/migrate/20230209105256_create_action_text_tables.action_text.rb @@ -0,0 +1,27 @@ +# This migration comes from action_text (originally 20180528164100) +class CreateActionTextTables < ActiveRecord::Migration[6.0] + def change + # Use Active Record's configured type for primary and foreign keys + primary_key_type, foreign_key_type = primary_and_foreign_key_types + + create_table :action_text_rich_texts, id: primary_key_type do |t| + t.string :name, null: false + t.text :body, size: :long + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type + + t.timestamps + + t.index [:record_type, :record_id, :name], name: "index_action_text_rich_texts_uniqueness", unique: true + end + end + + private + + def primary_and_foreign_key_types + config = Rails.configuration.generators + setting = config.options[config.orm][:primary_key_type] + primary_key_type = setting || :primary_key + foreign_key_type = setting || :bigint + [primary_key_type, foreign_key_type] + end +end diff --git a/db/migrate/20230215085312_migrate_message_body_to_action_text.rb b/db/migrate/20230215085312_migrate_message_body_to_action_text.rb new file mode 100644 index 00000000..37f8a69c --- /dev/null +++ b/db/migrate/20230215085312_migrate_message_body_to_action_text.rb @@ -0,0 +1,32 @@ +class MigrateMessageBodyToActionText < ActiveRecord::Migration[7.0] + include ActionView::Helpers::TextHelper + + class Message < ApplicationRecord + has_rich_text :body + end + + def change + reversible do |dir| + dir.up do + rename_column :messages, :body, :body_old + Message.all.each do |message| + message.update(body: simple_format(message.body_old)) + message.body.update(record_type: :Message) # action_text_rich_texts uses STI record_type field and has to be set to the real model + end + remove_column :messages, :body_old, :text + end + dir.down do + execute "ALTER TABLE `messages` ADD `body_old` text" + execute "UPDATE `messages` m + INNER JOIN `action_text_rich_texts` a + ON m.id = a.record_id + set m.body_old = a.body" + Message.all.each do |message| + message.update(body_old: strip_tags(message.body_old)) + end + execute "DELETE FROM `action_text_rich_texts` WHERE `action_text_rich_texts`.`record_type` = 'Message'" + execute "ALTER TABLE `messages` CHANGE `body_old` `body` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL;" + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ce812b3f..4c853039 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,54 +2,70 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_02_05_090257) do +ActiveRecord::Schema[7.0].define(version: 2023_02_15_085312) do + create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| + t.string "name", null: false + t.text "body", size: :long + t.string "record_type", null: false + t.bigint "record_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true + end - create_table "active_storage_attachments", id: :integer, force: :cascade do |t| + create_table "active_storage_attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false t.bigint "record_id", null: false t.bigint "blob_id", null: false - t.datetime "created_at", null: false + t.datetime "created_at", precision: nil, null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", id: :integer, force: :cascade do |t| + create_table "active_storage_blobs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "key", null: false t.string "filename", null: false t.string "content_type" t.text "metadata" t.bigint "byte_size", null: false - t.string "checksum", null: false - t.datetime "created_at", null: false + t.string "checksum" + t.datetime "created_at", precision: nil, null: false + t.string "service_name", null: false t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end - create_table "article_categories", id: :integer, force: :cascade do |t| + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| + t.integer "blob_id", null: false + t.string "variation_digest", null: false + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true + end + + create_table "article_categories", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", default: "", null: false t.string "description" t.index ["name"], name: "index_article_categories_on_name", unique: true end - create_table "article_prices", id: :integer, force: :cascade do |t| + create_table "article_prices", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "article_id", null: false t.decimal "price", precision: 8, scale: 2, default: "0.0", null: false t.decimal "tax", precision: 8, scale: 2, default: "0.0", null: false t.decimal "deposit", precision: 8, scale: 2, default: "0.0", null: false t.integer "unit_quantity" - t.datetime "created_at" + t.datetime "created_at", precision: nil t.index ["article_id"], name: "index_article_prices_on_article_id" end - create_table "articles", id: :integer, force: :cascade do |t| + create_table "articles", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", default: "", null: false t.integer "supplier_id", default: 0, null: false t.integer "article_category_id", default: 0, null: false @@ -58,15 +74,15 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.boolean "availability", default: true, null: false t.string "manufacturer" t.string "origin" - t.datetime "shared_updated_on" + t.datetime "shared_updated_on", precision: nil t.decimal "price", precision: 8, scale: 2 t.float "tax" t.decimal "deposit", precision: 8, scale: 2, default: "0.0" t.integer "unit_quantity", default: 1, null: false t.string "order_number" - t.datetime "created_at" - t.datetime "updated_at" - t.datetime "deleted_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.datetime "deleted_at", precision: nil t.string "type" t.integer "quantity", default: 0 t.index ["article_category_id"], name: "index_articles_on_article_category_id" @@ -75,31 +91,31 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["type"], name: "index_articles_on_type" end - create_table "assignments", id: :integer, force: :cascade do |t| + create_table "assignments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "user_id", default: 0, null: false t.integer "task_id", default: 0, null: false t.boolean "accepted", default: false t.index ["user_id", "task_id"], name: "index_assignments_on_user_id_and_task_id", unique: true end - create_table "bank_accounts", id: :integer, force: :cascade do |t| + create_table "bank_accounts", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "iban" t.string "description" t.decimal "balance", precision: 12, scale: 2, default: "0.0", null: false - t.datetime "last_import" + t.datetime "last_import", precision: nil t.string "import_continuation_point" t.integer "bank_gateway_id" end - create_table "bank_gateways", id: :integer, force: :cascade do |t| + create_table "bank_gateways", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "url", null: false t.string "authorization" t.integer "unattended_user_id" end - create_table "bank_transactions", id: :integer, force: :cascade do |t| + create_table "bank_transactions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "bank_account_id", null: false t.string "external_id" t.date "date" @@ -108,32 +124,32 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.string "reference" t.text "text" t.text "receipt" - t.binary "image", limit: 16777215 + t.binary "image", size: :medium t.integer "financial_link_id" t.index ["financial_link_id"], name: "index_bank_transactions_on_financial_link_id" end - create_table "documents", id: :integer, force: :cascade do |t| + create_table "documents", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name" t.string "mime" - t.binary "data", limit: 4294967295 + t.binary "data", size: :long t.integer "created_by_user_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.integer "parent_id" t.index ["parent_id"], name: "index_documents_on_parent_id" end - create_table "financial_links", id: :integer, force: :cascade do |t| + create_table "financial_links", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.text "note" end - create_table "financial_transaction_classes", id: :integer, force: :cascade do |t| + create_table "financial_transaction_classes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.boolean "ignore_for_account_balance", default: false, null: false end - create_table "financial_transaction_types", id: :integer, force: :cascade do |t| + create_table "financial_transaction_types", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.integer "financial_transaction_class_id", null: false t.string "name_short" @@ -141,12 +157,12 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["name_short"], name: "index_financial_transaction_types_on_name_short" end - create_table "financial_transactions", id: :integer, force: :cascade do |t| + create_table "financial_transactions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "ordergroup_id" t.decimal "amount", precision: 8, scale: 2, default: "0.0", null: false t.text "note", null: false t.integer "user_id", default: 0, null: false - t.datetime "created_on", null: false + t.datetime "created_on", precision: nil, null: false t.integer "financial_transaction_type_id", null: false t.integer "financial_link_id" t.integer "reverts_id" @@ -155,20 +171,20 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["reverts_id"], name: "index_financial_transactions_on_reverts_id", unique: true end - create_table "group_order_article_quantities", id: :integer, force: :cascade do |t| + create_table "group_order_article_quantities", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "group_order_article_id", default: 0, null: false t.integer "quantity", default: 0 t.integer "tolerance", default: 0 - t.datetime "created_on", null: false + t.datetime "created_on", precision: nil, null: false t.index ["group_order_article_id"], name: "index_group_order_article_quantities_on_group_order_article_id" end - create_table "group_order_articles", id: :integer, force: :cascade do |t| + create_table "group_order_articles", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "group_order_id", default: 0, null: false t.integer "order_article_id", default: 0, null: false t.integer "quantity", default: 0, null: false t.integer "tolerance", default: 0, null: false - t.datetime "updated_on", null: false + t.datetime "updated_on", precision: nil, null: false t.decimal "result", precision: 8, scale: 3 t.decimal "result_computed", precision: 8, scale: 3 t.index ["group_order_id", "order_article_id"], name: "goa_index", unique: true @@ -176,12 +192,12 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["order_article_id"], name: "index_group_order_articles_on_order_article_id" end - create_table "group_orders", id: :integer, force: :cascade do |t| + create_table "group_orders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "ordergroup_id" t.integer "order_id", default: 0, null: false t.decimal "price", precision: 8, scale: 2, default: "0.0", null: false t.integer "lock_version", default: 0, null: false - t.datetime "updated_on", null: false + t.datetime "updated_on", precision: nil, null: false t.integer "updated_by_user_id" t.decimal "transport", precision: 8, scale: 2 t.index ["order_id"], name: "index_group_orders_on_order_id" @@ -189,18 +205,18 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["ordergroup_id"], name: "index_group_orders_on_ordergroup_id" end - create_table "groups", id: :integer, force: :cascade do |t| + create_table "groups", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "type", default: "", null: false t.string "name", default: "", null: false t.string "description" t.decimal "account_balance", precision: 12, scale: 2, default: "0.0", null: false - t.datetime "created_on", null: false + t.datetime "created_on", precision: nil, null: false t.boolean "role_admin", default: false, null: false t.boolean "role_suppliers", default: false, null: false t.boolean "role_article_meta", default: false, null: false t.boolean "role_finance", default: false, null: false t.boolean "role_orders", default: false, null: false - t.datetime "deleted_at" + t.datetime "deleted_at", precision: nil t.string "contact_person" t.string "contact_phone" t.string "contact_address" @@ -214,16 +230,16 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["name"], name: "index_groups_on_name", unique: true end - create_table "invites", id: :integer, force: :cascade do |t| + create_table "invites", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "token", default: "", null: false - t.datetime "expires_at", null: false + t.datetime "expires_at", precision: nil, null: false t.integer "group_id", default: 0, null: false t.integer "user_id", default: 0, null: false t.string "email", default: "", null: false t.index ["token"], name: "index_invites_on_token" end - create_table "invoices", id: :integer, force: :cascade do |t| + create_table "invoices", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "supplier_id" t.string "number" t.date "date" @@ -232,16 +248,16 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.decimal "amount", precision: 8, scale: 2, default: "0.0", null: false t.decimal "deposit", precision: 8, scale: 2, default: "0.0", null: false t.decimal "deposit_credit", precision: 8, scale: 2, default: "0.0", null: false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.integer "created_by_user_id" t.string "attachment_mime" - t.binary "attachment_data", limit: 16777215 + t.binary "attachment_data", size: :medium t.integer "financial_link_id" t.index ["supplier_id"], name: "index_invoices_on_supplier_id" end - create_table "links", id: :integer, force: :cascade do |t| + create_table "links", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "url", null: false t.integer "workgroup_id" @@ -249,81 +265,80 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.string "authorization" end - create_table "mail_delivery_status", id: :integer, force: :cascade do |t| - t.datetime "created_at" + create_table "mail_delivery_status", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| + t.datetime "created_at", precision: nil t.string "email", null: false t.string "message", null: false t.string "attachment_mime" - t.binary "attachment_data", limit: 4294967295 + t.binary "attachment_data", size: :long t.index ["email"], name: "index_mail_delivery_status_on_email" end - create_table "memberships", id: :integer, force: :cascade do |t| + create_table "memberships", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "group_id", default: 0, null: false t.integer "user_id", default: 0, null: false t.index ["user_id", "group_id"], name: "index_memberships_on_user_id_and_group_id", unique: true end - create_table "message_recipients", id: :integer, force: :cascade do |t| + create_table "message_recipients", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "message_id", null: false t.integer "user_id", null: false t.integer "email_state", default: 0, null: false - t.datetime "read_at" + t.datetime "read_at", precision: nil t.index ["message_id"], name: "index_message_recipients_on_message_id" t.index ["user_id", "read_at"], name: "index_message_recipients_on_user_id_and_read_at" end - create_table "messages", id: :integer, force: :cascade do |t| + create_table "messages", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "sender_id" t.string "subject", null: false - t.text "body" t.boolean "private", default: false - t.datetime "created_at" + t.datetime "created_at", precision: nil t.integer "reply_to" t.integer "group_id" t.string "salt" - t.binary "received_email", limit: 16777215 + t.binary "received_email", size: :medium end - create_table "oauth_access_grants", id: :integer, force: :cascade do |t| + create_table "oauth_access_grants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false t.integer "expires_in", null: false t.text "redirect_uri", null: false - t.datetime "created_at", null: false - t.datetime "revoked_at" + t.datetime "created_at", precision: nil, null: false + t.datetime "revoked_at", precision: nil t.string "scopes" t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true end - create_table "oauth_access_tokens", id: :integer, force: :cascade do |t| + create_table "oauth_access_tokens", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false t.string "refresh_token" t.integer "expires_in" - t.datetime "revoked_at" - t.datetime "created_at", null: false + t.datetime "revoked_at", precision: nil + t.datetime "created_at", precision: nil, null: false t.string "scopes" t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id" t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true end - create_table "oauth_applications", id: :integer, force: :cascade do |t| + create_table "oauth_applications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false t.text "redirect_uri", null: false t.string "scopes", default: "", null: false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.boolean "confidential", default: true, null: false t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end - create_table "order_articles", id: :integer, force: :cascade do |t| + create_table "order_articles", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "order_id", default: 0, null: false t.integer "article_id", default: 0, null: false t.integer "quantity", default: 0, null: false @@ -337,45 +352,45 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["order_id"], name: "index_order_articles_on_order_id" end - create_table "order_comments", id: :integer, force: :cascade do |t| + create_table "order_comments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "order_id" t.integer "user_id" t.text "text" - t.datetime "created_at" + t.datetime "created_at", precision: nil t.index ["order_id"], name: "index_order_comments_on_order_id" end - create_table "orders", id: :integer, force: :cascade do |t| + create_table "orders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "supplier_id" t.text "note" - t.datetime "starts" - t.datetime "ends" + t.datetime "starts", precision: nil + t.datetime "ends", precision: nil t.string "state", default: "open" t.integer "lock_version", default: 0, null: false t.integer "updated_by_user_id" t.decimal "foodcoop_result", precision: 8, scale: 2 t.integer "created_by_user_id" - t.datetime "boxfill" + t.datetime "boxfill", precision: nil t.integer "invoice_id" t.date "pickup" - t.datetime "last_sent_mail" + t.datetime "last_sent_mail", precision: nil t.integer "end_action", default: 0, null: false t.decimal "transport", precision: 8, scale: 2 t.index ["state"], name: "index_orders_on_state" end - create_table "page_versions", id: :integer, force: :cascade do |t| + create_table "page_versions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "page_id" t.integer "lock_version" t.text "body" t.integer "updated_by" t.integer "redirect" t.integer "parent_id" - t.datetime "updated_at" + t.datetime "updated_at", precision: nil t.index ["page_id"], name: "index_page_versions_on_page_id" end - create_table "pages", id: :integer, force: :cascade do |t| + create_table "pages", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "title" t.text "body" t.string "permalink" @@ -383,41 +398,41 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.integer "updated_by" t.integer "redirect" t.integer "parent_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.index ["permalink"], name: "index_pages_on_permalink" t.index ["title"], name: "index_pages_on_title" end - create_table "periodic_task_groups", id: :integer, force: :cascade do |t| + create_table "periodic_task_groups", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.date "next_task_date" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end - create_table "poll_choices", id: :integer, force: :cascade do |t| + create_table "poll_choices", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "poll_vote_id", null: false t.integer "choice", null: false t.integer "value", null: false t.index ["poll_vote_id", "choice"], name: "index_poll_choices_on_poll_vote_id_and_choice", unique: true end - create_table "poll_votes", id: :integer, force: :cascade do |t| + create_table "poll_votes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "poll_id", null: false t.integer "user_id", null: false t.integer "ordergroup_id" t.text "note" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.index ["poll_id", "user_id", "ordergroup_id"], name: "index_poll_votes_on_poll_id_and_user_id_and_ordergroup_id", unique: true end - create_table "polls", id: :integer, force: :cascade do |t| + create_table "polls", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "created_by_user_id", null: false t.string "name", null: false t.text "description" - t.datetime "starts" - t.datetime "ends" + t.datetime "starts", precision: nil + t.datetime "ends", precision: nil t.boolean "one_vote_per_ordergroup", default: false, null: false t.text "required_ordergroup_custom_fields" t.text "required_user_custom_fields" @@ -427,66 +442,66 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.integer "multi_select_count", default: 0, null: false t.integer "min_points" t.integer "max_points" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil t.index ["final_choice"], name: "index_polls_on_final_choice" end - create_table "printer_job_updates", id: :integer, force: :cascade do |t| + create_table "printer_job_updates", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "printer_job_id", null: false - t.datetime "created_at", null: false + t.datetime "created_at", precision: nil, null: false t.string "state", null: false t.text "message" t.index ["printer_job_id", "created_at"], name: "index_printer_job_updates_on_printer_job_id_and_created_at" end - create_table "printer_jobs", id: :integer, force: :cascade do |t| + create_table "printer_jobs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "order_id" t.string "document", null: false t.integer "created_by_user_id", null: false t.integer "finished_by_user_id" - t.datetime "finished_at" + t.datetime "finished_at", precision: nil t.index ["finished_at"], name: "index_printer_jobs_on_finished_at" end - create_table "settings", id: :integer, force: :cascade do |t| + create_table "settings", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "var", null: false t.text "value" t.integer "thing_id" t.string "thing_type", limit: 30 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["thing_type", "thing_id", "var"], name: "index_settings_on_thing_type_and_thing_id_and_var", unique: true end - create_table "stock_changes", id: :integer, force: :cascade do |t| + create_table "stock_changes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "stock_event_id" t.integer "order_id" t.integer "stock_article_id" t.integer "quantity", default: 0 - t.datetime "created_at" + t.datetime "created_at", precision: nil t.index ["stock_article_id"], name: "index_stock_changes_on_stock_article_id" t.index ["stock_event_id"], name: "index_stock_changes_on_stock_event_id" end - create_table "stock_events", id: :integer, force: :cascade do |t| + create_table "stock_events", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "supplier_id" t.date "date" - t.datetime "created_at" + t.datetime "created_at", precision: nil t.text "note" t.integer "invoice_id" t.string "type", null: false t.index ["supplier_id"], name: "index_stock_events_on_supplier_id" end - create_table "supplier_categories", id: :integer, force: :cascade do |t| + create_table "supplier_categories", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.string "description" t.integer "financial_transaction_class_id" t.integer "bank_account_id" end - create_table "suppliers", id: :integer, force: :cascade do |t| + create_table "suppliers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", default: "", null: false t.string "address", default: "", null: false t.string "phone", default: "", null: false @@ -501,21 +516,21 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.string "note" t.integer "shared_supplier_id" t.string "min_order_quantity" - t.datetime "deleted_at" + t.datetime "deleted_at", precision: nil t.string "shared_sync_method" t.string "iban" t.integer "supplier_category_id" t.index ["name"], name: "index_suppliers_on_name", unique: true end - create_table "tasks", id: :integer, force: :cascade do |t| + create_table "tasks", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", default: "", null: false t.text "description" t.date "due_date" t.boolean "done", default: false t.integer "workgroup_id" - t.datetime "created_on", null: false - t.datetime "updated_on", null: false + t.datetime "created_on", precision: nil, null: false + t.datetime "updated_on", precision: nil, null: false t.integer "required_users", default: 1 t.integer "duration", default: 1 t.integer "periodic_task_group_id" @@ -525,7 +540,7 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.index ["workgroup_id"], name: "index_tasks_on_workgroup_id" end - create_table "users", id: :integer, force: :cascade do |t| + create_table "users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "nick" t.string "password_hash", default: "", null: false t.string "password_salt", default: "", null: false @@ -533,15 +548,16 @@ ActiveRecord::Schema.define(version: 2021_02_05_090257) do t.string "last_name", default: "", null: false t.string "email", default: "", null: false t.string "phone" - t.datetime "created_on", null: false + t.datetime "created_on", precision: nil, null: false t.string "reset_password_token" - t.datetime "reset_password_expires" - t.datetime "last_login" - t.datetime "last_activity" - t.datetime "deleted_at" + t.datetime "reset_password_expires", precision: nil + t.datetime "last_login", precision: nil + t.datetime "last_activity", precision: nil + t.datetime "deleted_at", precision: nil t.string "iban" t.index ["email"], name: "index_users_on_email", unique: true t.index ["nick"], name: "index_users_on_nick", unique: true end + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" end diff --git a/db/seeds/minimal.seeds.rb b/db/seeds/minimal.seeds.rb index d38ef10e..bbf97e10 100644 --- a/db/seeds/minimal.seeds.rb +++ b/db/seeds/minimal.seeds.rb @@ -2,30 +2,30 @@ # Create working group with full rights administrators = Workgroup.create!( - :name => "Administrators", - :description => "System administrators.", - :role_admin => true, - :role_finance => true, - :role_article_meta => true, - :role_pickups => true, - :role_suppliers => true, - :role_orders => true + name: 'Administrators', + description: 'System administrators.', + role_admin: true, + role_finance: true, + role_article_meta: true, + role_pickups: true, + role_suppliers: true, + role_orders: true ) # Create admin user User.create!( - :nick => "admin", - :first_name => "Anton", - :last_name => "Administrator", - :email => "admin@foo.test", - :password => "secret", - :groups => [administrators] + nick: 'admin', + first_name: 'Anton', + last_name: 'Administrator', + email: 'admin@foo.test', + password: 'secret', + groups: [administrators] ) # First entry for financial transaction types -financial_transaction_class = FinancialTransactionClass.create!(:name => "Other") -FinancialTransactionType.create!(:name => "Foodcoop", :financial_transaction_class_id => financial_transaction_class.id) +financial_transaction_class = FinancialTransactionClass.create!(name: 'Other') +FinancialTransactionType.create!(name: 'Foodcoop', financial_transaction_class_id: financial_transaction_class.id) # First entry for article categories -SupplierCategory.create!(:name => "Other", :financial_transaction_class_id => financial_transaction_class.id) -ArticleCategory.create!(:name => "Other", :description => "other, misc, unknown") +SupplierCategory.create!(name: 'Other', financial_transaction_class_id: financial_transaction_class.id) +ArticleCategory.create!(name: 'Other', description: 'other, misc, unknown') diff --git a/db/seeds/seed_helper.rb b/db/seeds/seed_helper.rb index 574be356..05272319 100644 --- a/db/seeds/seed_helper.rb +++ b/db/seeds/seed_helper.rb @@ -8,10 +8,10 @@ def seed_group_orders # order 3..12 times a random article go = og.group_orders.create!(order: order, updated_by_user_id: 1) - (3 + rand(10)).times do + rand(3..12).times do goa = go.group_order_articles.find_or_create_by!(order_article: order.order_articles.offset(rand(noas)).first) unit_quantity = goa.order_article.price.unit_quantity - goa.update_quantities rand([4, 2 * unit_quantity + 2].max), rand(unit_quantity) + goa.update_quantities rand([4, (unit_quantity * 2) + 2].max), rand(unit_quantity) end end # update totals diff --git a/db/seeds/small.en.seeds.rb b/db/seeds/small.en.seeds.rb index 52f0b9db..6c832e1d 100644 --- a/db/seeds/small.en.seeds.rb +++ b/db/seeds/small.en.seeds.rb @@ -1,174 +1,300 @@ -require_relative 'seed_helper.rb' +require_relative 'seed_helper' ## Financial transaction classes -FinancialTransactionClass.create!(:id => 1, :name => 'Standard') -FinancialTransactionClass.create!(:id => 2, :name => 'Foodsoft') +FinancialTransactionClass.create!(id: 1, name: 'Standard') +FinancialTransactionClass.create!(id: 2, name: 'Foodsoft') ## Suppliers & articles -SupplierCategory.create!(:id => 1, :name => "Other", :financial_transaction_class_id => 1) +SupplierCategory.create!(id: 1, name: 'Other', financial_transaction_class_id: 1) Supplier.create!([ - { :id => 1, :name => "Beautiful bakery", :supplier_category_id => 1, :address => "Smallstreet 1, Cookilage", :phone => "0123456789", :email => "info@bbakery.test", :min_order_quantity => "100" }, - { :id => 2, :name => "Chocolatiers", :supplier_category_id => 1, :address => "Multatuliroad 1, Amsterdam", :phone => "0123456789", :email => "info@chocolatiers.test", :url => "http://www.chocolatiers.test/", :contact_person => "Max Pure", :delivery_days => "Tue, Fr (Amsterdam)" }, - { :id => 3, :name => "Cheesemaker", :supplier_category_id => 1, :address => "Cheesestreet 5, London", :phone => "0123456789", :url => "http://www.cheesemaker.test/" }, - { :id => 4, :name => "The Nuthome", :supplier_category_id => 1, :address => "Alexanderplatz, Berlin", :phone => "0123456789", :email => "info@thenuthome.test", :url => "http://www.thenuthome.test/", :note => "delivery in Berlin; €9 delivery costs for orders under €123" } + { id: 1, name: 'Beautiful bakery', supplier_category_id: 1, + address: 'Smallstreet 1, Cookilage', phone: '0123456789', email: 'info@bbakery.test', min_order_quantity: '100' }, + { id: 2, name: 'Chocolatiers', supplier_category_id: 1, + address: 'Multatuliroad 1, Amsterdam', phone: '0123456789', email: 'info@chocolatiers.test', url: 'http://www.chocolatiers.test/', contact_person: 'Max Pure', delivery_days: 'Tue, Fr (Amsterdam)' }, + { id: 3, name: 'Cheesemaker', supplier_category_id: 1, + address: 'Cheesestreet 5, London', phone: '0123456789', url: 'http://www.cheesemaker.test/' }, + { id: 4, name: 'The Nuthome', supplier_category_id: 1, + address: 'Alexanderplatz, Berlin', phone: '0123456789', email: 'info@thenuthome.test', url: 'http://www.thenuthome.test/', note: 'delivery in Berlin; €9 delivery costs for orders under €123' } ]) -ArticleCategory.create!(:id => 1, :name => "Other", :description => "other, misc, unknown") -ArticleCategory.create!(:id => 2, :name => "Fruit") -ArticleCategory.create!(:id => 3, :name => "Vegetables") -ArticleCategory.create!(:id => 4, :name => "Potatoes & onions") -ArticleCategory.create!(:id => 5, :name => "Bread & Bakery") -ArticleCategory.create!(:id => 6, :name => "Drinks", :description => "juice, fruit juice, vegetable juice, soda") -ArticleCategory.create!(:id => 7, :name => "Herbs & Spices") -ArticleCategory.create!(:id => 8, :name => "Milk & products", :description => "milk, butter, cream, yoghurt, cheese, eggs, milk substitutes") -ArticleCategory.create!(:id => 9, :name => "Fish & Sea") -ArticleCategory.create!(:id => 10, :name => "Meat") -ArticleCategory.create!(:id => 11, :name => "Oils & Fats") -ArticleCategory.create!(:id => 12, :name => "Grains & Legumes") -ArticleCategory.create!(:id => 13, :name => "Nuts & Seeds") -ArticleCategory.create!(:id => 14, :name => "Sugar & Sweets") +ArticleCategory.create!(id: 1, name: 'Other', description: 'other, misc, unknown') +ArticleCategory.create!(id: 2, name: 'Fruit') +ArticleCategory.create!(id: 3, name: 'Vegetables') +ArticleCategory.create!(id: 4, name: 'Potatoes & onions') +ArticleCategory.create!(id: 5, name: 'Bread & Bakery') +ArticleCategory.create!(id: 6, name: 'Drinks', description: 'juice, fruit juice, vegetable juice, soda') +ArticleCategory.create!(id: 7, name: 'Herbs & Spices') +ArticleCategory.create!(id: 8, name: 'Milk & products', + description: 'milk, butter, cream, yoghurt, cheese, eggs, milk substitutes') +ArticleCategory.create!(id: 9, name: 'Fish & Sea') +ArticleCategory.create!(id: 10, name: 'Meat') +ArticleCategory.create!(id: 11, name: 'Oils & Fats') +ArticleCategory.create!(id: 12, name: 'Grains & Legumes') +ArticleCategory.create!(id: 13, name: 'Nuts & Seeds') +ArticleCategory.create!(id: 14, name: 'Sugar & Sweets') -Article.create!(:name => "Brown whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brown half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brown sesame whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brown sesame half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Light wheat whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Light wheat half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bread with sunflower seeds whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bread with sunflower seeds half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bread with walnuts whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bread with walnuts half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kennemerlandbread whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kennemerlandbread half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Maize bread whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Maize bread half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 1200 gram whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 1200 gram half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 900 gram whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 900 gram half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Speltbread whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Speltbread half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Country bread 900gram whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Country bread 900gram half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "White whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "White half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "White with poppy seeds whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "White with poppy seeds half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Fig bread whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Fig bread half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Beer-based bread whole", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Beer-based bread half", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Raisin bun", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.99E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Muesli bun", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brioche", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.99E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brown croissant", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Croissants", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Cheese croissants", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolatecroissants", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Soepstengels white", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Soepstengels volkoren", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.99E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Pumpkin-seed buns", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.88E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "White buns", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.66E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brown buns", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.66E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Tomato-feta bread", :supplier_id => 1, :article_category_id => 5, :unit => "pc", :note => "organic", :availability => true, :manufacturer => "The Baker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolate Bar Milk (37%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolate Bar Pure (68%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolate Bar Milk (40%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolate Bar Pure (75%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocolate Bar Swan Pure (75%)", :supplier_id => 2, :article_category_id => 14, :unit => "120gr", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.66E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Cacao nibs", :supplier_id => 2, :article_category_id => 14, :unit => "1 kg", :note => "organic", :availability => true, :manufacturer => "Chocolatemakers", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Cheese Cow-young", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.88E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- young matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 12) -Article.create!(:name => "Cheese cow- extra matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.12E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "cheese Cow- old", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "cheese cow -very old", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.12E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese Cow-nettle young", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- nettle young matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.1075E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- nettle matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese Cow-cumin young", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- cumin young matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.1075E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cheese cow- cumin matured", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "organic", :availability => true, :manufacturer => "Cheesefarm", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cashew nuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.4444E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 22, :order_number => ":b936051") -Article.create!(:name => "Hazel white", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":9e3f85b") -Article.create!(:name => "Hazel brown", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":d278041") -Article.create!(:name => "Almond Brown Spanish", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.999E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":0b51a8d") -Article.create!(:name => "Brazil nuts (organic)", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 20, :order_number => ":01e59e3") -Article.create!(:name => "Organic walnut light halves", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":7ff8587") -Article.create!(:name => "Pinenuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":aa88d9f") -Article.create!(:name => "Pumpkin", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":e63069b") -Article.create!(:name => "Sunflower seeds (organic)", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.999E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":0428388") -Article.create!(:name => "Amandel White Spaans", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "organic", :availability => true, :price => 0.66666E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":a8f0734") -Article.create!(:name => "Cashew", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":1d26958") -Article.create!(:name => "Almonds blanched", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":31439e2") -Article.create!(:name => "Almonds natural", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":9c49374") -Article.create!(:name => "Walnut ELH halves", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.4444E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":92907d1") -Article.create!(:name => "Walnut ELP parts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":395640e") -Article.create!(:name => "Brazil nuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":710acbb") -Article.create!(:name => "Macadamia type 0", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":bbaf40b") -Article.create!(:name => "Pecan", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55555E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":7958183") -Article.create!(:name => "Hazelnuts natural", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":50392a8") -Article.create!(:name => "Hazelnuts blanched", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":4fe6525") -Article.create!(:name => "Mixed Nuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c051b22") -Article.create!(:name => "Peanuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.777E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":f507577") -Article.create!(:name => "Small peanuts", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":ce563bb") -Article.create!(:name => "Medjoul dates", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":8232061") -Article.create!(:name => "Turkish apricots natural", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":185084f") -Article.create!(:name => "Turkish apricots sulfurised", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":2b2fb20") -Article.create!(:name => "Spanish Figs", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.444E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":82590b1") -Article.create!(:name => "Turkish Figs", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.555E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":cabeeb6") -Article.create!(:name => "Sour Apricots South-Africa", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":2ac18b7") -Article.create!(:name => "Blue raisins Flames", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":16bfa75") -Article.create!(:name => "Yellow Raisins", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":1c59324") -Article.create!(:name => "Red Raisins", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c3fcd84") -Article.create!(:name => "Cranberries whole", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.222E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":921c168") -Article.create!(:name => "Dried apples", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.555E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":902c67b") -Article.create!(:name => "Dried plums without core", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.222E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":a847f91") -Article.create!(:name => "Pumpkin seeds", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.111E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":535645f") -Article.create!(:name => "Sunflower seeds", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.666E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":4ab9a83") -Article.create!(:name => "Linseed", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":04be223") -Article.create!(:name => "Poppy seeds", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.7777E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":ec5b2b9") -Article.create!(:name => "Pine nuts medium china", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":0e5b0b8") -Article.create!(:name => "Goji berries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":d52ee00") -Article.create!(:name => "Mulberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.5555E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":5f46bd5") -Article.create!(:name => "Peeled Hemp", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.5555E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c39165b") -Article.create!(:name => "Incaberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":8d44fe7") -Article.create!(:name => "Blueberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":9a95422") -Article.create!(:name => "Chia seeds", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55555E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":416d57b") -Article.create!(:name => "Coconut grated", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":b3f65e4") +Article.create!(name: 'Brown whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brown half', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brown sesame whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brown sesame half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Light wheat whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Light wheat half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bread with sunflower seeds whole', supplier_id: 1, article_category_id: 5, + unit: 'pc', note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bread with sunflower seeds half', supplier_id: 1, article_category_id: 5, + unit: 'pc', note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bread with walnuts whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bread with walnuts half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kennemerlandbread whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kennemerlandbread half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Maize bread whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Maize bread half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 1200 gram whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 1200 gram half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 900 gram whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 900 gram half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Speltbread whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Speltbread half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Country bread 900gram whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Country bread 900gram half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'White whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'White half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'White with poppy seeds whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'White with poppy seeds half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Fig bread whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Fig bread half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Beer-based bread whole', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Beer-based bread half', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Raisin bun', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.99E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Muesli bun', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brioche', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.99E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brown croissant', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Croissants', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Cheese croissants', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolatecroissants', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Soepstengels white', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Soepstengels volkoren', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.99E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Pumpkin-seed buns', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.88E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'White buns', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.66E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brown buns', supplier_id: 1, article_category_id: 5, unit: 'pc', note: 'organic', + availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.66E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Tomato-feta bread', supplier_id: 1, article_category_id: 5, unit: 'pc', + note: 'organic', availability: true, manufacturer: 'The Baker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolate Bar Milk (37%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolate Bar Pure (68%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolate Bar Milk (40%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolate Bar Pure (75%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocolate Bar Swan Pure (75%)', supplier_id: 2, article_category_id: 14, + unit: '120gr', note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.66E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Cacao nibs', supplier_id: 2, article_category_id: 14, unit: '1 kg', + note: 'organic', availability: true, manufacturer: 'Chocolatemakers', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Cheese Cow-young', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.88E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- young matured', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- matured', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 12) +Article.create!(name: 'Cheese cow- extra matured', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.12E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'cheese Cow- old', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'cheese cow -very old', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.12E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese Cow-nettle young', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- nettle young matured', supplier_id: 3, article_category_id: 8, + unit: 'kg', note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.1075E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- nettle matured', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese Cow-cumin young', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- cumin young matured', supplier_id: 3, article_category_id: 8, + unit: 'kg', note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.1075E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cheese cow- cumin matured', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'organic', availability: true, manufacturer: 'Cheesefarm', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cashew nuts', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.4444E2, tax: 6.0, deposit: 0.0, unit_quantity: 22, order_number: ':b936051') +Article.create!(name: 'Hazel white', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':9e3f85b') +Article.create!(name: 'Hazel brown', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':d278041') +Article.create!(name: 'Almond Brown Spanish', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.999E1, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':0b51a8d') +Article.create!(name: 'Brazil nuts (organic)', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 20, order_number: ':01e59e3') +Article.create!(name: 'Organic walnut light halves', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':7ff8587') +Article.create!(name: 'Pinenuts', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'organic', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':aa88d9f') +Article.create!(name: 'Pumpkin', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'organic', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':e63069b') +Article.create!(name: 'Sunflower seeds (organic)', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.999E1, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':0428388') +Article.create!(name: 'Amandel White Spaans', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'organic', availability: true, price: 0.66666E3, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':a8f0734') +Article.create!(name: 'Cashew', supplier_id: 4, article_category_id: 13, unit: 'kg', availability: true, + price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':1d26958') +Article.create!(name: 'Almonds blanched', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':31439e2') +Article.create!(name: 'Almonds natural', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':9c49374') +Article.create!(name: 'Walnut ELH halves', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.4444E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':92907d1') +Article.create!(name: 'Walnut ELP parts', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':395640e') +Article.create!(name: 'Brazil nuts', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':710acbb') +Article.create!(name: 'Macadamia type 0', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':bbaf40b') +Article.create!(name: 'Pecan', supplier_id: 4, article_category_id: 13, unit: 'kg', availability: true, + price: 0.55555E3, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':7958183') +Article.create!(name: 'Hazelnuts natural', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':50392a8') +Article.create!(name: 'Hazelnuts blanched', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':4fe6525') +Article.create!(name: 'Mixed Nuts', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c051b22') +Article.create!(name: 'Peanuts', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.777E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':f507577') +Article.create!(name: 'Small peanuts', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':ce563bb') +Article.create!(name: 'Medjoul dates', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':8232061') +Article.create!(name: 'Turkish apricots natural', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':185084f') +Article.create!(name: 'Turkish apricots sulfurised', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':2b2fb20') +Article.create!(name: 'Spanish Figs', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.444E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':82590b1') +Article.create!(name: 'Turkish Figs', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.555E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':cabeeb6') +Article.create!(name: 'Sour Apricots South-Africa', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':2ac18b7') +Article.create!(name: 'Blue raisins Flames', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':16bfa75') +Article.create!(name: 'Yellow Raisins', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':1c59324') +Article.create!(name: 'Red Raisins', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c3fcd84') +Article.create!(name: 'Cranberries whole', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.222E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':921c168') +Article.create!(name: 'Dried apples', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.555E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':902c67b') +Article.create!(name: 'Dried plums without core', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.222E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':a847f91') +Article.create!(name: 'Pumpkin seeds', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.111E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':535645f') +Article.create!(name: 'Sunflower seeds', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.666E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':4ab9a83') +Article.create!(name: 'Linseed', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55E0, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':04be223') +Article.create!(name: 'Poppy seeds', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.7777E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':ec5b2b9') +Article.create!(name: 'Pine nuts medium china', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':0e5b0b8') +Article.create!(name: 'Goji berries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':d52ee00') +Article.create!(name: 'Mulberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.5555E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':5f46bd5') +Article.create!(name: 'Peeled Hemp', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.5555E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c39165b') +Article.create!(name: 'Incaberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':8d44fe7') +Article.create!(name: 'Blueberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':9a95422') +Article.create!(name: 'Chia seeds', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55555E3, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':416d57b') +Article.create!(name: 'Coconut grated', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55E0, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':b3f65e4') ## Members & groups -User.create!(:id => 1, :nick => "admin", :password => "secret", :first_name => "Anton", :last_name => "Administrator", :email => "admin@foo.test", :phone => "+4421486548", :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00') -User.create!(:id => 2, :nick => "john", :password => "secret", :first_name => "John", :last_name => "Doe", :email => "john@doe.test", :created_on => 'Sun, 19 Jan 2014 17:38:22 UTC +00:00') -User.create!(:id => 3, :nick => "peter", :password => "secret", :first_name => "Peter", :last_name => "Peters", :email => "peter@peters.test", :phone => "+4711235486811", :created_on => 'Sat, 25 Jan 2014 20:20:36 UTC +00:00') -User.create!(:id => 4, :nick => "jan", :password => "secret", :first_name => "Jan", :last_name => "Lou", :email => "jan@lou.test", :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00') -User.create!(:id => 5, :nick => "mary", :password => "secret", :first_name => "Mary", :last_name => "Lou", :email => "marie@lou.test", :created_on => 'Mon, 03 Feb 2014 11:47:17 UTC +00:00') -User.find_by_nick("mary").update(last_activity: 5.days.ago) +User.create!(id: 1, nick: 'admin', password: 'secret', first_name: 'Anton', last_name: 'Administrator', + email: 'admin@foo.test', phone: '+4421486548', created_on: 'Wed, 15 Jan 2014 16:15:33 UTC +00:00') +User.create!(id: 2, nick: 'john', password: 'secret', first_name: 'John', last_name: 'Doe', + email: 'john@doe.test', created_on: 'Sun, 19 Jan 2014 17:38:22 UTC +00:00') +User.create!(id: 3, nick: 'peter', password: 'secret', first_name: 'Peter', last_name: 'Peters', + email: 'peter@peters.test', phone: '+4711235486811', created_on: 'Sat, 25 Jan 2014 20:20:36 UTC +00:00') +User.create!(id: 4, nick: 'jan', password: 'secret', first_name: 'Jan', last_name: 'Lou', + email: 'jan@lou.test', created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00') +User.create!(id: 5, nick: 'mary', password: 'secret', first_name: 'Mary', last_name: 'Lou', + email: 'marie@lou.test', created_on: 'Mon, 03 Feb 2014 11:47:17 UTC +00:00') +User.find_by_nick('mary').update(last_activity: 5.days.ago) -Workgroup.create!(:id => 1, :name => "Administrators", :description => "System administrators.", :account_balance => 0.0, :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00', :role_admin => true, :role_suppliers => true, :role_article_meta => true, :role_finance => true, :role_orders => true, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 2, :name => "Finances", :account_balance => 0.0, :created_on => 'Sun, 19 Jan 2014 17:40:03 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => true, :role_orders => false, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 3, :name => "Ordering", :account_balance => 0.0, :created_on => 'Thu, 20 Feb 2014 14:44:47 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => true, :role_finance => false, :role_orders => true, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 4, :name => "Assortment", :account_balance => 0.0, :created_on => 'Wed, 09 Apr 2014 12:24:55 UTC +00:00', :role_admin => false, :role_suppliers => true, :role_article_meta => true, :role_finance => false, :role_orders => false, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 5, :name => "Admin Administrator", :account_balance => 0.0, :created_on => 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :stats => { :jobs_size => 0, :orders_sum => 1021.74 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => true) -Ordergroup.create!(:id => 6, :name => "Pete's house", :account_balance => -0.35E2, :created_on => 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "Piet Pieterssen", :stats => { :jobs_size => 0, :orders_sum => 60.96 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 7, :name => "Jan Klaassen", :account_balance => -0.35E2, :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "Jan Klaassen", :stats => { :jobs_size => 0, :orders_sum => 0 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 8, :name => "John Doe", :account_balance => 0.90E2, :created_on => 'Wed, 09 Apr 2014 12:23:29 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "John Doe", :stats => { :jobs_size => 0, :orders_sum => 0 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) +Workgroup.create!(id: 1, name: 'Administrators', description: 'System administrators.', + account_balance: 0.0, created_on: 'Wed, 15 Jan 2014 16:15:33 UTC +00:00', role_admin: true, role_suppliers: true, role_article_meta: true, role_finance: true, role_orders: true, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 2, name: 'Finances', account_balance: 0.0, + created_on: 'Sun, 19 Jan 2014 17:40:03 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: true, role_orders: false, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 3, name: 'Ordering', account_balance: 0.0, + created_on: 'Thu, 20 Feb 2014 14:44:47 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: true, role_finance: false, role_orders: true, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 4, name: 'Assortment', account_balance: 0.0, + created_on: 'Wed, 09 Apr 2014 12:24:55 UTC +00:00', role_admin: false, role_suppliers: true, role_article_meta: true, role_finance: false, role_orders: false, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 5, name: 'Admin Administrator', account_balance: 0.0, + created_on: 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, stats: { jobs_size: 0, orders_sum: 1021.74 }, next_weekly_tasks_number: 8, ignore_apple_restriction: true) +Ordergroup.create!(id: 6, name: "Pete's house", account_balance: -0.35E2, + created_on: 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'Piet Pieterssen', stats: { jobs_size: 0, orders_sum: 60.96 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 7, name: 'Jan Klaassen', account_balance: -0.35E2, + created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'Jan Klaassen', stats: { jobs_size: 0, orders_sum: 0 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 8, name: 'John Doe', account_balance: 0.90E2, + created_on: 'Wed, 09 Apr 2014 12:23:29 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'John Doe', stats: { jobs_size: 0, orders_sum: 0 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) -Membership.create!(:group_id => 1, :user_id => 1) -Membership.create!(:group_id => 5, :user_id => 1) -Membership.create!(:group_id => 2, :user_id => 2) -Membership.create!(:group_id => 8, :user_id => 2) -Membership.create!(:group_id => 6, :user_id => 3) -Membership.create!(:group_id => 7, :user_id => 4) -Membership.create!(:group_id => 8, :user_id => 4) -Membership.create!(:group_id => 3, :user_id => 4) -Membership.create!(:group_id => 7, :user_id => 5) -Membership.create!(:group_id => 3, :user_id => 5) -Membership.create!(:group_id => 4, :user_id => 5) +Membership.create!(group_id: 1, user_id: 1) +Membership.create!(group_id: 5, user_id: 1) +Membership.create!(group_id: 2, user_id: 2) +Membership.create!(group_id: 8, user_id: 2) +Membership.create!(group_id: 6, user_id: 3) +Membership.create!(group_id: 7, user_id: 4) +Membership.create!(group_id: 8, user_id: 4) +Membership.create!(group_id: 3, user_id: 4) +Membership.create!(group_id: 7, user_id: 5) +Membership.create!(group_id: 3, user_id: 5) +Membership.create!(group_id: 4, user_id: 5) ## Orders & OrderArticles @@ -182,10 +308,15 @@ seed_group_orders ## Finances -FinancialTransactionType.create!(:id => 1, :name => "Foodcoop", :financial_transaction_class_id => 1) +FinancialTransactionType.create!(id: 1, name: 'Foodcoop', financial_transaction_class_id: 1) -FinancialTransaction.create!(:id => 1, :ordergroup_id => 5, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 3, :ordergroup_id => 6, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 4, :ordergroup_id => 7, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 5, :ordergroup_id => 5, :amount => 0.35E2, :note => "payment", :user_id => 2, :created_on => 'Wed, 05 Feb 2014 16:49:24 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 6, :ordergroup_id => 8, :amount => 0.90E2, :note => "Bank transfer", :user_id => 2, :created_on => 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', :financial_transaction_type_id => 1) +FinancialTransaction.create!(id: 1, ordergroup_id: 5, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 3, ordergroup_id: 6, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 4, ordergroup_id: 7, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 5, ordergroup_id: 5, amount: 0.35E2, note: 'payment', user_id: 2, + created_on: 'Wed, 05 Feb 2014 16:49:24 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 6, ordergroup_id: 8, amount: 0.90E2, note: 'Bank transfer', user_id: 2, + created_on: 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', financial_transaction_type_id: 1) diff --git a/db/seeds/small.nl.seeds.rb b/db/seeds/small.nl.seeds.rb index afa9cc04..1dd39992 100644 --- a/db/seeds/small.nl.seeds.rb +++ b/db/seeds/small.nl.seeds.rb @@ -1,173 +1,300 @@ -require_relative 'seed_helper.rb' +require_relative 'seed_helper' ## Financial transaction classes -FinancialTransactionClass.create!(:id => 1, :name => 'Standaard') -FinancialTransactionClass.create!(:id => 2, :name => 'Foodsoft') +FinancialTransactionClass.create!(id: 1, name: 'Standaard') +FinancialTransactionClass.create!(id: 2, name: 'Foodsoft') ## Suppliers & articles -SupplierCategory.create!(:id => 1, :name => "Other", :financial_transaction_class_id => 1) +SupplierCategory.create!(id: 1, name: 'Other', financial_transaction_class_id: 1) Supplier.create!([ - { :id => 1, :name => "Koekenbakker", :supplier_category_id => 1, :address => "Dorpsstraat 1, Koekange", :phone => "012 3456789", :email => "info@dekoekenbakker.test", :min_order_quantity => "100" }, - { :id => 2, :name => "Chocolademakkers", :supplier_category_id => 1, :address => "Multatuliweg 1, Amsterdam", :phone => "012 3456789", :email => "info@chocolademakkers.test", :url => "http://www.chocolademakkers.test/", :contact_person => "Max Puur", :delivery_days => "di, vr (Amsterdam)" }, - { :id => 3, :name => "Kaasmaker", :supplier_category_id => 1, :address => "Waagplein, Alkmaar", :phone => "012 3456789", :url => "http://www.kaaskamer.test/" }, - { :id => 4, :name => "Notenhuis", :supplier_category_id => 1, :address => "Damrak 1, Amsterdam", :phone => "012 3456789", :email => "info@notenhuis.test", :url => "http://www.notenhuis.test/", :note => "leveren in Amsterdam; €9 leverkosten bij bestellingen onder €123" } + { id: 1, name: 'Koekenbakker', supplier_category_id: 1, + address: 'Dorpsstraat 1, Koekange', phone: '012 3456789', email: 'info@dekoekenbakker.test', min_order_quantity: '100' }, + { id: 2, name: 'Chocolademakkers', supplier_category_id: 1, + address: 'Multatuliweg 1, Amsterdam', phone: '012 3456789', email: 'info@chocolademakkers.test', url: 'http://www.chocolademakkers.test/', contact_person: 'Max Puur', delivery_days: 'di, vr (Amsterdam)' }, + { id: 3, name: 'Kaasmaker', supplier_category_id: 1, address: 'Waagplein, Alkmaar', + phone: '012 3456789', url: 'http://www.kaaskamer.test/' }, + { id: 4, name: 'Notenhuis', supplier_category_id: 1, address: 'Damrak 1, Amsterdam', + phone: '012 3456789', email: 'info@notenhuis.test', url: 'http://www.notenhuis.test/', note: 'leveren in Amsterdam; €9 leverkosten bij bestellingen onder €123' } ]) -ArticleCategory.create!(:id => 1, :name => "Other", :description => "overig, anders, onbekend") -ArticleCategory.create!(:id => 2, :name => "Fruit") -ArticleCategory.create!(:id => 3, :name => "Groenten") -ArticleCategory.create!(:id => 4, :name => "Aardappels & uien") -ArticleCategory.create!(:id => 5, :name => "Brood & Bakkerij") -ArticleCategory.create!(:id => 6, :name => "Dranken", :description => "sap, fruitsap, groentesap, frisdrank") -ArticleCategory.create!(:id => 7, :name => "Kruiden", :description => "kruiden, specerijen, conserveringsmiddelen, extracten") -ArticleCategory.create!(:id => 8, :name => "Zuivel", :description => "melk, boter, room, yoghurt, kaas, eieren, zuivelvervangers") -ArticleCategory.create!(:id => 9, :name => "Vis & Zee", :description => "vis, schaaldieren, schelpdieren") -ArticleCategory.create!(:id => 10, :name => "Vlees & Gevogelte") -ArticleCategory.create!(:id => 11, :name => "Oliën & Vetten") -ArticleCategory.create!(:id => 12, :name => "Graan & Peulvruchten") -ArticleCategory.create!(:id => 13, :name => "Noten & Zaden") -ArticleCategory.create!(:id => 14, :name => "Zoetwaren & Zoetstof") +ArticleCategory.create!(id: 1, name: 'Other', description: 'overig, anders, onbekend') +ArticleCategory.create!(id: 2, name: 'Fruit') +ArticleCategory.create!(id: 3, name: 'Groenten') +ArticleCategory.create!(id: 4, name: 'Aardappels & uien') +ArticleCategory.create!(id: 5, name: 'Brood & Bakkerij') +ArticleCategory.create!(id: 6, name: 'Dranken', description: 'sap, fruitsap, groentesap, frisdrank') +ArticleCategory.create!(id: 7, name: 'Kruiden', + description: 'kruiden, specerijen, conserveringsmiddelen, extracten') +ArticleCategory.create!(id: 8, name: 'Zuivel', + description: 'melk, boter, room, yoghurt, kaas, eieren, zuivelvervangers') +ArticleCategory.create!(id: 9, name: 'Vis & Zee', description: 'vis, schaaldieren, schelpdieren') +ArticleCategory.create!(id: 10, name: 'Vlees & Gevogelte') +ArticleCategory.create!(id: 11, name: 'Oliën & Vetten') +ArticleCategory.create!(id: 12, name: 'Graan & Peulvruchten') +ArticleCategory.create!(id: 13, name: 'Noten & Zaden') +ArticleCategory.create!(id: 14, name: 'Zoetwaren & Zoetstof') -Article.create!(:name => "Volkoren heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Volkoren half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Volkoren sesam heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Volkoren sesam half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Licht tarwe heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Licht tarwe half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Zonnebloempitbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Zonnebloempitbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Walnoten vloer heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Walnoten vloer half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kennemerlandbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kennemerlandbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Maisbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Maisbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 1200 gram heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 1200 gram half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 900 gram heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Oberlander 900 gram half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Speltbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Speltbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Landbrood 900gram heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Landbrood 900gram half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Wit heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Wit half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Wit met maanzaad heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Wit met maanzaad half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Vijgenbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Vijgenbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bierborstelbrood heel", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.33E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bierborstelbrood half", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Krentenbol", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.99E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Mueslibol", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Brioche", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.91E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Volkoren croissant", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Croissants", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kaas croissants", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladecroissants", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Soepstengels wit", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Soepstengels volkoren", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.99E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Pompoenpitten broodjes", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.88E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Witte kadetjes", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.66E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Bruine kadetjes", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.66E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Tomaten feta broodje", :supplier_id => 1, :article_category_id => 5, :unit => "stuk", :note => "bio", :availability => true, :manufacturer => "De Bakker", :origin => "NL", :price => 0.11E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladereep Melk (37%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladereep Puur (68%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladereep Drie Mensen Melk (40%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladereep Drie Mensen Puur (75%)", :supplier_id => 2, :article_category_id => 14, :unit => "90gr", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.22E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Chocoladereep Zwaan Puur (75%)", :supplier_id => 2, :article_category_id => 14, :unit => "120gr", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.66E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Cacao nibs", :supplier_id => 2, :article_category_id => 14, :unit => "1 kg", :note => "bio", :availability => true, :manufacturer => "Chocolademakkers", :origin => "NL", :price => 0.10E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1) -Article.create!(:name => "Kaas Koe-jong", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.88E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- jong belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 12) -Article.create!(:name => "Kaas koe- extra belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "kaas Koe- oud", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.1375E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "kaas koe -overjarig", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas Koe-brandnetel jong", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- brandnetel jong belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- brandnetel belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas Koe-komijn jong", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.99E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- komijn jong belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Kaas koe- komijn belegen", :supplier_id => 3, :article_category_id => 8, :unit => "kg", :note => "bio", :availability => true, :manufacturer => "Kaasboerderij", :origin => "NL", :price => 0.11E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 8) -Article.create!(:name => "Cashewnoten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.4444E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 22, :order_number => ":b936051") -Article.create!(:name => "Hazel wit", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":9e3f85b") -Article.create!(:name => "Hazel bruin", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":d278041") -Article.create!(:name => "Amandel Bruin Spaans", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.999E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":0b51a8d") -Article.create!(:name => "Paranoten (bio)", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 20, :order_number => ":01e59e3") -Article.create!(:name => "Bio walnoten light halfjes", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":7ff8587") -Article.create!(:name => "Pijnboompitten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":aa88d9f") -Article.create!(:name => "Pompoen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":e63069b") -Article.create!(:name => "Zonnepitten (bio)", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.999E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 25, :order_number => ":0428388") -Article.create!(:name => "Amandel Wit Spaans", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :note => "bio", :availability => true, :price => 0.66666E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 10, :order_number => ":a8f0734") -Article.create!(:name => "Cashew", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":1d26958") -Article.create!(:name => "Amandelen geblancheerd", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":31439e2") -Article.create!(:name => "Amandelen naturel", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":9c49374") -Article.create!(:name => "Walnoot ELH hafjes", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.4444E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":92907d1") -Article.create!(:name => "Walnoot ELP stukjes", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":395640e") -Article.create!(:name => "Paranoten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":710acbb") -Article.create!(:name => "Macadamia Stijl 0", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":bbaf40b") -Article.create!(:name => "Pecan", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55555E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":7958183") -Article.create!(:name => "Hazelnoten naturel", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.6666E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":50392a8") -Article.create!(:name => "Hazelnoten geblancheerd", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":4fe6525") -Article.create!(:name => "Gemengde Noten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.333E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c051b22") -Article.create!(:name => "Pinda's", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.777E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":f507577") -Article.create!(:name => "Vliespinda's klein", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.8888E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":ce563bb") -Article.create!(:name => "Medjoul dadels", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.3333E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":8232061") -Article.create!(:name => "Turkse Abrikozen ongezwaveld", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":185084f") -Article.create!(:name => "Turkse Abrikozen gezwaveld", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":2b2fb20") -Article.create!(:name => "Spaanse Vijgen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.444E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":82590b1") -Article.create!(:name => "Turkse Vijgen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.555E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":cabeeb6") -Article.create!(:name => "Zure Abrikozen Zuid Afrika", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":2ac18b7") -Article.create!(:name => "Blauwe rozijnen Flames", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":16bfa75") -Article.create!(:name => "Gele Rozijnen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":1c59324") -Article.create!(:name => "Rode Rozijnen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.1111E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c3fcd84") -Article.create!(:name => "Cranberries heel", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.222E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":921c168") -Article.create!(:name => "Gedroogde Appeltjes", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.555E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":902c67b") -Article.create!(:name => "Gedroogde pruimen zonder pit", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.222E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":a847f91") -Article.create!(:name => "Pompoenpitten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.111E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":535645f") -Article.create!(:name => "Zonnenbloepitten", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.666E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":4ab9a83") -Article.create!(:name => "Lijnzaad", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":04be223") -Article.create!(:name => "Maanzaad", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.7777E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":ec5b2b9") -Article.create!(:name => "Pijnboompitten medium china", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":0e5b0b8") -Article.create!(:name => "Goji bessen", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":d52ee00") -Article.create!(:name => "Mulberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.5555E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":5f46bd5") -Article.create!(:name => "Gepelde Hennep", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.5555E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":c39165b") -Article.create!(:name => "Incaberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.888E1, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":8d44fe7") -Article.create!(:name => "Blueberries", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.2222E2, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":9a95422") -Article.create!(:name => "Chia zaad", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55555E3, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":416d57b") -Article.create!(:name => "Cocos Rasp", :supplier_id => 4, :article_category_id => 13, :unit => "kg", :availability => true, :price => 0.55E0, :tax => 6.0, :deposit => 0.0, :unit_quantity => 1, :order_number => ":b3f65e4") +Article.create!(name: 'Volkoren heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Volkoren half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Volkoren sesam heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Volkoren sesam half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Licht tarwe heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Licht tarwe half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Zonnebloempitbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Zonnebloempitbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Walnoten vloer heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Walnoten vloer half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kennemerlandbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kennemerlandbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Maisbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Maisbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 1200 gram heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 1200 gram half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 900 gram heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Oberlander 900 gram half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Speltbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Speltbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Landbrood 900gram heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Landbrood 900gram half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Wit heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Wit half', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Wit met maanzaad heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Wit met maanzaad half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Vijgenbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Vijgenbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bierborstelbrood heel', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.33E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bierborstelbrood half', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Krentenbol', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.99E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Mueslibol', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Brioche', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.91E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Volkoren croissant', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Croissants', supplier_id: 1, article_category_id: 5, unit: 'stuk', note: 'bio', + availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kaas croissants', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladecroissants', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Soepstengels wit', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Soepstengels volkoren', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.99E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Pompoenpitten broodjes', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.88E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Witte kadetjes', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.66E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Bruine kadetjes', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.66E0, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Tomaten feta broodje', supplier_id: 1, article_category_id: 5, unit: 'stuk', + note: 'bio', availability: true, manufacturer: 'De Bakker', origin: 'NL', price: 0.11E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladereep Melk (37%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'bio', availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladereep Puur (68%)', supplier_id: 2, article_category_id: 14, unit: '90gr', + note: 'bio', availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladereep Drie Mensen Melk (40%)', supplier_id: 2, article_category_id: 14, + unit: '90gr', note: 'bio', availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladereep Drie Mensen Puur (75%)', supplier_id: 2, article_category_id: 14, + unit: '90gr', note: 'bio', availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.22E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Chocoladereep Zwaan Puur (75%)', supplier_id: 2, article_category_id: 14, + unit: '120gr', note: 'bio', availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.66E1, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Cacao nibs', supplier_id: 2, article_category_id: 14, unit: '1 kg', note: 'bio', + availability: true, manufacturer: 'Chocolademakkers', origin: 'NL', price: 0.10E2, tax: 6.0, deposit: 0.0, unit_quantity: 1) +Article.create!(name: 'Kaas Koe-jong', supplier_id: 3, article_category_id: 8, unit: 'kg', note: 'bio', + availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.88E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- jong belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 12) +Article.create!(name: 'Kaas koe- extra belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'kaas Koe- oud', supplier_id: 3, article_category_id: 8, unit: 'kg', note: 'bio', + availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.1375E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'kaas koe -overjarig', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas Koe-brandnetel jong', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- brandnetel jong belegen', supplier_id: 3, article_category_id: 8, + unit: 'kg', note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- brandnetel belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas Koe-komijn jong', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.99E1, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- komijn jong belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Kaas koe- komijn belegen', supplier_id: 3, article_category_id: 8, unit: 'kg', + note: 'bio', availability: true, manufacturer: 'Kaasboerderij', origin: 'NL', price: 0.11E2, tax: 6.0, deposit: 0.0, unit_quantity: 8) +Article.create!(name: 'Cashewnoten', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'bio', + availability: true, price: 0.4444E2, tax: 6.0, deposit: 0.0, unit_quantity: 22, order_number: ':b936051') +Article.create!(name: 'Hazel wit', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'bio', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':9e3f85b') +Article.create!(name: 'Hazel bruin', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'bio', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':d278041') +Article.create!(name: 'Amandel Bruin Spaans', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.999E1, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':0b51a8d') +Article.create!(name: 'Paranoten (bio)', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 20, order_number: ':01e59e3') +Article.create!(name: 'Bio walnoten light halfjes', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':7ff8587') +Article.create!(name: 'Pijnboompitten', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':aa88d9f') +Article.create!(name: 'Pompoen', supplier_id: 4, article_category_id: 13, unit: 'kg', note: 'bio', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':e63069b') +Article.create!(name: 'Zonnepitten (bio)', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.999E1, tax: 6.0, deposit: 0.0, unit_quantity: 25, order_number: ':0428388') +Article.create!(name: 'Amandel Wit Spaans', supplier_id: 4, article_category_id: 13, unit: 'kg', + note: 'bio', availability: true, price: 0.66666E3, tax: 6.0, deposit: 0.0, unit_quantity: 10, order_number: ':a8f0734') +Article.create!(name: 'Cashew', supplier_id: 4, article_category_id: 13, unit: 'kg', availability: true, + price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':1d26958') +Article.create!(name: 'Amandelen geblancheerd', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':31439e2') +Article.create!(name: 'Amandelen naturel', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':9c49374') +Article.create!(name: 'Walnoot ELH hafjes', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.4444E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':92907d1') +Article.create!(name: 'Walnoot ELP stukjes', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':395640e') +Article.create!(name: 'Paranoten', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':710acbb') +Article.create!(name: 'Macadamia Stijl 0', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':bbaf40b') +Article.create!(name: 'Pecan', supplier_id: 4, article_category_id: 13, unit: 'kg', availability: true, + price: 0.55555E3, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':7958183') +Article.create!(name: 'Hazelnoten naturel', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.6666E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':50392a8') +Article.create!(name: 'Hazelnoten geblancheerd', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':4fe6525') +Article.create!(name: 'Gemengde Noten', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.333E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c051b22') +Article.create!(name: "Pinda's", supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.777E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':f507577') +Article.create!(name: "Vliespinda's klein", supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.8888E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':ce563bb') +Article.create!(name: 'Medjoul dadels', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.3333E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':8232061') +Article.create!(name: 'Turkse Abrikozen ongezwaveld', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':185084f') +Article.create!(name: 'Turkse Abrikozen gezwaveld', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':2b2fb20') +Article.create!(name: 'Spaanse Vijgen', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.444E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':82590b1') +Article.create!(name: 'Turkse Vijgen', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.555E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':cabeeb6') +Article.create!(name: 'Zure Abrikozen Zuid Afrika', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':2ac18b7') +Article.create!(name: 'Blauwe rozijnen Flames', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':16bfa75') +Article.create!(name: 'Gele Rozijnen', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':1c59324') +Article.create!(name: 'Rode Rozijnen', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.1111E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c3fcd84') +Article.create!(name: 'Cranberries heel', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.222E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':921c168') +Article.create!(name: 'Gedroogde Appeltjes', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.555E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':902c67b') +Article.create!(name: 'Gedroogde pruimen zonder pit', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.222E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':a847f91') +Article.create!(name: 'Pompoenpitten', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.111E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':535645f') +Article.create!(name: 'Zonnenbloepitten', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.666E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':4ab9a83') +Article.create!(name: 'Lijnzaad', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55E0, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':04be223') +Article.create!(name: 'Maanzaad', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.7777E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':ec5b2b9') +Article.create!(name: 'Pijnboompitten medium china', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':0e5b0b8') +Article.create!(name: 'Goji bessen', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':d52ee00') +Article.create!(name: 'Mulberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.5555E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':5f46bd5') +Article.create!(name: 'Gepelde Hennep', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.5555E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':c39165b') +Article.create!(name: 'Incaberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.888E1, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':8d44fe7') +Article.create!(name: 'Blueberries', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.2222E2, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':9a95422') +Article.create!(name: 'Chia zaad', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55555E3, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':416d57b') +Article.create!(name: 'Cocos Rasp', supplier_id: 4, article_category_id: 13, unit: 'kg', + availability: true, price: 0.55E0, tax: 6.0, deposit: 0.0, unit_quantity: 1, order_number: ':b3f65e4') ## Members & groups -User.create!(:id => 1, :nick => "admin", :password => "secret", :first_name => "Anton", :last_name => "Administrator", :email => "admin@foo.test", :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00') -User.create!(:id => 2, :nick => "john", :password => "secret", :first_name => "John", :last_name => "Doe", :email => "john@doe.test", :created_on => 'Sun, 19 Jan 2014 17:38:22 UTC +00:00') -User.create!(:id => 3, :nick => "peter", :password => "secret", :first_name => "Peter", :last_name => "Pieterssen", :email => "peter@pieterssen.test", :created_on => 'Sat, 25 Jan 2014 20:20:36 UTC +00:00') -User.create!(:id => 4, :nick => "jan", :password => "secret", :first_name => "Jan", :last_name => "Klaassen", :email => "jan@klaassen.test", :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00') -User.create!(:id => 5, :nick => "mary", :password => "secret", :first_name => "Marie", :last_name => "Klaassen", :email => "mary@klaassen.test", :created_on => 'Mon, 03 Feb 2014 11:47:17 UTC +00:00') +User.create!(id: 1, nick: 'admin', password: 'secret', first_name: 'Anton', last_name: 'Administrator', + email: 'admin@foo.test', created_on: 'Wed, 15 Jan 2014 16:15:33 UTC +00:00') +User.create!(id: 2, nick: 'john', password: 'secret', first_name: 'John', last_name: 'Doe', + email: 'john@doe.test', created_on: 'Sun, 19 Jan 2014 17:38:22 UTC +00:00') +User.create!(id: 3, nick: 'peter', password: 'secret', first_name: 'Peter', last_name: 'Pieterssen', + email: 'peter@pieterssen.test', created_on: 'Sat, 25 Jan 2014 20:20:36 UTC +00:00') +User.create!(id: 4, nick: 'jan', password: 'secret', first_name: 'Jan', last_name: 'Klaassen', + email: 'jan@klaassen.test', created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00') +User.create!(id: 5, nick: 'mary', password: 'secret', first_name: 'Marie', last_name: 'Klaassen', + email: 'mary@klaassen.test', created_on: 'Mon, 03 Feb 2014 11:47:17 UTC +00:00') -Workgroup.create!(:id => 1, :name => "Admins", :description => "Beheerders", :account_balance => 0.0, :created_on => 'Wed, 15 Jan 2014 16:15:33 UTC +00:00', :role_admin => true, :role_suppliers => true, :role_article_meta => true, :role_finance => true, :role_orders => true, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 2, :name => "Financiën", :account_balance => 0.0, :created_on => 'Sun, 19 Jan 2014 17:40:03 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => true, :role_orders => false, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 3, :name => "Bestellen", :account_balance => 0.0, :created_on => 'Thu, 20 Feb 2014 14:44:47 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => true, :role_finance => false, :role_orders => true, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Workgroup.create!(:id => 4, :name => "Assortiment", :account_balance => 0.0, :created_on => 'Wed, 09 Apr 2014 12:24:55 UTC +00:00', :role_admin => false, :role_suppliers => true, :role_article_meta => true, :role_finance => false, :role_orders => false, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 5, :name => "Admin Administrator", :account_balance => 0.0, :created_on => 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :stats => { :jobs_size => 0, :orders_sum => 1021.74 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => true) -Ordergroup.create!(:id => 6, :name => "Peter's huis", :account_balance => -0.35E2, :created_on => 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "Piet Pieterssen", :stats => { :jobs_size => 0, :orders_sum => 60.96 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 7, :name => "Jan Klaassen", :account_balance => -0.35E2, :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "Jan Klaassen", :stats => { :jobs_size => 0, :orders_sum => 0 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) -Ordergroup.create!(:id => 8, :name => "John Doe", :account_balance => 0.90E2, :created_on => 'Wed, 09 Apr 2014 12:23:29 UTC +00:00', :role_admin => false, :role_suppliers => false, :role_article_meta => false, :role_finance => false, :role_orders => false, :contact_person => "John Doe", :stats => { :jobs_size => 0, :orders_sum => 0 }, :next_weekly_tasks_number => 8, :ignore_apple_restriction => false) +Workgroup.create!(id: 1, name: 'Admins', description: 'Beheerders', account_balance: 0.0, + created_on: 'Wed, 15 Jan 2014 16:15:33 UTC +00:00', role_admin: true, role_suppliers: true, role_article_meta: true, role_finance: true, role_orders: true, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 2, name: 'Financiën', account_balance: 0.0, + created_on: 'Sun, 19 Jan 2014 17:40:03 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: true, role_orders: false, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 3, name: 'Bestellen', account_balance: 0.0, + created_on: 'Thu, 20 Feb 2014 14:44:47 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: true, role_finance: false, role_orders: true, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Workgroup.create!(id: 4, name: 'Assortiment', account_balance: 0.0, + created_on: 'Wed, 09 Apr 2014 12:24:55 UTC +00:00', role_admin: false, role_suppliers: true, role_article_meta: true, role_finance: false, role_orders: false, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 5, name: 'Admin Administrator', account_balance: 0.0, + created_on: 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, stats: { jobs_size: 0, orders_sum: 1021.74 }, next_weekly_tasks_number: 8, ignore_apple_restriction: true) +Ordergroup.create!(id: 6, name: "Peter's huis", account_balance: -0.35E2, + created_on: 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'Piet Pieterssen', stats: { jobs_size: 0, orders_sum: 60.96 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 7, name: 'Jan Klaassen', account_balance: -0.35E2, + created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'Jan Klaassen', stats: { jobs_size: 0, orders_sum: 0 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) +Ordergroup.create!(id: 8, name: 'John Doe', account_balance: 0.90E2, + created_on: 'Wed, 09 Apr 2014 12:23:29 UTC +00:00', role_admin: false, role_suppliers: false, role_article_meta: false, role_finance: false, role_orders: false, contact_person: 'John Doe', stats: { jobs_size: 0, orders_sum: 0 }, next_weekly_tasks_number: 8, ignore_apple_restriction: false) -Membership.create!(:group_id => 1, :user_id => 1) -Membership.create!(:group_id => 5, :user_id => 1) -Membership.create!(:group_id => 2, :user_id => 2) -Membership.create!(:group_id => 8, :user_id => 2) -Membership.create!(:group_id => 6, :user_id => 3) -Membership.create!(:group_id => 7, :user_id => 4) -Membership.create!(:group_id => 8, :user_id => 4) -Membership.create!(:group_id => 3, :user_id => 4) -Membership.create!(:group_id => 7, :user_id => 5) -Membership.create!(:group_id => 3, :user_id => 5) -Membership.create!(:group_id => 4, :user_id => 5) +Membership.create!(group_id: 1, user_id: 1) +Membership.create!(group_id: 5, user_id: 1) +Membership.create!(group_id: 2, user_id: 2) +Membership.create!(group_id: 8, user_id: 2) +Membership.create!(group_id: 6, user_id: 3) +Membership.create!(group_id: 7, user_id: 4) +Membership.create!(group_id: 8, user_id: 4) +Membership.create!(group_id: 3, user_id: 4) +Membership.create!(group_id: 7, user_id: 5) +Membership.create!(group_id: 3, user_id: 5) +Membership.create!(group_id: 4, user_id: 5) ## Orders & OrderArticles @@ -181,10 +308,15 @@ seed_group_orders ## Finances -FinancialTransactionType.create!(:id => 1, :name => "Foodcoop", :financial_transaction_class_id => 1) +FinancialTransactionType.create!(id: 1, name: 'Foodcoop', financial_transaction_class_id: 1) -FinancialTransaction.create!(:id => 1, :ordergroup_id => 5, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 3, :ordergroup_id => 6, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 4, :ordergroup_id => 7, :amount => -0.35E2, :note => "Membership fee for ordergroup", :user_id => 1, :created_on => 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 5, :ordergroup_id => 5, :amount => 0.35E2, :note => "payment", :user_id => 2, :created_on => 'Wed, 05 Feb 2014 16:49:24 UTC +00:00', :financial_transaction_type_id => 1) -FinancialTransaction.create!(:id => 6, :ordergroup_id => 8, :amount => 0.90E2, :note => "Bank transfer", :user_id => 2, :created_on => 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', :financial_transaction_type_id => 1) +FinancialTransaction.create!(id: 1, ordergroup_id: 5, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Sat, 18 Jan 2014 00:38:48 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 3, ordergroup_id: 6, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Sat, 25 Jan 2014 20:20:37 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 4, ordergroup_id: 7, amount: -0.35E2, + note: 'Membership fee for ordergroup', user_id: 1, created_on: 'Mon, 27 Jan 2014 16:22:14 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 5, ordergroup_id: 5, amount: 0.35E2, note: 'payment', user_id: 2, + created_on: 'Wed, 05 Feb 2014 16:49:24 UTC +00:00', financial_transaction_type_id: 1) +FinancialTransaction.create!(id: 6, ordergroup_id: 8, amount: 0.90E2, note: 'Bank transfer', user_id: 2, + created_on: 'Mon, 17 Feb 2014 16:19:34 UTC +00:00', financial_transaction_type_id: 1) diff --git a/doc/API.md b/doc/API.md index 2e09cfa4..f295e82f 100644 --- a/doc/API.md +++ b/doc/API.md @@ -5,9 +5,11 @@ like listing open orders, updating the ordergroup's order, and listing financial transactions. Not all Foodsoft functionality is available through the API, but we're open for new additions. -The API is documented using [Open API 2.0](https://github.com/OAI/OpenAPI-Specification) -/ [Swagger](https://swagger.io/) in [swagger.v1.yml](swagger.v1.yml). +The API is documented using [Open API 3.0.1](https://github.com/OAI/OpenAPI-Specification) +/ [Swagger](https://swagger.io/) in [swagger.yaml](/swagger/v1/swagger.yaml). This provides a machine-readable reference that is used to provide documentation. +It is generated by [rswag](https://github.com/rswag) wich also provides api-tests. +It can be generated running `RAILS_ENV=test rails rswag`. **Note:** the current OAuth scopes may be subject to change, until the next release of Foodsoft. diff --git a/doc/SETUP_DEVELOPMENT.md b/doc/SETUP_DEVELOPMENT.md index 319c2787..ac086416 100644 --- a/doc/SETUP_DEVELOPMENT.md +++ b/doc/SETUP_DEVELOPMENT.md @@ -12,7 +12,7 @@ If instead you just want to run Foodsoft without changing its code, please refer **System requirements**: [rbenv](https://github.com/rbenv/rbenv), -[Ruby 2.6+](https://www.ruby-lang.org/en/downloads/), +[Ruby 2.7+](https://www.ruby-lang.org/en/downloads/), [Bundler](http://bundler.io/), [MySQL](http://mysql.com/) / [SQLite](http://sqlite.org/), [Redis](http://redis.io/) (optional). @@ -32,6 +32,10 @@ If instead you just want to run Foodsoft without changing its code, please refer Have a look how to avoid that in the [Docker Development Setup](./SETUP_DEVELOPMENT_DOCKER.md#prerequisites-windows-only) instructions. +1. Install developement packages. For Debian/Ubuntu: + + sudo apt install default-libmysqlclient-dev libmagic-dev libxml2-dev libxslt-dev + 1. Install and setup rbenv and Bundler. For Debian/Ubuntu: sudo apt install rbenv @@ -175,4 +179,4 @@ within a docker image. While the default [`Dockerfile`](../Dockerfile) is setup use docker-compose (using [`docker-compose-dev.yml`](../docker-compose-dev.yml)) to setup the whole stack at once. -See [Setup Development Docker](./SETUP_DEVELOPMENT_DOCKER.md) for a detailed description. \ No newline at end of file +See [Setup Development Docker](./SETUP_DEVELOPMENT_DOCKER.md) for a detailed description. diff --git a/doc/swagger.v1.yml b/doc/swagger.v1.yml deleted file mode 100644 index d8e793d3..00000000 --- a/doc/swagger.v1.yml +++ /dev/null @@ -1,1106 +0,0 @@ -swagger: '2.0' -info: - title: Foodsoft API v1 - version: '1.0.0' - description: > - [Foodsoft](https://github.com/foodcoops/foodsoft) is web-based software to manage - a non-profit food coop (product catalog, ordering, accounting, job scheduling). - - - This is a description of Foodsoft's API v1. - - - Note that each food cooperative typically has their own instance (on a shared - server or their own installation), and there are just as many APIs (if the Foodsoft - version is recent enough). - This API description points to the default development url with the default - Foodsoft scope - that would be [http://localhost:3000/f](http://localhost:3000/f). - - You may find the search parameters for index endpoints lacking. They are not - documented here, because there are too many combinations. For now, you'll need - to resort to [Ransack](https://github.com/activerecord-hackery/ransack) and - looking at Foodsoft's `ransackable_*` model class methods. -externalDocs: - description: General Foodsoft API documentation - url: https://github.com/foodcoops/foodsoft/blob/master/doc/API.md - -# development url with default scope -host: localhost:3000 -schemes: - - 'http' -basePath: /f/api/v1 - -produces: - - 'application/json' - -paths: - /user: - get: - summary: info about the currently logged-in user - tags: - - 1. User - responses: - 200: - description: success - schema: - type: object - properties: - user: - $ref: '#/definitions/User' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['user:read', 'user:write'] - - /user/financial_overview: - get: - summary: financial summary about the currently logged-in user - tags: - - 1. User - - 6. FinancialTransaction - responses: - 200: - description: success - schema: - type: object - properties: - financial_overview: - $ref: '#/definitions/FinanceOverview' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['finance:user'] - - /user/financial_transactions: - get: - summary: financial transactions of the member's ordergroup - tags: - - 1. User - - 6. FinancialTransaction - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - financial_transactions: - type: array - items: - $ref: '#/definitions/FinancialTransaction' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup or missing scope - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['finance:user'] - post: - summary: create new financial transaction (requires enabled self service) - tags: - - 1. User - - 6. FinancialTransaction - parameters: - - in: body - name: body - description: financial transaction to create - required: true - schema: - $ref: '#/definitions/FinancialTransactionForCreate' - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction: - $ref: '#/definitions/FinancialTransaction' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup, is below minimum balance, self service is disabled, or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: financial transaction type not found - schema: - $ref: '#/definitions/Error404' - 422: - description: invalid parameter value - schema: - $ref: '#/definitions/Error422' - /user/financial_transactions/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find financial transaction by id - tags: - - 1. User - - 6. FinancialTransaction - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction: - $ref: '#/definitions/FinancialTransaction' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['finance:user'] - - /user/group_order_articles: - get: - summary: group order articles - tags: - - 1. User - - 2. Order - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - - $ref: '#/parameters/q_ordered' - responses: - 200: - description: success - schema: - type: object - properties: - group_order_articles: - type: array - items: - $ref: '#/definitions/GroupOrderArticle' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup or missing scope - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['group_orders:user'] - post: - summary: create new group order article - tags: - - 1. User - - 2. Order - parameters: - - in: body - name: body - description: group order article to create - required: true - schema: - $ref: '#/definitions/GroupOrderArticleForCreate' - responses: - 200: - description: success - schema: - type: object - properties: - group_order_article: - $ref: '#/definitions/GroupOrderArticle' - order_article: - $ref: '#/definitions/OrderArticle' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: order article not found in open orders - schema: - $ref: '#/definitions/Error404' - 422: - description: invalid parameter value or group order article already exists - schema: - $ref: '#/definitions/Error422' - /user/group_order_articles/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find group order article by id - tags: - - 1. User - - 2. Order - responses: - 200: - description: success - schema: - type: object - properties: - group_order_article: - $ref: '#/definitions/GroupOrderArticle' - order_article: - $ref: '#/definitions/OrderArticle' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['group_orders:user'] - patch: - summary: update a group order article (but delete if quantity and tolerance are zero) - tags: - - 1. User - - 2. Order - parameters: - - $ref: '#/parameters/idInUrl' - - in: body - name: body - description: group order article update - required: true - schema: - $ref: '#/definitions/GroupOrderArticleForUpdate' - responses: - 200: - description: success - schema: - type: object - properties: - group_order_article: - $ref: '#/definitions/GroupOrderArticle' - order_article: - $ref: '#/definitions/OrderArticle' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: order article not found in open orders - schema: - $ref: '#/definitions/Error404' - 422: - description: invalid parameter value - schema: - $ref: '#/definitions/Error422' - delete: - summary: remove group order article - tags: - - 1. User - - 2. Order - parameters: - - $ref: '#/parameters/idInUrl' - responses: - 200: - description: success - schema: - type: object - properties: - group_order_article: - $ref: '#/definitions/GroupOrderArticle' - order_article: - $ref: '#/definitions/OrderArticle' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope - schema: - $ref: '#/definitions/Error403' - 404: - description: order article not found in open orders - schema: - $ref: '#/definitions/Error404' - - /financial_transactions: - get: - summary: financial transactions - tags: - - 6. FinancialTransaction - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - financial_transactions: - type: array - items: - $ref: '#/definitions/FinancialTransaction' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['finance:read', 'finance:write'] - /financial_transactions/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find financial transaction by id - tags: - - 6. FinancialTransaction - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction: - $ref: '#/definitions/FinancialTransaction' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['finance:read', 'finance:write'] - /orders: - get: - summary: orders - tags: - - 2. Order - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - orders: - type: array - items: - $ref: '#/definitions/Order' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['orders:read', 'orders:write'] - /orders/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find order by id - tags: - - 2. Order - responses: - 200: - description: success - schema: - type: object - properties: - order: - $ref: '#/definitions/Order' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['orders:read', 'orders:write'] - /order_articles: - get: - summary: order articles - tags: - - 2. Order - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - - $ref: '#/parameters/q_ordered' - responses: - 200: - description: success - schema: - type: object - properties: - order_articles: - type: array - items: - $ref: '#/definitions/OrderArticle' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['group_orders:user'] - /order_articles/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find order article by id - tags: - - 2. Order - responses: - 200: - description: success - schema: - type: object - properties: - order_article: - $ref: '#/definitions/OrderArticle' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['orders:read', 'orders:write'] - /article_categories: - get: - summary: article categories - tags: - - 2. Category - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - article_categories: - type: array - items: - $ref: '#/definitions/ArticleCategory' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - - security: - - foodsoft_auth: ['all'] - /article_categories/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find article category by id - tags: - - 2. Category - responses: - 200: - description: success - schema: - type: object - properties: - article_category: - $ref: '#/definitions/ArticleCategory' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['all'] - - /financial_transaction_classes: - get: - summary: financial transaction classes - tags: - - 2. Category - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction_classes: - type: array - items: - $ref: '#/definitions/FinancialTransactionClass' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - - security: - - foodsoft_auth: ['all'] - /financial_transaction_classes/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find financial transaction class by id - tags: - - 2. Category - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction_class: - $ref: '#/definitions/FinancialTransactionClass' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['all'] - - /financial_transaction_types: - get: - summary: financial transaction types - tags: - - 2. Category - parameters: - - $ref: '#/parameters/page' - - $ref: '#/parameters/per_page' - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction_types: - type: array - items: - $ref: '#/definitions/FinancialTransactionType' - meta: - $ref: '#/definitions/Meta' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - - security: - - foodsoft_auth: ['all'] - /financial_transaction_types/{id}: - parameters: - - $ref: '#/parameters/idInUrl' - get: - summary: find financial transaction type by id - tags: - - 2. Category - responses: - 200: - description: success - schema: - type: object - properties: - financial_transaction_type: - $ref: '#/definitions/FinancialTransactionType' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 404: - description: not found - schema: - $ref: '#/definitions/Error404' - security: - - foodsoft_auth: ['all'] - - /config: - get: - summary: configuration variables - tags: - - 7. General - responses: - 200: - description: success - schema: - type: object - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - 403: - description: missing scope or no permission - schema: - $ref: '#/definitions/Error403' - security: - - foodsoft_auth: ['config:user', 'config:read', 'config:write'] - /navigation: - get: - summary: navigation - tags: - - 7. General - responses: - 200: - description: success - schema: - type: object - properties: - navigation: - $ref: '#/definitions/Navigation' - 401: - description: not logged-in - schema: - $ref: '#/definitions/Error401' - security: - - foodsoft_auth: [] - -parameters: - # url parameters - idInUrl: - name: id - type: integer - in: path - minimum: 1 - required: true - - # query parameters - page: - name: page - type: integer - in: query - description: page number - minimum: 0 - default: 0 - per_page: - name: per_page - type: integer - in: query - description: items per page - minimum: 0 - default: 20 - - # non-ransack query parameters - q_ordered: - name: q[ordered] - type: string - in: query - description: "'member' show articles ordered by the user's ordergroup, 'all' by all members, and 'supplier' ordered at the supplier" - enum: ['member', 'all', 'supplier'] - -definitions: - # models - User: - type: object - properties: - id: - type: integer - name: - type: string - description: full name - email: - type: string - description: email address - locale: - type: string - description: language code - required: ['id', 'name', 'email'] - - FinancialTransactionForCreate: - type: object - properties: - amount: - type: number - description: amount credited (negative for a debit transaction) - financial_transaction_type_id: - type: integer - description: id of the type of the transaction - note: - type: string - description: note entered with the transaction - required: ['amount', 'financial_transaction_type_id', 'note'] - FinancialTransaction: - allOf: - - $ref: '#/definitions/FinancialTransactionForCreate' - - type: object - properties: - id: - type: integer - user_id: - type: ['integer', 'null'] - description: id of user who entered the transaction (may be null for deleted users or 0 for a system user) - user_name: - type: ['string', 'null'] - description: name of user who entered the transaction (may be null or empty string for deleted users or system users) - financial_transaction_type_name: - type: string - description: name of the type of the transaction - created_at: - type: string - format: date-time - description: when the transaction was entered - required: ['id', 'user_id', 'user_name', 'financial_transaction_type_name', 'created_at'] - - FinancialTransactionClass: - type: object - properties: - id: - type: integer - name: - type: string - description: full name - required: ['id', 'name'] - - FinancialTransactionType: - type: object - properties: - id: - type: integer - name: - type: string - description: full name - name_short: - type: ['string', 'null'] - description: short name (used for bank transfers) - bank_account_id: - type: ['integer', 'null'] - description: id of the bank account used for this transaction type - bank_account_name: - type: ['string', 'null'] - description: name of the bank account used for this transaction type - bank_account_iban: - type: ['string', 'null'] - description: IBAN of the bank account used for this transaction type - financial_transaction_class_id: - type: integer - description: id of the class of the transaction - financial_transaction_class_name: - type: string - description: name of the class of the transaction - required: ['id', 'name', 'financial_transaction_class_id', 'financial_transaction_class_name'] - - FinanceOverview: - type: object - properties: - account_balance: - type: number - description: booked accout balance of ordergroup - available_funds: - type: number - description: fund available to order articles - financial_transaction_class_sums: - type: array - items: - type: object - properties: - id: - type: integer - description: id of the financial transaction class - name: - type: string - description: name of the financial transaction class - amount: - type: number - description: sum of the amounts belonging to the financial transaction class - required: ['id', 'name', 'amount'] - required: ['account_balance', 'available_funds', 'financial_transaction_class_sums'] - - ArticleCategory: - type: object - properties: - id: - type: integer - name: - type: string - required: ['id', 'name'] - - Order: - type: object - properties: - id: - type: integer - name: - type: string - description: name of the order's supplier (or stock) - starts: - type: string - format: date-time - description: when the order was opened - ends: - type: ['string', 'null'] - format: date-time - description: when the order will close or was closed - boxfill: - type: ['string', 'null'] - format: date-time - description: when the order will enter or entered the boxfill phase - pickup: - type: ['string', 'null'] - format: date - description: pickup date - is_open: - type: boolean - description: if the order is currently open or not - is_boxfill: - type: boolean - description: if the order is currently in the boxfill phase or not - - Article: - type: object - properties: - id: - type: integer - name: - type: string - supplier_id: - type: integer - description: id of supplier, or 0 for stock articles - supplier_name: - type: ['string', 'null'] - description: name of the supplier, or null for stock articles - unit: - type: string - description: amount of each unit, e.g. "100 g" or "kg" - unit_quantity: - type: integer - description: units can only be ordered from the supplier in multiples of unit_quantity - note: - type: ['string', 'null'] - description: generic note - manufacturer: - type: ['string', 'null'] - description: manufacturer - origin: - type: ['string', 'null'] - description: origin, preferably (starting with a) 2-letter ISO country code - article_category_id: - type: integer - description: id of article category - quantity_available: - type: integer - description: number of units available (only present on stock articles) - required: ['id', 'name', 'supplier_id', 'supplier_name', 'unit', 'unit_quantity', 'note', 'manufacturer', 'origin', 'article_category_id'] - - OrderArticle: - type: object - properties: - id: - type: integer - order_id: - type: integer - description: id of order this order article belongs to - price: - type: number - format: float - description: foodcoop price - quantity: - type: integer - description: number of units ordered by members - tolerance: - type: integer - description: number of extra units that members are willing to buy to fill a box - units_to_order: - type: integer - description: number of units to order from the supplier - article: - $ref: '#/definitions/Article' - - GroupOrderArticleForUpdate: - type: object - properties: - quantity: - type: integer - description: number of units ordered by the user's ordergroup - tolerance: - type: integer - description: number of extra units the user's ordergroup is willing to buy for filling a box - GroupOrderArticleForCreate: - allOf: - - $ref: '#/definitions/GroupOrderArticleForUpdate' - - type: object - properties: - order_article_id: - type: integer - description: id of order article - GroupOrderArticle: - allOf: - - $ref: '#/definitions/GroupOrderArticleForCreate' - - type: object - properties: - id: - type: integer - result: - type: number - format: float - description: number of units the user's ordergroup will receive or has received - total_price: - type: number - format: float - description: total price of this group order article - - Navigation: - type: array - items: - type: object - properties: - name: - type: string - description: title - url: - type: string - description: link - items: - $ref: '#/definitions/Navigation' - required: ['name'] - minProperties: 2 # name+url or name+items - - # collection meta object in root of a response - Meta: - type: object - properties: - page: - type: integer - description: page number of the returned collection - per_page: - type: integer - description: number of items per page - total_pages: - type: integer - description: total number of pages - total_count: - type: integer - description: total number of items in the collection - required: ['page', 'per_page', 'total_pages', 'total_count'] - - Error: - type: object - properties: - error: - type: string - description: error code - error_description: - type: string - description: human-readable error message (localized) - Error404: - type: object - properties: - error: - type: string - description: 'not_found' - error_description: - $ref: '#/definitions/Error/properties/error_description' - Error401: - type: object - properties: - error: - type: string - description: 'unauthorized' - error_description: - $ref: '#/definitions/Error/properties/error_description' - Error403: - type: object - properties: - error: - type: string - description: 'forbidden or invalid_scope' - error_description: - $ref: '#/definitions/Error/properties/error_description' - Error422: - type: object - properties: - error: - type: string - description: unprocessable entity - error_description: - $ref: '#/definitions/Error/properties/error_description' - - -securityDefinitions: - foodsoft_auth: - type: oauth2 - flow: implicit - authorizationUrl: http://localhost:3000/f/oauth/authorize - scopes: - config:user: reading Foodsoft configuration for regular users - config:read: reading Foodsoft configuration values - config:write: reading and updating Foodsoft configuration values - finance:user: accessing your own financial transactions - finance:read: reading all financial transactions - finance:write: reading and creating financial transactions - user:read: reading your own user profile - user:write: reading and updating your own user profile - offline_access: retain access after user has logged out diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 0a8b3fec..b0a325db 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -11,6 +11,7 @@ services: build: context: . dockerfile: Dockerfile-dev + platform: linux/x86_64 command: ./proc-start worker volumes: - bundle:/usr/local/bundle diff --git a/lib/date_time_attribute_validate.rb b/lib/date_time_attribute_validate.rb deleted file mode 100644 index 08138d02..00000000 --- a/lib/date_time_attribute_validate.rb +++ /dev/null @@ -1,62 +0,0 @@ -# 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 -> { self.send("#{attribute}_datetime_value_valid") } - - # allow resetting the field to nil - before_validation do - if self.instance_variable_get("@#{attribute}_is_set") - date = self.instance_variable_get("@#{attribute}_date_value") - time = self.instance_variable_get("@#{attribute}_time_value") - if date.blank? && time.blank? - self.send("#{attribute}=", nil) - end - end - end - - # remember old date and time values - define_method("#{attribute}_date_value=") do |val| - self.instance_variable_set("@#{attribute}_is_set", true) - self.instance_variable_set("@#{attribute}_date_value", val) - self.send("#{attribute}_date=", val) rescue nil - end - define_method("#{attribute}_time_value=") do |val| - self.instance_variable_set("@#{attribute}_is_set", true) - self.instance_variable_set("@#{attribute}_time_value", val) - self.send("#{attribute}_time=", val) rescue nil - end - - # fallback to field when values are not set - define_method("#{attribute}_date_value") do - self.instance_variable_get("@#{attribute}_date_value") || self.send("#{attribute}_date").try { |e| e.strftime('%Y-%m-%d') } - end - define_method("#{attribute}_time_value") do - self.instance_variable_get("@#{attribute}_time_value") || self.send("#{attribute}_time").try { |e| e.strftime('%H:%M') } - end - - private - - # validate date and time - define_method("#{attribute}_datetime_value_valid") do - date = self.instance_variable_get("@#{attribute}_date_value") - unless date.blank? || (Date.parse(date) rescue nil) - errors.add(attribute, "is not a valid date") # @todo I18n - end - time = self.instance_variable_get("@#{attribute}_time_value") - unless time.blank? || (Time.parse(time) rescue nil) - errors.add(attribute, "is not a valid time") # @todo I18n - end - end - end - end - end -end diff --git a/lib/foodsoft_date_util.rb b/lib/foodsoft_date_util.rb deleted file mode 100644 index 98dc1c61..00000000 --- a/lib/foodsoft_date_util.rb +++ /dev/null @@ -1,28 +0,0 @@ -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 = (schedule.next_occurrence(from).to_time rescue nil) - end - if options && options[:time] && occ - occ = occ.beginning_of_day.advance(seconds: Time.parse(options[:time]).seconds_since_midnight) - end - occ - end - - # @param p [String, Symbol, Hash, IceCube::Rule] What to return a rule from. - # @return [IceCube::Rule] Recurring rule - def self.rule_from(p) - if p.is_a? String - IceCube::Rule.from_ical(p) - elsif p.is_a? Hash - IceCube::Rule.from_hash(p) - else - p - end - end -end diff --git a/lib/foodsoft_file.rb b/lib/foodsoft_file.rb deleted file mode 100644 index 95d06c60..00000000 --- a/lib/foodsoft_file.rb +++ /dev/null @@ -1,25 +0,0 @@ -# Foodsoft-file import -class FoodsoftFile - # parses a string from a foodsoft-file - # returns two arrays with articles and outlisted_articles - # the parsed article is a simple hash - def self.parse(file, options = {}) - SpreadsheetFile.parse file, options do |row, row_index| - next if row[2].blank? - - article = { :order_number => row[1], - :name => row[2], - :note => row[3], - :manufacturer => row[4], - :origin => row[5], - :unit => row[6], - :price => row[7], - :tax => row[8], - :deposit => (row[9].nil? ? "0" : row[9]), - :unit_quantity => row[10], - :article_category => row[13] } - status = row[0] && row[0].strip.downcase == 'x' ? :outlisted : nil - yield status, article, row_index - end - end -end diff --git a/lib/order_txt.rb b/lib/order_txt.rb deleted file mode 100644 index 5ad1fba6..00000000 --- a/lib/order_txt.rb +++ /dev/null @@ -1,30 +0,0 @@ -class OrderTxt - def initialize(order, options = {}) - @order = order - end - - # Renders the fax-text-file - # e.g. for easier use with online-fax-software, which don't accept pdf-files - def to_txt - supplier = @order.supplier - contact = FoodsoftConfig[:contact].symbolize_keys - text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name]) - text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank? - text += "\n" + I18n.t('orders.fax.delivery_day') - text += "\n\n#{supplier.name}\n#{supplier.address}\n#{Supplier.human_attribute_name(:fax)}: #{supplier.fax}\n\n" - text += "****** " + I18n.t('orders.fax.to_address') + "\n\n" - text += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n" - text += "****** " + I18n.t('orders.fax.articles') + "\n\n" - text += "%8s %8s %s\n" % [I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'), I18n.t('orders.fax.name')] - # now display all ordered articles - @order.order_articles.ordered.includes([:article, :article_price]).each do |oa| - text += "%8s %8d %s\n" % [oa.article.order_number, oa.units_to_order.to_i, oa.article.name] - end - text - end - - # Helper method to test pdf via rails console: OrderTxt.new(order).save_tmp - def save_tmp - File.write("#{Rails.root}/tmp/#{self.class.to_s.underscore}.txt", to_csv.force_encoding("UTF-8")) - end -end diff --git a/lib/tasks/foodsoft.rake b/lib/tasks/foodsoft.rake index 760cd5bc..caa54a1a 100644 --- a/lib/tasks/foodsoft.rake +++ b/lib/tasks/foodsoft.rake @@ -1,61 +1,61 @@ # put in here all foodsoft tasks # => :environment loads the environment an gives easy access to the application -namespace :foodsoft do - desc "Finish ended orders" - task :finish_ended_orders => :environment do +namespace :foodsoft do # rubocop:disable Metrics/BlockLength + desc 'Finish ended orders' + task finish_ended_orders: :environment do Order.finish_ended! end - desc "Notify users of upcoming tasks" - task :notify_upcoming_tasks => :environment do + desc 'Notify users of upcoming tasks' + task notify_upcoming_tasks: :environment do tasks = Task.where(done: false, due_date: 1.day.from_now.to_date) for task in tasks rake_say "Send notifications for #{task.name} to .." for user in task.users - if user.settings.notify['upcoming_tasks'] - Mailer.deliver_now_with_user_locale user do - Mailer.upcoming_tasks(user, task) - end + next unless user.settings.notify['upcoming_tasks'] + + Mailer.deliver_now_with_user_locale user do + Mailer.upcoming_tasks(user, task) end end end end - desc "Notify workgroup of upcoming weekly task" - task :notify_users_of_weekly_task => :environment do - tasks = Task.where(done: false, due_date: 7.day.from_now.to_date) + desc 'Notify workgroup of upcoming weekly task' + task notify_users_of_weekly_task: :environment do + tasks = Task.where(done: false, due_date: 7.days.from_now.to_date) for task in tasks - unless task.enough_users_assigned? - workgroup = task.workgroup - if workgroup - rake_say "Notify workgroup: #{workgroup.name} for task #{task.name}" - for user in workgroup.users - if user.receive_email? - Mailer.deliver_now_with_user_locale user do - Mailer.not_enough_users_assigned(task, user) - end - end - end + next if task.enough_users_assigned? + + workgroup = task.workgroup + next unless workgroup + + rake_say "Notify workgroup: #{workgroup.name} for task #{task.name}" + for user in workgroup.users + next unless user.receive_email? + + Mailer.deliver_now_with_user_locale user do + Mailer.not_enough_users_assigned(task, user) end end end end - desc "Create upcoming periodic tasks" - task :create_upcoming_periodic_tasks => :environment do + desc 'Create upcoming periodic tasks' + task create_upcoming_periodic_tasks: :environment do for tg in PeriodicTaskGroup.all created_until = tg.create_tasks_for_upfront_days rake_say "created until #{created_until}" end end - desc "Parse incoming email on stdin (options: RECIPIENT=foodcoop.handling)" - task :parse_reply_email => :environment do + desc 'Parse incoming email on stdin (options: RECIPIENT=foodcoop.handling)' + task parse_reply_email: :environment do FoodsoftMailReceiver.received ENV.fetch('RECIPIENT', nil), STDIN.read end - desc "Start STMP server for incoming email (options: SMTP_SERVER_PORT=2525, SMTP_SERVER_HOST=0.0.0.0)" - task :reply_email_smtp_server => :environment do + desc 'Start STMP server for incoming email (options: SMTP_SERVER_PORT=2525, SMTP_SERVER_HOST=0.0.0.0)' + task reply_email_smtp_server: :environment do port = ENV['SMTP_SERVER_PORT'].present? ? ENV['SMTP_SERVER_PORT'].to_i : 2525 host = ENV.fetch('SMTP_SERVER_HOST', nil) rake_say "Started SMTP server for incoming email on port #{port}." @@ -64,8 +64,8 @@ namespace :foodsoft do server.join end - desc "Import and assign bank transactions" - task :import_and_assign_bank_transactions => :environment do + desc 'Import and assign bank transactions' + task import_and_assign_bank_transactions: :environment do BankAccount.find_each do |ba| importer = ba.find_connector next unless importer @@ -79,6 +79,19 @@ namespace :foodsoft do rake_say "#{ba.name}: imported #{importer.count}, assigned #{assign_count}" end end + + desc 'Prune attachments older than maximum age' + task prune_old_attachments: :environment do + if FoodsoftConfig[:attachment_retention_days] + rake_say "Pruning attachments older than #{FoodsoftConfig[:attachment_retention_days]} days" + ActiveStorage::Attachment.where("created_at < ?", FoodsoftConfig[:attachment_retention_days].days.ago).each do |attachment| + rake_say attachment.inspect + attachment.purge_later + end + else + rake_say "Please configure your app_config.yml accordingly:\nattachment_retention_days: " + end + end end # Helper diff --git a/lib/tasks/foodsoft_setup.rake b/lib/tasks/foodsoft_setup.rake index baa483d1..e7fe0a3b 100644 --- a/lib/tasks/foodsoft_setup.rake +++ b/lib/tasks/foodsoft_setup.rake @@ -9,14 +9,14 @@ module Colors end { - :black => 30, - :red => 31, - :green => 32, - :yellow => 33, - :blue => 34, - :magenta => 35, - :cyan => 36, - :white => 37 + black: 30, + red: 31, + green: 32, + yellow: 33, + blue: 34, + magenta: 35, + cyan: 36, + white: 37 }.each do |key, color_code| define_method key do |text| colorize(text, color_code) @@ -26,72 +26,73 @@ end include Colors namespace :foodsoft do - desc "Setup foodsoft" + desc 'Setup foodsoft' task :setup_development do - puts yellow "This task will help you get your foodcoop running in development." + puts yellow 'This task will help you get your foodcoop running in development.' setup_bundler setup_app_config setup_development setup_database setup_storage start_mailcatcher - puts yellow "All done! Your foodsoft setup should be running smoothly." + puts yellow 'All done! Your foodsoft setup should be running smoothly.' start_server end - desc "Setup foodsoft" + desc 'Setup foodsoft' task :setup_development_docker do - puts yellow "This task will help you get your foodcoop running in development via docker." + puts yellow 'This task will help you get your foodcoop running in development via docker.' setup_app_config setup_development setup_storage setup_run_rake_db_setup - puts yellow "All done! Your foodsoft setup should be running smoothly via docker." + puts yellow 'All done! Your foodsoft setup should be running smoothly via docker.' end namespace :setup do - desc "Initialize stock configuration" + desc 'Initialize stock configuration' task :stock_config do setup_app_config + setup_storage setup_development end end end def setup_bundler - puts yellow "Installing bundler if not installed..." + puts yellow 'Installing bundler if not installed...' %x(if [ -z `which bundle` ]; then gem install bundler --no-rdoc --no-ri; fi) - puts yellow "Executing bundle install..." - %x(bundle install) + puts yellow 'Executing bundle install...' + `bundle install` end def setup_database file = 'config/database.yml' if ENV['DATABASE_URL'] - puts blue "DATABASE_URL found, please remember to also set it when running Foodsoft" + puts blue 'DATABASE_URL found, please remember to also set it when running Foodsoft' return nil end return nil if skip?(file) - database = ask("What kind of database do you use?\nOptions:\n(1) MySQL\n(2) SQLite", ["1", "2"]) - if database == "1" - puts yellow "Using MySQL..." - %x(cp -p #{Rails.root.join("#{file}.MySQL_SAMPLE")} #{Rails.root.join(file)}) - elsif database == "2" - puts yellow "Using SQLite..." - %x(cp -p #{Rails.root.join("#{file}.SQLite_SAMPLE")} #{Rails.root.join(file)}) + database = ask("What kind of database do you use?\nOptions:\n(1) MySQL\n(2) SQLite", %w[1 2]) + if database == '1' + puts yellow 'Using MySQL...' + `cp -p #{Rails.root.join("#{file}.MySQL_SAMPLE")} #{Rails.root.join(file)}` + elsif database == '2' + puts yellow 'Using SQLite...' + `cp -p #{Rails.root.join("#{file}.SQLite_SAMPLE")} #{Rails.root.join(file)}` end reminder(file) - puts blue "IMPORTANT: Edit (rake-generated) config/database.yml with valid username and password for EACH env before continuing!" - finished = ask("Finished?\nOptions:\n(y) Yes", ["y"]) + puts blue 'IMPORTANT: Edit (rake-generated) config/database.yml with valid username and password for EACH env before continuing!' + finished = ask("Finished?\nOptions:\n(y) Yes", ['y']) setup_run_rake_db_setup if finished end def setup_run_rake_db_setup - Rake::Task["db:setup"].reenable - db_setup = capture_stdout { Rake::Task["db:setup"].invoke } + Rake::Task['db:setup'].reenable + db_setup = capture_stdout { Rake::Task['db:setup'].invoke } puts db_setup end @@ -101,7 +102,7 @@ def setup_app_config return nil if skip?(file) puts yellow "Copying #{file}..." - %x(cp -p #{sample} #{Rails.root.join(file)}) + `cp -p #{sample} #{Rails.root.join(file)}` reminder(file) end @@ -110,7 +111,7 @@ def setup_development return nil if skip?(file) puts yellow "Copying #{file}..." - %x(cp -p #{Rails.root.join("#{file}.SAMPLE")} #{Rails.root.join(file)}) + `cp -p #{Rails.root.join("#{file}.SAMPLE")} #{Rails.root.join(file)}` reminder(file) end @@ -119,18 +120,18 @@ def setup_storage return nil if skip?(file) puts yellow "Copying #{file}..." - %x(cp -p #{Rails.root.join("#{file}.SAMPLE")} #{Rails.root.join(file)}) + `cp -p #{Rails.root.join("#{file}.SAMPLE")} #{Rails.root.join(file)}` reminder(file) end def start_mailcatcher return nil if ENV['MAILCATCHER_PORT'] # skip when it has an existing Docker container - mailcatcher = ask("Do you want to start mailcatcher?\nOptions:\n(y) Yes\n(n) No", ["y", "n"]) - if mailcatcher === "y" - puts yellow "Starting mailcatcher at http://localhost:1080..." - %x(mailcatcher) - end + mailcatcher = ask("Do you want to start mailcatcher?\nOptions:\n(y) Yes\n(n) No", %w[y n]) + return unless mailcatcher === 'y' + + puts yellow 'Starting mailcatcher at http://localhost:1080...' + `mailcatcher` end def start_server @@ -143,7 +144,7 @@ def ask(question, answers = false) puts question input = STDIN.gets.chomp if input.blank? || (answers && !answers.include?(input)) - puts red "Your Input is not valid. Try again!" + puts red 'Your Input is not valid. Try again!' input = ask(question, answers) end input @@ -151,8 +152,11 @@ end def skip?(file) output = false - skip = ask(cyan("We found #{file}!\nOptions:\n(1) Skip step\n(2) Force rewrite"), ["1", "2"]) if File.exists?(Rails.root.join(file)) - output = true if skip == "1" + if File.exist?(Rails.root.join(file)) + skip = ask(cyan("We found #{file}!\nOptions:\n(1) Skip step\n(2) Force rewrite"), + %w[1 2]) + end + output = true if skip == '1' output end diff --git a/lib/tasks/multicoops.rake b/lib/tasks/multicoops.rake index f0c76b8f..1e5ef44d 100644 --- a/lib/tasks/multicoops.rake +++ b/lib/tasks/multicoops.rake @@ -4,23 +4,21 @@ namespace :multicoops do desc 'Runs a specific rake task for each registered foodcoop, use rake multicoops:run TASK=db:migrate' - task :run => :environment do + task run: :environment do task_to_run = ENV.fetch('TASK', nil) last_error = nil FoodsoftConfig.each_coop do |coop| - begin - rake_say "Run '#{task_to_run}' for #{coop}" - Rake::Task[task_to_run].execute - rescue => error - last_error = error - ExceptionNotifier.notify_exception(error, data: { foodcoop: coop }) - end + rake_say "Run '#{task_to_run}' for #{coop}" + Rake::Task[task_to_run].execute + rescue StandardError => e + last_error = e + ExceptionNotifier.notify_exception(e, data: { foodcoop: coop }) end raise last_error if last_error end desc 'Runs a specific rake task for a single coop, use rake mutlicoops:run_single TASK=db:migrate FOODCOOP=demo' - task :run_single => :environment do + task run_single: :environment do task_to_run = ENV.fetch('TASK', nil) FoodsoftConfig.select_foodcoop ENV.fetch('FOODCOOP', nil) rake_say "Run '#{task_to_run}' for #{ENV.fetch('FOODCOOP', nil)}" diff --git a/lib/tasks/resque.rake b/lib/tasks/resque.rake index 2c50bf69..8705035a 100644 --- a/lib/tasks/resque.rake +++ b/lib/tasks/resque.rake @@ -1,32 +1,32 @@ -require "resque/tasks" +require 'resque/tasks' def run_worker(queue, count = 1) puts "Starting #{count} worker(s) with QUEUE: #{queue}" - ops = { :pgroup => true, :err => ["log/resque_worker_foodsoft_notifier.log", "a"], - :out => ["log/resque_worker_foodsoft_notifier.log", "a"] } - env_vars = { "QUEUE" => queue.to_s, "PIDFILE" => "tmp/pids/resque_worker_foodsoft_notifier.pid" } - count.times { + ops = { pgroup: true, err: ['log/resque_worker_foodsoft_notifier.log', 'a'], + out: ['log/resque_worker_foodsoft_notifier.log', 'a'] } + env_vars = { 'QUEUE' => queue.to_s, 'PIDFILE' => 'tmp/pids/resque_worker_foodsoft_notifier.pid' } + count.times do ## Using Kernel.spawn and Process.detach because regular system() call would ## cause the processes to quit when capistrano finishes - pid = spawn(env_vars, "bundle exec rake resque:work", ops) + pid = spawn(env_vars, 'bundle exec rake resque:work', ops) Process.detach(pid) - } + end end namespace :resque do - task :setup => :environment + task setup: :environment - desc "Restart running workers" + desc 'Restart running workers' task :restart_workers do Rake::Task['resque:stop_workers'].invoke Rake::Task['resque:start_workers'].invoke end - desc "Quit running workers" + desc 'Quit running workers' task :stop_workers do pids = File.read('tmp/pids/resque_worker_foodsoft_notifier.pid').split("\n") if pids.empty? - puts "No workers to kill" + puts 'No workers to kill' else syscmd = "kill -s QUIT #{pids.join(' ')}" puts "Running syscmd: #{syscmd}" @@ -34,8 +34,8 @@ namespace :resque do end end - desc "Start workers" + desc 'Start workers' task :start_workers do - run_worker("foodsoft_notifier") + run_worker('foodsoft_notifier') end end diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake index 4f0c4081..a90fa407 100644 --- a/lib/tasks/rspec.rake +++ b/lib/tasks/rspec.rake @@ -2,7 +2,7 @@ begin require 'rspec/core/rake_task' task(:spec).clear RSpec::Core::RakeTask.new(:spec) - task :default => :spec + task default: :spec # Use `rspec` to run a single test. When a test fails in rake but not # with rspec, you can use the following to run a single test using rake: diff --git a/plugins/article_import/app/overrides/articles/upload/replace_label_with_file_format_option.html.haml.deface b/plugins/article_import/app/overrides/articles/upload/replace_label_with_file_format_option.html.haml.deface new file mode 100644 index 00000000..a662d619 --- /dev/null +++ b/plugins/article_import/app/overrides/articles/upload/replace_label_with_file_format_option.html.haml.deface @@ -0,0 +1,10 @@ +/ insert_after 'erb:contains("file_field")' +- if FoodsoftArticleImport.enabled? + %label(for="articles_file") + %strong="select the file type you are about to upload" + =f.collection_select :type, FoodsoftArticleImport::FORMATS , :to_s, :to_s +/ insert_before 'erb:contains("articles_outlist_absent")' +-if FoodsoftArticleImport.enabled? + %label(for="articles_update_category") + = f.check_box "update_category" + = t 'Kategorien aus der Datei übernehmen und erstellen.' \ No newline at end of file diff --git a/plugins/article_import/app/overrides/controllers/articles_controller_override.rb b/plugins/article_import/app/overrides/controllers/articles_controller_override.rb new file mode 100644 index 00000000..7777064e --- /dev/null +++ b/plugins/article_import/app/overrides/controllers/articles_controller_override.rb @@ -0,0 +1,20 @@ +if FoodsoftArticleImport.enabled? + ArticlesController.class_eval do + def parse_upload + uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file') + type = params[:articles]['type'] + options = { filename: uploaded_file.original_filename } + options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1') + options[:convert_units] = (params[:articles]['convert_units'] == '1') + options[:update_category] = (params[:articles]['update_category'] == '1') + + @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_file uploaded_file.tempfile, type, options + if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? + redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.parse_upload.notice') + end + @ignored_article_count = 0 + rescue => error + redirect_to upload_supplier_articles_path(@supplier), :alert => I18n.t('errors.general_msg', :msg => error.message) + end + end +end \ No newline at end of file diff --git a/plugins/article_import/app/overrides/models/article_override.rb b/plugins/article_import/app/overrides/models/article_override.rb new file mode 100644 index 00000000..5ca177ad --- /dev/null +++ b/plugins/article_import/app/overrides/models/article_override.rb @@ -0,0 +1,39 @@ +if FoodsoftArticleImport.enabled? + Article.class_eval do + def unequal_attributes(new_article, options = {}) + # try to convert different units when desired + if options[:convert_units] == false + new_price = nil + new_unit_quantity = nil + else + new_price, new_unit_quantity = convert_units(new_article) + end + if new_price && new_unit_quantity + new_unit = self.unit + else + new_price = new_article.price + new_unit_quantity = new_article.unit_quantity + new_unit = new_article.unit + end + + attribute_hash = { + :name => [self.name, new_article.name], + :manufacturer => [self.manufacturer, new_article.manufacturer.to_s], + :origin => [self.origin, new_article.origin], + :unit => [self.unit, new_unit], + :price => [self.price.to_f.round(2), new_price.to_f.round(2)], + :tax => [self.tax, new_article.tax], + :deposit => [self.deposit.to_f.round(2), new_article.deposit.to_f.round(2)], + # take care of different num-objects. + :unit_quantity => [self.unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f], + :note => [self.note.to_s, new_article.note.to_s] + } + if options[:update_category] == true + new_article_category = new_article.article_category + attribute_hash[:article_category] = [self.article_category, new_article_category] unless new_article_category.blank? + end + + Article.compare_attributes(attribute_hash) + end + end +end \ No newline at end of file diff --git a/plugins/article_import/app/overrides/models/supplier_override.rb b/plugins/article_import/app/overrides/models/supplier_override.rb new file mode 100644 index 00000000..62885aac --- /dev/null +++ b/plugins/article_import/app/overrides/models/supplier_override.rb @@ -0,0 +1,53 @@ +if FoodsoftArticleImport.enabled? + Supplier.class_eval do + # Synchronise articles with spreadsheet. + # + # @param file [File] Spreadsheet file to parse + # @param options [Hash] Options passed to {FoodsoftArticleImport#parse} except when listed here. + # @option options [Boolean] :outlist_absent Set to +true+ to remove articles not in spreadsheet. + # @option options [Boolean] :convert_units Omit or set to +true+ to keep current units, recomputing unit quantity and price. + def sync_from_file(file, type, options = {}) + all_order_numbers = [] + updated_article_pairs, outlisted_articles, new_articles = [], [], [] + custom_codes_path = File.join(Rails.root, "config", "custom_codes.yml") + opts = options.except(:convert_units, :outlist_absent) + custom_codes_file_path = custom_codes_path if File.exist?(custom_codes_path) + FoodsoftArticleImport.parse(file, custom_file_path: custom_codes_file_path, type: type, **opts) do |new_attrs, status, line| + article = articles.undeleted.where(order_number: new_attrs[:order_number]).first + + if new_attrs[:article_category].present? && options[:update_category] + new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category]) || ArticleCategory.create_or_find_by!(name: new_attrs[:article_category]) + else + new_attrs[:article_category] = nil + end + + new_attrs[:tax] ||= FoodsoftConfig[:tax_default] + new_article = articles.build(new_attrs) + if status.nil? + if article.nil? + new_articles << new_article + else + unequal_attributes = article.unequal_attributes(new_article, options.slice(:convert_units, :update_category)) + unless unequal_attributes.empty? + article.attributes = unequal_attributes + updated_article_pairs << [article, unequal_attributes] + end + end + elsif status == :outlisted && article.present? + outlisted_articles << article + + # stop when there is a parsing error + elsif status.is_a? String + # @todo move I18n key to model + raise I18n.t('articles.model.error_parse', :msg => status, :line => line.to_s) + end + + all_order_numbers << article.order_number if article + end + if options[:outlist_absent] + outlisted_articles += articles.undeleted.where.not(order_number: all_order_numbers + [nil]) + end + [updated_article_pairs, outlisted_articles, new_articles] + end + end +end diff --git a/plugins/article_import/foodsoft_article_import.gemspec b/plugins/article_import/foodsoft_article_import.gemspec new file mode 100644 index 00000000..b030005f --- /dev/null +++ b/plugins/article_import/foodsoft_article_import.gemspec @@ -0,0 +1,20 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require "foodsoft_article_import/version" + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "foodsoft_article_import" + s.version = FoodsoftArticleImport::VERSION + s.authors = ["viehlieb"] + s.email = ["foodsoft@local-it.org"] + s.summary = "Manages manual article import from file. File Formats supported are: foodsoft file(csv), bnn files (.bnn) and odin files (xml)" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + + s.add_dependency "rails" + s.add_dependency "deface", "~> 1.0" + s.add_dependency 'roo', '~> 2.9.0' + s.add_development_dependency 'simplecov' +end diff --git a/plugins/article_import/lib/foodsoft_article_import.rb b/plugins/article_import/lib/foodsoft_article_import.rb new file mode 100644 index 00000000..5bd48dd8 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +require "deface" +require 'foodsoft_article_import/engine' +require 'digest/sha1' +require 'tempfile' +require 'csv' +require 'yaml' +require 'active_support/core_ext/hash/keys' +require_relative 'foodsoft_article_import/bnn' +require_relative 'foodsoft_article_import/odin' +require_relative 'foodsoft_article_import/foodsoft' +module FoodsoftArticleImport + class ConversionFailedException < StandardError; end + + FORMATS = %w(bnn foodsoft odin).freeze + def self.enabled? + FoodsoftConfig[:use_article_import] + end + + def self.file_formats + @@file_formats ||= { + 'bnn' => FoodsoftArticleImport::Bnn, + 'foodsoft' => FoodsoftArticleImport::Foodsoft, + 'odin' => FoodsoftArticleImport::Odin, + }.freeze + end + + # Parse file by type (one of {.file_formats}) + # + # @param file [File, Tempfile] + # @option opts [String] type file format (required) (see {.file_formats}) + # @return [File, Roo::Spreadsheet] file with encoding set if needed + def self.parse(file, custom_file_path: nil, type: nil, **opts, &blk) + @@filename = opts[:filename] if opts[:filename] + custom_file_path ||= nil + type ||= 'bnn' + parser = file_formats[type] + if block_given? + parser.parse(file, custom_file_path: custom_file_path, &blk) + else + data = [] + parser.parse(file, custom_file_path: custom_file_path) { |a| data << a } + data + end + end + + # Helper method to generate an article number for suppliers that do not have one + def self.generate_number(article) + # something unique, but not too unique + s = "#{article[:name]}-#{article[:unit_quantity]}x#{article[:unit]}" + s = s.downcase.gsub(/[^a-z0-9.]/, '') + # prefix abbreviated sha1-hash with colon to indicate that it's a generated number + article[:order_number] = ":#{Digest::SHA1.hexdigest(s)[-7..]}" + article + end + + # Helper method for opening a spreadsheet file + # + # @param file [File] file to open + # @param filename [String, NilClass] optional filename for guessing the file format + # @param encoding [String, NilClass] optional CSV encoding + # @param col_sep [String, NilClass] optional column separator + # @return [Roo::Spreadsheet] + def self.open_spreadsheet(file, encoding: nil, col_sep: nil, liberal_parsing: nil) + opts = { csv_options: {} } + opts[:csv_options][:encoding] = encoding if encoding + opts[:csv_options][:col_sep] = col_sep if col_sep + opts[:csv_options][:liberal_parsing] = true if liberal_parsing + opts[:extension] = File.extname(File.basename(file)) if file + begin + Roo::Spreadsheet.open(file, **opts) + rescue StandardError => e + raise "Failed to parse foodsoft file. make sure file format is correct: #{e.message}" + end + end +end diff --git a/plugins/article_import/lib/foodsoft_article_import/bnn.rb b/plugins/article_import/lib/foodsoft_article_import/bnn.rb new file mode 100644 index 00000000..008b6029 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/bnn.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# Module for translation and parsing of BNN-files (www.n-bnn.de) +# +module FoodsoftArticleImport + module Bnn + @@codes = {} + @@midgard = {} + # Loads the codes_file config/bnn_codes.yml into the class variable @@codes + def self.load_codes(custom_file_path = nil) + @gem_lib = File.expand_path '..', __dir__ + dir = File.join @gem_lib, 'foodsoft_article_import' + begin + @@codes = YAML.safe_load(File.open(File.join(dir, 'bnn_codes.yml'))).symbolize_keys + if custom_file_path + custom_codes = YAML.safe_load(File.open(custom_file_path)).symbolize_keys + custom_codes.each_key do |key| + custom_codes[key] = custom_codes[key].merge @@codes[key] if @@codes.keys.include?(key) + @@codes = @@codes.merge custom_codes + end + end + @@midgard = YAML.safe_load(File.open(File.join(dir, 'midgard_codes.yml'))).symbolize_keys + rescue StandardError => e + raise "Failed to load bnn_codes: #{dir}/{bnn,midgard}_codes.yml: #{e.message}" + end + end + + $missing_bnn_codes = [] + + # translates codes from BNN to foodsoft-code + def self.translate(key, value) + if @@codes[key][value] + @@codes[key][value] + elsif @@midgard[key] + @@midgard[key][value] + elsif !value.nil? + $missing_bnn_codes << value + nil + end + end + + NAME = 'BNN (CSV)' + OUTLIST = false + OPTIONS = { + encoding: 'IBM850', + col_sep: ';' + }.freeze + + # parses a bnn-file + def self.parse(file, custom_file_path: nil, **opts) + custom_file_path ||= nil + encoding = opts[:encoding] || OPTIONS[:encoding] + col_sep = opts[:col_sep] || OPTIONS[:col_sep] + load_codes(custom_file_path) + CSV.foreach(file, { col_sep: col_sep, encoding: encoding, headers: true }).with_index(1) do |row, i| + # check if the line is empty + unless row[0] == '' || row[0].nil? + article = { + name: row[6], + order_number: row[0], + note: row[7], + manufacturer: translate(:manufacturer, row[10]), + origin: row[12], + article_category: translate(:category, row[16]), + unit: row[23], + price: row[37], + tax: translate(:tax, row[33]), + unit_quantity: row[22] + } + # TODO: Complete deposit list.... + article.merge!(deposit: translate(:deposit, row[26])) if translate(:deposit, row[26]) + + if !row[62].nil? + # consider special prices + article[:note] = "Sonderpreis: #{article[:price]} von #{row[62]} bis #{row[63]}" + yield article, :special, i + + # Check now for article status, we only consider outlisted articles right now + # N=neu, A=Änderung, X=ausgelistet, R=Restbestand, + # V=vorübergehend ausgelistet, W=wiedergelistet + elsif row[1] == 'X' || row[1] == 'V' + yield article, :outlisted, i + else + yield article, nil, i + end + end + end + end + end +end diff --git a/plugins/article_import/lib/foodsoft_article_import/bnn_codes.yml b/plugins/article_import/lib/foodsoft_article_import/bnn_codes.yml new file mode 100644 index 00000000..dfb226e7 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/bnn_codes.yml @@ -0,0 +1,1119 @@ +# BNN Codes +tax: + "1": 7.0 + "2": 19.0 + "3": 10.7 + +deposit: + "930190": 0.08 + "930200": 0.08 + "930205": 0.08 + "930210": 0.08 + "930230": 0.08 + "930260": 0.08 + "930270": 0.08 + "930280": 0.08 + "998010": 0.08 + "998016": 0.08 + "998350": 0.08 + "998360": 0.08 + "998427": 0.08 + "998440": 0.08 + "998460": 0.08 + "998470": 0.08 + "998500": 0.08 + "998510": 0.08 + "998700": 0.08 + "998710": 0.08 + "998725": 0.08 + "998730": 0.08 + "998750": 0.08 + "998760": 0.08 + "998790": 0.08 + "998800": 0.08 + "998810": 0.08 + "998840": 0.08 + "998860": 0.08 + "998880": 0.08 + "998887": 0.08 + "900010": 0.15 + "900020": 0.15 + "900030": 0.15 + "900040": 0.15 + "900050": 0.15 + "900070": 0.15 + "900075": 0.15 + "900085": 0.15 + "900089": 0.15 + "900850": 0.15 + "900860": 0.15 + "900870": 0.15 + "900890": 0.15 + "930010": 0.15 + "930020": 0.15 + "930030": 0.15 + "930035": 0.15 + "930050": 0.15 + "930090": 0.15 + "930110": 0.15 + "930120": 0.15 + "930130": 0.15 + "930320": 0.15 + "930325": 0.15 + "955230": 0.15 + "998000": 0.15 + "999983": 0.15 + "999985": 0.15 + "998040": 0.15 + "998060": 0.15 + "998070": 0.15 + "998080": 0.15 + "998100": 0.15 + "998110": 0.15 + "998300": 0.15 + "998310": 0.15 + "998320": 0.15 + "998330": 0.15 + "998340": 0.15 + "998352": 0.15 + "998370": 0.15 + "998380": 0.15 + "998390": 0.15 + "998405": 0.15 + "998417": 0.15 + "998450": 0.15 + "998480": 0.15 + "998520": 0.15 + "999200": 0.15 + "900892": 0.25 + "930290": 0.25 + "999980": 0.25 + "998020": 0.25 + "998030": 0.25 + "998090": 0.25 + "998366": 0.25 + "998420": 0.25 + "998437": 0.25 + "998740": 0.25 + "998770": 0.25 + "930450": 0.50 + "930440": 1.00 + "930460": 1.00 + "930256": 1.50 + "930257": 1.50 + "930250": 30.00 + "930252": 30.00 + "998720": 30.00 + "998830": 30.00 + "998870": 30.00 + +manufacturer: + bro: Brodowin + AAB: Azienda Agricola Bettili + ABD: Agro Bio Drom, Frankreich + ABM: Maintal GmbH + ABT: Albtal Naturkost GmbH + ACF: Arcada France S.A. + ACL: Achleitner Biohof GmbH + ADC: Coste Vincent & Francoise + ADM: Antersdorfer Mühle GmbH + ADR: Faan Zuidhorn + AFH: Doris Wallbaum & Co + agm: Angermeier Weinimport + AGR: Agrano GmbH & Co KG + AGX: Agrexco Ltd. + akr: Biohof Barnsen + ALB: Alber Pilzkonservenfabrik + alf: Bioland-Hof Altfeld + ALL: Allgäuland Käsereien GmbH + ALM: Allmendinger Metzgerei + ALN: AL Naturkost Handels GmbH + ALO: Allos-Walter Lang GmbH + ALR: Allerleirauh GmbH + ALS: Alsan Werk + ALT: Alberts-Tofuhaus + ALV: Alva + AMA: Amazonas Naturpr.Handels GmbH + AMS: Santa Fe Europe GmbH + AMW: AlmaWin GmbH + AND: Andechser Molkerei GmbH + ANF: Allgäu Natur GmbH + ANG: Angelmaier OHG + ANI: Anis de l' Abbaye + ANJ: Atelier Niedernjesa + ANL: Anderlbauer Frasdorf + ANN: AN, Ne Bienenwachskerzen + ANT: Antico Forno a Legna + APR: Apeiron + ARC: Arche Naturprodukte GmbH + ARG: ARGANDÓR + ARI: Aries Umweltprodukte + ARO: Aromabalance, Silvia Plum + art: Artesia + asc: Asch + ASS: Assindia - GmbH + ATX: Ute Arnswald + AUR: Auro Pflanzenchemie AG + BAB: Baba-Laffa, W. Maguid + BAC: Hof Backensholz + BAE: Biofarm A.E. + bai: Bioland-Hof Gerhard Baiker + bäj: Bärthele, Joachim + BAK: Bauckhof demeter Naturkost + BAL: Ballybrado, Cahir Co. + BAS: Bastiaansen Bio-Kaas B.V. + BAU: BioBauernmarkt Chiemgau eG + bba: Bäckerei Bahde + bbi: Bäckerei Bihn + BBN: Bio Bavaria Naturkost + BBO: Flemming Naturkost + BBR: Mineralbrunnen AG + BBW: Bioland Ba-Wü GmbH + bdh: Butendiek-Hof + bdp: Biodynaminska Produkter + BDW: Bio-Dienst Weiss GmbH + BEL: Bellenature + BER: Bergquell GmbH & Co.KG + BEU: Beutelsbacher GmbH + BFA: Biofarms s.r.l. + BFG: Burkhardt Feinkostwerke GmbH + BFR: Bioforce A.Vogel GbmH + BFS: Bruno Fischer GmbH Naturkost + bgb: Heinz Bursch + BGH: Burgunderhof digestif´s GmbH + BHÄ: Bäck. Härdtner + bhb: Bäckerei Höhenberg + bhc: Bioland-Hof Christiansen´s + bhk: Bioland-Hof Klauser + bhl: Bioland Hof Lesker KG + bhm: Bioland-Hof Hubert Merz + BHO: Barnhouse Naturprodukte GmbH + bhö: Bioland-Hof Hörz + bhr: Bioland-Hof Martin Häring + BIA: Bio Aras, ELAFOS + BIB: Bio Bärchen Vertriebs GmbH + bid: Imkerei Binder + BIF: Biofrisch GmbH + BIH: Markus Bihler GmbH + bil: BioHof Laurer + bim: Bieringer Mühle + BIN: Bingenheimer Saatgut AG + BIO: Bio Akademie + BIP: Bio Plus GmbH + BIR: Käserei H. Birkenstock GmbH + BIS: Bio Service SRL + BIT: Bioturm + BIV: BioVita Naturkost GmbH + BIZ: Biozyklische Produkte AG + BJS: Josef Schäfers + BKB: Bio Korn Biscuits + bkf: Bakenhus Biofleisch GmbH + bkh: Bauckhof OHG + BKM: Biokosma GmbH + bko: Bäckerei Kostner + BKP: Brava cv + BKT: Biokorntakt Vertriebs GmbH + BKW: Liane und Roman Wirth + BLA: biolare ? Pilzanbau + BLB: Blütenland Bienenh. + blg: Bollinger, Bernd + BLI: Holle Baby Food GmbH + BLL: Boller Fruchtsäfte + BLM: Binger Lammbräu GmbH + BLN: Bioland GmbH Nord + BLR: Ökofrost GmbH + BLS: Bioland Südtirol + BMA: BIOMAS + BMK: N.V. Biomilk S.A. + BML: Bio Molkerei Lembach + bms: Bioland-Milchschafhof + bmü: Bannmühle + bmw: Barbara Müller Handels GmbH + BND: Bionade GHmbH + BNE: Bionest + BOA: Bio-Obst Augustin KG + BOB: Bobalis + BOH: Boschenhof GbR + BOI: Bois Naturkost GmbH + BOK: Bodensee Kelterei GmbH + BOL: Bohlsener Mühle + böm: BÖMO + BON: Allos Walter Lang GmbH + bos: Biol-Dyn. Obstbau Seger + BOW: Gandha BV + BPL: F.J. Moog SARL + BPR: Bioprim + bra: Brack Kaffee + brb: Rüdiger Born + BRE: Gewürzmühle Brecht GmbH + BRI: Brio Spa + BRO: Jules Brocherin S.A. + brs: Asgaard + BRT: Candy Factory KG + bru: Biolandhof Brummer-Bange + bsh: Bioland Schleswig-Holstein + BSK: Wiggensbach GmbH + bsp: Bioland-Hof Speidel + bsw: Bioland-Hof Schulte-Walter + BTH: Biothek Handels-GmbH + btm: EZG Bioland-Südgetreide + BTR: Biotropic GmbH + BUC: Bucheckchen + BUF: Bodin & Fils + BÃœH: Metzgerei Bühler GmbH + BUK: Borghoff & Kötter Gbr + bup: Braun U. Partner + BUR: Burk's Fränkische Öko-Nudeln + BVE: Bio Vegan GmbH + bvg: Bioland Vermarktungs GmbH + BVI: abacco B.V. + BWH: Bornwiesenhof + BWL: Haya Lebensmittel GmbH + BYO: Byodo Naturkost GmbH + CAA: Coop.Agr. ARABIOS.a.r.l. + CAB: Erich Boden Delikatessen + CAL: Calendula Naturkost Backstube + CAM: Campobelle s.r.l. + can: Anthal Canadi + CAR: Care Naturkost GmbH & Co. + CAV: Cal Valls + CBR: C. Berger + CBU: Weingut Clemens Busch + CDO: Cha Do Teehandel + CES: Il Cesto + CGL: Castiglioni S.P.A. + CHE: Chemviron Carbon gmbH + CHI: Chiemgauer Naturfleisch GmbH + CHR: De Rit Handels GmbH + CLO: Clostermann + CMD: CMD Naturkosmetik + CNH: Chiemgauer Naturkosthandel + COL: Chocolat Schönenberger AG + COR: C&C Fine Foods, Niederlassung + COS: Cosmoveda, G. Eckerle + CTE: Castle Tea + CUM: Cumnatura + CVE: Campina Verde ecosol S.L. + DAG: De Dageraad + DAK: Die andere Konditorei + DAM: Dachswanger Hof + DAN: Danival + DAV: Davert GmbH + DBB: Die Beerenbauern + DBH: CW Öko Ei GmbH + DDC: Domain de Clairac, Frankreich + DDI: Demeter Dienste + DDM: Domaine du Midi GmbH + DEH: Bio-Hofmolkerei Dehlwes + DEN: dennree-Versorgungs GmbH + DET: Detmers Getreide GmbH + DFE: Demeter Felderzeugnisse GmbH + DFR: Daniel Frank + DGU: Dal Gustaio + DIE: Helmut Arendt + DIN: Alfons u. Franz Neumeier GbR + dio: Dionisio de Nova Garcia + dis: Schulze-Schleppinghoff + DIW: Weingut Hans Diwald + DKA: Dr. Klaus Karg + DKG: Geifertshofen GmbH + dko: Disselkoen Organics + dma: Demeterhof Massmann + dmä: Demeterhof Mäck + dnj: Danner, Johann Georg + DOT: Dottenfelder Hof + dpr: Demeterhof Preller + DRM: Dr. Martins da Cunha GmbH + DSE: Deutsche See + dsw: Dirk Schulze-Wethmar + DUN: Dunn's of Dublin + DWE: Dwersteg Destillerie + DWF: Dworschak-Fleischmann GbR + dwp: Dritte Welt Partner + DYF: Dynamis France + DZW: De Zwalm + EBR: Die Regionalen + ECO: Ecomel B.V. + ECP: ecopan-Naturkost GmbH + ecr: Ecoregion + ECV: ECOVER Products nv + EGG: Eggert´s Tiefkühl-Service + EGL: Wilhelm Egle GmbH + EGM: Ökoland GmbH + EIQ: Ei.Q. GmbH + EIS: Eisblümerl Naturkost + eit: Eitzinger Franz + ELK: Naturkost Elkershausen GmbH + EOG: EOS Getränke GmbH + EOS: Eosta International BV (NL) + EPI: epikouros + ERD: ErdmannHauser GmbH + ERH: Frank Erhardt + ERN: ERNTESEGEN Naturkost GmbH + EUH: Eurohealth AG + EUN: Euronat ? Bretagne + EVS: Evers Naturkost GmbH + EWE: Ernst Weber Naturkost + fah: FairHandeln + FAI: Frucht Agentur Iberia + FAL: Breisgaumilch GmbH, Fallers + FDO: Fattoria degli Orsi + fig: Fattoria degli Orsi + FIN: Finck + fis: Brauerei Rupert Fisch + FIT: Fitne GmbH + FKS: Hermann Stiefel + FKW: Fruchsaftkelterei Klaus Weber + FLA: Flamant Vert S.A.R.L. + FLC: Flockenhaus + fle: Fleckenbühler Landprodukte + FLN: Flemming-Naturkost + FLO: Florin, Angelika Trankle + FLP: Fleur Products BV + FON: Fontaine Nahrungsmittel GmbH + FOR: Forte + FÖR: Förster + FPF: Fahrenzhausen GmbH + fps: Franz Baumann + FRC: Francia Mozzarella + FRE: Hofladen Frey + FRH: Freiheithof + FRI: Frisetta GmbH + FRK: Frischkeim Naturkost GmbH + FRO: Fromin GmbH + FRT: Florentin-Mediterranean Food + FVG: FALA Verkaufsgesellschaft mbH + FZI: Feinkäserei Zimmermann + FZS: Sommer & Co.KG + gah: Gärtnerei Amaranth + gäh: Gärtnerei Halmberg + gäk: Gärtnerei Kienast + gal: Gallung´s Ziegenhof + GAR: Gärtnerei Landes + GBA: Gerald Bartke GmbH + gch: Gärtnerei Christian Hiss + gcl: Gut Clarenhof + gdi: Gärtnerei Distel + GDR: Graindrops + GEB: Martina Gebhardt GmbH + GEE: Lupina Handels GmbH + GEH: Georg Gehrsitz GmbH & Co.KG + GEP: GEPA + GFH: Mechthild & Andres Klose + GFR: Gebr. Franz GmbH + ggk: Burkhard Dreckel + gha: Gärtnerei Andreas Hankel + gho: Gärtnerei Horizont + gie: Giegold Hefefabrik + gip: Gilchinger Pilzzucht + gks: Gärtnerei Klein Sigi + GLG: Glafey-Lichte GmbH + gli: Glitz Ehringhausen + GLM: Gläserne Meierei GmbH + GOL: Golden Temple + GÖM: Grünsfelder GmbH & Co.KG. + gom: Gomille, Torsten + GOV: Govinda?s Naturkost GbR + gpb: Gerhard Preuschl Biolandhof + gpe: Gesa Petersen + GPN: Grüner Punkt Naturkost GmbH + GRE: C.F. Grell Nachf. + GRM: Grindsted Mejeri + GRN: Biotropic GmbH + GRU: Gruel Biolandhof + GSD: Gerhard Schürholz GmbH + GSE: GSE Vertrieb + gsi: Gregor Sing + gsl: Gärtnerei Schmälzle + gub: Gärtnerei Ulenburg + GUD: Gude GmbH + GUL: Gesund & Leben + GUR: Gurtmann, Christoph + GUT: Gute Zeiten GmbH + GUZ: Glahn & Zindl + GWF: GWF eG + HAA: Haaner Felsenquelle GmbH + hag: Hartmann Getränke + hal: Haldenhof + HAM: Hamfelder Hof + HAN: Handelskontor Willmann + HÄR: Härle + HAU: Dr. Hauschka ? Wala GmbH + HAW: hawo´s Getreidemühlen GmbH + HBG: Hornberger Lebensquell + hdk: Hof Dinkler + HEC: Heuck Landbäckerei + HEI: Heidelberger Naturfarben + hek: Hecker Naturkost + HER: Herbaria Kräuterparadies GmbH + HES: Weingut Heiner Sauer + HEU: Heuschrecke Naturkost GmbH + hex: Hexerküche + HFA: Hof Farrenau, Deimling GbR + hfm: Hofgemeinschaft Fischermühle + hfn: Hofgemeinschaft Fischermühle + hgo: Imkerei Oswald + hha: Hasso Hasbach + HIE: Hierl- Der Nudelmacher + HIN: Mathias Kloppenborg GmbH + HKH: Hofkäserei Heggelbach + hkk: Hekking + HLE: Holzlehner + HLW: Herrmannsdorfer Werkstätten + HMB: Humbel Brennerei + HMS: Handelsag. Rolf Schekerka + HMÃœ: Peter & Martina Linxweiler + HOC: Hoch GmbH Oblatenfabrik + hof: Hofmark Brauerei + hoh: Dorfgem. Hohenroth + hoi: Henri Willig B.V. + HOL: Holle Baby Food GmbH + HÖL: Höllensprudel + hom: Hoffmeier + HOR: Horizon Natuurvoeding B.V. + HÖR: Bäckerei Hörtling + HOV: H2Ovital oHG + HOY: Hoyer GmbH + HPG: Horn Papiergroßhandel + hse: Herbert Seitz + hst: Hof Steinrausch + hum: Huber, Martin + HUN: hanf & natur + HUZ: Huzo + hwi: Henri Willig, Kaasm. + HWL: Hawlik Pilzbrut GmbH + IAB: Imkerei (Reiner) Bienefeld + ibe: Imkerei Berrenrath + ilk: Imkerei Ludger Klinker + ILU: ILUMINA GmbH + imb: Imkerei Betz + IMF: Hain Celestial Europe BVBA + imm: Mohr + Müller Imkerei + irf: Imkerei Feldt + IRM: Imkerei Roland Maier + ISA: ISANA GmbH & Co.KG + ISE: Klaus Wolf + ISK: Isko Vertriebs GmbH + IUM: I&M Inge Stamm GmbH + jäh: Jähnke Naturkost + JAN: BioFleischerei Jansen + JAS: Jasci Donatello + JAT: Jatex Handels-GmbH + jbe: Beck Schafhof + JOP: Jogopur Yogoferm GmbH + JOR: Jordan Cereals Ltd. + KÄB: (Martin) Bauhofer Käserei + KÄL: Inntaler GmbH + KAM: KAMUT Association of Europe + KAN: Kanne Brottrunk GmbH & Co.KG + KAT: Karibu Trade + kbh: Kiebitzhof + KBW: Klosterbrauerei Weißenohe + kei: Keil, Sepp + KER: Keramik & Kerzen + kgg: Kelterei Gregor Greimel + KGÖ: Karl Gröner GmbH + KGV: Klostergut Volkenroda + KHA: Konrad Halder + KIL: Kilian + KIP: Kipepeo BIO & FAIR GmbH + KKL: KKL-Naturwaren + KKV: Vollwertbäckerei König + KLA: AlmaWin GmbH + KLD: Keimland + KLG: Keimling Naturkost GmbH + klk: Käserei Schlierbach + klo: Klotz, Martin + KMF: Käsemanufaktur, Lothar Müller + KNE: Getreidemühle Knecht KG + KNÖ: Robert Knöbel + KON: Engelhard GmbH & Co. KG + KOR: Kornblume, F.+ B.Brinkmann + KPL: Kräutergarten Pommernland + KRA: Kranichhof + KRÄ: KAULFUSS + KRE: Krämers Ernährung + krr: Fachkrankenhaus Ringgenhof + KTO: Kato + KUC: Kolla & Co + KWE: Köhlerei Wengert + LAB: Labroco Agrarconsult + LAF: Hofgut Algertshausen + LAL: La Luna del Rospo + läm: Lämmerhof + LAN: Landkrone GmbH + LAS: Lakhsmi + LAV: laverana GmbH + LBI: Do-it Dutsch + LEB: U. Walter GmbH - Lebensbaum + LHQ: St. Leonhardquelle + LIL: Legend International Ltd. + LIM: Lima NV + LIR: Lily Rose + LIW: Lichtwurzel, Imton + LJL: Johann Langgartner + LMC: Lammersiek & CO. + LNA: Grabower Süßwaren GmbH + LÖC: Löcke Bio-Pilzzucht + LOG: Logona Hans Hansel GmbH + LSP: La Spinosa ( Weingut) + LTA: Lauretana, Wasser Import GmbH + LUB: Lubs GmbH + lüp: Lübcke Papier GmbH & Co KG + LUV: Luvos Heilerde + MAB: Bio-Nahrungsmittel GmbH + mah: Hof Mahlitzsch + MAI: Maisch + map: Maple GmbH + MAR: Marschland NK GmbH + MÄR: Märkisches Landbrot GmbH + MAT: Martinshof GmbH + MÄU: Gebr. Grund GmbH & Co.KG + MAY: Mayka Naturbackwaren GmbH + MAZ: Mazer, Bernd + MCA: Mustiola International SRL + med: Medousa, Griechenland Importe + MEK: Weingut Meinklang + met: Metsä Tissue GmbH + MGN: Naturland-Bauern e.G. + mhb: Meyerhof Belm + mhh: ?Die Meierei? Hansfelder Hof + MID: Midi + mig: Migliore + mil: Miller GmbH & Co.KG Agnes + MKF: merkur frucht Freiburg GmbH + MKL: Makulaku Confectionery Ltd + MLL: Mollis Kinderprodukte GmbH + MMC: MM Cosmetic GmbH + MOA: MolenAartje B.V. Natudis + MOB: Frisetta GmbH & Co.KG + MOC: Wasserprinz; div. Anbieter + MOI: Moin BioTK-Bachwaren + MOK: Mokobella EU GmbH + MOL: Moltex Baby-Hygiene GmbH + MOR: EgeSun GmbH + MÖR: Mörk Naturkostprodukte + MOU: Wertform GmbH & Co + MSV: mesa verde + MTB: Mont´Albano + MUL: Multikost Vertriebs GmbH + MWO: Milchwerke Oberfranken + MZG: Sieben Zwerge GmbH + mzi: Mathias Zipf + NAB: Hubert Tempelmann e.K. + NAC: BodyWise (UK)Ltd. + NAG: Tofumanufaktur Nagel GmbH + NAM: Naturmaelk A.m.b.a. + NAP: Combu Cha + NAT: Naturata e.G. + nbm: Nußbaumer, Roman + NBO: Nürnberger Bio-Originale + NCO: Natur Compagnie GmbH + NEE: Neue Erde GmbH + NEG: Neu´s GmbH & Co. KG + NEU: Gebr. Ehrnsperger e.K. + nhb: Naturlandhof Biberger + NHU: Natur Hurtig (Himalaya Salz) + nlg: Nordland, Lebensgem. + NMA: NaturMarkt GmbH + nmd: Münzner + NNA: Mensch & Natur AG + NOK: Noka-Sojamanufaktur GmbH + NPG: Nette Papier GmbH + NSC: Naturkost Schuchardt + NTM: Natumi GmbH Produkte & Ideen + NTR: Naturian Ökoweine OHG + NTU: Naturion + NUR: IL NURAGHE GmbH + NUT: Nutrifors AG + NWA: Nikolaihof Wachau - Weingut + NWR: Öko-Norm GmbH + OAT: Ceba Foods AB + obb: Obsthof Bruno Brugger + obm: Obsthof Bernd Majer + OBS: Öko-Bauernhöfe Sachsen GmbH + ÖBW: Brodowin Ökodorf + OCB: OCB-Vertriebs-GmbH + ODE: Odenwald EKO Brood en Banket + ODI: ODIN Holland C.V. + ÖER: Öko Ernte GmbH + ÖFA: Öko Feinkost Andechs Gmbh + ÖFR: Ökofrost GmbH + ogh: Demeter Gärtnerei Obergrashof + ohc: Obsthof Cordes + OHE: Obsthof Heinrich + ohh: Obsthof Hermann Helde + OHL: Ohling, Andreas + ÖKB: ÖkoBo + ÖKH: Ökohum Vertriebs GmbH + ÖKL: Ökoland GmbH Nord + ÖKN: Ökonatur + ÖKU: Naturkosthandel Ökollus + ÖLI: Öko-Line + ÖMA: ÖMA- Beer GmbH + OML: Ostermühle Naturkost GmbH + ÖMS: Ölmühle Solling GmbH + ORG: Organix4U GmbH + ORH: Obsthof Robert Hartmann + ORO: Organic Oils S.P.A. + ort: Biofrucht Ortlieb GbR + osn: Osning-Getränke GmbH + ÖWK: Haus am Goldberg GmbH + ÖWR: Weingut Richard Schmidt + ÖWS: Öko-Weinimport Schmid + pab: Bacchini Roberto & C. S.n.c. + PAN: Pasta Nuova GmbH + pau: Bioland Gemüse Paul + PEM: PEMA Heinrich Leupoldt KG + PER: Perger Getränke GmbH + PET: Marcel Petite, Frankreich + PFH: Gabriele Gersdorf GmbH + PFO: Papierf. Oberschmitten GmbH + PID: MW BGL Chiemgau eG + PIM: Pinzgauer Molkerei + PIN: Pinkus Müller GmbH & Co.KG + PLA: Käserei Plangger Ges.m.b.H. + PLG: Peralge, Marciella Callegarie + PLO: div. Anbieter + PMI: Peterstaler Mineralquellen + PMP: Progeo Mangimi Petfood + PNA: Pro Natura S.A. + PÖB: Pötzelberger + pod: Poder GmbH + PÖS: PINGU-Öko-Tiefkühlservice + PRG: Provence Regime S.A. + PRN: Pro Natur GmbH + PRO: Probio Handelsgesellschaft + PRV: Provamel + PVL: Primavera Life GmbH + RAA: Raab + RAC: Rachelli Italia s.r.l. + rad: Radicula GmbH (Avalon) + ran: Randegger Ottilienquell + RAP: Rapunzel Naturkost AG + RBB: Michael Krieger KG + rde: Roman Denis Bioland-Gemüsebau + rds: Rudolf Schramm + RED: Redecker + rei: Reicheneder, Gerhard + rha: Hofgut Rengoldshausen + RHG: Rheinland-Höfe GmbH + RHÖ: Rhöner (Brauerei) + RIE: Peter Riegel Weinimport GmbH + RIN: Ringenwalde Werkhof + RIS: Ristic + RIT: De Rit Handels GmbH + RLG: Metzgerei Rieblinger + ROB: Geflügelhof RoBert?s + rog: Söbbeke GmbH & Co. KG + ROH: Geflügelhof Rothäusle + ROL: C. F. Rolle Mühle GmbH + ROM: Rosmarin Ingo Karrasch GbR + ROS: Hubmann- Rosengarten + RÖS: Georg Rösner Vertriebs GmbH + ROT: Rother Bräu + rsh: Rösslerhof + RTE: Manfred und Christine Rothe + RUH: Riensch & Held GmbH & Co. KG + RUN: Runge Nahrungsmittel GmbH + RUS: Ruschin Makrobiotik GmbH + RZO: Rzollhäusle, H.R. Hauser + SAB: SANBEAM Gesunde Produkte GmbH + SAC: Petersilchen Sanchon GmbH + säh: Karla u. Sebastian Schäfer + SAL: Salomon + SAN: Sante Naturkosmetik GmbH + SAO: Gsund & Schön Sanoll + SAR: Sanatur GmbH + SAS: S´Atra Sardigna Coop A.r.l. + SAV: Santaverde GmbH + sbä: Steinofen Bäcker + SBG: Mol Hohenlohe-Franken e.G. + SBH: Matthias Höfflin + SBM: Saarpfälzische Bio-Höfe GmbH + SCH: Naturkost Schramm GmbH + SCK: Walter Rau GmbH & Co. KG + SDE: Robert Schindele GesmbH + SDL: Siegfried Schedel + SEE: Weingut W. Seeber + SEG: Sennerei Walchsee GmbH + SEK: Sekowa Seibold KG + SEL: La Selva Vertriebs-GmbH + SFE: Hof Mühlenberg E. Schiffers + sfm: Schäfer, Martin (Michaelshof) + SFO: Sinfo Naturkost & Naturwaren + SHE: Weingut Schäfer-Heinrich + shh: Max Fischer + SIN: Singer + SJF: Sojafarm + sjh: Metzgerei Schojohann + sjs: Schaut, Josef + SKA: Schönegger Käse-Alm GmbH + SLC: Svenska LantChips AB + sle: Schilling, Erich + slm: Salm, Elvira (Limberger) + SMA: Sana-Mare + SND: Weingut Sander + SNF: Sanoflore + SNI: Weingut Stortz-Nicolaus + snn: Monika u. Thomas Sannmann + SNT: Sonett OHG + SNZ: Schnitzer Bräu + SOB: SOBO Naturkost + SÖB: Söbbeke GmbH & Co. KG + SOD: Sodasan GmbH + SOE: Salamita Soc. Coop. A.r.l. + SOF: Soto Feinkost, Oskar Schramm + SOJ: Triballat Noyal + SON: Sonne GmbH + SOT: Allgäuland + SPA: Spaichinger Nudelmacher GmbH + spe: Speckhan, Rudolf + SPH: Spreewälder Hirsemühle + SPI: Spielberger KG Naturata e.G. + SPL: Silver Plastic GmbH & Co. KG + SPR: B & G Sprossenparadies GmbH + sqn: St. Nikolaus Quelle + SRH: Scharein, Hubert + SSC: Sural-Sacicc + SSI: Santisi Vollkornnudeln + STB: Seitenbacher GmbH Naturkost + STE: Steck + sth: Scholtenhof + STN: Sonnentor GmbH + STY: Teebaumöl Kosmetik + STZ: Schnitzer OHG + SUN: Sunval Nahrungsmittel GmbH + SVA: Svadesha Naturkost- Vertrieb + SVE: Svenska Lant Chips + svm: Hof von der Mehden + SWE: Schuldt & Weber + swo: Schwollener Sprudel + sww: Karin u. Corney Weimeijer + SWZ: Schweizer GmbH + SYM: Sympakorn + tag: Tagwerk + TAI: Life Food GmbH + TAP: TAPIR Wachswaren GmbH + TAR: Tarpa Naturkost + TAU: Tautropfen GmbH + TDP: Terra di Puglia + TEL: Hakle GmbH + TER: Terrasana Naturvoeding BV + TES: Terra Soleil + TEU: Teutoburger Ölmühle + TFO: Faan Zuidhorn BV. F.Andringa + TIL: Tilouche Fruchtimport GmbH + TLI: CV Ter Linde + TMJ: Thise Mejeri + TOF: Ökofrost GmbH + TOP: ToPas GmbH + tph: T.Port Hamburg GmbH & Co. + TRA: Tradin Organic B.V. + tro: Tropenfruchtimport GmbH + TUC: Tra Terra e Cielo + ubm: Upländer Bauernmolkerei GmbH + uhe: Uta Helberg + ulh: Ulmenhof + una: Uli Natterer + uns: Unseld´s Backstube + unt: Ulrich u. Monika Unterweger + URD: Uni-Vert + URT: Urtekram A/S + VAV: Vallée-Verte Handelsges. mbH + vbr: Vollkornbäckerei Rasche + VEN: eco cosmetics GmbH & Co. KG + VGB: Bioland Schleswig Holstein + VGE: Verlag gesund essen GmbH + VGF: Hansen & Koschmieder GmbH + VIA: Viana Naturkost GmbH + VIB: Fattoria VIB + VIV: S.A. Viver, Frankreich + VIZ: Vino Zero + VLV: VivoLo Vin, Ökoweinhandel + vms: Traitteur Villemin GmbH + VNI: L. Weinrich GmbH & Co.KG + VOE: voelkel GmbH + VOL: Volvic, Frankreich + VUN: Velazquez Universal s.L. + VVE: Viola Verde GmbH + vzw: Hüser van Zwoll GmbH & Co. KG + waa: Gartenbau Waas + wal: Walter, J. + WAT: Walter Thies Zellglas + WBT: WBT SRL + WDL: Milchkoop Wendland GmbH + WDM: Windmill Organics Foods Ltd. + WDN: Großbäckerei Wendeln + web: Weber GmbH + WEG: Weingut O. Gottschalk + WEH: Peter Werth + WEL: Weleda AG + WEN: Wilhelm Weber GmbH + WER: Werz GmbH & Co. KG + WGP: Wagner Tiefkühlprodukte GmbH + who: Westhof GmbH + WHS: Deutsche Parmalat GmbH + WIE: Weingut Stephanshof + WLM: Ecover Belgium n.v. + wmr: Richard Wirthmüller + WOB: Bäckerei Wolfgruber OHG + WOL: Verlag Fred Wollner GbR + WOO: woodshade organics ApS + WPA: Wepa P. Krengel GmbH & Co. KG + WRK: Weingut Friedhelm Rinklin + WSB: Battenfeld-Spanier + wsq: Wittenseer Quelle + WTI: WTI GmbH + WUN: Wunderland e.V. + WUR: Wurzel Fachgroßhandel + WÃœR: Prima Käse, Jürgen Würth + wwb: Westerwald Bio GmbH + wwi: Weber, Wilhelm + WYS: KAMO, Peter Wyssling + wzb: Wenzelburger GbR. + YAK: Faan Zuidhorn BV. F. Andringa + YAR: Yarrah Food/Vink Sales BV + ZAN: Zann Bio-Center + ZAP: Zapparoli + ZEL: Zellertaler Kellerei GmbH + ZIM: E. Zimmermann GmbH & Co + ZLN: Pastificio Zanellini spa + ZNL: Zagler´s Naturladen + zsl: Ziegenhof Schlatt + ZWE: Zwergenwiese Naturkost GmbH + ZWI: E. Zwicky (Deutschland) GmbH + ZWÖ: Weingut im Zwölberich + +category: + "01": Brot und Backwaren + "02": Milch, Milchprodukte, Eier, Tofu + "03": Obst, Gemüse, Sprossen, Pilze + "04": Fleisch, Wurst, Snacks + "05": Getreide, Ölsaaten, Nußkerne + "06": Nudeln, Trockenfrüchte, Müsli + "07": Brotaufstriche, Honig, Nußmuse + "08": Würzmittel, Öle, Fette + "09": Süßwaren, Gebäck, Pudding + "10": Spezialsortimente + "11": Tee, Kaffee, Kakao + "12": Getränke + "13": Kräuter, Heilmittel, Ätherische Öle + "14": Körperpflege und Kosmetik + "15": Wasch- und Reinigungsmittel + "16": Haushaltsgeräte + "17": Bücher und Zeitschriften + "18": Papier, Schreibwaren, Spielzeug + "19": Textilien und Schuhe + "20": Farben, Bau- u. Wohnmaterial + "0101": Brot + "0102": Brötchen, Semmeln, Brezen + "0103": Spezialitäten + "0111": Standardgebäck + "0112": Saisongebäck + "0113": Kuchen, Torten + "0121": Pikantes Gebäck + "0131": Sonstiges vom Bäcker + "0201": Milch + "0202": Sauermilchprodukte + "0203": Quark + "0204": Joghurt + "0205": Pudding + "0206": Sahne, Butter, Sonstiges + "0211": Ziegen-/Schafsmilchprodukte + "0221": Frischkäse + "0222": Weichkäse + "0223": Halbfester Schnittkäse + "0224": Schnittkäse + "0225": Hartkäse + "0231": Ziegen-/Schafskäse + "0241": Eier + "0251": Tofu, Tempeh + "0252": Soja-Frischprodukte + "0253": Soja- und Reisgetränke + "0254": Sojapudding + "0301": Obst, heimisch + "0302": Südfrüchte + "0303": Beeren + "0304": Exoten + "0311": Kartoffeln + "0312": Wurzelgemüse + "0313": Salate + "0314": Blatt- und Zwiebelgemüse + "0315": Kohlgemüse + "0316": Fruchtgemüse und Spezialitäten + "0321": Kräuter + "0331": Keime und Sprossen + "0341": Pilze + "0351": Nüsse in Schale + "0399": Div. Frischprodukte + "0401": Fleisch + "0402": Geflügel + "0411": Wurst + "0421": Fisch + "0422": Fischerzeugnisse + "0431": Burger, Kroketten + "0441": Sonstige Snacks + "0501": Getreide + "0502": Hülsenfrüchte + "0511": Ölsaaten + "0521": Nußkerne + "0531": Keimsaaten + "0601": Getreideprodukte + "0602": Flocken + "0603": Nudeln + "0611": Sojaerzeugnisse + "0621": Trockenfrüchte + "0631": Müsli + "0632": Krunchy + "0701": Würzige Aufstriche + "0702": Fruchtaufstriche + "0711": Honig + "0712": Honigprodukte + "0721": Nußmuse + "0801": Salz und Kräutersalz + "0802": Essig + "0803": Senf + "0804": Suppen und Soßen + "0805": Sojasoße und Miso + "0806": Würzmittel + "0811": Gewürze + "0812": Gewürzmischungen + "0813": Gewürzöle + "0821": Speiseöle + "0822": Margarine + "0831": Pikante Konserven + "0832": Süße Konserven + "0841": Fertiggerichte + "0842": Halbfertiggerichte + "0901": Frucht- und Knusperriegel + "0902": Bonbons und Lutscher + "0903": Schokolade + "0904": Pralinen + "0911": Dauergebäck + "0912": Waffeln + "0913": Kekse + "0914": Knabbergebäck + "0921": Süßmittel + "0922": Obstdicksäfte + "0923": Carob + "0931": Pudding + "0932": Back- und Geliermittel + "0933": Kochhilfen, Fermente + "1001": Säuglingsbreie + "1002": Babykost + "1011": Makrobiotische Spezialitäten + "1021": 3. Welt-Solidaritätswaren + "1031": Tiefkühlkost + "1051": Tiernahrung + "1101": Früchtetee + "1102": Kräutertee + "1103": Kräutertee-Mischungen + "1104": Rooibos + "1105": Gewürztee + "1111": Schwarzer Tee + "1112": Grüner Tee + "1113": Aromatisierter Tee + "1121": Bohnenkaffee + "1122": Ersatzkaffee + "1131": Kakao + "1132": Schokoladengetränke + "1201": Wasser + "1211": Fruchtsäfte + "1212": Fruchtnektare, Limonade, Schorle + "1213": Gemüsesäfte + "1215": Kwaszgetränke, Getreidegetränke, Diätgetränke + "1221": Bier + "1231": Rotwein + "1232": Rosé-Wein + "1233": Weißwein + "1241": Cidre + "1242": Schaumwein + "1251": Spirituosen + "1301": Heilkräuter + "1302": Kräutermischungen + "1311": Freiverkäufliche Arzneimittel + "1312": Kur- und Heilmittel + "1321": Ätherische Öle + "1322": Ätherische Ölmischungen + "1331": Duftlampen und Rauchgefäße + "1332": Zubehör für Duftwerk + "1341": Räucherwerk + "1401": Seife + "1402": Gesichtsreinigung und -pflege + "1403": Körperöl und Körperpflege + "1404": Haarpflege + "1405": Zahn- und Mundpflege + "1406": Handcreme + "1407": Fußpflege + "1411": Badezusätze und Duschpräparate + "1412": Deo, Eau de Toilette + "1413": Rasierzubehör + "1414": Sonnenschutz + "1415": Baby- und Kinderpflege + "1421": Dekorativkosmetik + "1422": Parfum + "1423": Sonstige Kosmetik + "1431": Zahnbürsten + "1432": Bürsten und Kämme + "1433": Kosmetikzubehör + "1441": Hygiene + "1451": Tierpflege + "1501": Waschmittel + "1502": Spülmittel + "1503": Reinigungsmittel + "1511": Dosierhilfsmittel + "1521": Schuhcreme + "1531": Insektenschutz, Düngemittel + "1601": Handmühlen + "1602": Elektromühlen + "1603": Kombi-Maschinen + "1604": Zubehör für Kombigeräte + "1611": Sonstige Haushaltsgeräte + "1612": Keimgeräte, Dörrapparate, Gärtöpfe + "1621": Küchenhelfer + "1622": Kaffee- und Teefilter + "1631": Haushaltswaren + "1701": Kochen und Backen + "1702": Ernährung und Gesundheit + "1703": Landwirtschaft und Garten + "1704": Ökologie und Ergänzendes + "1705": Baubiologie + "1706": Esoterisches + "1707": Sonstige Bücher + "1711": Zeitschriften + "1801": Schmuckpapier + "1802": Schulpapier + "1803": Neutrales Papier + "1804": Formdrucke + "1805": Geschenkpapier + "1806": Sonstiges Papier + "1811": Stifte + "1812": Malbedarf + "1813": Knetwachs + "1821": Kerzen + "1831": Spielzeug + "1832": Bastelbedarf + "1841": Edelsteine + "1851": CD's + "1852": MC's + "1901": Windeln + "1902": Baby- und Kinderwäsche + "1903": Erwachsenenwäsche + "1904": Oberbekleidung + "1905": Strümpfe + "1911": Schuhe und Einlegesohlen + "2001": Imprägnierung, Lasur, Balsame + "2002": Lacke + "2003": Wandfarben + "2004": Kleber + "2009": Sonstige Farben, Lösemittel + "2011": Tapeten + "2012": Bodenbeläge + "2013": Dämmstoffe + "2019": Sonstige Baumaterialien + "2021": Mobiliar + "2022": Matratzen + "2023": Heimtextilien + "2029": Sonstige Wohnmaterialien + "2031": Werkzeug, Hilfsmittel \ No newline at end of file diff --git a/plugins/article_import/lib/foodsoft_article_import/dnb_codes.yml b/plugins/article_import/lib/foodsoft_article_import/dnb_codes.yml new file mode 100644 index 00000000..b7cf5b02 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/dnb_codes.yml @@ -0,0 +1,129 @@ + +# from http://www.nieuweband.nl/producten/groepen/ +indeling: + 1: Verswaren + 50: Kaas + 62: Schapenkaas + + 2: Basisproducten + 850: Noten + 855: Noten grootverbruik + 700: Peulvruchten + 705: Peulvruchten grootverbruik + 340: Rijst + 341: Rijst grootverbruik + 450: Vlokken + 455: Vlokken grootverbruik + 800: Zaden en pitten + 805: Zaden en pitten grootverbruik + 603: Melen grootverbruik + + 3: Ontbijt en lunch + 943: Marmelade + 1272: Muesli en poppies + 1000: Notenpasta + 1276: Ontbijtmelen + 1295: Rijstwafels + 1290: Roggebrood + 1270: Sandwichspread + 940: Vruchtenbeleg + 942: Vruchtenjam + 944: Vruchtenstroop + 1300: Knäckebröd, toast en beschuit + + 4: Warme maaltijd + 1820: Mosterd + 1610: Olijfolie + 1600: Olijven + 1451: Peulvruchtenconserven + 1957: Pindasaus + 1960: Sambal, ketjap en pittige smaakmakers + 2170: Seitan + 2260: Siropen + 2248: Smaakmakers + 1500: Soepen en bouillon + 1515: Soepstengels + 2000: Sojasauzen + 2250: Suiker + 1452: Tafelzuren + 1590: Tamme-kastanje-producten + 1975: Thaise keuken + 1900: Tomatenproducten + 1670: Vetten + 1930: Visconserven + 2175: Vleesvervangers + 1360: Vruchtencompote + 1400: Vruchtenconserven + 1350: Vruchtenmoes en -puree + 2249: Zout en kruidenzout + + 5: Sappen en dranken + 2605: Rode wijn Oostenrijk + 2604: Rode wijn Portugal + 2602: Rode wijn Spanje + 2608: Rode wijn Zuid-Afrika + 2612: Rosé Spanje + 2617: Rosé Zuid-Afrika + 2420: Smoothies + 2455: Sojamelkproducten + 2505: Speciaalbieren + 2400: Vruchtensappen + 2490: Waterijs + 2637: Witte wijn Argentinië + 2630: Witte wijn Frankrijk + 2634: Witte wijn Griekenland + 2631: Witte wijn Italië + 2635: Witte wijn Oostenrijk + 2632: Witte wijn Spanje + 2638: Witte wijn Zuid-Afrika + + 6: Warme dranken en theekruiden + 3102: Kruidenthee builtjes + 3100: Kruidenthee los + 3020: Kruidenthee met geneeskrachtige werking + 3009: Rooibosthee + 3010: Thee grootverpakking + 3052: Theekruiden + 3008: Witte thee + 3011: Yogi spice tea + 3012: Yogi tao tea + 3000: Zwarte thee + + 7: Versnaperingen + 3552: Lollies + 3470: Nougat en fudge + 3570: Raw Food + 3360: Rozijntjes in kinderverpakking + 3410: Snijkoek + 3555: Snoep met suiker + 3550: Snoep zonder suiker + 3405: Stroopwafels + 3350: Tortillachips en salsa + 3358: Zoete chips + 3540: Zoethoutstokjes + 3365: Zoutjes, hartige bites en popcorn + 3530: Laurierdrop + + 8: Persoonlijke verzorging en cosmetica + 5036: Lavera + 5037: Namaste + 5040: Natracare + 5042: Odylique + 5049: Sonett + 5055: Urtekram + 5065: Weleda + + 9: Natuurtherapeutisch + 5455: Kruidentincturen + 5420: Propolis-producten + 5245: Zelfzorgmiddelen + 5280: Huid- en massage-olie + + 10: Non Food + 5517: Luiers en babydoekjes + 5510: Maandverband en tampons + 5520: Toiletpapier e.d. + 5890: Voor kinderen (en volwassenen) + 5650: Was- en schoonmaakmiddelen + 5515: Watten + 5610: Luchtverfrissers diff --git a/plugins/article_import/lib/foodsoft_article_import/engine.rb b/plugins/article_import/lib/foodsoft_article_import/engine.rb new file mode 100644 index 00000000..a2eee118 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/engine.rb @@ -0,0 +1,12 @@ +module FoodsoftArticleImport + class Engine < ::Rails::Engine + config.to_prepare do + Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| + require_dependency(c) + end + end + def default_foodsoft_config(cfg) + cfg[:use_article_import] = false + end + end +end diff --git a/plugins/article_import/lib/foodsoft_article_import/foodsoft.rb b/plugins/article_import/lib/foodsoft_article_import/foodsoft.rb new file mode 100644 index 00000000..25ff4bad --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/foodsoft.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# Module for Foodsoft-file import +# The Foodsoft-file is a CSV-file, with semicolon-separated columns, or ODS/XLS/XLSX + +require 'roo' +require 'roo-xls' + +module FoodsoftArticleImport + module Foodsoft + NAME = 'Foodsoft (CSV, ODS, XLS, XLSX)' + OUTLIST = false + OPTIONS = { + encoding: 'UTF-8', + col_sep: ';' + }.freeze + + # Parses Foodsoft file + # the yielded article is a simple hash + def self.parse(file, custom_file_path: nil) + custom_file_path ||= nil + opts = OPTIONS.dup + + ss = FoodsoftArticleImport.open_spreadsheet(file, **opts) + + header_row = true + ss.sheet(0).each.with_index(1) do |row, i| + # skip first header row + if header_row + header_row = false + next + end + # skip empty lines + if row[2].to_s.strip.empty? + # raise no order number given + yield nil, nil, i + next + end + + 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], + unit_quantity: row[10], + article_category: row[13] } + article.merge!(deposit: row[9]) unless row[9].nil? + FoodsoftArticleImport.generate_number(article) if article[:order_number].to_s.strip.empty? + if row[6].nil? || row[7].nil? || row[8].nil? + yield article, 'Error: unit, price and tax must be entered', i + else + yield article, (row[0] == 'x' ? :outlisted : nil), i + end + end + end + end +end diff --git a/plugins/article_import/lib/foodsoft_article_import/midgard_codes.yml b/plugins/article_import/lib/foodsoft_article_import/midgard_codes.yml new file mode 100644 index 00000000..8777e2e7 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/midgard_codes.yml @@ -0,0 +1,294 @@ +manufacturer: + "61": Maintal + AB: Agrobioservice + AD: Anita Dehnert + AH: Phyto Treasures e.K. + AO: Arganöl + AR: ARIES + AS: Abraham Schinken + Ad: Molkerei Andechs + An: Frans Andringa + Ap: Apfeltraum + Ar: Provamel über Arche + Ay: Aytem + BA: BauckHof Amelinghausn + BB: Bakenhus Biofleisch GmbH + BC: Bio-Bäckerei Bucco + BD: Biosa + BF: Bruno Fischer + BG: Bauers Garten + BH: Bauck Hof + BHA: Bauck Hof Amelinghausen + BI: Biofarben + BK: Burger Knäcke + BKO: BioKräuterei Oberhavel + BL: Beumer & Lutum + BM: Bohlsener Mühle + BN: Brochenin + BOD: Bode Naturkost + BR: Luchs Bier + BT: Beltane Naturkost GmbH + BU: Baumschule am Butzelberg + BV: BIO VITA + BZ: Biozeit + Ba: Bauck demeter Produkte + Bb: Beutelsbacher + Bd: Biosa Danmark Aps + Be: Behncken + Bf: Backforum + Bg: Butzelberg + Bh: Barnhouse + Bj: Milchschafhof Brünjes + Bk: Blank + Bm: Biomax + Bn: Bentele + Bo: Bobalis + Bt: Bretti's + Bu: Biogärtnerei Bauer + By: Byodo + CA: Care + CF: CLUB Feinkost + CI: CIDRERIE + CV: Cosmoveda + Ca: Campo + Cl: Obsthof Clostermann + Co: Obsthof Cordes (Heinrich) + Cp: Campobello + Cs: Cosmoveda + Ct: cbet GmbH + DA: Danival + DE: DEMETER-Erzeugergemeinschaft + DH: Dieter Hein Wurstwaren + DM: Dr. Martins + DN: Hof Dannwisch + DO: Donath-Mühle + DR: De Rit + DV: Davert + DW: Vovic / Evian + De: Dennree + Dk: Dinkula + EB: Erich Boden + EH: Engemann Handel + EI: Natürlich Eistert + ELM: BIONADE + EN: Provence Regime + EO: Eosta + ER: Euresis + Eb: Eisblümerl + Eh: Erhardt Meerrettichprodukte + Ei: Eiland + El: Kelterei Elm + En: Eichhorn + Er: Erdmannhauser Brezelfabrik + Es: Erntesegen + FB: Flensburger Brauerei + FE: Frucht-Express + FF: Schiffers + FI: Fromi GmbH + FL: Florian Kerzen + FR: I Frutti del Sole + FU: Future 3000 + Fh: Florahof + Fq: Fläming-Quelle + Fr: Frunet + Ft: Fontaine + GA: Bio-Gärtnerei Altglobsow + GG: Naturhof Günter Gaßmann + GH: Gutshöfe + GN: Nesse Gewürze + GO: Der Georgshof + GS: Gut Schmerwitz + GT: Gut Temmen + Gb: Grabower + Gl: Glaciar + Go: Golden Temple + Gr: Grützdorfer + Gw: Gwidon Zastawa + Gä: Gärtnerei am Bauerngut + GÖ: Stadtgut Görlitz + HA: Haaner Felsenquelle + HB: Hof Bockum + HF: Hühnerhof Falkenthal + HK: Heinz Ketchup + HM: Hof Marienhöhe + HO: Hoffmann + HS: Obstbau H. Schalkau + Ha: Hake + Hc: Hoch Oblatenfabrik + He: Hennicke + Hk: Natur Obsthof Hauke + Hl: Heidehof + Ho: Holle + Hu: Humanopolis + Hü: Hütterman + IC: Japan Grüntee + IN: Isola della Natura + IOC: IOC + IS: Isana + Ib: Iberia + Il: Il Nuraghe + Is: ISANA + JH: Beerenobst + JS: Juers Fruchtchips + Je: Jelitta Käse + KD: Kristdyn + KG: Kräuter Gut + KK: 74271 + KN: Öko-Gartenbau + KP: Kräutergarten Pommerland + Ka: Kanne + Kg: Karg Brotgenuß + Kä: Kärrners + Kö: Obsthof König + LB: Lammsbräu + LE: LEEB Schaf- und Ziegenmolkerei + LI: Legend Organics + LM: LeMar + LS: La Selva + La: Lahmann + Lb: Lebensbaum + Le: Leuchtenberg Sauerkrautfabrik + Lh: Lindenhof + Li: Lima Belgien + Lk: Landkrone + Ln: Land in Sicht + Ls: Lubs GmbH + Lu: Luvos Heilerde + Lw: Gärtnerei Löwenzahn + MA: Mack + MB: Mabutake + ME: Martin Evers + MH: Märkische Heide + MI: Martin Ibele + MII: Katal. Olivenöl + ML: Märkisches Landbrot + MM: Bioland Imkerei + MT: Maintal + MV: MegaVega Limited + MY: Mayka Brezel + Ma: Marschland + Mg: MIDGARD + Mh: Melchhof + Mn: Mosna + Mo: Mosaikwerkstätten + My: MAYKA, Brezelfabrik + Mü: Hofmolkerei GmbH Münchehofe + MÖ: Märkischer Ökovertrieb + NE: Natürlich Eistert + NM: + NO: Nürnberger Bio Originale + NQ: Pineo Wasser + Na: NATURATA + Nt: Natumi + OTC: OTC + Od: ODIN Holland + PB: Peter Bentele + PG: Pilzgarten + PH: Biopilzhof + PM: Pinkus Müller + PN: Pro Natura + Pi: Piding + Pt: Port International + QB: Panettoncino + RB: Rother Bräu + RP: Rheinsberger Preussenquelle + RS: rosmarin BIOBACK + RZ: Ranch Zempow + Ra: Raab + Rb: Rabenhorst + Re: Rebgarten + Rg: Rosengarten + Rh: Rotenhäusler + Ro: Geflügelhof Robert + RoL: Robert´s LOSE + Rt: Rottstock + Rö: Römerquelle + SB: Sabines Bauernhof + SBP: Stiftelsen Bananen + SC: Sommer & Co. + SF: Sprossen + SH: Spreewälder Hirse + SI: SINFO + SK: Spargelhof Kreienbaum + SL: St. Leonhardsquelle + SM: Seenlan Müritz + SO: Sonett + SR: Sprossenmanufaktur GbR + STN: Sonnentor + SV: SANTAVERDE ALOE VERA + Sa: Salamita + Sb: Hans Hermann Soetbeer + Sc: Schulz-Deetz + Sch: Hof Schütte + Sd: Savid + Se: Sekem, Ägypten + Sf: Sauerkonservenfabrik Schweizer + Sh: Kombucha + Si: Land in Sicht + Sk: Schock Ludwigsburg + Sm: Schramm + So: Sophienhof + Sp: Spielberger + Sr: Sanmar + St: Steck Senf + StB: Stralsunder Brauerei + Su: Sun,Backwaren aus Norwegen + Sv: Sunval demeter-Produkte + Sw: Szilleweit + Sy: Synanon + Sz: Schrozberg + Sü: Südasien + TB: Team Blue + TF: Terra Frischdienst + TN: Tofu Nagel + TR: Teltower Rübchen + Ta: Tarpa + Te: Teutoburger Ölmühle + Ti: Tiedemann + Tm: Tillmann + Tr: tri d´Aix + Tt: Tautropfen + Tö: Töpfer Rohrzucker + UK: Udo Kolm Bananen + UL: Gärtnerei Ulenburg + UV: Uni-Vert + Ul: Ulenburg Bioland Gemüse + VA: Kleingenossenschaft VENUSTA + VD: V & D + VE: Vega e.K. + VG: Biolog. Vollwertgetränke + VT: Vogt + VV: Vallé Käse + Vi: Viana Tofu + VlV: Vivo Lo Vin + Vo: Voelkel + WB: Weber + WD: Werder Feinkost GmbH + WH: Weide-Hardebek + WK: BioCompany Kaffee + WL: Wendland Storchenmilch + WP: Plosewasser + WR: Speickwerke + WS: Weingut Sander + Wa: Watzkendorf + We: Wendts + Wh: Molkerei Weißenhorn + Wz: Werz Heidenheim + ZA: Bio-Center Zann + ZF: Obsthof zum Felde + ZG: Zwergenwiese + ZI: Biolandhof Zielke + ZK: Ziegenkäserei Karolinenhof + ZP: Bioland Ranch Zempow + ZW: Zellertaler Wein + bF: bio Frische + bi: biosanica + dB: ÖMA-d`Beers, Kisslegg im Algäu + eu: felicia + fa: familia Müsli + ha: Hawlik + vL: v.d.Linden + öG: Öko-Gartenbau + öh: ökohum Blumenerde + ÖL: Öko-Line + ÖS: Ölmühle Solling \ No newline at end of file diff --git a/plugins/article_import/lib/foodsoft_article_import/odin.rb b/plugins/article_import/lib/foodsoft_article_import/odin.rb new file mode 100644 index 00000000..bdfa605d --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/odin.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Article import for De Nieuw Band XML file +# +# Always contains full assortment, including recently outlisted articles. +# To make sure we don't keep old articles when a number of updates was missed, +# +OUTLIST+ is set to +true+ to remove articles not present in the file. +# +require 'nokogiri' + +module FoodsoftArticleImport + class Odin + NAME = 'De Nieuwe Band (XML)' + OUTLIST = true + OPTIONS = {}.freeze + + # parses a string or file + def self.parse(file, custom_file_path: nil, **_opts) + custom_file_path ||= nil + xml = File.open(file) + doc = Nokogiri.XML(xml, nil, nil, + Nokogiri::XML::ParseOptions::RECOVER + + Nokogiri::XML::ParseOptions::NONET + + Nokogiri::XML::ParseOptions::COMPACT) # do not modify doc! + load_codes(custom_file_path) + doc.search('product').each.with_index(1) do |row, i| + # create a new article + unit = row.search('eenheid').text + unit = case unit.strip + when '' then 'st' + when 'stuk' then 'st' + when 'g' then 'gr' # need at least 2 chars + when 'l' then 'ltr' + else unit + end + inhoud = row.search('inhoud').text + inhoud.to_s.strip.empty? or (inhoud.to_f - 1).abs > 1e-3 and unit = inhoud.gsub(/\.0+\s*$/, '') + unit + deposit = row.search('statiegeld').text + deposit.to_s.strip.empty? and deposit = 0 + category = [ + @@codes[:indeling][row.search('indeling').text.to_i], + @@codes[:indeling][row.search('subindeling').text.to_i] + ].compact.join(' - ') + + status = row.search('status').text == 'Actief' ? nil : :outlisted + article = {} + unless row.search('bestelnummer').text == '' + article = { order_number: row.search('bestelnummer').text, + # :ean => row.search('eancode').text, + name: row.search('omschrijving').text, + note: row.search('kwaliteit').text, + manufacturer: row.search('merk').text, + origin: row.search('herkomst').text, + unit: unit, + price: row.search('prijs inkoopprijs').text, + unit_quantity: row.search('sve').text, + tax: row.search('btw').text, + deposit: deposit, + article_category: category } + end + yield article, status, i + end + end + + @@codes = {} + + def self.load_codes(custom_file_path = nil) + @gem_lib = File.expand_path '..', __dir__ + dir = File.join @gem_lib, 'foodsoft_article_import' + begin + @@codes = YAML.safe_load(File.open(File.join(dir, 'dnb_codes.yml'))).symbolize_keys + if custom_file_path + custom_codes = YAML.safe_load(File.open(custom_file_path)).symbolize_keys + custom_codes.each_key do |key| + custom_codes[key] = custom_codes[key].merge @@codes[key] if @@codes.keys.include?(key) + @@codes = @@codes.merge custom_codes + end + end + @@codes + rescue StandardError => e + raise "Failed to load dnb_codes: #{dir}/dnb_codes.yml: #{e.message}" + end + end + end +end diff --git a/plugins/article_import/lib/foodsoft_article_import/version.rb b/plugins/article_import/lib/foodsoft_article_import/version.rb new file mode 100644 index 00000000..5be60bf7 --- /dev/null +++ b/plugins/article_import/lib/foodsoft_article_import/version.rb @@ -0,0 +1,3 @@ +module FoodsoftArticleImport + VERSION = "0.0.1" +end diff --git a/plugins/article_import/spec/app_config.yml b/plugins/article_import/spec/app_config.yml new file mode 100644 index 00000000..e31af571 --- /dev/null +++ b/plugins/article_import/spec/app_config.yml @@ -0,0 +1,34 @@ +# Minimal Foodsoft configuration +# +# Without those settings, Foodsoft may not even work. +# This file is used when running tests. When plugins would modify foodsoft behaviour +# and they are enabled in the sample configuration, there is stable base to test with. + +default: &defaults + multi_coop_install: false + use_self_service: true + default_scope: 'f' + + name: FC Minimal + + # true by default to keep compat with older installations, but test with false here + use_nick: false + use_article_import: true + + price_markup: 5 + + # do we really need the following ones? + tax_default: 6.0 + email_sender: noreply@minimal.test + + host: localhost + + +development: + <<: *defaults + +test: + <<: *defaults + +production: + <<: *defaults diff --git a/plugins/article_import/spec/files/bnn/bnn_bad_encoding.BNN b/plugins/article_import/spec/files/bnn/bnn_bad_encoding.BNN new file mode 100644 index 00000000..0b7cb14e --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_bad_encoding.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +64721;A;;;4280001958081;4280001958203;Greek Dressing - Kräuter Mix;Oregano, Basilikum und Minze;;;med;;GR;C%;DE-ÖKO-001;120;1302;10;55;;1;6 x35g;6;35g;1;N;930190;99260;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +;;99 \ No newline at end of file diff --git a/plugins/article_import/spec/files/bnn/bnn_flawless.BNN b/plugins/article_import/spec/files/bnn/bnn_flawless.BNN new file mode 100644 index 00000000..3229196c --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_flawless.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +64721;X;;;4280001958081;4280001958203;Greek Dressing - Kr„uter Mix;Oregano, Basilikum und Minze;;;med;;GR;C%;DE-™KO-001;120;1302;10;55;;1;6 x35g;6;35g;1;N;930190;99260;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +;;99 diff --git a/plugins/article_import/spec/files/bnn/bnn_flawless_category.BNN b/plugins/article_import/spec/files/bnn/bnn_flawless_category.BNN new file mode 100644 index 00000000..78234d92 --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_flawless_category.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +64721;A;;;4280001958081;4280001958203;Greek Dressing - Kr„uter Mix;Oregano, Basilikum und Minze;;;med;;GR;C%;DE-™KO-001;120;4000;10;55;;1;6 x35g;6;35g;1;N;930190;99260;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +;;99 diff --git a/plugins/article_import/spec/files/bnn/bnn_flawless_special.BNN b/plugins/article_import/spec/files/bnn/bnn_flawless_special.BNN new file mode 100644 index 00000000..0f285f6b --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_flawless_special.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +64721;A;;;4280001958081;4280001958203;Greek Dressing - Kr„uter Mix;Oregano, Basilikum und Minze;;;med;;GR;C%;DE-™KO-001;120;1302;10;55;;1;6 x35g;6;35g;1;N;930190;99260;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;20230101;20230201;;Kg;28,571;; +;;99 diff --git a/plugins/article_import/spec/files/bnn/bnn_missing_entries.BNN b/plugins/article_import/spec/files/bnn/bnn_missing_entries.BNN new file mode 100644 index 00000000..6c8dafe9 --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_missing_entries.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +64721;A;;;4280001958081;4280001958203;Greek Dressing - Kr„uter Mix;Oregano, Basilikum und Minze;;;HDE;;GR;C%;DE-™KO-001;120;1100;10;55;;1;6 x35g;6;35g;1;N;;99260;;1,41;;;;1;;;4,49;2,89;J;;;;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +;;99 diff --git a/plugins/article_import/spec/files/bnn/bnn_missing_order_number.BNN b/plugins/article_import/spec/files/bnn/bnn_missing_order_number.BNN new file mode 100644 index 00000000..aadcb9b6 --- /dev/null +++ b/plugins/article_import/spec/files/bnn/bnn_missing_order_number.BNN @@ -0,0 +1,3 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +;A;;;4280001958081;4280001958203;Greek Dressing - Kr„uter Mix;Oregano, Basilikum und Minze;;;HDE;;GR;C%;DE-™KO-001;120;1100;10;55;;1;6 x35g;6;35g;1;N;;99260;;1,41;;;;1;;;4,49;2,89;J;;;;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +;;99 diff --git a/plugins/article_import/spec/files/bnn/demo_file.BNN b/plugins/article_import/spec/files/bnn/demo_file.BNN new file mode 100644 index 00000000..3d9cc23f --- /dev/null +++ b/plugins/article_import/spec/files/bnn/demo_file.BNN @@ -0,0 +1,7 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +5;;;;4280001958081;4280001958203;Žpfel Elstar;erntefrisch und knackig;;;obb;;D;C%;DE-?KO-001;120;0301;10;55;;1;10 x1kg;10;1kg;1;N;;;;1,41;;;;1;;;4,49;2,89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;1;; +6;;;;4280001958081;4280001958203;Brokkoli;gesund und lecker;;;fig;;IT;C%;DE-?KO-001;120;03;10;55;;1;6 x400g;6;400g;1;N;;;;1,41;;;;1;;;4,49;2,99;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2,5;; +7;;;;4280001958081;4280001958203;Tomaten;pomodori italiani, demeter;;;TDP;;IT;C%;DE-?KO-001;120;03;10;55;;1;20 x500g;20;500g;1;N;;;;1,41;;;;1;;;4,49;3,19;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;; +8;;;;4280001958081;4280001958203;Reis;Reis im Vorratssack, demeter;;;FIN;;D;C%;DE-?KO-001;120;05;10;55;;1;12 x3k;12;3kg;1;N;;;;1,41;;;;1;;;4,49;3,49;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;0,3;; +9;;;;4280001958081;4280001958203;Spaghetti;100% italienisches Hartweizengrie?;;;ZLN;;D;C%;DE-?KO-001;120;06;10;55;;1;4 x500g;4;500g;1;N;;;;1,41;;;;1;;;4,49;2,99;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;2;; +10;;;;4280001958081;4280001958203;Kartoffeln;vorwiegend festkochend;;;rsh;;D;C%;DE-?KO-001;120;0311;10;55;;1;6 x5Kg;6;5Kg;1;N;;;;1,41;;;;1;;;4,49;3,00;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;0.2;; diff --git a/plugins/article_import/spec/files/custom_codes.yml b/plugins/article_import/spec/files/custom_codes.yml new file mode 100644 index 00000000..5e9020f3 --- /dev/null +++ b/plugins/article_import/spec/files/custom_codes.yml @@ -0,0 +1,8 @@ +# BNN Codes +category: + "4000": "Schuhe" +additional: + "additional": "value" +indeling: + 11: Test Indeling + 111: Test Subindeling \ No newline at end of file diff --git a/plugins/article_import/spec/files/foodsoft/foodsoft_flawless.csv b/plugins/article_import/spec/files/foodsoft/foodsoft_flawless.csv new file mode 100644 index 00000000..a9a94c22 --- /dev/null +++ b/plugins/article_import/spec/files/foodsoft/foodsoft_flawless.csv @@ -0,0 +1,3 @@ +status;number;name;note;manufacturer;origin;unit ;clear price;tax;deposit;unit quantity;scale quantity;scale price;category +;1;product;bio;someone;eu;1 kg;1.23;6;0;10;;;coolstuff +;12;other product;bio;someone;eu;2 kg;3.45;6;0;10;;;coolstuff \ No newline at end of file diff --git a/plugins/article_import/spec/files/foodsoft/foodsoft_generate_order_number.csv b/plugins/article_import/spec/files/foodsoft/foodsoft_generate_order_number.csv new file mode 100644 index 00000000..a50dde34 --- /dev/null +++ b/plugins/article_import/spec/files/foodsoft/foodsoft_generate_order_number.csv @@ -0,0 +1,3 @@ +status;number;name;note;manufacturer;origin;unit ;clear price;tax;deposit;unit quantity;scale quantity;scale price;category +;;product;bio;someone;eu;1 kg;1.23;6;0;10;;;coolstuff +;;other product;bio;someone;eu;2 kg;3.45;6;0;10;;;coolstuff \ No newline at end of file diff --git a/plugins/article_import/spec/files/foodsoft/foodsoft_missing_entries.csv b/plugins/article_import/spec/files/foodsoft/foodsoft_missing_entries.csv new file mode 100644 index 00000000..560c11af --- /dev/null +++ b/plugins/article_import/spec/files/foodsoft/foodsoft_missing_entries.csv @@ -0,0 +1,2 @@ +status;number;name;note;manufacturer;origin;unit ;clear price;tax;deposit;unit quantity;scale quantity;scale price;category +;12;product;bio;;eu;1 kg;1.23;;0;10;;;coolstuff \ No newline at end of file diff --git a/plugins/article_import/spec/files/odin/odin_flawless.xml b/plugins/article_import/spec/files/odin/odin_flawless.xml new file mode 100644 index 00000000..5b5a28fc --- /dev/null +++ b/plugins/article_import/spec/files/odin/odin_flawless.xml @@ -0,0 +1,75 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +nucli rose +Nucli rose + +0 +0 +0 +750 +g +Stuk +0 +NELEMAN +Biologisch + + +ES + +21 +1017515 +0109 +6 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +4.52 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/files/odin/odin_flawless_custom_category.xml b/plugins/article_import/spec/files/odin/odin_flawless_custom_category.xml new file mode 100644 index 00000000..460da24c --- /dev/null +++ b/plugins/article_import/spec/files/odin/odin_flawless_custom_category.xml @@ -0,0 +1,77 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +nucli rose +Nucli rose + +0 +0 +0 +750 +g +Stuk +0 +NELEMAN +Biologisch + + +ES + +21 +1017515 +0109 +11 +111 +6 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +4.52 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/files/odin/odin_missing_entries.xml b/plugins/article_import/spec/files/odin/odin_missing_entries.xml new file mode 100644 index 00000000..5089b911 --- /dev/null +++ b/plugins/article_import/spec/files/odin/odin_missing_entries.xml @@ -0,0 +1,75 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +nucli rose +Nucli rose + +0 +0 +0 +750 + +Stuk +0 + +Biologisch + + +ES + +21 +1017515 +0109 +6 +Non Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +4.52 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/files/odin/odin_missing_order_number.xml b/plugins/article_import/spec/files/odin/odin_missing_order_number.xml new file mode 100644 index 00000000..d43a9439 --- /dev/null +++ b/plugins/article_import/spec/files/odin/odin_missing_order_number.xml @@ -0,0 +1,75 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +nucli rose +Nucli rose + +0 +0 +0 +750 +g +Stuk +0 +NELEMAN +Biologisch + + +ES + +21 +1017515 + +6 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +4.52 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/fixtures/bnn_file01.bnn b/plugins/article_import/spec/fixtures/bnn_file01.bnn new file mode 100644 index 00000000..b75b63cf --- /dev/null +++ b/plugins/article_import/spec/fixtures/bnn_file01.bnn @@ -0,0 +1,5 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +29932;;;;4280001958081;4280001958203;Walnoten (ongeroosterd);bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;1 kg;1;N;930190;99260;;1,41;;;;1;;;4,49;2,34;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +28391;;;;4280001958081;4280001958203;Pijnboompitten;dem;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;100 g;10;N;930190;99260;;1,41;;;;1;;;5,56;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;; +1829;;;;4280001958081;4280001958203;Appelsap (verpakt);;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;4x250 ml;10;4x250 ml;10;N;930190;99260;;3,21;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;ml;28,571;; +177813;;;;4280001958081;4280001958203;Tomaten;bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;500 g;20;N;930190;99260;;1,20;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;; \ No newline at end of file diff --git a/plugins/article_import/spec/fixtures/bnn_file_02.bnn b/plugins/article_import/spec/fixtures/bnn_file_02.bnn new file mode 100644 index 00000000..e3dba5bb --- /dev/null +++ b/plugins/article_import/spec/fixtures/bnn_file_02.bnn @@ -0,0 +1,2 @@ +BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1 +1;;;;4280001958081;4280001958203;Tomatoes;organic;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;20;500 g;1;N;930190;99260;;1,41;;;;1;;;4,49;1,20;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;; \ No newline at end of file diff --git a/plugins/article_import/spec/fixtures/odin_file_01.xml b/plugins/article_import/spec/fixtures/odin_file_01.xml new file mode 100644 index 00000000..3b60e83e --- /dev/null +++ b/plugins/article_import/spec/fixtures/odin_file_01.xml @@ -0,0 +1,273 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +Walnoten (ongeroosterd) +Nucli rose + +0 +0 +0 +1 +kg +Stuk +0 +Het warme woud +bio + + +NL + +6 +1017515 +29932 +10 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +2.34 +7.95 + + + +8719325207668 +Pijnboompitten +Nucli rose + +0 +0 +0 +100 +g +Stuk +0 +NELEMAN +dem + + +TR + +6 +1017515 +28391 +10 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +5.56 +7.95 + + + +8719325207668 +Appelsap (verpakt) +Nucli rose + +0 +0 +0 +4x250 +ml +Stuk +0.4 +Appelgaarde + + + +DE + +6 +1017515 +1829 +10 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +3.21 +7.95 + + + +8719325207668 +Tomaten +Nucli rose + +0 +0 +0 +500 +g +Stuk +0 +De röde hof +bio + + +DE + +6 +1017515 +177813 +20 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +1.2 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/fixtures/odin_file_02.xml b/plugins/article_import/spec/fixtures/odin_file_02.xml new file mode 100644 index 00000000..c732b4d5 --- /dev/null +++ b/plugins/article_import/spec/fixtures/odin_file_02.xml @@ -0,0 +1,75 @@ + + + +1039 +1.08 +Estafette Associatie C.V. +Geldermalsen + + +8719325207668 +Tomatoes +Nucli rose + +0 +0 +0 +500 +g +Stuk +0 +De röde hof +organic + + +Somewhere, UK + +6 +1017515 +1 +20 +Actief +druiven* +0 +0 +2 +2 +0 +0 +0 +2 +2 +0 +2 +0 +2 +0 +2 +2 +2 +2 +1 +0 +2 +0 +2 +2 + + + +0 +0 +0 +0 +1 + +2 +0 + +adviesprijs +2022-08-18 +1.2 +7.95 + + + \ No newline at end of file diff --git a/plugins/article_import/spec/integration/articles_spec.rb b/plugins/article_import/spec/integration/articles_spec.rb new file mode 100644 index 00000000..0a547515 --- /dev/null +++ b/plugins/article_import/spec/integration/articles_spec.rb @@ -0,0 +1,169 @@ +require_relative '../test_helper' +require_relative '../../../../spec/spec_helper' + +feature ArticlesController do + + let(:user) { create(:user, groups: [create(:workgroup, role_article_meta: true)]) } + let(:supplier) { create(:supplier) } + let!(:article_category) { create(:article_category) } + + before { login user } + + describe ':index', js: true do + before do + login user + visit supplier_articles_path(supplier_id: supplier.id) + end + + it 'can visit supplier articles path' do + expect(page).to have_content(supplier.name) + expect(page).to have_content(I18n.t('articles.index.edit_all')) + end + + it 'can create a new article' do + click_on I18n.t('articles.index.new') + expect(page).to have_selector('form#new_article') + article = build(:article, supplier: supplier, article_category: article_category) + within('#new_article') do + fill_in 'article_name', :with => article.name + fill_in 'article_unit', :with => article.unit + select article.article_category.name, :from => 'article_article_category_id' + fill_in 'article_price', :with => article.price + fill_in 'article_unit_quantity', :with => article.unit_quantity + fill_in 'article_tax', :with => article.tax + fill_in 'article_deposit', :with => article.deposit + # "Element cannot be scrolled into view" error, js as workaround + # find('input[type="submit"]').click + page.execute_script('$("form#new_article").submit();') + end + expect(page).to have_content(article.name) + end + end + + describe ':upload' do + let(:filename) { 'foodsoft_file_02.csv' } + let(:file) { Rails.root.join("spec/fixtures/#{filename}") } + + before do + visit upload_supplier_articles_path(supplier_id: supplier.id) + attach_file 'articles_file', file + end + + Dir.glob('spec/fixtures/foodsoft_file_01.*') do |test_file| + describe "can import articles from #{test_file}" do + let(:file) { Rails.root.join(test_file) } + + it do + find("#articles_type option[value='foodsoft']").select_option + find('input[type="submit"]').click + expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio â—Ž" + expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten" + + 4.times do |i| + all("tr:nth-child(#{i + 1}) select > option")[1].select_option + end + find('input[type="submit"]').click + expect(page).to have_content("Pijnboompitten") + + expect(supplier.articles.count).to eq 4 + end + end + end + + Dir.glob('spec/fixtures/bnn_file_01.*') do |test_file| + describe "can import articles from #{test_file}" do + let(:file) { Rails.root.join(test_file) } + + it do + find("#articles_type option[value='bnn']").select_option + find('input[type="submit"]').click + expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio" + expect(find("tr:nth-child(1) #new_articles__name").value).to eq "Walnoten (ongeroosterd)" + # set article category + 4.times do |i| + all("tr:nth-child(#{i + 1}) select > option")[1].select_option + end + find('input[type="submit"]').click + + expect(page).to have_content("Pijnboompitten") + + expect(supplier.articles.count).to eq 4 + end + end + end + end + + describe "updates" do + file_paths = ['spec/fixtures/foodsoft_file_02.csv', 'plugins/article_import/spec/fixtures/bnn_file_02.bnn', 'plugins/article_import/spec/fixtures/odin_file_02.xml'] + let(:filename) { 'foodsoft_file_02.csv' } + let(:file) { Rails.root.join("spec/fixtures/#{filename}") } + let(:val) { 'foodsoft' } + let(:type) { %w[foodsoft bnn odin] } + + before do + visit upload_supplier_articles_path(supplier_id: supplier.id) + attach_file 'articles_file', file + find("#articles_type option[value='#{val}']").select_option + end + + file_paths.each_with_index do |test_file, index| + describe "updates article for #{test_file}" do + let(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g') } + let(:file) { Rails.root.join(test_file) } + let(:val) { type[index] } + + it do + article.reload + find('input[type="submit"]').click + expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes' + find('input[type="submit"]').click + article.reload + expect(article.name).to eq 'Tomatoes' + if type[index] == "odin" + expect([article.unit, article.unit_quantity, article.price]).to eq ['500gr', 20, 1.20] + else + expect([article.unit, article.unit_quantity, article.price]).to eq ['500 g', 20, 1.20] + end + end + + it "handles missing data" do + find('input[type="submit"]').click # to overview + find('input[type="submit"]').click # missing category, re-show form + expect(find('tr.alert')).to be_present + expect(supplier.articles.count).to eq 0 + + all("tr select > option")[1].select_option + find('input[type="submit"]').click # now it should succeed + expect(supplier.articles.count).to eq 1 + end + end + + describe "can remove an existing article" do + let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 99999) } + + it do + check('articles_outlist_absent') + find('input[type="submit"]').click + expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present + + all("tr select > option")[1].select_option + find('input[type="submit"]').click + expect(article.reload.deleted?).to be true + end + end + + describe "can convert units when updating" do + let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g') } + + it do + check('articles_convert_units') + find('input[type="submit"]').click + expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes' + find('input[type="submit"]').click + article.reload + expect([article.unit, article.unit_quantity, article.price]).to eq ['250 g', 40, 0.6] + end + end + end + end +end diff --git a/plugins/article_import/spec/integration/supplier_spec.rb b/plugins/article_import/spec/integration/supplier_spec.rb new file mode 100644 index 00000000..d80c0d52 --- /dev/null +++ b/plugins/article_import/spec/integration/supplier_spec.rb @@ -0,0 +1,21 @@ +require_relative '../test_helper' +require_relative '../../../../spec/spec_helper' + +describe Supplier do + let(:supplier) { create :supplier } + + context 'syncs from file' do + it 'imports and updates articles' do + article1 = create(:article, supplier: supplier, order_number: 177813, unit: '250 g', price: 0.1) + article2 = create(:article, supplier: supplier, order_number: 12345) + supplier.articles = [article1, article2] + options = { filename: 'foodsoft_file_01.csv' } + options[:outlist_absent] = true + options[:convert_units] = true + updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), 'foodsoft', options) + expect(new_articles.length).to be > 0 + expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten' + expect(outlisted_articles.first).to eq article2 + end + end +end \ No newline at end of file diff --git a/plugins/article_import/spec/lib/bnn/foodsoft_article_import_bnn_spec.rb b/plugins/article_import/spec/lib/bnn/foodsoft_article_import_bnn_spec.rb new file mode 100644 index 00000000..d50aa591 --- /dev/null +++ b/plugins/article_import/spec/lib/bnn/foodsoft_article_import_bnn_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../../../lib/foodsoft_article_import' + +describe FoodsoftArticleImport do + files_path = File.expand_path '../../files', __dir__ + bnn_files_path = File.join(files_path, 'bnn') + + dummy_article = { name: 'Greek Dressing - Kräuter Mix', order_number: '64721', note: 'Oregano, Basilikum und Minze', + manufacturer: 'Medousa, Griechenland Importe', origin: 'GR', article_category: 'Kräutermischungen', unit: '35g', price: '2,89', tax: 7.0, unit_quantity: '6' } + + article = dummy_article.merge({ deposit: 0.08 }) + article_special = article.merge(note: 'Sonderpreis: 2,89 von 20230101 bis 20230201') + + article_2 = dummy_article.merge({ manufacturer: nil, article_category: nil }) + + article_custom_code = article.merge(article_category: 'Schuhe') + + empty = {} + + context 'bnn' do + it 'parses bnn file correctly without type parameter' do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless.BNN'))) do |new_attrs, status, _line| + expect(new_attrs).to eq article + expect(status).to eq :outlisted + end + end + it 'parses file correctly with type parameter' do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless.BNN')), type: 'bnn') do |new_attrs, status, _line| + expect(new_attrs).to eq article + expect(status).to eq :outlisted + end + end + it 'raises error wenn wrong type (except odin) specified' do + expect do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless.BNN')), type: 'foodsoft') + end.to raise_error(RuntimeError) + + expect(FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless.BNN')), type: 'odin')).to eq [] + end + it 'parses article with special correctly' do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless_special.BNN')), type: 'bnn') do |new_attrs, status, _line| + expect(new_attrs).to eq article_special + expect(status).to eq :special + end + end + it 'parses missing entries correctly' do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_missing_entries.BNN')), type: 'bnn') do |new_attrs, status, _line| + expect(new_attrs).to eq article_2 + expect(status).to eq nil + end + end + it 'skips rows without order_number' do + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_missing_order_number.BNN')), type: 'bnn') do |new_attrs, _status, _line| + expect(new_attrs).to eq empty + end + end + it 'joins custom_codes file' do + custom_file_path = File.join(files_path, 'custom_codes.yml').to_s + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_flawless_category.BNN')), custom_file_path: custom_file_path, type: 'bnn') do |new_attrs, _status, _line| + expect(new_attrs).to eq article_custom_code + end + end + it 'parses file with different encoding' do + # the bnn file is loaded with encoding ibm850. If file is not ibm850 encoded, some characters might look weird + FoodsoftArticleImport.parse(File.open(File.join(bnn_files_path, 'bnn_bad_encoding.BNN')), type: 'bnn') do |new_attrs, _status, _line| + expect(new_attrs[:order_number]).to eq('64721') + expect(new_attrs[:name]).to eq('Greek Dressing - Kräuter Mix') + end + end + end +end diff --git a/plugins/article_import/spec/lib/foodsoft/foodsoft_article_import_foodsoft_spec.rb b/plugins/article_import/spec/lib/foodsoft/foodsoft_article_import_foodsoft_spec.rb new file mode 100644 index 00000000..ce78b12b --- /dev/null +++ b/plugins/article_import/spec/lib/foodsoft/foodsoft_article_import_foodsoft_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'foodsoft_article_import' + +describe FoodsoftArticleImport do + files_path = File.expand_path '../../files', __dir__ + foodsoft_files_path = File.join(files_path, 'foodsoft') + + dummy_article = { order_number: '1', name: 'product', note: 'bio', manufacturer: 'someone', origin: 'eu', + unit: '1 kg', price: '1.23', tax: '6', unit_quantity: '10', article_category: 'coolstuff', deposit: '0' } + + dummy_article_2 = { order_number: '12', name: 'other product', note: 'bio', manufacturer: 'someone', + origin: 'eu', unit: '2 kg', price: '3.45', tax: '6', unit_quantity: '10', article_category: 'coolstuff', deposit: '0' } + + articles = [dummy_article, dummy_article_2] + + dummy_article_3 = dummy_article.merge({ order_number: ':d8df298' }) + dummy_article_4 = dummy_article_2.merge({ order_number: ':1f37e39' }) + articles_number_generated = [dummy_article_3, dummy_article_4] + empty = {} + + context 'foodsoft' do + it 'parses file correctly with type parameter foodsoft' do + count = 0 + FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_flawless.csv')), type: 'foodsoft') do |new_attrs, status, _line| + expect(new_attrs).to eq articles[count] + expect(status).to eq nil + count += 1 + end + end + + it 'raises error wenn wrong type specified' do + expect(FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_flawless.csv')), type: 'odin')).to eq [] + + expect(FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_flawless.csv')), type: 'bnn')).to eq [] + end + + it 'parses missing entries correctly' do + FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_missing_entries.csv')), type: 'foodsoft') do |new_attrs, status, _line| + expect(status).to eq 'Error: unit, price and tax must be entered' + expect(new_attrs[:unit]).to eq '1 kg' + expect(new_attrs[:manufacturer]).to eq nil + end + end + + it 'generates order numbers for articles without order number' do + count = 0 + FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_generate_order_number.csv')), type: 'foodsoft') do |new_attrs, _status, _line| + expect(new_attrs).to eq articles_number_generated[count] + count += 1 + end + end + + xit 'joins custom_codes file' do + custom_file_path = File.join(files_path, 'custom_codes.yml').to_s + FoodsoftArticleImport.parse(File.open(File.join(foodsoft_files_path, 'foodsoft_flawless_custom_category.csv')), custom_file_path: custom_file_path, type: 'foodsoft') do |new_attrs, _status, _line| + expect(new_attrs[:article_category]).to eq 'Test Indeling - Test Subindeling' + end + end + end +end diff --git a/plugins/article_import/spec/lib/odin/foodsoft_article_import_odin_spec.rb b/plugins/article_import/spec/lib/odin/foodsoft_article_import_odin_spec.rb new file mode 100644 index 00000000..af3da3f4 --- /dev/null +++ b/plugins/article_import/spec/lib/odin/foodsoft_article_import_odin_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../../../lib/foodsoft_article_import' + +describe FoodsoftArticleImport do + files_path = File.expand_path '../../files', __dir__ + odin_files_path = File.join(files_path, 'odin') + + dummy_article = { order_number: '0109', name: 'nucli rose', note: 'Biologisch', manufacturer: 'NELEMAN', + origin: 'ES', unit: '750gr', price: '4.52', unit_quantity: '6', tax: '21', deposit: '0', article_category: '' } + + empty = {} + + context 'odin' do + it 'parses file correctly with type parameter odin' do + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_flawless.xml')), type: 'odin') do |new_attrs, status, _line| + expect(new_attrs).to eq dummy_article + expect(status).to eq nil + end + end + + it 'raises error wenn wrong type specified' do + expect do + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_flawless.xml')), type: 'foodsoft') + end.to raise_error(RuntimeError) + + expect do + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_flawless.xml')), type: 'bnn') + end.to raise_error(CSV::MalformedCSVError) + end + + it 'parses missing entries correctly' do + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_missing_entries.xml')), type: 'odin') do |new_attrs, status, _line| + expect(status).to eq :outlisted + expect(new_attrs[:unit]).to eq '750st' + expect(new_attrs[:manufacturer]).to eq '' + end + end + + it 'skips rows without order_number' do + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_missing_order_number.xml')), type: 'odin') do |new_attrs, _status, _line| + expect(new_attrs).to eq empty + end + end + + it 'joins custom_codes file' do + custom_file_path = File.join(files_path, 'custom_codes.yml').to_s + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'odin_flawless_custom_category.xml')), custom_file_path: custom_file_path, type: 'odin') do |new_attrs, _status, _line| + expect(new_attrs[:article_category]).to eq 'Test Indeling - Test Subindeling' + end + end + + xit 'parses dummy_article with special correctly' do + # TODO: find out whether there are special prices for odin files + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'bnn_flawless_special.BNN')), type: 'bnn') do |new_attrs, _status, _line| + expect(new_attrs.manufacturer).to eq nil + expect(new_attrs.unit).to eq '750st' + end + end + + xit 'parses file with different encoding' do + # the bnn file is loaded with encoding ibm850. If file is not ibm850 encoded, some characters might look weird + FoodsoftArticleImport.parse(File.open(File.join(odin_files_path, 'bnn_bad_encoding.BNN')), type: 'bnn') do |new_attrs, _status, _line| + expect(new_attrs[:order_number]).to eq('64721') + expect(new_attrs[:name]).to eq('Greek Dressing - Kräuter Mix') + end + end + end +end diff --git a/plugins/article_import/spec/test_helper.rb b/plugins/article_import/spec/test_helper.rb new file mode 100644 index 00000000..527a16d8 --- /dev/null +++ b/plugins/article_import/spec/test_helper.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module TestHelper + ENV["FOODSOFT_APP_CONFIG"] = "plugins/article_import/spec/app_config.yml" +end + +RSpec.configure do |config| + config.include TestHelper, :type => :feature +end diff --git a/plugins/current_orders/app/controllers/current_orders/articles_controller.rb b/plugins/current_orders/app/controllers/current_orders/articles_controller.rb index 0e4b7dd9..51111f3f 100644 --- a/plugins/current_orders/app/controllers/current_orders/articles_controller.rb +++ b/plugins/current_orders/app/controllers/current_orders/articles_controller.rb @@ -1,6 +1,6 @@ class CurrentOrders::ArticlesController < ApplicationController before_action :authenticate_orders - before_action :find_order_and_order_article, only: [:index, :show] + before_action :find_order_and_order_article, only: %i[index show] def index # sometimes need to pass id as parameter for forms @@ -26,13 +26,13 @@ class CurrentOrders::ArticlesController < ApplicationController def find_order_and_order_article @current_orders = Order.finished_not_closed - unless params[:order_id].blank? + if params[:order_id].blank? + @order_articles = OrderArticle.where(order_id: @current_orders.all.map(&:id)) + else @order = Order.find(params[:order_id]) @order_articles = @order.order_articles - else - @order_articles = OrderArticle.where(order_id: @current_orders.all.map(&:id)) end - @q = OrderArticle.search(params[:q]) + @q = OrderArticle.ransack(params[:q]) @order_articles = @order_articles.ordered.merge(@q.result).includes(:article, :article_price) @order_article = @order_articles.where(id: params[:id]).first end diff --git a/plugins/current_orders/app/controllers/current_orders/group_orders_controller.rb b/plugins/current_orders/app/controllers/current_orders/group_orders_controller.rb index 717ebba8..945bfd30 100644 --- a/plugins/current_orders/app/controllers/current_orders/group_orders_controller.rb +++ b/plugins/current_orders/app/controllers/current_orders/group_orders_controller.rb @@ -4,10 +4,10 @@ class CurrentOrders::GroupOrdersController < ApplicationController def index # XXX code duplication lib/foodsoft_current_orders/app/controllers/current_orders/ordergroups_controller.rb - @order_ids = Order.where(state: ['open', 'finished']).all.map(&:id) - @goas = GroupOrderArticle.includes(:group_order => :ordergroup).includes(:order_article) + @order_ids = Order.where(state: %w[open finished]).all.map(&:id) + @goas = GroupOrderArticle.includes(group_order: :ordergroup).includes(:order_article) .where(group_orders: { order_id: @order_ids, ordergroup_id: @ordergroup.id }).ordered - @articles_grouped_by_category = @goas.includes(:order_article => { :article => :article_category }) + @articles_grouped_by_category = @goas.includes(order_article: { article: :article_category }) .order('articles.name') .group_by { |a| a.order_article.article.article_category.name } .sort { |a, b| a[0] <=> b[0] } @@ -18,8 +18,8 @@ class CurrentOrders::GroupOrdersController < ApplicationController # XXX code duplication from GroupOrdersController def ensure_ordergroup_member @ordergroup = @current_user.ordergroup - if @ordergroup.nil? - redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member') - end + return unless @ordergroup.nil? + + redirect_to root_url, alert: I18n.t('group_orders.errors.no_member') end end diff --git a/plugins/current_orders/app/controllers/current_orders/ordergroups_controller.rb b/plugins/current_orders/app/controllers/current_orders/ordergroups_controller.rb index 708016a9..94390a0a 100644 --- a/plugins/current_orders/app/controllers/current_orders/ordergroups_controller.rb +++ b/plugins/current_orders/app/controllers/current_orders/ordergroups_controller.rb @@ -1,6 +1,6 @@ class CurrentOrders::OrdergroupsController < ApplicationController before_action :authenticate_orders - before_action :find_group_orders, only: [:index, :show] + before_action :find_group_orders, only: %i[index show] def index # sometimes need to pass id as parameter for forms @@ -34,8 +34,11 @@ class CurrentOrders::OrdergroupsController < ApplicationController @all_ordergroups.sort_by! { |o| @ordered_group_ids.include?(o.id) ? o.name : "ZZZZZ#{o.name}" } @ordergroup = Ordergroup.find(params[:id]) unless params[:id].nil? - @goas = GroupOrderArticle.includes(:group_order, :order_article => [:article, :article_price]) - .where(group_orders: { order_id: @order_ids, ordergroup_id: @ordergroup.id }).ordered.all unless @ordergroup.nil? + return if @ordergroup.nil? + + @goas = GroupOrderArticle.includes(:group_order, order_article: %i[article article_price]) + .where(group_orders: { order_id: @order_ids, + ordergroup_id: @ordergroup.id }).ordered.all end helper_method \ diff --git a/plugins/current_orders/app/documents/multiple_orders_by_articles.rb b/plugins/current_orders/app/documents/multiple_orders_by_articles.rb index 48d8a058..95b2e3b6 100644 --- a/plugins/current_orders/app/documents/multiple_orders_by_articles.rb +++ b/plugins/current_orders/app/documents/multiple_orders_by_articles.rb @@ -77,11 +77,11 @@ class MultipleOrdersByArticles < OrderPdf .includes(:article).references(:article) .reorder('order_articles.order_id, articles.name') .preload(:article_price) # preload not join, just in case it went missing - .preload(:order, :group_order_articles => { :group_order => :ordergroup }) + .preload(:order, group_order_articles: { group_order: :ordergroup }) end - def each_order_article - order_articles.find_each_with_order(batch_size: BATCH_SIZE) { |oa| yield oa } + def each_order_article(&block) + order_articles.find_each_with_order(batch_size: BATCH_SIZE, &block) end def group_order_articles_for(order_article) @@ -90,7 +90,7 @@ class MultipleOrdersByArticles < OrderPdf goas end - def each_group_order_article_for(group_order) - group_order_articles_for(group_order).each { |goa| yield goa } + def each_group_order_article_for(group_order, &block) + group_order_articles_for(group_order).each(&block) end end diff --git a/plugins/current_orders/app/documents/multiple_orders_by_groups.rb b/plugins/current_orders/app/documents/multiple_orders_by_groups.rb index 0c9eefa9..a09ef1d4 100644 --- a/plugins/current_orders/app/documents/multiple_orders_by_groups.rb +++ b/plugins/current_orders/app/documents/multiple_orders_by_groups.rb @@ -113,7 +113,7 @@ class MultipleOrdersByGroups < OrderPdf s end - def each_ordergroup + def each_ordergroup(&block) ordergroups.find_in_batches_with_order(batch_size: BATCH_SIZE) do |ordergroups| @group_order_article_batch = GroupOrderArticle .joins(:group_order) @@ -121,8 +121,8 @@ class MultipleOrdersByGroups < OrderPdf .where(group_orders: { ordergroup_id: ordergroups.map(&:id) }) .order('group_orders.order_id, group_order_articles.id') .preload(group_orders: { order: :supplier }) - .preload(order_article: [:article, :article_price, :order]) - ordergroups.each { |ordergroup| yield ordergroup } + .preload(order_article: %i[article article_price order]) + ordergroups.each(&block) end end @@ -130,7 +130,7 @@ class MultipleOrdersByGroups < OrderPdf @group_order_article_batch.select { |goa| goa.group_order.ordergroup_id == ordergroup.id } end - def each_group_order_article_for(ordergroup) - group_order_articles_for(ordergroup).each { |goa| yield goa } + def each_group_order_article_for(ordergroup, &block) + group_order_articles_for(ordergroup).each(&block) end end diff --git a/plugins/current_orders/app/helpers/current_orders_helper.rb b/plugins/current_orders/app/helpers/current_orders_helper.rb index 3bbab482..c62493a1 100644 --- a/plugins/current_orders/app/helpers/current_orders_helper.rb +++ b/plugins/current_orders/app/helpers/current_orders_helper.rb @@ -6,7 +6,9 @@ module CurrentOrdersHelper elsif funds == 0 I18n.t('helpers.current_orders.pay_none') else - content_tag :b, I18n.t('helpers.current_orders.pay_amount', amount: number_to_currency(-ordergroup.get_available_funds)) + content_tag :b, + I18n.t('helpers.current_orders.pay_amount', + amount: number_to_currency(-ordergroup.get_available_funds)) end end end diff --git a/plugins/current_orders/config/locales/tr.yml b/plugins/current_orders/config/locales/tr.yml new file mode 100644 index 00000000..7087f541 --- /dev/null +++ b/plugins/current_orders/config/locales/tr.yml @@ -0,0 +1,73 @@ +tr: + config: + hints: + use_current_orders: Åžimdiki_sipariÅŸler eklentisini aktif et. SipariÅŸ izni olan üyelerin, SipariÅŸler menüsünde üç yeni ekran kullanarak birden çok sipariÅŸte üye miktarlarını deÄŸiÅŸtirmesine olanak tanır. Özellikle teslim alma günleri için faydalıdır. + keys: + use_current_orders: Ekstra dağıtım ekranları + current_orders: + articles: + article: + counts: '%{ordergroups} sipariÅŸ grubu, %{articles} farklı ürün sipariÅŸ etti.' + no_selection: Hangi üyenin ne sipariÅŸ ettiÄŸini görmek için bir ürün seçin, veya saÄŸ taraftan toplama listelerini indirin. + article_info: + origin_in: '%{origin} içinde' + supplied_by: '%{supplier} tarafından' + supplied_and_made_by: '%{manufacturer} tarafından yapıldı' + supplied_by_made_by: '%{supplier} tarafından saÄŸlandı, %{manufacturer} tarafından yapıldı' + unit: '%{unit} başına' + from: '%{supplier} tarafından' + form: + article_placeholder: Ãœrünleri arayın... + current_orders: Tüm güncel sipariÅŸler + index: + title: Ãœrünleri dağıt + ordergroups: + piece: adet + unit: birim + add_new: Bir sipariÅŸ grubu ekleyin... + show: + title: ! '%{name}' + navigation: + receive: Teslim al + articles: Dağıtım + ordergroups: Ãœye sipariÅŸleri + group_orders: + index: + title: Mevcut sipariÅŸleriniz + ordergroups: + articles: + add_new: Yeni bir ürün ekleyin... + no_selection: Hangi sipariÅŸ grubunu göstermek istediÄŸinizi seçin. + form: + ordergroup_placeholder: Bir sipariÅŸ grubu seçin... + index: + title: Ãœrünler - sipariÅŸ grubu için + payment_bar: + account_balance: Hesap bakiyesi + new_pin: PIN + new_transaction: Yeni iÅŸlem + payment: ! 'Ödeme:' + show: + title: '%{name} adına ürünler' + orders: + receive: + title: SipariÅŸleri al + no_finished_orders: Åžu anda alınacak sipariÅŸ yok. + documents: + multiple_orders_by_articles: + filename: Ãœrüne göre mevcut sipariÅŸler + title: Mevcut sipariÅŸler - ürüne göre + multiple_orders_by_groups: + filename: Gruba göre mevcut sipariÅŸler + title: Mevcut sipariÅŸler - gruba göre + helpers: + current_orders: + pay_done: Tamamen ödendi + pay_none: Ödenecek bir ÅŸey yok + pay_amount: Ödenecek tutar %{amount} + js: + current_orders: + articles: + above: '%{count} adet mevcut stoktan fazla' + below: '%{count} adet mevcut stokta kaldı' + equal: Tümü dağıtıldı diff --git a/plugins/current_orders/config/routes.rb b/plugins/current_orders/config/routes.rb index f642fc31..aeb2b014 100644 --- a/plugins/current_orders/config/routes.rb +++ b/plugins/current_orders/config/routes.rb @@ -1,27 +1,27 @@ Rails.application.routes.draw do scope '/:foodcoop' do namespace :current_orders do - resources :ordergroups, :only => [:index, :show] do + resources :ordergroups, only: %i[index show] do collection do get :show_on_group_order_article_create get :show_on_group_order_article_update end end - resources :articles, :only => [:index, :show] do + resources :articles, only: %i[index show] do collection do get :show_on_group_order_article_create end end - resource :orders, :only => [:show] do + resource :orders, only: [:show] do collection do get :my get :receive end end - resources :group_orders, :only => [:index] + resources :group_orders, only: [:index] end end end diff --git a/plugins/current_orders/foodsoft_current_orders.gemspec b/plugins/current_orders/foodsoft_current_orders.gemspec index c444fbec..f8e22ce6 100644 --- a/plugins/current_orders/foodsoft_current_orders.gemspec +++ b/plugins/current_orders/foodsoft_current_orders.gemspec @@ -1,20 +1,21 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_current_orders/version" +require 'foodsoft_current_orders/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_current_orders" + s.name = 'foodsoft_current_orders' s.version = FoodsoftCurrentOrders::VERSION - s.authors = ["wvengen"] - s.email = ["dev-voko@willem.engen.nl"] - s.homepage = "https://github.com/foodcoop-adam/foodsoft" - s.summary = "Quick support for working on all currently active orders in foodsoft." - s.description = "" + s.authors = ['wvengen'] + s.email = ['dev-voko@willem.engen.nl'] + s.homepage = 'https://github.com/foodcoop-adam/foodsoft' + s.summary = 'Quick support for working on all currently active orders in foodsoft.' + s.description = '' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/current_orders/lib/foodsoft_current_orders.rb b/plugins/current_orders/lib/foodsoft_current_orders.rb index 5a9a7c82..8227bea4 100644 --- a/plugins/current_orders/lib/foodsoft_current_orders.rb +++ b/plugins/current_orders/lib/foodsoft_current_orders.rb @@ -1,5 +1,5 @@ -require "deface" -require "foodsoft_current_orders/engine" +require 'deface' +require 'foodsoft_current_orders/engine' module FoodsoftCurrentOrders def self.enabled? diff --git a/plugins/current_orders/lib/foodsoft_current_orders/engine.rb b/plugins/current_orders/lib/foodsoft_current_orders/engine.rb index 07427b56..c6236acb 100644 --- a/plugins/current_orders/lib/foodsoft_current_orders/engine.rb +++ b/plugins/current_orders/lib/foodsoft_current_orders/engine.rb @@ -4,12 +4,15 @@ module FoodsoftCurrentOrders return unless FoodsoftCurrentOrders.enabled? return if primary[:orders].nil? - cond = Proc.new { current_user.role_orders? } + cond = proc { current_user.role_orders? } [ SimpleNavigation::Item.new(primary, :stage_divider, nil, nil, class: 'divider', if: cond), - SimpleNavigation::Item.new(primary, :current_orders_receive, I18n.t('current_orders.navigation.receive'), context.receive_current_orders_orders_path, if: cond), - SimpleNavigation::Item.new(primary, :current_orders_articles, I18n.t('current_orders.navigation.articles'), context.current_orders_articles_path, if: cond), - SimpleNavigation::Item.new(primary, :current_orders_ordergroups, I18n.t('current_orders.navigation.ordergroups'), context.current_orders_ordergroups_path, if: cond) + SimpleNavigation::Item.new(primary, :current_orders_receive, I18n.t('current_orders.navigation.receive'), + context.receive_current_orders_orders_path, if: cond), + SimpleNavigation::Item.new(primary, :current_orders_articles, I18n.t('current_orders.navigation.articles'), + context.current_orders_articles_path, if: cond), + SimpleNavigation::Item.new(primary, :current_orders_ordergroups, + I18n.t('current_orders.navigation.ordergroups'), context.current_orders_ordergroups_path, if: cond) ].each { |i| primary[:orders].sub_navigation.items << i } end end diff --git a/plugins/current_orders/lib/foodsoft_current_orders/version.rb b/plugins/current_orders/lib/foodsoft_current_orders/version.rb index af58aa9c..9e916ba6 100644 --- a/plugins/current_orders/lib/foodsoft_current_orders/version.rb +++ b/plugins/current_orders/lib/foodsoft_current_orders/version.rb @@ -1,3 +1,3 @@ module FoodsoftCurrentOrders - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/discourse/Rakefile b/plugins/discourse/Rakefile index cb56e2e5..abec3d42 100755 --- a/plugins/discourse/Rakefile +++ b/plugins/discourse/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/discourse/app/controllers/discourse_controller.rb b/plugins/discourse/app/controllers/discourse_controller.rb index 5a65f61c..0e62f14d 100644 --- a/plugins/discourse/app/controllers/discourse_controller.rb +++ b/plugins/discourse/app/controllers/discourse_controller.rb @@ -11,7 +11,7 @@ class DiscourseController < ApplicationController def redirect_to_with_payload(url, payload) base64_payload = Base64.strict_encode64 payload.to_query - sso = CGI::escape base64_payload + sso = CGI.escape base64_payload sig = get_hmac_hex_string base64_payload redirect_to "#{url}#{url.include?('?') ? '&' : '?'}sso=#{sso}&sig=#{sig}" end @@ -21,7 +21,7 @@ class DiscourseController < ApplicationController payload.symbolize_keys! end - def get_hmac_hex_string payload + def get_hmac_hex_string(payload) discourse_sso_secret = FoodsoftConfig[:discourse_sso_secret] OpenSSL::HMAC.hexdigest 'sha256', discourse_sso_secret, payload end diff --git a/plugins/discourse/app/controllers/discourse_login_controller.rb b/plugins/discourse/app/controllers/discourse_login_controller.rb index 1c8fe938..bd7a81e3 100644 --- a/plugins/discourse/app/controllers/discourse_login_controller.rb +++ b/plugins/discourse/app/controllers/discourse_login_controller.rb @@ -5,7 +5,7 @@ class DiscourseLoginController < DiscourseController def initiate discourse_url = FoodsoftConfig[:discourse_url] - nonce = SecureRandom.hex() + nonce = SecureRandom.hex return_sso_url = url_for(action: :callback, only_path: false) session[:discourse_sso_nonce] = nonce @@ -36,7 +36,7 @@ class DiscourseLoginController < DiscourseController user.save! login_and_redirect_to_return_to user, notice: I18n.t('discourse.callback.logged_in') - rescue => error - redirect_to login_url, alert: error.to_s + rescue StandardError => e + redirect_to login_url, alert: e.to_s end end diff --git a/plugins/discourse/app/controllers/discourse_sso_controller.rb b/plugins/discourse/app/controllers/discourse_sso_controller.rb index e8f742b6..04dc8d1c 100644 --- a/plugins/discourse/app/controllers/discourse_sso_controller.rb +++ b/plugins/discourse/app/controllers/discourse_sso_controller.rb @@ -17,7 +17,7 @@ class DiscourseSsoController < DiscourseController external_id: "#{FoodsoftConfig.scope}/#{current_user.id}", username: current_user.nick, name: current_user.name - rescue => error - redirect_to root_url, alert: error.to_s + rescue StandardError => e + redirect_to root_url, alert: e.to_s end end diff --git a/plugins/discourse/config/locales/tr.yml b/plugins/discourse/config/locales/tr.yml new file mode 100644 index 00000000..0cb231c8 --- /dev/null +++ b/plugins/discourse/config/locales/tr.yml @@ -0,0 +1,6 @@ +tr: + discourse: + callback: + invalid_nonce: Geçersiz nonce + invalid_signature: Geçersiz imza + logged_in: GiriÅŸ yapıldı! diff --git a/plugins/discourse/foodsoft_discourse.gemspec b/plugins/discourse/foodsoft_discourse.gemspec index 4400bcf4..1e6113ce 100644 --- a/plugins/discourse/foodsoft_discourse.gemspec +++ b/plugins/discourse/foodsoft_discourse.gemspec @@ -1,20 +1,21 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_discourse/version" +require 'foodsoft_discourse/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_discourse" + s.name = 'foodsoft_discourse' s.version = FoodsoftDiscourse::VERSION - s.authors = ["paroga"] - s.email = ["paroga@paroga.com"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Discourse plugin for foodsoft." - s.description = "Allow SSO login via Discourse" + s.authors = ['paroga'] + s.email = ['paroga@paroga.com'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Discourse plugin for foodsoft.' + s.description = 'Allow SSO login via Discourse' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb b/plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb index 901979b1..6ddfdb13 100644 --- a/plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb +++ b/plugins/discourse/lib/foodsoft_discourse/redirect_to_login.rb @@ -2,7 +2,7 @@ module FoodsoftDiscourse module RedirectToLogin def self.included(base) # :nodoc: base.class_eval do - alias foodsoft_discourse_orig_redirect_to_login redirect_to_login + alias_method :foodsoft_discourse_orig_redirect_to_login, :redirect_to_login def redirect_to_login(options = {}) if FoodsoftDiscourse.enabled? && !FoodsoftConfig[:discourse_sso] @@ -18,5 +18,5 @@ end # modify existing helper ActiveSupport.on_load(:after_initialize) do - Concerns::Auth.send :include, FoodsoftDiscourse::RedirectToLogin + Concerns::Auth.include FoodsoftDiscourse::RedirectToLogin end diff --git a/plugins/discourse/lib/foodsoft_discourse/version.rb b/plugins/discourse/lib/foodsoft_discourse/version.rb index 2b8d4138..60d38a51 100644 --- a/plugins/discourse/lib/foodsoft_discourse/version.rb +++ b/plugins/discourse/lib/foodsoft_discourse/version.rb @@ -1,3 +1,3 @@ module FoodsoftDiscourse - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/documents/Rakefile b/plugins/documents/Rakefile index 2834c5f3..861a530a 100755 --- a/plugins/documents/Rakefile +++ b/plugins/documents/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/documents/app/controllers/documents_controller.rb b/plugins/documents/app/controllers/documents_controller.rb index b97470a5..7290ef3c 100644 --- a/plugins/documents/app/controllers/documents_controller.rb +++ b/plugins/documents/app/controllers/documents_controller.rb @@ -4,16 +4,16 @@ class DocumentsController < ApplicationController before_action -> { require_plugin_enabled FoodsoftDocuments } def index - if params["sort"] - sort = case params["sort"] - when "name" then "data IS NULL DESC, name" - when "created_at" then "created_at" - when "name_reverse" then "data IS NULL, name DESC" - when "created_at_reverse" then "created_at DESC" + sort = if params['sort'] + case params['sort'] + when 'name' then 'data IS NULL DESC, name' + when 'created_at' then 'created_at' + when 'name_reverse' then 'data IS NULL, name DESC' + when 'created_at_reverse' then 'created_at DESC' end - else - sort = "data IS NULL DESC, name" - end + else + 'data IS NULL DESC, name' + end @documents = Document.where(parent: @document).page(params[:page]).per(@per_page).order(sort) end @@ -34,22 +34,22 @@ class DocumentsController < ApplicationController if @document.name.empty? name = File.basename(data.original_filename) - @document.name = name.gsub(/[^\w\.\-]/, '_') + @document.name = name.gsub(/[^\w.-]/, '_') end end @document.created_by = current_user @document.save! redirect_to @document.parent || documents_path, notice: t('.notice') - rescue => error - redirect_to @document.parent || documents_path, alert: t('.error', error: error.message) + rescue StandardError => e + redirect_to @document.parent || documents_path, alert: t('.error', error: e.message) end def update @document = Document.find(params[:id]) @document.update_attribute(:parent_id, params[:parent_id]) redirect_to @document.parent || documents_path, notice: t('.notice') - rescue => error - redirect_to @document.parent || documents_path, alert: t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to @document.parent || documents_path, alert: t('errors.general_msg', msg: e.message) end def destroy @@ -60,8 +60,8 @@ class DocumentsController < ApplicationController else redirect_to documents_path, alert: t('.no_right') end - rescue => error - redirect_to documents_path, alert: t('.error', error: error.message) + rescue StandardError => e + redirect_to documents_path, alert: t('.error', error: e.message) end def show diff --git a/plugins/documents/config/locales/tr.yml b/plugins/documents/config/locales/tr.yml new file mode 100644 index 00000000..05003fdf --- /dev/null +++ b/plugins/documents/config/locales/tr.yml @@ -0,0 +1,40 @@ +tr: + activerecord: + attributes: + document: + created_at: OluÅŸturulma tarihi + created_by: Tarafından oluÅŸturuldu + data: Veri + mime: MIME türü + name: Ad + config: + hints: + documents_allowed_extension: Ä°zin verilen dosya uzantılarının boÅŸluklarla ayrılmış bir listesi. + use_documents: Gıda kooperatifi menüsüne temel bir belge paylaşım sayfası ekleyin. + keys: + documents_allowed_extension: Ä°zin verilen uzantılar + use_documents: Belgeleri etkinleÅŸtir + navigation: + documents: Belgeler + documents: + create: + error: 'Belge veya klasör oluÅŸturulamadı: %{error}' + not_allowed_mime: '"%{mime}" dosya türüne izin verilmiyor. Lütfen bunu beyaz listeye almak için bir yöneticiyle iletiÅŸime geçin.' + notice: Belge veya klasör oluÅŸturuldu + destroy: + error: 'Belge veya klasör silinemedi: %{error}' + no_right: Belgeyi veya klasörü silmek için yeterli yetkiniz yok + notice: Belge veya klasör silindi + form: + new: Yeni belge + new_folder: Yeni klasör + submit: OluÅŸtur + index: + new: Yeni belge yükle + new_folder: Yeni klasör oluÅŸtur + title: Belgeler + move: + root_folder: BaÅŸlangıç + title: Taşı + update: + notice: Belge veya klasör taşındı diff --git a/plugins/documents/foodsoft_documents.gemspec b/plugins/documents/foodsoft_documents.gemspec index 1301fa11..63d3b428 100644 --- a/plugins/documents/foodsoft_documents.gemspec +++ b/plugins/documents/foodsoft_documents.gemspec @@ -1,21 +1,22 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_documents/version" +require 'foodsoft_documents/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_documents" + s.name = 'foodsoft_documents' s.version = FoodsoftDocuments::VERSION - s.authors = ["paroga"] - s.email = ["paroga@paroga.com"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Documents plugin for foodsoft." - s.description = "Adds simple document management to foodsoft." + s.authors = ['paroga'] + s.email = ['paroga@paroga.com'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Documents plugin for foodsoft.' + s.description = 'Adds simple document management to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" - s.add_dependency "ruby-filemagic" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.add_dependency 'ruby-filemagic' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/documents/lib/foodsoft_documents/engine.rb b/plugins/documents/lib/foodsoft_documents/engine.rb index 970b3aa5..de81904c 100644 --- a/plugins/documents/lib/foodsoft_documents/engine.rb +++ b/plugins/documents/lib/foodsoft_documents/engine.rb @@ -8,9 +8,9 @@ module FoodsoftDocuments sub_nav.items << SimpleNavigation::Item.new(primary, :documents, I18n.t('navigation.documents'), context.documents_path) # move to right before tasks item - if i = sub_nav.items.index(sub_nav[:tasks]) - sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) - end + return unless i = sub_nav.items.index(sub_nav[:tasks]) + + sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) end def default_foodsoft_config(cfg) diff --git a/plugins/documents/lib/foodsoft_documents/version.rb b/plugins/documents/lib/foodsoft_documents/version.rb index 6e57dbb3..7096e468 100644 --- a/plugins/documents/lib/foodsoft_documents/version.rb +++ b/plugins/documents/lib/foodsoft_documents/version.rb @@ -1,3 +1,3 @@ module FoodsoftDocuments - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/links/Rakefile b/plugins/links/Rakefile index fb6356f8..a9902fd3 100755 --- a/plugins/links/Rakefile +++ b/plugins/links/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/links/app/controllers/admin/links_controller.rb b/plugins/links/app/controllers/admin/links_controller.rb index 2b6a7a35..dc8b359e 100644 --- a/plugins/links/app/controllers/admin/links_controller.rb +++ b/plugins/links/app/controllers/admin/links_controller.rb @@ -37,8 +37,8 @@ class Admin::LinksController < Admin::BaseController link = Link.find(params[:id]) link.destroy! redirect_to admin_links_path - rescue => error - redirect_to admin_links_path, I18n.t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to admin_links_path, I18n.t('errors.general_msg', msg: e.message) end private diff --git a/plugins/links/app/controllers/links_controller.rb b/plugins/links/app/controllers/links_controller.rb index 143fc63d..df38bee3 100644 --- a/plugins/links/app/controllers/links_controller.rb +++ b/plugins/links/app/controllers/links_controller.rb @@ -5,9 +5,7 @@ class LinksController < ApplicationController link = Link.find(params[:id]) url = link.url - if link.workgroup && !current_user.role_admin? && !link.workgroup.member?(current_user) - return deny_access - end + return deny_access if link.workgroup && !current_user.role_admin? && !link.workgroup.member?(current_user) if link.indirect uri = URI.parse url @@ -19,11 +17,9 @@ class LinksController < ApplicationController url = result.header['Location'] - unless url - return redirect_to root_url, alert: t('.indirect_no_location') - end + return redirect_to root_url, alert: t('.indirect_no_location') unless url end - redirect_to url, status: 302 + redirect_to url, status: :found, allow_other_host: true end end diff --git a/plugins/links/config/locales/tr.yml b/plugins/links/config/locales/tr.yml new file mode 100644 index 00000000..2de2dc20 --- /dev/null +++ b/plugins/links/config/locales/tr.yml @@ -0,0 +1,23 @@ +tr: + activerecord: + attributes: + link: + name: Adı + url: URL + workgroup: Çalışma Grubu + indirect: Dolaylı + authorization: Yetkilendirme BaÅŸlığı + admin: + links: + index: + title: Linkler + new_link: Yeni link ekle + form: + description: Bir çalışma grubu seçildiÄŸinde, link yalnızca grubun üyelerine görünür. 'Dolaylı' seçeneÄŸi, Foodsoft'un kullanıcıları URL tarafından döndürülen adrese yönlendirmesine izin verir. Bu seçenek, yalnızca tam iÅŸlevselliÄŸi doÄŸru anlaşıldığında etkinleÅŸtirilmelidir. + links: + show: + indirect_no_location: Yapılandırılmış URL, yönlendirme için bir Konum baÅŸlığı döndürmedi. + navigation: + admin: + links: Linkler + links: Linkler diff --git a/plugins/links/foodsoft_links.gemspec b/plugins/links/foodsoft_links.gemspec index e899c082..c879f4b1 100644 --- a/plugins/links/foodsoft_links.gemspec +++ b/plugins/links/foodsoft_links.gemspec @@ -1,20 +1,21 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_links/version" +require 'foodsoft_links/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_links" + s.name = 'foodsoft_links' s.version = FoodsoftLinks::VERSION - s.authors = ["paroga"] - s.email = ["paroga@paroga.com"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Links plugin for foodsoft." - s.description = "Adds simple link management to foodsoft." + s.authors = ['paroga'] + s.email = ['paroga@paroga.com'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Links plugin for foodsoft.' + s.description = 'Adds simple link management to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/links/lib/foodsoft_links/engine.rb b/plugins/links/lib/foodsoft_links/engine.rb index ab6d9175..52672597 100644 --- a/plugins/links/lib/foodsoft_links/engine.rb +++ b/plugins/links/lib/foodsoft_links/engine.rb @@ -1,7 +1,7 @@ module FoodsoftLinks class Engine < ::Rails::Engine def navigation(primary, context) - primary.item :links, I18n.t('navigation.links'), '#', if: Proc.new { visble_links(context).any? } do |subnav| + primary.item :links, I18n.t('navigation.links'), '#', if: proc { visble_links(context).any? } do |subnav| visble_links(context).each do |link| subnav.item link.id, link.name, context.link_path(link) end @@ -11,15 +11,15 @@ module FoodsoftLinks primary.items.insert(i, primary.items.delete_at(-1)) end - unless primary[:admin].nil? - sub_nav = primary[:admin].sub_navigation - sub_nav.items << - SimpleNavigation::Item.new(primary, :links, I18n.t('navigation.admin.links'), context.admin_links_path) - # move to right before config item - if i = sub_nav.items.index(sub_nav[:config]) - sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) - end - end + return if primary[:admin].nil? + + sub_nav = primary[:admin].sub_navigation + sub_nav.items << + SimpleNavigation::Item.new(primary, :links, I18n.t('navigation.admin.links'), context.admin_links_path) + # move to right before config item + return unless i = sub_nav.items.index(sub_nav[:config]) + + sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) end def visble_links(context) diff --git a/plugins/links/lib/foodsoft_links/version.rb b/plugins/links/lib/foodsoft_links/version.rb index 20341ca4..707cfdb3 100644 --- a/plugins/links/lib/foodsoft_links/version.rb +++ b/plugins/links/lib/foodsoft_links/version.rb @@ -1,3 +1,3 @@ module FoodsoftLinks - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/messages/Rakefile b/plugins/messages/Rakefile index ac014bdd..9e2cbeab 100755 --- a/plugins/messages/Rakefile +++ b/plugins/messages/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/messages/app/controllers/admin/messagegroups_controller.rb b/plugins/messages/app/controllers/admin/messagegroups_controller.rb index cce57474..76fbbfb9 100644 --- a/plugins/messages/app/controllers/admin/messagegroups_controller.rb +++ b/plugins/messages/app/controllers/admin/messagegroups_controller.rb @@ -4,7 +4,7 @@ class Admin::MessagegroupsController < Admin::BaseController def index @messagegroups = Messagegroup.order('name ASC') # if somebody uses the search field: - @messagegroups = @messagegroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].blank? + @messagegroups = @messagegroups.where('name LIKE ?', "%#{params[:query]}%") if params[:query].present? @messagegroups = @messagegroups.page(params[:page]).per(@per_page) end @@ -13,7 +13,7 @@ class Admin::MessagegroupsController < Admin::BaseController @messagegroup = Messagegroup.find(params[:id]) @messagegroup.destroy redirect_to admin_messagegroups_url, notice: t('admin.messagegroups.destroy.notice') - rescue => error - redirect_to admin_messagegroups_url, alert: t('admin.messagegroups.destroy.error', error: error.message) + rescue StandardError => e + redirect_to admin_messagegroups_url, alert: t('admin.messagegroups.destroy.error', error: e.message) end end diff --git a/plugins/messages/app/controllers/messagegroups_controller.rb b/plugins/messages/app/controllers/messagegroups_controller.rb index e9ba6770..7629eab8 100644 --- a/plugins/messages/app/controllers/messagegroups_controller.rb +++ b/plugins/messages/app/controllers/messagegroups_controller.rb @@ -1,17 +1,17 @@ class MessagegroupsController < ApplicationController def index - @messagegroups = Messagegroup.order("name") + @messagegroups = Messagegroup.order('name') end def join @messagegroup = Messagegroup.find(params[:id]) @messagegroup.users << current_user - redirect_to messagegroups_url, :notice => I18n.t('messagegroups.join.notice') + redirect_to messagegroups_url, notice: I18n.t('messagegroups.join.notice') end def leave @messagegroup = Messagegroup.find(params[:id]) @messagegroup.users.destroy(current_user) - redirect_to messagegroups_url, :notice => I18n.t('messagegroups.leave.notice') + redirect_to messagegroups_url, notice: I18n.t('messagegroups.leave.notice') end end diff --git a/plugins/messages/app/controllers/messages_controller.rb b/plugins/messages/app/controllers/messages_controller.rb index 628f145b..aadaad77 100644 --- a/plugins/messages/app/controllers/messages_controller.rb +++ b/plugins/messages/app/controllers/messages_controller.rb @@ -10,21 +10,21 @@ class MessagesController < ApplicationController def new @message = Message.new(params[:message]) - if @message.reply_to - original_message = Message.find(@message.reply_to) - if original_message.reply_to - @message.reply_to = original_message.reply_to - end - if original_message.is_readable_for?(current_user) - @message.add_recipients [original_message.sender_id] - @message.group_id = original_message.group_id - @message.private = original_message.private - @message.subject = I18n.t('messages.model.reply_subject', :subject => original_message.subject) - @message.body = I18n.t('messages.model.reply_header', :user => original_message.sender.display, :when => I18n.l(original_message.created_at, :format => :short)) + "\n" - original_message.body.each_line { |l| @message.body += I18n.t('messages.model.reply_indent', :line => l) } - else - redirect_to new_message_url, alert: I18n.t('messages.new.error_private') - end + return unless @message.reply_to + + original_message = Message.find(@message.reply_to) + @message.reply_to = original_message.reply_to if original_message.reply_to + if original_message.is_readable_for?(current_user) + @message.add_recipients [original_message.sender_id] + @message.group_id = original_message.group_id + @message.private = original_message.private + @message.subject = I18n.t('messages.model.reply_subject', subject: original_message.subject) + @message.body = I18n.t('messages.model.reply_header', user: original_message.sender.display, + when: I18n.l(original_message.created_at, format: :short)) + "\n" + @message.body = I18n.t('messages.model.reply_header', user: original_message.sender.display, when: I18n.l(original_message.created_at, format: :short)) + "\n" \ + + "
" + original_message.body.to_trix_html + "
" + else + redirect_to new_message_url, alert: I18n.t('messages.new.error_private') end end @@ -33,18 +33,18 @@ class MessagesController < ApplicationController @message = @current_user.send_messages.new(params[:message]) if @message.save DeliverMessageJob.perform_later(@message) - redirect_to messages_url, :notice => I18n.t('messages.create.notice') + redirect_to messages_url, notice: I18n.t('messages.create.notice') else - render :action => 'new' + render action: 'new' end end # Shows a single message. def show @message = Message.find(params[:id]) - unless @message.is_readable_for?(current_user) - redirect_to messages_url, alert: I18n.t('messages.new.error_private') - end + return if @message.is_readable_for?(current_user) + + redirect_to messages_url, alert: I18n.t('messages.new.error_private') end def toggle_private diff --git a/plugins/messages/app/helpers/messages_helper.rb b/plugins/messages/app/helpers/messages_helper.rb index d5371fe4..c385a17f 100644 --- a/plugins/messages/app/helpers/messages_helper.rb +++ b/plugins/messages/app/helpers/messages_helper.rb @@ -1,11 +1,11 @@ module MessagesHelper def format_subject(message, length) if message.subject.length > length - subject = truncate(message.subject, :length => length) - body = "" + subject = truncate(message.subject, length: length) + body = '' else subject = message.subject - body = truncate(message.body, :length => length - subject.length) + body = truncate(message.body.to_plain_text, length: length - subject.length) end "#{link_to(h(subject), message)} #{h(body)}".html_safe end diff --git a/plugins/messages/app/mail_receivers/messages_mail_receiver.rb b/plugins/messages/app/mail_receivers/messages_mail_receiver.rb index e9ca99f3..006c3f8d 100644 --- a/plugins/messages/app/mail_receivers/messages_mail_receiver.rb +++ b/plugins/messages/app/mail_receivers/messages_mail_receiver.rb @@ -1,4 +1,4 @@ -require "email_reply_trimmer" +require 'email_reply_trimmer' class MessagesMailReceiver def self.regexp @@ -9,29 +9,25 @@ class MessagesMailReceiver @message = Message.find_by_id(match[:message_id]) @user = User.find_by_id(match[:user_id]) - raise "Message could not be found" if @message.nil? - raise "User could not be found" if @user.nil? + raise 'Message could not be found' if @message.nil? + raise 'User could not be found' if @user.nil? hash = @message.mail_hash_for_user(@user) - raise "Hash does not match expectations" unless hash.casecmp(match[:hash]) == 0 + raise 'Hash does not match expectations' unless hash.casecmp(match[:hash]) == 0 end def received(data) mail = Mail.new data mail_part = get_mail_part(mail) - raise "No valid content could be found" if mail_part.nil? + raise 'No valid content could be found' if mail_part.nil? body = mail_part.body.decoded - unless mail_part.content_type_parameters.nil? - body = body.force_encoding mail_part.content_type_parameters[:charset] - end + body = body.force_encoding mail_part.content_type_parameters[:charset] unless mail_part.content_type_parameters.nil? - if MIME::Type.simplified(mail_part.content_type) == "text/html" - body = Nokogiri::HTML(body).text - end + body = Nokogiri::HTML(body).text if MIME::Type.simplified(mail_part.content_type) == 'text/html' - body.encode!(Encoding::default_internal) + body.encode!(Encoding.default_internal) body = EmailReplyTrimmer.trim(body) raise BlankBodyException if body.empty? @@ -39,16 +35,16 @@ class MessagesMailReceiver group: @message.group, private: @message.private, received_email: data - if @message.reply_to - message.reply_to_message = @message.reply_to_message - else - message.reply_to_message = @message - end - if mail.subject - message.subject = mail.subject.gsub("[#{FoodsoftConfig[:name]}] ", "") - else - message.subject = I18n.t('messages.model.reply_subject', subject: message.reply_to_message.subject) - end + message.reply_to_message = if @message.reply_to + @message.reply_to_message + else + @message + end + message.subject = if mail.subject + mail.subject.gsub("[#{FoodsoftConfig[:name]}] ", '') + else + I18n.t('messages.model.reply_subject', subject: message.reply_to_message.subject) + end message.add_recipients [@message.sender_id] message.save! @@ -64,9 +60,7 @@ class MessagesMailReceiver for part in mail.parts part = get_mail_part(part) content_type = MIME::Type.simplified(part.content_type) - if content_type == "text/plain" || !mail_part && content_type == "text/html" - mail_part = part - end + mail_part = part if content_type == 'text/plain' || (!mail_part && content_type == 'text/html') end mail_part end diff --git a/plugins/messages/app/models/message.rb b/plugins/messages/app/models/message.rb index f6b03c10..0dd1db19 100644 --- a/plugins/messages/app/models/message.rb +++ b/plugins/messages/app/models/message.rb @@ -1,17 +1,17 @@ -require "base32" +require 'base32' class Message < ApplicationRecord - belongs_to :sender, class_name: 'User', foreign_key: 'sender_id' - belongs_to :group, optional: true, class_name: 'Group', foreign_key: 'group_id' + belongs_to :sender, class_name: 'User' + belongs_to :group, optional: true, class_name: 'Group' belongs_to :reply_to_message, optional: true, class_name: 'Message', foreign_key: 'reply_to' has_many :message_recipients, dependent: :destroy has_many :recipients, through: :message_recipients, source: :user attr_accessor :send_method, :recipient_tokens, :order_id - scope :threads, -> { where(:reply_to => nil) } - scope :thread, ->(id) { where("id = ? OR reply_to = ?", id, id) } - scope :readable_for, ->(user) { + scope :threads, -> { where(reply_to: nil) } + scope :thread, ->(id) { where('id = ? OR reply_to = ?', id, id) } + scope :readable_for, lambda { |user| user_id = user.try(&:id) joins(:message_recipients) @@ -20,7 +20,9 @@ class Message < ApplicationRecord } validates_presence_of :message_recipients, :subject, :body - validates_length_of :subject, :in => 1..255 + validates_length_of :subject, in: 1..255 + + has_rich_text :body after_initialize do @recipients_ids ||= [] @@ -33,7 +35,7 @@ class Message < ApplicationRecord def create_message_recipients user_ids = @recipients_ids user_ids += User.undeleted.pluck(:id) if send_method == 'all' - user_ids += Group.find(group_id).users.pluck(:id) unless group_id.blank? + user_ids += Group.find(group_id).users.pluck(:id) if group_id.present? user_ids += Order.find(order_id).users_ordered.pluck(:id) if send_method == 'order' user_ids.uniq.each do |user_id| @@ -47,7 +49,7 @@ class Message < ApplicationRecord end def group_id=(group_id) - group = Group.find(group_id) unless group_id.blank? + group = Group.find(group_id) if group_id.present? if group @send_method = 'workgroup' if group.type == 'Workgroup' @send_method = 'ordergroup' if group.type == 'Ordergroup' @@ -96,29 +98,29 @@ class Message < ApplicationRecord def mail_hash_for_user(user) digest = Digest::SHA1.new - digest.update self.id.to_s - digest.update ":" + digest.update id.to_s + digest.update ':' digest.update salt - digest.update ":" + digest.update ':' digest.update user.id.to_s Base32.encode digest.digest end # Returns true if this message is a system message, i.e. was sent automatically by Foodsoft itself. def system_message? - self.sender_id.nil? + sender_id.nil? end def sender_name - system_message? ? I18n.t('layouts.foodsoft') : sender.display rescue "?" + system_message? ? I18n.t('layouts.foodsoft') : sender.display + rescue StandardError + '?' end - def recipients_ids - @recipients_ids - end + attr_reader :recipients_ids def last_reply - Message.where(reply_to: self.id).order(:created_at).last + Message.where(reply_to: id).order(:created_at).last end def is_readable_for?(user) @@ -135,6 +137,6 @@ class Message < ApplicationRecord private def create_salt - self.salt = [Array.new(6) { rand(256).chr }.join].pack("m").chomp + self.salt = [Array.new(6) { rand(256).chr }.join].pack('m').chomp end end diff --git a/plugins/messages/app/models/message_recipient.rb b/plugins/messages/app/models/message_recipient.rb index e205ea5b..671b557d 100644 --- a/plugins/messages/app/models/message_recipient.rb +++ b/plugins/messages/app/models/message_recipient.rb @@ -2,5 +2,5 @@ class MessageRecipient < ActiveRecord::Base belongs_to :message belongs_to :user - enum email_state: [:pending, :sent, :skipped] + enum email_state: %i[pending sent skipped] end diff --git a/plugins/messages/app/models/messagegroup.rb b/plugins/messages/app/models/messagegroup.rb index 7c7f6c03..93666dd5 100644 --- a/plugins/messages/app/models/messagegroup.rb +++ b/plugins/messages/app/models/messagegroup.rb @@ -1,5 +1,3 @@ class Messagegroup < Group validates_uniqueness_of :name - - protected end diff --git a/plugins/messages/app/views/messages/new.haml b/plugins/messages/app/views/messages/new.haml index 57d6b452..d288cd72 100644 --- a/plugins/messages/app/views/messages/new.haml +++ b/plugins/messages/app/views/messages/new.haml @@ -110,7 +110,7 @@ = f.input :recipient_tokens, :input_html => { 'data-pre' => User.where(id: @message.recipients_ids).map(&:token_attributes).to_json } = f.input :private, inline_label: t('.hint_private') = f.input :subject, input_html: {class: 'input-xxlarge'} - = f.input :body, input_html: {class: 'input-xxlarge', rows: 13} + = f.rich_text_area :body, input_html: {class: 'input-xxlarge', rows: 13} .form-actions = f.submit class: 'btn btn-primary' = link_to t('ui.or_cancel'), :back diff --git a/plugins/messages/app/views/messages/show.haml b/plugins/messages/app/views/messages/show.haml index 36e7b570..8b3f7c1c 100644 --- a/plugins/messages/app/views/messages/show.haml +++ b/plugins/messages/app/views/messages/show.haml @@ -33,7 +33,7 @@ - if @message.can_toggle_private?(current_user) = link_to t('.change_visibility'), toggle_private_message_path(@message), method: :post, class: 'btn btn-mini' %hr/ - %p= simple_format(h(@message.body)) + .trix-content= @message.body %hr/ %p = link_to t('.reply'), new_message_path(:message => {:reply_to => @message.id}), class: 'btn' diff --git a/plugins/messages/app/views/messages_mailer/foodsoft_message.html.haml b/plugins/messages/app/views/messages_mailer/foodsoft_message.html.haml new file mode 100644 index 00000000..7ca572f3 --- /dev/null +++ b/plugins/messages/app/views/messages_mailer/foodsoft_message.html.haml @@ -0,0 +1,11 @@ += raw @message.body +%hr +%ul + - if @message.group + %li= t '.footer_group', group: @message.group.name + %li + %a{href: new_message_url('message[reply_to]' => @message.id)}= t '.reply' + %li + %a{href: message_url(@message)}= t '.see_message_online' + %li + %a{href: my_profile_url}= t '.messaging_options' diff --git a/plugins/messages/config/locales/de.yml b/plugins/messages/config/locales/de.yml index f1615163..eb8cff21 100644 --- a/plugins/messages/config/locales/de.yml +++ b/plugins/messages/config/locales/de.yml @@ -138,6 +138,9 @@ de: Antworten: %{reply_url} Nachricht online einsehen: %{msg_url} Nachrichten-Einstellungen: %{profile_url} + reply: Antworten + see_message_online: Nachricht online einsehen + messaging_options: Nachrichten-Einstellungen footer_group: | Gesendet an Gruppe: %{group} navigation: diff --git a/plugins/messages/config/locales/en.yml b/plugins/messages/config/locales/en.yml index ede3f88c..ccd8bb6c 100644 --- a/plugins/messages/config/locales/en.yml +++ b/plugins/messages/config/locales/en.yml @@ -140,6 +140,9 @@ en: Reply: %{reply_url} See message online: %{msg_url} Messaging options: %{profile_url} + reply: Reply + see_message_online: See message online + messaging_options: Messaging options footer_group: | Sent to group: %{group} navigation: diff --git a/plugins/messages/config/locales/fr.yml b/plugins/messages/config/locales/fr.yml index 54584b48..67d452c5 100644 --- a/plugins/messages/config/locales/fr.yml +++ b/plugins/messages/config/locales/fr.yml @@ -67,6 +67,9 @@ fr: Répondre: %{reply_url} Afficher ce message dans ton navigateur: %{msg_url} Préférences des messages: %{profile_url} + reply: Répondre + see_message_online: Afficher ce message dans ton navigateur + messaging_options: Préférences des messages simple_form: labels: settings: diff --git a/plugins/messages/config/locales/nl.yml b/plugins/messages/config/locales/nl.yml index d3960a23..56738c0b 100644 --- a/plugins/messages/config/locales/nl.yml +++ b/plugins/messages/config/locales/nl.yml @@ -140,6 +140,9 @@ nl: Antwoorden: %{reply_url} Bericht online lezen: %{msg_url} Berichtinstellingen: %{profile_url} + reply: Antwoorden + see_message_online: Bericht online lezen + messaging_options: Berichtinstellingen footer_group: | Verzenden aan groep: %{group} navigation: diff --git a/plugins/messages/config/locales/tr.yml b/plugins/messages/config/locales/tr.yml new file mode 100644 index 00000000..002d1c9b --- /dev/null +++ b/plugins/messages/config/locales/tr.yml @@ -0,0 +1,156 @@ +tr: + activerecord: + attributes: + message: + body: Mesaj İçeriği + messagegroup_id: Mesaj Grubu + order_id: Sipariş + ordergroup_id: Sipariş Grubu + private: Özel + recipient_tokens: (Ek) alıcılar + send_method: + all: Tüm üyelere gönder + recipients: Belirli üyelere gönder + order: Bir siparişe katılan üyelere gönder + ordergroup: Bir sipariş grubunun üyelerine gönder + messagegroup: Bir mesaj grubunun üyelerine gönder + workgroup: Bir iş grubunun üyelerine gönder + send_to_all: Tüm üyelere gönder + subject: Konu + workgroup_id: İş Grubu + messagegroup: + description: Açıklama + name: Ad + user_tokens: Üyeler + models: + message: Mesaj + messagegroup: Mesaj grubu + admin: + ordergroups: + show: + send_message: Mesaj gönder + users: + show: + send_message: Mesaj gönder + config: + hints: + mailing_list: Tüm üyelere için mesajlaşma sistemi yerine kullanılabilecek posta listesi e-posta adresi. + mailing_list_subscribe: Üyelerin abone olmak için bir e-posta gönderebileceği e-posta adresi. + use_messages: Üyelerin Foodsoft içinde birbirleriyle iletişim kurmasına izin ver. + keys: + use_messages: Mesajlar + mailing_list: Posta Listesi + mailing_list_subscribe: Posta Listesi Aboneliği + helpers: + messages: + write_message: Mesaj yaz + submit: + message: + create: Mesaj gönder + home: + index: + messages: + title: En yeni mesajlar + view_all: + text: '%{messages} veya %{threads} göster' + messages: tüm mesajlar + threads: konular + start_nav: + write_message: Mesaj yaz + messagegroups: + index: + body: 'Bir mesaj grubu, bir posta listesi gibi: o gruba üye olabilir (veya çıkabilir) ve o gruba gönderilen güncellemeleri alabilirsiniz.' + title: Mesaj grupları + join: + error: 'Mesaj grubuna katılamadı: %{error}' + notice: Mesaj grubuna katıldınız + leave: + error: 'Mesaj grubu terk edilemedi: %{error}' + notice: Mesaj grubundan ayrıldınız + messagegroup: + join: Mesaj grubuna katıl + leave: Mesaj grubundan ayrıl + messages: + actionbar: + message_threads: Konu olarak göster + messagegroups: Gruplara abone ol + messages: Liste olarak göster + new: Yeni mesaj + create: + notice: Mesaj kaydedildi ve gönderilecek. + index: + title: Mesajlar + messages: + reply: Yanıtla + model: + reply_header: ! '%{user} %{when} tarihinde yazdı:' + reply_indent: ! '> %{line}' + reply_subject: ! 'Yanıt: %{subject}' + new: + error_private: Üzgünüz, bu mesaj özel. + hint_private: Mesaj Foodsoft posta kutusunda gösterilmez. + list: + desc: ! 'Lütfen tüm mesajları şu mailing-liste gönderin: %{list}' + mail: örneğin %{email} adresine bir e-posta ile. + subscribe: 'E-posta listesi hakkında daha fazla bilgi edinebilirsiniz: %{link}.' + subscribe_msg: Önce e-posta listesine kaydolmanız gerekebilir. + wiki: Wiki (page Posta-listesi) + message: mesaj + no_user_found: Kullanıcı bulunamadı. + order_item: "%{supplier_name} (Pickup: %{pickup})" + reply_to: Bu mesaj, başka bir %{link} yanıtıdır. + search: Ara ... + search_user: Kullanıcı ara + title: Yeni mesaj + show: + all_messages: Tüm mesajlar + change_visibility: 'Değiştir' + from: ! 'Kimden:' + group: 'Grup:' + reply: Yanıtla + reply_to: 'Yanıtla:' + sent_on: ! 'Gönderildi:' + subject: ! 'Konu:' + title: Mesajı Göster + to: 'Kime:' + visibility: 'Görünürlük:' + visibility_private: 'Özel' + visibility_public: 'Genel' + thread: + all_message_threads: Tüm mesaj konuları + reply: Yanıtla + toggle_private: + not_allowed: Mesajın görünürlüğünü değiştiremezsiniz. + message_threads: + groupmessage_threads: + show_message_threads: tümünü göster + index: + general: Genel + title: Mesaj Konuları + message_threads: + last_reply_at: Son yanıt tarihi + last_reply_by: Son yanıtlayan + started_at: Başlangıç tarihi + started_by: Başlatan + show: + general: Genel + messages_mailer: + foodsoft_message: + footer: | + Yanıt: %{reply_url} + Mesajı çevrimiçi görüntüle: %{msg_url} + Mesajlaşma seçenekleri:: %{profile_url} + footer_group: | + Gruba gönderildi: %{group} + navigation: + admin: + messagegroups: Mesaj grupları + messages: Mesajlar + shared: + user_form_fields: + messagegroups: Mesaj gruplarına katıl veya ayrıl + simple_form: + labels: + settings: + messages: + send_as_email: Mesajları e-posta olarak al diff --git a/plugins/messages/config/routes.rb b/plugins/messages/config/routes.rb index d66eebdd..6d276428 100644 --- a/plugins/messages/config/routes.rb +++ b/plugins/messages/config/routes.rb @@ -1,13 +1,13 @@ Rails.application.routes.draw do scope '/:foodcoop' do - resources :messages, :only => [:index, :show, :new, :create] do + resources :messages, only: %i[index show new create] do member do get :thread post :toggle_private end end - resources :message_threads, :only => [:index, :show] + resources :message_threads, only: %i[index show] resources :messagegroups, only: [:index] do member do diff --git a/plugins/messages/db/migrate/20160226000000_add_email_to_message.rb b/plugins/messages/db/migrate/20160226000000_add_email_to_message.rb index 411600c7..034b023b 100644 --- a/plugins/messages/db/migrate/20160226000000_add_email_to_message.rb +++ b/plugins/messages/db/migrate/20160226000000_add_email_to_message.rb @@ -1,6 +1,6 @@ class AddEmailToMessage < ActiveRecord::Migration[4.2] def change add_column :messages, :salt, :string - add_column :messages, :received_email, :binary, :limit => 1.megabyte + add_column :messages, :received_email, :binary, limit: 1.megabyte end end diff --git a/plugins/messages/foodsoft_messages.gemspec b/plugins/messages/foodsoft_messages.gemspec index 0dfc7163..e7967191 100644 --- a/plugins/messages/foodsoft_messages.gemspec +++ b/plugins/messages/foodsoft_messages.gemspec @@ -1,25 +1,26 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_messages/version" +require 'foodsoft_messages/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_messages" + s.name = 'foodsoft_messages' s.version = FoodsoftMessages::VERSION - s.authors = ["robwa"] - s.email = ["foodsoft-messages@ini.tiative.net"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Messaging plugin for foodsoft." - s.description = "Adds the ability to exchange messages to foodsoft." + s.authors = ['robwa'] + s.email = ['foodsoft-messages@ini.tiative.net'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Messaging plugin for foodsoft.' + s.description = 'Adds the ability to exchange messages to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "base32" - s.add_dependency "deface", "~> 1.0" - s.add_dependency "email_reply_trimmer" - s.add_dependency "mail" + s.add_dependency 'rails' + s.add_dependency 'base32' + s.add_dependency 'deface', '~> 1.0' + s.add_dependency 'email_reply_trimmer' + s.add_dependency 'mail' - s.add_development_dependency "sqlite3" + s.add_development_dependency 'sqlite3' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/messages/lib/foodsoft_messages.rb b/plugins/messages/lib/foodsoft_messages.rb index b457c8f1..9fc71928 100644 --- a/plugins/messages/lib/foodsoft_messages.rb +++ b/plugins/messages/lib/foodsoft_messages.rb @@ -1,7 +1,7 @@ -require "foodsoft_messages/engine" -require "foodsoft_messages/mail_receiver" -require "foodsoft_messages/user_link" -require "deface" +require 'foodsoft_messages/engine' +require 'foodsoft_messages/mail_receiver' +require 'foodsoft_messages/user_link' +require 'deface' module FoodsoftMessages # Return whether messages are used or not. diff --git a/plugins/messages/lib/foodsoft_messages/engine.rb b/plugins/messages/lib/foodsoft_messages/engine.rb index 0f67abb7..f054ada6 100644 --- a/plugins/messages/lib/foodsoft_messages/engine.rb +++ b/plugins/messages/lib/foodsoft_messages/engine.rb @@ -12,15 +12,16 @@ module FoodsoftMessages sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) end end - unless primary[:admin].nil? - sub_nav = primary[:admin].sub_navigation - sub_nav.items << - SimpleNavigation::Item.new(primary, :messagegroups, I18n.t('navigation.admin.messagegroups'), context.admin_messagegroups_path) - # move to right before config item - if i = sub_nav.items.index(sub_nav[:config]) - sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) - end - end + return if primary[:admin].nil? + + sub_nav = primary[:admin].sub_navigation + sub_nav.items << + SimpleNavigation::Item.new(primary, :messagegroups, I18n.t('navigation.admin.messagegroups'), + context.admin_messagegroups_path) + # move to right before config item + return unless i = sub_nav.items.index(sub_nav[:config]) + + sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) end def default_foodsoft_config(cfg) diff --git a/plugins/messages/lib/foodsoft_messages/user_link.rb b/plugins/messages/lib/foodsoft_messages/user_link.rb index bfab42b6..6fcf99c4 100644 --- a/plugins/messages/lib/foodsoft_messages/user_link.rb +++ b/plugins/messages/lib/foodsoft_messages/user_link.rb @@ -8,7 +8,7 @@ module FoodsoftMessages show_user user else link_to show_user(user), new_message_path('message[mail_to]' => user.id), - :title => I18n.t('helpers.messages.write_message') + title: I18n.t('helpers.messages.write_message') end end end @@ -18,5 +18,5 @@ end # modify existing helper ActiveSupport.on_load(:after_initialize) do - ApplicationHelper.send :include, FoodsoftMessages::UserLink + ApplicationHelper.include FoodsoftMessages::UserLink end diff --git a/plugins/messages/lib/foodsoft_messages/version.rb b/plugins/messages/lib/foodsoft_messages/version.rb index 2da75575..6209100d 100644 --- a/plugins/messages/lib/foodsoft_messages/version.rb +++ b/plugins/messages/lib/foodsoft_messages/version.rb @@ -1,3 +1,3 @@ module FoodsoftMessages - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/polls/Rakefile b/plugins/polls/Rakefile index 2834c5f3..861a530a 100755 --- a/plugins/polls/Rakefile +++ b/plugins/polls/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/polls/app/controllers/polls_controller.rb b/plugins/polls/app/controllers/polls_controller.rb index b0c1a9eb..110c4d3f 100644 --- a/plugins/polls/app/controllers/polls_controller.rb +++ b/plugins/polls/app/controllers/polls_controller.rb @@ -27,9 +27,9 @@ class PollsController < ApplicationController def edit @poll = Poll.find(params[:id]) - if user_has_no_right - redirect_to polls_path, alert: t('.no_right') - end + return unless user_has_no_right + + redirect_to polls_path, alert: t('.no_right') end def update @@ -53,8 +53,8 @@ class PollsController < ApplicationController @poll.destroy redirect_to polls_path, notice: t('.notice') end - rescue => error - redirect_to polls_path, alert: t('.error', error: error.message) + rescue StandardError => e + redirect_to polls_path, alert: t('.error', error: e.message) end def vote @@ -73,25 +73,25 @@ class PollsController < ApplicationController @poll_vote = @poll.poll_votes.where(attributes).first_or_initialize - if request.post? - @poll_vote.update!(note: params[:note], user: current_user) + return unless request.post? - if @poll.single_select? - choices = {} - choice = params[:choice] - choices[choice] = '1' if choice - else - choices = params[:choices].try(:to_h) || {} - end + @poll_vote.update!(note: params[:note], user: current_user) - @poll_vote.poll_choices = choices.map do |choice, value| - poll_choice = @poll_vote.poll_choices.where(choice: choice).first_or_initialize - poll_choice.update!(value: value) - poll_choice - end - - redirect_to @poll + if @poll.single_select? + choices = {} + choice = params[:choice] + choices[choice] = '1' if choice + else + choices = params[:choices].try(:to_h) || {} end + + @poll_vote.poll_choices = choices.map do |choice, value| + poll_choice = @poll_vote.poll_choices.where(choice: choice).first_or_initialize + poll_choice.update!(value: value) + poll_choice + end + + redirect_to @poll end private diff --git a/plugins/polls/config/locales/tr.yml b/plugins/polls/config/locales/tr.yml new file mode 100644 index 00000000..ed2ea4c0 --- /dev/null +++ b/plugins/polls/config/locales/tr.yml @@ -0,0 +1,67 @@ +tr: +activerecord: + attributes: + poll: + choices: Seçenekler + created_at: Oluşturulma tarihi + created_by: Oluşturan + description: Açıklama + ends: Bitiş tarihi + name: Adı + max_points: Maksimum puan + min_points: Minimum puan + multi_select_count: Maksimum seçim sayısı + one_vote_per_ordergroup: Sadece bir oylama her sipariş grubu için + starts: Başlangıç tarihi + voting_method: Oylama yöntemi + voting_methods: + event: Etkinlik + single_select: Tek seçim + multi_select: Birden fazla seçim + points: Puanlar + resistance_points: Direniş puanları + poll_vote: + name: Adı + note: Not + updated_at: Son güncelleme + models: + poll: Oylama +config: + hints: + use_polls: Basit anketleri etkinleştirin. + keys: + use_polls: Anketleri etkinleştirin +navigation: + polls: Anketler +polls: + choice: + remove: Sil + create: + error: 'Anket oluşturulamadı: %{error}' + notice: Anket oluşturuldu + edit: + title: Anketi düzenle + form: + already_voted: Oylama yapıldığından seçenekler değiştirilemez. + new_choice: Yeni seçenek + required_ordergroup_custom_field: 'Sipariş grubunun ''%{label}'' alanı boş olamaz.' + required_user_custom_field: 'Kullanıcının ''%{label}'' alanı boş olamaz.' + destroy: + error: 'Anket silinemedi: %{error}' + no_right: Bu anketi silme izniniz yok + notice: Anket silindi + index: + new_poll: Yeni anket + title: Anketler + new: + title: Yeni anket + polls: + vote: Oyla + show: + vote: Oyla + update: + error: 'Anket güncellenemedi: %{error}' + notice: Anket güncellendi + vote: + submit: Oyla + no_right: Bu ankete katılamazsınız diff --git a/plugins/polls/db/migrate/20181110000000_create_polls.rb b/plugins/polls/db/migrate/20181110000000_create_polls.rb index 990e75f0..120b7eef 100644 --- a/plugins/polls/db/migrate/20181110000000_create_polls.rb +++ b/plugins/polls/db/migrate/20181110000000_create_polls.rb @@ -24,14 +24,14 @@ class CreatePolls < ActiveRecord::Migration[4.2] t.references :ordergroup t.text :note t.timestamps - t.index [:poll_id, :user_id, :ordergroup_id], unique: true + t.index %i[poll_id user_id ordergroup_id], unique: true end create_table :poll_choices do |t| t.references :poll_vote, null: false t.integer :choice, null: false t.integer :value, null: false - t.index [:poll_vote_id, :choice], unique: true + t.index %i[poll_vote_id choice], unique: true end end end diff --git a/plugins/polls/db/migrate/20181120000000_increase_choices_size.rb b/plugins/polls/db/migrate/20181120000000_increase_choices_size.rb index d809e3ea..621863dd 100644 --- a/plugins/polls/db/migrate/20181120000000_increase_choices_size.rb +++ b/plugins/polls/db/migrate/20181120000000_increase_choices_size.rb @@ -1,5 +1,5 @@ class IncreaseChoicesSize < ActiveRecord::Migration[4.2] def up - change_column :polls, :choices, :text, limit: 65535 + change_column :polls, :choices, :text, limit: 65_535 end end diff --git a/plugins/polls/foodsoft_polls.gemspec b/plugins/polls/foodsoft_polls.gemspec index 63e7db29..607a1276 100644 --- a/plugins/polls/foodsoft_polls.gemspec +++ b/plugins/polls/foodsoft_polls.gemspec @@ -1,20 +1,21 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_polls/version" +require 'foodsoft_polls/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_polls" + s.name = 'foodsoft_polls' s.version = FoodsoftPolls::VERSION - s.authors = ["paroga"] - s.email = ["paroga@paroga.com"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Polls plugin for foodsoft." - s.description = "Adds possibility to do polls with foodsoft." + s.authors = ['paroga'] + s.email = ['paroga@paroga.com'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Polls plugin for foodsoft.' + s.description = 'Adds possibility to do polls with foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/polls/lib/foodsoft_polls/engine.rb b/plugins/polls/lib/foodsoft_polls/engine.rb index a76399f0..e4812345 100644 --- a/plugins/polls/lib/foodsoft_polls/engine.rb +++ b/plugins/polls/lib/foodsoft_polls/engine.rb @@ -8,9 +8,9 @@ module FoodsoftPolls sub_nav.items << SimpleNavigation::Item.new(primary, :polls, I18n.t('navigation.polls'), context.polls_path) # move to right before tasks item - if i = sub_nav.items.index(sub_nav[:tasks]) - sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) - end + return unless i = sub_nav.items.index(sub_nav[:tasks]) + + sub_nav.items.insert(i, sub_nav.items.delete_at(-1)) end end end diff --git a/plugins/polls/lib/foodsoft_polls/version.rb b/plugins/polls/lib/foodsoft_polls/version.rb index 5f3fb96d..84369283 100644 --- a/plugins/polls/lib/foodsoft_polls/version.rb +++ b/plugins/polls/lib/foodsoft_polls/version.rb @@ -1,3 +1,3 @@ module FoodsoftPolls - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/printer/Rakefile b/plugins/printer/Rakefile index 1c9d9839..fbf50e1d 100755 --- a/plugins/printer/Rakefile +++ b/plugins/printer/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/printer/app/controllers/printer_controller.rb b/plugins/printer/app/controllers/printer_controller.rb index 178787da..78f13377 100644 --- a/plugins/printer/app/controllers/printer_controller.rb +++ b/plugins/printer/app/controllers/printer_controller.rb @@ -37,9 +37,7 @@ class PrinterController < ApplicationController job = PrinterJob.unfinished.find_by_id(json[:id]) return unless job - if json[:state] - job.add_update! json[:state], json[:message] - end + job.add_update! json[:state], json[:message] if json[:state] job.finish! if json[:finish] end diff --git a/plugins/printer/app/controllers/printer_jobs_controller.rb b/plugins/printer/app/controllers/printer_jobs_controller.rb index 37c864e9..4ba13803 100644 --- a/plugins/printer/app/controllers/printer_jobs_controller.rb +++ b/plugins/printer/app/controllers/printer_jobs_controller.rb @@ -15,7 +15,7 @@ class PrinterJobsController < ApplicationController state = order.open? ? 'queued' : 'ready' count = 0 PrinterJob.transaction do - %w(articles fax groups matrix).each do |document| + %w[articles fax groups matrix].each do |document| next unless FoodsoftConfig["printer_print_order_#{document}"] job = PrinterJob.create! order: order, document: document, created_by: current_user @@ -47,7 +47,7 @@ class PrinterJobsController < ApplicationController job = PrinterJob.find(params[:id]) job.finish! current_user redirect_to printer_jobs_path, notice: t('.notice') - rescue => error - redirect_to printer_jobs_path, t('errors.general_msg', msg: error.message) + rescue StandardError => e + redirect_to printer_jobs_path, t('errors.general_msg', msg: e.message) end end diff --git a/plugins/printer/config/locales/tr.yml b/plugins/printer/config/locales/tr.yml new file mode 100644 index 00000000..93346360 --- /dev/null +++ b/plugins/printer/config/locales/tr.yml @@ -0,0 +1,32 @@ +tr: + config: + keys: + printer_print_order_articles: Sipariş makbuzu PDF'lerini yazdır + printer_print_order_fax: Fax PDF'lerini yazdır + printer_print_order_groups: Grup PDF'lerini yazdır + printer_print_order_matrix: Matris PDF'lerini yazdır + printer_token: Gizli token + use_printer: Yazıcı kullan + helpers: + submit: + printer_job: + create: Yazıcı görevi oluştur + navigation: + orders: + printer_jobs: Yazıcı görevleri + orders: + show: + confirm_create_printer_job: Bu sipariş için bir yazıcı görevi zaten oluşturuldu. Yeni bir tane oluşturmak istiyor musunuz? + printer_jobs: + create: + notice: '%{count} yazıcı görevi oluşturuldu.' + destroy: + notice: Yazıcı görevi silindi. + index: + finished: Tamamlandı + pending: Beklemede + queued: Siparişin kapatılması bekleniyor + requeued: Yeniden sıraya konuldu + title: Yazıcı görevleri + show: + title: Yazıcı görevi %{id} diff --git a/plugins/printer/config/routes.rb b/plugins/printer/config/routes.rb index c81fc786..298ddfea 100644 --- a/plugins/printer/config/routes.rb +++ b/plugins/printer/config/routes.rb @@ -4,7 +4,7 @@ Rails.application.routes.draw do get :socket, on: :collection end - resources :printer_jobs, only: [:index, :create, :show, :destroy] do + resources :printer_jobs, only: %i[index create show destroy] do post :requeue, on: :member get :document, on: :member end diff --git a/plugins/printer/db/migrate/20181201000000_create_printer_jobs.rb b/plugins/printer/db/migrate/20181201000000_create_printer_jobs.rb index ee7665e4..36d175c5 100644 --- a/plugins/printer/db/migrate/20181201000000_create_printer_jobs.rb +++ b/plugins/printer/db/migrate/20181201000000_create_printer_jobs.rb @@ -15,6 +15,6 @@ class CreatePrinterJobs < ActiveRecord::Migration[4.2] t.text :message end - add_index :printer_job_updates, [:printer_job_id, :created_at] + add_index :printer_job_updates, %i[printer_job_id created_at] end end diff --git a/plugins/printer/foodsoft_printer.gemspec b/plugins/printer/foodsoft_printer.gemspec index d0eea89a..a6e54455 100644 --- a/plugins/printer/foodsoft_printer.gemspec +++ b/plugins/printer/foodsoft_printer.gemspec @@ -1,21 +1,22 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_printer/version" +require 'foodsoft_printer/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_printer" + s.name = 'foodsoft_printer' s.version = FoodsoftPrinter::VERSION - s.authors = ["paroga"] - s.email = ["paroga@paroga.com"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Printer plugin for foodsoft." - s.description = "Add a printer queue to foodsoft." + s.authors = ['paroga'] + s.email = ['paroga@paroga.com'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Printer plugin for foodsoft.' + s.description = 'Add a printer queue to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" - s.add_dependency "deface", "~> 1.0" - s.add_dependency "tubesock" + s.add_dependency 'rails' + s.add_dependency 'deface', '~> 1.0' + s.add_dependency 'tubesock' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/printer/lib/foodsoft_printer/engine.rb b/plugins/printer/lib/foodsoft_printer/engine.rb index 22144e30..8f1f00cc 100644 --- a/plugins/printer/lib/foodsoft_printer/engine.rb +++ b/plugins/printer/lib/foodsoft_printer/engine.rb @@ -3,18 +3,19 @@ module FoodsoftPrinter def navigation(primary, context) return unless FoodsoftPrinter.enabled? - unless primary[:orders].nil? - sub_nav = primary[:orders].sub_navigation - sub_nav.items << - SimpleNavigation::Item.new(primary, :printer_jobs, I18n.t('navigation.orders.printer_jobs'), context.printer_jobs_path) - end + return if primary[:orders].nil? + + sub_nav = primary[:orders].sub_navigation + sub_nav.items << + SimpleNavigation::Item.new(primary, :printer_jobs, I18n.t('navigation.orders.printer_jobs'), + context.printer_jobs_path) end def default_foodsoft_config(cfg) cfg[:use_printer] = false end - initializer 'foodsoft_printer.order_printer_jobs' do |app| + initializer 'foodsoft_printer.order_printer_jobs' do |_app| if Rails.configuration.cache_classes OrderPrinterJobs.install else diff --git a/plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb b/plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb index 7501a69e..4c7eeeaa 100644 --- a/plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb +++ b/plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb @@ -4,14 +4,14 @@ module FoodsoftPrinter base.class_eval do has_many :printer_jobs, dependent: :destroy - alias foodsoft_printer_orig_finish! finish! + alias_method :foodsoft_printer_orig_finish!, :finish! def finish!(user) foodsoft_printer_orig_finish!(user) - unless finished? - printer_jobs.unfinished.each do |job| - job.add_update! 'ready' - end + return if finished? + + printer_jobs.unfinished.each do |job| + job.add_update! 'ready' end end end diff --git a/plugins/printer/lib/foodsoft_printer/version.rb b/plugins/printer/lib/foodsoft_printer/version.rb index 17bd39cb..e9d2ad84 100644 --- a/plugins/printer/lib/foodsoft_printer/version.rb +++ b/plugins/printer/lib/foodsoft_printer/version.rb @@ -1,3 +1,3 @@ module FoodsoftPrinter - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/uservoice/foodsoft_uservoice.gemspec b/plugins/uservoice/foodsoft_uservoice.gemspec index f33760fb..4defe395 100644 --- a/plugins/uservoice/foodsoft_uservoice.gemspec +++ b/plugins/uservoice/foodsoft_uservoice.gemspec @@ -1,20 +1,21 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_uservoice/version" +require 'foodsoft_uservoice/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_uservoice" + s.name = 'foodsoft_uservoice' s.version = FoodsoftUservoice::VERSION - s.authors = ["wvengen"] - s.email = ["dev-foodsoft@willem.engen.nl"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Uservoice plugin for foodsoft." - s.description = "Adds a uservoice feedback button to foodsoft." + s.authors = ['wvengen'] + s.email = ['dev-foodsoft@willem.engen.nl'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Uservoice plugin for foodsoft.' + s.description = 'Adds a uservoice feedback button to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['README.md'] - s.add_dependency "rails" - s.add_dependency "content_for_in_controllers" + s.add_dependency 'rails' + s.add_dependency 'content_for_in_controllers' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/uservoice/lib/foodsoft_uservoice.rb b/plugins/uservoice/lib/foodsoft_uservoice.rb index b4718445..2d5b764b 100644 --- a/plugins/uservoice/lib/foodsoft_uservoice.rb +++ b/plugins/uservoice/lib/foodsoft_uservoice.rb @@ -1,5 +1,5 @@ -require "content_for_in_controllers" -require "foodsoft_uservoice/engine" +require 'content_for_in_controllers' +require 'foodsoft_uservoice/engine' module FoodsoftUservoice # enabled when configured, but can still be disabled by use_uservoice option @@ -19,11 +19,11 @@ module FoodsoftUservoice # include uservoice javascript api_key = FoodsoftConfig[:uservoice]['api_key'] - js_pre = "UserVoice=window.UserVoice||[];" + js_pre = 'UserVoice=window.UserVoice||[];' js_load = "var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/#{view_context.j api_key}.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s);" # configuration - sections = FoodsoftConfig[:uservoice].reject { |k, v| k == 'api_key' } + sections = FoodsoftConfig[:uservoice].except('api_key') sections.each_pair do |k, v| if k == 'identify' v['id'] = current_user.try(:id) if v.include?('id') @@ -48,5 +48,5 @@ module FoodsoftUservoice end ActiveSupport.on_load(:after_initialize) do - ApplicationController.send :include, FoodsoftUservoice::LoadUservoice + ApplicationController.include FoodsoftUservoice::LoadUservoice end diff --git a/plugins/uservoice/lib/foodsoft_uservoice/version.rb b/plugins/uservoice/lib/foodsoft_uservoice/version.rb index 8d78e3de..e806ff1d 100644 --- a/plugins/uservoice/lib/foodsoft_uservoice/version.rb +++ b/plugins/uservoice/lib/foodsoft_uservoice/version.rb @@ -1,3 +1,3 @@ module FoodsoftUservoice - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/wiki/Rakefile b/plugins/wiki/Rakefile index 5d2e31db..dd14bed8 100755 --- a/plugins/wiki/Rakefile +++ b/plugins/wiki/Rakefile @@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' Bundler::GemHelper.install_tasks @@ -34,4 +34,4 @@ Rake::TestTask.new(:test) do |t| t.verbose = false end -task :default => :test +task default: :test diff --git a/plugins/wiki/app/controllers/pages_controller.rb b/plugins/wiki/app/controllers/pages_controller.rb index c065abe7..0e28e2d5 100644 --- a/plugins/wiki/app/controllers/pages_controller.rb +++ b/plugins/wiki/app/controllers/pages_controller.rb @@ -1,20 +1,20 @@ class PagesController < ApplicationController before_action -> { require_plugin_enabled FoodsoftWiki } - before_action :catch_special_pages, only: [:show, :new] + before_action :catch_special_pages, only: %i[show new] - skip_before_action :authenticate, :only => :all - before_action :only => :all do - authenticate_or_token(['wiki', 'all']) + skip_before_action :authenticate, only: :all + before_action only: :all do + authenticate_or_token(%w[wiki all]) end before_action do content_for :head, view_context.rss_meta_tag end def index - @page = Page.find_by_permalink "Home" + @page = Page.find_by_permalink 'Home' if @page - render :action => 'show' + render action: 'show' else redirect_to all_pages_path end @@ -34,11 +34,11 @@ class PagesController < ApplicationController end if @page.nil? - redirect_to new_page_path(:title => params[:permalink]) + redirect_to new_page_path(title: params[:permalink]) elsif @page.redirect? page = Page.find_by_id(@page.redirect) unless page.nil? - flash[:notice] = I18n.t('pages.cshow.redirect_notice', :page => @page.title) + flash[:notice] = I18n.t('pages.cshow.redirect_notice', page: @page.title) redirect_to wiki_page_path(page.permalink) end end @@ -46,12 +46,12 @@ class PagesController < ApplicationController def new @page = Page.new - @page.title = params[:title].gsub("_", " ") if params[:title] + @page.title = params[:title].gsub('_', ' ') if params[:title] @page.parent = Page.find_by_permalink(params[:parent]) if params[:parent] respond_to do |format| format.html # new.html.erb - format.xml { render :xml => @page } + format.xml { render xml: @page } end end @@ -60,36 +60,32 @@ class PagesController < ApplicationController end def create - @page = Page.new(params[:page].merge({ :user => current_user })) + @page = Page.new(params[:page].merge({ user: current_user })) if params[:preview] - render :action => 'new' + render action: 'new' + elsif @page.save + flash[:notice] = I18n.t('pages.create.notice') + redirect_to(wiki_page_path(@page.permalink)) else - if @page.save - flash[:notice] = I18n.t('pages.create.notice') - redirect_to(wiki_page_path(@page.permalink)) - else - render :action => "new" - end + render action: 'new' end end def update @page = Page.find(params[:id]) - @page.attributes = params[:page].merge({ :user => current_user }) + @page.attributes = params[:page].merge({ user: current_user }) if params[:preview] @page.attributes = params[:page] - render :action => 'edit' + render action: 'edit' + elsif @page.save + @page.parent_id = parent_id if params[:parent_id].present? \ + && params[:parent_id] != @page_id + flash[:notice] = I18n.t('pages.update.notice') + redirect_to wiki_page_path(@page.permalink) else - if @page.save - @page.parent_id = parent_id if (!params[:parent_id].blank? \ - && params[:parent_id] != @page_id) - flash[:notice] = I18n.t('pages.update.notice') - redirect_to wiki_page_path(@page.permalink) - else - render :action => "edit" - end + render action: 'edit' end rescue ActiveRecord::StaleObjectError flash[:error] = I18n.t('pages.error_stale_object') @@ -100,7 +96,7 @@ class PagesController < ApplicationController @page = Page.find(params[:id]) @page.destroy - flash[:notice] = I18n.t('pages.destroy.notice', :page => @page.title) + flash[:notice] = I18n.t('pages.destroy.notice', page: @page.title) redirect_to wiki_path end @@ -109,23 +105,23 @@ class PagesController < ApplicationController @partial = params[:view] || 'site_map' if params[:name] - @pages = @pages.where("title LIKE ?", "%#{params[:name]}%").limit(20) + @pages = @pages.where('title LIKE ?', "%#{params[:name]}%").limit(20) @partial = 'title_list' end - if params[:sort] - sort = case params[:sort] - when "title" then "title" - when "title_reverse" then "title DESC" - when "last_updated" then "updated_at DESC" - when "last_updated_reverse" then "updated_at" + sort = if params[:sort] + case params[:sort] + when 'title' then 'title' + when 'title_reverse' then 'title DESC' + when 'last_updated' then 'updated_at DESC' + when 'last_updated_reverse' then 'updated_at' end - else - sort = "title" - end + else + 'title' + end @pages = @pages.order(sort) respond_to do |format| format.html - format.rss { render :layout => false } + format.rss { render layout: false } end end @@ -150,15 +146,15 @@ class PagesController < ApplicationController def variables keys = Foodsoft::ExpansionVariables.variables.keys - @variables = Hash[keys.map { |k| [k, Foodsoft::ExpansionVariables.get(k)] }] + @variables = keys.index_with { |k| Foodsoft::ExpansionVariables.get(k) } render 'variables' end private def catch_special_pages - if params[:id] == 'Help:Foodsoft_variables' - variables - end + return unless params[:id] == 'Help:Foodsoft_variables' + + variables end end diff --git a/plugins/wiki/app/helpers/pages_helper.rb b/plugins/wiki/app/helpers/pages_helper.rb index 2c1479f3..869f59d1 100644 --- a/plugins/wiki/app/helpers/pages_helper.rb +++ b/plugins/wiki/app/helpers/pages_helper.rb @@ -2,70 +2,70 @@ module PagesHelper include WikiCloth def rss_meta_tag - tag.link(rel: "alternate", type: "application/rss+xml", title: "RSS", href: all_pages_rss_url).html_safe + tag.link(rel: 'alternate', type: 'application/rss+xml', title: 'RSS', href: all_pages_rss_url).html_safe end def wikified_body(body, title = nil) FoodsoftWiki::WikiParser.new(data: body + "\n", params: { referer: title }).to_html(noedit: true).html_safe - rescue => e + rescue StandardError => e # try the following with line breaks: === one === == two == = three = content_tag :span, class: 'alert alert-error' do - I18n.t '.wikicloth_exception', :msg => e + I18n.t '.wikicloth_exception', msg: e end.html_safe end def link_to_wikipage(page, text = nil) - if text == nil - link_to page.title, wiki_page_path(:permalink => page.permalink) + if text.nil? + link_to page.title, wiki_page_path(permalink: page.permalink) else - link_to text, wiki_page_path(:permalink => page.permalink) + link_to text, wiki_page_path(permalink: page.permalink) end end def link_to_wikipage_by_permalink(permalink, text = nil) - unless permalink.blank? - page = Page.find_by_permalink(permalink) - if page.nil? - if text.nil? - link_to permalink, new_page_path(:title => permalink) - else - link_to text, new_page_path(:title => permalink) - end + return if permalink.blank? + + page = Page.find_by_permalink(permalink) + if page.nil? + if text.nil? + link_to permalink, new_page_path(title: permalink) else - link_to_wikipage(page, text) + link_to text, new_page_path(title: permalink) end + else + link_to_wikipage(page, text) end end def generate_toc(body) toc = String.new - body.gsub(/^([=]{1,6})\s*(.*?)\s*(\1)/) do - number = $1.length - 1 - name = $2 + body.gsub(/^(={1,6})\s*(.*?)\s*(\1)/) do + number = ::Regexp.last_match(1).length - 1 + name = ::Regexp.last_match(2) - toc << "*" * number + " #{name}\n" + toc << (('*' * number) + " #{name}\n") end - unless toc.blank? - FoodsoftWiki::WikiParser.new(data: toc).to_html.gsub(/
  • ([^<>\n]*)/) do - name = $1 - anchor = name.gsub(/\s/, '_').gsub(/[^a-zA-Z_]/, '') - "
  • #{name.truncate(20)}" - end.html_safe - end + return if toc.blank? + + FoodsoftWiki::WikiParser.new(data: toc).to_html.gsub(/
  • ([^<>\n]*)/) do + name = ::Regexp.last_match(1) + anchor = name.gsub(/\s/, '_').gsub(/[^a-zA-Z_]/, '') + "
  • #{name.truncate(20)}" + end.html_safe end def parent_pages_to_select(current_page) - unless current_page.homepage? # Homepage is the page trees root! + if current_page.homepage? + [] + else # Homepage is the page trees root! Page.non_redirected.reject { |p| p == current_page || p.ancestors.include?(current_page) } - else - Array.new end end # return url for all_pages rss feed def all_pages_rss_url(options = {}) - token = TokenVerifier.new(['wiki', 'all']).generate - all_pages_url({ :format => 'rss', :token => token }.merge(options)) + token = TokenVerifier.new(%w[wiki all]).generate + all_pages_url({ format: 'rss', token: token }.merge(options)) end end diff --git a/plugins/wiki/app/models/page.rb b/plugins/wiki/app/models/page.rb index e773afa7..26d2393d 100644 --- a/plugins/wiki/app/models/page.rb +++ b/plugins/wiki/app/models/page.rb @@ -1,61 +1,62 @@ class Page < ApplicationRecord include ActsAsTree - belongs_to :user, :foreign_key => 'updated_by' + belongs_to :user, foreign_key: 'updated_by' acts_as_versioned version_column: :lock_version - self.non_versioned_columns += %w(permalink created_at title) + self.non_versioned_columns += %w[permalink created_at title] - acts_as_tree :order => "title" + acts_as_tree order: 'title' attr_accessor :old_title # Save title to create redirect page when editing title validates_presence_of :title, :body validates_uniqueness_of :permalink, :title - before_validation :set_permalink, :on => :create - before_validation :update_permalink, :on => :update + before_validation :set_permalink, on: :create + before_validation :update_permalink, on: :update after_update :create_redirect - scope :non_redirected, -> { where(:redirect => nil) } - scope :no_parent, -> { where(:parent_id => nil) } + scope :non_redirected, -> { where(redirect: nil) } + scope :no_parent, -> { where(parent_id: nil) } def self.permalink(title) - title.gsub(/[\/\.,;@\s]/, "_").gsub(/[\"\']/, "") + title.gsub(%r{[/.,;@\s]}, '_').gsub(/["']/, '') end def homepage? - permalink == "Home" + permalink == 'Home' end def self.dashboard - where(permalink: "Dashboard").first + where(permalink: 'Dashboard').first end def self.public_front_page - where(permalink: "Public_frontpage").first + where(permalink: 'Public_frontpage').first end def self.welcome_mail - where(permalink: "Welcome_mail").first + where(permalink: 'Welcome_mail').first end def set_permalink - unless title.blank? - self.permalink = Page.count == 0 ? "Home" : Page.permalink(title) - end + return if title.blank? + + self.permalink = Page.count == 0 ? 'Home' : Page.permalink(title) end def diff current = versions.latest - old = versions.where(["page_id = ? and lock_version < ?", current.page_id, current.lock_version]).order('lock_version DESC').first + old = versions.where(['page_id = ? and lock_version < ?', current.page_id, + current.lock_version]).order('lock_version DESC').first if old o = '' Diffy::Diff.new(old.body, current.body).each do |line| case line - when /^\+/ then o += "#{line.chomp}
    " unless line.chomp == "+" - when /^-/ then o += "#{line.chomp}
    " unless line.chomp == "-" + when /^\+/ then o += "#{line.chomp}
    " unless line.chomp == '+' + when /^-/ then o += "#{line.chomp}
    " unless line.chomp == '-' end end o @@ -67,19 +68,19 @@ class Page < ApplicationRecord protected def update_permalink - if changed.include?("title") - set_permalink - self.old_title = changes["title"].first # Save title for creating redirect - end + return unless changed.include?('title') + + set_permalink + self.old_title = changes['title'].first # Save title for creating redirect end def create_redirect - unless old_title.blank? - Page.create :redirect => id, - :title => old_title, - :body => I18n.t('model.page.redirect', :title => title), - :permalink => Page.permalink(old_title), - :updated_by => updated_by - end + return if old_title.blank? + + Page.create redirect: id, + title: old_title, + body: I18n.t('model.page.redirect', title: title), + permalink: Page.permalink(old_title), + updated_by: updated_by end end diff --git a/plugins/wiki/app/views/pages/all.rss.builder b/plugins/wiki/app/views/pages/all.rss.builder index f7194763..7f0b4e10 100644 --- a/plugins/wiki/app/views/pages/all.rss.builder +++ b/plugins/wiki/app/views/pages/all.rss.builder @@ -1,16 +1,16 @@ -xml.instruct! :xml, :version => "1.0" -xml.rss :version => "2.0" do +xml.instruct! :xml, version: '1.0' +xml.rss version: '2.0' do xml.channel do - xml.title FoodsoftConfig[:name] + " Wiki" - xml.description "" + xml.title FoodsoftConfig[:name] + ' Wiki' + xml.description '' xml.link FoodsoftConfig[:homepage] for page in @pages xml.item do xml.title page.title - xml.description page.diff, :type => "html" + xml.description page.diff, type: 'html' xml.author User.find_by_id(page.updated_by).try(:display) - xml.pubDate page.updated_at.to_s(:rfc822) + xml.pubDate page.updated_at.to_fs(:rfc822) xml.link wiki_page_path(page.permalink) xml.guid page.updated_at.to_i end diff --git a/plugins/wiki/config/locales/tr.yml b/plugins/wiki/config/locales/tr.yml new file mode 100644 index 00000000..077c08e1 --- /dev/null +++ b/plugins/wiki/config/locales/tr.yml @@ -0,0 +1,107 @@ +tr: + activerecord: + attributes: + page: + body: İçerik + parent_id: Ãœst sayfa + title: BaÅŸlık + config: + hints: + use_wiki: Düzenlenebilir wiki sayfalarını etkinleÅŸtirin. + keys: + use_wiki: Wiki'yi etkinleÅŸtir + model: + page: + redirect: '[[%{title}]] sayfasına yönlendiriliyor...' + navigation: + wiki: + all_pages: Tüm Sayfalar + home: Ana Sayfa + title: Wiki + pages: + all: + new_page: Yeni sayfa oluÅŸtur + recent_changes: Son deÄŸiÅŸiklikler + search: + action: Ara + placeholder: Sayfa baÅŸlığı .. + site_map: Site Haritası + title: Tüm Wiki sayfaları + title_list: Sayfa listesi + body: + title_toc: İçindekiler + wikicloth_exception: 'Ãœzgünüm, wiki sayfasını yorumlarken bir hata oluÅŸtu: %{msg}. Lütfen düzeltin ve sayfayı tekrar kaydedin.' + create: + notice: Sayfa oluÅŸturuldu. + cshow: + error_noexist: Sayfa mevcut deÄŸil! + redirect_notice: '%{page} sayfasından yönlendirildi ...' + destroy: + notice: Sayfa '%{page}' ve tüm alt sayfaları baÅŸarıyla silindi. + diff: + title: "%{title} - %{old} versiyonundan %{new} versiyonuna deÄŸiÅŸiklikler" + edit: + title: Sayfayı düzenle + error_stale_object: Uyarı, sayfa baÅŸka biri tarafından düzenlendi. Lütfen tekrar deneyin. + form: + help: + bold: kalın + external_link_ex: Dış baÄŸlantı + external_links: Dış + heading: '%{level}. düzey' + headings: BaÅŸlık + image_link_title: Resim baÅŸlığı + image_links: Resimler + italic: italik + link_lists: Liste hakkında daha fazlası + link_table: Tablo biçimlendirmesi + link_templates: Åžablonlar + link_variables: Foodsoft deÄŸiÅŸkenleri + list_item_1: Ä°lk liste maddesi + list_item_2: Ä°kinci liste maddesi + noformat: Biçimlendirme yok + ordered_list: Numaralı liste + section_block: Blok biçimlendirmesi + section_character: Karakter biçimlendirmesi + section_link: BaÄŸlantı biçimlendirmesi + section_more: Daha fazla konu + section_table: Tablo biçimlendirmesi + see_tables: '%{tables_link} göz atın' + tables_link: Tablolar + text: metin + title: Hızlı biçimlendirme yardımı + unordered_list: Madde listesi + wiki_link_ex: Foodsoft Wiki Sayfası + wiki_links: Wiki baÄŸlantıları + preview: Önizleme + last_updated: Son güncelleme + new: + title: Yeni bir wiki sayfası oluÅŸturun + page_list_item: + date_format: ! '%d %B %Y, %H:%M:%S' + show: + date_format: ! '%d-%m-%Y %H:%M' + delete: Sayfayı sil + delete_confirm: Tüm alt sayfaların da silineceÄŸi uyarısını dikkate alarak devam etmek istediÄŸinizden emin misiniz? + diff: Versiyonları karşılaÅŸtır + edit: Sayfayı düzenle + last_updated: Son güncelleme %{when} tarihinde %{user} tarafından yapıldı + subpages: alt sayfalar + title_versions: Versiyonlar + versions: Versiyonlar (%{count}) + title: BaÅŸlık + update: + notice: Sayfa güncellendi + variables: + description: DeÄŸiÅŸkenler bilgiyi baÅŸka bir yerden getirir. DeÄŸiÅŸkeni kullandığınızda, görüntülendiÄŸinde deÄŸeriyle deÄŸiÅŸtirilir. Foodsoft'un adınız, adresiniz, yazılım sürümü ve üye ve tedarikçi sayısı gibi birçok önceden tanımlanmış deÄŸiÅŸkeni vardır. Tüm deÄŸiÅŸkenler için aÅŸağıdaki tabloya bakın. Bunları wiki sayfalarında ve ayak kısımlarında (yapılandırma ekranından) kullanabilirsiniz. + title: Foodsoft deÄŸiÅŸkenleri + value: Güncel deÄŸer + variable: DeÄŸiÅŸken + version: + author: 'Yazar: %{user}' + date_format: ! '%a, %d-%m-%Y, %H:%M' + revert: Bu sürüme geri dön + title: ! '%{title} - sürüm %{version}' + title_version: Sürüm + view_current: Åžu anki sürümü gör + diff --git a/plugins/wiki/config/routes.rb b/plugins/wiki/config/routes.rb index 4ebad572..ad713366 100644 --- a/plugins/wiki/config/routes.rb +++ b/plugins/wiki/config/routes.rb @@ -1,12 +1,12 @@ Rails.application.routes.draw do scope '/:foodcoop' do resources :pages do - get :all, :on => :collection - get :version, :on => :member - get :revert, :on => :member - get :diff, :on => :member + get :all, on: :collection + get :version, on: :member + get :revert, on: :member + get :diff, on: :member end get '/wiki/:permalink' => 'pages#show', :as => 'wiki_page' # , :constraints => {:permalink => /[^\s]+/} - get '/wiki' => 'pages#show', :defaults => { :permalink => 'Home' }, :as => 'wiki' + get '/wiki' => 'pages#show', :defaults => { permalink: 'Home' }, :as => 'wiki' end end diff --git a/plugins/wiki/db/migrate/20090325175756_create_pages.rb b/plugins/wiki/db/migrate/20090325175756_create_pages.rb index 846decf8..cdd00e2b 100644 --- a/plugins/wiki/db/migrate/20090325175756_create_pages.rb +++ b/plugins/wiki/db/migrate/20090325175756_create_pages.rb @@ -4,7 +4,7 @@ class CreatePages < ActiveRecord::Migration[4.2] t.string :title t.text :body t.string :permalink - t.integer :lock_version, :default => 0 + t.integer :lock_version, default: 0 t.integer :updated_by t.integer :redirect t.integer :parent_id diff --git a/plugins/wiki/foodsoft_wiki.gemspec b/plugins/wiki/foodsoft_wiki.gemspec index 371872c5..58be331d 100644 --- a/plugins/wiki/foodsoft_wiki.gemspec +++ b/plugins/wiki/foodsoft_wiki.gemspec @@ -1,26 +1,27 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "foodsoft_wiki/version" +require 'foodsoft_wiki/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "foodsoft_wiki" + s.name = 'foodsoft_wiki' s.version = FoodsoftWiki::VERSION - s.authors = ["wvengen"] - s.email = ["dev-foodsoft@willem.engen.nl"] - s.homepage = "https://github.com/foodcoops/foodsoft" - s.summary = "Wiki plugin for foodsoft." - s.description = "Adds a wiki to foodsoft." + s.authors = ['wvengen'] + s.email = ['dev-foodsoft@willem.engen.nl'] + s.homepage = 'https://github.com/foodcoops/foodsoft' + s.summary = 'Wiki plugin for foodsoft.' + s.description = 'Adds a wiki to foodsoft.' - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*'] + ['Rakefile', 'README.md'] - s.add_dependency "rails" + s.add_dependency 'rails' s.add_dependency 'wikicloth' s.add_dependency 'twitter-text', '~> 1.14' # wikicloth doesn't support version 2 s.add_dependency 'acts_as_versioned' # need git version, make sure that is included in foodsoft's Gemfile - s.add_dependency "deface", "~> 1.0" + s.add_dependency 'deface', '~> 1.0' s.add_dependency 'diffy' s.add_dependency 'content_for_in_controllers' - s.add_development_dependency "sqlite3" + s.add_development_dependency 'sqlite3' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/plugins/wiki/lib/foodsoft_wiki/engine.rb b/plugins/wiki/lib/foodsoft_wiki/engine.rb index 4cc20f6a..ae2ce462 100644 --- a/plugins/wiki/lib/foodsoft_wiki/engine.rb +++ b/plugins/wiki/lib/foodsoft_wiki/engine.rb @@ -8,17 +8,17 @@ module FoodsoftWiki subnav.item :all_pages, I18n.t('navigation.wiki.all_pages'), ctx.all_pages_path, id: nil end # move this last added item to just after the foodcoop menu - if i = primary.items.index(primary[:foodcoop]) - primary.items.insert(i + 1, primary.items.delete_at(-1)) - end + return unless i = primary.items.index(primary[:foodcoop]) + + primary.items.insert(i + 1, primary.items.delete_at(-1)) end def default_foodsoft_config(cfg) cfg[:use_wiki] = true end - initializer "foodsoft_wiki.assets.precompile" do |app| - app.config.assets.precompile += %w(icons/feed-icon-14x14.png) + initializer 'foodsoft_wiki.assets.precompile' do |app| + app.config.assets.precompile += %w[icons/feed-icon-14x14.png] end end end diff --git a/plugins/wiki/lib/foodsoft_wiki/mailer.rb b/plugins/wiki/lib/foodsoft_wiki/mailer.rb index 83a110f1..4b7a892d 100644 --- a/plugins/wiki/lib/foodsoft_wiki/mailer.rb +++ b/plugins/wiki/lib/foodsoft_wiki/mailer.rb @@ -3,10 +3,10 @@ module FoodsoftWiki def self.included(base) # :nodoc: base.class_eval do # modify user presentation link to writing a message for the user - def additonal_welcome_text(user) - if FoodsoftWiki.enabled? && (page = Page.welcome_mail) - page.body - end + def additonal_welcome_text(_user) + return unless FoodsoftWiki.enabled? && (page = Page.welcome_mail) + + page.body end end end @@ -15,5 +15,5 @@ end # modify existing helper ActiveSupport.on_load(:after_initialize) do - Mailer.send :include, FoodsoftWiki::Mailer + Mailer.include FoodsoftWiki::Mailer end diff --git a/plugins/wiki/lib/foodsoft_wiki/version.rb b/plugins/wiki/lib/foodsoft_wiki/version.rb index 2a67a94e..580ee3ed 100644 --- a/plugins/wiki/lib/foodsoft_wiki/version.rb +++ b/plugins/wiki/lib/foodsoft_wiki/version.rb @@ -1,3 +1,3 @@ module FoodsoftWiki - VERSION = "0.0.1" + VERSION = '0.0.1' end diff --git a/plugins/wiki/lib/foodsoft_wiki/wiki_parser.rb b/plugins/wiki/lib/foodsoft_wiki/wiki_parser.rb index 37e58465..6e14d2a8 100644 --- a/plugins/wiki/lib/foodsoft_wiki/wiki_parser.rb +++ b/plugins/wiki/lib/foodsoft_wiki/wiki_parser.rb @@ -10,7 +10,7 @@ module FoodsoftWiki link_attributes_for do |page| permalink = Page.permalink(page) - if Page.exists?(:permalink => permalink) + if Page.exists?(permalink: permalink) { href: url_for(:wiki_page_path, permalink: permalink) } elsif page.include? '#' # If "Foo#Bar" does not exist then consider "Foo" with anchor. @@ -20,8 +20,8 @@ module FoodsoftWiki end end - section_link do |section| - "" + section_link do |_section| + '' end def to_html(render_options = {}) @@ -41,7 +41,7 @@ module FoodsoftWiki return { href: '#' + anchor } if page.empty? permalink = Page.permalink(page) - if Page.exists?(:permalink => permalink) + if Page.exists?(permalink: permalink) { href: url_for(:wiki_page_path, permalink: permalink, anchor: anchor) } else # Do not suggest to use number signs in the title. diff --git a/script/rails b/script/rails index bd79dce5..3c234a25 100755 --- a/script/rails +++ b/script/rails @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. -APP_PATH = File.expand_path('../../config/application', __FILE__) -require File.expand_path('../../config/boot', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) +require File.expand_path('../config/boot', __dir__) require 'rails/commands' diff --git a/spec/api/v1/order_articles_spec.rb b/spec/api/v1/order_articles_spec.rb deleted file mode 100644 index e65867db..00000000 --- a/spec/api/v1/order_articles_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'spec_helper' - -# Most routes are tested in the swagger_spec, this tests (non-ransack) parameters. -describe Api::V1::OrderArticlesController, type: :controller do - include ApiOAuth - let(:api_scopes) { ['orders:read'] } - - let(:json_order_articles) { json_response['order_articles'] } - let(:json_order_article_ids) { json_order_articles.map { |joa| joa["id"] } } - - describe "GET :index" do - context "with param q[ordered]" do - let(:order) { create(:order, article_count: 4) } - let(:order_articles) { order.order_articles } - - before do - order_articles[0].update!(quantity: 0, tolerance: 0, units_to_order: 0) - order_articles[1].update!(quantity: 1, tolerance: 0, units_to_order: 0) - order_articles[2].update!(quantity: 0, tolerance: 1, units_to_order: 0) - order_articles[3].update!(quantity: 0, tolerance: 0, units_to_order: 1) - end - - it "(unset)" do - get :index, params: { foodcoop: 'f' } - expect(json_order_articles.count).to eq 4 - end - - it "all" do - get :index, params: { foodcoop: 'f', q: { ordered: 'all' } } - expect(json_order_article_ids).to match_array order_articles[1..2].map(&:id) - end - - it "supplier" do - get :index, params: { foodcoop: 'f', q: { ordered: 'supplier' } } - expect(json_order_article_ids).to match_array [order_articles[3].id] - end - - it "member" do - get :index, params: { foodcoop: 'f', q: { ordered: 'member' } } - expect(json_order_articles.count).to eq 0 - end - - context "when ordered by user" do - let(:user) { create(:user, :ordergroup) } - let(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - - before do - create(:group_order_article, group_order: go, order_article: order_articles[1], quantity: 1) - create(:group_order_article, group_order: go, order_article: order_articles[2], tolerance: 0) - end - - it "member" do - get :index, params: { foodcoop: 'f', q: { ordered: 'member' } } - expect(json_order_article_ids).to match_array order_articles[1..2].map(&:id) - end - end - end - end -end diff --git a/spec/api/v1/swagger_spec.rb b/spec/api/v1/swagger_spec.rb deleted file mode 100644 index 3da37332..00000000 --- a/spec/api/v1/swagger_spec.rb +++ /dev/null @@ -1,284 +0,0 @@ -require 'spec_helper' -require 'apivore' - -# we want to load a local file in YAML-format instead of a served JSON file -class SwaggerCheckerFile < Apivore::SwaggerChecker - def fetch_swagger! - YAML.load(File.read(swagger_path)) - end -end - -describe 'API v1', type: :apivore, order: :defined do - include ApiHelper - - subject { SwaggerCheckerFile.instance_for Rails.root.join('doc', 'swagger.v1.yml') } - - context 'has valid paths' do - context 'user' do - let(:api_scopes) { ['user:read'] } - # create multiple users to make sure we're getting the authenticated user, not just any - let!(:other_user_1) { create :user } - let!(:user) { create :user } - let!(:other_user_2) { create :user } - - it { is_expected.to validate(:get, '/user', 200, api_auth) } - it { is_expected.to validate(:get, '/user', 401) } - - it_handles_invalid_token_and_scope(:get, '/user') - end - - context 'user/financial_overview' do - let(:api_scopes) { ['finance:user'] } - let!(:user) { create :user, :ordergroup } - - it { is_expected.to validate(:get, '/user/financial_overview', 200, api_auth) } - it { is_expected.to validate(:get, '/user/financial_overview', 401) } - - it_handles_invalid_token_and_scope(:get, '/user/financial_overview') - end - - context 'user/financial_transactions' do - let(:api_scopes) { ['finance:user'] } - let(:other_user) { create :user, :ordergroup } - let!(:other_ft_1) { create :financial_transaction, ordergroup: other_user.ordergroup } - - context 'without ordergroup' do - it { is_expected.to validate(:get, '/user/financial_transactions', 403, api_auth) } - it { is_expected.to validate(:get, '/user/financial_transactions/{id}', 403, api_auth({ 'id' => other_ft_1.id })) } - end - - context 'with ordergroup' do - let(:user) { create :user, :ordergroup } - let!(:ft_1) { create :financial_transaction, ordergroup: user.ordergroup } - let!(:ft_2) { create :financial_transaction, ordergroup: user.ordergroup } - let!(:ft_3) { create :financial_transaction, ordergroup: user.ordergroup } - - let(:create_params) { { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: ft_1.financial_transaction_type.id, note: 'note' } } } } - - it { is_expected.to validate(:get, '/user/financial_transactions', 200, api_auth) } - it { is_expected.to validate(:get, '/user/financial_transactions/{id}', 200, api_auth({ 'id' => ft_2.id })) } - it { is_expected.to validate(:get, '/user/financial_transactions/{id}', 404, api_auth({ 'id' => other_ft_1.id })) } - it { is_expected.to validate(:get, '/user/financial_transactions/{id}', 404, api_auth({ 'id' => FinancialTransaction.last.id + 1 })) } - - context 'without using self service' do - it { is_expected.to validate(:post, '/user/financial_transactions', 403, api_auth(create_params)) } - end - - context 'with using self service' do - before { FoodsoftConfig[:use_self_service] = true } - - it { is_expected.to validate(:post, '/user/financial_transactions', 200, api_auth(create_params)) } - - context 'with invalid financial transaction type' do - let(:create_params) { { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: -1, note: 'note' } } } } - - it { is_expected.to validate(:post, '/user/financial_transactions', 404, api_auth(create_params)) } - end - - context 'without note' do - let(:create_params) { { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: ft_1.financial_transaction_type.id } } } } - - it { is_expected.to validate(:post, '/user/financial_transactions', 422, api_auth(create_params)) } - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - it { is_expected.to validate(:post, '/user/financial_transactions', 403, api_auth(create_params)) } - end - end - - it_handles_invalid_token_and_scope(:get, '/user/financial_transactions') - it_handles_invalid_token_and_scope(:post, '/user/financial_transactions', -> { api_auth(create_params) }) - it_handles_invalid_token_and_scope(:get, '/user/financial_transactions/{id}', -> { api_auth('id' => ft_2.id) }) - end - end - - context 'user/group_order_articles' do - let(:api_scopes) { ['group_orders:user'] } - let(:order) { create(:order, article_count: 2) } - - let(:user_2) { create :user, :ordergroup } - let(:group_order_2) { create(:group_order, order: order, ordergroup: user_2.ordergroup) } - let!(:goa_2) { create :group_order_article, order_article: order.order_articles[0], group_order: group_order_2 } - - before { group_order_2.update_price!; user_2.ordergroup.update_stats! } - - context 'without ordergroup' do - it { is_expected.to validate(:get, '/user/group_order_articles', 403, api_auth) } - it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 403, api_auth({ 'id' => goa_2.id })) } - end - - context 'with ordergroup' do - let(:user) { create :user, :ordergroup } - let(:update_params) { { 'id' => goa.id, '_data' => { group_order_article: { quantity: goa.quantity + 1, tolerance: 0 } } } } - let(:create_params) { { '_data' => { group_order_article: { order_article_id: order.order_articles[1].id, quantity: 1 } } } } - let(:group_order) { create(:group_order, order: order, ordergroup: user.ordergroup) } - let!(:goa) { create :group_order_article, order_article: order.order_articles[0], group_order: group_order } - - before { group_order.update_price!; user.ordergroup.update_stats! } - - it { is_expected.to validate(:get, '/user/group_order_articles', 200, api_auth) } - it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 200, api_auth({ 'id' => goa.id })) } - it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 404, api_auth({ 'id' => goa_2.id })) } - it { is_expected.to validate(:get, '/user/group_order_articles/{id}', 404, api_auth({ 'id' => GroupOrderArticle.last.id + 1 })) } - - it { is_expected.to validate(:post, '/user/group_order_articles', 200, api_auth(create_params)) } - it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 200, api_auth(update_params)) } - it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({ 'id' => goa.id })) } - - context 'with an existing group_order_article' do - let(:create_params) { { '_data' => { group_order_article: { order_article_id: order.order_articles[0].id, quantity: 1 } } } } - - it { is_expected.to validate(:post, '/user/group_order_articles', 422, api_auth(create_params)) } - end - - context 'with invalid parameter values' do - let(:create_params) { { '_data' => { group_order_article: { order_article_id: order.order_articles[0].id, quantity: -1 } } } } - let(:update_params) { { 'id' => goa.id, '_data' => { group_order_article: { quantity: -1, tolerance: 0 } } } } - - it { is_expected.to validate(:post, '/user/group_order_articles', 422, api_auth(create_params)) } - it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 422, api_auth(update_params)) } - end - - context 'with a closed order' do - let(:order) { create(:order, article_count: 2, state: :finished) } - - it { is_expected.to validate(:post, '/user/group_order_articles', 404, api_auth(create_params)) } - it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 404, api_auth(update_params)) } - it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 404, api_auth({ 'id' => goa.id })) } - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - it { is_expected.to validate(:post, '/user/group_order_articles', 403, api_auth(create_params)) } - it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 403, api_auth(update_params)) } - it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({ 'id' => goa.id })) } - end - - context 'without enough apple points' do - before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) } - - it { is_expected.to validate(:post, '/user/group_order_articles', 403, api_auth(create_params)) } - it { is_expected.to validate(:patch, '/user/group_order_articles/{id}', 403, api_auth(update_params)) } - it { is_expected.to validate(:delete, '/user/group_order_articles/{id}', 200, api_auth({ 'id' => goa.id })) } - end - - it_handles_invalid_token_and_scope(:get, '/user/group_order_articles') - it_handles_invalid_token_and_scope(:post, '/user/group_order_articles', -> { api_auth(create_params) }) - it_handles_invalid_token_and_scope(:get, '/user/group_order_articles/{id}', -> { api_auth({ 'id' => goa.id }) }) - it_handles_invalid_token_and_scope(:patch, '/user/group_order_articles/{id}', -> { api_auth(update_params) }) - it_handles_invalid_token_and_scope(:delete, '/user/group_order_articles/{id}', -> { api_auth({ 'id' => goa.id }) }) - end - end - - context 'config' do - let(:api_scopes) { ['config:user'] } - - it { is_expected.to validate(:get, '/config', 200, api_auth) } - it { is_expected.to validate(:get, '/config', 401) } - - it_handles_invalid_token_and_scope(:get, '/config') - end - - context 'navigation' do - it { is_expected.to validate(:get, '/navigation', 200, api_auth) } - it { is_expected.to validate(:get, '/navigation', 401) } - - it_handles_invalid_token(:get, '/navigation') - end - - context 'financial_transactions' do - let(:api_scopes) { ['finance:read'] } - let(:user) { create(:user, :role_finance) } - let(:other_user) { create :user, :ordergroup } - let!(:ft_1) { create :financial_transaction, ordergroup: other_user.ordergroup } - let!(:ft_2) { create :financial_transaction, ordergroup: other_user.ordergroup } - - it { is_expected.to validate(:get, '/financial_transactions', 200, api_auth) } - it { is_expected.to validate(:get, '/financial_transactions/{id}', 200, api_auth({ 'id' => ft_2.id })) } - it { is_expected.to validate(:get, '/financial_transactions/{id}', 404, api_auth({ 'id' => FinancialTransaction.last.id + 1 })) } - - context 'without role_finance' do - let(:user) { create(:user) } - - it { is_expected.to validate(:get, '/financial_transactions', 403, api_auth) } - it { is_expected.to validate(:get, '/financial_transactions/{id}', 403, api_auth({ 'id' => ft_2.id })) } - end - - it_handles_invalid_token_and_scope(:get, '/financial_transactions') - it_handles_invalid_token_and_scope(:get, '/financial_transactions/{id}', -> { api_auth({ 'id' => ft_2.id }) }) - end - - context 'financial_transaction_classes' do - let!(:cla_1) { create :financial_transaction_class } - let!(:cla_2) { create :financial_transaction_class } - - it { is_expected.to validate(:get, '/financial_transaction_classes', 200, api_auth) } - it { is_expected.to validate(:get, '/financial_transaction_classes/{id}', 200, api_auth({ 'id' => cla_2.id })) } - it { is_expected.to validate(:get, '/financial_transaction_classes/{id}', 404, api_auth({ 'id' => cla_2.id + 1 })) } - - it_handles_invalid_token(:get, '/financial_transaction_classes') - it_handles_invalid_token(:get, '/financial_transaction_classes/{id}', -> { api_auth({ 'id' => cla_1.id }) }) - end - - context 'financial_transaction_types' do - let!(:tpy_1) { create :financial_transaction_type } - let!(:tpy_2) { create :financial_transaction_type } - - it { is_expected.to validate(:get, '/financial_transaction_types', 200, api_auth) } - it { is_expected.to validate(:get, '/financial_transaction_types/{id}', 200, api_auth({ 'id' => tpy_2.id })) } - it { is_expected.to validate(:get, '/financial_transaction_types/{id}', 404, api_auth({ 'id' => tpy_2.id + 1 })) } - - it_handles_invalid_token(:get, '/financial_transaction_types') - it_handles_invalid_token(:get, '/financial_transaction_types/{id}', -> { api_auth({ 'id' => tpy_1.id }) }) - end - - context 'orders' do - let(:api_scopes) { ['orders:read'] } - let!(:order) { create :order } - - it { is_expected.to validate(:get, '/orders', 200, api_auth) } - it { is_expected.to validate(:get, '/orders/{id}', 200, api_auth({ 'id' => order.id })) } - it { is_expected.to validate(:get, '/orders/{id}', 404, api_auth({ 'id' => Order.last.id + 1 })) } - - it_handles_invalid_token_and_scope(:get, '/orders') - it_handles_invalid_token_and_scope(:get, '/orders/{id}', -> { api_auth({ 'id' => order.id }) }) - end - - context 'order_articles' do - let(:api_scopes) { ['orders:read'] } - let!(:order_article) { create(:order, article_count: 1).order_articles.first } - let!(:stock_article) { create(:stock_article) } - let!(:stock_order_article) { create(:stock_order, article_ids: [stock_article.id]).order_articles.first } - - it { is_expected.to validate(:get, '/order_articles', 200, api_auth) } - it { is_expected.to validate(:get, '/order_articles/{id}', 200, api_auth({ 'id' => order_article.id })) } - it { is_expected.to validate(:get, '/order_articles/{id}', 200, api_auth({ 'id' => stock_order_article.id })) } - it { is_expected.to validate(:get, '/order_articles/{id}', 404, api_auth({ 'id' => Article.last.id + 1 })) } - - it_handles_invalid_token_and_scope(:get, '/order_articles') - it_handles_invalid_token_and_scope(:get, '/order_articles/{id}', -> { api_auth({ 'id' => order_article.id }) }) - end - - context 'article_categories' do - let!(:cat_1) { create :article_category } - let!(:cat_2) { create :article_category } - - it { is_expected.to validate(:get, '/article_categories', 200, api_auth) } - it { is_expected.to validate(:get, '/article_categories/{id}', 200, api_auth({ 'id' => cat_2.id })) } - it { is_expected.to validate(:get, '/article_categories/{id}', 404, api_auth({ 'id' => cat_2.id + 1 })) } - - it_handles_invalid_token(:get, '/article_categories') - it_handles_invalid_token(:get, '/article_categories/{id}', -> { api_auth({ 'id' => cat_1.id }) }) - end - end - - # needs to be last context so it is always run at the end - context 'and finally' do - it 'tests all documented routes' do - is_expected.to validate_all_paths - end - end -end diff --git a/spec/api/v1/user/financial_transactions_spec.rb b/spec/api/v1/user/financial_transactions_spec.rb deleted file mode 100644 index c7e8f826..00000000 --- a/spec/api/v1/user/financial_transactions_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'spec_helper' - -# Most routes are tested in the swagger_spec, this tests endpoints that change data. -describe Api::V1::User::FinancialTransactionsController, type: :controller do - include ApiOAuth - let(:user) { create(:user, :ordergroup) } - let(:api_scopes) { ['finance:user'] } - - let(:ftc1) { create :financial_transaction_class } - let(:ftc2) { create :financial_transaction_class } - let(:ftt1) { create :financial_transaction_type, financial_transaction_class: ftc1 } - let(:ftt2) { create :financial_transaction_type, financial_transaction_class: ftc2 } - let(:ftt3) { create :financial_transaction_type, financial_transaction_class: ftc2 } - - let(:amount) { rand(-100..100) } - let(:note) { Faker::Lorem.sentence } - - let(:json_ft) { json_response['financial_transaction'] } - - shared_examples "financial_transactions endpoint success" do - before { request } - - it "returns status 200" do - expect(response).to have_http_status :ok - end - end - - shared_examples "financial_transactions create/update success" do - include_examples "financial_transactions endpoint success" - - it "returns the financial_transaction" do - expect(json_ft['id']).to be_present - expect(json_ft['financial_transaction_type_id']).to eq ftt1.id - expect(json_ft['financial_transaction_type_name']).to eq ftt1.name - expect(json_ft['amount']).to eq amount - expect(json_ft['note']).to eq note - expect(json_ft['user_id']).to eq user.id - end - - it "updates the financial_transaction" do - resulting_ft = FinancialTransaction.where(id: json_ft['id']).first - expect(resulting_ft).to be_present - expect(resulting_ft.financial_transaction_type).to eq ftt1 - expect(resulting_ft.amount).to eq amount - expect(resulting_ft.note).to eq note - expect(resulting_ft.user).to eq user - end - end - - shared_examples "financial_transactions endpoint failure" do |status| - it "returns status #{status}" do - request - expect(response.status).to eq status - end - - it "does not change the ordergroup" do - expect { request }.to_not change { - user.ordergroup.attributes - } - end - - it "does not change the financial_transactions of ordergroup" do - expect { request }.to_not change { - user.ordergroup.financial_transactions.count - } - end - end - - describe "POST :create" do - let(:ft_params) { { amount: amount, financial_transaction_type_id: ftt1.id, note: note } } - let(:request) { post :create, params: { financial_transaction: ft_params, foodcoop: 'f' } } - - context 'without using self service' do - include_examples "financial_transactions endpoint failure", 403 - end - - context 'with using self service' do - before { FoodsoftConfig[:use_self_service] = true } - - context "with no existing financial transaction" do - include_examples "financial_transactions create/update success" - end - - context "with existing financial transaction" do - before { user.ordergroup.add_financial_transaction! 5000, 'for ordering', user, ftt3 } - - include_examples "financial_transactions create/update success" - end - - context "with invalid financial transaction type" do - let(:ft_params) { { amount: amount, financial_transaction_type_id: -1, note: note } } - - include_examples "financial_transactions endpoint failure", 404 - end - - context "without note" do - let(:ft_params) { { amount: amount, financial_transaction_type_id: ftt1.id } } - - include_examples "financial_transactions endpoint failure", 422 - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - include_examples "financial_transactions endpoint failure", 403 - end - end - end -end diff --git a/spec/api/v1/user/group_order_articles_spec.rb b/spec/api/v1/user/group_order_articles_spec.rb deleted file mode 100644 index 3bfa299e..00000000 --- a/spec/api/v1/user/group_order_articles_spec.rb +++ /dev/null @@ -1,220 +0,0 @@ -require 'spec_helper' - -# Most routes are tested in the swagger_spec, this tests endpoints that change data. -describe Api::V1::User::GroupOrderArticlesController, type: :controller do - include ApiOAuth - let(:user) { create(:user, :ordergroup) } - let(:json_goa) { json_response['group_order_article'] } - let(:json_oa) { json_response['order_article'] } - let(:api_scopes) { ['group_orders:user'] } - - let(:order) { create(:order, article_count: 1) } - let(:oa_1) { order.order_articles.first } - - let(:other_quantity) { rand(1..10) } - let(:other_tolerance) { rand(1..10) } - let(:user_other) { create(:user, :ordergroup) } - let!(:go_other) { create(:group_order, order: order, ordergroup: user_other.ordergroup) } - let!(:goa_other) { create(:group_order_article, group_order: go_other, order_article: oa_1, quantity: other_quantity, tolerance: other_tolerance) } - - before { go_other.update_price!; user_other.ordergroup.update_stats! } - - shared_examples "group_order_articles endpoint success" do - before { request } - - it "returns status 200" do - expect(response).to have_http_status :ok - end - - it "returns the order_article" do - expect(json_oa['id']).to eq oa_1.id - expect(json_oa['quantity']).to eq new_quantity + other_quantity - expect(json_oa['tolerance']).to eq new_tolerance + other_tolerance - end - - it "updates the group_order" do - go = nil - expect { - request - go = user.ordergroup.group_orders.where(order: order).last - }.to change { go&.updated_by }.to(user) - .and change { go&.price } - end - end - - shared_examples "group_order_articles create/update success" do - include_examples "group_order_articles endpoint success" - - it "returns the group_order_article" do - expect(json_goa['id']).to be_present - expect(json_goa['order_article_id']).to eq oa_1.id - expect(json_goa['quantity']).to eq new_quantity - expect(json_goa['tolerance']).to eq new_tolerance - end - - it "updates the group_order_article" do - resulting_goa = GroupOrderArticle.where(id: json_goa['id']).first - expect(resulting_goa).to be_present - expect(resulting_goa.quantity).to eq new_quantity - expect(resulting_goa.tolerance).to eq new_tolerance - end - end - - shared_examples "group_order_articles endpoint failure" do |status| - it "returns status #{status}" do - request - expect(response.status).to eq status - end - - it "does not change the group_order" do - expect { request }.to_not change { - go = user.ordergroup.group_orders.where(order: order).last - go&.attributes - } - end - - it "does not change the group_order_article" do - expect { request }.to_not change { - goa = GroupOrderArticle.joins(:group_order) - .where(order_article_id: oa_1.id, group_orders: { ordergroup: user.ordergroup }).last - goa&.attributes - } - end - end - - describe "POST :create" do - let(:new_quantity) { rand(1..10) } - let(:new_tolerance) { rand(1..10) } - - let(:goa_params) { { order_article_id: oa_1.id, quantity: new_quantity, tolerance: new_tolerance } } - let(:request) { post :create, params: { group_order_article: goa_params, foodcoop: 'f' } } - - context "with no existing group_order" do - include_examples "group_order_articles create/update success" - end - - context "with an existing group_order" do - let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - - include_examples "group_order_articles create/update success" - end - - context "with an existing group_order_article" do - let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1, quantity: 0, tolerance: 1) } - - before { go.update_price!; user.ordergroup.update_stats! } - - include_examples "group_order_articles endpoint failure", 422 - end - - context "with invalid parameter values" do - let(:goa_params) { { order_article_id: oa_1.id, quantity: -1, tolerance: new_tolerance } } - - include_examples "group_order_articles endpoint failure", 422 - end - - context 'with a closed order' do - let(:order) { create(:order, article_count: 1, state: :finished) } - - include_examples "group_order_articles endpoint failure", 404 - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - include_examples "group_order_articles endpoint failure", 403 - end - - context 'without enough apple points' do - before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) } - - include_examples "group_order_articles endpoint failure", 403 - end - end - - describe "PATCH :update" do - let(:new_quantity) { rand(2..10) } - let(:goa_params) { { quantity: new_quantity, tolerance: new_tolerance } } - let(:request) { patch :update, params: { id: goa.id, group_order_article: goa_params, foodcoop: 'f' } } - let(:new_tolerance) { rand(2..10) } - - let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1, quantity: 1, tolerance: 0) } - - before { go.update_price!; user.ordergroup.update_stats! } - - context "happy flow" do - include_examples "group_order_articles create/update success" - end - - context "with invalid parameter values" do - let(:goa_params) { { order_article_id: oa_1.id, quantity: -1, tolerance: new_tolerance } } - - include_examples "group_order_articles endpoint failure", 422 - end - - context 'with a closed order' do - let(:order) { create(:order, article_count: 1, state: :finished) } - - include_examples "group_order_articles endpoint failure", 404 - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - include_examples "group_order_articles endpoint failure", 403 - end - - context 'without enough apple points' do - before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) } - - include_examples "group_order_articles endpoint failure", 403 - end - end - - describe "DELETE :destroy" do - let(:new_quantity) { 0 } - let(:request) { delete :destroy, params: { id: goa.id, foodcoop: 'f' } } - let(:new_tolerance) { 0 } - - let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1) } - - before { go.update_price!; user.ordergroup.update_stats! } - - shared_examples "group_order_articles destroy success" do - include_examples "group_order_articles endpoint success" - - it "does not return the group_order_article" do - expect(json_goa).to be_nil - end - - it "deletes the group_order_article" do - expect(GroupOrderArticle.where(id: goa.id)).to be_empty - end - end - - context "happy flow" do - include_examples "group_order_articles destroy success" - end - - context 'with a closed order' do - let(:order) { create(:order, article_count: 1, state: :finished) } - - include_examples "group_order_articles endpoint failure", 404 - end - - context 'without enough balance' do - before { FoodsoftConfig[:minimum_balance] = 1000 } - - include_examples "group_order_articles destroy success" - end - - context 'without enough apple points' do - before { allow_any_instance_of(Ordergroup).to receive(:not_enough_apples?).and_return(true) } - - include_examples "group_order_articles destroy success" - end - end -end diff --git a/spec/api/v1/user/ordergroup_spec.rb b/spec/api/v1/user/ordergroup_spec.rb deleted file mode 100644 index 5eacb63e..00000000 --- a/spec/api/v1/user/ordergroup_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'spec_helper' - -describe Api::V1::User::OrdergroupController, type: :controller do - include ApiOAuth - let(:user) { create :user, :ordergroup } - let(:api_scopes) { ['finance:user'] } - - let(:ftc1) { create :financial_transaction_class } - let(:ftc2) { create :financial_transaction_class } - let(:ftt1) { create :financial_transaction_type, financial_transaction_class: ftc1 } - let(:ftt2) { create :financial_transaction_type, financial_transaction_class: ftc2 } - let(:ftt3) { create :financial_transaction_type, financial_transaction_class: ftc2 } - - describe "GET :financial_overview" do - let(:order) { create(:order, article_count: 1) } - let(:json_financial_overview) { json_response['financial_overview'] } - let(:oa_1) { order.order_articles.first } - - let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } - let!(:goa) { create(:group_order_article, group_order: go, order_article: oa_1, quantity: 1, tolerance: 0) } - - before { go.update_price!; user.ordergroup.update_stats! } - - before do - og = user.ordergroup - og.add_financial_transaction!(-1, '-1', user, ftt1) - og.add_financial_transaction!(2, '2', user, ftt1) - og.add_financial_transaction!(3, '3', user, ftt1) - - og.add_financial_transaction!(-10, '-10', user, ftt2) - og.add_financial_transaction!(20, '20', user, ftt2) - og.add_financial_transaction!(30, '30', user, ftt2) - - og.add_financial_transaction!(-100, '-100', user, ftt3) - og.add_financial_transaction!(200, '200', user, ftt3) - og.add_financial_transaction!(300, '300', user, ftt3) - end - - it "returns correct values" do - get :financial_overview, params: { foodcoop: 'f' } - expect(json_financial_overview['account_balance']).to eq 444 - expect(json_financial_overview['available_funds']).to eq 444 - go.price - - ftcs = Hash[json_financial_overview['financial_transaction_class_sums'].map { |x| [x['id'], x] }] - - ftcs1 = ftcs[ftc1.id] - expect(ftcs1['name']).to eq ftc1.name - expect(ftcs1['amount']).to eq 4 - - ftcs2 = ftcs[ftc2.id] - expect(ftcs2['name']).to eq ftc2.name - expect(ftcs2['amount']).to eq 440 - end - end -end diff --git a/spec/app_config.yml b/spec/app_config.yml index 2e146be9..a9bd72b0 100644 --- a/spec/app_config.yml +++ b/spec/app_config.yml @@ -6,6 +6,7 @@ default: &defaults multi_coop_install: false + use_self_service: true default_scope: 'f' name: FC Minimal diff --git a/spec/controllers/finance/ordergroups_controller_spec.rb b/spec/controllers/finance/ordergroups_controller_spec.rb new file mode 100644 index 00000000..73c1f3bb --- /dev/null +++ b/spec/controllers/finance/ordergroups_controller_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Finance::OrdergroupsController do + include ActionView::Helpers::NumberHelper + render_views + + let(:user) { create(:user, :role_finance, :role_orders, :ordergroup) } + let(:fin_trans_type1) { create(:financial_transaction_type) } + let(:fin_trans_type2) { create(:financial_transaction_type) } + let(:fin_trans1) do + create(:financial_transaction, + user: user, + amount: 100, + ordergroup: user.ordergroup, + financial_transaction_type: fin_trans_type1) + end + let(:fin_trans2) do + create(:financial_transaction, + user: user, + amount: 200, + ordergroup: user.ordergroup, + financial_transaction_type: fin_trans_type1) + end + let(:fin_trans3) do + create(:financial_transaction, + user: user, + amount: 42.23, + ordergroup: user.ordergroup, + financial_transaction_type: fin_trans_type2) + end + + before { login user } + + describe 'GET index' do + before do + fin_trans1 + fin_trans2 + fin_trans3 + end + + it 'renders index page' do + get_with_defaults :index + expect(response).to have_http_status(:success) + end + + it 'calculates total balance sums correctly' do + get_with_defaults :index + expect(response).to have_http_status(:success) + + assert_select "#total_balance#{fin_trans_type1.financial_transaction_class_id}", number_to_currency(300) + assert_select "#total_balance#{fin_trans_type2.financial_transaction_class_id}", number_to_currency(42.23) + assert_select '#total_balance_sum', number_to_currency(342.23) + end + end +end diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb new file mode 100644 index 00000000..525c27e4 --- /dev/null +++ b/spec/controllers/home_controller_spec.rb @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe HomeController do + let(:user) { create(:user) } + + describe 'GET index' do + describe 'NOT logged in' do + it 'redirects' do + get_with_defaults :profile + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(login_path) + end + end + + describe 'logged in' do + before { login user } + + it 'succeeds' do + get_with_defaults :index + expect(response).to have_http_status(:success) + end + end + end + + describe 'GET profile' do + before { login user } + + it 'succeeds' do + get_with_defaults :profile + expect(response).to have_http_status(:success) + end + end + + describe 'GET reference_calculator' do + describe 'with simple user' do + before { login user } + + it 'redirects to home' do + get_with_defaults :reference_calculator + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(root_path) + end + end + + describe 'with ordergroup user' do + let(:og_user) { create(:user, :ordergroup) } + + before { login og_user } + + it 'succeeds' do + get_with_defaults :reference_calculator + expect(response).to have_http_status(:success) + end + end + end + + describe 'GET update_profile' do + describe 'with simple user' do + let(:unchanged_attributes) { user.attributes.slice('first_name', 'last_name', 'email') } + let(:changed_attributes) { attributes_for(:user) } + let(:invalid_attributes) { { email: 'e.mail.com' } } + + before { login user } + + it 'stays on profile after update with invalid attributes' do + get_with_defaults :update_profile, params: { user: invalid_attributes } + expect(response).to have_http_status(:success) + end + + it 'redirects to profile after update with unchanged attributes' do + get_with_defaults :update_profile, params: { user: unchanged_attributes } + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(my_profile_path) + end + + it 'redirects to profile after update' do + patch :update_profile, params: { foodcoop: FoodsoftConfig[:default_scope], user: changed_attributes } + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(my_profile_path) + expect(flash[:notice]).to match(/#{I18n.t('home.changes_saved')}/) + expect(user.reload.attributes.slice(:first_name, :last_name, + :email)).to eq(changed_attributes.slice('first_name', 'last_name', 'email')) + end + end + + describe 'with ordergroup user' do + let(:og_user) { create(:user, :ordergroup) } + let(:unchanged_attributes) { og_user.attributes.slice('first_name', 'last_name', 'email') } + let(:changed_attributes) { unchanged_attributes.merge({ ordergroup: { contact_address: 'new Adress 7' } }) } + + before { login og_user } + + it 'redirects to home after update' do + get_with_defaults :update_profile, params: { user: changed_attributes } + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(my_profile_path) + expect(og_user.reload.ordergroup.contact_address).to eq('new Adress 7') + end + end + end + + describe 'GET ordergroup' do + describe 'with simple user' do + before { login user } + + it 'redirects to home' do + get_with_defaults :ordergroup + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(root_path) + end + end + + describe 'with ordergroup user' do + let(:og_user) { create(:user, :ordergroup) } + + before { login og_user } + + it 'succeeds' do + get_with_defaults :ordergroup + expect(response).to have_http_status(:success) + end + end + end + + describe 'GET cancel_membership' do + describe 'with simple user without group' do + before { login user } + + it 'fails' do + expect do + get_with_defaults :cancel_membership + end.to raise_error(ActiveRecord::RecordNotFound) + expect do + get_with_defaults :cancel_membership, params: { membership_id: 424_242 } + end.to raise_error(ActiveRecord::RecordNotFound) + end + end + + describe 'with ordergroup user' do + let(:fin_user) { create(:user, :role_finance) } + + before { login fin_user } + + it 'removes user from group' do + membership = fin_user.memberships.first + get_with_defaults :cancel_membership, params: { group_id: fin_user.groups.first.id } + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(my_profile_path) + expect(flash[:notice]).to match(/#{I18n.t('home.ordergroup_cancelled', group: membership.group.name)}/) + end + + it 'removes user membership' do + membership = fin_user.memberships.first + get_with_defaults :cancel_membership, params: { membership_id: membership.id } + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(my_profile_path) + expect(flash[:notice]).to match(/#{I18n.t('home.ordergroup_cancelled', group: membership.group.name)}/) + end + end + end +end diff --git a/spec/factories/delivery.rb b/spec/factories/delivery.rb index 5d27d870..e3b37d0a 100644 --- a/spec/factories/delivery.rb +++ b/spec/factories/delivery.rb @@ -2,7 +2,7 @@ require 'factory_bot' FactoryBot.define do factory :delivery do - supplier { create :supplier } + supplier { create(:supplier) } date { Time.now } end end diff --git a/spec/factories/group_order.rb b/spec/factories/group_order.rb index f7e910df..d62172ea 100644 --- a/spec/factories/group_order.rb +++ b/spec/factories/group_order.rb @@ -4,6 +4,6 @@ FactoryBot.define do # requires order factory :group_order do ordergroup { create(:user, groups: [FactoryBot.create(:ordergroup)]).ordergroup } - updated_by { create :user } + updated_by { create(:user) } end end diff --git a/spec/factories/invoice.rb b/spec/factories/invoice.rb index b3e65a17..3564d977 100644 --- a/spec/factories/invoice.rb +++ b/spec/factories/invoice.rb @@ -3,9 +3,9 @@ require 'factory_bot' FactoryBot.define do factory :invoice do supplier - number { rand(1..99999) } + number { rand(1..99_999) } amount { rand(0.1..26.0).round(2) } - created_by { create :user } + created_by { create(:user) } after :create do |invoice| invoice.supplier.reload diff --git a/spec/factories/order.rb b/spec/factories/order.rb index 87febae2..970bd040 100644 --- a/spec/factories/order.rb +++ b/spec/factories/order.rb @@ -3,10 +3,10 @@ require 'factory_bot' FactoryBot.define do factory :order do starts { Time.now } - supplier { create :supplier, article_count: (article_count.nil? ? true : article_count) } + supplier { create(:supplier, article_count: (article_count.nil? ? true : article_count)) } article_ids { supplier.articles.map(&:id) unless supplier.nil? } - created_by { create :user } - updated_by { create :user } + created_by { create(:user) } + updated_by { create(:user) } transient do article_count { true } diff --git a/spec/factories/supplier.rb b/spec/factories/supplier.rb index 67ba3528..ef592a60 100644 --- a/spec/factories/supplier.rb +++ b/spec/factories/supplier.rb @@ -10,8 +10,8 @@ FactoryBot.define do article_count { 0 } end - before :create do |supplier, evaluator| - next if supplier.class == SharedSupplier + before :create do |supplier, _evaluator| + next if supplier.instance_of?(SharedSupplier) next if supplier.supplier_category_id? supplier.supplier_category = create :supplier_category @@ -20,7 +20,7 @@ FactoryBot.define do after :create do |supplier, evaluator| article_count = evaluator.article_count article_count = rand(1..99) if article_count == true - create_list :article, article_count, supplier: supplier + create_list(:article, article_count, supplier: supplier) end factory :shared_supplier, class: 'SharedSupplier' diff --git a/spec/factories/user.rb b/spec/factories/user.rb index a6067e6b..9563c15c 100644 --- a/spec/factories/user.rb +++ b/spec/factories/user.rb @@ -10,21 +10,21 @@ FactoryBot.define do factory :admin do sequence(:nick) { |n| "admin#{n}" } first_name { 'Administrator' } - after :create do |user, evaluator| - create :workgroup, role_admin: true, user_ids: [user.id] + after :create do |user, _evaluator| + create(:workgroup, role_admin: true, user_ids: [user.id]) end end trait :ordergroup do - after :create do |user, evaluator| - create :ordergroup, user_ids: [user.id] + after :create do |user, _evaluator| + create(:ordergroup, user_ids: [user.id]) end end - [:ordergroup, :finance, :invoices, :article_meta, :suppliers, :pickups, :orders].each do |role| + %i[ordergroup finance invoices article_meta suppliers pickups orders].each do |role| trait "role_#{role}".to_sym do - after :create do |user, evaluator| - create :workgroup, "role_#{role}" => true, user_ids: [user.id] + after :create do |user, _evaluator| + create(:workgroup, "role_#{role}" => true, user_ids: [user.id]) end end end @@ -37,7 +37,7 @@ FactoryBot.define do type { 'Workgroup' } end - factory :ordergroup, class: "Ordergroup" do + factory :ordergroup, class: 'Ordergroup' do type { 'Ordergroup' } sequence(:name) { |n| "Order group ##{n}" } # workaround to avoid needing to save the ordergroup diff --git a/spec/integration/articles_spec.rb b/spec/integration/articles_spec.rb index bbd5e375..bddd80d6 100644 --- a/spec/integration/articles_spec.rb +++ b/spec/integration/articles_spec.rb @@ -1,9 +1,9 @@ require_relative '../spec_helper' feature ArticlesController do - let(:user) { create :user, groups: [create(:workgroup, role_article_meta: true)] } - let(:supplier) { create :supplier } - let!(:article_category) { create :article_category } + let(:user) { create(:user, groups: [create(:workgroup, role_article_meta: true)]) } + let(:supplier) { create(:supplier) } + let!(:article_category) { create(:article_category) } before { login user } @@ -18,15 +18,15 @@ feature ArticlesController do it 'can create a new article' do click_on I18n.t('articles.index.new') expect(page).to have_selector('form#new_article') - article = build :article, supplier: supplier, article_category: article_category + article = build(:article, supplier: supplier, article_category: article_category) within('#new_article') do - fill_in 'article_name', :with => article.name - fill_in 'article_unit', :with => article.unit - select article.article_category.name, :from => 'article_article_category_id' - fill_in 'article_price', :with => article.price - fill_in 'article_unit_quantity', :with => article.unit_quantity - fill_in 'article_tax', :with => article.tax - fill_in 'article_deposit', :with => article.deposit + fill_in 'article_name', with: article.name + fill_in 'article_unit', with: article.unit + select article.article_category.name, from: 'article_article_category_id' + fill_in 'article_price', with: article.price + fill_in 'article_unit_quantity', with: article.unit_quantity + fill_in 'article_tax', with: article.tax + fill_in 'article_deposit', with: article.deposit # "Element cannot be scrolled into view" error, js as workaround # find('input[type="submit"]').click page.execute_script('$("form#new_article").submit();') @@ -50,22 +50,22 @@ feature ArticlesController do it do find('input[type="submit"]').click - expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio â—Ž" - expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten" + expect(find('tr:nth-child(1) #new_articles__note').value).to eq 'bio â—Ž' + expect(find('tr:nth-child(2) #new_articles__name').value).to eq 'Pijnboompitten' 4.times do |i| all("tr:nth-child(#{i + 1}) select > option")[1].select_option end find('input[type="submit"]').click - expect(page).to have_content("Pijnboompitten") + expect(page).to have_content('Pijnboompitten') expect(supplier.articles.count).to eq 4 end end end - describe "can update existing article" do - let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g' } + describe 'can update existing article' do + let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g') } it do find('input[type="submit"]').click @@ -77,35 +77,35 @@ feature ArticlesController do end end - describe "handles missing data" do + describe 'handles missing data' do it do find('input[type="submit"]').click # to overview find('input[type="submit"]').click # missing category, re-show form expect(find('tr.alert')).to be_present expect(supplier.articles.count).to eq 0 - all("tr select > option")[1].select_option + all('tr select > option')[1].select_option find('input[type="submit"]').click # now it should succeed expect(supplier.articles.count).to eq 1 end end - describe "can remove an existing article" do - let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 99999 } + describe 'can remove an existing article' do + let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 99_999) } it do check('articles_outlist_absent') find('input[type="submit"]').click expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present - all("tr select > option")[1].select_option + all('tr select > option')[1].select_option find('input[type="submit"]').click expect(article.reload.deleted?).to be true end end - describe "can convert units when updating" do - let!(:article) { create :article, supplier: supplier, order_number: 1, unit: '250 g' } + describe 'can convert units when updating' do + let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g') } it do check('articles_convert_units') diff --git a/spec/integration/balancing_spec.rb b/spec/integration/balancing_spec.rb index 556d102d..d8e58e6d 100644 --- a/spec/integration/balancing_spec.rb +++ b/spec/integration/balancing_spec.rb @@ -1,17 +1,17 @@ require_relative '../spec_helper' feature 'settling an order', js: true do - let(:ftt) { create :financial_transaction_type } - let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] } - let(:user) { create :user, groups: [create(:ordergroup)] } - let(:supplier) { create :supplier } - let(:article) { create :article, supplier: supplier, unit_quantity: 1 } - let(:order) { create :order, supplier: supplier, article_ids: [article.id] } # need to ref article - let(:go1) { create :group_order, order: order } - let(:go2) { create :group_order, order: order } + let(:ftt) { create(:financial_transaction_type) } + let(:admin) { create(:user, groups: [create(:workgroup, role_finance: true)]) } + let(:user) { create(:user, groups: [create(:ordergroup)]) } + let(:supplier) { create(:supplier) } + let(:article) { create(:article, supplier: supplier, unit_quantity: 1) } + let(:order) { create(:order, supplier: supplier, article_ids: [article.id]) } # need to ref article + let(:go1) { create(:group_order, order: order) } + let(:go2) { create(:group_order, order: order) } let(:oa) { order.order_articles.find_by_article_id(article.id) } - let(:goa1) { create :group_order_article, group_order: go1, order_article: oa } - let(:goa2) { create :group_order_article, group_order: go2, order_article: oa } + let(:goa1) { create(:group_order_article, group_order: go1, order_article: oa) } + let(:goa2) { create(:group_order_article, group_order: go2, order_article: oa) } before do goa1.update_quantities(3, 0) @@ -22,6 +22,9 @@ feature 'settling an order', js: true do goa2.reload end + before { visit new_finance_order_path(order_id: order.id) } + before { login admin } + it 'has correct order result' do expect(oa.quantity).to eq(4) expect(oa.tolerance).to eq(0) @@ -29,10 +32,6 @@ feature 'settling an order', js: true do expect(goa2.result).to eq(1) end - before { login admin } - - before { visit new_finance_order_path(order_id: order.id) } - it 'has product ordered visible' do expect(page).to have_content(article.name) expect(page).to have_selector("#order_article_#{oa.id}") @@ -59,7 +58,7 @@ feature 'settling an order', js: true do click_link I18n.t('ui.delete') end end - expect(page).to_not have_selector("#order_article_#{oa.id}") + expect(page).not_to have_selector("#order_article_#{oa.id}") expect(OrderArticle.exists?(oa.id)).to be true oa.reload expect(oa.quantity).to eq(4) @@ -77,7 +76,7 @@ feature 'settling an order', js: true do click_link I18n.t('ui.delete') end end - expect(page).to_not have_selector("#order_article_#{oa.id}") + expect(page).not_to have_selector("#order_article_#{oa.id}") expect(OrderArticle.exists?(oa.id)).to be false end @@ -87,7 +86,7 @@ feature 'settling an order', js: true do within("#group_order_article_#{goa1.id}") do click_link I18n.t('ui.delete') end - expect(page).to_not have_selector("#group_order_article_#{goa1.id}") + expect(page).not_to have_selector("#group_order_article_#{goa1.id}") expect(OrderArticle.exists?(oa.id)).to be true expect(GroupOrderArticle.exists?(goa1.id)).to be true goa1.reload @@ -103,7 +102,7 @@ feature 'settling an order', js: true do within("#group_order_article_#{goa1.id}") do click_link I18n.t('ui.delete') end - expect(page).to_not have_selector("#group_order_article_#{goa1.id}") + expect(page).not_to have_selector("#group_order_article_#{goa1.id}") expect(OrderArticle.exists?(oa.id)).to be true expect(GroupOrderArticle.exists?(goa1.id)).to be false end @@ -134,15 +133,15 @@ feature 'settling an order', js: true do end expect(page).to have_selector('form#new_group_order_article') within('#new_group_order_article') do - select user.ordergroup.name, :from => 'group_order_article_ordergroup_id' + select user.ordergroup.name, from: 'group_order_article_ordergroup_id' find_by_id('group_order_article_result').set(8) sleep 0.25 find('input[type="submit"]').click end - expect(page).to_not have_selector('form#new_group_order_article') + expect(page).not_to have_selector('form#new_group_order_article') expect(page).to have_content(user.ordergroup.name) goa = GroupOrderArticle.last - expect(goa).to_not be_nil + expect(goa).not_to be_nil expect(goa.result).to eq 8 expect(page).to have_selector("#group_order_article_#{goa.id}") expect(find("#r_#{goa.id}").value.to_f).to eq 8 @@ -169,8 +168,8 @@ feature 'settling an order', js: true do end it 'can add an article' do - new_article = create :article, supplier: supplier - expect(page).to_not have_content(new_article.name) + new_article = create(:article, supplier: supplier) + expect(page).not_to have_content(new_article.name) click_link I18n.t('finance.balancing.edit_results_by_articles.add_article') expect(page).to have_selector('form#new_order_article') within('#new_order_article') do @@ -178,8 +177,8 @@ feature 'settling an order', js: true do sleep 0.25 find('input[type="submit"]').click end - expect(page).to_not have_selector('form#new_order_article') + expect(page).not_to have_selector('form#new_order_article') expect(page).to have_content(new_article.name) - expect(order.order_articles.where(article_id: new_article.id)).to_not be_nil + expect(order.order_articles.where(article_id: new_article.id)).not_to be_nil end end diff --git a/spec/integration/config_spec.rb b/spec/integration/config_spec.rb index 91f376dd..4f67802a 100644 --- a/spec/integration/config_spec.rb +++ b/spec/integration/config_spec.rb @@ -3,7 +3,7 @@ require_relative '../spec_helper' feature 'admin/configs' do let(:name) { Faker::Lorem.words(number: rand(2..4)).join(' ') } let(:email) { Faker::Internet.email } - let(:admin) { create :admin } + let(:admin) { create(:admin) } before { login admin } @@ -51,13 +51,13 @@ feature 'admin/configs' do end def compact_hash_deep!(h) - h.each do |k, v| + h.each do |_k, v| if v.is_a? Hash compact_hash_deep!(v) - v.reject! { |k, v| v.blank? } + v.compact_blank! end end - h.reject! { |k, v| v.blank? } + h.compact_blank! h end end diff --git a/spec/integration/home_spec.rb b/spec/integration/home_spec.rb index 313b9afe..87390bd9 100644 --- a/spec/integration/home_spec.rb +++ b/spec/integration/home_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' feature 'my profile page' do - let(:user) { create :user } + let(:user) { create(:user) } before { login user } diff --git a/spec/integration/login_spec.rb b/spec/integration/login_spec.rb index 49af6852..747d170f 100644 --- a/spec/integration/login_spec.rb +++ b/spec/integration/login_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' feature LoginController do - let(:user) { create :user } + let(:user) { create(:user) } describe 'forgot password' do before { visit forgot_password_path } @@ -36,7 +36,7 @@ feature LoginController do it 'is not accessible' do expect(page).to have_selector '.alert-error' - expect(page).to_not have_selector 'input[type=password]' + expect(page).not_to have_selector 'input[type=password]' end end diff --git a/spec/integration/order_spec.rb b/spec/integration/order_spec.rb index dd768997..37b9e60a 100644 --- a/spec/integration/order_spec.rb +++ b/spec/integration/order_spec.rb @@ -1,12 +1,12 @@ require_relative '../spec_helper' feature Order, js: true do - let(:admin) { create :user, groups: [create(:workgroup, role_orders: true)] } - let(:article) { create :article, unit_quantity: 1 } - let(:order) { create :order, supplier: article.supplier, article_ids: [article.id] } # need to ref article - let(:go1) { create :group_order, order: order } + let(:admin) { create(:user, groups: [create(:workgroup, role_orders: true)]) } + let(:article) { create(:article, unit_quantity: 1) } + let(:order) { create(:order, supplier: article.supplier, article_ids: [article.id]) } # need to ref article + let(:go1) { create(:group_order, order: order) } let(:oa) { order.order_articles.find_by_article_id(article.id) } - let(:goa1) { create :group_order_article, group_order: go1, order_article: oa } + let(:goa1) { create(:group_order_article, group_order: go1, order_article: oa) } before { login admin } diff --git a/spec/integration/product_distribution_example_spec.rb b/spec/integration/product_distribution_example_spec.rb index e15642f1..2c1af327 100644 --- a/spec/integration/product_distribution_example_spec.rb +++ b/spec/integration/product_distribution_example_spec.rb @@ -1,12 +1,12 @@ require_relative '../spec_helper' feature 'product distribution', js: true do - let(:ftt) { create :financial_transaction_type } - let(:admin) { create :admin } - let(:user_a) { create :user, groups: [create(:ordergroup)] } - let(:user_b) { create :user, groups: [create(:ordergroup)] } - let(:supplier) { create :supplier } - let(:article) { create :article, supplier: supplier, unit_quantity: 5 } + let(:ftt) { create(:financial_transaction_type) } + let(:admin) { create(:admin) } + let(:user_a) { create(:user, groups: [create(:ordergroup)]) } + let(:user_b) { create(:user, groups: [create(:ordergroup)]) } + let(:supplier) { create(:supplier) } + let(:article) { create(:article, supplier: supplier, unit_quantity: 5) } let(:order) { create(:order, supplier: supplier, article_ids: [article.id]) } let(:oa) { order.order_articles.first } @@ -50,10 +50,10 @@ feature 'product distribution', js: true do expect(oa.quantity).to eq(6) expect(oa.tolerance).to eq(1) # Gruppe a bekommt 3 einheiten. - goa_a = oa.group_order_articles.joins(:group_order).where(:group_orders => { :ordergroup_id => user_a.ordergroup.id }).first + goa_a = oa.group_order_articles.joins(:group_order).where(group_orders: { ordergroup_id: user_a.ordergroup.id }).first expect(goa_a.result).to eq(3) # gruppe b bekommt 2 einheiten. - goa_b = oa.group_order_articles.joins(:group_order).where(:group_orders => { :ordergroup_id => user_b.ordergroup.id }).first + goa_b = oa.group_order_articles.joins(:group_order).where(group_orders: { ordergroup_id: user_b.ordergroup.id }).first expect(goa_b.result).to eq(2) end end diff --git a/spec/integration/receive_spec.rb b/spec/integration/receive_spec.rb index 3b65107e..6bf021e8 100644 --- a/spec/integration/receive_spec.rb +++ b/spec/integration/receive_spec.rb @@ -1,15 +1,15 @@ require_relative '../spec_helper' feature 'receiving an order', js: true do - let(:admin) { create :user, groups: [create(:workgroup, role_orders: true)] } - let(:supplier) { create :supplier } - let(:article) { create :article, supplier: supplier, unit_quantity: 3 } - let(:order) { create :order, supplier: supplier, article_ids: [article.id] } # need to ref article - let(:go1) { create :group_order, order: order } - let(:go2) { create :group_order, order: order } + let(:admin) { create(:user, groups: [create(:workgroup, role_orders: true)]) } + let(:supplier) { create(:supplier) } + let(:article) { create(:article, supplier: supplier, unit_quantity: 3) } + let(:order) { create(:order, supplier: supplier, article_ids: [article.id]) } # need to ref article + let(:go1) { create(:group_order, order: order) } + let(:go2) { create(:group_order, order: order) } let(:oa) { order.order_articles.find_by_article_id(article.id) } - let(:goa1) { create :group_order_article, group_order: go1, order_article: oa } - let(:goa2) { create :group_order_article, group_order: go2, order_article: oa } + let(:goa1) { create(:group_order_article, group_order: go1, order_article: oa) } + let(:goa2) { create(:group_order_article, group_order: go2, order_article: oa) } # set quantities of group_order_articles def set_quantities(q1, q2) @@ -46,7 +46,7 @@ feature 'receiving an order', js: true do it 'has product not ordered invisible' do set_quantities [0, 0], [0, 0] visit receive_order_path(id: order.id) - expect(page).to_not have_selector("#order_article_#{oa.id}") + expect(page).not_to have_selector("#order_article_#{oa.id}") end it 'is not received by default' do @@ -58,7 +58,7 @@ feature 'receiving an order', js: true do it 'does not change anything when received is ordered' do set_quantities [2, 0], [3, 2] visit receive_order_path(id: order.id) - fill_in "order_articles_#{oa.id}_units_received", :with => oa.units_to_order + fill_in "order_articles_#{oa.id}_units_received", with: oa.units_to_order find('input[type="submit"]').click expect(page).to have_selector('body') check_quantities 2, 2, 4 @@ -67,7 +67,7 @@ feature 'receiving an order', js: true do it 'redistributes properly when received is more' do set_quantities [2, 0], [3, 2] visit receive_order_path(id: order.id) - fill_in "order_articles_#{oa.id}_units_received", :with => 3 + fill_in "order_articles_#{oa.id}_units_received", with: 3 find('input[type="submit"]').click expect(page).to have_selector('body') check_quantities 3, 2, 5 @@ -76,7 +76,7 @@ feature 'receiving an order', js: true do it 'redistributes properly when received is less' do set_quantities [2, 0], [3, 2] visit receive_order_path(id: order.id) - fill_in "order_articles_#{oa.id}_units_received", :with => 1 + fill_in "order_articles_#{oa.id}_units_received", with: 1 find('input[type="submit"]').click expect(page).to have_selector('body') check_quantities 1, 2, 1 diff --git a/spec/integration/session_spec.rb b/spec/integration/session_spec.rb index 2571ccab..e264efcb 100644 --- a/spec/integration/session_spec.rb +++ b/spec/integration/session_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' feature 'the session' do - let(:user) { create :user } + let(:user) { create(:user) } describe 'login page' do it 'is accessible' do @@ -11,7 +11,7 @@ feature 'the session' do it 'logs me in' do login user - expect(page).to_not have_selector('.alert-error') + expect(page).not_to have_selector('.alert-error') end it 'does not log me in with wrong password' do @@ -21,10 +21,10 @@ feature 'the session' do it 'can log me in using an email address' do visit login_path - fill_in 'nick', :with => user.email - fill_in 'password', :with => user.password + fill_in 'nick', with: user.email + fill_in 'password', with: user.password find('input[type=submit]').click - expect(page).to_not have_selector('.alert-error') + expect(page).not_to have_selector('.alert-error') end end end diff --git a/spec/integration/supplier_spec.rb b/spec/integration/supplier_spec.rb index 178892b8..5683d8da 100644 --- a/spec/integration/supplier_spec.rb +++ b/spec/integration/supplier_spec.rb @@ -1,20 +1,20 @@ require_relative '../spec_helper' feature 'supplier' do - let(:supplier) { create :supplier } - let(:user) { create :user, :role_suppliers } + let(:supplier) { create(:supplier) } + let(:user) { create(:user, :role_suppliers) } before { login user } describe 'create new' do it 'can be created' do - create :supplier_category + create(:supplier_category) visit new_supplier_path - supplier = build :supplier + supplier = build(:supplier) within('#new_supplier') do - fill_in 'supplier_name', :with => supplier.name - fill_in 'supplier_address', :with => supplier.address - fill_in 'supplier_phone', :with => supplier.phone + fill_in 'supplier_name', with: supplier.name + fill_in 'supplier_address', with: supplier.address + fill_in 'supplier_phone', with: supplier.phone find('input[type="submit"]').click end expect(page).to have_content(supplier.name) @@ -38,7 +38,7 @@ feature 'supplier' do end it 'can be updated' do - new_supplier = build :supplier + new_supplier = build(:supplier) supplier visit edit_supplier_path(id: supplier.id) fill_in 'supplier_name', with: new_supplier.name diff --git a/spec/lib/bank_account_information_importer_spec.rb b/spec/lib/bank_account_information_importer_spec.rb index 40c3b1ea..cbe48fe2 100644 --- a/spec/lib/bank_account_information_importer_spec.rb +++ b/spec/lib/bank_account_information_importer_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' describe BankTransaction do - let(:bank_account) { create :bank_account } + let(:bank_account) { create(:bank_account) } it 'empty content' do content = '' @@ -188,7 +188,7 @@ describe BankTransaction do expect(bt.date).to eq('2019-02-13'.to_date) expect(bt.text).to eq('Deutsche Bundesbahn') expect(bt.iban).to eq('DE72957284895783674747') - expect(bt.reference).to eq("743574386368 Muenchen-Hamburg 27.03.2019") + expect(bt.reference).to eq('743574386368 Muenchen-Hamburg 27.03.2019') expect(bt.receipt).to eq('Lastschrift') end @@ -277,7 +277,7 @@ describe BankTransaction do expect(bt.date).to eq('2019-02-14'.to_date) expect(bt.text).to eq('superbank AG') expect(bt.iban).to be_nil - expect(bt.reference).to eq("Ãœberweisung US, Wechselspesen u Provision") + expect(bt.reference).to eq('Ãœberweisung US, Wechselspesen u Provision') expect(bt.receipt).to eq('Spesen/Gebühren') end @@ -384,7 +384,7 @@ describe BankTransaction do expect(bank_account.last_transaction_date).to eq('2020-01-01'.to_date) expect(bank_account.balance).to eq(22) - bt1 = bank_account.bank_transactions.find_by_external_id("T1") + bt1 = bank_account.bank_transactions.find_by_external_id('T1') expect(bt1.amount).to eq(11) expect(bt1.date).to eq('2020-01-01'.to_date) expect(bt1.text).to eq('DN1') @@ -392,7 +392,7 @@ describe BankTransaction do expect(bt1.reference).to eq('') expect(bt1.receipt).to eq('AI1') - bt2 = bank_account.bank_transactions.find_by_external_id("T2") + bt2 = bank_account.bank_transactions.find_by_external_id('T2') expect(bt2.amount).to eq(-22) expect(bt2.date).to eq('2010-02-01'.to_date) expect(bt2.text).to eq('CN2') @@ -400,7 +400,7 @@ describe BankTransaction do expect(bt2.reference).to eq('RI2') expect(bt2.receipt).to be_nil - bt3 = bank_account.bank_transactions.find_by_external_id("T3") + bt3 = bank_account.bank_transactions.find_by_external_id('T3') expect(bt3.amount).to eq(33) expect(bt3.date).to eq('2000-03-01'.to_date) expect(bt3.text).to eq('DN3') diff --git a/spec/lib/bank_transaction_reference_spec.rb b/spec/lib/bank_transaction_reference_spec.rb index a03e901b..de999906 100644 --- a/spec/lib/bank_transaction_reference_spec.rb +++ b/spec/lib/bank_transaction_reference_spec.rb @@ -34,62 +34,65 @@ describe BankTransactionReference do end it 'returns correct value for FS1A1' do - expect(BankTransactionReference.parse('FS1A1')).to match({ group: 1, parts: { "A" => 1 } }) + expect(BankTransactionReference.parse('FS1A1')).to match({ group: 1, parts: { 'A' => 1 } }) end it 'returns correct value for FS1.2A3' do - expect(BankTransactionReference.parse('FS1.2A3')).to match({ group: 1, user: 2, parts: { "A" => 3 } }) + expect(BankTransactionReference.parse('FS1.2A3')).to match({ group: 1, user: 2, parts: { 'A' => 3 } }) end it 'returns correct value for FS1A2B3C4' do - expect(BankTransactionReference.parse('FS1A2B3C4')).to match({ group: 1, parts: { "A" => 2, "B" => 3, "C" => 4 } }) + expect(BankTransactionReference.parse('FS1A2B3C4')).to match({ group: 1, parts: { 'A' => 2, 'B' => 3, 'C' => 4 } }) end it 'returns correct value for FS1A2B3A4' do - expect(BankTransactionReference.parse('FS1A2B3A4')).to match({ group: 1, parts: { "A" => 6, "B" => 3 } }) + expect(BankTransactionReference.parse('FS1A2B3A4')).to match({ group: 1, parts: { 'A' => 6, 'B' => 3 } }) end it 'returns correct value for FS1A2.34B5.67C8.90' do - expect(BankTransactionReference.parse('FS1A2.34B5.67C8.90')).to match({ group: 1, parts: { "A" => 2.34, "B" => 5.67, "C" => 8.90 } }) + expect(BankTransactionReference.parse('FS1A2.34B5.67C8.90')).to match({ group: 1, + parts: { 'A' => 2.34, 'B' => 5.67, + 'C' => 8.90 } }) end it 'returns correct value for FS123A456 with comma-separated prefix' do - expect(BankTransactionReference.parse('x,FS123A456')).to match({ group: 123, parts: { "A" => 456 } }) + expect(BankTransactionReference.parse('x,FS123A456')).to match({ group: 123, parts: { 'A' => 456 } }) end it 'returns correct value for FS123A456 with minus-separated prefix' do - expect(BankTransactionReference.parse('x-FS123A456')).to match({ group: 123, parts: { "A" => 456 } }) + expect(BankTransactionReference.parse('x-FS123A456')).to match({ group: 123, parts: { 'A' => 456 } }) end it 'returns correct value for FS123A456 with semicolon-separated prefix' do - expect(BankTransactionReference.parse('x;FS123A456')).to match({ group: 123, parts: { "A" => 456 } }) + expect(BankTransactionReference.parse('x;FS123A456')).to match({ group: 123, parts: { 'A' => 456 } }) end it 'returns correct value for FS123A456 with space-separated prefix' do - expect(BankTransactionReference.parse('x FS123A456')).to match({ group: 123, parts: { "A" => 456 } }) + expect(BankTransactionReference.parse('x FS123A456')).to match({ group: 123, parts: { 'A' => 456 } }) end it 'returns correct value for FS234A567 with comma-separated suffix' do - expect(BankTransactionReference.parse('FS234A567,x')).to match({ group: 234, parts: { "A" => 567 } }) + expect(BankTransactionReference.parse('FS234A567,x')).to match({ group: 234, parts: { 'A' => 567 } }) end it 'returns correct value for FS234A567 with minus-separated suffix' do - expect(BankTransactionReference.parse('FS234A567-x')).to match({ group: 234, parts: { "A" => 567 } }) + expect(BankTransactionReference.parse('FS234A567-x')).to match({ group: 234, parts: { 'A' => 567 } }) end it 'returns correct value for FS234A567 with space-separated suffix' do - expect(BankTransactionReference.parse('FS234A567 x')).to match({ group: 234, parts: { "A" => 567 } }) + expect(BankTransactionReference.parse('FS234A567 x')).to match({ group: 234, parts: { 'A' => 567 } }) end it 'returns correct value for FS234A567 with semicolon-separated suffix' do - expect(BankTransactionReference.parse('FS234A567;x')).to match({ group: 234, parts: { "A" => 567 } }) + expect(BankTransactionReference.parse('FS234A567;x')).to match({ group: 234, parts: { 'A' => 567 } }) end it 'returns correct value for FS234A567 with minus-separated suffix' do - expect(BankTransactionReference.parse('FS234A567-x')).to match({ group: 234, parts: { "A" => 567 } }) + expect(BankTransactionReference.parse('FS234A567-x')).to match({ group: 234, parts: { 'A' => 567 } }) end it 'returns correct value for FS34.56A67.89 with prefix and suffix' do - expect(BankTransactionReference.parse('prefix FS34.56A67.89, suffix')).to match({ group: 34, user: 56, parts: { "A" => 67.89 } }) + expect(BankTransactionReference.parse('prefix FS34.56A67.89, suffix')).to match({ group: 34, user: 56, + parts: { 'A' => 67.89 } }) end end diff --git a/spec/lib/foodsoft_mail_receiver_spec.rb b/spec/lib/foodsoft_mail_receiver_spec.rb index 59eab47b..47ddb57d 100644 --- a/spec/lib/foodsoft_mail_receiver_spec.rb +++ b/spec/lib/foodsoft_mail_receiver_spec.rb @@ -6,55 +6,6 @@ describe FoodsoftMailReceiver do @server.start end - it 'does not accept empty addresses' do - begin - FoodsoftMailReceiver.received('', 'body') - rescue => error - expect(error.to_s).to include 'missing' - end - end - - it 'does not accept invalid addresses' do - begin - FoodsoftMailReceiver.received('invalid', 'body') - rescue => error - expect(error.to_s).to include 'has an invalid format' - end - end - - it 'does not accept invalid scope in address' do - begin - FoodsoftMailReceiver.received('invalid.invalid', 'body') - rescue => error - expect(error.to_s).to include 'could not be found' - end - end - - it 'does not accept address without handler' do - begin - address = "#{FoodsoftConfig[:default_scope]}.invalid" - FoodsoftMailReceiver.received(address, 'body') - rescue => error - expect(error.to_s).to include 'invalid format for recipient' - end - end - - it 'does not accept invalid addresses via SMTP' do - expect { - Net::SMTP.start(@server.hosts.first, @server.ports.first) do |smtp| - smtp.send_message 'body', 'from@example.com', 'invalid' - end - }.to raise_error(Net::SMTPFatalError) - end - - it 'does not accept invalid addresses via SMTP' do - expect { - Net::SMTP.start(@server.hosts.first, @server.ports.first) do |smtp| - smtp.send_message 'body', 'from@example.com', 'invalid' - end - }.to raise_error(Net::SMTPFatalError) - end - # TODO: Reanable this test. # It raised "Mysql2::Error: Lock wait timeout exceeded" at time of writing. # it 'accepts bounce mails via SMTP' do @@ -74,4 +25,45 @@ describe FoodsoftMailReceiver do after :all do @server.shutdown end + + it 'does not accept empty addresses' do + FoodsoftMailReceiver.received('', 'body') + rescue StandardError => e + expect(e.to_s).to include 'missing' + end + + it 'does not accept invalid addresses' do + FoodsoftMailReceiver.received('invalid', 'body') + rescue StandardError => e + expect(e.to_s).to include 'has an invalid format' + end + + it 'does not accept invalid scope in address' do + FoodsoftMailReceiver.received('invalid.invalid', 'body') + rescue StandardError => e + expect(e.to_s).to include 'could not be found' + end + + it 'does not accept address without handler' do + address = "#{FoodsoftConfig[:default_scope]}.invalid" + FoodsoftMailReceiver.received(address, 'body') + rescue StandardError => e + expect(e.to_s).to include 'invalid format for recipient' + end + + it 'does not accept invalid addresses via SMTP' do + expect do + Net::SMTP.start(@server.hosts.first, @server.ports.first) do |smtp| + smtp.send_message 'body', 'from@example.com', 'invalid' + end + end.to raise_error(Net::SMTPFatalError) + end + + it 'does not accept invalid addresses via SMTP' do + expect do + Net::SMTP.start(@server.hosts.first, @server.ports.first) do |smtp| + smtp.send_message 'body', 'from@example.com', 'invalid' + end + end.to raise_error(Net::SMTPFatalError) + end end diff --git a/spec/lib/token_verifier_spec.rb b/spec/lib/token_verifier_spec.rb index d4a7e5ea..3097e888 100644 --- a/spec/lib/token_verifier_spec.rb +++ b/spec/lib/token_verifier_spec.rb @@ -6,12 +6,12 @@ describe TokenVerifier do let(:msg) { v.generate } it 'validates' do - expect { v.verify(msg) }.to_not raise_error + expect { v.verify(msg) }.not_to raise_error end it 'validates when recreated' do v2 = TokenVerifier.new(prefix) - expect { v2.verify(msg) }.to_not raise_error + expect { v2.verify(msg) }.not_to raise_error end it 'does not validate with a different prefix' do @@ -32,7 +32,9 @@ describe TokenVerifier do end it 'does not validate a random string' do - expect { v.verify(Faker::Lorem.characters(number: 100)) }.to raise_error(ActiveSupport::MessageVerifier::InvalidSignature) + expect do + v.verify(Faker::Lorem.characters(number: 100)) + end.to raise_error(ActiveSupport::MessageVerifier::InvalidSignature) end it 'returns the message' do diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 519f5b64..3a810827 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -1,11 +1,11 @@ require_relative '../spec_helper' describe Article do - let(:supplier) { create :supplier } - let(:article) { create :article, supplier: supplier } + let(:supplier) { create(:supplier) } + let(:article) { create(:article, supplier: supplier) } it 'has a unique name' do - article2 = build :article, supplier: supplier, name: article.name + article2 = build(:article, supplier: supplier, name: article.name) expect(article2).to be_invalid end @@ -21,21 +21,33 @@ describe Article do end it 'returns false when invalid unit' do - article1 = build :article, supplier: supplier, unit: 'invalid' + article1 = build(:article, supplier: supplier, unit: 'invalid') expect(article.convert_units(article1)).to be false end + it 'returns false if unit = 0' do + article1 = build(:article, supplier: supplier, unit: '1kg', price: 2, unit_quantity: 1) + article2 = build(:article, supplier: supplier, unit: '0kg', price: 2, unit_quantity: 1) + expect(article1.convert_units(article2)).to be false + end + + it 'returns false if unit becomes zero because of , symbol in unit format' do + article1 = build(:article, supplier: supplier, unit: '0,8kg', price: 2, unit_quantity: 1) + article2 = build(:article, supplier: supplier, unit: '0,9kg', price: 2, unit_quantity: 1) + expect(article1.convert_units(article2)).to be false + end + it 'converts from ST to KI (german foodcoops legacy)' do - article1 = build :article, supplier: supplier, unit: 'ST' - article2 = build :article, supplier: supplier, name: 'banana 10-12 St', price: 12.34, unit: 'KI' + article1 = build(:article, supplier: supplier, unit: 'ST') + article2 = build(:article, supplier: supplier, name: 'banana 10-12 St', price: 12.34, unit: 'KI') new_price, new_unit_quantity = article1.convert_units(article2) expect(new_unit_quantity).to eq 10 expect(new_price).to eq 1.23 end it 'converts from g to kg' do - article1 = build :article, supplier: supplier, unit: 'kg' - article2 = build :article, supplier: supplier, unit: 'g', price: 0.12, unit_quantity: 1500 + article1 = build(:article, supplier: supplier, unit: 'kg') + article2 = build(:article, supplier: supplier, unit: 'g', price: 0.12, unit_quantity: 1500) new_price, new_unit_quantity = article1.convert_units(article2) expect(new_unit_quantity).to eq 1.5 expect(new_price).to eq 120 @@ -43,7 +55,7 @@ describe Article do end it 'computes changed article attributes' do - article2 = build :article, supplier: supplier, name: 'banana' + article2 = build(:article, supplier: supplier, name: 'banana') expect(article.unequal_attributes(article2)[:name]).to eq 'banana' end @@ -59,10 +71,16 @@ describe Article do expect(article.gross_price).to be >= article.price end - it 'computes the fc price correctly' do - expect(article.fc_price).to eq((article.gross_price * 1.05).round(2)) + [[nil, 1], + [0, 1], + [5, 1.05], + [42, 1.42], + [100, 2]].each do |price_markup, percent| + it "computes the fc price with price_markup #{price_markup} correctly" do + FoodsoftConfig.config['price_markup'] = price_markup + expect(article.fc_price).to eq((article.gross_price * percent).round(2)) + end end - it 'knows when it is deleted' do expect(supplier.deleted?).to be false supplier.mark_as_deleted @@ -83,7 +101,7 @@ describe Article do end it 'is knows its open order' do - order = create :order, supplier: supplier, article_ids: [article.id] + order = create(:order, supplier: supplier, article_ids: [article.id]) expect(article.in_open_order).to eq(order) end @@ -91,26 +109,26 @@ describe Article do expect(article.shared_article).to be_nil end - describe 'connected to a shared database', :type => :feature do - let(:shared_article) { create :shared_article } - let(:supplier) { create :supplier, shared_supplier_id: shared_article.supplier_id } - let(:article) { create :article, supplier: supplier, order_number: shared_article.order_number } + describe 'connected to a shared database', type: :feature do + let(:shared_article) { create(:shared_article) } + let(:supplier) { create(:supplier, shared_supplier_id: shared_article.supplier_id) } + let(:article) { create(:article, supplier: supplier, order_number: shared_article.order_number) } it 'can be found in the shared database' do - expect(article.shared_article).to_not be_nil + expect(article.shared_article).not_to be_nil end it 'can find updates' do changed = article.shared_article_changed? - expect(changed).to_not be_falsey + expect(changed).not_to be_falsey expect(changed.length).to be > 1 end it 'can be synchronised' do - # TODO move article sync from supplier to article + # TODO: move article sync from supplier to article article # need to reference for it to exist when syncing updated_article = supplier.sync_all[0].select { |s| s[0].id == article.id }.first[0] - article.update(updated_article.attributes.reject { |k, v| k == 'id' or k == 'type' }) + article.update(updated_article.attributes.reject { |k, _v| %w[id type].include?(k) }) expect(article.name).to eq(shared_article.name) # now synchronising shouldn't change anything anymore expect(article.shared_article_changed?).to be_falsey @@ -131,9 +149,9 @@ describe Article do article.unit = '200g' article.shared_updated_on -= 1 # to make update do something article.save! - # TODO get sync functionality in article + # TODO: get sync functionality in article updated_article = supplier.sync_all[0].select { |s| s[0].id == article.id }.first[0] - article.update!(updated_article.attributes.reject { |k, v| k == 'id' or k == 'type' }) + article.update!(updated_article.attributes.reject { |k, _v| %w[id type].include?(k) }) expect(article.unit).to eq '200g' expect(article.unit_quantity).to eq 5 expect(article.price).to be_within(0.005).of(shared_article.price / 5) diff --git a/spec/models/bank_transaction_spec.rb b/spec/models/bank_transaction_spec.rb index 21d6185e..14172676 100644 --- a/spec/models/bank_transaction_spec.rb +++ b/spec/models/bank_transaction_spec.rb @@ -1,24 +1,32 @@ require_relative '../spec_helper' describe BankTransaction do - let(:bank_account) { create :bank_account } - let(:ordergroup) { create :ordergroup } - let(:supplier) { create :supplier, iban: Faker::Bank.iban } - let!(:user) { create :user, groups: [ordergroup] } - let!(:ftt_a) { create :financial_transaction_type, name_short: 'A' } - let!(:ftt_b) { create :financial_transaction_type, name_short: 'B' } + let(:bank_account) { create(:bank_account) } + let(:ordergroup) { create(:ordergroup) } + let(:supplier) { create(:supplier, iban: Faker::Bank.iban) } + let!(:user) { create(:user, groups: [ordergroup]) } + let!(:ftt_a) { create(:financial_transaction_type, name_short: 'A') } + let!(:ftt_b) { create(:financial_transaction_type, name_short: 'B') } describe 'supplier' do - let!(:invoice1) { create :invoice, supplier: supplier, number: '11', amount: 10 } - let!(:invoice2) { create :invoice, supplier: supplier, number: '22', amount: 20 } - let!(:invoice3) { create :invoice, supplier: supplier, number: '33', amount: 30 } - let!(:invoice4) { create :invoice, supplier: supplier, number: '44', amount: 40 } - let!(:invoice5) { create :invoice, supplier: supplier, number: '55', amount: 50 } + let!(:invoice1) { create(:invoice, supplier: supplier, number: '11', amount: 10) } + let!(:invoice2) { create(:invoice, supplier: supplier, number: '22', amount: 20) } + let!(:invoice3) { create(:invoice, supplier: supplier, number: '33', amount: 30) } + let!(:invoice4) { create(:invoice, supplier: supplier, number: '44', amount: 40) } + let!(:invoice5) { create(:invoice, supplier: supplier, number: '55', amount: 50) } - let!(:bank_transaction1) { create :bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '11', amount: 10 } - let!(:bank_transaction2) { create :bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '22', amount: -20 } - let!(:bank_transaction3) { create :bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '33,44', amount: -70 } - let!(:bank_transaction4) { create :bank_transaction, bank_account: bank_account, iban: supplier.iban, text: '55', amount: -50 } + let!(:bank_transaction1) do + create(:bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '11', amount: 10) + end + let!(:bank_transaction2) do + create(:bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '22', amount: -20) + end + let!(:bank_transaction3) do + create(:bank_transaction, bank_account: bank_account, iban: supplier.iban, reference: '33,44', amount: -70) + end + let!(:bank_transaction4) do + create(:bank_transaction, bank_account: bank_account, iban: supplier.iban, text: '55', amount: -50) + end it 'ignores invoices with invalid amount' do expect(bank_transaction1.assign_to_invoice).to be false @@ -49,14 +57,26 @@ describe BankTransaction do end describe 'ordergroup' do - let!(:bank_transaction1) { create :bank_transaction, bank_account: bank_account, reference: "invalid", amount: 10 } - let!(:bank_transaction2) { create :bank_transaction, bank_account: bank_account, reference: "FS99A10", amount: 10 } - let!(:bank_transaction3) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}.99A10", amount: 10 } - let!(:bank_transaction4) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10", amount: 99 } - let!(:bank_transaction5) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10", amount: 10 } - let!(:bank_transaction6) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10B20", amount: 30 } - let!(:bank_transaction7) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}.#{user.id}A10", amount: 10 } - let!(:bank_transaction8) { create :bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}X10", amount: 10 } + let!(:bank_transaction1) { create(:bank_transaction, bank_account: bank_account, reference: 'invalid', amount: 10) } + let!(:bank_transaction2) { create(:bank_transaction, bank_account: bank_account, reference: 'FS99A10', amount: 10) } + let!(:bank_transaction3) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}.99A10", amount: 10) + end + let!(:bank_transaction4) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10", amount: 99) + end + let!(:bank_transaction5) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10", amount: 10) + end + let!(:bank_transaction6) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}A10B20", amount: 30) + end + let!(:bank_transaction7) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}.#{user.id}A10", amount: 10) + end + let!(:bank_transaction8) do + create(:bank_transaction, bank_account: bank_account, reference: "FS#{ordergroup.id}X10", amount: 10) + end it 'ignores transaction with invalid reference' do expect(bank_transaction1.assign_to_ordergroup).to be_nil diff --git a/spec/models/delivery_spec.rb b/spec/models/delivery_spec.rb index b48449ab..80f4c2ed 100644 --- a/spec/models/delivery_spec.rb +++ b/spec/models/delivery_spec.rb @@ -1,8 +1,8 @@ require_relative '../spec_helper' describe Delivery do - let(:delivery) { create :delivery } - let(:stock_article) { create :stock_article, price: 3 } + let(:delivery) { create(:delivery) } + let(:stock_article) { create(:stock_article, price: 3) } it 'creates new stock_changes' do delivery.new_stock_changes = ([ diff --git a/spec/models/group_order_article_spec.rb b/spec/models/group_order_article_spec.rb index 42178a6c..434f9a29 100644 --- a/spec/models/group_order_article_spec.rb +++ b/spec/models/group_order_article_spec.rb @@ -1,10 +1,10 @@ require_relative '../spec_helper' describe GroupOrderArticle do - let(:user) { create :user, groups: [create(:ordergroup)] } + let(:user) { create(:user, groups: [create(:ordergroup)]) } let(:order) { create(:order) } - let(:go) { create :group_order, order: order, ordergroup: user.ordergroup } - let(:goa) { create :group_order_article, group_order: go, order_article: order.order_articles.first } + let(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } + let(:goa) { create(:group_order_article, group_order: go, order_article: order.order_articles.first) } it 'has zero quantity by default' do expect(goa.quantity).to eq(0) end it 'has zero tolerance by default' do expect(goa.tolerance).to eq(0) end @@ -12,9 +12,9 @@ describe GroupOrderArticle do it 'has zero total price by default' do expect(goa.total_price).to eq(0) end describe do - let(:article) { create :article, supplier: order.supplier, unit_quantity: 1 } - let(:oa) { order.order_articles.create(:article => article) } - let(:goa) { create :group_order_article, group_order: go, order_article: oa } + let(:article) { create(:article, supplier: order.supplier, unit_quantity: 1) } + let(:oa) { order.order_articles.create(article: article) } + let(:goa) { create(:group_order_article, group_order: go, order_article: oa) } it 'can be ordered by piece' do goa.update_quantities(1, 0) @@ -23,7 +23,8 @@ describe GroupOrderArticle do end it 'can be ordered in larger amounts' do - quantity, tolerance = rand(13..99), rand(0..99) + quantity = rand(13..99) + tolerance = rand(0..99) goa.update_quantities(quantity, tolerance) expect(goa.quantity).to eq(quantity) expect(goa.tolerance).to eq(tolerance) @@ -52,10 +53,10 @@ describe GroupOrderArticle do end describe 'distribution strategy' do - let(:article) { create :article, supplier: order.supplier, unit_quantity: 1 } - let(:oa) { order.order_articles.create(:article => article) } - let(:goa) { create :group_order_article, group_order: go, order_article: oa } - let!(:goaq) { create :group_order_article_quantity, group_order_article: goa, quantity: 4, tolerance: 6 } + let(:article) { create(:article, supplier: order.supplier, unit_quantity: 1) } + let(:oa) { order.order_articles.create(article: article) } + let(:goa) { create(:group_order_article, group_order: go, order_article: oa) } + let!(:goaq) { create(:group_order_article_quantity, group_order_article: goa, quantity: 4, tolerance: 6) } it 'can calculate the result for the distribution strategy "first order first serve"' do res = goa.calculate_result(2) diff --git a/spec/models/group_order_spec.rb b/spec/models/group_order_spec.rb index a2b8a2c5..648fa100 100644 --- a/spec/models/group_order_spec.rb +++ b/spec/models/group_order_spec.rb @@ -1,8 +1,8 @@ require_relative '../spec_helper' describe GroupOrder do - let(:user) { create :user, groups: [create(:ordergroup)] } - let(:order) { create :order } + let(:user) { create(:user, groups: [create(:ordergroup)]) } + let(:order) { create(:order) } # the following two tests are currently disabled - https://github.com/foodcoops/foodsoft/issues/158 @@ -15,7 +15,7 @@ describe GroupOrder do # end describe do - let(:go) { create :group_order, order: order, ordergroup: user.ordergroup } + let(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } it 'has zero price initially' do expect(go.price).to eq(0) diff --git a/spec/models/order_article_spec.rb b/spec/models/order_article_spec.rb index 829678a9..cef0e09c 100644 --- a/spec/models/order_article_spec.rb +++ b/spec/models/order_article_spec.rb @@ -1,14 +1,14 @@ require 'spec_helper' describe OrderArticle do - let(:order) { create :order, article_count: 1 } + let(:order) { create(:order, article_count: 1) } let(:oa) { order.order_articles.first } it 'is not ordered by default' do expect(OrderArticle.ordered.count).to eq 0 end - [:units_to_order, :units_billed, :units_received].each do |units| + %i[units_to_order units_billed units_received].each do |units| it "is ordered when there are #{units.to_s.gsub '_', ' '}" do oa.update_attribute units, rand(1..99) expect(OrderArticle.ordered.count).to eq 1 @@ -46,15 +46,15 @@ describe OrderArticle do end describe 'redistribution' do - let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] } - let(:article) { create :article, unit_quantity: 3 } - let(:order) { create :order, article_ids: [article.id] } - let(:go1) { create :group_order, order: order } - let(:go2) { create :group_order, order: order } - let(:go3) { create :group_order, order: order } - let(:goa1) { create :group_order_article, group_order: go1, order_article: oa } - let(:goa2) { create :group_order_article, group_order: go2, order_article: oa } - let(:goa3) { create :group_order_article, group_order: go3, order_article: oa } + let(:admin) { create(:user, groups: [create(:workgroup, role_finance: true)]) } + let(:article) { create(:article, unit_quantity: 3) } + let(:order) { create(:order, article_ids: [article.id]) } + let(:go1) { create(:group_order, order: order) } + let(:go2) { create(:group_order, order: order) } + let(:go3) { create(:group_order, order: order) } + let(:goa1) { create(:group_order_article, group_order: go1, order_article: oa) } + let(:goa2) { create(:group_order_article, group_order: go2, order_article: oa) } + let(:goa3) { create(:group_order_article, group_order: go3, order_article: oa) } # set quantities of group_order_articles def set_quantities(q1, q2, q3) @@ -79,21 +79,21 @@ describe OrderArticle do it 'does nothing when nothing has changed' do set_quantities [3, 2], [1, 3], [1, 0] - expect(oa.redistribute 6, [:tolerance, nil]).to eq [1, 0] + expect(oa.redistribute(6, [:tolerance, nil])).to eq [1, 0] goa_reload expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [4, 1, 1] end it 'works when there is nothing to distribute' do set_quantities [3, 2], [1, 3], [1, 0] - expect(oa.redistribute 0, [:tolerance, nil]).to eq [0, 0] + expect(oa.redistribute(0, [:tolerance, nil])).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [0, 0, 0] end it 'works when quantity needs to be reduced' do set_quantities [3, 2], [1, 3], [1, 0] - expect(oa.redistribute 4, [:tolerance, nil]).to eq [0, 0] + expect(oa.redistribute(4, [:tolerance, nil])).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 0] end @@ -101,28 +101,28 @@ describe OrderArticle do it 'works when quantity is increased within quantity' do set_quantities [3, 0], [2, 0], [2, 0] expect([goa1, goa2, goa3].map(&:result)).to eq [3, 2, 1] - expect(oa.redistribute 7, [:tolerance, nil]).to eq [0, 0] + expect(oa.redistribute(7, [:tolerance, nil])).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result).map(&:to_i)).to eq [3, 2, 2] end it 'works when there is just one for the first' do set_quantities [3, 2], [1, 3], [1, 0] - expect(oa.redistribute 1, [:tolerance, nil]).to eq [0, 0] + expect(oa.redistribute(1, [:tolerance, nil])).to eq [0, 0] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [1, 0, 0] end it 'works when there is tolerance and left-over' do set_quantities [3, 2], [1, 1], [1, 0] - expect(oa.redistribute 10, [:tolerance, nil]).to eq [3, 2] + expect(oa.redistribute(10, [:tolerance, nil])).to eq [3, 2] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [5, 2, 1] end it 'works when redistributing without tolerance' do set_quantities [3, 2], [1, 3], [1, 0] - expect(oa.redistribute 8, [nil]).to eq [3] + expect(oa.redistribute(8, [nil])).to eq [3] goa_reload expect([goa1, goa2, goa3].map(&:result)).to eq [3, 1, 1] end @@ -131,17 +131,18 @@ describe OrderArticle do describe 'boxfill' do before { FoodsoftConfig[:use_boxfill] = true } - let(:article) { create :article, unit_quantity: 6 } - let(:order) { create :order, article_ids: [article.id], starts: 1.week.ago } + let(:article) { create(:article, unit_quantity: 6) } + let(:order) { create(:order, article_ids: [article.id], starts: 1.week.ago) } let(:oa) { order.order_articles.first } - let(:go) { create :group_order, order: order } - let(:goa) { create :group_order_article, group_order: go, order_article: oa } + let(:go) { create(:group_order, order: order) } + let(:goa) { create(:group_order_article, group_order: go, order_article: oa) } - shared_examples "boxfill" do |success, q| + shared_examples 'boxfill' do |success, q| # initial situation before do goa.update_quantities(*q.keys[0]) - oa.update_results!; oa.reload + oa.update_results! + oa.reload end # check starting condition @@ -172,11 +173,11 @@ describe OrderArticle do let(:boxfill_from) { 1.hour.from_now } context 'decreasing the missing units' do - include_examples "boxfill", true, [6, 0] => [5, 0], [6, 0, 0] => [5, 0, 1] + include_examples 'boxfill', true, [6, 0] => [5, 0], [6, 0, 0] => [5, 0, 1] end context 'decreasing the tolerance' do - include_examples "boxfill", true, [1, 2] => [1, 1], [1, 2, 3] => [1, 1, 4] + include_examples 'boxfill', true, [1, 2] => [1, 1], [1, 2, 3] => [1, 1, 4] end end @@ -184,27 +185,27 @@ describe OrderArticle do let(:boxfill_from) { 1.second.ago } context 'changing nothing in particular' do - include_examples "boxfill", true, [4, 1] => [4, 1], [4, 1, 1] => [4, 1, 1] + include_examples 'boxfill', true, [4, 1] => [4, 1], [4, 1, 1] => [4, 1, 1] end context 'increasing missing units' do - include_examples "boxfill", false, [3, 0] => [2, 0], [3, 0, 3] => [3, 0, 3] + include_examples 'boxfill', false, [3, 0] => [2, 0], [3, 0, 3] => [3, 0, 3] end context 'increasing tolerance' do - include_examples "boxfill", true, [2, 1] => [2, 2], [2, 1, 3] => [2, 2, 2] + include_examples 'boxfill', true, [2, 1] => [2, 2], [2, 1, 3] => [2, 2, 2] end context 'decreasing quantity to fix missing units' do - include_examples "boxfill", true, [7, 0] => [6, 0], [7, 0, 5] => [6, 0, 0] + include_examples 'boxfill', true, [7, 0] => [6, 0], [7, 0, 5] => [6, 0, 0] end context 'decreasing quantity keeping missing units equal' do - include_examples "boxfill", false, [7, 0] => [1, 0], [7, 0, 5] => [7, 0, 5] + include_examples 'boxfill', false, [7, 0] => [1, 0], [7, 0, 5] => [7, 0, 5] end context 'moving tolerance to quantity' do - include_examples "boxfill", true, [4, 2] => [6, 0], [4, 2, 0] => [6, 0, 0] + include_examples 'boxfill', true, [4, 2] => [6, 0], [4, 2, 0] => [6, 0, 0] end # @todo enable test when tolerance doesn't count in missing_units # context 'decreasing tolerance' do diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index aee48f33..71c46a84 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -1,14 +1,14 @@ require_relative '../spec_helper' describe Order do - let!(:ftt) { create :financial_transaction_type } - let(:user) { create :user, groups: [create(:ordergroup)] } + let!(:ftt) { create(:financial_transaction_type) } + let(:user) { create(:user, groups: [create(:ordergroup)]) } it 'automaticly finishes ended' do - create :order, created_by: user, starts: Date.yesterday, ends: 1.hour.from_now - create :order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago - create :order, created_by: user, starts: Date.yesterday, ends: 1.hour.from_now, end_action: :auto_close - order = create :order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close + create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.from_now) + create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago) + create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.from_now, end_action: :auto_close) + order = create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close) Order.finish_ended! order.reload @@ -19,10 +19,10 @@ describe Order do end describe 'state scopes and boolean getters' do - let!(:open_order) { create :order, state: 'open' } - let!(:finished_order) { create :order, state: 'finished' } - let!(:received_order) { create :order, state: 'received' } - let!(:closed_order) { create :order, state: 'closed' } + let!(:open_order) { create(:order, state: 'open') } + let!(:finished_order) { create(:order, state: 'finished') } + let!(:received_order) { create(:order, state: 'received') } + let!(:closed_order) { create(:order, state: 'closed') } it 'retrieves open orders in the "open" scope' do expect(Order.open.count).to eq(1) @@ -72,8 +72,9 @@ describe Order do end it 'sends mail if min_order_quantity has been reached' do - create :user, groups: [create(:ordergroup)] - create :order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close_and_send_min_quantity + create(:user, groups: [create(:ordergroup)]) + create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, + end_action: :auto_close_and_send_min_quantity) Order.finish_ended! expect(ActionMailer::Base.deliveries.count).to eq 1 @@ -84,7 +85,7 @@ describe Order do end it 'needs order articles' do - supplier = create :supplier, article_count: 0 + supplier = create(:supplier, article_count: 0) expect(build(:order, supplier: supplier)).to be_invalid end @@ -93,35 +94,35 @@ describe Order do end describe 'with articles' do - let(:order) { create :order } + let(:order) { create(:order) } it 'is open by default' do expect(order).to be_open end - it 'is not finished by default' do expect(order).to_not be_finished end - it 'is not closed by default' do expect(order).to_not be_closed end + it 'is not finished by default' do expect(order).not_to be_finished end + it 'is not closed by default' do expect(order).not_to be_closed end it 'has valid order articles' do order.order_articles.each { |oa| expect(oa).to be_valid } end it 'can be finished' do - # TODO randomise user + # TODO: randomise user order.finish!(user) - expect(order).to_not be_open + expect(order).not_to be_open expect(order).to be_finished - expect(order).to_not be_closed + expect(order).not_to be_closed end it 'can be closed' do - # TODO randomise user + # TODO: randomise user order.finish!(user) order.close!(user) - expect(order).to_not be_open + expect(order).not_to be_open expect(order).to be_closed end end describe 'with a default end date' do - let(:order) { create :order } + let(:order) { create(:order) } before do FoodsoftConfig[:order_schedule] = { ends: { recurr: 'FREQ=WEEKLY;BYDAY=MO', time: '9:00' } } @@ -138,10 +139,10 @@ describe Order do end describe 'mapped to GroupOrders' do - let!(:user) { create :user, groups: [create(:ordergroup)] } - let!(:order) { create :order } - let!(:order2) { create :order } - let!(:go) { create :group_order, order: order, ordergroup: user.ordergroup } + let!(:user) { create(:user, groups: [create(:ordergroup)]) } + let!(:order) { create(:order) } + let!(:order2) { create(:order) } + let!(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } it 'to map a user\'s GroupOrders to a list of Orders' do orders = Order.ordergroup_group_orders_map(user.ordergroup) @@ -156,10 +157,10 @@ describe Order do describe 'balancing charges correct amounts' do let!(:transport) { rand(0.1..26.0).round(2) } - let!(:order) { create :order, article_count: 1 } + let!(:order) { create(:order, article_count: 1) } let!(:oa) { order.order_articles.first } - let!(:go) { create :group_order, order: order, transport: transport } - let!(:goa) { create :group_order_article, group_order: go, order_article: oa, quantity: 1 } + let!(:go) { create(:group_order, order: order, transport: transport) } + let!(:goa) { create(:group_order_article, group_order: go, order_article: oa, quantity: 1) } before do goa.update_quantities(1, 0) diff --git a/spec/models/ordergroup_spec.rb b/spec/models/ordergroup_spec.rb index a7f0c94a..cdac353d 100644 --- a/spec/models/ordergroup_spec.rb +++ b/spec/models/ordergroup_spec.rb @@ -1,22 +1,22 @@ require_relative '../spec_helper' describe Ordergroup do - let(:ftc1) { create :financial_transaction_class } - let(:ftc2) { create :financial_transaction_class } - let(:ftt1) { create :financial_transaction_type, financial_transaction_class: ftc1 } - let(:ftt2) { create :financial_transaction_type, financial_transaction_class: ftc2 } - let(:ftt3) { create :financial_transaction_type, financial_transaction_class: ftc2 } - let(:user) { create :user, groups: [create(:ordergroup)] } + let(:ftc1) { create(:financial_transaction_class) } + let(:ftc2) { create(:financial_transaction_class) } + let(:ftt1) { create(:financial_transaction_type, financial_transaction_class: ftc1) } + let(:ftt2) { create(:financial_transaction_type, financial_transaction_class: ftc2) } + let(:ftt3) { create(:financial_transaction_type, financial_transaction_class: ftc2) } + let(:user) { create(:user, groups: [create(:ordergroup)]) } it 'shows no active ordergroups when all orders are older than 3 months' do - order = create :order, starts: 4.months.ago + order = create(:order, starts: 4.months.ago) user.ordergroup.group_orders.create!(order: order) expect(Ordergroup.active).to be_empty end it 'shows active ordergroups when there are recent orders' do - order = create :order, starts: 2.days.ago + order = create(:order, starts: 2.days.ago) user.ordergroup.group_orders.create!(order: order) expect(Ordergroup.active).not_to be_empty @@ -24,17 +24,17 @@ describe Ordergroup do describe 'sort correctly' do it 'by name' do - group_b = create :ordergroup, name: 'bbb' - group_a = create :ordergroup, name: 'aaa' - group_c = create :ordergroup, name: 'ccc' + group_b = create(:ordergroup, name: 'bbb') + group_a = create(:ordergroup, name: 'aaa') + group_c = create(:ordergroup, name: 'ccc') expect(Ordergroup.sort_by_param('name')).to eq([group_a, group_b, group_c]) end it 'reverse by name' do - group_b = create :ordergroup, name: 'bbb' - group_a = create :ordergroup, name: 'aaa' - group_c = create :ordergroup, name: 'ccc' + group_b = create(:ordergroup, name: 'bbb') + group_a = create(:ordergroup, name: 'aaa') + group_c = create(:ordergroup, name: 'ccc') expect(Ordergroup.sort_by_param('name_reverse')).to eq([group_c, group_b, group_a]) end @@ -43,9 +43,9 @@ describe Ordergroup do users_b = [create(:user)] users_a = [] users_c = [create(:user), create(:user), create(:user)] - group_b = create :ordergroup, name: 'bbb', user_ids: users_b.map(&:id) - group_a = create :ordergroup, name: 'aaa', user_ids: users_a.map(&:id) - group_c = create :ordergroup, name: 'ccc', user_ids: users_c.map(&:id) + group_b = create(:ordergroup, name: 'bbb', user_ids: users_b.map(&:id)) + group_a = create(:ordergroup, name: 'aaa', user_ids: users_a.map(&:id)) + group_c = create(:ordergroup, name: 'ccc', user_ids: users_c.map(&:id)) expect(Ordergroup.sort_by_param('members_count')).to eq([group_a, group_b, group_c]) end @@ -54,39 +54,39 @@ describe Ordergroup do users_b = [create(:user)] users_a = [] users_c = [create(:user), create(:user), create(:user)] - group_b = create :ordergroup, name: 'bbb', user_ids: users_b.map(&:id) - group_a = create :ordergroup, name: 'aaa', user_ids: users_a.map(&:id) - group_c = create :ordergroup, name: 'ccc', user_ids: users_c.map(&:id) + group_b = create(:ordergroup, name: 'bbb', user_ids: users_b.map(&:id)) + group_a = create(:ordergroup, name: 'aaa', user_ids: users_a.map(&:id)) + group_c = create(:ordergroup, name: 'ccc', user_ids: users_c.map(&:id)) expect(Ordergroup.sort_by_param('members_count_reverse')).to eq([group_c, group_b, group_a]) end it 'by last_user_activity' do - user_b = create :user, last_activity: 3.days.ago - user_a = create :user, last_activity: 5.days.ago - user_c = create :user, last_activity: Time.now - group_b = create :ordergroup, name: 'bbb', user_ids: [user_b.id] - group_a = create :ordergroup, name: 'aaa', user_ids: [user_a.id] - group_c = create :ordergroup, name: 'ccc', user_ids: [user_c.id] + user_b = create(:user, last_activity: 3.days.ago) + user_a = create(:user, last_activity: 5.days.ago) + user_c = create(:user, last_activity: Time.now) + group_b = create(:ordergroup, name: 'bbb', user_ids: [user_b.id]) + group_a = create(:ordergroup, name: 'aaa', user_ids: [user_a.id]) + group_c = create(:ordergroup, name: 'ccc', user_ids: [user_c.id]) expect(Ordergroup.sort_by_param('last_user_activity')).to eq([group_a, group_b, group_c]) end it 'reverse by last_user_activity' do - user_b = create :user, last_activity: 3.days.ago - user_a = create :user, last_activity: 5.days.ago - user_c = create :user, last_activity: Time.now - group_b = create :ordergroup, name: 'bbb', user_ids: [user_b.id] - group_a = create :ordergroup, name: 'aaa', user_ids: [user_a.id] - group_c = create :ordergroup, name: 'ccc', user_ids: [user_c.id] + user_b = create(:user, last_activity: 3.days.ago) + user_a = create(:user, last_activity: 5.days.ago) + user_c = create(:user, last_activity: Time.now) + group_b = create(:ordergroup, name: 'bbb', user_ids: [user_b.id]) + group_a = create(:ordergroup, name: 'aaa', user_ids: [user_a.id]) + group_c = create(:ordergroup, name: 'ccc', user_ids: [user_c.id]) expect(Ordergroup.sort_by_param('last_user_activity_reverse')).to eq([group_c, group_b, group_a]) end it 'by last_order' do - group_b = create :ordergroup, name: 'bbb' - group_a = create :ordergroup, name: 'aaa' - group_c = create :ordergroup, name: 'ccc' + group_b = create(:ordergroup, name: 'bbb') + group_a = create(:ordergroup, name: 'aaa') + group_c = create(:ordergroup, name: 'ccc') group_b.group_orders.create! order: create(:order, starts: 6.days.ago) group_a.group_orders.create! order: create(:order, starts: 4.months.ago) group_c.group_orders.create! order: create(:order, starts: Time.now) @@ -95,9 +95,9 @@ describe Ordergroup do end it 'reverse by last_order' do - group_b = create :ordergroup, name: 'bbb' - group_a = create :ordergroup, name: 'aaa' - group_c = create :ordergroup, name: 'ccc' + group_b = create(:ordergroup, name: 'bbb') + group_a = create(:ordergroup, name: 'aaa') + group_c = create(:ordergroup, name: 'ccc') group_b.group_orders.create! order: create(:order, starts: 6.days.ago) group_a.group_orders.create! order: create(:order, starts: 4.months.ago) group_c.group_orders.create! order: create(:order, starts: Time.now) diff --git a/spec/models/supplier_spec.rb b/spec/models/supplier_spec.rb index 70ba6def..5216b8e9 100644 --- a/spec/models/supplier_spec.rb +++ b/spec/models/supplier_spec.rb @@ -1,17 +1,19 @@ require_relative '../spec_helper' describe Supplier do - let(:supplier) { create :supplier } + let(:supplier) { create(:supplier) } context 'syncs from file' do it 'imports and updates articles' do - article1 = create(:article, supplier: supplier, order_number: 177813, unit: '250 g', price: 0.1) - article2 = create(:article, supplier: supplier, order_number: 12345) + article1 = create(:article, supplier: supplier, order_number: 177_813, unit: '250 g', price: 0.1) + article2 = create(:article, supplier: supplier, order_number: 12_345) supplier.articles = [article1, article2] options = { filename: 'foodsoft_file_01.csv' } options[:outlist_absent] = true options[:convert_units] = true - updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), options) + updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file( + Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), options + ) expect(new_articles.length).to be > 0 expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten' expect(outlisted_articles.first).to eq article2 @@ -19,14 +21,16 @@ describe Supplier do end it 'return correct tolerance' do - supplier = create :supplier, articles: create_list(:article, 1, unit_quantity: 1) + supplier = create(:supplier) + supplier.articles = create_list(:article, 1, unit_quantity: 1) expect(supplier.has_tolerance?).to be false - supplier2 = create :supplier, articles: create_list(:article, 1, unit_quantity: 2) + supplier2 = create(:supplier) + supplier2.articles = create_list(:article, 1, unit_quantity: 2) expect(supplier2.has_tolerance?).to be true end it 'deletes the supplier and its articles' do - supplier = create :supplier, article_count: 3 + supplier = create(:supplier, article_count: 3) supplier.articles.each { |a| allow(a).to receive(:mark_as_deleted) } supplier.mark_as_deleted supplier.articles.each { |a| expect(a).to have_received(:mark_as_deleted) } @@ -34,29 +38,29 @@ describe Supplier do end it 'has a unique name' do - supplier2 = build :supplier, name: supplier.name + supplier2 = build(:supplier, name: supplier.name) expect(supplier2).to be_invalid end it 'has valid articles' do - supplier = create :supplier, article_count: true + supplier = create(:supplier, article_count: true) supplier.articles.each { |a| expect(a).to be_valid } end context 'connected to a shared supplier' do let(:shared_sync_method) { nil } - let(:shared_supplier) { create :shared_supplier } - let(:supplier) { create :supplier, shared_supplier: shared_supplier, shared_sync_method: shared_sync_method } + let(:shared_supplier) { create(:shared_supplier) } + let(:supplier) { create(:supplier, shared_supplier: shared_supplier, shared_sync_method: shared_sync_method) } - let!(:synced_shared_article) { create :shared_article, shared_supplier: shared_supplier } - let!(:updated_shared_article) { create :shared_article, shared_supplier: shared_supplier } - let!(:new_shared_article) { create :shared_article, shared_supplier: shared_supplier } + let!(:synced_shared_article) { create(:shared_article, shared_supplier: shared_supplier) } + let!(:updated_shared_article) { create(:shared_article, shared_supplier: shared_supplier) } + let!(:new_shared_article) { create(:shared_article, shared_supplier: shared_supplier) } - let!(:removed_article) { create :article, supplier: supplier, order_number: '10001-ABC' } + let!(:removed_article) { create(:article, supplier: supplier, order_number: '10001-ABC') } let!(:updated_article) do updated_shared_article.build_new_article(supplier).tap do |article| article.article_category = create :article_category - article.origin = "FubarX1" + article.origin = 'FubarX1' article.shared_updated_on = 1.day.ago article.save! end @@ -75,7 +79,7 @@ describe Supplier do it 'returns the expected articles' do updated_article_pairs, outlisted_articles, new_articles = supplier.sync_all - expect(updated_article_pairs).to_not be_empty + expect(updated_article_pairs).not_to be_empty expect(updated_article_pairs[0][0].id).to eq updated_article.id expect(updated_article_pairs[0][1].keys).to include :origin @@ -91,13 +95,13 @@ describe Supplier do it 'returns the expected articles' do updated_article_pairs, outlisted_articles, new_articles = supplier.sync_all - expect(updated_article_pairs).to_not be_empty + expect(updated_article_pairs).not_to be_empty expect(updated_article_pairs[0][0].id).to eq updated_article.id expect(updated_article_pairs[0][1].keys).to include :origin expect(outlisted_articles).to eq [removed_article] - expect(new_articles).to_not be_empty + expect(new_articles).not_to be_empty expect(new_articles[0].order_number).to eq new_shared_article.number expect(new_articles[0].availability?).to be true end @@ -109,13 +113,13 @@ describe Supplier do it 'returns the expected articles' do updated_article_pairs, outlisted_articles, new_articles = supplier.sync_all - expect(updated_article_pairs).to_not be_empty + expect(updated_article_pairs).not_to be_empty expect(updated_article_pairs[0][0].id).to eq updated_article.id expect(updated_article_pairs[0][1].keys).to include :origin expect(outlisted_articles).to eq [removed_article] - expect(new_articles).to_not be_empty + expect(new_articles).not_to be_empty expect(new_articles[0].order_number).to eq new_shared_article.number expect(new_articles[0].availability?).to be false end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 59a797de..def2d1f8 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,9 +2,9 @@ require_relative '../spec_helper' describe User do it 'is correctly created' do - user = create :user, + user = create(:user, nick: 'johnnydoe', first_name: 'Johnny', last_name: 'DoeBar', - email: 'johnnydoe@foodcoop.test', phone: '+1234567890' + email: 'johnnydoe@foodcoop.test', phone: '+1234567890') expect(user.nick).to eq('johnnydoe') expect(user.first_name).to eq('Johnny') expect(user.last_name).to eq('DoeBar') @@ -14,7 +14,7 @@ describe User do end describe 'does not have the role' do - let(:user) { create :user } + let(:user) { create(:user) } it 'admin' do expect(user.role_admin?).to be_falsey end it 'finance' do expect(user.role_finance?).to be_falsey end @@ -24,7 +24,7 @@ describe User do end describe do - let(:user) { create :user, password: 'blahblahblah' } + let(:user) { create(:user, password: 'blahblahblah') } it 'can authenticate with correct password' do expect(User.authenticate(user.nick, 'blahblahblah')).to be_truthy @@ -74,124 +74,124 @@ describe User do end describe 'admin' do - let(:user) { create :admin } + let(:user) { create(:admin) } it 'default admin role' do expect(user.role_admin?).to be_truthy end end describe 'sort correctly' do it 'by nick' do - user_b = create :user, nick: 'bbb' - user_a = create :user, nick: 'aaa' - user_c = create :user, nick: 'ccc' + user_b = create(:user, nick: 'bbb') + user_a = create(:user, nick: 'aaa') + user_c = create(:user, nick: 'ccc') expect(User.sort_by_param('nick')).to eq([user_a, user_b, user_c]) end it 'reverse by nick' do - user_b = create :user, nick: 'bbb' - user_a = create :user, nick: 'aaa' - user_c = create :user, nick: 'ccc' + user_b = create(:user, nick: 'bbb') + user_a = create(:user, nick: 'aaa') + user_c = create(:user, nick: 'ccc') expect(User.sort_by_param('nick_reverse')).to eq([user_c, user_b, user_a]) end it 'by name' do - user_b = create :user, first_name: 'aaa', last_name: 'bbb' - user_a = create :user, first_name: 'aaa', last_name: 'aaa' - user_c = create :user, first_name: 'ccc', last_name: 'aaa' + user_b = create(:user, first_name: 'aaa', last_name: 'bbb') + user_a = create(:user, first_name: 'aaa', last_name: 'aaa') + user_c = create(:user, first_name: 'ccc', last_name: 'aaa') expect(User.sort_by_param('name')).to eq([user_a, user_b, user_c]) end it 'reverse by name' do - user_b = create :user, first_name: 'aaa', last_name: 'bbb' - user_a = create :user, first_name: 'aaa', last_name: 'aaa' - user_c = create :user, first_name: 'ccc', last_name: 'aaa' + user_b = create(:user, first_name: 'aaa', last_name: 'bbb') + user_a = create(:user, first_name: 'aaa', last_name: 'aaa') + user_c = create(:user, first_name: 'ccc', last_name: 'aaa') expect(User.sort_by_param('name_reverse')).to eq([user_c, user_b, user_a]) end it 'by email' do - user_b = create :user, email: 'bbb@dummy.com' - user_a = create :user, email: 'aaa@dummy.com' - user_c = create :user, email: 'ccc@dummy.com' + user_b = create(:user, email: 'bbb@dummy.com') + user_a = create(:user, email: 'aaa@dummy.com') + user_c = create(:user, email: 'ccc@dummy.com') expect(User.sort_by_param('email')).to eq([user_a, user_b, user_c]) end it 'reverse by email' do - user_b = create :user, email: 'bbb@dummy.com' - user_a = create :user, email: 'aaa@dummy.com' - user_c = create :user, email: 'ccc@dummy.com' + user_b = create(:user, email: 'bbb@dummy.com') + user_a = create(:user, email: 'aaa@dummy.com') + user_c = create(:user, email: 'ccc@dummy.com') expect(User.sort_by_param('email_reverse')).to eq([user_c, user_b, user_a]) end it 'by phone' do - user_b = create :user, phone: 'bbb' - user_a = create :user, phone: 'aaa' - user_c = create :user, phone: 'ccc' + user_b = create(:user, phone: 'bbb') + user_a = create(:user, phone: 'aaa') + user_c = create(:user, phone: 'ccc') expect(User.sort_by_param('phone')).to eq([user_a, user_b, user_c]) end it 'reverse by phone' do - user_b = create :user, phone: 'bbb' - user_a = create :user, phone: 'aaa' - user_c = create :user, phone: 'ccc' + user_b = create(:user, phone: 'bbb') + user_a = create(:user, phone: 'aaa') + user_c = create(:user, phone: 'ccc') expect(User.sort_by_param('phone_reverse')).to eq([user_c, user_b, user_a]) end it 'by last_activity' do - user_b = create :user, last_activity: 3.days.ago - user_a = create :user, last_activity: 5.days.ago - user_c = create :user, last_activity: Time.now + user_b = create(:user, last_activity: 3.days.ago) + user_a = create(:user, last_activity: 5.days.ago) + user_c = create(:user, last_activity: Time.now) expect(User.sort_by_param('last_activity')).to eq([user_a, user_b, user_c]) end it 'reverse by last_activity' do - user_b = create :user, last_activity: 3.days.ago - user_a = create :user, last_activity: 5.days.ago - user_c = create :user, last_activity: Time.now + user_b = create(:user, last_activity: 3.days.ago) + user_a = create(:user, last_activity: 5.days.ago) + user_c = create(:user, last_activity: Time.now) expect(User.sort_by_param('last_activity_reverse')).to eq([user_c, user_b, user_a]) end it 'by ordergroup' do - user_b = create :user, groups: [create(:workgroup, name: 'a'), create(:ordergroup, name: 'bb')] - user_a = create :user, groups: [create(:workgroup, name: 'b'), create(:ordergroup, name: 'aa')] - user_c = create :user, groups: [create(:workgroup, name: 'c'), create(:ordergroup, name: 'cc')] + user_b = create(:user, groups: [create(:workgroup, name: 'a'), create(:ordergroup, name: 'bb')]) + user_a = create(:user, groups: [create(:workgroup, name: 'b'), create(:ordergroup, name: 'aa')]) + user_c = create(:user, groups: [create(:workgroup, name: 'c'), create(:ordergroup, name: 'cc')]) expect(User.sort_by_param('ordergroup')).to eq([user_a, user_b, user_c]) end it 'reverse by ordergroup' do - user_b = create :user, groups: [create(:workgroup, name: 'a'), create(:ordergroup, name: 'bb')] - user_a = create :user, groups: [create(:workgroup, name: 'b'), create(:ordergroup, name: 'aa')] - user_c = create :user, groups: [create(:workgroup, name: 'c'), create(:ordergroup, name: 'cc')] + user_b = create(:user, groups: [create(:workgroup, name: 'a'), create(:ordergroup, name: 'bb')]) + user_a = create(:user, groups: [create(:workgroup, name: 'b'), create(:ordergroup, name: 'aa')]) + user_c = create(:user, groups: [create(:workgroup, name: 'c'), create(:ordergroup, name: 'cc')]) expect(User.sort_by_param('ordergroup_reverse')).to eq([user_c, user_b, user_a]) end it 'and users are only listed once' do - create :user + create(:user) expect(User.sort_by_param('ordergroup').size).to eq(1) end it 'and users belonging to a workgroup are only listed once' do - create :admin + create(:admin) expect(User.sort_by_param('ordergroup').size).to eq(1) end it 'and users belonging to 2 ordergroups are only listed once' do - user = create :user - create :ordergroup, user_ids: [user.id] - create :ordergroup, user_ids: [user.id] + user = create(:user) + create(:ordergroup, user_ids: [user.id]) + create(:ordergroup, user_ids: [user.id]) expect(User.sort_by_param('ordergroup').size).to eq(1) end diff --git a/spec/requests/api/v1/article_categories_controller_spec.rb b/spec/requests/api/v1/article_categories_controller_spec.rb new file mode 100644 index 00000000..209349a2 --- /dev/null +++ b/spec/requests/api/v1/article_categories_controller_spec.rb @@ -0,0 +1,53 @@ +require 'swagger_helper' + +describe Api::V1::ArticleCategoriesController do + include ApiHelper + + path '/article_categories' do + get 'article categories' do + tags 'Category' + produces 'application/json' + pagination_param + let(:order_article) { create(:order, article_count: 1).order_articles.first } + let(:stock_article) { create(:stock_article) } + let(:stock_order_article) { create(:stock_order, article_ids: [stock_article.id]).order_articles.first } + + response '200', 'success' do + schema type: :object, properties: { + article_categories: { + type: :array, + items: { + '$ref': '#/components/schemas/ArticleCategory' + } + } + } + run_test! + end + + it_handles_invalid_token + end + end + + path '/article_categories/{id}' do + get 'find article category by id' do + tags 'Category' + produces 'application/json' + id_url_param + + response '200', 'article category found' do + schema type: :object, properties: { + article_categories: { + type: :array, + items: { + '$ref': '#/components/schemas/ArticleCategory' + } + } + } + let(:id) { create(:article_category, name: 'dairy').id } + run_test! + end + it_handles_invalid_token_with_id + it_cannot_find_object + end + end +end diff --git a/spec/requests/api/v1/configs_controller_spec.rb b/spec/requests/api/v1/configs_controller_spec.rb new file mode 100644 index 00000000..1809065a --- /dev/null +++ b/spec/requests/api/v1/configs_controller_spec.rb @@ -0,0 +1,20 @@ +require 'swagger_helper' + +describe Api::V1::ConfigsController do + include ApiHelper + + path '/config' do + get 'configuration variables' do + tags 'General' + produces 'application/json' + let(:api_scopes) { ['config:user'] } + + response '200', 'success' do + schema type: :object, properties: {} + run_test! + end + + it_handles_invalid_token_and_scope + end + end +end diff --git a/spec/requests/api/v1/financial_transaction_classes_controller_spec.rb b/spec/requests/api/v1/financial_transaction_classes_controller_spec.rb new file mode 100644 index 00000000..4db7f2b7 --- /dev/null +++ b/spec/requests/api/v1/financial_transaction_classes_controller_spec.rb @@ -0,0 +1,54 @@ +require 'swagger_helper' + +describe Api::V1::FinancialTransactionClassesController do + include ApiHelper + + path '/financial_transaction_classes' do + get 'financial transaction classes' do + tags 'Category' + produces 'application/json' + pagination_param + let(:financial_transaction_class) { create(:financial_transaction_class) } + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref' => '#/components/schemas/Meta' }, + financial_transaction_class: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransactionClass' + } + } + } + + run_test! + end + + it_handles_invalid_token + end + end + + path '/financial_transaction_classes/{id}' do + get 'Retrieves a financial transaction class' do + tags 'Category' + produces 'application/json' + id_url_param + + response '200', 'financial transaction class found' do + schema type: :object, properties: { + financial_transaction_classes: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransactionClass' + } + } + } + let(:id) { create(:financial_transaction_class).id } + run_test! + end + + it_handles_invalid_token_with_id + it_cannot_find_object 'financial transaction class not found' + end + end +end diff --git a/spec/requests/api/v1/financial_transaction_types_controller_spec.rb b/spec/requests/api/v1/financial_transaction_types_controller_spec.rb new file mode 100644 index 00000000..d061214e --- /dev/null +++ b/spec/requests/api/v1/financial_transaction_types_controller_spec.rb @@ -0,0 +1,52 @@ +require 'swagger_helper' + +describe Api::V1::FinancialTransactionTypesController do + include ApiHelper + + path '/financial_transaction_types' do + get 'financial transaction types' do + tags 'Category' + produces 'application/json' + pagination_param + let(:financial_transaction_type) { create(:financial_transaction_type) } + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref' => '#/components/schemas/Meta' }, + financial_transaction_type: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransactionType' + } + } + } + run_test! + end + + it_handles_invalid_token + end + end + + path '/financial_transaction_types/{id}' do + get 'find financial transaction type by id' do + tags 'Category' + produces 'application/json' + id_url_param + + response '200', 'financial transaction type found' do + schema type: :object, properties: { + financial_transaction_types: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransactionType' + } + } + } + let(:id) { create(:financial_transaction_type).id } + run_test! + end + + it_handles_invalid_token_with_id + it_cannot_find_object 'financial transaction type not found' + end + end +end diff --git a/spec/requests/api/v1/financial_transactions_controller_spec.rb b/spec/requests/api/v1/financial_transactions_controller_spec.rb new file mode 100644 index 00000000..915f3891 --- /dev/null +++ b/spec/requests/api/v1/financial_transactions_controller_spec.rb @@ -0,0 +1,58 @@ +require 'swagger_helper' + +describe Api::V1::FinancialTransactionsController do + include ApiHelper + let!(:finance_user) { create(:user, groups: [create(:workgroup, role_finance: true)]) } + let!(:api_scopes) { ['finance:read', 'finance:write'] } + let(:api_access_token) do + create(:oauth2_access_token, resource_owner_id: finance_user.id, scopes: api_scopes&.join(' ')).token + end + let(:financial_transaction) { create(:financial_transaction, user: user) } + + path '/financial_transactions' do + get 'financial transactions' do + tags 'Financial Transaction' + produces 'application/json' + pagination_param + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref' => '#/components/schemas/Meta' }, + financial_transaction: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransaction' + } + } + } + + run_test! + end + it_handles_invalid_token_and_scope + end + end + + path '/financial_transactions/{id}' do + get 'Retrieves a financial transaction ' do + tags 'Financial Transaction' + produces 'application/json' + id_url_param + + response '200', 'financial transaction found' do + schema type: :object, properties: { + financial_transaction: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransaction' + } + } + } + let(:id) { FinancialTransaction.create(user: user).id } + run_test! + end + it_handles_invalid_token_with_id + it_handles_invalid_scope_with_id + it_cannot_find_object 'financial transaction not found' + end + end +end diff --git a/spec/requests/api/v1/navigations_controller_spec.rb b/spec/requests/api/v1/navigations_controller_spec.rb new file mode 100644 index 00000000..13c0a449 --- /dev/null +++ b/spec/requests/api/v1/navigations_controller_spec.rb @@ -0,0 +1,24 @@ +require 'swagger_helper' + +describe Api::V1::NavigationsController do + include ApiHelper + + path '/navigation' do + get 'navigation' do + tags 'General' + produces 'application/json' + + response '200', 'success' do + schema type: :object, properties: { + navigation: { + '$ref' => '#/components/schemas/Navigation' + } + } + + run_test! + end + + it_handles_invalid_token + end + end +end diff --git a/spec/requests/api/v1/order_articles_controller_spec.rb b/spec/requests/api/v1/order_articles_controller_spec.rb new file mode 100644 index 00000000..97fea3bb --- /dev/null +++ b/spec/requests/api/v1/order_articles_controller_spec.rb @@ -0,0 +1,115 @@ +require 'swagger_helper' + +describe Api::V1::OrderArticlesController do + include ApiHelper + + path '/order_articles' do + get 'order articles' do + tags 'Order' + produces 'application/json' + pagination_param + q_ordered_url_param + + let(:api_scopes) { ['orders:read', 'orders:write'] } + let(:order) { create(:order, article_count: 4) } + let(:order_articles) { order.order_articles } + + before do + order_articles[0].update! quantity: 0, tolerance: 0, units_to_order: 0 + order_articles[1].update! quantity: 1, tolerance: 0, units_to_order: 0 + order_articles[2].update! quantity: 0, tolerance: 1, units_to_order: 0 + order_articles[3].update! quantity: 0, tolerance: 0, units_to_order: 1 + end + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref' => '#/components/schemas/Meta' }, + order_articles: { + type: :array, + items: { + '$ref': '#/components/schemas/OrderArticle' + } + } + } + describe '(unset)' do + run_test! + end + + describe 'all' do + let(:q) { { q: { ordered: 'all' } } } + + run_test! do |response| + json_order_articles = JSON.parse(response.body)['order_articles'] + json_order_article_ids = json_order_articles.map { |d| d['id'].to_i } + expect(json_order_article_ids).to match_array order_articles[1..2].map(&:id) + end + end + + describe 'when ordered by supplier' do + let(:q) { { q: { ordered: 'supplier' } } } + + run_test! do |response| + json_order_articles = JSON.parse(response.body)['order_articles'] + json_order_article_ids = json_order_articles.map { |d| d['id'].to_i } + expect(json_order_article_ids).to match_array [order_articles[3].id] + end + end + + describe 'when ordered by member' do + let(:q) { { q: { ordered: 'member' } } } + + run_test! do |response| + json_order_articles = JSON.parse(response.body)['order_articles'] + expect(json_order_articles.count).to eq 0 + end + end + + context 'when ordered by user' do + let(:user) { create(:user, :ordergroup) } + let(:go) { create(:group_order, order: order, ordergroup: user.ordergroup) } + + before do + create(:group_order_article, group_order: go, order_article: order_articles[1], quantity: 1) + create(:group_order_article, group_order: go, order_article: order_articles[2], tolerance: 0) + end + + describe 'member' do + let(:q) { { q: { ordered: 'member' } } } + + run_test! do |response| + json_order_articles = JSON.parse(response.body)['order_articles'] + json_order_article_ids = json_order_articles.map { |d| d['id'].to_i } + expect(json_order_article_ids).to match_array order_articles[1..2].map(&:id) + end + end + end + end + + it_handles_invalid_token_and_scope + end + end + + path '/order_articles/{id}' do + get 'order articles' do + tags 'Order' + produces 'application/json' + id_url_param + let(:api_scopes) { ['orders:read', 'orders:write'] } + + response '200', 'success' do + schema type: :object, properties: { + order_article: { + '$ref': '#/components/schemas/OrderArticle' + } + } + let(:order) { create(:order, article_count: 1) } + let(:id) { order.order_articles.first.id } + + run_test! + end + + it_handles_invalid_token_and_scope + it_cannot_find_object 'order article not found' + end + end +end diff --git a/spec/requests/api/v1/orders_controller_spec.rb b/spec/requests/api/v1/orders_controller_spec.rb new file mode 100644 index 00000000..0ad4131e --- /dev/null +++ b/spec/requests/api/v1/orders_controller_spec.rb @@ -0,0 +1,55 @@ +require 'swagger_helper' + +describe Api::V1::OrdersController do + include ApiHelper + let(:api_scopes) { ['orders:read'] } + + path '/orders' do + get 'orders' do + tags 'Order' + produces 'application/json' + pagination_param + let(:order) { create(:order) } + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref' => '#/components/schemas/Meta' }, + ordes: { + type: :array, + items: { + '$ref': '#/components/schemas/Order' + } + } + } + + run_test! + end + + it_handles_invalid_token_and_scope + end + end + + path '/orders/{id}' do + get 'Order' do + tags 'Order' + produces 'application/json' + id_url_param + + let(:order) { create(:order) } + + response '200', 'success' do + schema type: :object, properties: { + order: { '$ref' => '#/components/schemas/Order' } + } + let(:id) { order.id } + + run_test! do |response| + expect(JSON.parse(response.body)['order']['id']).to eq order.id + end + end + + it_handles_invalid_token_and_scope + it_cannot_find_object 'order not found' + end + end +end diff --git a/spec/requests/api/v1/user/financial_transactions_spec.rb b/spec/requests/api/v1/user/financial_transactions_spec.rb new file mode 100644 index 00000000..c37d5b22 --- /dev/null +++ b/spec/requests/api/v1/user/financial_transactions_spec.rb @@ -0,0 +1,107 @@ +require 'swagger_helper' + +describe 'User' do + include ApiHelper + + let(:api_scopes) { ['finance:user'] } + let(:user) { create(:user, groups: [create(:ordergroup)]) } + let(:other_user2) { create(:user) } + let(:ft) { create(:financial_transaction, user: user, ordergroup: user.ordergroup) } + + before do + ft + end + + path '/user/financial_transactions' do + post 'create new financial transaction (requires enabled self service)' do + tags 'Financial Transaction' + consumes 'application/json' + produces 'application/json' + + parameter name: :financial_transaction, in: :body, schema: { + type: :object, + properties: { + amount: { type: :integer }, + financial_transaction_type: { type: :integer }, + note: { type: :string } + } + } + + let(:financial_transaction) do + { amount: 3, financial_transaction_type_id: create(:financial_transaction_type).id, note: 'lirum larum' } + end + + response '200', 'success' do + schema type: :object, properties: { + financial_transaction: { '$ref': '#/components/schemas/FinancialTransaction' } + } + run_test! + end + + it_handles_invalid_token_with_id + it_handles_invalid_scope_with_id 'user has no ordergroup, is below minimum balance, self service is disabled, or missing scope' + + response '404', 'financial transaction type not found' do + schema '$ref' => '#/components/schemas/Error404' + let(:financial_transaction) { { amount: 3, financial_transaction_type_id: 'invalid', note: 'lirum larum' } } + run_test! + end + + response '422', 'invalid parameter value' do + schema '$ref' => '#/components/schemas/Error422' + let(:financial_transaction) { { amount: "abc", financial_transaction_type_id: create(:financial_transaction_type).id, note: "foo bar" } } + run_test! + end + end + + get "financial transactions of the member's ordergroup" do + tags 'User', 'Financial Transaction' + produces 'application/json' + pagination_param + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref': '#/components/schemas/Meta' }, + financial_transaction: { + type: :array, + items: { + '$ref': '#/components/schemas/FinancialTransaction' + } + } + } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['financial_transactions'].first['id']).to eq(ft.id) + end + end + + it_handles_invalid_token_and_scope + end + end + + path '/user/financial_transactions/{id}' do + get 'find financial transaction by id' do + tags 'User', 'Financial Transaction' + produces 'application/json' + id_url_param + + response '200', 'success' do + schema type: :object, properties: { + financial_transaction: { + '$ref': '#/components/schemas/FinancialTransaction' + } + } + let(:id) { ft.id } + run_test! do |response| + data = JSON.parse(response.body) + expect(data['financial_transaction']['id']).to eq(ft.id) + end + end + + it_handles_invalid_token_with_id + it_handles_invalid_scope_with_id 'user has no ordergroup or missing scope' + it_cannot_find_object 'financial transaction not found' + end + end +end diff --git a/spec/requests/api/v1/user/group_order_articles_spec.rb b/spec/requests/api/v1/user/group_order_articles_spec.rb new file mode 100644 index 00000000..e93c7ecf --- /dev/null +++ b/spec/requests/api/v1/user/group_order_articles_spec.rb @@ -0,0 +1,194 @@ +require 'swagger_helper' + +describe 'User' do + include ApiHelper + + let(:api_scopes) { ['group_orders:user'] } + let(:user) { create(:user, groups: [create(:ordergroup)]) } + let(:other_user2) { create(:user) } + let(:order) { create(:order, article_count: 4) } + let(:order_articles) { order.order_articles } + let(:group_order) { create(:group_order, ordergroup: user.ordergroup, order_id: order.id) } + let(:goa) { create(:group_order_article, group_order: group_order, order_article: order_articles.first) } + + before do + goa + end + + path '/user/group_order_articles' do + get 'group order articles' do + tags 'User', 'Order' + produces 'application/json' + pagination_param + q_ordered_url_param + + response '200', 'success' do + schema type: :object, properties: { + meta: { '$ref': '#/components/schemas/Meta' }, + group_order_article: { + type: :array, + items: { + '$ref': '#/components/schemas/GroupOrderArticle' + } + } + } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['group_order_articles'].first['id']).to eq(goa.id) + end + end + + it_handles_invalid_token + it_handles_invalid_scope 'user has no ordergroup or missing scope' + end + + post 'create new group order article' do + tags 'User', 'Order' + consumes 'application/json' + produces 'application/json' + parameter name: :group_order_article, in: :body, + description: 'group order article to create', + required: true, + schema: { '$ref': '#/components/schemas/GroupOrderArticleForCreate' } + + let(:group_order_article) { { order_article_id: order_articles.last.id, quantity: 1, tolerance: 2 } } + response '200', 'success' do + schema type: :object, properties: { + group_order_article: { + '$ref': '#/components/schemas/GroupOrderArticle' + }, + order_article: { + '$ref': '#/components/schemas/OrderArticle' + } + } + run_test! + end + + it_handles_invalid_token_with_id + it_handles_invalid_scope_with_id 'user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope' + + response '404', 'order article not found in open orders' do + let(:group_order_article) { { order_article_id: 'invalid', quantity: 1, tolerance: 2 } } + schema '$ref' => '#/components/schemas/Error404' + run_test! + end + + response '422', 'invalid parameter value or group order article already exists' do + let(:group_order_article) { { order_article_id: goa.order_article_id, quantity: 1, tolerance: 2 } } + schema '$ref' => '#/components/schemas/Error422' + run_test! + end + end + end + + path '/user/group_order_articles/{id}' do + get 'find group order article by id' do + tags 'User', 'Order' + produces 'application/json' + id_url_param + + response '200', 'success' do + schema type: :object, properties: { + group_order_article: { + '$ref': '#/components/schemas/GroupOrderArticle' + }, + order_article: { + '$ref': '#/components/schemas/OrderArticle' + } + } + + let(:id) { goa.id } + run_test! do |response| + data = JSON.parse(response.body) + expect(data['group_order_article']['id']).to eq(goa.id) + end + end + + it_handles_invalid_scope_with_id + it_handles_invalid_token_with_id + it_cannot_find_object 'group order article not found' + end + + patch 'update a group order article (but delete if quantity and tolerance are zero)' do + tags 'User', 'Order' + consumes 'application/json' + produces 'application/json' + id_url_param + parameter name: :group_order_article, in: :body, + description: 'group order article update', + required: true, + schema: { '$ref': '#/components/schemas/GroupOrderArticleForUpdate' } + + let(:id) { goa.id } + let(:group_order_article) { { order_article_id: goa.order_article_id, quantity: 2, tolerance: 2 } } + + response '200', 'success' do + schema type: :object, properties: { + group_order_article: { + '$ref': '#/components/schemas/GroupOrderArticle' + } + } + run_test! + end + + response 401, 'not logged-in' do + schema '$ref' => '#/components/schemas/Error401' + let(:Authorization) { 'abc' } # rubocop:disable RSpec/VariableName + run_test! + end + + response 403, + 'user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope' do + let(:api_scopes) { ['none'] } + schema '$ref' => '#/components/schemas/Error403' + run_test! + end + + response '404', 'order article not found in open orders' do + schema type: :object, properties: { + group_order_article: { + '$ref': '#/components/schemas/GroupOrderArticle' + } + } + let(:id) { 'invalid' } + run_test! + end + + response '422', 'invalid parameter value' do + let(:group_order_article) { { order_article_id: 'invalid', quantity: -5, tolerance: 'invalid' } } + schema '$ref' => '#/components/schemas/Error422' + run_test! + end + end + + delete 'remove group order article' do + tags 'User', 'Order' + consumes 'application/json' + produces 'application/json' + id_url_param + let(:api_scopes) { ['group_orders:user'] } + + response '200', 'success' do + schema type: :object, properties: { + group_order_article: { + '$ref': '#/components/schemas/GroupOrderArticle' + } + } + let(:id) { goa.id } + run_test! + end + + it_handles_invalid_token_with_id + + response 403, + 'user has no ordergroup, order not open, is below minimum balance, has not enough apple points, or missing scope' do + let(:api_scopes) { ['none'] } + schema '$ref' => '#/components/schemas/Error403' + run_test! + end + + it_cannot_find_object 'order article not found in open orders' + end + end +end diff --git a/spec/requests/api/v1/user/users_spec.rb b/spec/requests/api/v1/user/users_spec.rb new file mode 100644 index 00000000..90e343fa --- /dev/null +++ b/spec/requests/api/v1/user/users_spec.rb @@ -0,0 +1,103 @@ +require 'swagger_helper' + +describe 'User' do + include ApiHelper + + path '/user' do + get 'info about the currently logged-in user' do + tags 'User' + produces 'application/json' + let(:api_scopes) { ['user:read'] } + let(:other_user1) { create(:user) } + let(:user) { create(:user) } + let(:other_user2) { create(:user) } + + response '200', 'success' do + schema type: :object, + properties: { + user: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string, + description: 'full name' + }, + email: { + type: :string, + description: 'email address' + }, + locale: { + type: :string, + description: 'language code' + } + }, + required: %w[id name email] + } + } + + run_test! do |response| + data = JSON.parse(response.body) + expect(data['user']['id']).to eq(user.id) + end + end + + it_handles_invalid_token_and_scope + end + end + + path '/user/financial_overview' do + get 'financial summary about the currently logged-in user' do + tags 'User', 'Financial Transaction' + produces 'application/json' + let(:user) { create(:user, :ordergroup) } + let(:api_scopes) { ['finance:user'] } + FinancialTransactionClass.create(name: 'TestTransaction') + + response 200, 'success' do + schema type: :object, + properties: { + financial_overview: { + type: :object, + properties: { + + account_balance: { + type: :number, + description: 'booked accout balance of ordergroup' + }, + available_funds: { + type: :number, + description: 'fund available to order articles' + }, + financial_transaction_class_sums: { + type: :array, + properties: { + id: { + type: :integer, + description: 'id of the financial transaction class' + }, + name: { + type: :string, + description: 'name of the financial transaction class' + }, + amount: { + type: :number, + description: 'sum of the amounts belonging to the financial transaction class' + } + }, + required: %w[id name amount] + } + }, + required: %w[account_balance available_funds financial_transaction_class_sums] + } + } + + run_test! + end + + it_handles_invalid_token_and_scope + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 88dea423..3bbf03ea 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,8 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' -ENV["RAILS_ENV"] ||= 'test' -ENV["FOODSOFT_APP_CONFIG"] ||= 'spec/app_config.yml' # load special foodsoft config +ENV['RAILS_ENV'] ||= 'test' +ENV['FOODSOFT_APP_CONFIG'] ||= 'spec/app_config.yml' # load special foodsoft config require_relative 'support/coverage' # needs to be first -require File.expand_path("../../config/environment", __FILE__) +require File.expand_path('../config/environment', __dir__) require 'rspec/rails' require 'capybara/rails' require 'capybara/apparition' @@ -17,17 +17,17 @@ end # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. -Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } RSpec.configure do |config| # We use capybara with webkit, and need database_cleaner - config.before(:each) do + config.before do DatabaseCleaner.strategy = (RSpec.current_example.metadata[:js] ? :truncation : :transaction) DatabaseCleaner.start # clean slate mail queues, not sure why needed - https://github.com/rspec/rspec-rails/issues/661 ActionMailer::Base.deliveries.clear end - config.after(:each) do + config.after do DatabaseCleaner.clean # Need to clear cache for RailsSettings::CachedSettings Rails.cache.clear @@ -35,7 +35,7 @@ RSpec.configure do |config| # reload foodsoft configuration, so that tests can use FoodsoftConfig.config[:foo]=x # without messing up tests run after that - config.before(:each) do + config.before do FoodsoftConfig.init FoodsoftConfig.init_mailing end @@ -49,10 +49,10 @@ RSpec.configure do |config| # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 - config.order = "random" + config.order = 'random' + config.include SpecTestHelper, type: :controller config.include SessionHelper, type: :feature - # Automatically determine spec from directory structure, see: # https://www.relishapp.com/rspec/rspec-rails/v/3-0/docs/directory-structure config.infer_spec_type_from_file_location! diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb index ee0225f5..3a6e7894 100644 --- a/spec/support/api_helper.rb +++ b/spec/support/api_helper.rb @@ -4,22 +4,63 @@ module ApiHelper included do let(:user) { create(:user) } let(:api_scopes) { [] } # empty scopes for stricter testing (in reality this would be default_scopes) - let(:api_access_token) { create(:oauth2_access_token, resource_owner_id: user.id, scopes: api_scopes&.join(' ')).token } - let(:api_authorization) { "Bearer #{api_access_token}" } + let(:api_access_token) do + create(:oauth2_access_token, resource_owner_id: user.id, scopes: api_scopes&.join(' ')).token + end + let(:Authorization) { "Bearer #{api_access_token}" } # rubocop:disable RSpec/VariableName - def self.it_handles_invalid_token(method, path, params_block = -> { api_auth }) + def self.it_handles_invalid_token context 'with invalid access token' do - let(:api_access_token) { 'abc' } + let(:Authorization) { 'abc' } # rubocop:disable RSpec/VariableName - it { is_expected.to validate(method, path, 401, instance_exec(¶ms_block)) } + response 401, 'not logged-in' do + schema '$ref' => '#/components/schemas/Error401' + run_test! + end end end - def self.it_handles_invalid_scope(method, path, params_block = -> { api_auth }) + def self.it_handles_invalid_token_with_id + context 'with invalid access token' do + let(:Authorization) { 'abc' } # rubocop:disable RSpec/VariableName + let(:id) { 42 } # id doesn't matter here + + response 401, 'not logged-in' do + schema '$ref' => '#/components/schemas/Error401' + run_test! + end + end + end + + def self.it_handles_invalid_scope(description = 'missing scope') context 'with invalid scope' do let(:api_scopes) { ['none'] } - it { is_expected.to validate(method, path, 403, instance_exec(¶ms_block)) } + response 403, description do + schema '$ref' => '#/components/schemas/Error403' + run_test! + end + end + end + + def self.it_handles_invalid_scope_with_id(description = 'missing scope') + context 'with invalid scope' do + let(:api_scopes) { ['none'] } + let(:id) { 42 } # id doesn't matter here + + response 403, description do + schema '$ref' => '#/components/schemas/Error403' + run_test! + end + end + end + + def self.it_cannot_find_object(description = 'not found') + let(:id) { 'invalid' } + + response 404, description do + schema '$ref' => '#/components/schemas/Error404' + run_test! end end @@ -27,13 +68,25 @@ module ApiHelper it_handles_invalid_token(*args) it_handles_invalid_scope(*args) end - end - # Add authentication to parameters for {Swagger::RspecHelpers#validate} - # @param params [Hash] Query parameters - # @return Query parameters with authentication header - # @see Swagger::RspecHelpers#validate - def api_auth(params = {}) - { '_headers' => { 'Authorization' => api_authorization } }.deep_merge(params) + def self.id_url_param + parameter name: :id, in: :path, type: :integer, required: true + end + + def self.pagination_param + parameter name: :per_page, in: :query, type: :integer, required: false + parameter name: :page, in: :query, type: :integer, required: false + end + + def self.q_ordered_url_param + parameter name: :q, in: :query, required: false, + description: "'member' show articles ordered by the user's ordergroup, 'all' by all members, and 'supplier' ordered at the supplier", + schema: { + type: :object, + properties: { + ordered: { '$ref' => '#/components/schemas/q_ordered' } + } + } + end end end diff --git a/spec/support/api_oauth.rb b/spec/support/api_oauth.rb index 00d5318d..da8a56c1 100644 --- a/spec/support/api_oauth.rb +++ b/spec/support/api_oauth.rb @@ -5,7 +5,7 @@ module ApiOAuth included do let(:user) { build(:user) } let(:api_scopes) { [] } # empty scopes for stricter testing (in reality this would be default_scopes) - let(:api_access_token) { double(:acceptable? => true, :accessible? => true, scopes: api_scopes) } + let(:api_access_token) { double(acceptable?: true, accessible?: true, scopes: api_scopes) } before { allow(controller).to receive(:doorkeeper_token) { api_access_token } } before { allow(controller).to receive(:current_user) { user } } diff --git a/spec/support/coverage.rb b/spec/support/coverage.rb index 20bbdcf3..b4142c3d 100644 --- a/spec/support/coverage.rb +++ b/spec/support/coverage.rb @@ -11,7 +11,7 @@ if ENV['COVERAGE'] or ENV['COVERALLS'] # slightly tweaked coverage reporting def cov_no_plugins(source_file, path) - source_file.filename =~ /#{path}/ and not source_file.filename =~ /\/lib\/foodsoft_.*\// + source_file.filename =~ /#{path}/ and !(source_file.filename =~ %r{/lib/foodsoft_.*/}) end SimpleCov.start do add_filter '/spec/' @@ -21,6 +21,6 @@ if ENV['COVERAGE'] or ENV['COVERALLS'] add_group 'Helpers' do |s| cov_no_plugins s, '/app/helpers/' end add_group 'Documents' do |s| cov_no_plugins s, '/app/documents/' end add_group 'Libraries' do |s| cov_no_plugins s, '/lib/' end - add_group 'Plugins' do |s| s.filename =~ /\/lib\/foodsoft_.*\// end + add_group 'Plugins' do |s| s.filename =~ %r{/lib/foodsoft_.*/} end end end diff --git a/spec/support/faker.rb b/spec/support/faker.rb index 47441ca8..6516fa92 100644 --- a/spec/support/faker.rb +++ b/spec/support/faker.rb @@ -2,7 +2,7 @@ module Faker class Unit class << self def unit - ['kg', '1L', '100ml', 'piece', 'bunch', '500g'].sample + %w[kg 1L 100ml piece bunch 500g].sample end end end diff --git a/spec/support/integration.rb b/spec/support/integration.rb index 26add35a..6882ac5a 100644 --- a/spec/support/integration.rb +++ b/spec/support/integration.rb @@ -1,4 +1,4 @@ # @see http://stackoverflow.com/a/11048669/2866660 def scrolldown - page.execute_script "window.scrollBy(0,10000)" + page.execute_script 'window.scrollBy(0,10000)' end diff --git a/spec/support/session_helper.rb b/spec/support/session_helper.rb index 31fb0946..1075695e 100644 --- a/spec/support/session_helper.rb +++ b/spec/support/session_helper.rb @@ -1,14 +1,15 @@ module SessionHelper def login(user = nil, password = nil) visit login_path - user = FactoryBot.create :user if user.nil? + user = FactoryBot.create(:user) if user.nil? if user.instance_of? ::User - nick, password = user.nick, user.password + nick = user.nick + password = user.password else nick = user end - fill_in 'nick', :with => nick - fill_in 'password', :with => password + fill_in 'nick', with: nick + fill_in 'password', with: password find('input[type=submit]').click end end diff --git a/spec/support/shared_database.rb b/spec/support/shared_database.rb index f6c0ff0a..180877ec 100644 --- a/spec/support/shared_database.rb +++ b/spec/support/shared_database.rb @@ -4,7 +4,7 @@ ActiveSupport.on_load(:after_initialize) do # But take care when designing tests using the shared database. SharedSupplier.establish_connection Rails.env.to_sym SharedArticle.establish_connection Rails.env.to_sym - # hack for different structure of shared database + # HACK: for different structure of shared database SharedArticle.class_eval do belongs_to :supplier, class_name: 'SharedSupplier' alias_attribute :number, :order_number diff --git a/spec/support/spec_test_helper.rb b/spec/support/spec_test_helper.rb new file mode 100644 index 00000000..f3737c15 --- /dev/null +++ b/spec/support/spec_test_helper.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module SpecTestHelper + def login(user) + user = User.find_by_nick(user.nick) + session[:user_id] = user.id + session[:scope] = FoodsoftConfig[:default_scope] # Save scope in session to not allow switching between foodcoops with one account + session[:locale] = user.locale + end + + def current_user + User.find(session[:user_id]) + end + + def get_with_defaults(action, params: {}, xhr: false, format: nil) + params['foodcoop'] = FoodsoftConfig[:default_scope] + get action, params: params, xhr: xhr, format: format + end +end + +RSpec.configure do |config| + config.include SpecTestHelper, type: :controller +end diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb new file mode 100644 index 00000000..dbe5a912 --- /dev/null +++ b/spec/swagger_helper.rb @@ -0,0 +1,514 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.configure do |config| + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the rswag-api to serve API descriptions, you'll need + # to ensure that it's configured to serve Swagger from the same folder + config.swagger_root = Rails.root.join('swagger').to_s + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the 'rswag:specs:swaggerize' rake task, the complete Swagger will + # be generated at the provided relative path under swagger_root + # By default, the operations defined in spec files are added to the first + # document below. You can override this behavior by adding a swagger_doc tag to the + # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' + config.swagger_docs = { + 'v1/swagger.yaml' => { + openapi: '3.0.3', + info: { + title: 'API V1', + version: 'v1' + }, + paths: {}, + components: { + schemas: { + pagination: { + type: :object, + properties: { + recordCount: { type: :integer }, + pageCount: { type: :integer }, + currentPage: { type: :integer }, + pageSize: { type: :integer } + }, + required: %w[recordCount pageCount currentPage pageSize] + }, + Order: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string, + description: "name of the order's supplier (or stock)" + }, + starts: { + type: :string, + format: 'date-time', + description: 'when the order was opened' + }, + ends: { + type: :string, + nullable: true, + format: 'date-time', + description: 'when the order will close or was closed' + }, + boxfill: { + type: :string, + nullable: true, + format: 'date-time', + description: 'when the order will enter or entered the boxfill phase' + }, + pickup: { + type: :string, + nullable: true, + format: :date, + description: 'pickup date' + }, + is_open: { + type: :boolean, + description: 'if the order is currently open or not' + }, + is_boxfill: { + type: :boolean, + description: 'if the order is currently in the boxfill phase or not' + } + } + }, + Article: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string + }, + supplier_id: { + type: :integer, + description: 'id of supplier, or 0 for stock articles' + }, + supplier_name: { + type: :string, + nullable: true, + description: 'name of the supplier, or null for stock articles' + }, + unit: { + type: :string, + description: 'amount of each unit, e.g. "100 g" or "kg"' + }, + unit_quantity: { + type: :integer, + description: 'units can only be ordered from the supplier in multiples of unit_quantity' + }, + note: { + type: :string, + nullable: true, + description: 'generic note' + }, + manufacturer: { + type: :string, + nullable: true, + description: 'manufacturer' + }, + origin: { + type: :string, + nullable: true, + description: 'origin, preferably (starting with a) 2-letter ISO country code' + }, + article_category_id: { + type: :integer, + description: 'id of article category' + }, + quantity_available: { + type: :integer, + description: 'number of units available (only present on stock articles)' + } + }, + required: %w[id name supplier_id supplier_name unit unit_quantity note manufacturer origin + article_category_id] + }, + OrderArticle: { + type: :object, + properties: { + id: { + type: :integer + }, + order_id: { + type: :integer, + description: 'id of order this order article belongs to' + }, + price: { + type: :number, + format: :float, + description: 'foodcoop price' + }, + quantity: { + type: :integer, + description: 'number of units ordered by members' + }, + tolerance: { + type: :integer, + description: 'number of extra units that members are willing to buy to fill a box' + }, + units_to_order: { + type: :integer, + description: 'number of units to order from the supplier' + }, + article: { + '$ref': '#/components/schemas/Article' + } + } + }, + ArticleCategory: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string + } + }, + required: %w[id name] + }, + FinancialTransaction: { + allOf: [ + { '$ref': '#/components/schemas/FinancialTransactionForCreate' }, + { + type: :object, + properties: { + id: { + type: :integer + }, + amount: { + type: :number, + format: :float, + description: 'amount credited (negative for a debit transaction)' + }, + financial_transaction_type_id: { + type: :integer, + description: 'id of the type of the transaction' + }, + note: { + type: :string, + description: 'note entered with the transaction' + }, + user_id: { + type: :integer, + nullable: true, + description: 'id of user who entered the transaction (may be null for deleted users or 0 for a system user)' + }, + user_name: { + type: :string, + nullable: true, + description: 'name of user who entered the transaction (may be null or empty string for deleted users or system users)' + }, + financial_transaction_type_name: { + type: :string, + description: 'name of the type of the transaction' + }, + created_at: { + type: :string, + format: 'date-time', + description: 'when the transaction was entered' + } + }, + required: %w[id user_id user_name financial_transaction_type_name created_at] + } + ] + }, + FinancialTransactionForCreate: { + type: :object, + properties: { + amount: { + type: :number, + format: :float, + description: 'amount credited (negative for a debit transaction)' + }, + financial_transaction_type_id: + { + type: :integer, + description: 'id of the type of the transaction' + }, + note: { + type: :string, + description: 'note entered with the transaction' + } + }, + required: %w[amount note user_id] + }, + FinancialTransactionClass: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string + } + }, + required: %w[id name] + }, + FinancialTransactionType: { + type: :object, + properties: { + id: { + type: :integer + }, + name: { + type: :string + }, + name_short: { + type: :string, + nullable: true, + description: 'short name (used for bank transfers)' + }, + bank_account_id: { + type: :integer, + nullable: true, + description: 'id of the bank account used for this transaction type' + }, + bank_account_name: { + type: :string, + nullable: true, + description: 'name of the bank account used for this transaction type' + }, + bank_account_iban: { + type: :string, + nullable: true, + description: 'IBAN of the bank account used for this transaction type' + }, + financial_transaction_class_id: { + type: :integer, + description: 'id of the class of the transaction' + }, + financial_transaction_class_name: { + type: :string, + description: 'name of the class of the transaction' + } + }, + required: %w[id name financial_transaction_class_id financial_transaction_class_name] + }, + GroupOrderArticleForUpdate: { + type: :object, + properties: { + quantity: + { + type: :integer, + description: 'number of units ordered by the users ordergroup' + }, + tolerance: + { + type: :integer, + description: 'number of extra units the users ordergroup is willing to buy for filling a box' + } + } + }, + GroupOrderArticleForCreate: { + allOf: [ + { '$ref': '#/components/schemas/GroupOrderArticleForUpdate' }, + { + type: :object, + properties: { + order_article_id: + { + type: :integer, + description: 'id of order article' + } + } + } + ] + }, + GroupOrderArticle: { + allOf: [ + { '$ref': '#/components/schemas/GroupOrderArticleForCreate' }, + { + type: :object, + properties: { + id: { + type: :integer + }, + result: { + type: :number, + format: :float, + description: 'number of units the users ordergroup will receive or has received' + }, + total_price: + { + type: :number, + format: :float, + description: 'total price of this group order article' + }, + order_article_id: + { + type: :integer, + description: 'id of order article' + } + }, + required: %w[order_article_id] + } + ] + }, + q_ordered: { + type: :object, + properties: { + ordered: { + type: :string, + enum: %w[member all supplier] + } + } + }, + Meta: { + type: :object, + properties: { + page: { + type: :integer, + description: 'page number of the returned collection' + }, + per_page: { + type: :integer, + description: 'number of items per page' + }, + total_pages: { + type: :integer, + description: 'total number of pages' + }, + total_count: { + type: :integer, + description: 'total number of items in the collection' + } + }, + required: %w[page per_page total_pages total_count] + }, + Navigation: { + type: :array, + items: { + type: :object, + properties: { + name: { + type: :string, + description: 'title' + }, + url: { + type: :string, + description: 'link' + }, + items: { + '$ref': '#/components/schemas/Navigation' + } + }, + required: ['name'], + minProperties: 2 # name+url or name+items + } + }, + Error: { + type: :object, + properties: { + error: { + type: :string, + description: 'error code' + }, + error_description: { + type: :string, + description: 'human-readable error message (localized)' + } + } + }, + Error401: { + type: :object, + properties: { + error: { + type: :string, + description: 'unauthorized' + }, + error_description: { + '$ref': '#/components/schemas/Error/properties/error_description' + } + } + }, + Error403: { + type: :object, + properties: { + error: { + type: :string, + description: 'forbidden or invalid_scope' + }, + error_description: { + '$ref': '#/components/schemas/Error/properties/error_description' + } + } + }, + Error404: { + type: :object, + properties: { + error: { + type: :string, + description: 'not_found' + }, + error_description: { + '$ref': '#/components/schemas/Error/properties/error_description' + } + } + }, + Error422: { + type: :object, + properties: { + error: { + type: :string, + description: 'unprocessable entity' + }, + error_description: { + '$ref': '#/components/schemas/Error/properties/error_description' + } + } + } + }, + securitySchemes: { + oauth2: { + type: :oauth2, + flows: { + implicit: { + authorizationUrl: 'http://localhost:3000/f/oauth/authorize', + scopes: { + 'config:user': 'reading Foodsoft configuration for regular users', + 'config:read': 'reading Foodsoft configuration values', + 'config:write': 'reading and updating Foodsoft configuration values', + 'finance:user': 'accessing your own financial transactions', + 'finance:read': 'reading all financial transactions', + 'finance:write': 'reading and creating financial transactions', + 'user:read': 'reading your own user profile', + 'user:write': 'reading and updating your own user profile', + offline_access: 'retain access after user has logged out' + } + } + } + } + } + }, + servers: [ + { + url: 'http://{defaultHost}/f/api/v1', + variables: { + defaultHost: { + default: 'localhost:3000' + } + } + } + ], + security: [ + oauth2: [ + 'user:read' + ] + ] + } + } + + # Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'. + # The swagger_docs configuration option has the filename including format in + # the key, this may want to be changed to avoid putting yaml in json files. + # Defaults to json. Accepts ':json' and ':yaml'. + config.swagger_format = :yaml +end diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 00000000..e69de29b